This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
sprintf(): handle mangled formats better with utf8
[perl5.git] / t / op / const-optree.t
CommitLineData
0b2df18f
FC
1#!perl
2
3# Test the various op trees that turn sub () { ... } into a constant, and
4# some variants that don’t.
5
6BEGIN {
7 chdir 't';
8 require './test.pl';
624c42e2 9 set_up_inc('../lib');
0b2df18f 10}
6dfba0aa 11plan 168;
0b2df18f 12
bbd7756c
FC
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
a41f70ad 17# finally - sub to run after the tests
bbd7756c
FC
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
0b2df18f 28
bbd7756c
FC
29# [perl #63540] Don’t treat sub { if(){.....}; "constant" } as a constant
30sub blonk { ++$blonk_was_called }
31push @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};
0b2df18f
FC
45
46# [perl #79908]
bbd7756c
FC
47push @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
57push @tests, {
cb12c7ef
FC
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
67push @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
77push @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
87push @tests, {
7f08c641
FC
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
97push @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
3c37c25a
FC
107# Explicit return after optimised statement, not at end of sub
108push @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
9d391755
FC
118# Explicit return after optimised statement, at end of sub [perl #123092]
119push @tests, {
120 nickname => 'sub () { 0; return $x }',
121 generator => sub { my $x = 5; sub () { 0; return $x } },
122 retval => 5,
123 same_retval => 0,
124 inlinable => 0,
125 deprecated => 0,
126 method => 0,
127};
128
f1603422
FC
129# Multiple closure tests
130push @tests, {
131 nickname => 'simple lexical after another closure and no lvalue',
132 generator => sub {
133 my $x = 5;
134 # This closure prevents inlining, though theoretically it shouldn’t
135 # have to. If you change the behaviour, just change the test. This
136 # fails the refcount check in op.c:op_const_sv, which is necessary for
137 # the sake of \(my $x = 1) (tested below).
138 my $sub1 = sub () { () = $x };
139 sub () { $x };
140 },
141 retval => 5,
142 same_retval => 0,
143 inlinable => 0,
144 deprecated => 0,
145 method => 0,
146};
147push @tests, {
148 nickname => 'simple lexical before another closure and no lvalue',
149 generator => sub {
150 my $x = 5;
151 my $ret = sub () { $x };
152 # This does not prevent inlining and never has.
153 my $sub1 = sub () { () = $x };
154 $ret;
155 },
156 retval => 5,
157 same_retval => 0,
158 inlinable => 1,
159 deprecated => 0,
160 method => 0,
161};
162push @tests, {
163 nickname => 'simple lexical after an lvalue closure',
164 generator => sub {
165 my $x = 5;
166 # This has always prevented inlining
167 my $sub1 = sub () { $x++ };
168 sub () { $x };
169 },
170 retval => 5,
171 same_retval => 0,
172 inlinable => 0,
173 deprecated => 0,
174 method => 0,
175};
176push @tests, {
177 nickname => 'simple lexical before an lvalue closure',
178 generator => sub {
179 my $x = 5;
180 my $ret = sub () { $x }; # <-- simple lexical op tree
181 # Traditionally this has not prevented inlining, though it should. But
182 # since $ret has a simple lexical op tree, we preserve backward-compat-
183 # ibility, but deprecate it.
184 my $sub1 = sub () { $x++ };
185 $ret;
186 },
187 retval => 5,
188 same_retval => 0,
189 inlinable => 1,
190 deprecated => 1,
191 method => 0,
192};
193push @tests, {
194 nickname => 'complex lexical op tree before an lvalue closure',
195 generator => sub {
196 my $x = 5;
197 my $ret = sub () { 0; $x }; # <-- more than just a lexical
198 # This used not to prevent inlining, though it should, and now does.
199 my $sub1 = sub () { $x++ };
200 $ret;
201 },
202 retval => 5,
203 same_retval => 0,
204 inlinable => 0,
205 deprecated => 0,
206 method => 0,
207};
208push @tests, {
209 nickname => 'complex lexical op tree before a nested lvalue closure',
210 generator => sub {
211 my $x = 5;
212 my $ret = sub () { 0; $x }; # <-- more than just a lexical
213 # This used not to prevent inlining, though it should, and now does.
214 my $sub1 = sub () { sub () { $x++ } }; # nested
215 $ret;
216 },
217 retval => 5,
218 same_retval => 0,
219 inlinable => 0,
220 deprecated => 0,
221 method => 0,
222};
223
4a991780
FC
224use feature 'state', 'lexical_subs';
225no warnings 'experimental::lexical_subs';
226
73c13e16
FC
227# Constant constants
228push @tests, {
229 nickname => 'sub with constant',
230 generator => sub { sub () { 8 } },
231 retval => 8,
232 same_retval => 0,
233 inlinable => 1,
234 deprecated => 0,
235 method => 0,
236};
237push @tests, {
238 nickname => 'sub with constant and return',
239 generator => sub { sub () { return 8 } },
240 retval => 8,
241 same_retval => 0,
242 inlinable => 0,
243 deprecated => 0,
244 method => 0,
245};
246push @tests, {
247 nickname => 'sub with optimised statement and constant',
248 generator => sub { sub () { 0; 8 } },
249 retval => 8,
250 same_retval => 0,
251 inlinable => 1,
252 deprecated => 0,
253 method => 0,
254};
255push @tests, {
256 nickname => 'sub with optimised statement, constant and return',
257 generator => sub { sub () { 0; return 8 } },
258 retval => 8,
259 same_retval => 0,
260 inlinable => 0,
261 deprecated => 0,
262 method => 0,
263};
264push @tests, {
265 nickname => 'my sub with constant',
266 generator => sub { my sub x () { 8 } \&x },
267 retval => 8,
268 same_retval => 0,
269 inlinable => 1,
270 deprecated => 0,
271 method => 0,
272};
273push @tests, {
274 nickname => 'my sub with constant and return',
275 generator => sub { my sub x () { return 8 } \&x },
276 retval => 8,
277 same_retval => 0,
278 inlinable => 0,
279 deprecated => 0,
280 method => 0,
281};
282push @tests, {
283 nickname => 'my sub with optimised statement and constant',
284 generator => sub { my sub x () { 0; 8 } \&x },
285 retval => 8,
286 same_retval => 0,
287 inlinable => 1,
288 deprecated => 0,
289 method => 0,
290};
291push @tests, {
292 nickname => 'my sub with optimised statement, constant and return',
293 generator => sub { my sub x () { 0; return 8 } \&x },
294 retval => 8,
295 same_retval => 0,
296 inlinable => 0,
297 deprecated => 0,
298 method => 0,
299};
300
03414f05
FC
301# String eval
302push @tests, {
303 nickname => 'sub () { $x } with eval in scope',
304 generator => sub {
305 my $outer = 43;
306 my $ret = sub () { $outer };
307 eval '$outer++';
308 $ret;
309 },
310 retval => 43,
311 same_retval => 0,
312 inlinable => 1,
313 deprecated => 1,
314 method => 0,
315};
316push @tests, {
317 nickname => 'sub () { $x } with s///ee in scope',
318 generator => sub {
319 my $outer = 43;
320 my $dummy = '$outer++';
321 my $ret = sub () { $outer };
322 $dummy =~ s//$dummy/ee;
323 $ret;
324 },
325 retval => 43,
326 same_retval => 0,
327 inlinable => 1,
328 deprecated => 1,
329 method => 0,
330};
331push @tests, {
332 nickname => 'sub () { $x } with eval not in scope',
333 generator => sub {
334 my $ret;
335 {
336 my $outer = 43;
337 $ret = sub () { $outer };
338 }
339 eval '';
340 $ret;
341 },
342 retval => 43,
343 same_retval => 0,
344 inlinable => 1,
345 deprecated => 0,
346 method => 0,
347};
348
4a991780
FC
349push @tests, {
350 nickname => 'sub () { my $x; state sub z { $x } $outer }',
351 generator => sub {
352 my $outer = 43;
353 sub () { my $x; state sub z { $x } $outer }
354 },
355 retval => 43,
356 same_retval => 0,
f1603422
FC
357 inlinable => 0,
358 deprecated => 0,
359 method => 0,
360};
361
362push @tests, {
363 nickname => 'closure after \(my $x=1)',
364 generator => sub {
365 $y = \(my $x = 1);
366 my $ret = sub () { $x };
367 $$y += 7;
368 $ret;
369 },
370 retval => 8,
371 same_retval => 0,
4a991780
FC
372 inlinable => 0,
373 deprecated => 0,
374 method => 0,
375};
376
7f08c641 377push @tests, {
bbd7756c
FC
378 nickname => 'sub:method with simple lexical',
379 generator => sub { my $y; sub():method{$y} },
380 retval => undef,
381 same_retval => 0,
382 inlinable => 1,
383 deprecated => 0,
384 method => 1,
385};
7a3e5b7e
FC
386push @tests, {
387 nickname => 'sub:method with constant',
388 generator => sub { sub():method{3} },
389 retval => 3,
390 same_retval => 0,
391 inlinable => 1,
392 deprecated => 0,
393 method => 1,
394};
395push @tests, {
396 nickname => 'my sub:method with constant',
397 generator => sub { my sub x ():method{3} \&x },
398 retval => 3,
399 same_retval => 0,
400 inlinable => 1,
401 deprecated => 0,
402 method => 1,
403};
bbd7756c 404
e4211fee
FC
405push @tests, {
406 nickname => 'sub closing over state var',
407 generator => sub { state $x = 3; sub () {$x} },
408 retval => 3,
409 same_retval => 0,
410 inlinable => 1,
411 deprecated => 0,
412 method => 0,
413};
414push @tests, {
415 nickname => 'sub closing over state var++',
416 generator => sub { state $x++; sub () { $x } },
417 retval => 1,
418 same_retval => 0,
419 inlinable => 1,
420 deprecated => 1,
421 method => 0,
422};
423
bbd7756c
FC
424
425use feature 'refaliasing';
426no warnings 'experimental::refaliasing';
427for \%_ (@tests) {
428 my $nickname = $_{nickname};
0ac016fc 429 my $w;
bbd7756c
FC
430 local $SIG{__WARN__} = sub { $w = shift };
431 my $sub = &{$_{generator}};
432 if (exists $_{deprecated}) {
433 if ($_{deprecated}) {
434 like $w, qr/^Constants from lexical variables potentially (?x:
9840d1d6
A
435 )modified elsewhere are deprecated\. This will (?x:
436 )not be allowed in Perl 5\.32 at /,
bbd7756c
FC
437 "$nickname is deprecated";
438 }
439 else {
440 is $w, undef, "$nickname is not deprecated";
441 }
442 }
443 if (exists $_{retval}) {
444 is &$sub, $_{retval}, "retval of $nickname";
445 }
446 if (exists $_{same_retval}) {
447 my $same = $_{same_retval} ? "same" : "different";
448 &{$_{same_retval} ? \&is : \&isnt}(
449 \scalar &$sub(), \scalar &$sub(),
450 "$nickname gives $same retval each call"
451 );
452 }
453 if (exists $_{inlinable}) {
454 local *temp_inlinability_test = $sub;
455 $w = undef;
456 use warnings 'redefine';
457 *temp_inlinability_test = sub (){};
458 my $S = $_{inlinable} ? "Constant s" : "S";
459 my $not = " not" x! $_{inlinable};
460 like $w, qr/^${S}ubroutine .* redefined at /,
461 "$nickname is$not inlinable";
462 }
463 if (exists $_{method}) {
464 local *time = $sub;
465 $w = undef;
466 use warnings 'ambiguous';
467 eval "()=time";
468 if ($_{method}) {
469 is $w, undef, "$nickname has :method attribute";
470 }
471 else {
472 like $w, qr/^Ambiguous call resolved as CORE::time\(\), (?x:
473 )qualify as such or use & at /,
474 "$nickname has no :method attribute";
475 }
0b2df18f 476 }
0b2df18f 477
bbd7756c
FC
478 &{$_{finally} or next}
479}
82e85a9c
FC
480
481# This used to fail an assertion in leave_scope. For some reason, it did
482# not fail within the framework above.
483sub { my $x = "x"; my $sub = sub () { $x }; undef $sub; } ->();
484pass("No assertion failure when turning on PADSTALE on lexical shared by"
485 ." erstwhile constant");
486
6dfba0aa
FC
487{
488 my $sub = sub {
489 my $x = "x"x2000; sub () {$x};
490 }->();
491 $y = &$sub;
492 $z = &$sub;
493 is $z, $y, 'inlinable sub ret vals are not swipable';
494}