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