http://www.perlmonks.org?node_id=1013574

moritz has asked for the wisdom of the Perl Monks concerning the following question:

At $dayjob I'm working on a project where the home-grown error reporting module occasionally warns Use of "goto" to jump into a construct is deprecated

So now I'm trying to find out how exactly goto LABEL works, in order to assess whether the whole error reporting module needs to be rewritten. And I found no documentation.

The closest I found was "goto only searches the immediate call stack for labels", but this alone doesn't explain how "jump into a construct" can happen at all -- if it only searched the caller's scope and the caller's caller's scope etc. for the label, it would never find label from inside a construct that's not in dyanmic call chain.

Some curious experiments:

$ perl -wE 'if (0) { label: say 1 }; goto label' Can't find label label at -e line 1. $ perl -wE 'if (1) { label: say 1;}; goto label' Use of "goto" to jump into a construct is deprecated at -e line 1. 1 Use of "goto" to jump into a construct is deprecated at -e line 1. 1 ^C # aborted, because it loops

So apparently, the construct containing the label has to have been executed once.

This works:

#!/usr/bin/perl use strict; use warnings; use 5.014; sub f { my $arg = shift; if ($arg == 0) { label: say 'here'; } elsif ($arg == 1) { # don't execute a branch with 'label:' } else { goto label } } f($_) for 0..2; __END__ here here

Whereas this one dies:

$ perl -wE 'sub g() { label: say "here" }; g; goto label' here Can't find label label at -e line 1.

I have trouble finding a clear mental model that can help me to explain all of this behavior. Who can help?

Replies are listed 'Best First'.
Re: How does 'goto LABEL' search for its label?
by Athanasius (Archbishop) on Jan 16, 2013 at 13:59 UTC

    From the Camel Book, 4th Edition, page 149:

    The goto LABEL form finds the statement labeled with LABEL and resumes execution there. It can’t be used to jump into any construct that requires initialization, such as a subroutine or a foreach loop. It also can’t be used to jump into a construct that has been optimized away.... It can be used to go almost anywhere else within the current block or any block in your dynamic scope (that is, a block you were called from). You can even goto out of subroutines, but it’s usually bettter to use some other construct.

    Update: See also the similar discussion on page 881, and note (for whatever it’s worth) the following rather cryptic comment in the section “Time Efficiency” on page 692:

    • Avoid goto. It scans outward from your current location for the indicated label.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: How does 'goto LABEL' search for its label?
by toolic (Bishop) on Jan 16, 2013 at 14:03 UTC
    Who can help?
    I certainly can't, but maybe B::Deparse sheds some light.
Re: How does 'goto LABEL' search for its label?
by LanX (Saint) on Jan 16, 2013 at 16:26 UTC
    Do you have a clear definition for "construct"?

    from perlglossary

    construct As a noun, a piece of syntax made up of smaller pieces.

    I think in this context it's supposed to mean "block of a loop".

    IMHO this discussion helps: cross scope gotos?

    From what I remember labels are only visible from the actual "frame" and parent frames.

    Frames are constructs which need an initialization like subs or for-each-loops (remember the aliased loop-var!).

    That means you can't jump into these constructs and bypass initialization.

    But if you call another sub from within such a frame, that means after run-time initialization, you can goto back.

    So lables are a run-time thing. They belong to the dynamic scope like localized vars.

    EDIT: I think the deprecation is supposed to disallow jumps into other kinds of loops like while¹ or c-style fors , which have a "logical" but no "physical" initialization like frames.

    HTH!

    Cheers Rolf

    ¹) remember that naked blocks are internally just while-loops

      I think in this context it's supposed to mean "block of a loop".

      goto says that literally

      IMHO this discussion helps...

      The whole time I was writing Re: How does 'goto LABEL' search for its label? I was thinking "dynamic scoping" but didn't have an example, and here it is

      $ perl -wE " sub F { G(); LF: say 6; } sub G { say 2; goto LF; } G; " 2 Can't find label LF at -e line 1. $ perl -wE " sub F { return G(); LF: say 6; } sub G { say 2; goto LF; +} F" 2 6

      yow :)

        I sympathized with the "yow" remark because it looked like the difference between finding the label and not finding the label was whether "return" was used. But that was a red herring.

        The real difference is whether the one-liner ends with "G" or with "F". The inclusion of the 'return' keyword makes no difference (but lacking the 'return' when calling "F" makes the reason for the output less obvious).

        Once I noticed the single-character difference off at the end of the long lines, then I lost sympathy for a "yow" response because the behavior is exactly as I would have expected from the documentation. And, it seems a reasonable restriction.

        Now, I think using 'goto' to leave a subroutine is a rather squirrely technique. I would much prefer that it produced a warning so you'd have to write such squirrely code more like:

        sub F { return G(); LF: say 6; } sub G { say 2; no warnings 'exiting'; goto LF; } G();

        Indeed, I'm not sure why the -w you used fails to trigger this:

        =item Exiting subroutine via %s (W exiting) You are exiting a subroutine by unconventional means, such as a goto, or a loop control statement.

        - tye        

        > > I think in this context it's supposed to mean "block of a loop".

        > goto says that literally

        Really? Where?

        And I hate the fact that it uses the term "construct" in two different meanings!!!

        I'd really love to have the $cash to hire a crowd of math students to look thru the perldocs to clean up the wording.

        Cheers Rolf

Re: How does 'goto LABEL' search for its label?
by Anonymous Monk on Jan 16, 2013 at 15:32 UTC

    goto says (among other things)

    Use of "goto-LABEL" or "goto-EXPR" to jump into a construct is deprecated and will issue a warning. Even then, it may not be used to go into any construct that requires initialization, such as a subroutine or a "foreach" loop. It also can't be used to go into a construct that is optimized away.

    So LABELs are like lexicals, except with only file-scope or subroutine-scope ( Lexical scoping like a fox )

    lexical(my) variables from one loop/block is not visible in another, but labels are , because they currently have file scope (like my $foo at the top of a file outside of any bare blocks)

    currently { L: say 1; } is like my $L; { $L=1; say $L; }

    But this will change, once  Use of "goto" to jump into a construct is deprecated is disallowed, labels will be lexically scoped like my $vars

    someday { L: say 1; } is like { my $L=1; say $L; }

Re: How does 'goto LABEL' search for its label? (call stack)
by tye (Sage) on Jan 17, 2013 at 03:53 UTC

    I believe it is a rather simple search up the call stack. And all of your examples agree with that. Don't confuse that with searching up a "stack" of nested (or even prior) lexical blocks.

    A 'goto' is visible inside of the bounds of the dynamic scope where it lives. The two things that most clearly define a dynamic scope are: sub, eval. This certainly includes the subs that you can define without using the 'sub' keyword by passing a block to a sub with a prototype that starts with a '&' character. It is less clear how similar the separation is for similar scopes that can be built in a lot of places in Perl, including: map, grep, s///e, sort.

    So apparently, the construct containing the label has to have been executed once.

    Not true. The construct just mustn't be optimized away as happens when you give an 'if' statement an expression that is known to be false at compile time.

    $ perl -wE 'sub g() { label: say "here" }; g; goto label' here Can't find label label at -e line 1.

    Of course. g() has already returned by the time you execute "goto label", so the contents of g()'s dynamic scope are not present further up the call stack, so Perl can't find the "label:".

    As for there being a new warning for "if(1){X:...} goto X:", I lack the details for why this got deprecated. It seems to go rather farther than any prior restrictions and seems likely to run afoul of rather typical "use of 'goto' for error handling" that I've seen (though not frequently):

    ... if( ... ) { goto ERR; } ... if( $fatal ) { ERR: ... clean up after fatal error ... }

    - tye        

      Thanks tye, your reply did clear up a few things.

      I had two problems with my mental model. I didn't remember that that if (0) { } gets optimized away. I also wrongly assumed that each block produces a stack frame.

      As for there being a new warning for "if(1){X:...} goto X:", I lack the details for why this got deprecated.

      I didn't find many details either, just that without it, the implementation of lexpads would be much simpler and saner.

      Now I must go back and study the crufty error handling code I inherited, and see if it can be rescued (or if I can safely argue that it must be rewritten to use something saner).

        just that without it, the implementation of lexpads would be much simpler and saner.

        Yeah, that would have been my best guess. I'm curious about how "much" simplification we are talking about. But I'll go digging in the p5p archives for more details if I find the time and motivation. :)

        I wonder if the complications could be avoided by having behavior closer to this pseudo-Perl code:

        if( $fatal ) ERR: { ... }

        So that the destination for "goto ERR;" would be the start of the lexical block instead of the first line in the lexical block.

        It would be nice if a label pointing to the first line of a lexical block could just be moved to point to the start of the lexical block, avoiding the complexity and the need for deprecation. That is:

        if( $fatal ) { OK: ... no need to deprecate this ... } if( $fatal ) { my $foo; BAD: ... "goto BAD" from outside this block deprecated ... }

        But, if one ends up using a Perl that has the deprecation but doesn't implement my idea, then it should often be relatively easy to rewrite the code to be more like:

        ... if( $whatever ) { goto ERR; } ... if( $fatal ) { # Used to be "ERR:" here goto ERR; } return ...; ERR: ... code moved from if( $fatal ) ...

        - tye        

      > As for there being a new warning for if(1){X:...} goto X:, I lack the details for why this got deprecated.

      It's analogous to if(0) the constant block is optimized to a do-block¹ ...

      > perl -MO=Deparse -E'if (1) { label: say 1;}; goto label' BEGIN { $^H{'feature_say'} = q(1); $^H{'feature_state'} = q(1); $^H{'feature_switch'} = q(1); } do { label: say 1 }; goto label;

      ... which seems to fall into the same category like other loop-like-blocks:

      ATM I have only 5.10 available. Could someone please test this with >= 5.12?

      perl -wE'do { label: say 1;}; goto label'

      EDIT: removed exit

      Update

      here test code w/o endless looping.

      perl -wE'my $x=0;do { label: say $x++; die if $x>1}; goto label' 0 1 Died at -e line 1.

      Update

      Thanks to davido for testing

      [davido]: LanX: perl -E 'goto IN; do{ IN: say "Hello world\n";};' does + produce the warning on 5.16.2.

      Cheers Rolf

      ¹) which is somehow strange, I expected a simple bare block.

Re: How does 'goto LABEL' search for its label?
by sundialsvc4 (Abbot) on Jan 16, 2013 at 16:12 UTC

    And I, for one, still will go on-record as having rejected in code review every piece of logic I have ever encountered (other than in profoundly specialized cases such as device-drivers and parsers) that contained a goto.   And here’s the very-simple reason why:

    The program’s internal state, at the point of the label, is solely determined by the internal state at every point that “goes to” that label ... not (only) by the statements which surround it or precede it.   You can, intentionally or worse-yet unintentionally, introduce an error of the most-undebuggable kind at any point in the future ... and if what you did is syntactically acceptable, the compiler won’t say a word of warning to the effect that you just planted a high explosive device with a hair-trigger underneath your foot.

    This causes the software complexity meter to spike “infinity,” and for no purpose that cannot be better-achieved in some other way.   Even the “toy” example in the OP is effectively impossible to understand ... even as-written, and only if some other goto is not later added, somewhere, anywhere, anytime.   (Yes, I am intentionally stretching syntactic truth slightly to make a point ...)   You have to think to realize that what this logic is actually doing is if ($arg != 1).

    The Perl language evolved a number of constructs, such as last LABELNAME, specifically to accommodate the control-constructs that do indeed occur from time to time in production programs, and to handle them in a way that still affords for efficient code-generation and pragmatic goof-detection.   But goto, except in the edge-cases I previously mentioned, is an unnecessary bad-idea that should never be accepted in production code.   (The edge-case exceptions are not frequent-enough to justify softening the word, “never.”)