cue: add Subsume to replace Subsumes

- API allows distinction between Final and non-Final
- return error

Change-Id: I98b86fee753f18e620d4f17e6887decdb66d5b08
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/4820
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/builtin.go b/cue/builtin.go
index 4b4a3c5..3fdf53c 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -231,7 +231,7 @@
 	return x
 }
 
-func (x *builtin) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *builtin) subsumesImpl(s *subsumer, v value) bool {
 	if y, ok := v.(*builtin); ok {
 		return x == y
 	}
@@ -295,6 +295,9 @@
 			if err, ok := err.Err.(*marshalError); ok && err.b != nil {
 				ret = err.b
 			}
+		case *valueError:
+			ret = err.err
+			ret = ctx.mkErr(src, x, ret, "error in call to %s: %v", x.name(ctx), err)
 		default:
 			// TODO: store the underlying error explicitly
 			ret = ctx.mkErr(src, x, "error in call to %s: %v", x.name(ctx), err)
diff --git a/cue/errors.go b/cue/errors.go
index f9c05ab..bf6739f 100644
--- a/cue/errors.go
+++ b/cue/errors.go
@@ -159,10 +159,17 @@
 func (x *bottom) kind() kind { return bottomKind }
 
 func (x *bottom) Positions(ctx *context) []token.Pos {
+	var a []token.Pos
 	if x.index != nil { // TODO: remove check?
-		return appendPositions(ctx, nil, x.pos)
+		a = appendPositions(ctx, nil, x.pos)
 	}
-	return nil
+	if w := x.wrapped; w != nil {
+		a = append(a, w.Positions(ctx)...)
+	}
+	for _, sub := range x.sub {
+		a = append(a, sub.Positions(ctx)...)
+	}
+	return a
 }
 
 func appendPositions(ctx *context, pos []token.Pos, src source) []token.Pos {
diff --git a/cue/examples_test.go b/cue/examples_test.go
index be46bbd..7f48b96 100644
--- a/cue/examples_test.go
+++ b/cue/examples_test.go
@@ -66,7 +66,7 @@
 	// json: cannot unmarshal string into Go struct field ab.B of type int
 }
 
-func ExampleValue_Subsumes() {
+func ExampleValue_Subsume() {
 	// Check compatibility of successive APIs.
 	var r cue.Runtime
 
@@ -105,13 +105,13 @@
 	}
 
 	// Is V2 backwards compatible with V1? In other words, does V2 subsume V1?
-	pass := v2.Value.Subsumes(v1.Value)
-	fmt.Println("V2 is backwards compatible with V1:", pass)
+	err = v2.Value.Subsume(v1.Value)
+	fmt.Println("V2 is backwards compatible with V1:", err)
 
-	pass = v3.Value.Subsumes(v2.Value)
-	fmt.Println("V3 is backwards compatible with V2:", pass)
+	err = v3.Value.Subsume(v2.Value)
+	fmt.Println("V3 is backwards compatible with V2:", err)
 
 	// Output:
-	// V2 is backwards compatible with V1: true
-	// V3 is backwards compatible with V2: false
+	// V2 is backwards compatible with V1: <nil>
+	// V3 is backwards compatible with V2: conflicting values <=150 and string (mismatched types number and string)
 }
diff --git a/cue/strip.go b/cue/strip.go
index 5d82834..76c3ca6 100644
--- a/cue/strip.go
+++ b/cue/strip.go
@@ -124,6 +124,6 @@
 	return &mergedValues{x.baseValue, vs}
 }
 
-func (x *mergedValues) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *mergedValues) subsumesImpl(s *subsumer, v value) bool {
 	return false
 }
diff --git a/cue/subsume.go b/cue/subsume.go
index 5cc134f..b4bc2b7 100644
--- a/cue/subsume.go
+++ b/cue/subsume.go
@@ -16,8 +16,44 @@
 
 import (
 	"bytes"
+
+	"cuelang.org/go/cue/token"
 )
 
+// TODO: it probably makes sense to have only two modes left: subsuming a schema
+// and subsuming a final value.
+
+func subsumes(v, w Value, mode subsumeMode) error {
+	ctx := v.ctx()
+	gt := v.eval(ctx)
+	lt := w.eval(ctx)
+	s := subsumer{ctx: ctx, mode: mode}
+	if !s.subsumes(gt, lt) {
+		var b *bottom
+		var ok bool
+		src := binSrc(token.NoPos, opUnify, gt, lt)
+		src2 := src
+		if s.gt != nil && s.lt != nil {
+			src := binSrc(token.NoPos, opUnify, s.gt, s.lt)
+			b, ok = binOp(ctx, src, opUnify, s.gt, s.lt).(*bottom)
+		}
+		if !ok {
+			b = ctx.mkErr(src, "value not an instance")
+		}
+		b = ctx.mkErr(src2, b, "%v", b)
+		return w.toErr(b)
+	}
+	return nil
+}
+
+type subsumer struct {
+	ctx  *context
+	mode subsumeMode
+
+	// recorded values where an error occurred.
+	gt, lt evaluated
+}
+
 type subsumeMode int
 
 const (
@@ -27,13 +63,18 @@
 
 	// subNoOptional ignores optional fields for the purpose of subsumption.
 	// This option is predominantly intended for implementing equality checks.
+	// TODO: may be unnecessary now subFinal is available.
 	subNoOptional
+
+	// the subsumed value is final
+	subFinal
 )
 
 // TODO: improve upon this highly inefficient implementation. There should
 // be a dedicated equal function once the dust settles.
 func equals(c *context, x, y value) bool {
-	return subsumes(c, x, y, subNoOptional) && subsumes(c, y, x, subNoOptional)
+	s := subsumer{ctx: c, mode: subNoOptional}
+	return s.subsumes(x, y) && s.subsumes(y, x)
 }
 
 // subsumes checks gt subsumes lt. If any of the values contains references or
@@ -41,9 +82,10 @@
 // subsumption is conservative; it may return false when a guarantee for
 // subsumption could be proven. For concreted values it returns the exact
 // relation. It never returns a false positive.
-func subsumes(ctx *context, gt, lt value, mode subsumeMode) bool {
-	var v, w value
-	if mode&subChoose == 0 {
+func (s *subsumer) subsumes(gt, lt value) (result bool) {
+	ctx := s.ctx
+	var v, w evaluated
+	if s.mode&subChoose == 0 {
 		v = gt.evalPartial(ctx)
 		w = lt.evalPartial(ctx)
 	} else {
@@ -62,7 +104,7 @@
 	case b&^(a&b) != 0:
 		// a does not have strictly more bits. This implies any ground kind
 		// subsuming a non-ground type.
-		return false
+		goto exit
 		// TODO: consider not supporting references.
 		// case (a|b)&(referenceKind) != 0:
 		// 	// no resolution if references are in play.
@@ -72,17 +114,17 @@
 	case *unification:
 		if _, ok := gt.(*unification); !ok {
 			for _, x := range lt.values {
-				if subsumes(ctx, gt, x, mode) {
+				if s.subsumes(gt, x) {
 					return true
 				}
 			}
-			return false
+			goto exit
 		}
 
 	case *disjunction:
 		if _, ok := gt.(*disjunction); !ok {
 			for _, x := range lt.values {
-				if !subsumes(ctx, gt, x.val, mode) {
+				if !s.subsumes(gt, x.val) {
 					return false
 				}
 			}
@@ -90,22 +132,41 @@
 		}
 	}
 
-	return gt.subsumesImpl(ctx, lt, mode)
+	result = gt.subsumesImpl(s, lt)
+exit:
+	if !result && s.gt == nil && s.lt == nil {
+		s.gt = v
+		s.lt = w
+	}
+	return result
 }
 
-func (x *structLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
-	ignoreOptional := mode&subNoOptional != 0
+func (x *structLit) subsumesImpl(s *subsumer, v value) bool {
+	ctx := s.ctx
+	ignoreOptional := s.mode&subNoOptional != 0
 	if o, ok := v.(*structLit); ok {
-		// TODO: consider what to do with templates. Perhaps we should always
-		// do subsumption on fully evaluated structs.
 		if x.optionals != nil && !ignoreOptional {
-			return false
+			if s.mode&subFinal == 0 {
+				// TODO: also cross-validate optional fields in the schema case.
+				return false
+			}
+			for _, b := range o.arcs {
+				if b.optional || b.definition {
+					continue
+				}
+				name := ctx.labelStr(b.feature)
+				arg := &stringLit{x.baseValue, name, nil}
+				u, _ := x.optionals.constraint(ctx, arg)
+				if u != nil && !s.subsumes(u, b.v) {
+					return false
+				}
+			}
 		}
 		if len(x.comprehensions) > 0 {
 			return false
 		}
 		if x.emit != nil {
-			if o.emit == nil || !subsumes(ctx, x.emit, o.emit, mode) {
+			if o.emit == nil || !s.subsumes(x.emit, o.emit) {
 				return false
 			}
 		}
@@ -118,29 +179,50 @@
 			b := o.lookup(ctx, a.feature)
 			if !a.optional && b.optional {
 				return false
-			} else if a.definition != b.definition {
-				return false
 			} else if b.val() == nil {
+				if a.definition && s.mode&subFinal != 0 {
+					continue
+				}
+				// if o is closed, the field is implicitly defined as _|_ and
+				// thus subsumed. Technically, this is even true if a is not
+				// optional, but in that case it means that o is invalid, so
+				// return false regardless
+				if a.optional && (o.closeStatus.shouldClose() || s.mode&subFinal != 0) {
+					continue
+				}
 				// If field a is optional and has value top, neither the
 				// omission of the field nor the field defined with any value
 				// may cause unification to fail.
-				return a.optional && isTop(a.v)
-			} else if !subsumes(ctx, a.v, b.val(), mode) {
+				if a.optional && isTop(a.v) {
+					continue
+				}
+				s.gt = a.val()
+				return false
+			} else if a.definition != b.definition {
+				return false
+			} else if !s.subsumes(a.v, b.val()) {
 				return false
 			}
 		}
 		// For closed structs, all arcs in b must exist in a.
 		if x.closeStatus.shouldClose() {
-			if !ignoreOptional && !o.closeStatus.shouldClose() {
+			if !ignoreOptional && !o.closeStatus.shouldClose() && s.mode&subFinal == 0 {
 				return false
 			}
+			ignoreOptional = ignoreOptional || s.mode&subFinal != 0
 			for _, b := range o.arcs {
 				if ignoreOptional && b.optional {
 					continue
 				}
 				a := x.lookup(ctx, b.feature)
 				if a.val() == nil {
-					return false
+					name := ctx.labelStr(b.feature)
+					arg := &stringLit{x.baseValue, name, nil}
+					u, _ := x.optionals.constraint(ctx, arg)
+					if u == nil { // subsumption already checked
+						s.lt = b.val()
+						return false
+					}
 				}
 			}
 		}
@@ -148,20 +230,21 @@
 	return !isBottom(v)
 }
 
-func (*top) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (*top) subsumesImpl(s *subsumer, v value) bool {
 	return true
 }
 
-func (x *bottom) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *bottom) subsumesImpl(s *subsumer, v value) bool {
 	// never called.
 	return v.kind() == bottomKind
 }
 
-func (x *basicType) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *basicType) subsumesImpl(s *subsumer, v value) bool {
 	return true
 }
 
-func (x *bound) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *bound) subsumesImpl(s *subsumer, v value) bool {
+	ctx := s.ctx
 	if isBottom(v) {
 		return true
 	}
@@ -243,81 +326,81 @@
 	return false
 }
 
-func (x *nullLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *nullLit) subsumesImpl(s *subsumer, v value) bool {
 	return true
 }
 
-func (x *boolLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *boolLit) subsumesImpl(s *subsumer, v value) bool {
 	return x.b == v.(*boolLit).b
 }
 
-func (x *stringLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *stringLit) subsumesImpl(s *subsumer, v value) bool {
 	return x.str == v.(*stringLit).str
 }
 
-func (x *bytesLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *bytesLit) subsumesImpl(s *subsumer, v value) bool {
 	return bytes.Equal(x.b, v.(*bytesLit).b)
 }
 
-func (x *numLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *numLit) subsumesImpl(s *subsumer, v value) bool {
 	b := v.(*numLit)
 	return x.v.Cmp(&b.v) == 0
 }
 
-func (x *durationLit) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *durationLit) subsumesImpl(s *subsumer, v value) bool {
 	return x.d == v.(*durationLit).d
 }
 
-func (x *list) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *list) subsumesImpl(s *subsumer, v value) bool {
 	switch y := v.(type) {
 	case *list:
-		if !subsumes(ctx, x.len, y.len, mode) {
+		if !s.subsumes(x.len, y.len) {
 			return false
 		}
 		// TODO: need to handle case where len(x.elem) > len(y.elem) explicitly
 		// if we introduce cap().
-		if !subsumes(ctx, x.elem, y.elem, mode) {
+		if !s.subsumes(x.elem, y.elem) {
 			return false
 		}
 		// TODO: assuming continuous indices, use merge sort if we allow
 		// sparse arrays.
 		for _, a := range y.elem.arcs[len(x.elem.arcs):] {
-			if !subsumes(ctx, x.typ, a.v, mode) {
+			if !s.subsumes(x.typ, a.v) {
 				return false
 			}
 		}
 		if y.isOpen() { // implies from first check that x.IsOpen.
-			return subsumes(ctx, x.typ, y.typ, 0)
+			return s.subsumes(x.typ, y.typ)
 		}
 		return true
 	}
 	return isBottom(v)
 }
 
-func (x *params) subsumes(ctx *context, y *params, mode subsumeMode) bool {
+func (x *params) subsumes(s *subsumer, y *params) bool {
 	// structural equivalence
 	// TODO: make agnostic to argument names.
 	if len(y.arcs) != len(x.arcs) {
 		return false
 	}
 	for i, a := range x.arcs {
-		if !subsumes(ctx, a.v, y.arcs[i].v, 0) {
+		if !s.subsumes(a.v, y.arcs[i].v) {
 			return false
 		}
 	}
 	return true
 }
 
-func (x *lambdaExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *lambdaExpr) subsumesImpl(s *subsumer, v value) bool {
 	// structural equivalence
 	if y, ok := v.(*lambdaExpr); ok {
-		return x.params.subsumes(ctx, y.params, 0) &&
-			subsumes(ctx, x.value, y.value, 0)
+		return x.params.subsumes(s, y.params) &&
+			s.subsumes(x.value, y.value)
 	}
 	return isBottom(v)
 }
 
-func (x *unification) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *unification) subsumesImpl(s *subsumer, v value) bool {
 	if y, ok := v.(*unification); ok {
 		// A unification subsumes another unification if for all values a in x
 		// there is a value b in y such that a subsumes b.
@@ -328,7 +411,7 @@
 	outer:
 		for _, vx := range x.values {
 			for _, vy := range y.values {
-				if subsumes(ctx, vx, vy, mode) {
+				if s.subsumes(vx, vy) {
 					continue outer
 				}
 			}
@@ -338,14 +421,14 @@
 	}
 	subsumed := true
 	for _, vx := range x.values {
-		subsumed = subsumed && subsumes(ctx, vx, v, mode)
+		subsumed = subsumed && s.subsumes(vx, v)
 	}
 	return subsumed
 }
 
 // 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 {
+func (x *disjunction) subsumesImpl(s *subsumer, v value) bool {
 	// A disjunction subsumes another disjunction if all values of v are
 	// subsumed by any of the values of x, and default values in v are subsumed
 	// by the default values of x.
@@ -358,7 +441,7 @@
 		for _, vd := range d.values {
 			// 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) {
+				if (vx.marked || !vd.marked) && s.subsumes(vx.val, vd.val) {
 					continue outer
 				}
 			}
@@ -368,7 +451,7 @@
 	}
 	// v is subsumed if any value in x subsumes v.
 	for _, vx := range x.values {
-		if subsumes(ctx, vx.val, v, 0) {
+		if s.subsumes(vx.val, v) {
 			return true
 		}
 	}
@@ -379,7 +462,7 @@
 // evaluation.
 
 // structural equivalence
-func (x *nodeRef) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *nodeRef) subsumesImpl(s *subsumer, v value) bool {
 	if r, ok := v.(*nodeRef); ok {
 		return x.node == r.node
 	}
@@ -387,14 +470,14 @@
 }
 
 // structural equivalence
-func (x *selectorExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *selectorExpr) subsumesImpl(s *subsumer, v value) bool {
 	if r, ok := v.(*selectorExpr); ok {
-		return x.feature == r.feature && subsumes(ctx, x.x, r.x, subChoose)
+		return x.feature == r.feature && s.subsumes(x.x, r.x) // subChoose
 	}
 	return isBottom(v)
 }
 
-func (x *interpolation) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *interpolation) subsumesImpl(s *subsumer, v value) bool {
 	switch v := v.(type) {
 	case *stringLit:
 		// Be conservative if not ground.
@@ -406,7 +489,7 @@
 			return false
 		}
 		for i, p := range x.parts {
-			if !subsumes(ctx, p, v.parts[i], 0) {
+			if !s.subsumes(p, v.parts[i]) {
 				return false
 			}
 		}
@@ -416,31 +499,31 @@
 }
 
 // structural equivalence
-func (x *indexExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *indexExpr) subsumesImpl(s *subsumer, v value) bool {
 	// TODO: what does it mean to subsume if the index value is not known?
 	if r, ok := v.(*indexExpr); ok {
 		// TODO: could be narrowed down if we know the exact value of the index
 		// and referenced value.
-		return subsumes(ctx, x.x, r.x, mode) && subsumes(ctx, x.index, r.index, 0)
+		return s.subsumes(x.x, r.x) && s.subsumes(x.index, r.index)
 	}
 	return isBottom(v)
 }
 
 // structural equivalence
-func (x *sliceExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *sliceExpr) subsumesImpl(s *subsumer, v value) bool {
 	// TODO: what does it mean to subsume if the index value is not known?
 	if r, ok := v.(*sliceExpr); ok {
 		// TODO: could be narrowed down if we know the exact value of the index
 		// and referenced value.
-		return subsumes(ctx, x.x, r.x, 0) &&
-			subsumes(ctx, x.lo, r.lo, 0) &&
-			subsumes(ctx, x.hi, r.hi, 0)
+		return s.subsumes(x.x, r.x) &&
+			s.subsumes(x.lo, r.lo) &&
+			s.subsumes(x.hi, r.hi)
 	}
 	return isBottom(v)
 }
 
 // structural equivalence
-func (x *customValidator) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *customValidator) subsumesImpl(s *subsumer, v value) bool {
 	y, ok := v.(*customValidator)
 	if !ok {
 		return isBottom(v)
@@ -449,7 +532,7 @@
 		return false
 	}
 	for i, v := range x.args {
-		if !subsumes(ctx, v, y.args[i], mode) {
+		if !s.subsumes(v, y.args[i]) {
 			return false
 		}
 	}
@@ -457,60 +540,60 @@
 }
 
 // structural equivalence
-func (x *callExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *callExpr) subsumesImpl(s *subsumer, v value) bool {
 	if c, ok := v.(*callExpr); ok {
 		if len(x.args) != len(c.args) {
 			return false
 		}
 		for i, a := range x.args {
-			if !subsumes(ctx, a, c.args[i], 0) {
+			if !s.subsumes(a, c.args[i]) {
 				return false
 			}
 		}
-		return subsumes(ctx, x.x, c.x, 0)
+		return s.subsumes(x.x, c.x)
 	}
 	return isBottom(v)
 }
 
 // structural equivalence
-func (x *unaryExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *unaryExpr) subsumesImpl(s *subsumer, v value) bool {
 	if b, ok := v.(*unaryExpr); ok {
-		return x.op == b.op && subsumes(ctx, x.x, b.x, 0)
+		return x.op == b.op && s.subsumes(x.x, b.x)
 	}
 	return isBottom(v)
 }
 
 // structural equivalence
-func (x *binaryExpr) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *binaryExpr) subsumesImpl(s *subsumer, v value) bool {
 	if b, ok := v.(*binaryExpr); ok {
 		return x.op == b.op &&
-			subsumes(ctx, x.left, b.left, 0) &&
-			subsumes(ctx, x.right, b.right, 0)
+			s.subsumes(x.left, b.left) &&
+			s.subsumes(x.right, b.right)
 	}
 	return isBottom(v)
 }
 
 // structural equivalence
-func (x *listComprehension) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *listComprehension) subsumesImpl(s *subsumer, v value) bool {
 	if b, ok := v.(*listComprehension); ok {
-		return subsumes(ctx, x.clauses, b.clauses, 0)
+		return s.subsumes(x.clauses, b.clauses)
 	}
 	return isBottom(v)
 }
 
 // structural equivalence
-func (x *structComprehension) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *structComprehension) subsumesImpl(s *subsumer, v value) bool {
 	if b, ok := v.(*structComprehension); ok {
-		return subsumes(ctx, x.clauses, b.clauses, 0)
+		return s.subsumes(x.clauses, b.clauses)
 	}
 	return isBottom(v)
 }
 
 // structural equivalence
-func (x *fieldComprehension) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *fieldComprehension) subsumesImpl(s *subsumer, v value) bool {
 	if b, ok := v.(*fieldComprehension); ok {
-		return subsumes(ctx, x.key, b.key, 0) &&
-			subsumes(ctx, x.val, b.val, 0) &&
+		return s.subsumes(x.key, b.key) &&
+			s.subsumes(x.val, b.val) &&
 			!x.opt && b.opt &&
 			x.def == b.def
 	}
@@ -518,27 +601,27 @@
 }
 
 // structural equivalence
-func (x *yield) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *yield) subsumesImpl(s *subsumer, v value) bool {
 	if b, ok := v.(*yield); ok {
-		return subsumes(ctx, x.value, b.value, 0)
+		return s.subsumes(x.value, b.value)
 	}
 	return isBottom(v)
 }
 
 // structural equivalence
-func (x *feed) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *feed) subsumesImpl(s *subsumer, v value) bool {
 	if b, ok := v.(*feed); ok {
-		return subsumes(ctx, x.source, b.source, 0) &&
-			subsumes(ctx, x.fn, b.fn, 0)
+		return s.subsumes(x.source, b.source) &&
+			s.subsumes(x.fn, b.fn)
 	}
 	return isBottom(v)
 }
 
 // structural equivalence
-func (x *guard) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
+func (x *guard) subsumesImpl(s *subsumer, v value) bool {
 	if b, ok := v.(*guard); ok {
-		return subsumes(ctx, x.condition, b.condition, 0) &&
-			subsumes(ctx, x.value, b.value, 0)
+		return s.subsumes(x.condition, b.condition) &&
+			s.subsumes(x.value, b.value)
 	}
 	return isBottom(v)
 }
diff --git a/cue/subsume_test.go b/cue/subsume_test.go
index 0a95aa0..96a1372 100644
--- a/cue/subsume_test.go
+++ b/cue/subsume_test.go
@@ -404,10 +404,24 @@
 		607: {subsumes: false, in: `a: close({b: 1}), b: close({b?: 1})`},
 		608: {subsumes: true, in: `a: {}, b: close({})`},
 		609: {subsumes: true, in: `a: {}, b: close({foo?: 1})`},
+		610: {subsumes: true, in: `a: {foo?:1}, b: close({})`},
 
 		// Definitions are not regular fields.
-		610: {subsumes: false, in: `a: {a :: 1}, b: {a: 1}`},
-		611: {subsumes: false, in: `a: {a: 1}, b: {a :: 1}`},
+		630: {subsumes: false, in: `a: {a :: 1}, b: {a: 1}`},
+		631: {subsumes: false, in: `a: {a: 1}, b: {a :: 1}`},
+
+		// Subsuming final values.
+		700: {subsumes: true, in: `a: {[string]: 1}, b: {foo: 1}`, mode: subFinal},
+		701: {subsumes: true, in: `a: {[string]: int}, b: {foo: 1}`, mode: subFinal},
+		702: {subsumes: true, in: `a: {["foo"]: int}, b: {foo: 1}`, mode: subFinal},
+		703: {subsumes: false, in: `a: close({["foo"]: 1}), b: {bar: 1}`, mode: subFinal},
+		704: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`, mode: subFinal},
+		705: {subsumes: true, in: `a: close({}), b: {foo?: 1}`, mode: subFinal},
+		706: {subsumes: true, in: `a: close({}), b: close({foo?: 1})`, mode: subFinal},
+		707: {subsumes: true, in: `a: {}, b: close({})`, mode: subFinal},
+		708: {subsumes: false, in: `a: {[string]: 1}, b: {foo: 2}`, mode: subFinal},
+		709: {subsumes: true, in: `a: {}, b: close({foo?: 1})`, mode: subFinal},
+		710: {subsumes: false, in: `a: {foo: [...string]}, b: {}`, mode: subFinal},
 	}
 
 	re := regexp.MustCompile(`a: (.*).*b: ([^\n]*)`)
@@ -432,7 +446,8 @@
 					b = arc.v
 				}
 			}
-			got := subsumes(ctx, a, b, tc.mode)
+			s := subsumer{ctx: ctx, mode: tc.mode}
+			got := s.subsumes(a, b)
 			if got != tc.subsumes {
 				t.Errorf("got %v; want %v (%v vs %v)", got, tc.subsumes, a.kind(), b.kind())
 			}
@@ -443,5 +458,5 @@
 func TestTouchBottom(t *testing.T) {
 	// Just call this function to mark coverage. It is otherwise never called.
 	var x bottom
-	x.subsumesImpl(nil, &bottom{}, 0)
+	x.subsumesImpl(nil, &bottom{})
 }
diff --git a/cue/types.go b/cue/types.go
index 89fcad7..e8ffdd1 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -1333,13 +1333,40 @@
 	}
 }
 
-// Subsumes reports whether w is an instance of v.
+// Subsume reports nil when w is an instance of v or an error otherwise.
+//
+// Without options, the entire value is considered for assumption, which means
+// Subsume tests whether  v is a backwards compatible (newer) API version of w.
+// Use the Final() to indicate that the subsumed value is data, and that
+//
+// Use the Final option to check subsumption if a w is known to be final,
+// and should assumed to be closed.
+//
+// Options are currently ignored and the function will panic if any are passed.
 //
 // Value v and w must be obtained from the same build.
 // TODO: remove this requirement.
+func (v Value) Subsume(w Value, opts ...Option) error {
+	var mode subsumeMode
+	o := getOptions(opts)
+	if o.final {
+		mode |= subFinal | subChoose
+	}
+	return subsumes(v, w, mode)
+}
+
+// Deprecated: use Subsume.
+//
+// Subsumes reports whether w is an instance of v.
+//
+// Without options, Subsumes checks whether v is a backwards compatbile schema
+// of w.
+//
+// By default, Subsumes tests whether two values are compatib
+// Value v and w must be obtained from the same build.
+// TODO: remove this requirement.
 func (v Value) Subsumes(w Value) bool {
-	ctx := v.ctx()
-	return subsumes(ctx, v.eval(ctx), w.eval(ctx), subChoose)
+	return subsumes(v, w, subChoose) == nil
 }
 
 // Unify reports the greatest lower bound of v and w.
@@ -1541,6 +1568,7 @@
 	omitDefinitions bool
 	omitOptional    bool
 	omitAttrs       bool
+	final           bool
 	disallowCycles  bool // implied by concrete
 }
 
@@ -1549,9 +1577,11 @@
 
 type option func(p *options)
 
-// Used in Iter, Validate, Subsume?, Fields() Syntax, Export
-
-// TODO: could also be used for subsumption.
+// Final indicates a value is final. It implicitly closes all structs and lists
+// in a value and selects defaults.
+func Final() Option {
+	return func(o *options) { o.final = true }
+}
 
 // Concrete ensures that all values are concrete.
 //
@@ -1915,7 +1945,8 @@
 		for _, disjunct := range x.values {
 			if disjunct.marked {
 				for _, n := range x.values {
-					if !n.marked && subsumes(v.ctx(), n.val, disjunct.val, 0) {
+					s := subsumer{ctx: v.ctx()}
+					if !n.marked && s.subsumes(n.val, disjunct.val) {
 						continue outer
 					}
 				}
diff --git a/cue/value.go b/cue/value.go
index 5d8564d..5df4323 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -39,7 +39,7 @@
 
 	// subsumesImpl is only defined for non-reference types.
 	// It should only be called by the subsumes function.
-	subsumesImpl(*context, value, subsumeMode) bool
+	subsumesImpl(*subsumer, value) bool
 }
 
 type evaluated interface {
@@ -767,7 +767,7 @@
 }
 
 // match reports whether a field with the given name may be added in the
-// associated struct as a new field. ok is false, fif there was any closed
+// associated struct as a new field. ok is false if there was any closed
 // struct that failed to match. Even if match returns false, there may still be
 // constraints represented by optionals that are to be applied to existing
 // concrete fields.
@@ -1695,7 +1695,8 @@
 		if isBottom(lt.val) {
 			return true
 		}
-		return (!lt.marked || gt.marked) && subsumes(ctx, gt.val, lt.val, 0)
+		s := subsumer{ctx: ctx}
+		return (!lt.marked || gt.marked) && s.subsumes(gt.val, lt.val)
 	}
 	k := 0
 
diff --git a/tools/trim/trim.go b/tools/trim/trim.go
index 55c22e7..e78be9b 100644
--- a/tools/trim/trim.go
+++ b/tools/trim/trim.go
@@ -401,7 +401,7 @@
 
 	default:
 		// Mark any subsumed part that is covered by generated config.
-		if in.Err() == nil && v.Subsumes(in) {
+		if in.Err() == nil && v.Subsume(in) == nil {
 			for _, v := range vSplit {
 				src := v.Source()
 				if t.canRemove(src) && !inNodes(gen, src) &&