-
-
Notifications
You must be signed in to change notification settings - Fork 490
Description
Description
findLast() and findLastIndex() produce a runtime error when used as values inside { } map literal expressions. All other predicate-based builtin functions (find, findIndex, filter, all, any, count, none, one) work correctly in the same position.
Minimal Reproduction
package main
import (
"fmt"
"github.com/expr-lang/expr"
)
func main() {
// These work fine:
run(`findLast([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)`) // OK: 5
run(`{"r": find([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}`) // OK: map[r:4]
run(`{"r": findIndex([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}`) // OK: map[r:3]
run(`{"r": filter([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}`) // OK: map[r:[4 5]]
run(`{"r": all([1.0, 2.0, 3.0, 4.0, 5.0], # > 0)}`) // OK: map[r:true]
// These crash:
run(`{"r": findLast([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}`) // ERROR
run(`{"r": findLastIndex([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}`) // ERROR
}
func run(expression string) {
program, err := expr.Compile(expression)
if err != nil {
fmt.Printf("COMPILE ERROR: %s\n %v\n", expression, err)
return
}
result, err := expr.Run(program, nil)
if err != nil {
fmt.Printf("RUN ERROR: %s\n %v\n", expression, err)
return
}
fmt.Printf("OK: %s\n %v\n", expression, result)
}Error
RUN ERROR: {"r": findLast([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}
interface conversion: interface {} is bool, not string (1:1)
| {"r": findLast([1.0, 2.0, 3.0, 4.0, 5.0], # > 3)}
| ^
Versions Affected
Tested on v1.17.6 and v1.17.8 — both reproduce. The bug appears to be long-standing.
Analysis
The compilation succeeds — the error is purely at runtime (expr.Run). The predicate # > 3 returns a bool, and somewhere in the VM execution path for findLast/findLastIndex inside a MapNode, a value is type-asserted as string when it is actually bool.
find and findIndex (which iterate forward) work fine in the same position. Only the reverse-iterating variants (findLast, findLastIndex) are affected.
Workaround
Extract findLast/findLastIndex into a let binding before the map literal:
let r = findLast([1.0, 2.0, 3.0, 4.0, 5.0], # > 3); {"result": r}