diff --git a/cue/subsume.go b/cue/subsume.go index 83cd174e2..d9a2257e9 100644 --- a/cue/subsume.go +++ b/cue/subsume.go @@ -87,7 +87,7 @@ func (x *structLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { if o, ok := v.(*structLit); ok { // TODO: consider what to do with templates. Perhaps we should always // do subsumption on fully evaluated structs. - if len(x.comprehensions) > 0 { //|| x.template != nil { + if len(x.comprehensions) > 0 || x.optionals != nil { return false } if x.emit != nil { @@ -114,6 +114,9 @@ func (x *structLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { } // For closed structs, all arcs in b must exist in a. if x.closeStatus.shouldClose() { + if !o.closeStatus.shouldClose() { + return false + } for _, b := range o.arcs { a := x.lookup(ctx, b.feature) if a.val() == nil { diff --git a/cue/subsume_test.go b/cue/subsume_test.go index b95dd72cb..decf340c7 100644 --- a/cue/subsume_test.go +++ b/cue/subsume_test.go @@ -364,8 +364,14 @@ func TestSubsume(t *testing.T) { // The one exception of the rule: there is no value of foo that can be // added to b which would cause the unification of a and b to fail. + // So an optional field with a value of top is equivalent to not + // defining one at all. 420: {subsumes: true, in: `a: {foo?: _}, b: {}`}, + 430: {subsumes: false, in: `a: {[_]: 4}, b: {[_]: int}`}, + // TODO: handle optionals. + 431: {subsumes: false, in: `a: {[_]: int}, b: {[_]: 2}`}, + // Lists 506: {subsumes: true, in: `a: [], b: [] `}, 507: {subsumes: true, in: `a: [1], b: [1] `}, @@ -379,11 +385,13 @@ func TestSubsume(t *testing.T) { // Closed structs. 600: {subsumes: false, in: `a: close({}), b: {a: 1}`}, - 601: {subsumes: true, in: `a: close({a: 1}), b: {a: 1}`}, + 601: {subsumes: false, in: `a: close({a: 1}), b: {a: 1}`}, 602: {subsumes: false, in: `a: close({a: 1, b: 1}), b: {a: 1}`}, 603: {subsumes: false, in: `a: {a: 1}, b: close({})`}, 604: {subsumes: true, in: `a: {a: 1}, b: close({a: 1})`}, 605: {subsumes: true, in: `a: {a: 1}, b: close({a: 1, b: 1})`}, + 606: {subsumes: true, in: `a: close({b?: 1}), b: close({b: 1})`}, + 607: {subsumes: false, in: `a: close({b: 1}), b: close({b?: 1})`}, // Definitions are not values. 610: {subsumes: false, in: `a: {a :: 1}, b: {a: 1}`},