Skip to content

WIP: Improve op/state.t pass rate (69 → 141/170)#414

Merged
fglock merged 6 commits into
masterfrom
fix/state-attribute-validation
Mar 31, 2026
Merged

WIP: Improve op/state.t pass rate (69 → 141/170)#414
fglock merged 6 commits into
masterfrom
fix/state-attribute-validation

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Mar 31, 2026

Summary

Improves op/state.t from 69/170 (40.6%) to 141/170 (82.9%) passing tests.

Changes

  • Interpreter: Fix state variable persistence - state $a (without initializer) was using RETRIEVE_BEGIN_SCALAR which destructively removes the variable from global storage on each call. Now uses STATE_INIT_SCALAR which preserves state across calls.
  • Interpreter: Fix bare block redo - Bare blocks compiled without LoopInfo, so redo/next/last could not find their target loop. Added LoopInfo push/pop around bare block compilation.
  • JVM backend: Dynamic goto EXPR fallback - goto EXPR (computed label) now triggers interpreter fallback instead of creating a GOTO marker that silently exits. The interpreter supports GOTO_DYNAMIC with label-to-PC lookup.
  • Parser: State in list context forbidden - Added compile-time validation for (state $a) = 1, state ($a, $b) = (), etc. with error "Initialization of state variables in list currently forbidden" (Perl 5 parity).

Remaining failures (15 tests + 14 blocked)

Tests Category Notes
42-43 :shared attribute Pre-existing; attribute silently accepted
62-68 goto + state interaction Pass standalone, fail in full test context
94, 96, 98, 100 given/when Likely incomplete given/when support
103, 106-107 State in closures/anon subs State sharing between closure copies
157-170 Blocked Never reached (test execution stops at 156)

Test plan

  • make passes (all unit tests)
  • op/state.t: 141/170 passing (up from 69)
  • Investigate remaining failures

Generated with Devin

fglock added a commit that referenced this pull request Mar 31, 2026
Record PR #414 results: op/state.t 69/170 → 141/170.
Mark item 1.5 as done, update Category 5 table, add progress tracking section.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
fglock and others added 6 commits March 31, 2026 21:53
…back

- BytecodeCompiler: Split BEGIN-captured variables (RETRIEVE_BEGIN_*) from
  state variables without initializers (STATE_INIT_*). State vars previously
  used RETRIEVE_BEGIN_SCALAR which calls removeGlobalVariable(), destroying
  state on each call. Now uses STATE_INIT_SCALAR which persists correctly.
- BytecodeCompiler: Add LoopInfo push/pop for bare blocks so redo/next/last
  can find their target loop in the interpreter.
- EmitControlFlow: Dynamic goto EXPR (computed label) now triggers interpreter
  fallback instead of creating a GOTO marker that silently exits. The
  interpreter supports GOTO_DYNAMIC with label-to-PC lookup.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Perl 5 forbids initializing state variables in list context, e.g.:
  (state $a) = 1;
  state ($a, $b) = ();
  (state $a, my $b) = ();

Added validation in ParseInfix.java that detects state declarations
on the left side of list assignments and throws:
"Initialization of state variables in list currently forbidden"

This fixes 46 tests in op/state.t (tests 108-153), bringing the
pass rate from 95/170 to 141/170.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Record PR #414 results: op/state.t 69/170 → 141/170.
Mark item 1.5 as done, update Category 5 table, add progress tracking section.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…tion

Interpreter backend:
- Add opcodes for symlink, chroot, mkdir, exec, fcntl, ioctl,
  getpwent, setpwent, endpwent, msgctl, shmctl, semctl
- Wire existing IPC opcodes (shmget, shmread, shmwrite, semget,
  semop, msgget, msgsnd, msgrcv) into CompileOperator
- Add NUL-byte guard for file test operators
- Add chroot and setpriority stubs

This fixes "Unsupported operator" errors when large test files
(like op/taint.t) trigger interpreter fallback.

CORE:: subroutine wrappers:
- Add lazy generation in all RuntimeCode.apply() overloads
- &CORE::reverse("hello") and similar calls now work correctly
- Previously only \&CORE::reverse worked; direct &CORE::X calls
  threw "Undefined subroutine" because the wrapper was never generated

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
When a GOTO or TAILCALL control flow marker was not handled by labeled
blocks in the interpreter CALL_SUB/CALL_METHOD handlers, it would
propagate all the way out and cause silent process exit. This happened
when code like eval { goto nonexistent_label } ran in functions
that triggered interpreter fallback.

Now, unhandled GOTO/TAILCALL markers check the evalCatchStack. If an
eval catch handler exists, the error variable is set to the error
message and execution jumps to the catch handler, matching the JVM
backend behavior.

Results: goto-sub.t improved from 17/44 to 32/44 (master baseline: 27/44)

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The JVM backend did not recognize goto \&NAME (backslash-reference to a
subroutine) as a tail-call form. It only handled goto &NAME (via
BinaryOperatorNode) and goto &{$var}/goto &$sub forms. The \&NAME
pattern fell through to the Dynamic goto EXPR catch-all, forcing
interpreter fallback.

This caused two problems:
1. Defer blocks did not fire before the tail-call (defer.t test 15)
2. Lexical closure variables were not properly shared after fallback

The fix recognizes OperatorNode("\\") containing a subroutine
reference and routes it through handleGotoSubroutine, which jumps to
returnLabel where try-finally cleanup (including defer blocks) runs
before the TAILCALL marker is returned to the caller.

Results: defer.t restored to 24/33 (master baseline), goto-sub.t 32/44

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/state-attribute-validation branch from cec6618 to c915666 Compare March 31, 2026 19:55
@fglock fglock merged commit d246cf2 into master Mar 31, 2026
2 checks passed
@fglock fglock deleted the fix/state-attribute-validation branch March 31, 2026 20:09
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