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: `