Skip to content

Implement Perl defer blocks (use feature 'defer')#301

Merged
fglock merged 2 commits intomasterfrom
feature/defer-blocks
Mar 11, 2026
Merged

Implement Perl defer blocks (use feature 'defer')#301
fglock merged 2 commits intomasterfrom
feature/defer-blocks

Conversation

@fglock
Copy link
Owner

@fglock fglock commented Mar 11, 2026

Summary

Implements Perl's defer statement from use feature 'defer', which schedules code blocks to run at scope exit in LIFO order.

Key Changes

  • DeferBlock class: Implements DynamicState to integrate with DynamicVariableManager cleanup
  • DeferNode AST node: New AST node for defer statements
  • Parser support: parseDeferStatement() in StatementParser, registered via StatementResolver
  • JVM backend: EmitStatement.emitDefer() compiles closure and pushes DeferBlock to DVM
  • Bytecode interpreter: PUSH_DEFER opcode (377) for deferred execution
  • Scope detection: FindDeclarationVisitor.containsLocalOrDefer() triggers cleanup for blocks with defer

Bug Fixes

goto &sub fixes

  • Fixed goto &sub parser: Parser was incorrectly transforming goto &sub into return &sub(@_), causing return inner() to be wrongly detected as a tail call. Now properly produces goto operator.
  • Fixed goto &{expr} tail calls: goto &{"subname"} (symbolic code dereference) was calling the function and using the return value as goto target instead of performing a tail call. Added handleGotoSubroutineBlock() in EmitControlFlow.java and corresponding support in interpreter.
  • Fixed goto &sub in $SIG{DIE}: Added TAILCALL trampoline in WarnDie.java to handle tail calls in die handlers.
  • Improved error messages: "Goto undefined subroutine" error message now matches Perl format for goto &sub with undefined subroutines.

Interpreter parity fixes

  • Fixed interpreter crash with tied() in scalar context: When tied() was used inside a ternary operator, the interpreter crashed with ClassCastException.
  • Fixed vec lvalue in interpreter: Added vec case in CompileAssignment.java for interpreter support.
  • Fixed |= and ^= operators: Changed to use polymorphic bitwiseOr/bitwiseXor instead of numeric-only ops.

Test Results

Test Status
Unit tests PASS (163 tests)
op/die_goto.t PASS (5/5)
uni/goto.t PASS (2/4) - remaining 2 fail due to pre-existing regex bug

Regression Analysis

All reported regressions have been investigated:

  • Fixed: op/die_goto.t, uni/goto.t tests 1-2
  • Pre-existing on master (not regressions): op/bop.t, op/warn.t, re/subst.t, re/pat_rt_report.t (MethodHandle conversion errors)

Known Limitations

  • last, next, redo don't trigger defer cleanup when jumping out of scope (documented in test)

Test plan

  • Unit tests pass (./gradlew test)
  • defer.t test file with 14 test cases
  • op/die_goto.t passes (5/5)
  • uni/goto.t tests 1-2 pass (goto &{expr})
  • Manual testing with both JVM and interpreter backends

Generated with Devin

@fglock fglock force-pushed the feature/defer-blocks branch from 7d29419 to b2f114c Compare March 11, 2026 11:34
fglock and others added 2 commits March 11, 2026 20:58
Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
## defer blocks (use feature 'defer')
- Implement defer statement for scope-exit code execution
- DeferBlock class wraps code ref for DynamicVariableManager cleanup
- Defer blocks capture enclosing @_ (Perl semantics)
- Exception-safe: last exception wins when multiple defers throw
- Compile-time checks prevent return/goto/last/next/redo in defer
- Same checks added for finally blocks

## goto &sub tail call fixes
- Fix goto &sub to run defer/local cleanup before tail call
- Fix goto &sub in both JVM and interpreter backends
- Fix goto &{expr} symbolic code dereference
- Add TAILCALL trampoline for goto &sub in __WARN__/__DIE__ handlers
- Check AUTOLOAD before throwing undefined subroutine error
- Improve error messages for undefined subroutines

## Interpreter parity fixes
- Add interpreter fallback for ASM frame computation failures
- Fix bitwise ops (|=, ^=) to use polymorphic methods
- Fix bitwise complement (~) operator
- Fix tied() in scalar context
- Fix vec lvalue support
- Fix hash/array slice assignment with PerlRange values
- Add kill operator

## Other fixes
- Fix eval not catching exceptions from defer blocks
- Fix #line directive for warn/die location messages
- Fix undefined subroutine error message format

## Documentation
- Update debugging skills with critical reminders
- Add regression tracking to AGENTS.md
- Document eval STRING uses interpreter by default

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

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock force-pushed the feature/defer-blocks branch from 80cb79b to 67c5ee0 Compare March 11, 2026 20:34
@fglock fglock merged commit e40845f into master Mar 11, 2026
2 checks passed
@fglock fglock deleted the feature/defer-blocks branch March 11, 2026 21:18
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