cue: bug fix: retain references with interleaved embedding

The implementation of closed structs did something
cute to retain the relative positioning of embedded
fields. This did not work, however.

Deletes the respective cuteness and adds tests.

Change-Id: Id109f6777bfb2070a4c058622dbba3ebdadc44ca
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3765
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/ast.go b/cue/ast.go
index f134276..263cee6 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -263,9 +263,11 @@
 					return v1.errf(x, "can only embed structs (found %v)", e.kind())
 				}
 				ret = mkBin(v1.ctx(), x.Pos(), opUnifyUnchecked, ret, e)
-				obj = &structLit{}
-				v1.object = obj
-				ret = mkBin(v1.ctx(), x.Pos(), opUnifyUnchecked, ret, obj)
+				// TODO: preserve order of embedded fields. We cannot split into
+				// separate unifications here, as recursive references point to
+				// obj and would have to be dereferenced and copied.
+				// Solving this is best done with a generic topological sort
+				// mechanism.
 
 			case *ast.Field, *ast.Alias:
 				v1.walk(e)
diff --git a/cue/ast_test.go b/cue/ast_test.go
index 1517236..83e3b7d 100644
--- a/cue/ast_test.go
+++ b/cue/ast_test.go
@@ -270,6 +270,27 @@
 			a: int @b('' ,b) // invalid
 		`,
 		out: "attribute missing ')':\n    test:2:16\nmissing ',' in struct literal:\n    test:3:3\n<0>{}",
+	}, {
+		in: `
+		a d: {
+			base
+			info :: {
+				...
+			}
+			Y: info.X
+		}
+
+		base :: {
+			info :: {...}
+		}
+
+		a <Name>: { info :: {
+			X: "foo"
+		}}
+		`,
+		out: `<0>{` +
+			`a: (<1>{d: <2>{info :: <3>{...}, Y: <2>.info.X}, <0>.base} & <4>{<>: <5>(Name: string)-><6>{info :: <7>C{X: "foo"}}, }), ` +
+			`base :: <8>C{info :: <9>{...}}}`,
 	}}
 	for _, tc := range testCases {
 		t.Run("", func(t *testing.T) {
diff --git a/cue/export_test.go b/cue/export_test.go
index f486c69..507b345 100644
--- a/cue/export_test.go
+++ b/cue/export_test.go
@@ -326,6 +326,8 @@
 			}
 		}`),
 	}, {
+		// TODO: positions of embedded structs is not preserved. Use some kind
+		// of topological sort to preserve order.
 		raw: true,
 		in: `{
 			emb :: {
@@ -358,13 +360,13 @@
 				a: 10
 			}
 			def :: {
-				emb
 				b: 2
+				emb
 			}
 			e :: {
-				f
 				<_>: <100
 				b:   int
+				f
 			}
 		}`),
 	}, {
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 9572a63..3e1aab0 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -2426,6 +2426,24 @@
 			`comp: <2>{txt: 2, regular: 4}, ` +
 			`select: <3>{opt: <4>.foo.opt, txt: 2, def :: 3, regular: 4, _hidden: 5}, ` +
 			`index: <5>{opt: <4>.foo["opt"], txt: 2, def :: _|_(<4>.foo["def"]:field "def" is a definition), regular: 4, _hidden: <4>.foo["_hidden"]}}`,
+	}, {
+		desc: "retain references with interleaved embedding",
+		in: `
+		a d: {
+			base
+			info :: {...}
+			Y: info.X
+		}
+
+		base :: {
+			info :: {...}
+		}
+
+		a <Name>: { info :: {
+			X: "foo"
+		}}
+		`,
+		out: `<0>{a: <1>{<>: <2>(Name: string)-><3>{info :: <4>C{X: "foo"}}, d: <5>C{info :: <6>C{X: "foo"}, Y: "foo"}}, base :: <7>C{info :: <8>{...}}}`,
 	}}
 	rewriteHelper(t, testCases, evalFull)
 }
@@ -2436,10 +2454,6 @@
 	// Don't remove. For debugging.
 	testCases := []testCase{{
 		in: `
-		Foo :: { a: int }
-
-		Foo
-		b: int
 		`,
 	}}
 	rewriteHelper(t, testCases, evalFull)