Skip to content

Fix interpreter: pop/push/shift/unshift on @{...} expressions#284

Merged
fglock merged 8 commits into
masterfrom
fix-interpreter-pop-blocknode
Mar 7, 2026
Merged

Fix interpreter: pop/push/shift/unshift on @{...} expressions#284
fglock merged 8 commits into
masterfrom
fix-interpreter-pop-blocknode

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Mar 7, 2026

Summary

  • Fix interpreter failing on pop/push/shift/unshift with block expressions like @{$h->{PATH}}
  • The root cause was that emitArrayOperandRegister and handlePushUnshift only handled OperatorNode as the operand of @, but @{expr} produces a BlockNode
  • Use compileNode() with explicit SCALAR context instead of raw accept() to ensure proper register allocation

Test plan

  • mvn test passes (156 tests)
  • Manual test with interpreter backend
  • JVM backend still works correctly

Generated with Devin

fglock and others added 8 commits March 7, 2026 17:18
The interpreter compiler was not handling BlockNode operands for array
deref expressions like @{$h->{PATH}}. When compiling pop/push/shift/
unshift with such expressions, the BlockNode was visited without proper
context, causing lastResultReg to be -1.

Changes:
- CompileOperator.emitArrayOperandRegister: Accept BlockNode alongside
  OperatorNode, and use compileNode() with SCALAR context instead of
  raw accept()
- BytecodeCompiler.handlePushUnshift: Use compileNode() with SCALAR
  context when evaluating array dereference operands

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

Co-Authored-By: Devin <noreply@cognition.ai>
Two interpreter parity fixes:

1. BytecodeInterpreter CALL_SUB: Resolve symbolic code references using
   current package for STRING/BYTE_STRING types. This fixes cases like
   \&$func() where $func contains a subroutine name string - the name
   is now resolved in the current package instead of main.

2. CompileAssignment: Handle BlockNode operands for @{...} = ... just
   like OperatorNode. This fixes assignment to array deref expressions
   like @{$h->{list}} = (1, 2, 3).

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

Co-Authored-By: Devin <noreply@cognition.ai>
- Add CODE_DEREF_NONSTRICT opcode (375) for &{$name} dynamic code refs
- Implement handler in SlowOpcodeHandler.executeCodeDerefNonStrict()
- Handle BlockNode for * (glob) dereference operator
- Fix tr/// to compile operands in SCALAR context

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

Co-Authored-By: Devin <noreply@cognition.ai>
Several places in the bytecode compiler were using accept(this) instead
of compileNode with RuntimeContextType.SCALAR for hash keys and array
indices. This caused ClassCastException when constant functions or
method calls were used as keys/indices, as they return RuntimeList
but the opcodes expect RuntimeScalar.

Fixed locations:
- BytecodeCompiler: handleArrayElementAccess, handleGeneralArrayAccess,
  handleArrayKeyValueSlice, handleHashSlice, handleHashKeyValueSlice,
  handleArraySlice (added BlockNode support)
- CompileAssignment: hash key compilation in 3 places, added %$ref = ...
  hash dereference assignment support
- CompileExistsDelete: visitExistsArrow, visitDeleteArrow,
  visitDeleteHashSlice, compileHashKey, compileArrayIndex
- CompileOperator: compileArrayIndex

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

Co-Authored-By: Devin <noreply@cognition.ai>
The issue was that 'my $x = EXPR if COND' was transformed to
'COND && (my $x = EXPR)' which would skip the variable declaration
entirely when the condition was false. This caused null pointer
exceptions in the interpreter when later code tried to access $x.

The fix transforms 'my $x = EXPR if COND' to '(my $x, COND && ($x = EXPR))'
using the comma operator. This ensures:
- The variable is always declared in the current scope (via 'my $x')
- The assignment only happens when the condition is true

This matches Perl behavior where 'my $x = 1 if 0' declares $x as undef.
Same fix applies to 'unless' modifier.

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

Co-Authored-By: Devin <noreply@cognition.ai>
The hash slice compiler was missing support for BlockNode operands,
which is needed for syntax like @{$$et{OPTIONS}}{qw(key1 key2)}.
This was causing 'Hash slice requires hash variable or reference'
compile error.

Added BlockNode handling to handleHashSlice() matching the existing
pattern in handleArraySlice().

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

Co-Authored-By: Devin <noreply@cognition.ai>
Added BlockNode handling for $#{BLOCK} syntax in both:
- CompileOperator.java: for reading $#{...}
- CompileAssignment.java: for assigning $#{...} -= value

This fixes compile error for syntax like:
  $#{$$dirInfo{VarFormatData}} -= 1 if $wasVar;

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

Co-Authored-By: Devin <noreply@cognition.ai>
The commit 1d2dde2 incorrectly forced SCALAR context for hash slice
keys like @hash{@Codes}, which prevented array keys from expanding.
Revert hash slice key compilation to use LIST context (like JVM
backend), while keeping SCALAR context for single element access.

This fixes 4340 test regressions in pack.t.

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

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock merged commit b05b956 into master Mar 7, 2026
2 checks passed
@fglock fglock deleted the fix-interpreter-pop-blocknode branch March 7, 2026 19:32
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