Skip to content

Commit c70ae1a

Browse files
authored
fix: foreach should not break init backtracking with DUPN (#3266)
Simplifies the compilation of `foreach` to avoid the problem in #3227 by removing unnecessary `FORK ... BACKTRACK`. Does not address `reduce`, which is less trivial.
1 parent f65d088 commit c70ae1a

File tree

2 files changed

+27
-39
lines changed

2 files changed

+27
-39
lines changed

src/compile.c

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -843,46 +843,26 @@ block gen_reduce(block source, block matcher, block init, block body) {
843843
}
844844

845845
block gen_foreach(block source, block matcher, block init, block update, block extract) {
846-
block output = gen_op_targetlater(JUMP);
847846
block state_var = gen_op_var_fresh(STOREV, "foreach");
848-
block loop = BLOCK(gen_op_simple(DUPN),
849-
// get a value from the source expression:
850-
source,
851-
// destructure the value into variable(s) for all the code
852-
// in the body to see
853-
bind_alternation_matchers(matcher,
854-
// load the loop state variable
855-
BLOCK(gen_op_bound(LOADV, state_var),
856-
// generate updated state
857-
update,
858-
// save the updated state for value extraction
859-
gen_op_simple(DUP),
860-
// save new state
861-
gen_op_bound(STOREV, state_var),
862-
// extract an output...
863-
extract,
864-
// ...and output it by jumping
865-
// past the BACKTRACK that comes
866-
// right after the loop body,
867-
// which in turn is there
868-
// because...
869-
//
870-
// (Incidentally, extract can also
871-
// backtrack, e.g., if it calls
872-
// empty, in which case we don't
873-
// get here.)
874-
output)));
875-
block foreach = BLOCK(gen_op_simple(DUP),
876-
init,
877-
state_var,
878-
gen_op_target(FORK, loop),
879-
loop,
880-
// ...at this point `foreach`'s original input
881-
// will be on top of the stack, and we don't
882-
// want to output it, so we backtrack.
883-
gen_op_simple(BACKTRACK));
884-
inst_set_target(output, foreach); // make that JUMP go bast the BACKTRACK at the end of the loop
885-
return foreach;
847+
return BLOCK(gen_op_simple(DUP),
848+
init,
849+
state_var,
850+
gen_op_simple(DUP),
851+
// get a value from the source expression:
852+
source,
853+
// destructure the value into variable(s) for all the code
854+
// in the body to see
855+
bind_alternation_matchers(matcher,
856+
// load the loop state variable
857+
BLOCK(gen_op_bound(LOADV, state_var),
858+
// generate updated state
859+
update,
860+
// save the updated state for value extraction
861+
gen_op_simple(DUP),
862+
// save new state
863+
gen_op_bound(STOREV, state_var),
864+
// extract an output...
865+
extract)));
886866
}
887867

888868
block gen_definedor(block a, block b) {

tests/jq.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2319,3 +2319,11 @@ try ltrimstr("x") catch "x", try rtrimstr("x") catch "x" | "ok"
23192319
try ["OK", setpath([[1]]; 1)] catch ["KO", .]
23202320
[]
23212321
["KO","Cannot update field at array index of array"]
2322+
2323+
# regression test for #3227
2324+
foreach .[] as $x (0, 1; . + $x)
2325+
[1, 2]
2326+
1
2327+
3
2328+
2
2329+
4

0 commit comments

Comments
 (0)