cue: bind reference as lookup in parent node
This is not fully correct, but is a trade-off between
two wrongs. See Issue #152.
The real underlying issue is an incorrect
forwarding/ copying of the root struct which is better
resolved as part of an overall performance overhaul.
Fixes #145
Change-Id: I7fa04d5f16ea6fb09686fb8a0c5864142635eb8c
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3581
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/testdata/script/eval_rec.txt b/cmd/cue/cmd/testdata/script/eval_rec.txt
new file mode 100644
index 0000000..5522df3
--- /dev/null
+++ b/cmd/cue/cmd/testdata/script/eval_rec.txt
@@ -0,0 +1,36 @@
+cue eval ./rec.cue -O
+cmp stdout expect-stdout
+-- rec.cue --
+Foo: {
+ foo?: Foo
+ bar: string
+ baz: bar + "2"
+}
+
+foo: Foo & {
+ foo: {
+ bar: "barNested"
+ }
+ bar: "barParent"
+}
+-- expect-stdout --
+Foo: {
+ foo?: {
+ bar: string
+ baz: bar + "2"
+ }
+ bar: string
+ baz: bar + "2"
+}
+foo: {
+ foo: {
+ foo?: {
+ bar: string
+ baz: bar + "2"
+ }
+ bar: "barNested"
+ baz: "barNested2"
+ }
+ bar: "barParent"
+ baz: "barParent2"
+}
diff --git a/cue/ast/astutil/resolve.go b/cue/ast/astutil/resolve.go
index d080688..b09d629 100644
--- a/cue/ast/astutil/resolve.go
+++ b/cue/ast/astutil/resolve.go
@@ -115,15 +115,17 @@
}
func (s *scope) lookup(name string) (obj, node ast.Node) {
- last := s
+ // TODO(#152): consider returning nil for obj if it is a reference to root.
+ // last := s
for s != nil {
if n, ok := s.index[name]; ok {
- if last.node == n {
- return nil, n
- }
+ // if last.node == n {
+ // return nil, n
+ // }
return s.node, n
}
- s, last = s.outer, s
+ // s, last = s.outer, s
+ s = s.outer
}
return nil, nil
}
diff --git a/cue/ast_test.go b/cue/ast_test.go
index 3550a1e..2b510ce 100644
--- a/cue/ast_test.go
+++ b/cue/ast_test.go
@@ -163,7 +163,9 @@
c: a // same node as b, as first node counts
d: a["a"]
`,
- out: `<0>{a: (<1>{a: <2>{b: <2>}} & <3>{b: <3>.a}), b: <0>.a.a, c: <0>.a, d: <0>.a["a"]}`,
+ out: `<0>{a: (<1>{a: <2>{b: <1>.a}} & <3>{b: <0>.a.a}), b: <0>.a.a, c: <0>.a, d: <0>.a["a"]}`,
+ // TODO(#152): should be
+ // out: `<0>{a: (<1>{a: <2>{b: <2>}} & <3>{b: <3>.a}), b: <0>.a.a, c: <0>.a, d: <0>.a["a"]}`,
}, {
// bunch of aliases
in: `
diff --git a/cue/export.go b/cue/export.go
index 72b25d5..9f55532 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -240,9 +240,9 @@
return false
}
-func (p *exporter) recExpr(v value, e evaluated) ast.Expr {
+func (p *exporter) recExpr(v value, e evaluated, optional bool) ast.Expr {
m := p.ctx.manifest(e)
- if !p.isComplete(m, false) && !p.mode.concrete {
+ if optional || (!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).
@@ -544,7 +544,7 @@
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))
+ list.Elts = append(list.Elts, p.recExpr(a.v, e, false))
}
}
max := maxNum(x.len)
@@ -673,7 +673,7 @@
if !doEval(p.mode) {
f.Value = p.expr(a.v)
} else {
- f.Value = p.recExpr(a.v, x.at(p.ctx, i))
+ f.Value = p.recExpr(a.v, x.at(p.ctx, i), a.optional)
}
p.inDef = oldInDef
if a.attrs != nil && !p.mode.omitAttrs {
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 4a42ca2..b5db22d 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -664,7 +664,9 @@
c: r["a"]
}
`,
- out: `<0>{r: <1>{a?: 3, b: <2>.a, c: <2>["a"]}}`,
+ out: `<0>{r: <1>{a?: 3, b: <2>.a, c: <3>.r["a"]}}`,
+ // TODO(#152): should be
+ // out: `<0>{r: <1>{a?: 3, b: <2>.a, c: <2>["a"]}}`,
}, {
desc: "bounds",
in: `
@@ -1385,7 +1387,9 @@
w: v & { b: 100 }
wp: v & { b: 100 }
`,
- out: `<0>{a: <1>{b: int}, c: <2>{b: 100, d: (<3>.a.b + 3)}, x: <4>{b: int, c: (<5>.b + 5)}, y: <6>{b: 100, c: 105}, v: <7>{b: int, c: (<8>.b + 5)}, w: <9>{b: 100, c: 105}, wp: <10>{b: 100, c: 105}}`,
+ out: `<0>{a: <1>{b: int}, c: <2>{b: 100, d: (<3>.a.b + 3)}, x: <4>{b: int, c: (<5>.b + 5)}, y: <6>{b: 100, c: 105}, v: <7>{b: int, c: (<3>.v.b + 5)}, w: <8>{b: 100, c: (<3>.v.b + 5)}, wp: <9>{b: 100, c: (<3>.v.b + 5)}}`,
+ // TODO(#152): should be
+ // out: `<0>{a: <1>{b: int}, c: <2>{b: 100, d: (<3>.a.b + 3)}, x: <4>{b: int, c: (<5>.b + 5)}, y: <6>{b: 100, c: 105}, v: <7>{b: int, c: (<8>.b + 5)}, w: <9>{b: 100, c: 105}, wp: <10>{b: 100, c: 105}}`,
}, {
desc: "references from template to concrete",
in: `