diff --git a/pod/perldiag.pod b/pod/perldiag.pod index e6250bd970ee..9f469aca0231 100644 --- a/pod/perldiag.pod +++ b/pod/perldiag.pod @@ -7845,6 +7845,10 @@ For speed and efficiency reasons, Perl internally does not do full reference-counting of iterated items, hence deleting such an item in the middle of an iteration causes Perl to see a freed value. +=item Use of "goto" to jump into a construct is no longer permitted + +(F) More TO COME. + =item Use of /g modifier is meaningless in split (W regexp) You used the /g modifier on the pattern for a C diff --git a/pp_ctl.c b/pp_ctl.c index 90853e010029..36959a8cd43d 100644 --- a/pp_ctl.c +++ b/pp_ctl.c @@ -3652,9 +3652,7 @@ PP(pp_goto) ? 2 : 1; if (enterops[i]) - deprecate_fatal_in(WARN_DEPRECATED__GOTO_CONSTRUCT, - "5.42", - "Use of \"goto\" to jump into a construct"); + croak("Use of \"goto\" to jump into a construct is no longer permitted"); } /* pop unwanted frames */ diff --git a/t/comp/package_block.t b/t/comp/package_block.t index e3494e57548c..bff8a29f838b 100644 --- a/t/comp/package_block.t +++ b/t/comp/package_block.t @@ -1,6 +1,6 @@ #!./perl -print "1..7\n"; +print "1..5\n"; $main::result = ""; eval q{ @@ -56,37 +56,4 @@ eval q{ }; print $main::result eq "a(2)b(4)c(6)d(8)e(10)f(12)" ? "ok 5\n" : "not ok 5\n"; -$main::result = ""; -$main::warning = ""; -$SIG{__WARN__} = sub { $main::warning .= $_[0]; }; -eval q{ - $main::result .= "a(".__PACKAGE__."/".eval("__PACKAGE__").")"; - goto l0; - $main::result .= "b(".__PACKAGE__."/".eval("__PACKAGE__").")"; - package Foo { - $main::result .= "c(".__PACKAGE__."/".eval("__PACKAGE__").")"; - l0: - $main::result .= "d(".__PACKAGE__."/".eval("__PACKAGE__").")"; - goto l1; - $main::result .= "e(".__PACKAGE__."/".eval("__PACKAGE__").")"; - } - $main::result .= "f(".__PACKAGE__."/".eval("__PACKAGE__").")"; - l1: - $main::result .= "g(".__PACKAGE__."/".eval("__PACKAGE__").")"; - goto l2; - $main::result .= "h(".__PACKAGE__."/".eval("__PACKAGE__").")"; - package Bar { - l2: - $main::result .= "i(".__PACKAGE__."/".eval("__PACKAGE__").")"; - } - $main::result .= "j(".__PACKAGE__."/".eval("__PACKAGE__").")"; -}; -print $main::result eq - "a(main/main)d(Foo/Foo)g(main/main)i(Bar/Bar)j(main/main)" ? - "ok 6\n" : "not ok 6\n"; -print $main::warning =~ /\A - Use\ of\ "goto"\ [^\n]*\ line\ 3\.\n - Use\ of\ "goto"\ [^\n]*\ line\ 15\.\n - \z/x ? "ok 7\n" : "not ok 7\n"; - 1; diff --git a/t/lib/croak/pp_ctl b/t/lib/croak/pp_ctl index 96f40cd458af..8bae4cbc0892 100644 --- a/t/lib/croak/pp_ctl +++ b/t/lib/croak/pp_ctl @@ -1,23 +1,20 @@ __END__ # NAME goto into foreach -no warnings 'deprecated'; goto f; foreach(1){f:} EXPECT -Can't "goto" into the middle of a foreach loop at - line 3. +Use of "goto" to jump into a construct is no longer permitted at - line 1. ######## # NAME goto into given -no warnings 'deprecated'; goto f; CORE::given(1){f:} EXPECT -Can't "goto" into a "given" block at - line 3. +Use of "goto" to jump into a construct is no longer permitted at - line 1. ######## # NAME goto from given topic expression -no warnings 'deprecated'; CORE::given(goto f){f:} EXPECT -Can't "goto" into a "given" block at - line 2. +Use of "goto" to jump into a construct is no longer permitted at - line 1. ######## # NAME goto into expression no warnings 'deprecated'; diff --git a/t/op/goto.t b/t/op/goto.t index 2661cf918924..393718bc27f2 100644 --- a/t/op/goto.t +++ b/t/op/goto.t @@ -6,54 +6,38 @@ BEGIN { chdir 't' if -d 't'; require "./test.pl"; set_up_inc( qw(. ../lib) ); - require './charset_tools.pl'; + require './charset_tools.pl'; } use warnings; use strict; use Config; -plan tests => 96; +plan tests => 50; our $TODO; -my $deprecated = 0; - -local $SIG{__WARN__} = sub { - if ($_[0] =~ m/jump into a construct.*?, and will become fatal in Perl 5\.42/) { - $deprecated++; - } - else { warn $_[0] } -}; - our $foo; while ($?) { $foo = 1; label1: - is($deprecated, 1, "following label1"); - $deprecated = 0; $foo = 2; goto label2; -} continue { +} +continue { $foo = 0; goto label4; label3: - is($deprecated, 1, "following label3"); - $deprecated = 0; $foo = 4; goto label4; } -is($deprecated, 0, "after 'while' loop"); -goto label1; $foo = 3; label2: -is($foo, 2, 'escape while loop'); -is($deprecated, 0, "following label2"); -goto label3; +is($foo, 3, 'escape while loop'); label4: -is($foo, 4, 'second escape while loop'); +is($foo, 3, 'second escape while loop'); my $r = run_perl(prog => 'goto foo;', stderr => 1); like($r, qr/label/, 'cant find label'); @@ -75,11 +59,7 @@ sub bar { } &bar; -fail('goto bypass'); -exit; - -FINALE: -is(curr_test(), 11, 'FINALE'); +pass('goto bypass'); # does goto LABEL handle block contexts correctly? # note that this scope-hopping differs from last & next, @@ -88,20 +68,20 @@ my $count = 0; my $cond = 1; for (1) { if ($cond == 1) { - $cond = 0; - goto OTHER; + $cond = 0; + goto OTHER; } elsif ($cond == 0) { OTHER: - $cond = 2; - is($count, 0, 'OTHER'); - $count++; - goto THIRD; + $cond = 2; + is($count, 0, 'OTHER'); + $count++; + goto THIRD; } else { - THIRD: - is($count, 1, 'THIRD'); - $count++; + THIRD: + is($count, 1, 'THIRD'); + $count++; } } is($count, 2, 'end of loop'); @@ -110,9 +90,9 @@ is($count, 2, 'end of loop'); # (BUG ID 20010309.004 (#5998)) for(my $i=0;!$i++;) { - my $x=1; - goto label; - label: is($x, 1, 'goto inside a for(;;) loop body from inside the body'); + my $x=1; + goto label; + label: is($x, 1, 'goto inside a for(;;) loop body from inside the body'); } # Does goto work correctly going *to* a for(;;) loop? @@ -121,28 +101,29 @@ for(my $i=0;!$i++;) { my ($z, $y) = (0); FORL1: for ($y=1; $z;) { ok($y, 'goto a for(;;) loop, from outside (does initializer)'); - goto TEST19} + goto TEST19 +} ($y,$z) = (0, 1); goto FORL1; # Even from within the loop? TEST19: $z = 0; FORL2: for($y=1; 1;) { - if ($z) { - ok($y, 'goto a for(;;) loop, from inside (does initializer)'); - last; - } - ($y, $z) = (0, 1); - goto FORL2; + if ($z) { + ok($y, 'goto a for(;;) loop, from inside (does initializer)'); + last; + } + ($y, $z) = (0, 1); + goto FORL2; } # Does goto work correctly within a try block? # (BUG ID 20000313.004) - [perl #2359] my $ok = 0; eval { - my $variable = 1; - goto LABEL20; - LABEL20: $ok = 1 if $variable; + my $variable = 1; + goto LABEL20; + LABEL20: $ok = 1 if $variable; }; ok($ok, 'works correctly within a try block'); is($@, "", '...and $@ not set'); @@ -150,9 +131,9 @@ is($@, "", '...and $@ not set'); # And within an eval-string? $ok = 0; eval q{ - my $variable = 1; - goto LABEL21; - LABEL21: $ok = 1 if $variable; + my $variable = 1; + goto LABEL21; + LABEL21: $ok = 1 if $variable; }; ok($ok, 'works correctly within an eval string'); is($@, "", '...and $@ still not set'); @@ -160,16 +141,17 @@ is($@, "", '...and $@ still not set'); # Test that goto works in nested eval-string $ok = 0; -{eval q{ - eval q{ - goto LABEL22; - }; - $ok = 0; - last; - - LABEL22: $ok = 1; -}; -$ok = 0 if $@; +{ + eval q{ + eval q{ + goto LABEL22; + }; + $ok = 0; + last; + + LABEL22: $ok = 1; + }; + $ok = 0 if $@; } ok($ok, 'works correctly in a nested eval string'); @@ -190,21 +172,11 @@ ok($ok, 'works correctly in a nested eval string'); $ok = 0; sub a { - A: { if ($false) { redo A; B: $ok = 1; redo A; } } - goto B unless $count++; + A: { if ($false) { redo A; B: $ok = 1; redo A; } } } - is($deprecated, 0, "before calling sub a()"); a(); - ok($ok, '#19061 loop label wiped away by goto'); - is($deprecated, 1, "after calling sub a()"); - $deprecated = 0; $ok = 0; - my $p; - for ($p=1;$p && goto A;$p=0) { A: $ok = 1 } - ok($ok, 'weird case of goto and for(;;) loop'); - is($deprecated, 1, "following goto and for(;;) loop"); - $deprecated = 0; } # bug #22181 - this used to coredump or make $x undefined, due to @@ -249,110 +221,6 @@ returned_label: is($count, 1, 'called i_return_a_label'); ok($ok, 'skipped to returned_label'); -goto moretests; -fail('goto moretests'); -exit; - -bypass: - -is(curr_test(), 9, 'eval "goto $x"'); - -{ - my $wherever = 'NOWHERE'; - eval { goto $wherever }; - like($@, qr/Can't find label NOWHERE/, 'goto NOWHERE sets $@'); -} - -{ - my $wherever = 'FINALE'; - goto $wherever; -} -fail('goto $wherever'); - -moretests: -# test goto duplicated labels. -{ - my $z = 0; - eval { - $z = 0; - for (0..1) { - L4: # not outer scope - $z += 10; - last; - } - goto L4 if $z == 10; - last; - }; - like($@, qr/Can't "goto" into the middle of a foreach loop/, - 'catch goto middle of foreach'); - - $z = 0; - # ambiguous label resolution (outer scope means endless loop!) - L1: - for my $x (0..1) { - $z += 10; - is($z, 10, 'prefer same scope (loop body) to outer scope (loop entry)'); - goto L1 unless $x; - $z += 10; - L1: - is($z, 10, 'prefer same scope: second'); - last; - } - - $z = 0; - L2: - { - $z += 10; - is($z, 10, 'prefer this scope (block body) to outer scope (block entry)'); - goto L2 if $z == 10; - $z += 10; - L2: - is($z, 10, 'prefer this scope: second'); - } - - - { - $z = 0; - while (1) { - L3: # not inner scope - $z += 10; - last; - } - is($z, 10, 'prefer this scope to inner scope'); - goto L3 if $z == 10; - $z += 10; - L3: # this scope ! - is($z, 10, 'prefer this scope to inner scope: second'); - } - - L4: # not outer scope - { - $z = 0; - while (1) { - L4: # not inner scope - $z += 1; - last; - } - is($z, 1, 'prefer this scope to inner,outer scopes'); - goto L4 if $z == 1; - $z += 10; - L4: # this scope ! - is($z, 1, 'prefer this scope to inner,outer scopes: second'); - } - - { - my $loop = 0; - for my $x (0..1) { - L2: # without this, fails 1 (middle) out of 3 iterations - $z = 0; - L2: - $z += 10; - is($z, 10, - "same label, multiple times in same scope (choose 1st) $loop"); - goto L2 if $z == 10 and not $loop++; - } - } -} # This bug was introduced in Aug 2010 by commit ac56e7de46621c6f # Peephole optimise adjacent pairs of nextstate ops. @@ -379,24 +247,22 @@ sub DEBUG_TIME() { my $out = ""; $out .= 'perl rules'; goto no_list; - no_list: - is($out, 'perl rules', '$out has not been erroneously reset to undef'); + no_list: + is($out, 'perl rules', '$out has not been erroneously reset to undef'); }; } -is($deprecated, 0, 'no warning was emitted'); - { my $r = runperl( - stderr => 1, - prog => + stderr => 1, + prog => 'for ($_=0;$_<3;$_++){A: if($_==1){next} if($_==2){$_++;goto A}}print qq(ok\n)' ); is($r, "ok\n", 'next and goto'); $r = runperl( - stderr => 1, - prog => + stderr => 1, + prog => 'for ($_=0;$_<3;$_++){A: if($_==1){$_++;redo} if($_==2){$_++;goto A}}print qq(ok\n)' ); is($r, "ok\n", 'redo and goto'); @@ -413,8 +279,6 @@ TODO: { } } -is($deprecated, 0, "following TODOed test for #43403"); - #74290 { my $x; @@ -466,9 +330,11 @@ if($foo eq $foo) { } $foo .= "[9]"; bulgaria: + package Tomsk; $foo .= "[1:".__PACKAGE__."]"; $foo .= "[2:".__PACKAGE__."]"; + package main; $foo .= "[3:".__PACKAGE__."]"; is($foo, "[0:main][1:Tomsk][2:Tomsk][3:main]", "label before package decl"); @@ -478,6 +344,7 @@ if($foo eq $foo) { goto adelaide; } $foo .= "[Z]"; + adelaide: package Cairngorm { $foo .= "[B:".__PACKAGE__."]"; @@ -574,93 +441,42 @@ eval { goto "\0" }; like $@, qr/^Can't find label \0 at /, 'goto "\0"'; TODO: { - local $::TODO = 'RT #45091: goto in CORE::GLOBAL::exit unsupported'; - fresh_perl_is(<<'EOC', "before\ndie handler\n", {stderr => 1}, 'RT #45091: goto in CORE::GLOBAL::EXIT'); - BEGIN { + local $::TODO = 'RT #45091: goto in CORE::GLOBAL::exit unsupported'; + fresh_perl_is(<<'EOC', "before\ndie handler\n", {stderr => 1}, 'RT #45091: goto in CORE::GLOBAL::EXIT'); +BEGIN { *CORE::GLOBAL::exit = sub { - goto FASTCGI_NEXT_REQUEST; + goto FASTCGI_NEXT_REQUEST; }; - } - while (1) { +} +while (1) { eval { that_cgi_script() }; FASTCGI_NEXT_REQUEST: last; - } +} - sub that_cgi_script { +sub that_cgi_script { local $SIG{__DIE__} = sub { print "die handler\n"; exit; print "exit failed?\n"; }; print "before\n"; eval { buggy_code() }; print "after\n"; - } - sub buggy_code { +} +sub buggy_code { die "error!"; print "after die\n"; - } +} EOC } sub revnumcmp ($$) { - goto FOO; - die; - FOO: - return $_[1] <=> $_[0]; + goto FOO; + die; + FOO: + return $_[1] <=> $_[0]; } is eval { join(":", sort revnumcmp (9,5,1,3,7)) }, "9:7:5:3:1", "can goto at top level of multicalled sub"; -# A bit strange, but goingto these constructs should not cause any stack -# problems. Let’s test them to make sure that is the case. -no warnings 'deprecated'; -is \sub :lvalue { goto d; ${*{scalar(do { d: \*foo })}} }->(), \$foo, - 'goto into rv2sv, rv2gv and scalar'; -is sub { goto e; $#{; do { e: \@_ } } }->(1..7), 6, - 'goto into $#{...}'; -is sub { goto f; prototype \&{; do { f: sub ($) {} } } }->(), '$', - 'goto into srefgen, prototype and rv2cv'; -is sub { goto g; ref do { g: [] } }->(), 'ARRAY', - 'goto into ref'; -is sub { goto j; defined undef ${; do { j: \(my $foo = "foo") } } }->(),'', - 'goto into defined and undef'; -is sub { goto k; study ++${; do { k: \(my $foo = "foo") } } }->(),'1', - 'goto into study and preincrement'; -is sub { goto l; ~-!${; do { l: \(my $foo = 0) } }++ }->(),~-1, - 'goto into complement, not, negation and postincrement'; -like sub { goto n; sin cos exp log sqrt do { n: 1 } }->(),qr/^0\.51439/, - 'goto into sin, cos, exp, log, and sqrt'; -ok sub { goto o; srand do { o: 0 } }->(), - 'goto into srand'; -cmp_ok sub { goto p; rand do { p: 1 } }->(), '<', 1, - 'goto into rand'; -is sub { goto r; chr ord length int hex oct abs do { r: -15.5 } }->(), 2, - 'goto into chr, ord, length, int, hex, oct and abs'; -is sub { goto t; ucfirst lcfirst uc lc do { t: "q" } }->(), 'Q', - 'goto into ucfirst, lcfirst, uc and lc'; -{ no strict; - is sub { goto u; \@{; quotemeta do { u: "." } } }->(), \@{'\.'}, - 'goto into rv2av and quotemeta'; -} -is join(" ",sub { goto v; %{; do { v: +{1..2} } } }->()), '1 2', - 'goto into rv2hv'; -is join(" ",sub { goto w; $_ || do { w: "w" } }->()), 'w', - 'goto into rhs of or'; -is join(" ",sub { goto x; $_ && do { x: "w" } }->()), 'w', - 'goto into rhs of and'; -is join(" ",sub { goto z; $_ ? do { z: "w" } : 0 }->()), 'w', - 'goto into first leg of ?:'; -is join(" ",sub { goto z; $_ ? 0 : do { z: "w" } }->()), 'w', - 'goto into second leg of ?:'; -is sub { goto z; caller do { z: 0 } }->(), 'main', - 'goto into caller'; -is sub { goto z; exit do { z: return "foo" } }->(), 'foo', - 'goto into exit'; -is sub { goto z; eval do { z: "'foo'" } }->(), 'foo', - 'goto into eval'; -TODO: { - local $TODO = "glob() does not currently return a list on VMS" if $^O eq 'VMS'; - is join(",",sub { goto z; glob do { z: "foo bar" } }->()), 'foo,bar', - 'goto into glob'; -} + # [perl #132799] # Erroneous inward goto warning, followed by crash. # The eval must be in an assignment. @@ -673,15 +489,44 @@ sub _routine { _routine(); pass("bug 132799"); -# [perl #132854] -# Goto the *first* parameter of a binary expression, which is harmless. -eval { - goto __GEN_2; - my $sent = do { - __GEN_2: +{ + # tests of __PACKAGE__ syntax: + # 2 tests moved from t/comp/package_block.t and modified to use inline + # package syntax + + $main::result = ""; + $main::warning = ""; + $SIG{__WARN__} = sub { $main::warning .= $_[0]; }; + eval q{ + $main::result .= "a(".__PACKAGE__."/".eval("__PACKAGE__").")"; + goto l0; + $main::result .= "b(".__PACKAGE__."/".eval("__PACKAGE__").")"; + + package Foo; + $main::result .= "c(".__PACKAGE__."/".eval("__PACKAGE__").")"; + l0: + $main::result .= "d(".__PACKAGE__."/".eval("__PACKAGE__").")"; + goto l1; + $main::result .= "e(".__PACKAGE__."/".eval("__PACKAGE__").")"; + + package main; + $main::result .= "f(".__PACKAGE__."/".eval("__PACKAGE__").")"; + l1: + $main::result .= "g(".__PACKAGE__."/".eval("__PACKAGE__").")"; + goto l2; + $main::result .= "h(".__PACKAGE__."/".eval("__PACKAGE__").")"; + + package Bar; + l2: + $main::result .= "i(".__PACKAGE__."/".eval("__PACKAGE__").")"; + + package main; + $main::result .= "j(".__PACKAGE__."/".eval("__PACKAGE__").")"; }; -}; -is $@,'', 'goto the first parameter of a binary expression [perl #132854]'; + my $expected = 'a(main/main)d(Foo/Foo)g(main/main)i(Bar/Bar)j(main/main)'; + is($main::result, $expected, "Got expected"); + ok(! $main::warning, "Jumping into labels in different packages ran without warnings"); +} # [GH #23806] { diff --git a/t/porting/deprecation.t b/t/porting/deprecation.t index 67f759e5c6c1..30c6aaf479e3 100644 --- a/t/porting/deprecation.t +++ b/t/porting/deprecation.t @@ -90,56 +90,3 @@ if (-e ".git") { "There should not be any new files which mention WARN_DEPRECATED"); } -# Test that deprecation warnings are produced under "use warnings" -# (set above) -{ - my $warning = "nada"; - local $SIG{__WARN__} = sub { $warning = $_[0] }; - my $count = 0; - while ($count<1) { - LABEL: $count++; - goto DONE if $count>1; - } - goto LABEL; - DONE: - like($warning, - qr/Use of "goto" to jump into a construct is deprecated, and will become fatal in Perl 5\.42/, - "Got expected deprecation warning"); -} -# Test that we can silence deprecation warnings with "no warnings 'deprecated'" -# as we used to. -{ - no warnings 'deprecated'; - my $warning = "nada"; - local $SIG{__WARN__} = sub { $warning = $_[0] }; - my $count = 0; - while ($count<1) { - LABEL: $count++; - goto DONE if $count>1; - } - goto LABEL; - DONE: - like($warning, qr/nada/, - "no warnings 'deprecated'; silenced deprecation warning as expected"); -} - -# Test that we can silence a specific deprecation warnings with "no warnings 'deprecated::$subcategory'" -# and that by doing so we don't silence any other deprecation warnings. -{ - no warnings 'deprecated::goto_construct'; - my $warning = "nada"; - local $SIG{__WARN__} = sub { $warning = $_[0] }; - my $count = 0; - while ($count<1) { - LABEL: $count++; - goto DONE if $count>1; - } - goto LABEL; - DONE: - like($warning, qr/nada/, - "no warnings 'deprecated::goto_construct'; silenced deprecation warning as expected"); - @INC = (); - do "regen.pl"; # this should produce a deprecation warning - like($warning, qr/is no longer in \@INC/, - "no warnings 'deprecated::goto_construct'; did not silence deprecated::dot_in_inc warnings"); -} diff --git a/t/uni/labels.t b/t/uni/labels.t index efae494fe252..ca8a72a98166 100644 --- a/t/uni/labels.t +++ b/t/uni/labels.t @@ -16,7 +16,7 @@ use feature qw 'unicode_strings evalbytes'; use charnames qw( :full ); -plan(10); +plan(11); LABEL: { pass("Sanity check, UTF-8 labels don't throw a syntax error."); @@ -47,10 +47,10 @@ SKIP: { eval "last E"; like $@, qr/Label not found for "last E" at/u, "last's error is UTF-8 clean"; - + eval "redo E"; like $@, qr/Label not found for "redo E" at/u, "redo's error is UTF-8 clean"; - + eval "next E"; like $@, qr/Label not found for "next E" at/u, "next's error is UTF-8 clean"; } @@ -75,12 +75,25 @@ like $@, qr/Unrecognized character/, "redo to downgradeable labels"; is $d, 0, "Latin-1 labels are reachable"; { - no warnings; - goto ここ; - - if (undef) { - ここ: { - pass("goto UTF-8 LABEL works."); + local $@; + eval { + goto ここ; + + if (undef) { + ここ: { + my $x = "jump goto UTF-8 LABEL no longer works"; + } } + }; + like($@, + qr/Use of "goto" to jump into a construct is no longer permitted/, + "Got expected error message"); +} + +{ + goto ここ; + + ここ: { + pass("UTF-8 labels are still valid, but you can no longer jump into a construct"); } }