From 6a88e86ddc27e858fe8269be860532986c168db7 Mon Sep 17 00:00:00 2001 From: lawrence3699 Date: Sun, 12 Apr 2026 21:27:06 +1000 Subject: [PATCH] fix(compiler): pop stale bool from stack in emitLoopBackwards emitLoopBackwards uses OpMoreOrEqual + OpJumpIfFalse to check the loop condition, but never pops the comparison result from the stack. This leaves a stale bool that corrupts the value stack for the parent context. When findLast or findLastIndex is used inside a map literal like {"r": findLast([1, 2, 3], # > 3)}, the OpMap opcode tries to pop a string key but finds the leftover bool instead, causing: interface conversion: interface {} is bool, not string The forward-iterating emitLoop avoids this by using OpJumpIfEnd, which checks scope.Index directly without touching the stack. Fix: add OpPop after OpJumpIfFalse (continue path) and after patchJump (exit path), matching the convention used by emitCond and every other OpJumpIfFalse callsite in the compiler. Fixes #950 --- compiler/compiler.go | 2 ++ expr_test.go | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/compiler/compiler.go b/compiler/compiler.go index f66cf9ed..68517535 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1213,12 +1213,14 @@ func (c *compiler) emitLoopBackwards(body func()) { c.emit(OpInt, 0) c.emit(OpMoreOrEqual) end := c.emit(OpJumpIfFalse, placeholder) + c.emit(OpPop) body() c.emit(OpDecrementIndex) c.emit(OpJumpBackward, c.calcBackwardJump(begin)) c.patchJump(end) + c.emit(OpPop) } func (c *compiler) PredicateNode(node *ast.PredicateNode) { diff --git a/expr_test.go b/expr_test.go index 1bce3c8d..fd1ce3ab 100644 --- a/expr_test.go +++ b/expr_test.go @@ -1275,6 +1275,22 @@ func TestExpr(t *testing.T) { `findLastIndex(1..9, # % 2 == 0)`, 7, }, + { + `{"r": findLast([1, 2, 3, 4, 5], # > 3)}`, + map[string]any{"r": 5}, + }, + { + `{"r": findLastIndex([1, 2, 3, 4, 5], # > 3)}`, + map[string]any{"r": 4}, + }, + { + `{"r": findLast([1, 2, 3], # > 10)}`, + map[string]any{"r": nil}, + }, + { + `{"a": findLast([1, 2, 3], # > 1), "b": findLastIndex([4, 5, 6], # > 4)}`, + map[string]any{"a": 3, "b": 2}, + }, { `filter(1..9, # % 2 == 0)[-1]`, 8,