This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Don’t inline sub(){ 0; return $x; ... }
[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 51;
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 with simple lexical unmodified elsewhere',
59   generator   => sub { my $x = 5; sub(){$x} },
60   retval      => 5,
61   same_retval => 0,
62   inlinable   => 1,
63   deprecated  => 0,
64   method      => 0,
65 };
66
67 push @tests, {
68   nickname    => 'return $variable modified elsewhere',
69   generator   => sub { my $x=5; my $ret = sub(){return $x}; $x = 7; $ret },
70   retval      => 7,
71   same_retval => 0,
72   inlinable   => 0,
73   deprecated  => 0,
74   method      => 0,
75 };
76
77 push @tests, {
78   nickname    => 'return $variable unmodified elsewhere',
79   generator   => sub { my $x = 5; sub(){return $x} },
80   retval      => 5,
81   same_retval => 0,
82   inlinable   => 0,
83   deprecated  => 0,
84   method      => 0,
85 };
86
87 push @tests, {
88   nickname    => 'sub () { 0; $x } with $x modified elsewhere',
89   generator   => sub { my $x = 5; my $ret = sub(){0;$x}; $x = 8; $ret },
90   retval      => 8,
91   same_retval => 0,
92   inlinable   => 0,
93   deprecated  => 0,
94   method      => 0,
95 };
96
97 push @tests, {
98   nickname    => 'sub () { 0; $x } with $x unmodified elsewhere',
99   generator   => sub { my $x = 5; my $y = $x; sub(){0;$x} },
100   retval      => 5,
101   same_retval => 0,
102   inlinable   => 1,
103   deprecated  => 0,
104   method      => 0,
105 };
106
107 # Explicit return after optimised statement, not at end of sub
108 push @tests, {
109   nickname    => 'sub () { 0; return $x; ... }',
110   generator   => sub { my $x = 5; sub () { 0; return $x; ... } },
111   retval      => 5,
112   same_retval => 0,
113   inlinable   => 0,
114   deprecated  => 0,
115   method      => 0,
116 };
117
118 use feature 'state', 'lexical_subs';
119 no warnings 'experimental::lexical_subs';
120
121 push @tests, {
122   nickname    => 'sub () { my $x; state sub z { $x } $outer }',
123   generator   => sub {
124     my $outer = 43;
125     sub () { my $x; state sub z { $x } $outer }
126   },
127   retval      => 43,
128   same_retval => 0,
129   inlinable   => 0,
130   deprecated  => 0,
131   method      => 0,
132 };
133
134 push @tests, {
135   nickname    => 'sub:method with simple lexical',
136   generator   => sub { my $y; sub():method{$y} },
137   retval      => undef,
138   same_retval => 0,
139   inlinable   => 1,
140   deprecated  => 0,
141   method      => 1,
142 };
143
144
145 use feature 'refaliasing';
146 no warnings 'experimental::refaliasing';
147 for \%_ (@tests) {
148     my $nickname = $_{nickname};
149     my $w;
150     local $SIG{__WARN__} = sub { $w = shift };
151     my $sub = &{$_{generator}};
152     if (exists $_{deprecated}) {
153         if ($_{deprecated}) {
154             like $w, qr/^Constants from lexical variables potentially (?x:
155                        )modified elsewhere are deprecated at /,
156                 "$nickname is deprecated";
157         }
158         else {
159             is $w, undef, "$nickname is not deprecated";
160         }
161     }
162     if (exists $_{retval}) {
163         is &$sub, $_{retval}, "retval of $nickname";
164     }
165     if (exists $_{same_retval}) {
166         my $same = $_{same_retval} ? "same" : "different";
167         &{$_{same_retval} ? \&is : \&isnt}(
168             \scalar &$sub(), \scalar &$sub(),
169             "$nickname gives $same retval each call"
170         );
171     }
172     if (exists $_{inlinable}) {
173         local *temp_inlinability_test = $sub;
174         $w = undef;
175         use warnings 'redefine';
176         *temp_inlinability_test = sub (){};
177         my $S = $_{inlinable} ? "Constant s" : "S";
178         my $not = " not" x! $_{inlinable};
179         like $w, qr/^${S}ubroutine .* redefined at /,
180                 "$nickname is$not inlinable";
181     }
182     if (exists $_{method}) {
183         local *time = $sub;
184         $w = undef;
185         use warnings 'ambiguous';
186         eval "()=time";
187         if ($_{method}) {
188             is $w, undef, "$nickname has :method attribute";
189         }
190         else {
191             like $w, qr/^Ambiguous call resolved as CORE::time\(\), (?x:
192                         )qualify as such or use & at /,
193                 "$nickname has no :method attribute";
194         }
195     }
196
197     &{$_{finally} or next}
198 }