This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Don’t inline sub(){0; $x} if $x changes elsewhere
[perl5.git] / t / op / const-optree.t
1 #!perl
2
3 # Test the various op trees that turn sub () { ... } into a constant, and
4 # some variants that don’t.
5
6 BEGIN {
7     chdir 't';
8     require './test.pl';
9     @INC = '../lib';
10 }
11 plan 26;
12
13 # @tests is an array of hash refs, each of which can have various keys:
14 #
15 #   nickname    - name of the sub to use in test names
16 #   generator   - a sub returning a code ref to test
17 #   finally     - sub to run after the other tests
18 #
19 # Each of the following gives expected test results.  If the key is
20 # omitted, the test is skipped:
21 #
22 #   retval      - the returned code ref’s return value
23 #   same_retval - whether the same scalar is returned each time
24 #   inlinable   - whether the sub is inlinable
25 #   deprecated  - whether the sub returning a code ref will emit a depreca-
26 #                 tion warning when called
27 #   method      - whether the sub has the :method attribute
28
29 # [perl #63540] Don’t treat sub { if(){.....}; "constant" } as a constant
30 sub blonk { ++$blonk_was_called }
31 push @tests, {
32   nickname    => 'sub with null+kids (if-block), then constant',
33   generator   => sub {
34     # This used to turn into a constant with the value of $x
35     my $x = 7;
36     sub() { if($x){ () = "tralala"; blonk() }; 0 }
37   },
38   retval      => 0,
39   same_retval => 0,
40   inlinable   => 0,
41   deprecated  => 0,
42   method      => 0,
43   finally     => sub { ok($blonk_was_called, 'RT #63540'); },
44 };
45
46 # [perl #79908]
47 push @tests, {
48   nickname    => 'sub with simple lexical modified elsewhere',
49   generator   => sub { my $x = 5; my $ret = sub(){$x}; $x = 7; $ret },
50   retval      => 5, # change to 7 when the deprecation cycle is over
51   same_retval => 0,
52   inlinable   => 1,
53   deprecated  => 1,
54   method      => 0,
55 };
56
57 push @tests, {
58   nickname    => 'sub () { 0; $x } with $x modified elsewhere',
59   generator   => sub { my $x = 5; my $ret = sub(){0;$x}; $x = 8; $ret },
60   retval      => 8,
61   same_retval => 0,
62   inlinable   => 0,
63   deprecated  => 0,
64   method      => 0,
65 };
66
67 push @tests, {
68   nickname    => 'sub () { 0; $x } with $x unmodified elsewhere',
69   generator   => sub { my $x = 5; my $y = $x; sub(){0;$x} },
70   retval      => 5,
71   same_retval => 0,
72   inlinable   => 1,
73   deprecated  => 0,
74   method      => 0,
75 };
76
77 push @tests, {
78   nickname    => 'sub:method with simple lexical',
79   generator   => sub { my $y; sub():method{$y} },
80   retval      => undef,
81   same_retval => 0,
82   inlinable   => 1,
83   deprecated  => 0,
84   method      => 1,
85 };
86
87
88 use feature 'refaliasing';
89 no warnings 'experimental::refaliasing';
90 for \%_ (@tests) {
91     my $nickname = $_{nickname};
92     my $w;
93     local $SIG{__WARN__} = sub { $w = shift };
94     my $sub = &{$_{generator}};
95     if (exists $_{deprecated}) {
96         if ($_{deprecated}) {
97             like $w, qr/^Constants from lexical variables potentially (?x:
98                        )modified elsewhere are deprecated at /,
99                 "$nickname is deprecated";
100         }
101         else {
102             is $w, undef, "$nickname is not deprecated";
103         }
104     }
105     if (exists $_{retval}) {
106         is &$sub, $_{retval}, "retval of $nickname";
107     }
108     if (exists $_{same_retval}) {
109         my $same = $_{same_retval} ? "same" : "different";
110         &{$_{same_retval} ? \&is : \&isnt}(
111             \scalar &$sub(), \scalar &$sub(),
112             "$nickname gives $same retval each call"
113         );
114     }
115     if (exists $_{inlinable}) {
116         local *temp_inlinability_test = $sub;
117         $w = undef;
118         use warnings 'redefine';
119         *temp_inlinability_test = sub (){};
120         my $S = $_{inlinable} ? "Constant s" : "S";
121         my $not = " not" x! $_{inlinable};
122         like $w, qr/^${S}ubroutine .* redefined at /,
123                 "$nickname is$not inlinable";
124     }
125     if (exists $_{method}) {
126         local *time = $sub;
127         $w = undef;
128         use warnings 'ambiguous';
129         eval "()=time";
130         if ($_{method}) {
131             is $w, undef, "$nickname has :method attribute";
132         }
133         else {
134             like $w, qr/^Ambiguous call resolved as CORE::time\(\), (?x:
135                         )qualify as such or use & at /,
136                 "$nickname has no :method attribute";
137         }
138     }
139
140     &{$_{finally} or next}
141 }