Skip to content

Fix tied variables in void context not calling FETCH#335

Merged
fglock merged 3 commits into
masterfrom
fix/tied-void-context-fetch
Mar 19, 2026
Merged

Fix tied variables in void context not calling FETCH#335
fglock merged 3 commits into
masterfrom
fix/tied-void-context-fetch

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Mar 19, 2026

Summary

Fixes #20 - Tied variables accessed in void context weren't calling FETCH.

Problem

When accessing tied hash/array elements in void context (e.g., $hash{key};), PerlOnJava was incorrectly optimizing out the access because:

  1. The JVM backend uses a lazy proxy pattern (RuntimeTiedHashProxyEntry) that only calls FETCH when the value is actually read
  2. In void context, after pushing the element reference onto the stack, it's immediately popped without being read
  3. This meant FETCH was never triggered, breaking expected Perl semantics

Solution

Added handleVoidContextForTied() in EmitOperator.java that:

  • Detects when we're about to discard a hash/array element value with POP
  • Calls getBoolean() on the value first to force FETCH to be invoked
  • Then discards the boolean result

This is applied to hash element ($hash{key}) and array element ($array[0]) dereference operators.

Changes

  • EmitOperator.java: Added handleVoidContextForTied() helper method
  • Dereference.java: Updated 6 locations to use the new helper for hash/array element operators
  • Added unit test tie_void_context.t with comprehensive test coverage

Known Limitation

eval { } blocks are not covered by this fix because PerlOnJava transforms them into sub { }->() at parse time. This is documented in the test file.

Test plan

  • make passes (all unit tests)
  • New test tie_void_context.t covers basic hash/array FETCH in void context
  • Scalar and list context control tests verify existing behavior unchanged

Generated with Devin

fglock and others added 3 commits March 19, 2026 12:36
When accessing tied hash/array elements in void context (e.g., $hash{key};),
PerlOnJava was incorrectly optimizing out the access because the lazy proxy
(RuntimeTiedHashProxyEntry) was never read. This broke expected Perl semantics
where tied variable access should trigger FETCH regardless of context.

The fix adds handleVoidContextForTied() in EmitOperator.java which calls
getBoolean() to force FETCH before the value is discarded with POP. This is
applied to hash element ($hash{key}) and array element ($array[0]) operators.

Known limitation: eval { } blocks transform the block into sub { }->() at
parse time, so the last statement runs in scalar context instead of void.
This is documented but not fixed in this PR.

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

Co-Authored-By: Devin <noreply@cognition.ai>
The initial tied void context fix called getBoolean() on all hash/array
element operations in void context. However, delete() can return null for
sparse arrays, causing NullPointerException.

Now only applies handleVoidContextForTied() for "get" operations. Delete
and exists operations use the standard handleVoidContext() (plain POP).

Also fixed slices to not double-pop in void context.

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

Co-Authored-By: Devin <noreply@cognition.ai>
In Perl, pop/shift/delete on sparse array elements should return undef,
not Java null. ArrayList.removeLast()/removeFirst() returns null when
the element at that position is null (sparse array).

This was the root cause of the NullPointerException in the tied void
context fix - delete() calls pop() for the last element.

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

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock force-pushed the fix/tied-void-context-fetch branch from 9b7408e to eefceed Compare March 19, 2026 11:36
@fglock fglock merged commit 4908709 into master Mar 19, 2026
2 checks passed
@fglock fglock deleted the fix/tied-void-context-fetch branch March 19, 2026 11:45
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.

tied variable in void context doesn't call FETCH

1 participant