Perl permits local jumps inside deferred cleanup
Perl 5.37.10 allows constant-label goto inside defer and finally when the destination stays inside the cleanup block. The change refines the boundary between cleanup logic and surrounding control flow.
A cleanup block needs to run while the surrounding program leaves a scope. Allowing that block to redirect the surrounding return or loop would make the original departure harder to reason about. Perl initially handled one part of this problem by rejecting every goto inside its new cleanup constructs. That also rejected jumps whose destinations stayed entirely inside the cleanup code.
Perl 5.37.10 narrows the restriction. A goto with a constant label can appear inside defer or finally when the label occurs within that block. The March 20 release entry identifies the development version, and its release notes explain the distinction. Cleanup still cannot use the new permission to escape its containing block.
I prefer explicit scope rules for this kind of facility. Perl already puts a substantial amount of behavior into compact syntax. Making the permitted destination precise helps a reader understand a cleanup block without having to reconstruct every possible surrounding exit.
Registration happens during execution
The version-specific syntax reference describes defer as storing a block for execution when its containing scope exits. Reaching the declaration registers the work. It does not execute the work immediately.
This is different from an END block, which the compiler queues regardless of whether execution reaches its source line. A defer after an earlier return never registers. The distinction lets cleanup correspond to an operation that the program actually reached, rather than merely to code that exists in the file.
Multiple deferred blocks in one scope run in reverse registration order. The last one reached runs first. That order matters when later work depends on an earlier resource or operation. The documented rule makes the ordering a consequence of execution, so declarations skipped on a branch do not enter the cleanup sequence.
Deferred code runs on ordinary fallthrough and on other departures from the containing scope. The reference includes returns, exceptions, and jumps among these departures. Loop controls can also leave the relevant scope. The reason for departure therefore does not require a second copy of the cleanup body at every exit.
A jump can stay within cleanup
The new rule accepts a statically identified destination within the cleanup block. This minimal example follows the form in the release notes:
use feature 'defer';
{
defer {
goto COMPLETE;
print "Skipped cleanup branch\n";
COMPLETE:
print "Cleanup complete\n";
}
print "Body complete\n";
}
The ordinary body prints first. During scope exit, the deferred block skips its first print and reaches the local label. The example deliberately uses an unconditional jump so that the permitted destination is visible without unrelated resource handling.
A constant target gives the implementation enough information to check containment. A target selected dynamically does not have that same property. The release notes grant permission to the constant-label case and require the label to be within the block. They do not grant general permission to compute a destination at runtime.
This distinction separates local control flow from replacement of the surrounding exit. A cleanup body can contain branches and repeated work of its own. What it cannot do under this rule is jump into the caller's continuation or elsewhere outside its boundary. The cleanup remains part of completing the scope exit already in progress.
Finally shares the cleanup restriction
Perl's experimental try and catch syntax can have a following finally block. The syntax reference says this cleanup runs after the rest of the construct, including when the try body transfers control elsewhere. It describes finally in terms of the same behavior as defer.
The distinction between a try body and its cleanup body is significant. A return inside try returns from the containing function. It does not merely produce the return value of an isolated exception-catching block. The cleanup body has stricter rules because it executes while that surrounding control flow completes.
There is a documentation wrinkle in this snapshot. The general finally description still lists goto among prohibited controls without spelling out the new local-label exception. The release notes explicitly add that exception for both cleanup forms. Reading the two documents together avoids treating the older general wording as a rejection of the release's stated change.
The final expression of finally does not replace the enclosing function's return value. That is another part of keeping cleanup separate from the operation it accompanies. A reader should not interpret the last expression in a cleanup body as the result of the original try computation.
Exceptions remain a separate case
The cleanup restrictions do not prohibit throwing an exception. The syntax reference says an exception raised by deferred code propagates to the caller. It also describes an unsettled case when cleanup itself throws while another exception is already unwinding. Beyond the caller receiving an exception, the behavior is unspecified.
That makes local jump permission a narrow control-flow improvement. It does not supply a new policy for combining failures during unwinding. Code with an error in the original operation and another in cleanup still faces the documented exception limitation.
I see the change as a useful refinement of an experimental feature because it matches the restriction to the boundary being protected. Internal branches need not alter the enclosing function's departure. Perl 5.37.10 permits the constant-label case while retaining the requirement that deferred execution finish within its own block.