From 06141e4cd9c59d3c380e97c5b853277d7e20cb76 Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Fri, 27 Sep 2024 13:58:40 +0200 Subject: [PATCH] internal/core/debug: add cycle detector Sometimes nodes are shared (using structure sharing) that have a structural cycle without reporting on that structural cycle. This caused printing to hang. This change detects such cases and modifies to output to inform about such cycles. Issue #2850 Signed-off-by: Marcel van Lohuizen Change-Id: Iba74c91729507a4782c7f5902ef2ac6ed2770716 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1201896 Reviewed-by: Matthew Sackman Unity-Result: CUE porcuepine TryBot-Result: CUEcueckoo --- internal/core/adt/unify.go | 2 ++ internal/core/debug/compact.go | 1 + internal/core/debug/debug.go | 27 +++++++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/internal/core/adt/unify.go b/internal/core/adt/unify.go index fb3b9f3ed3d..4261d6ad982 100644 --- a/internal/core/adt/unify.go +++ b/internal/core/adt/unify.go @@ -438,6 +438,8 @@ func (n *nodeContext) updateScalar() { } func (n *nodeContext) completeAllArcs(needs condition, mode runMode) bool { + // TODO: this can go when lookup vs evaluation distinction + // has been improved. if n.node.status == evaluatingArcs { // NOTE: this was an "incomplete" error pre v0.6. If this is a problem // we could make this a CycleError. Technically, this may be correct, diff --git a/internal/core/debug/compact.go b/internal/core/debug/compact.go index 87c31fca787..a0d0494e90b 100644 --- a/internal/core/debug/compact.go +++ b/internal/core/debug/compact.go @@ -89,6 +89,7 @@ func (w *compactPrinter) node(n adt.Node) { case *adt.Vertex: if v, ok := w.printShared(x); !ok { w.node(v) + w.popVertex() } case adt.Value: diff --git a/internal/core/debug/debug.go b/internal/core/debug/debug.go index ce37ee58696..c3cddc23ebb 100644 --- a/internal/core/debug/debug.go +++ b/internal/core/debug/debug.go @@ -70,6 +70,9 @@ type printer struct { indent string cfg *Config + // keep track of vertices to avoid cycles. + stack []*adt.Vertex + // modes: // - show vertex // - show original conjuncts @@ -144,9 +147,32 @@ func (w *printer) printShared(v *adt.Vertex) (x *adt.Vertex, ok bool) { w.shared(s) return v, true } + if !w.pushVertex(v) { + if s != nil { + w.shared(s) + w.string(" =>") + } + w.shared(v) + return v, true + } return v, false } +func (w *printer) pushVertex(v *adt.Vertex) bool { + for _, x := range w.stack { + if x == v { + w.string("") + return false + } + } + w.stack = append(w.stack, v) + return true +} + +func (w *printer) popVertex() { + w.stack = w.stack[:len(w.stack)-1] +} + func (w *printer) shortError(errs errors.Error) { for { msg, args := errs.Msg() @@ -201,6 +227,7 @@ func (w *printer) node(n adt.Node) { if ok { return } + defer w.popVertex() var kind adt.Kind if x.BaseValue != nil {