Fix reduce to preserve its input across init backtracking (#3633)

`reduce f as $x (init; update)` evaluated `f` against a stack slot
that forkpoints created while evaluating `init` restore. Both the
`DUPN` opening the reduction loop and the `LOADVN` retrieving the
result pop that slot destructively, nulling it out, so an `init`
that backtracks (e.g. `0, 0`) made `f` see `null` and fail with
"Cannot iterate over null".

Duplicate the input before the `FORK` instead of inside the loop,
and drop the leftover copy with `POP` once the loop is exhausted.
The slot left live at the `FORK` is then a block pushed after
`init` completed, which no earlier forkpoint can reach, so nulling
it is harmless. This is the same approach `gen_foreach` has used
since c70ae1a.

`DUPN` had no other user, so remove the opcode.
This commit is contained in:
itchyny authored and GitHub committed 2026-09-19 15:21:29 +09:00
1 parent 4beed4e01a
commit df24981307
4 files changed
+10 -11

No files matched your search

+7 -1
View File
@@ -2571,7 +2571,13 @@ try ["OK", setpath([[1]]; 1)] catch ["KO", .]
[]
["KO","Cannot update field at array index of array"]
# regression test for #3227
# regression test for #3227 (reduce)
[] | reduce .[] as $x (0, 1; .)
null
0
1
# regression test for #3227 (foreach)
foreach .[] as $x (0, 1; . + $x)
[1, 2]
1