From 424fe388fbc5125efc06130c855cab681d6bdc0e Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Sat, 9 May 2026 13:37:45 +0200 Subject: [PATCH] internal/core/adt: propagate cycle errors via state in reportFieldError MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a vertex has a CycleError recorded in its scheduler state (for example, from a failed comprehension task) but the error has not yet been promoted to BaseValue, reportFieldError now adopts that code so that lookups against the vertex surface CycleError rather than the generic IncompleteError. This allows validate to propagate the cycle through to the consumer. Pulled out of the upcoming dependency-tracking comprehension pushdown change so the diff there is smaller and so any regression introduced by this rule alone can be bisected independently. Signed-off-by: Marcel van Lohuizen Change-Id: I120b96b2e141bb5cae40cbf34f6f28dee56d1e6b Reviewed-on: https://cue.gerrithub.io/c/cue-lang/cue/+/1236931 Unity-Result: CUE porcuepine Reviewed-by: Daniel Martí TryBot-Result: CUEcueckoo --- cue/testdata/eval/disjunctioncross.txtar | 2 +- internal/core/adt/errors.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cue/testdata/eval/disjunctioncross.txtar b/cue/testdata/eval/disjunctioncross.txtar index 92ab5f556..cf5261a4b 100644 --- a/cue/testdata/eval/disjunctioncross.txtar +++ b/cue/testdata/eval/disjunctioncross.txtar @@ -176,7 +176,7 @@ issue3967: { }) } -- out/errors.txt -- -[incomplete] issue3967.error.nested.out: undefined field: kind: +[cycle] issue3967.error.nested.out: undefined field: kind: ./in.cue:131:17 [cycle] issue3967.error.local.out.oneOrTwo.two: circular dependency in evaluation of conditionals: X.two.optional changed after evaluation: ./in.cue:150:10 diff --git a/internal/core/adt/errors.go b/internal/core/adt/errors.go index 2edbe0a70..d1bb69a6c 100644 --- a/internal/core/adt/errors.go +++ b/internal/core/adt/errors.go @@ -279,6 +279,13 @@ func (v *Vertex) reportFieldError(c *OpContext, pos token.Pos, f Feature, intMsg // If v is an error, we need to adopt the worst error. if b := v.Bottom(); b != nil && !isCyclePlaceholder(b) { code = b.Code + } else if s := v.state; s != nil && s.errs != nil && s.errs.Code == CycleError { + // Also check cycle errors in the state (e.g. from failed + // comprehension tasks) that have not yet been promoted to BaseValue. + // This ensures that when a comp fails due to a mutual cycle, lookups + // of fields on the same vertex report CycleError instead of + // IncompleteError, allowing validate to propagate the cycle. + code = s.errs.Code } else if !v.Accept(c, f) { code = EvalError } -- 2.51.2