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));
|
||||
}
|
||||
|
||||
|
||||
@@ -428,13 +428,6 @@ jv jq_next(jq_state *jq) {
|
||||
break;
|
||||
}
|
||||
|
||||
case DUPN: {
|
||||
jv v = stack_popn(jq);
|
||||
stack_push(jq, jv_copy(v));
|
||||
stack_push(jq, v);
|
||||
break;
|
||||
}
|
||||
|
||||
case DUP2: {
|
||||
jv keep = stack_pop(jq);
|
||||
jv v = stack_pop(jq);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
OP(LOADK, CONSTANT, 1, 1)
|
||||
OP(DUP, NONE, 1, 2)
|
||||
OP(DUPN, NONE, 1, 2)
|
||||
OP(DUP2, NONE, 2, 3)
|
||||
OP(PUSHK_UNDER, CONSTANT, 1, 2)
|
||||
OP(POP, NONE, 1, 0)
|
||||
|
||||
+7
-1
@@ -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
|
||||
|
||||
Reference in new issue
Block a user