Control flow

goto#

Transfer execution elsewhere in the program without returning.

goto has three unrelated forms that share only a keyword. The useful one in modern Perl is goto &NAME — the tail-call form, which replaces the current subroutine frame with a call to another sub while preserving @_. The goto LABEL and goto EXPR forms exist, are constrained, and are almost never the right tool.

Synopsis#

goto LABEL
goto EXPR
goto &NAME

What you get back#

goto does not return. Control leaves the goto statement and does not come back. The return value of the construct the goto eventually lands in is the value of the enclosing expression.

For goto &NAME, the return value is whatever the target subroutine returns, propagated to the original caller as if the target had been called directly.

The tail-call form: goto &NAME#

This is the form that matters. It exits the current subroutine — undoing any local bindings — and immediately calls NAME with the current @_. The current frame is replaced, not stacked on top of.

Consequences:

  • caller inside the target sub sees the original caller, not the sub that issued the goto. The intermediate frame is gone.

  • @_ is passed through as-is. Modifications to @_ before the goto are visible to the target.

  • local changes made in the departing sub are undone before the target runs, exactly as they would be on a normal return.

  • The target does not have to know it was reached via goto &; from its perspective it was called normally.

The canonical use is AUTOLOAD dispatch: resolve the real sub, then goto it so stack traces, caller, and wantarray all behave as though the real sub had been called in the first place.

NAME need not be a literal name. It can be any expression that yields a code reference — a scalar holding a coderef, a block returning one, or a dereferenced slot.

goto &$coderef;
goto &{ $dispatch{$op} };

For recursive tail calls, goto __SUB__ jumps to the currently executing sub without re-resolving its name — safe under renaming and anonymous subs.

The goto LABEL form#

Finds the statement labeled LABEL within the current dynamic scope and resumes execution there. In practice this means it can jump out of blocks and out of subroutines, but it cannot jump into a construct that requires initialization — a subroutine body, a foreach loop, a given block, the parameter of a binary or list operator, or code the optimizer removed. Jumping into such a construct is a fatal error as of Perl 5.44.

Most of what goto LABEL could do is better expressed with labeled loop control (last LABEL, next LABEL, redo LABEL) or with exceptions (die / eval). Reach for those first.

The goto EXPR form#

Evaluates EXPR. If it yields a code reference, behaves like goto &NAME. If it yields a string, that string is used as a label name resolved at runtime — a computed goto. The same restrictions as goto LABEL apply to the label case.

Two parser quirks to remember:

  • goto EXPR is exempt from the “looks like a function” rule. Parentheses after goto do not necessarily delimit its argument: goto("NE")."XT" is goto NEXT, not goto "NE" followed by a useless concatenation.

  • goto has the same precedence as assignment, unlike most named operators.

Examples#

Tail-call dispatch from AUTOLOAD — the target sub sees the original caller, not AUTOLOAD:

sub AUTOLOAD {
    our $AUTOLOAD;
    my ($method) = $AUTOLOAD =~ /::(\w+)\z/;
    my $code = $self->can_really($method)
        or die "no such method: $method";
    goto &$code;              # @_ forwarded, caller() unchanged
}

Tail recursion without growing the call stack. __SUB__ resolves to the currently running sub:

sub fact {
    my ($n, $acc) = @_;
    $acc //= 1;
    return $acc if $n <= 1;
    @_ = ($n - 1, $acc * $n);
    goto __SUB__;
}

Computed goto via goto EXPR. The label is chosen at runtime from a list:

goto ("FOO", "BAR", "GLARCH")[$i];
FOO:    handle_foo();    return;
BAR:    handle_bar();    return;
GLARCH: handle_glarch(); return;

Delegating to a coderef held in a dispatch table. The caller never sees the intermediate frame:

my %ops = (
    add => sub { $_[0] + $_[1] },
    mul => sub { $_[0] * $_[1] },
);

sub run_op {
    my $op = shift;
    goto &{ $ops{$op} // die "unknown op: $op" };
}

Modifying @_ before a tail call — changes are visible to the target:

sub wrap {
    unshift @_, "prefix";
    goto &real_handler;       # real_handler sees ("prefix", @orig_args)
}

Edge cases#

  • goto LABEL cannot enter a sub, foreach, or given. As of Perl 5.44 this is a fatal error, not a warning. It also cannot jump into optimized-away code or into most operator operands — the one exception is the first operand of a binary operator (for =, the right-hand side).

  • goto LABEL cannot get out of a sort comparison block. The comparator runs in a context that does not permit the jump.

  • goto &NAME discards local bindings from the departing sub before the target runs. Anything the sub localized is restored as though the sub had returned normally.

  • caller is invisible past a goto &. The departing frame is gone. This is the whole point of the form for AUTOLOAD — do not rely on seeing the intermediate sub in a stack trace.

  • @_ aliasing is preserved. The target sub’s @_ aliases the same scalars the departing sub’s @_ did. Assigning to $_[0] in the target still modifies the caller’s argument.

  • goto &$coderef requires a real coderef. A string that looks like a sub name does not work in the & form — use goto EXPR (without the &) for that, or resolve the name to a coderef first with \&{$name}.

  • goto has assignment precedence. goto "A" . "B" is goto "AB", not (goto "A") . "B".

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • last — exit the innermost or a named enclosing loop; the right tool for most “jump out of here” cases

  • next — skip to the next iteration of a loop, with optional label

  • redo — restart the current loop iteration without re-evaluating the condition

  • return — leave the current sub with a value; compare with goto &NAME which leaves the sub by replacing it

  • caller — inspect the call stack; note that goto & makes the departing frame invisible to it