From f21553f1a57bd07c592b1768e57ebacadce145e9 Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Tue, 19 Mar 2024 14:36:32 +0100 Subject: [PATCH] internal/core/adt: add methods for accessing Conjuncts The new evaluator uses a tree of conjuncts, instead of a list. Much of the existing code does not handle this properly. The plan is to replace access to Conjuncts with the use of visitor methods. Issue #2886 Signed-off-by: Marcel van Lohuizen Change-Id: Idba1f3af0ede1184932c6152b4e5a2cd92e510c5 --- cmd/cue/cmd/custom.go | 14 +- cue/testdata/builtins/056_issue314.txtar | 43 ----- cue/testdata/builtins/incomplete.txtar | 201 ----------------------- cue/types.go | 107 +++++++----- internal/core/adt/composite.go | 63 +++++++ internal/core/dep/dep.go | 15 +- internal/core/dep/dep_test.go | 5 +- internal/core/dep/mixed.go | 12 +- internal/core/export/export.go | 15 +- internal/core/export/expr.go | 12 +- internal/core/export/extract.go | 12 +- internal/core/export/value.go | 5 +- internal/core/runtime/extern.go | 5 +- internal/task/task.go | 6 +- 14 files changed, 188 insertions(+), 327 deletions(-) diff --git a/cmd/cue/cmd/custom.go b/cmd/cue/cmd/custom.go index d1d6e19be67..0be34f4e428 100644 --- a/cmd/cue/cmd/custom.go +++ b/cmd/cue/cmd/custom.go @@ -29,6 +29,7 @@ import ( "cuelang.org/go/cue" "cuelang.org/go/cue/errors" "cuelang.org/go/cue/token" + "cuelang.org/go/internal/core/adt" itask "cuelang.org/go/internal/task" "cuelang.org/go/internal/value" _ "cuelang.org/go/pkg/tool/cli" // Register tasks @@ -92,17 +93,22 @@ func customCommand(c *Command, typ, name string, tools *cue.Instance) (*cobra.Co // Ensure there is at least one tool file. // TODO: remove this block to allow commands to be defined in any file. -outer: for _, v := range []cue.Value{tools.Lookup(typ), o} { _, w := value.ToInternal(v) - for _, c := range w.Conjuncts { + hasToolFile := false + w.VisitLeafConjuncts(func(c adt.Conjunct) bool { src := c.Source() if src == nil { - continue + return true } if strings.HasSuffix(src.Pos().Filename(), "_tool.cue") { - break outer + hasToolFile = true + return false } + return true + }) + if hasToolFile { + break } if err := v.Err(); err != nil { return nil, err diff --git a/cue/testdata/builtins/056_issue314.txtar b/cue/testdata/builtins/056_issue314.txtar index 797390ba09d..6320d65b295 100644 --- a/cue/testdata/builtins/056_issue314.txtar +++ b/cue/testdata/builtins/056_issue314.txtar @@ -85,49 +85,6 @@ Retain: 17 Unifications: 45 Conjuncts: 77 Disjuncts: 62 --- out/evalalpha -- -(struct){ - x: (#struct){ - s: (string){ "myname" } - out: (string){ "myname" } - } - #T: (#struct){ - s: (string){ string } - out: (_|_){ - // [incomplete] #T.out: error in call to text/template.Execute: cannot convert non-concrete value string: - // ./in.cue:14:7 - } - } - #V: (#struct){ - s: (string){ string } - out: (_|_){ - // [incomplete] cannot convert incomplete value "string" to JSON: - // ./in.cue:20:7 - } - } - #U: (#struct){ - s: (string){ string } - out: (_|_){ - // [incomplete] #U.out: error in call to encoding/yaml.Marshal: incomplete value string: - // ./in.cue:26:7 - // ./in.cue:25:7 - } - } -} --- diff/-out/evalalpha<==>+out/eval -- -diff old new ---- old -+++ new -@@ -8,7 +8,6 @@ - out: (_|_){ - // [incomplete] #T.out: error in call to text/template.Execute: cannot convert non-concrete value string: - // ./in.cue:14:7 -- // ./in.cue:15:3 - } - } - #V: (#struct){ --- diff/todo/p2 -- -error positions -- out/eval -- (struct){ x: (#struct){ diff --git a/cue/testdata/builtins/incomplete.txtar b/cue/testdata/builtins/incomplete.txtar index 3465fe95b85..886702135cc 100644 --- a/cue/testdata/builtins/incomplete.txtar +++ b/cue/testdata/builtins/incomplete.txtar @@ -110,207 +110,6 @@ Retain: 61 Unifications: 109 Conjuncts: 264 Disjuncts: 156 --- out/evalalpha -- -Errors: -badListType.decimal: cannot use 2 (type int) as list in argument 1 to list.Max: - ./in.cue:79:11 -badListType.str: cannot use 2 (type int) as list in argument 1 to strings.Join: - ./in.cue:79:11 -badListError.x: invalid operands 2 and "foo" to '+' (type int and string): - ./in.cue:85:11 - ./in.cue:86:11 - -Result: -(_|_){ - // [eval] - list1: (struct){ - Out1: (#list){ - 0: (_|_){ - // [incomplete] list1._Sub: undefined field: b: - // ./in.cue:19:10 - } - } - Out2: (#list){ - 0: (_|_){ - // [incomplete] list1._Sub: undefined field: b: - // ./in.cue:19:10 - } - } - Out3: (#list){ - 0: (_|_){ - // [incomplete] list1._Sub: undefined field: b: - // ./in.cue:19:10 - } - } - Top: (#list){ - 0: (_|_){ - // [incomplete] list1._Sub: undefined field: b: - // ./in.cue:19:10 - } - } - _Sub: (_|_){ - // [incomplete] list1._Sub: undefined field: b: - // ./in.cue:19:10 - } - a: (struct){ - } - } - list2: (struct){ - Out1: (_|_){ - // [incomplete] list2.#Sub: undefined field: b: - // ./in.cue:33:10 - } - Out2: (_|_){ - // [incomplete] list2.#Sub: undefined field: b: - // ./in.cue:33:10 - } - Out3: (_|_){ - // [incomplete] list2.#Sub: undefined field: b: - // ./in.cue:33:10 - } - _Top: (_|_){ - // [incomplete] list2.#Sub: undefined field: b: - // ./in.cue:33:10 - } - #Sub: (_|_){ - // [incomplete] list2.#Sub: undefined field: b: - // ./in.cue:33:10 - } - a: (struct){ - } - } - value1: (struct){ - a: (_|_){ - // [incomplete] value1.a: unresolved disjunction 'sf' | 'dd' (type bytes): - // ./in.cue:39:5 - } - } - value2: (_|_){ - // [incomplete] value2: unresolved disjunction 'sf' | 'dd' (type bytes): - // ./in.cue:43:2 - } - incompleteArgDecimalList: (struct){ - a: (#struct){ - param: (int){ 123 } - transformed: (int){ 123 } - max: (int){ 123 } - } - #a: (#struct){ - param: (int){ int } - transformed: (_|_){ - // [incomplete] incompleteArgDecimalList.#a.transformed: operand param of '+' not concrete (was int): - // ./in.cue:50:17 - } - max: (_|_){ - // [incomplete] incompleteArgDecimalList.#a.0: operand param of '+' not concrete (was int): - // ./in.cue:50:17 - } - } - } - incompleteArgStringList: (struct){ - a: (#struct){ - param: (string){ "123" } - transformed: (string){ "123" } - joined: (string){ "123" } - } - #a: (#struct){ - param: (string){ string } - transformed: (_|_){ - // [incomplete] incompleteArgStringList.#a.transformed: non-concrete value string in operand to +: - // ./in.cue:59:16 - // ./in.cue:58:16 - } - joined: (_|_){ - // [incomplete] incompleteArgStringList.#a.0: non-concrete value string in operand to +: - // ./in.cue:59:16 - // ./in.cue:58:16 - } - } - } - incompleteList: (struct){ - x: (_){ _ } - decimal: (_|_){ - // [incomplete] incompleteList.decimal: non-concrete list for argument 0: - // ./in.cue:66:11 - } - str: (_|_){ - // [incomplete] incompleteList.str: non-concrete list for argument 0: - // ./in.cue:67:11 - } - } - incompleteListError: (struct){ - x: (_|_){ - // [incomplete] incompleteListError.x: non-concrete value _ in operand to +: - // ./in.cue:72:11 - } - y: (_){ _ } - decimal: (_|_){ - // [incomplete] incompleteListError.x: non-concrete value _ in operand to +: - // ./in.cue:72:11 - } - str: (_|_){ - // [incomplete] incompleteListError.x: non-concrete value _ in operand to +: - // ./in.cue:72:11 - } - } - badListType: (_|_){ - // [eval] - x: (int){ 2 } - decimal: (_|_){ - // [eval] badListType.decimal: cannot use 2 (type int) as list in argument 1 to list.Max: - // ./in.cue:79:11 - } - str: (_|_){ - // [eval] badListType.str: cannot use 2 (type int) as list in argument 1 to strings.Join: - // ./in.cue:79:11 - } - } - badListError: (_|_){ - // [eval] - x: (_|_){ - // [eval] badListError.x: invalid operands 2 and "foo" to '+' (type int and string): - // ./in.cue:85:11 - // ./in.cue:86:11 - } - y: (string){ "foo" } - decimal: (_|_){ - // [eval] badListError.x: invalid operands 2 and "foo" to '+' (type int and string): - // ./in.cue:85:11 - // ./in.cue:86:11 - } - str: (_|_){ - // [eval] badListError.x: invalid operands 2 and "foo" to '+' (type int and string): - // ./in.cue:85:11 - // ./in.cue:86:11 - } - } - multipleErrors: (struct){ - #T: (#struct){ - params: (#struct){ - x: (string){ string } - y: (string){ string } - } - out: (_|_){ - // [incomplete] multipleErrors.#T.out: error in call to text/template.Execute: cannot convert non-concrete value string: - // ./in.cue:99:8 - } - } - } -} --- diff/-out/evalalpha<==>+out/eval -- -diff old new ---- old -+++ new -@@ -180,7 +180,6 @@ - out: (_|_){ - // [incomplete] multipleErrors.#T.out: error in call to text/template.Execute: cannot convert non-concrete value string: - // ./in.cue:99:8 -- // ./in.cue:96:4 - } - } - } --- diff/todo/p3 -- -Error position -- out/eval -- Errors: badListType.decimal: cannot use 2 (type int) as list in argument 1 to list.Max: diff --git a/cue/types.go b/cue/types.go index a34134d18e6..2a54133d6fb 100644 --- a/cue/types.go +++ b/cue/types.go @@ -624,11 +624,16 @@ func newChildValue(o *structValue, i int) Value { // otherwise. func Dereference(v Value) Value { n := v.v - if n == nil || len(n.Conjuncts) != 1 { + if n == nil { return v } - env, expr := n.Conjuncts[0].EnvExpr() + c, count := n.SingleConjunct() + if count != 1 { + return v + } + + env, expr := c.EnvExpr() // TODO: consider supporting unwrapping of structs or comprehensions around // a single embedded reference. @@ -637,7 +642,7 @@ func Dereference(v Value) Value { return v } - c := adt.MakeRootConjunct(env, expr) + c = adt.MakeRootConjunct(env, expr) ctx := v.ctx() n, b := ctx.Resolve(c, r) @@ -1054,10 +1059,11 @@ func (v hiddenValue) Split() []Value { return nil } a := []Value{} - for _, x := range v.v.Conjuncts { + v.v.VisitLeafConjuncts(func(x adt.Conjunct) bool { env, expr := x.EnvExpr() a = append(a, remakeValue(v, env, expr)) - } + return true + }) return a } @@ -1069,10 +1075,17 @@ func (v Value) Source() ast.Node { if v.v == nil { return nil } - if len(v.v.Conjuncts) == 1 { - return v.v.Conjuncts[0].Source() + count := 0 + var src ast.Node + v.v.VisitLeafConjuncts(func(c adt.Conjunct) bool { + src = c.Source() + count++ + return true + }) + if count > 1 || src == nil { + src = v.v.Value().Source() } - return v.v.Value().Source() + return src } // If v exactly represents a package, BuildInstance returns @@ -1110,18 +1123,19 @@ func (v Value) Pos() token.Pos { } // Pick the most-concrete field. var p token.Pos - for _, c := range v.v.Conjuncts { + v.v.VisitLeafConjuncts(func(c adt.Conjunct) bool { x := c.Elem() pp := pos(x) if pp == token.NoPos { - continue + return true } p = pp // Prefer struct conjuncts with actual fields. if s, ok := x.(*adt.StructLit); ok && len(s.Fields) > 0 { - break + return false } - } + return true + }) return p } @@ -1966,11 +1980,11 @@ func (v hiddenValue) Reference() (inst *Instance, path []string) { // is not a reference. func (v Value) ReferencePath() (root Value, p Path) { // TODO: don't include references to hidden fields. - if v.v == nil || len(v.v.Conjuncts) != 1 { + c, count := v.v.SingleConjunct() + if count != 1 { return Value{}, Path{} } ctx := v.ctx() - c := v.v.Conjuncts[0] env, expr := c.EnvExpr() @@ -2298,41 +2312,46 @@ func (v Value) Expr() (Op, []Value) { if v.v.IsData() { expr = v.v.Value() + goto process - } else { - switch len(v.v.Conjuncts) { - case 0: - if v.v.BaseValue == nil { - return NoOp, []Value{makeValue(v.idx, v.v, v.parent_)} // TODO: v? - } - expr = v.v.Value() - - case 1: - // the default case, processed below. - c := v.v.Conjuncts[0] - env, expr = c.EnvExpr() - if w, ok := expr.(*adt.Vertex); ok { - return Value{v.idx, w, v.parent_}.Expr() - } + } - default: - a := []Value{} - ctx := v.ctx() - for _, c := range v.v.Conjuncts { - // Keep parent here. TODO: do we need remove the requirement - // from other conjuncts? - n := &adt.Vertex{ - Parent: v.v.Parent, - Label: v.v.Label, - } - n.AddConjunct(c) - n.Finalize(ctx) - a = append(a, makeValue(v.idx, n, v.parent_)) - } - return adt.AndOp, a + switch c, count := v.v.SingleConjunct(); count { + case 0: + if v.v.BaseValue == nil { + return NoOp, []Value{makeValue(v.idx, v.v, v.parent_)} // TODO: v? + } + expr = v.v.Value() + + case 1: + // the default case, processed below. + env = c.Env + env, expr = c.EnvExpr() + if w, ok := expr.(*adt.Vertex); ok { + return Value{v.idx, w, v.parent_}.Expr() } + + default: + a := []Value{} + ctx := v.ctx() + v.v.VisitLeafConjuncts(func(c adt.Conjunct) bool { + // Keep parent here. TODO: do we need remove the requirement + // from other conjuncts? + n := &adt.Vertex{ + Parent: v.v.Parent, + Label: v.v.Label, + } + n.AddConjunct(c) + n.Finalize(ctx) + a = append(a, makeValue(v.idx, n, v.parent_)) + return true + }) + + return adt.AndOp, a } +process: + // TODO: replace appends with []Value{}. For not leave. a := []Value{} op := NoOp diff --git a/internal/core/adt/composite.go b/internal/core/adt/composite.go index 81017d1ac9b..9185c701937 100644 --- a/internal/core/adt/composite.go +++ b/internal/core/adt/composite.go @@ -237,6 +237,10 @@ type Vertex struct { // // This value may be nil, in which case the Arcs are considered to define // the final value of this Vertex. + // + // TODO: all access to Conjuncts should go through functions like + // VisitLeafConjuncts and VisitAllConjuncts. We should probably make this + // an unexported field. Conjuncts []Conjunct // Structs is a slice of struct literals that contributed to this value. @@ -558,6 +562,65 @@ func (v *Vertex) setParentDone() { } } +// VisitLeafConjuncts visits all conjuncts that are leafs of the ConjunctGroup tree. +func (v *Vertex) VisitLeafConjuncts(f func(Conjunct) bool) { + v.visitConjuncts(v.Conjuncts, f) +} + +func (v *Vertex) visitConjuncts(a []Conjunct, f func(Conjunct) bool) bool { + for _, c := range a { + switch x := c.x.(type) { + case *ConjunctGroup: + if !v.visitConjuncts(*x, f) { + return false + } + default: + if !f(c) { + return false + } + } + } + return true +} + +// VisitAllConjuncts visits all conjuncts of v, including ConjunctGroups. +// Note that ConjunctGroups do not have an Environment associated with them. +func (v *Vertex) VisitAllConjuncts(f func(c Conjunct, isLeaf bool)) { + v.visitAllConjuncts(v.Conjuncts, f) +} + +func (v *Vertex) visitAllConjuncts(a []Conjunct, f func(c Conjunct, isLeaf bool)) { + for _, c := range a { + switch x := c.x.(type) { + case *ConjunctGroup: + f(c, false) + v.visitAllConjuncts(*x, f) + default: + f(c, true) + } + } +} + +// SingleConjunct reports whether there is a single leaf conjunct and returns 1 +// if so. It will return 0 if there are no conjuncts or 2 if there are more than +// 1. +// +// This is an often-used operation. +func (v *Vertex) SingleConjunct() (c Conjunct, count int) { + if v == nil { + return c, 0 + } + v.VisitLeafConjuncts(func(x Conjunct) bool { + c = x + if count++; count > 1 { + return false + } + return true + }) + + return c, count +} + // Value returns the Value of v without definitions if it is a scalar // or itself otherwise. func (v *Vertex) Value() Value { diff --git a/internal/core/dep/dep.go b/internal/core/dep/dep.go index b8cde15df35..ebdc8ca0b2a 100644 --- a/internal/core/dep/dep.go +++ b/internal/core/dep/dep.go @@ -232,9 +232,10 @@ func (v *visitor) visit(n *adt.Vertex, top bool) (err error) { } }() - for _, x := range n.Conjuncts { + n.VisitLeafConjuncts(func(x adt.Conjunct) bool { v.markExpr(x.Env, x.Elem()) - } + return true + }) return nil } @@ -485,11 +486,12 @@ func hasLetParent(v *adt.Vertex) bool { // markConjuncts transitively marks all reference of the current node. func (c *visitor) markConjuncts(v *adt.Vertex) { - for _, x := range v.Conjuncts { + v.VisitLeafConjuncts(func(x adt.Conjunct) bool { // Use Elem instead of Expr to preserve the Comprehension to, in turn, // ensure an Environment is inserted for the Value clause. c.markExpr(x.Env, x.Elem()) - } + return true + }) } // markInternalResolvers marks dependencies for rootless nodes. As these @@ -506,9 +508,10 @@ func (c *visitor) markInternalResolvers(env *adt.Environment, r adt.Resolver, v // As lets have no path and we otherwise will not process them, we set // processing all to true. if c.marked != nil && hasLetParent(v) { - for _, x := range v.Conjuncts { + v.VisitLeafConjuncts(func(x adt.Conjunct) bool { c.marked.markExpr(x.Expr()) - } + return true + }) } c.markConjuncts(v) diff --git a/internal/core/dep/dep_test.go b/internal/core/dep/dep_test.go index 212bb02c049..3c39951d458 100644 --- a/internal/core/dep/dep_test.go +++ b/internal/core/dep/dep_test.go @@ -137,10 +137,11 @@ func TestX(t *testing.T) { ctxt := eval.NewContext(r, n) - for _, c := range n.Conjuncts { + n.VisitLeafConjuncts(func(c adt.Conjunct) bool { str := debug.NodeString(ctxt, c.Elem(), nil) t.Log(str) - } + return true + }) w := &strings.Builder{} fmt.Fprintln(w) diff --git a/internal/core/dep/mixed.go b/internal/core/dep/mixed.go index 05c25912c00..51ab7dbe282 100644 --- a/internal/core/dep/mixed.go +++ b/internal/core/dep/mixed.go @@ -29,12 +29,13 @@ import ( // and comprehension sources. func (v *visitor) dynamic(n *adt.Vertex, top bool) { found := false - for _, c := range n.Conjuncts { + n.VisitLeafConjuncts(func(c adt.Conjunct) bool { if v.marked[c.Expr()] { found = true - break + return false } - } + return true + }) if !found { return @@ -66,9 +67,10 @@ func (m marked) markExpr(x adt.Expr) { case nil: case *adt.Vertex: - for _, c := range x.Conjuncts { + x.VisitLeafConjuncts(func(c adt.Conjunct) bool { m.markExpr(c.Expr()) - } + return true + }) case *adt.BinaryExpr: if x.Op == adt.AndOp { diff --git a/internal/core/export/export.go b/internal/core/export/export.go index a007d7f2cb7..0e6d1b82161 100644 --- a/internal/core/export/export.go +++ b/internal/core/export/export.go @@ -183,10 +183,10 @@ func (e *exporter) toFile(v *adt.Vertex, x ast.Expr) *ast.File { if e.cfg.AddPackage { pkgName := "" pkg := &ast.Package{} - for _, c := range v.Conjuncts { + v.VisitLeafConjuncts(func(c adt.Conjunct) bool { f, _ := c.Source().(*ast.File) if f == nil { - continue + return true } if _, name, _ := internal.PackageInfo(f); name != "" { @@ -198,7 +198,8 @@ func (e *exporter) toFile(v *adt.Vertex, x ast.Expr) *ast.File { ast.AddComment(pkg, doc) } } - } + return true + }) if pkgName != "" { pkg.Name = ast.NewIdent(pkgName) @@ -389,9 +390,10 @@ func (e *exporter) markUsedFeatures(x adt.Expr) { switch x := n.(type) { case *adt.Vertex: if !x.IsData() { - for _, c := range x.Conjuncts { + x.VisitLeafConjuncts(func(c adt.Conjunct) bool { w.Elem(c.Elem()) - } + return true + }) } case *adt.DynamicReference: @@ -571,7 +573,8 @@ func (e *exporter) resolveLet(env *adt.Environment, x *adt.LetReference) ast.Exp return e.expr(env, x.X) } - return e.expr(ref.Conjuncts[0].EnvExpr()) + c, _ := ref.SingleConjunct() + return e.expr(c.EnvExpr()) case let.Expr == nil: label := e.uniqueLetIdent(x.Label, x.X) diff --git a/internal/core/export/expr.go b/internal/core/export/expr.go index 20106c21beb..317b5d7301f 100644 --- a/internal/core/export/expr.go +++ b/internal/core/export/expr.go @@ -79,12 +79,13 @@ func (e *exporter) expr(env *adt.Environment, v adt.Elem) (result ast.Expr) { } // Should this be the arcs label? a := []conjunct{} - for _, c := range x.Conjuncts { + x.VisitLeafConjuncts(func(c adt.Conjunct) bool { if c, ok := c.Elem().(*adt.Comprehension); ok && !c.DidResolve() { - continue + return true } a = append(a, conjunct{c, 0}) - } + return true + }) return e.mergeValues(adt.InvalidLabel, x, a, x.Conjuncts...) @@ -425,9 +426,10 @@ func (e *conjuncts) addExpr(env *adt.Environment, src *adt.Vertex, x adt.Elem, i switch { default: - for _, c := range v.Conjuncts { + v.VisitLeafConjuncts(func(c adt.Conjunct) bool { e.addExpr(c.Env, v, c.Elem(), false) - } + return true + }) case v.IsData(): e.structs = append(e.structs, v.Structs...) diff --git a/internal/core/export/extract.go b/internal/core/export/extract.go index 087da045fbf..30a7cb5b0be 100644 --- a/internal/core/export/extract.go +++ b/internal/core/export/extract.go @@ -71,10 +71,10 @@ func extractDocs(v *adt.Vertex, a []adt.Conjunct) (docs []*ast.CommentGroup) { newFields := []*ast.Field{} - for _, x := range p.Conjuncts { + p.VisitLeafConjuncts(func(x adt.Conjunct) bool { f, ok := x.Source().(*ast.Field) if !ok || !hasShorthandValue(f) { - continue + return true } nested := nestedField(f) @@ -88,7 +88,8 @@ func extractDocs(v *adt.Vertex, a []adt.Conjunct) (docs []*ast.CommentGroup) { } } } - } + return true + }) fields = newFields } @@ -144,9 +145,10 @@ func containsDoc(a []*ast.CommentGroup, cg *ast.CommentGroup) bool { } func ExtractFieldAttrs(v *adt.Vertex) (attrs []*ast.Attribute) { - for _, x := range v.Conjuncts { + v.VisitLeafConjuncts(func(x adt.Conjunct) bool { attrs = extractFieldAttrs(attrs, x.Field()) - } + return true + }) return attrs } diff --git a/internal/core/export/value.go b/internal/core/export/value.go index e54aefcbc7f..51617d9399e 100644 --- a/internal/core/export/value.go +++ b/internal/core/export/value.go @@ -102,11 +102,12 @@ func (e *exporter) vertex(n *adt.Vertex) (result ast.Expr) { if result == nil { // fall back to expression mode a := []ast.Expr{} - for _, c := range n.Conjuncts { + n.VisitLeafConjuncts(func(c adt.Conjunct) bool { if x := e.expr(c.Env, c.Elem()); x != dummyTop { a = append(a, x) } - } + return true + }) result = ast.NewBinExpr(token.AND, a...) } diff --git a/internal/core/runtime/extern.go b/internal/core/runtime/extern.go index 5cf9c0b3609..a7c8eefce68 100644 --- a/internal/core/runtime/extern.go +++ b/internal/core/runtime/extern.go @@ -74,9 +74,10 @@ func (r *Runtime) injectImplementations(b *build.Instance, v *adt.Vertex) (errs d.errs = errors.Append(d.errs, d.addFile(f)) } - for _, c := range v.Conjuncts { + v.VisitLeafConjuncts(func(c adt.Conjunct) bool { d.decorateConjunct(c.Elem(), v) - } + return true + }) return d.errs } diff --git a/internal/task/task.go b/internal/task/task.go index 1c2475fb6ab..9131baa7da7 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -23,6 +23,7 @@ import ( "cuelang.org/go/cue" "cuelang.org/go/cue/errors" "cuelang.org/go/cue/token" + "cuelang.org/go/internal/core/adt" "cuelang.org/go/internal/value" ) @@ -113,11 +114,12 @@ func (t *taskError) Position() token.Pos { func (t *taskError) InputPositions() (a []token.Pos) { _, nx := value.ToInternal(t.v) - for _, x := range nx.Conjuncts { + nx.VisitLeafConjuncts(func(x adt.Conjunct) bool { if src := x.Source(); src != nil { a = append(a, src.Pos()) } - } + return true + }) return a }