Skip to content

fix: preserve @_ aliasing across goto &SUB tail calls#643

Merged
fglock merged 1 commit intomasterfrom
fix/goto-sub-aliasing
Apr 30, 2026
Merged

fix: preserve @_ aliasing across goto &SUB tail calls#643
fglock merged 1 commit intomasterfrom
fix/goto-sub-aliasing

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Apr 30, 2026

Summary

Two issues exposed by ./jcpan -t JSON::Validator::Ref:

  1. goto &SUB no longer preserves @_ aliasing.
    The bytecode for tail calls was emitting argsValue.getList().getArrayOfAlias(). RuntimeArray.getList() deep-copies every element via new RuntimeScalar(element), so by the time the target sub sees @_ the elements are no longer aliased to the caller's variables. As a result $_[N] = ... in the target sub silently failed to mutate the caller, even though direct (non-goto) calls work fine. JSON::Validator's coerce/boolean validators (and many other CPAN modules) depend on this. Fix: skip getList() and call getArrayOfAlias() directly on the RuntimeBase value, matching the pattern already used by EmitBlock for for loops. Applied to both handleGotoSubroutine and handleGotoSubroutineBlock.

  2. jcpan -t Module::Name couldn't test older releases.
    _allow_installing was running before the test phase ($run_allow_installing_within_test = 1), so jcpan -t aborted with a "downgrade / outdated dist" stop for any module whose only available release is older than what's already installed (e.g. JSON::Validator::Ref only exists in JSON-Validator-4.25 even though JSON::Validator is now indexed at 5.15). The check is now deferred to the install phase; full installs still prompt.

Effect on jcpan -t JSON::Validator::Ref

Before After
Test files failed 19/77 17/77
Subtests failed 65/824 14/824
coerce.t 3/48 ok 48/48 ok
jv-array.t 33/35 ok 35/35 ok
jv-boolean.t 46/56 ok 50/56 ok

Remaining failures are unrelated root causes (Mojo::IOLoop shim returning undef for io/timer, YAML::XS checksum, openapiv3 explode edge case) and out of scope.

Test plan

  • Repro confirmed: sub bar { $_[0] = "X" } sub foo { goto &bar } my $x = "orig"; foo($x); print $x printed orig before, X after.
  • make (full unit-test suite) passes.
  • t/coerce.t, t/jv-array.t from JSON-Validator-4.25 now fully pass; t/jv-boolean.t improved.

Generated with Devin

@fglock fglock force-pushed the fix/goto-sub-aliasing branch from 84d7ff4 to 9dc1be9 Compare April 30, 2026 14:33
Two issues exposed by `jcpan -t JSON::Validator::Ref`:

1. `goto &SUB` was emitted as `argsValue.getList().getArrayOfAlias()`.
   `RuntimeArray.getList()` deep-copies each element via
   `new RuntimeScalar(element)`, which destroys aliasing of the caller's
   `@_`. After the tail call, `$_[N] = ...` in the target sub no longer
   propagated to the caller's variable. JSON::Validator's
   `_validate_type_boolean` (and many similar helpers) rely on this Perl
   semantic to coerce values in place. Fix: skip `getList()` and call
   `getArrayOfAlias()` directly on the `RuntimeBase` value, matching the
   pattern already used by `EmitBlock` for `for` loops. Applied to both
   `handleGotoSubroutine` (`goto &NAME` / `goto \&NAME`) and
   `handleGotoSubroutineBlock` (`goto &{expr}` / `goto &$var`).

2. CPAN.pm's `_allow_installing` early-stop check was running before the
   test phase (`$run_allow_installing_within_test = 1`), so
   `jcpan -t Module::Name` could not test older releases that contain
   modules no longer indexed under the same distribution (e.g.
   `JSON::Validator::Ref` only exists in JSON-Validator-4.25 even though
   the indexed `JSON::Validator` dist is now 5.15). Defer the check to
   the install phase. For full installs the prompt still fires.

Effect on `jcpan -t JSON::Validator::Ref`: failing subtests drop from
65/824 to 14/824 (e.g. coerce.t goes 0/48 → 48/48; jv-array.t 33/35 →
35/35). Remaining failures are unrelated (Mojo::IOLoop shim, YAML::XS
checksum, openapiv3 explode) and out of scope for this PR.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@fglock fglock force-pushed the fix/goto-sub-aliasing branch from 9dc1be9 to d28ab2f Compare April 30, 2026 14:53
@fglock fglock merged commit 52418d9 into master Apr 30, 2026
2 checks passed
@fglock fglock deleted the fix/goto-sub-aliasing branch April 30, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant