Skip to content

jq: computed-bracket .E[K] evaluates the target once instead of re-evaluating it per key output #2914

Description

@newhoggy

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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    SonnetSuitable for a Sonnet-class model to implement

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions