This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
perldelta - move split change to other perlfunc changes and add issue link
[perl5.git] / t / op / smartmatch.t
CommitLineData
0d863452
RH
1#!./perl
2
3BEGIN {
a817e89d 4 chdir 't' if -d 't';
0d863452 5 require './test.pl';
43ece5b1 6 set_up_inc('../lib');
0d863452
RH
7}
8use strict;
289d21b2 9use warnings;
7896dde7
Z
10no warnings 'uninitialized';
11no warnings 'experimental::smartmatch';
12
13++$|;
14
15use Tie::Array;
16use Tie::Hash;
17
18# Predeclare vars used in the tests:
19my @empty;
20my %empty;
21my @sparse; $sparse[2] = 2;
22
23my $deep1 = []; push @$deep1, $deep1;
24my $deep2 = []; push @$deep2, $deep2;
25
26my @nums = (1..10);
27tie my @tied_nums, 'Tie::StdArray';
28@tied_nums = (1..10);
29
30my %hash = (foo => 17, bar => 23);
31tie my %tied_hash, 'Tie::StdHash';
32%tied_hash = %hash;
33
34{
35 package Test::Object::NoOverload;
36 sub new { bless { key => 1 } }
e8fe1b7c 37}
7896dde7
Z
38
39{
40 package Test::Object::StringOverload;
41 use overload '""' => sub { "object" }, fallback => 1;
42 sub new { bless { key => 1 } }
d6851fe9
TC
43}
44
7896dde7
Z
45{
46 package Test::Object::WithOverload;
47 sub new { bless { key => ($_[1] // 'magic') } }
48 use overload '~~' => sub {
49 my %hash = %{ $_[0] };
50 if ($_[2]) { # arguments reversed ?
51 return $_[1] eq reverse $hash{key};
52 }
53 else {
54 return $_[1] eq $hash{key};
55 }
8a0ed425 56 };
7896dde7
Z
57 use overload '""' => sub { "stringified" };
58 use overload 'eq' => sub {"$_[0]" eq "$_[1]"};
8a0ed425 59}
7896dde7
Z
60
61our $ov_obj = Test::Object::WithOverload->new;
62our $ov_obj_2 = Test::Object::WithOverload->new("object");
63our $obj = Test::Object::NoOverload->new;
64our $str_obj = Test::Object::StringOverload->new;
65
66my %refh;
67unless (is_miniperl()) {
68 require Tie::RefHash;
69 tie %refh, 'Tie::RefHash';
70 $refh{$ov_obj} = 1;
71}
72
73my @keyandmore = qw(key and more);
74my @fooormore = qw(foo or more);
75my %keyandmore = map { $_ => 0 } @keyandmore;
76my %fooormore = map { $_ => 0 } @fooormore;
77
78# Load and run the tests
79plan tests => 349+4;
80
81while (<DATA>) {
82 SKIP: {
83 next if /^#/ || !/\S/;
84 chomp;
85 my ($yn, $left, $right, $note) = split /\t+/;
86
87 local $::TODO = $note =~ /TODO/;
88
89 die "Bad test spec: ($yn, $left, $right)" if $yn =~ /[^!@=]/;
90
91 my $tstr = "$left ~~ $right";
92
93 test_again:
94 my $res;
95 if ($note =~ /NOWARNINGS/) {
96 $res = eval "no warnings; $tstr";
97 }
98 else {
99 skip_if_miniperl("Doesn't work with miniperl", $yn =~ /=/ ? 2 : 1)
100 if $note =~ /MINISKIP/;
101 $res = eval $tstr;
102 }
103
104 chomp $@;
105
106 if ( $yn =~ /@/ ) {
107 ok( $@ ne '', "$tstr dies" )
108 and print "# \$\@ was: $@\n";
109 } else {
110 my $test_name = $tstr . ($yn =~ /!/ ? " does not match" : " matches");
111 if ( $@ ne '' ) {
112 fail($test_name);
113 print "# \$\@ was: $@\n";
114 } else {
115 ok( ($yn =~ /!/ xor $res), $test_name );
116 }
117 }
118
119 if ( $yn =~ s/=// ) {
120 $tstr = "$right ~~ $left";
121 goto test_again;
122 }
123 }
124}
125
126sub foo {}
127sub bar {42}
128sub gorch {42}
129sub fatal {die "fatal sub\n"}
130
131# to test constant folding
132sub FALSE() { 0 }
133sub TRUE() { 1 }
134sub NOT_DEF() { undef }
135
136{
137 # [perl #123860]
138 # this can but might not crash
139 # This can but might not crash
140 #
141 # The second smartmatch would leave a &PL_sv_no on the stack for
142 # each key it checked in %!, this could then cause various types of
143 # crash or assertion failure.
144 #
145 # This isn't guaranteed to crash, but if the stack issue is
146 # re-introduced it will probably crash in one of the many smoke
147 # builds.
148 fresh_perl_is('print (q(x) ~~ q(x)) | (/x/ ~~ %!)', "1",
149 { switches => [ "-MErrno", "-M-warnings=experimental::smartmatch" ] },
150 "don't fill the stack with rubbish");
151}
152
153{
154 # [perl #123860] continued;
155 # smartmatch was failing to SPAGAIN after pushing an SV and calling
156 # pp_match, which may have resulted in the stack being realloced
157 # in the meantime. Test this by filling the stack with pregressively
158 # larger amounts of data. At some point the stack will get realloced.
159 my @a = qw(x);
160 my %h = qw(x 1);
161 my @args;
162 my $x = 1;
163 my $bad = -1;
164 for (1..1000) {
165 push @args, $_;
166 my $exp_n = join '-', (@args, $x == 0);
167 my $exp_y = join '-', (@args, $x == 1);
168
169 my $got_an = join '-', (@args, (/X/ ~~ @a));
170 my $got_ay = join '-', (@args, (/x/ ~~ @a));
171 my $got_hn = join '-', (@args, (/X/ ~~ %h));
172 my $got_hy = join '-', (@args, (/x/ ~~ %h));
173
174 if ( $exp_n ne $got_an || $exp_n ne $got_hn
175 || $exp_y ne $got_ay || $exp_y ne $got_hy
176 ) {
177 $bad = $_;
178 last;
179 }
180 }
181 is($bad, -1, "RT 123860: stack realloc");
182}
183
184
185{
186 # [perl #130705]
187 # Perl_ck_smartmatch would turn the match in:
188 # 0 =~ qr/1/ ~~ 0 # parsed as (0 =~ qr/1/) ~~ 0
189 # into a qr, leaving the initial 0 on the stack after execution
190 #
191 # Similarly for: 0 ~~ (0 =~ qr/1/)
192 #
193 # Either caused an assertion failure in the context of warn (or print)
194 # if there was some other operator's arguments left on the stack, as with
195 # the test cases.
196 fresh_perl_is('print(0->[0 =~ qr/1/ ~~ 0])', '',
197 { switches => [ "-M-warnings=experimental::smartmatch" ] },
198 "don't qr-ify left-side match against a stacked argument");
199 fresh_perl_is('print(0->[0 ~~ (0 =~ qr/1/)])', '',
200 { switches => [ "-M-warnings=experimental::smartmatch" ] },
201 "don't qr-ify right-side match against a stacked argument");
8a0ed425 202}
8a0ed425 203
7896dde7
Z
204# Prefix character :
205# - expected to match
206# ! - expected to not match
207# @ - expected to be a compilation failure
208# = - expected to match symmetrically (runs test twice)
209# Data types to test :
210# undef
211# Object-overloaded
212# Object
213# Coderef
214# Hash
215# Hashref
216# Array
217# Arrayref
218# Tied arrays and hashes
219# Arrays that reference themselves
220# Regex (// and qr//)
221# Range
222# Num
223# Str
224# Other syntactic items of interest:
225# Constants
226# Values returned by a sub call
227__DATA__
228# Any ~~ undef
229! $ov_obj undef
230! $obj undef
231! sub {} undef
232! %hash undef
233! \%hash undef
234! {} undef
235! @nums undef
236! \@nums undef
237! [] undef
238! %tied_hash undef
239! @tied_nums undef
240! $deep1 undef
241! /foo/ undef
242! qr/foo/ undef
243! 21..30 undef
244! 189 undef
245! "foo" undef
246! "" undef
247! !1 undef
248 undef undef
249 (my $u) undef
250 NOT_DEF undef
251 &NOT_DEF undef
252
253# Any ~~ object overloaded
254! \&fatal $ov_obj
255 'cigam' $ov_obj
256! 'cigam on' $ov_obj
257! ['cigam'] $ov_obj
258! ['stringified'] $ov_obj
259! { cigam => 1 } $ov_obj
260! { stringified => 1 } $ov_obj
261! $obj $ov_obj
262! undef $ov_obj
263
264# regular object
265@ $obj $obj
266@ $ov_obj $obj
267=@ \&fatal $obj
268@ \&FALSE $obj
269@ \&foo $obj
270@ sub { 1 } $obj
271@ sub { 0 } $obj
272@ %keyandmore $obj
273@ {"key" => 1} $obj
274@ @fooormore $obj
275@ ["key" => 1] $obj
276@ /key/ $obj
277@ qr/key/ $obj
278@ "key" $obj
279@ FALSE $obj
280
281# regular object with "" overload
282@ $obj $str_obj
283=@ \&fatal $str_obj
284@ \&FALSE $str_obj
285@ \&foo $str_obj
286@ sub { 1 } $str_obj
287@ sub { 0 } $str_obj
288@ %keyandmore $str_obj
289@ {"object" => 1} $str_obj
290@ @fooormore $str_obj
291@ ["object" => 1] $str_obj
292@ /object/ $str_obj
293@ qr/object/ $str_obj
294@ "object" $str_obj
295@ FALSE $str_obj
296# Those will treat the $str_obj as a string because of fallback:
297
298# object (overloaded or not) ~~ Any
299 $obj qr/NoOverload/
300 $ov_obj qr/^stringified$/
301= "$ov_obj" "stringified"
302= "$str_obj" "object"
303!= $ov_obj "stringified"
304 $str_obj "object"
305 $ov_obj 'magic'
306! $ov_obj 'not magic'
307
308# ~~ Coderef
309 sub{0} sub { ref $_[0] eq "CODE" }
310 %fooormore sub { $_[0] =~ /^(foo|or|more)$/ }
311! %fooormore sub { $_[0] =~ /^(foo|or|less)$/ }
312 \%fooormore sub { $_[0] =~ /^(foo|or|more)$/ }
313! \%fooormore sub { $_[0] =~ /^(foo|or|less)$/ }
314 +{%fooormore} sub { $_[0] =~ /^(foo|or|more)$/ }
315! +{%fooormore} sub { $_[0] =~ /^(foo|or|less)$/ }
316 @fooormore sub { $_[0] =~ /^(foo|or|more)$/ }
317! @fooormore sub { $_[0] =~ /^(foo|or|less)$/ }
318 \@fooormore sub { $_[0] =~ /^(foo|or|more)$/ }
319! \@fooormore sub { $_[0] =~ /^(foo|or|less)$/ }
320 [@fooormore] sub { $_[0] =~ /^(foo|or|more)$/ }
321! [@fooormore] sub { $_[0] =~ /^(foo|or|less)$/ }
322 %fooormore sub{@_==1}
323 @fooormore sub{@_==1}
324 "foo" sub { $_[0] =~ /^(foo|or|more)$/ }
325! "more" sub { $_[0] =~ /^(foo|or|less)$/ }
326 /fooormore/ sub{ref $_[0] eq 'Regexp'}
327 qr/fooormore/ sub{ref $_[0] eq 'Regexp'}
328 1 sub{shift}
329! 0 sub{shift}
330! undef sub{shift}
331 undef sub{not shift}
332 NOT_DEF sub{not shift}
333 &NOT_DEF sub{not shift}
334 FALSE sub{not shift}
335 [1] \&bar
336 {a=>1} \&bar
337 qr// \&bar
338! [1] \&foo
339! {a=>1} \&foo
340 $obj sub { ref($_[0]) =~ /NoOverload/ }
341 $ov_obj sub { ref($_[0]) =~ /WithOverload/ }
342# empty stuff matches, because the sub is never called:
343 [] \&foo
344 {} \&foo
345 @empty \&foo
346 %empty \&foo
347! qr// \&foo
348! undef \&foo
349 undef \&bar
350@ undef \&fatal
351@ 1 \&fatal
352@ [1] \&fatal
353@ {a=>1} \&fatal
354@ "foo" \&fatal
355@ qr// \&fatal
356# sub is not called on empty hashes / arrays
357 [] \&fatal
358 +{} \&fatal
359 @empty \&fatal
360 %empty \&fatal
361# sub is not special on the left
362 sub {0} qr/^CODE/
363 sub {0} sub { ref shift eq "CODE" }
364
365# HASH ref against:
366# - another hash ref
367 {} {}
368=! {} {1 => 2}
369 {1 => 2} {1 => 2}
370 {1 => 2} {1 => 3}
371=! {1 => 2} {2 => 3}
372= \%main:: {map {$_ => 'x'} keys %main::}
373
374# - tied hash ref
375= \%hash \%tied_hash
376 \%tied_hash \%tied_hash
377!= {"a"=>"b"} \%tied_hash
378= %hash %tied_hash
379 %tied_hash %tied_hash
380!= {"a"=>"b"} %tied_hash
381 $ov_obj %refh MINISKIP
382! "$ov_obj" %refh MINISKIP
383 [$ov_obj] %refh MINISKIP
384! ["$ov_obj"] %refh MINISKIP
385 %refh %refh MINISKIP
386
387# - an array ref
388# (since this is symmetrical, tests as well hash~~array)
389= [keys %main::] \%::
390= [qw[STDIN STDOUT]] \%::
391=! [] \%::
392=! [""] {}
393=! [] {}
394=! @empty {}
395= [undef] {"" => 1}
396= [""] {"" => 1}
397= ["foo"] { foo => 1 }
398= ["foo", "bar"] { foo => 1 }
399= ["foo", "bar"] \%hash
400= ["foo"] \%hash
401=! ["quux"] \%hash
402= [qw(foo quux)] \%hash
403= @fooormore { foo => 1, or => 2, more => 3 }
404= @fooormore %fooormore
405= @fooormore \%fooormore
406= \@fooormore %fooormore
407
408# - a regex
409= qr/^(fo[ox])$/ {foo => 1}
410= /^(fo[ox])$/ %fooormore
411=! qr/[13579]$/ +{0..99}
412=! qr/a*/ {}
413= qr/a*/ {b=>2}
414= qr/B/i {b=>2}
415= /B/i {b=>2}
416=! qr/a+/ {b=>2}
417= qr/^à/ {"à"=>2}
418
419# - a scalar
420 "foo" +{foo => 1, bar => 2}
421 "foo" %fooormore
422! "baz" +{foo => 1, bar => 2}
423! "boz" %fooormore
424! 1 +{foo => 1, bar => 2}
425! 1 %fooormore
426 1 { 1 => 3 }
427 1.0 { 1 => 3 }
428! "1.0" { 1 => 3 }
429! "1.0" { 1.0 => 3 }
430 "1.0" { "1.0" => 3 }
431 "à" { "à" => "À" }
432
433# - undef
434! undef { hop => 'zouu' }
435! undef %hash
436! undef +{"" => "empty key"}
437! undef {}
438
439# ARRAY ref against:
440# - another array ref
441 [] []
442=! [] [1]
443 [["foo"], ["bar"]] [qr/o/, qr/a/]
444! [["foo"], ["bar"]] [qr/ARRAY/, qr/ARRAY/]
445 ["foo", "bar"] [qr/o/, qr/a/]
446! [qr/o/, qr/a/] ["foo", "bar"]
447 ["foo", "bar"] [["foo"], ["bar"]]
448! ["foo", "bar"] [qr/o/, "foo"]
449 ["foo", undef, "bar"] [qr/o/, undef, "bar"]
450! ["foo", undef, "bar"] [qr/o/, "", "bar"]
451! ["foo", "", "bar"] [qr/o/, undef, "bar"]
452 $deep1 $deep1
453 @$deep1 @$deep1
454! $deep1 $deep2
455
456= \@nums \@tied_nums
457= @nums \@tied_nums
458= \@nums @tied_nums
459= @nums @tied_nums
460
461# - an object
462! $obj @fooormore
463 $obj [sub{ref shift}]
464
465# - a regex
466= qr/x/ [qw(foo bar baz quux)]
467=! qr/y/ [qw(foo bar baz quux)]
468= /x/ [qw(foo bar baz quux)]
469=! /y/ [qw(foo bar baz quux)]
470= /FOO/i @fooormore
471=! /bar/ @fooormore
472
473# - a number
474 2 [qw(1.00 2.00)]
475 2 [qw(foo 2)]
476 2.0_0e+0 [qw(foo 2)]
477! 2 [qw(1foo bar2)]
478
479# - a string
480! "2" [qw(1foo 2bar)]
481 "2bar" [qw(1foo 2bar)]
482
483# - undef
484 undef [1, 2, undef, 4]
485! undef [1, 2, [undef], 4]
486! undef @fooormore
487 undef @sparse
488 undef [undef]
489! 0 [undef]
490! "" [undef]
491! undef [0]
492! undef [""]
493
494# - nested arrays and ~~ distributivity
495 11 [[11]]
496! 11 [[12]]
497 "foo" [{foo => "bar"}]
498! "bar" [{foo => "bar"}]
499
500# Number against number
501 2 2
502 20 2_0
503! 2 3
504 0 FALSE
505 3-2 TRUE
506! undef 0
507! (my $u) 0
508
509# Number against string
510= 2 "2"
511= 2 "2.0"
512! 2 "2bananas"
513!= 2_3 "2_3" NOWARNINGS
514 FALSE "0"
515! undef "0"
516! undef ""
517
518# Regex against string
519 "x" qr/x/
520! "x" qr/y/
521
522# Regex against number
523 12345 qr/3/
524! 12345 qr/7/
525
526# array/hash against string
527 @fooormore "".\@fooormore
528! @keyandmore "".\@fooormore
529 %fooormore "".\%fooormore
530! %keyandmore "".\%fooormore
531
532# Test the implicit referencing
533 7 @nums
534 @nums \@nums
535! @nums \\@nums
536 @nums [1..10]
537! @nums [0..9]
538
539 "foo" %hash
540 /bar/ %hash
541 [qw(bar)] %hash
542! [qw(a b c)] %hash
543 %hash %hash
544 %hash +{%hash}
545 %hash \%hash
546 %hash %tied_hash
547 %tied_hash %tied_hash
548 %hash { foo => 5, bar => 10 }
549! %hash { foo => 5, bar => 10, quux => 15 }
550
551 @nums { 1, '', 2, '' }
552 @nums { 1, '', 12, '' }
553! @nums { 11, '', 12, '' }
554
555# array slices
556 @nums[0..-1] []
557 @nums[0..0] [1]
558! @nums[0..1] [0..2]
559 @nums[0..4] [1..5]
560
561! undef @nums[0..-1]
562 1 @nums[0..0]
563 2 @nums[0..1]
564! @nums[0..1] 2
8a0ed425 565
7896dde7 566 @nums[0..1] @nums[0..1]
25233fb4 567
7896dde7
Z
568# hash slices
569 @keyandmore{qw(not)} [undef]
570 @keyandmore{qw(key)} [0]
25233fb4 571
7896dde7
Z
572 undef @keyandmore{qw(not)}
573 0 @keyandmore{qw(key and more)}
574! 2 @keyandmore{qw(key and)}
fb24bb2d 575
7896dde7
Z
576 @fooormore{qw(foo)} @keyandmore{qw(key)}
577 @fooormore{qw(foo or more)} @keyandmore{qw(key and more)}
fb24bb2d 578
7896dde7
Z
579# UNDEF
580! 3 undef
581! 1 undef
582! [] undef
583! {} undef
584! \%::main undef
585! [1,2] undef
586! %hash undef
587! @nums undef
588! "foo" undef
589! "" undef
590! !1 undef
591! \&foo undef
592! sub { } undef