cue: allow valid reference cycles in export
Fixes #58
Change-Id: Ic2e4d1f02c002a678d72f6ff45b207857f8971d2
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3440
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/ast.go b/cue/ast.go
index 8f72cb5..07c4d1a 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -151,7 +151,7 @@
for _, a := range r.arcs {
if a.feature == label {
return &selectorExpr{newExpr(n),
- &nodeRef{baseValue: newExpr(n), node: r}, label}
+ &nodeRef{baseValue: newExpr(n), node: r, label: label}, label}
}
}
if v.inSelector > 0 {
@@ -486,12 +486,10 @@
ret = &nodeRef{baseValue: newExpr(n), node: n2}
ret = &selectorExpr{newExpr(n), ret, f}
} else {
+ // Package or direct ancestor node.
n2 := v.mapScope(n.Node)
- ref := &nodeRef{baseValue: newExpr(n), node: n2}
+ ref := &nodeRef{baseValue: newExpr(n), node: n2, label: f}
ret = ref
- if inst := v.ctx().getImportFromNode(n2); inst != nil {
- ref.short = f
- }
}
case *ast.BottomLit:
diff --git a/cue/binop.go b/cue/binop.go
index 7a54bdb..0bec239 100644
--- a/cue/binop.go
+++ b/cue/binop.go
@@ -1137,7 +1137,9 @@
max, ok := n.(*numLit)
if !ok || len(xa) < max.intValue(ctx) {
src := mkBin(ctx, src.Pos(), op, x.typ, y.typ)
- typ = binOp(ctx, src, op, x.typ.(evaluated), y.typ.(evaluated))
+ xt := x.typ.evalPartial(ctx)
+ yt := y.typ.evalPartial(ctx)
+ typ = binOp(ctx, src, op, xt, yt)
if isBottom(typ) {
return ctx.mkErr(src, "conflicting list element types: %v", typ)
}
diff --git a/cue/copy.go b/cue/copy.go
index 879758a..335a29d 100644
--- a/cue/copy.go
+++ b/cue/copy.go
@@ -26,7 +26,7 @@
if node == x.node {
return x, false
}
- return &nodeRef{x.baseValue, node, x.short}, false
+ return &nodeRef{x.baseValue, node, x.label}, false
case *structLit:
arcs := make(arcs, len(x.arcs))
diff --git a/cue/eval.go b/cue/eval.go
index 9874a9b..7a3dea9 100644
--- a/cue/eval.go
+++ b/cue/eval.go
@@ -248,15 +248,14 @@
defer func() { ctx.debugPrint("result:", result) }()
}
n := x.len.evalPartial(ctx)
- t := x.typ.evalPartial(ctx)
- if err := firstBottom(n, t); err != nil {
- return err
+ if isBottom(n) {
+ return n
}
s := x.elem.evalPartial(ctx).(*structLit)
- if s == x.elem && n == x.len && t == x.typ {
+ if s == x.elem && n == x.len {
return x
}
- return &list{x.baseValue, s, t, n}
+ return &list{x.baseValue, s, x.typ, n}
}
func (x *listComprehension) evalPartial(ctx *context) evaluated {
diff --git a/cue/export.go b/cue/export.go
index 3d32ba8..72b25d5 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -226,18 +226,50 @@
return s
}
+func (p *exporter) isComplete(v value, all bool) bool {
+ switch x := v.(type) {
+ case *numLit, *stringLit, *bytesLit, *nullLit, *boolLit:
+ return true
+ case *list:
+ return true
+ case *structLit:
+ return !all
+ case *bottom:
+ return !isIncomplete(x)
+ }
+ return false
+}
+
+func (p *exporter) recExpr(v value, e evaluated) ast.Expr {
+ m := p.ctx.manifest(e)
+ if !p.isComplete(m, false) && !p.mode.concrete {
+ // TODO: do something more principled than this hack.
+ // This likely requires disjunctions to keep track of original
+ // values (so using arcs instead of values).
+ p := &exporter{p.ctx, options{concrete: true, raw: true}, p.stack, p.top, p.imports, p.inDef}
+ if _, ok := v.(*disjunction); ok || isBottom(e) {
+ return p.expr(v)
+ }
+ return p.expr(e)
+ }
+ return p.expr(e)
+}
+
func (p *exporter) expr(v value) ast.Expr {
// TODO: use the raw expression for convert incomplete errors downstream
// as well.
- if doEval(p.mode) {
+ if doEval(p.mode) || p.mode.concrete {
e := v.evalPartial(p.ctx)
x := p.ctx.manifest(e)
- if isIncomplete(x) {
+
+ if !p.isComplete(x, true) {
if isBottom(e) {
p = &exporter{p.ctx, options{raw: true}, p.stack, p.top, p.imports, p.inDef}
return p.expr(v)
}
- v = e
+ if doEval(p.mode) {
+ v = e
+ }
} else {
v = x
}
@@ -258,15 +290,18 @@
return ast.NewSel(ast.NewIdent(short), x.Name)
case *nodeRef:
- if x.short == 0 {
+ if x.label == 0 {
+ // NOTE: this nodeRef is used within a selector.
return nil
}
- inst := p.ctx.getImportFromNode(x.node)
- if inst == nil {
- return nil // should not happen!
+ short := p.ctx.labelStr(x.label)
+
+ if inst := p.ctx.getImportFromNode(x.node); inst != nil {
+ return ast.NewIdent(p.shortName(inst, short, inst.ImportPath))
}
- short := p.ctx.labelStr(x.short)
- return ast.NewIdent(p.shortName(inst, short, inst.ImportPath))
+
+ // fix shadowed label.
+ return ast.NewIdent(short)
case *selectorExpr:
n := p.expr(x.x)
@@ -504,8 +539,13 @@
case *list:
list := &ast.ListLit{}
var expr ast.Expr = list
- for _, e := range x.elem.arcs {
- list.Elts = append(list.Elts, p.expr(e.v))
+ for i, a := range x.elem.arcs {
+ if !doEval(p.mode) {
+ list.Elts = append(list.Elts, p.expr(a.v))
+ } else {
+ e := x.elem.at(p.ctx, i)
+ list.Elts = append(list.Elts, p.recExpr(a.v, e))
+ }
}
max := maxNum(x.len)
num, ok := max.(*numLit)
@@ -576,6 +616,7 @@
return nil, err
}
}
+
for _, a := range x.arcs {
p.stack = append(p.stack, remap{
key: x,
@@ -632,13 +673,7 @@
if !doEval(p.mode) {
f.Value = p.expr(a.v)
} else {
- e := x.at(p.ctx, i)
- if v := p.ctx.manifest(e); isIncomplete(v) && !p.mode.concrete && isBottom(e) {
- p := &exporter{p.ctx, options{raw: true}, p.stack, p.top, p.imports, p.inDef}
- f.Value = p.expr(a.v)
- } else {
- f.Value = p.expr(e)
- }
+ f.Value = p.recExpr(a.v, x.at(p.ctx, i))
}
p.inDef = oldInDef
if a.attrs != nil && !p.mode.omitAttrs {
diff --git a/cue/export_test.go b/cue/export_test.go
index b1c179e..80241aa 100644
--- a/cue/export_test.go
+++ b/cue/export_test.go
@@ -198,8 +198,8 @@
}`,
out: unindent(`
{
- a: 1 | 2
- b: [1 | 2]
+ a: 1 | 2 | *_|_
+ b: [1 | 2 | *_|_]
}`),
}, {
raw: true,
@@ -447,6 +447,102 @@
}
}`),
+ }, {
+ raw: true,
+ eval: true,
+ in: `{
+ Foo :: {
+ Bar :: Foo | string
+ }
+ }`,
+ out: unindent(`
+ {
+ Foo :: {
+ Bar :: Foo | string
+ }
+ }`),
+ }, {
+ raw: true,
+ eval: true,
+ in: `{
+ FindInMap :: {
+ "Fn::FindInMap" :: [string | FindInMap]
+ }
+ a: [...string]
+ }`,
+ out: unindent(`
+ {
+ FindInMap :: {
+ "Fn::FindInMap" :: [string | FindInMap]
+ }
+ a: []
+ }`)}, {
+ raw: true,
+ eval: true,
+ in: `{
+ And :: {
+ "Fn::And": [...(3 | And)]
+ }
+ Ands: And & {
+ "Fn::And" : [_]
+ }
+ }`,
+ out: unindent(`
+ {
+ And :: {
+ "Fn::And": []
+ }
+ Ands "Fn::And": [3 | And]
+ }`),
+ }, {
+ raw: true,
+ eval: true,
+ in: `{
+ Foo :: {
+ sgl: Bar
+ ref: null | Foo
+ ext: Bar | null
+ ref: null | Foo
+ ref2: null | Foo.sgl
+ ...
+ }
+ Foo :: {
+ Foo: 2
+ ...
+ }
+ Bar :: string
+ }`,
+ out: unindent(`
+ {
+ FOO = Foo
+ FOO658221 = Foo
+ Foo :: {
+ Foo: 2
+ sgl: string
+ ref: null | {
+ Foo: 2
+ sgl: Bar
+ ref: (null | FOO) & (null | FOO)
+ ext: Bar | null
+ ref2: null | FOO.sgl
+ }
+ ext: Bar | null
+ ref2: null | FOO658221.sgl
+ }
+ Bar :: string
+ }`),
+ }, {
+ raw: true,
+ eval: true,
+ in: `{
+ A: [uint]
+ B: A & ([10] | [192])
+ }`,
+ out: unindent(`
+ {
+ A: [>=0]
+ B: [10] | [192]
+ }`),
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
diff --git a/cue/rewrite_test.go b/cue/rewrite_test.go
index b9738c1..0e1fa08 100644
--- a/cue/rewrite_test.go
+++ b/cue/rewrite_test.go
@@ -80,7 +80,7 @@
case *list:
elm := rewriteRec(ctx, x.elem, x.elem, m).(*structLit)
len := rewriteRec(ctx, x.len, x.len.(evaluated), m)
- typ := rewriteRec(ctx, x.typ, x.typ.(evaluated), m)
+ typ := rewriteRec(ctx, x.typ, x.typ.evalPartial(ctx), m)
return &list{x.baseValue, elm, typ, len}
default:
return eval
diff --git a/cue/value.go b/cue/value.go
index 8b3ebfe..d1a1b1d 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -565,7 +565,7 @@
return arc{}
}
}
- return arc{cache: x.typ.(evaluated), v: x.typ}
+ return arc{cache: x.typ.evalPartial(ctx), v: x.typ}
}
func (x *list) isOpen() bool {
@@ -966,7 +966,7 @@
type nodeRef struct {
baseValue
node scope
- short label // only for packages, otherwise 0
+ label label // for direct ancestor nodes
}
func (x *nodeRef) kind() kind {
diff --git a/doc/cmd/cue.md b/doc/cmd/cue.md
index 86d32a2..58677ba 100644
--- a/doc/cmd/cue.md
+++ b/doc/cmd/cue.md
@@ -260,7 +260,6 @@
```
--dryrun force overwriting existing files
--files force overwriting existing files
- --fix string apply given fix
-f, --force force overwriting existing files
-h, --help help for import
--list concatenate multiple objects into a list
diff --git a/doc/tutorial/basics/defaults.md b/doc/tutorial/basics/defaults.md
index 99d9e48..c348c25 100644
--- a/doc/tutorial/basics/defaults.md
+++ b/doc/tutorial/basics/defaults.md
@@ -29,5 +29,5 @@
`$ cue eval defaults.cue`
```
replicas: 1
-protocol: "tcp" | "udp"
+protocol: "tcp" | "udp" | *_|_
```
\ No newline at end of file
diff --git a/doc/tutorial/basics/lists.md b/doc/tutorial/basics/lists.md
index 5bde4be..1bde51e 100644
--- a/doc/tutorial/basics/lists.md
+++ b/doc/tutorial/basics/lists.md
@@ -39,7 +39,7 @@
`$ cue eval -i lists.cue`
```
IP: [uint8, uint8, uint8, uint8]
-PrivateIP: [10, uint8, uint8, uint8] | [192, 168, uint8, uint8] | [172, >=16 & <=32, uint8, uint8]
+PrivateIP: [10, uint8, uint8, uint8] | [192, 168, uint8, uint8] | [172, >=16 & <=32 & uint8, uint8, uint8]
myIP: [10, 2, 3, 4]
yourIP: _|_ // empty disjunction: [((10 & (int & >=0 & int & <=255)) & 11),((int & >=0 & int & <=255) & 1),((int & >=0 & int & <=255) & 2),((int & >=0 & int & <=255) & 3)]
```
\ No newline at end of file
diff --git a/doc/tutorial/kubernetes/testdata/manual.out b/doc/tutorial/kubernetes/testdata/manual.out
index e4a2183..58b85a0 100644
--- a/doc/tutorial/kubernetes/testdata/manual.out
+++ b/doc/tutorial/kubernetes/testdata/manual.out
@@ -33,14 +33,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -83,14 +83,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -250,14 +250,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -419,14 +419,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -586,14 +586,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -753,14 +753,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -922,14 +922,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -1089,14 +1089,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -1258,14 +1258,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -1308,14 +1308,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -1461,14 +1461,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -1787,14 +1787,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -2009,14 +2009,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -2206,14 +2206,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -2377,14 +2377,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -2564,14 +2564,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -2614,14 +2614,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -2866,14 +2866,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -3115,14 +3115,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -3348,14 +3348,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -3580,14 +3580,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -3816,14 +3816,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -4052,14 +4052,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -4246,14 +4246,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -4305,14 +4305,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -4562,14 +4562,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -4788,14 +4788,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -5024,14 +5024,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -5716,14 +5716,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -5766,14 +5766,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -6057,14 +6057,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -6243,14 +6243,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}
@@ -6858,14 +6858,14 @@
template: {
spec: _|_ // undefined field "volume"
metadata: {
- labels: label
+ labels: X.label
}
}
}
metadata: {
- name: name
+ name: X.name
labels: {
- component: label.component
+ component: X.label.component
}
}
}