cue: tighten subsumes for disjunction
now we have explicitly marked defaults, we can tighten
subsumption
Change-Id: I8d689d74b8c11924453ceeefd22dcd19b73c6f8a
diff --git a/cue/subsume.go b/cue/subsume.go
index 008e5ae..fccc07c 100644
--- a/cue/subsume.go
+++ b/cue/subsume.go
@@ -210,18 +210,23 @@
// subsumes for disjunction is logically precise. However, just like with
// structural subsumption, it should not have to be called after evaluation.
func (x *disjunction) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+ // A disjunction subsumes another disjunction if all values of v are
+ // subsumed by the values of x, and default values in v are subsumed by the
+ // default values of x.
+ //
+ // This assumes that overlapping ranges in x are merged. If this is not the
+ // case, subsumes will return a false negative, which is allowed.
if d, ok := v.(*disjunction); ok {
- // TODO: the result of subsuming a value by a disjunction is logically
- // the subsumed value. However, since we have default semantics, we
- // should mark a resulting subsumed value as ambiguous if necessary.
- // Also, prove that the returned subsumed single value is always the
- // left-most matching value.
- return false
// at least one value in x should subsume each value in d.
+ outer:
for _, vd := range d.values {
- if !subsumes(ctx, x, vd.val, 0) {
- return false
+ // v is subsumed if any value in x subsumes v.
+ for _, vx := range x.values {
+ if (vx.marked || !vd.marked) && subsumes(ctx, vx.val, vd.val, 0) {
+ continue outer
+ }
}
+ return false
}
return true
}
diff --git a/cue/subsume_test.go b/cue/subsume_test.go
index 2781be5..b48f8ef 100644
--- a/cue/subsume_test.go
+++ b/cue/subsume_test.go
@@ -146,11 +146,8 @@
71: {subsumes: false, in: `a: {a:1, b:1}, b: {a:1}`},
72: {subsumes: false, in: `a: {s: { a:1} }, b: { s: {}}`},
- // Disjunction TODO: for now these two are false: unifying may result in
- // an ambiguity that we are currently not handling, so safer to not
- // unify.
- 84: {subsumes: false, in: `a: 1 | 2, b: 2 | 1`},
- 85: {subsumes: false, in: `a: 1 | 2, b: 1 | 2`},
+ 84: {subsumes: true, in: `a: 1 | 2, b: 2 | 1`},
+ 85: {subsumes: true, in: `a: 1 | 2, b: 1 | 2`},
86: {subsumes: true, in: `a: number, b: 2 | 1`},
87: {subsumes: true, in: `a: number, b: 2 | 1`},
@@ -254,6 +251,12 @@
146: {subsumes: false, in: ` a: "s \(d)m\(d) e", b: "s a e", d: _`},
147: {subsumes: true, in: ` a: 7080, b: 7080 | int`, mode: subChoose},
+
+ // Defaults
+ 150: {subsumes: false, in: `a: number | *1, b: number | *2`},
+ 151: {subsumes: true, in: `a: number | *2, b: number | *2`},
+ 152: {subsumes: true, in: `a: int | *float, b: int | *2.0`},
+ 154: {subsumes: true, in: `a: number, b: number | *2`},
}
re := regexp.MustCompile(`a: (.*).*b: ([^\n]*)`)