Severity: Low
Summary
Found while implementing #2259's fix (getpath's path-context argument generator was
eagerly drained instead of pulled lazily). path_context_step_computed_index (.E[K] | key
etc., src/jq/eval_generic.rs) evaluates the bracket's target E exactly once, before the
key stream K is pulled at all, and reuses that one value for every key output.
Real jq desugars E[K] as K as $k | E | .[$k] — confirmed live with unbuffered output
against /usr/bin/jq 1.7.1:
$ echo '{"a":1,"b":2}' | jq --unbuffered -c '(debug("target")|.)[("a", (debug("key2")|"x"))]'
["DEBUG:","target"]
1
["DEBUG:","key2"]
["DEBUG:","target"]
null
target fires once per key output (interleaved with each key's own side effects), not
once up front. succinctly currently evaluates the key stream first (in full) and the target
second (once), so both the ordering and the "does E re-run per key" question diverge
from jq's real desugaring whenever E itself is a multi-output generator with observable
side effects, or is expected to see a different ambient value per key iteration.
Why this is out of scope for #2259
#2259 was specifically about getpath's own argument generator being pulled eagerly
instead of lazily. This issue's mechanism is different and, empirically, deeper: it isn't
just "stop pulling after a failure" (the shape #2259 fixed) but "the target must be
re-evaluated fresh for every key output, interleaved with that key's own evaluation" — a
structural change to path_context_step_computed_index, not a sink conversion of an
existing collector. Attempting it as a drive-by inside #2259's PR risked introducing a
different (also-wrong) ordering without full verification.
Suggested fix direction
Restructure path_context_step_computed_index so the key stream drives a loop that
re-evaluates target fresh per key (via path_context_step_target), rather than computing
targets once outside the loop. Needs its own oracle-verified test matrix (single-output
target/key is unaffected; the multi-output×multi-output combination is where the current
implementation and jq's true semantics can diverge in both output values and side-effect
order).
Severity: Low
Summary
Found while implementing #2259's fix (
getpath's path-context argument generator waseagerly drained instead of pulled lazily).
path_context_step_computed_index(.E[K] | keyetc.,
src/jq/eval_generic.rs) evaluates the bracket's targetEexactly once, before thekey stream
Kis pulled at all, and reuses that one value for every key output.Real jq desugars
E[K]asK as $k | E | .[$k]— confirmed live with unbuffered outputagainst
/usr/bin/jq1.7.1:targetfires once per key output (interleaved with each key's own side effects), notonce up front. succinctly currently evaluates the key stream first (in full) and the target
second (once), so both the ordering and the "does
Ere-run per key" question divergefrom jq's real desugaring whenever
Eitself is a multi-output generator with observableside effects, or is expected to see a different ambient value per key iteration.
Why this is out of scope for #2259
#2259 was specifically about
getpath's own argument generator being pulled eagerlyinstead of lazily. This issue's mechanism is different and, empirically, deeper: it isn't
just "stop pulling after a failure" (the shape #2259 fixed) but "the target must be
re-evaluated fresh for every key output, interleaved with that key's own evaluation" — a
structural change to
path_context_step_computed_index, not a sink conversion of anexisting collector. Attempting it as a drive-by inside #2259's PR risked introducing a
different (also-wrong) ordering without full verification.
Suggested fix direction
Restructure
path_context_step_computed_indexso the key stream drives a loop thatre-evaluates
targetfresh per key (viapath_context_step_target), rather than computingtargetsonce outside the loop. Needs its own oracle-verified test matrix (single-outputtarget/key is unaffected; the multi-output×multi-output combination is where the current
implementation and jq's true semantics can diverge in both output values and side-effect
order).