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:
1 parent
4beed4e01a
commit
df24981307
4 files changed
+10
-11
No files matched your search
+3
-2
@@ -832,8 +832,7 @@ static block bind_alternation_matchers(block matchers, block body) {
|
||||
|
||||
block gen_reduce(block source, block matcher, block init, block body) {
|
||||
block res_var = gen_op_var_fresh(STOREV, "reduce");
|
||||
block loop = BLOCK(gen_op_simple(DUPN),
|
||||
source,
|
||||
block loop = BLOCK(source,
|
||||
bind_alternation_matchers(matcher,
|
||||
BLOCK(gen_op_bound(LOADVN, res_var),
|
||||
body,
|
||||
@@ -842,8 +841,10 @@ block gen_reduce(block source, block matcher, block init, block body) {
|
||||
return BLOCK(gen_op_simple(DUP),
|
||||
init,
|
||||
res_var,
|
||||
gen_op_simple(DUP),
|
||||
gen_op_target(FORK, loop),
|
||||
loop,
|
||||
gen_op_simple(POP),
|
||||
gen_op_bound(LOADVN, res_var));
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user