cue: exempt aliases and embeddings from recursive closing

See comments in spec.

Issue #40.

Change-Id: I7e17864c649ef3576d6992eb5a2b530c76f5496e
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3043
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/cue/ast.go b/cue/ast.go
index 3bf6798..b8163ee 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -244,7 +244,10 @@
 				v1.object.addTemplate(v.ctx(), token.NoPos, template)
 
 			case *ast.EmbedDecl:
+				old := v.ctx().inDefinition
+				v.ctx().inDefinition = 0
 				e := v1.walk(x.Expr)
+				v.ctx().inDefinition = old
 				if isBottom(e) {
 					return e
 				}
@@ -498,7 +501,10 @@
 		}
 
 		if a, ok := n.Node.(*ast.Alias); ok {
+			old := v.ctx().inDefinition
+			v.ctx().inDefinition = 0
 			ret = v.walk(a.Expr)
+			v.ctx().inDefinition = old
 			break
 		}
 
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 9a0f471..9839e49 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -1230,6 +1230,31 @@
 			`ctcp: <16>C{x: int}, ` +
 			`ctct: <17>{x: int, ...}}`,
 	}, {
+		desc: "excluded embedding from closing",
+		in: `
+		S :: {
+			a: { c: int }
+			{
+				c: { d: int }
+			}
+			B = { open: int }
+			b: B
+		}
+		V: S & {
+			c e: int
+			b extra: int
+		}
+		`,
+		out: `<0>{` +
+			`S :: <1>C{` +
+			`a: <2>C{c: int}, ` +
+			`c: <3>{d: int}, ` +
+			`b: <4>{open: int}}, ` +
+			`V: <5>C{` +
+			`a: <6>C{c: int}, ` +
+			`c: <7>{d: int, e: int}, ` +
+			`b: <8>{open: int, extra: int}}}`,
+	}, {
 		desc: "closing with failed optional",
 		in: `
 		k1 :: {a: int, b?: int} & {a: int} // closed({a: int})
diff --git a/doc/ref/spec.md b/doc/ref/spec.md
index ca04260..6b694f1 100644
--- a/doc/ref/spec.md
+++ b/doc/ref/spec.md
@@ -1198,9 +1198,32 @@
 It is illegal to have a regular field and a definition with the same name
 within the same struct.
 Literal structs that are part of a definition's value are implicitly closed.
+This excludes literals structs in embeddings and aliases.
 An ellipsis `...` in such literal structs keeps them open,
 as it defines `_` for all labels.
+<!--
+Excluding embeddings from recursive closing allows comprehensions to be
+interpreted as embeddings without some exception. For instance,
+    if x > 2 {
+        foo: string
+    }
+should not cause any failure. It is also consistent with embeddings being
+opened when included in a closed struct.
 
+Finally, excluding embeddings from recursive closing allows for
+a mechanism to not recursively close, without needing an additional language
+construct, such as a triple colon or something else:
+foo :: {
+    {
+        // not recursively closed
+    }
+    ... // include this to not close outer struct
+}
+
+Including aliases from this exclusion, which are more a separate definition
+than embedding seems sensible, and allows for an easy mechanism to avoid
+closing, aside from embedding.
+-->
 
 ```
 // MyStruct is closed and as there is no expression label or `...`, we know