cue: move up processing of comprehension
This needs to happen before a comprehension is closed.
Fixes #153
Change-Id: I75bfc43a3c8f4844423bbb7135d4158e21879021
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3783
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/binop.go b/cue/binop.go
index 278e251..d2ebfb6 100644
--- a/cue/binop.go
+++ b/cue/binop.go
@@ -605,7 +605,7 @@
}
lambda := ctx.deref(l).(*lambdaExpr)
if finalize {
- lambda.value = wrapFinalize(lambda.value)
+ lambda.value = wrapFinalize(ctx, lambda.value)
}
return lambda, nil
}
@@ -705,10 +705,10 @@
}
w := b.v
if x.closeStatus.shouldFinalize() {
- w = wrapFinalize(w)
+ w = wrapFinalize(ctx, w)
}
if y.closeStatus.shouldFinalize() {
- v = wrapFinalize(v)
+ v = wrapFinalize(ctx, v)
}
v = mkBin(ctx, src.Pos(), op, w, v)
obj.arcs[i].v = v
diff --git a/cue/builtin.go b/cue/builtin.go
index 7c667b7..3613b96 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -204,7 +204,7 @@
if len(d) == 0 {
// TODO(manifest): This should not be unconditionally incomplete,
// but it requires results from comprehensions and all to have
- // some special status. Mayb this can be solved by having results
+ // some special status. Maybe this can be solved by having results
// of list comprehensions be open if they result from iterating over
// an open list or struct. This would actually be exactly what
// that means. The error here could then only add an incomplete
diff --git a/cue/eval.go b/cue/eval.go
index ba20f4a..01051f1 100644
--- a/cue/eval.go
+++ b/cue/eval.go
@@ -321,7 +321,7 @@
func (x *closeIfStruct) evalPartial(ctx *context) evaluated {
v := x.value.evalPartial(ctx)
- updateCloseStatus(v)
+ updateCloseStatus(ctx, v)
return v
}
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index b48d5e1..7f1d4c9 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -1476,13 +1476,20 @@
}
...
}
+
+ E :: A & {
+ for k, v in { f3: int } {
+ "\(k)": v
+ }
+ }
`,
out: `<0>{` +
+ `E :: _|_(int:field "f3" not allowed in closed struct), ` +
`A :: <1>C{f1: int, f2: int}, ` +
- `a: <2>C{f1: int, f2: int, f3: int}, ` +
- `B :: <3>C{f1: int}, ` +
- `C :: <4>C{f1: int}, ` +
- `D :: <5>{f1: int, ...}` +
+ `a: _|_(int:field "f3" not allowed in closed struct), ` +
+ `B :: <2>C{f1: int}, ` +
+ `C :: <3>C{f1: int}, ` +
+ `D :: <4>{f1: int, ...}` +
`}`,
}, {
desc: "incomplete comprehensions",
@@ -2573,6 +2580,30 @@
}
`,
out: `<0>{Workflow :: <1>C{jobs: <2>{<>: <3>(jobID: string)-><4>C{}, }, JobID :: or ([ <5>for k, _ in <6>.jobs yield <5>.k ])}, foo: <7>C{jobs: <8>{<>: <9>(jobID: string)-><10>C{}, foo: <11>C{}}, JobID :: "foo"}}`,
+ }, {
+ desc: "Issue #153",
+ in: `
+ Foo: {
+ listOfCloseds: [...Closed]
+ }
+
+ Closed :: {
+ a: int | *0
+ }
+
+ Junk: {
+ b: 2
+ }
+
+ Foo & {
+ listOfCloseds: [{
+ for k, v in Junk {
+ "\(k)": v
+ }
+ }]
+ }
+ `,
+ out: `<0>{<1>{listOfCloseds: [_|_(2:field "b" not allowed in closed struct)]}, Foo: <2>{listOfCloseds: []}, Closed :: <3>C{a: 0}, Junk: <4>{b: 2}}`,
}}
rewriteHelper(t, testCases, evalFull)
}
diff --git a/cue/rewrite.go b/cue/rewrite.go
index 3f81daf..f329f29 100644
--- a/cue/rewrite.go
+++ b/cue/rewrite.go
@@ -39,7 +39,7 @@
if v == x.value {
return x
}
- return wrapFinalize(v)
+ return wrapFinalize(ctx, v)
}
func (x *structLit) rewrite(ctx *context, fn rewriteFunc) value {
diff --git a/cue/value.go b/cue/value.go
index 495f4d5..17a8ec2 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -796,7 +796,7 @@
// it is safe to cache the result.
ctx.cycleErr = false
- updateCloseStatus(v)
+ updateCloseStatus(ctx, v)
x.arcs[i].cache = v
if doc != nil {
x.arcs[i].docs = &docNode{n: doc, left: x.arcs[i].docs}
@@ -862,7 +862,7 @@
orig := *x
orig.comprehensions = incomplete
orig.emit = nil
- n = binOp(ctx, src, opUnifyUnchecked, &orig, n)
+ n = binOp(ctx, src, opUnify, &orig, n)
default:
if len(comprehensions) == len(incomplete) {
@@ -984,13 +984,13 @@
value
}
-func wrapFinalize(v value) value {
+func wrapFinalize(ctx *context, v value) value {
if v.kind().isAnyOf(structKind | listKind) {
switch x := v.(type) {
case *top:
return v
case *structLit, *list, *disjunction:
- updateCloseStatus(v)
+ updateCloseStatus(ctx, v)
case *closeIfStruct:
return x
}
@@ -999,23 +999,27 @@
return v
}
-func updateCloseStatus(v value) {
+func updateCloseStatus(ctx *context, v value) {
switch x := v.(type) {
case *structLit:
- if x.closeStatus.shouldClose() {
- x.closeStatus = isClosed
+ y, err := x.expandFields(ctx)
+ if err == nil {
+ if x.closeStatus.shouldClose() {
+ x.closeStatus = isClosed
+ }
+ x.closeStatus |= shouldFinalize
+ y.closeStatus = x.closeStatus
}
- x.closeStatus |= shouldFinalize
case *disjunction:
for _, d := range x.values {
- d.val = wrapFinalize(d.val)
+ d.val = wrapFinalize(ctx, d.val)
}
case *list:
- wrapFinalize(x.elem)
+ wrapFinalize(ctx, x.elem)
if x.typ != nil {
- wrapFinalize(x.typ)
+ wrapFinalize(ctx, x.typ)
}
}
}