From d30d51afa4d9192aa0efba03c9fdd13249a7101e Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Fri, 15 May 2026 18:03:41 +0200 Subject: [PATCH] internal/core/adt: re-deref v in Vertex.lookup after state.process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit state.process can cause v to share with another vertex via scheduleVertexConjuncts in a resolver task (e.g. a let arc's resolver pointing v at its target). Without re-derefing, the subsequent LookupRaw operates on the pre-share v and misses fields that only became reachable via the new BaseValue. This fixes regression1 in pushdown.txtar: in #Primary: { _Y: _, let _X = _Y, if len(_X.spec.command) > 0 {...} } out: #Rollout & {_X: x} the comprehension's _X.spec lookup derefs through the let to _Y; _Y then shares with out._X (= x) during processing, but the original lookup stayed on the un-shared _Y, producing a stale "undefined field: spec" error instead of finding x.spec. Signed-off-by: Marcel van Lohuizen Change-Id: Iaca296df2120548da4931155ae1de8bd09d11323 Reviewed-on: https://cue.gerrithub.io/c/cue-lang/cue/+/1237197 Reviewed-by: Daniel Martí TryBot-Result: CUEcueckoo --- cue/testdata/comprehensions/pushdown.txtar | 13 ++++--------- internal/core/adt/unify.go | 9 +++++++++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/cue/testdata/comprehensions/pushdown.txtar b/cue/testdata/comprehensions/pushdown.txtar index d66cc11ad..6f1cef748 100644 --- a/cue/testdata/comprehensions/pushdown.txtar +++ b/cue/testdata/comprehensions/pushdown.txtar @@ -1341,12 +1341,9 @@ regression1: { spec: command: ["a"] patch: {} } - out: #Rollout & {_X: x} @test(eq:todo, at=out,{ + out: #Rollout & {_X: x} @test(eq, at=out,{ _Y: {spec: {command: ["a"]}, patch: {}} command: ["a"] - }) @test(eq, { - _X: {spec: {command: ["a"]}, patch: {}} - out: _|_ @test(err, code=incomplete, contains="regression1.out.out: undefined field: spec", pos=[5:13]) }) } regression2: { @@ -1517,14 +1514,12 @@ voidEliminationSuccess.derefDisj2.E.h: operand g of '!' not concrete (was bool): ./rework.cue:5:13 regression1.#Rollout.out: _X.patch undefined as _X is incomplete (type _): ./rework.cue:11:30 -[incomplete] regression1.out.out: undefined field: spec: - ./rework.cue:5:13 [incomplete] regression2.#Primary: undefined field: spec: - ./rework.cue:28:13 + ./rework.cue:25:13 [incomplete] regression2.#Rollout.out: _Y.spec undefined as _Y is incomplete (type _): - ./rework.cue:28:10 + ./rework.cue:25:10 regression2.#Rollout.out: _X.patch undefined as _X is incomplete (type _): - ./rework.cue:34:30 + ./rework.cue:31:30 [incomplete] letCycleSelfKeyword.#E: cannot range over s.hosts (incomplete type _): ./rework2.cue:7:13 [incomplete] letCycleSelfKeyword2.#E: cannot range over s.hosts (incomplete): diff --git a/internal/core/adt/unify.go b/internal/core/adt/unify.go index 302b30d15..30f2cb87c 100644 --- a/internal/core/adt/unify.go +++ b/internal/core/adt/unify.go @@ -764,6 +764,15 @@ func (v *Vertex) lookup(c *OpContext, pos token.Pos, f Feature, flags Flags) *Ve // TODO: verify lookup types. + // Re-deref v: state.process may have caused v to share with another + // vertex (e.g. via scheduleVertexConjuncts in a resolver task), so + // further lookup must follow the new BaseValue. + if v2 := v.DerefValue(); v2 != v { + v = v2 + // TODO: might result in better error message if not set. + state = v.getState(c) + } + arc := v.LookupRaw(f) // We leave further dereferencing to the caller, but we do dereference for // the remainder of this function to be able to check the status. -- 2.51.2