internal/core/subsume: initial implementation

Change-Id: I3bc7e2e74d8bc16f282deec511f437d0f6ff53cc
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6621
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/core/adt/composite.go b/internal/core/adt/composite.go
index 3246c5d..d7c51ca 100644
--- a/internal/core/adt/composite.go
+++ b/internal/core/adt/composite.go
@@ -74,6 +74,11 @@
 	// status indicates the evaluation progress of this vertex.
 	status VertexStatus
 
+	// isData indicates that this Vertex is to be interepreted as data: pattern
+	// and additional constraints, as well as optional fields, should be
+	// ignored.
+	isData bool
+
 	// Value is the value associated with this vertex. For lists and structs
 	// this is a sentinel value indicating its kind.
 	Value Value
@@ -148,6 +153,39 @@
 	v.status = s
 }
 
+// IsData reports whether v should be interpreted in data mode. In other words,
+// it tells whether optional field matching and non-regular fields, like
+// definitions and hidden fields, should be ignored.
+func (v *Vertex) IsData() bool {
+	return v.isData || len(v.Conjuncts) == 0
+}
+
+// ToDataSingle creates a new Vertex that represents just the regular fields
+// of this vertex. Arcs are left untouched.
+// It is used by cue.Eval to convert nodes to data on per-node basis.
+func (v *Vertex) ToDataSingle() *Vertex {
+	w := *v
+	w.isData = true
+	return &w
+}
+
+// ToDataAll returns a new v where v and all its descendents contain only
+// the regular fields.
+func (v *Vertex) ToDataAll() *Vertex {
+	arcs := make([]*Vertex, len(v.Arcs))
+	for i, a := range v.Arcs {
+		arcs[i] = a.ToDataAll()
+	}
+	w := *v
+	w.Arcs = arcs
+	w.isData = true
+	return &w
+}
+
+// func (v *Vertex) IsEvaluating() bool {
+// 	return v.Value == cycle
+// }
+
 func (v *Vertex) IsErr() bool {
 	// if v.Status() > Evaluating {
 	if _, ok := v.Value.(*Bottom); ok {
@@ -228,8 +266,23 @@
 	// constraints, and additional constraints that match f and inserts them in
 	// arc. Use f is 0 to match all additional constraints only.
 	MatchAndInsert(c *OpContext, arc *Vertex)
+
+	// OptionalTypes returns a bit field with the type of optional constraints
+	// that are represented by this Acceptor.
+	OptionalTypes() OptionalType
 }
 
+// OptionalType is a bit field of the type of optional constraints in use by an
+// Acceptor.
+type OptionalType int
+
+const (
+	HasField OptionalType = 1 << iota
+	HasPattern
+	HasAdditional
+	IsOpen
+)
+
 func (v *Vertex) Kind() Kind {
 	// This is possible when evaluating comprehensions. It is potentially
 	// not known at this time what the type is.
@@ -239,6 +292,17 @@
 	return v.Value.Kind()
 }
 
+func (v *Vertex) OptionalTypes() OptionalType {
+	switch {
+	case v.Closed != nil:
+		return v.Closed.OptionalTypes()
+	case v.IsList():
+		return 0
+	default:
+		return IsOpen
+	}
+}
+
 func (v *Vertex) IsClosed(ctx *OpContext) bool {
 	switch x := v.Value.(type) {
 	case *ListMarker:
diff --git a/internal/core/debug/compact.go b/internal/core/debug/compact.go
index 722a480..b40ada5 100644
--- a/internal/core/debug/compact.go
+++ b/internal/core/debug/compact.go
@@ -34,7 +34,7 @@
 func (w *compactPrinter) node(n adt.Node) {
 	switch x := n.(type) {
 	case *adt.Vertex:
-		if x.Value == nil || (w.cfg.Raw && len(x.Conjuncts) > 0) {
+		if x.Value == nil || (w.cfg.Raw && !x.IsData()) {
 			for i, c := range x.Conjuncts {
 				if i > 0 {
 					w.string(" & ")
diff --git a/internal/core/eval/closed.go b/internal/core/eval/closed.go
index 10cee11..c909b43 100644
--- a/internal/core/eval/closed.go
+++ b/internal/core/eval/closed.go
@@ -97,6 +97,13 @@
 	}
 }
 
+func (a *acceptor) OptionalTypes() (mask adt.OptionalType) {
+	for _, f := range a.fields {
+		mask |= f.OptionalTypes()
+	}
+	return mask
+}
+
 // CloseDef defines how individual FieldSets (corresponding to conjuncts)
 // combine to determine whether a field is contained in a closed set.
 //
diff --git a/internal/core/eval/optionals.go b/internal/core/eval/optionals.go
index 1dbd376..8d77a57 100644
--- a/internal/core/eval/optionals.go
+++ b/internal/core/eval/optionals.go
@@ -37,6 +37,25 @@
 	isOpen     bool // has a ...
 }
 
+func (o *fieldSet) OptionalTypes() (mask adt.OptionalType) {
+	for _, f := range o.fields {
+		if len(f.optional) > 0 {
+			mask = adt.HasField
+			break
+		}
+	}
+	if o.bulk != nil {
+		mask |= adt.HasPattern
+	}
+	if o.additional != nil {
+		mask |= adt.HasAdditional
+	}
+	if o.isOpen {
+		mask |= adt.IsOpen
+	}
+	return mask
+}
+
 type field struct {
 	label    adt.Feature
 	optional []adt.Node
diff --git a/internal/core/subsume/structural.go b/internal/core/subsume/structural.go
new file mode 100644
index 0000000..16e7a0e
--- /dev/null
+++ b/internal/core/subsume/structural.go
@@ -0,0 +1,281 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package subsume
+
+// TODO: structural subsumption has not yet been implemented.
+
+import "cuelang.org/go/internal/core/adt"
+
+func (s *subsumer) subsumes(gt, lt adt.Conjunct) bool {
+	if gt == lt {
+		return true
+	}
+
+	// First try evaluating at the value level.
+	x, _ := gt.Expr().(adt.Value)
+	y, _ := lt.Expr().(adt.Value)
+	if x == nil {
+		// Fall back to structural.
+		return s.structural(gt, lt)
+	}
+	if y == nil {
+		return false
+	}
+
+	return s.values(x, y)
+}
+
+func (s *subsumer) conjunct(gt, lt adt.Conjunct) bool {
+	return false
+}
+
+func (s *subsumer) c(env *adt.Environment, x adt.Expr) adt.Conjunct {
+	return adt.MakeConjunct(env, x)
+}
+
+func isBottomConjunct(c adt.Conjunct) bool {
+	b, _ := c.Expr().(*adt.Bottom)
+	return b != nil
+}
+
+func (s *subsumer) node(env *adt.Environment, up int32) *adt.Vertex {
+	for ; up != 0; up-- {
+		env = env.Up
+	}
+	return env.Vertex
+}
+
+func (s *subsumer) structural(a, b adt.Conjunct) bool {
+	if y, ok := b.Expr().(*adt.LetReference); ok {
+		return s.conjunct(a, s.c(b.Env, y.X))
+	}
+	if isBottomConjunct(b) {
+		return true
+	}
+
+	switch x := a.Expr().(type) {
+	case *adt.DisjunctionExpr:
+
+	case *adt.StructLit:
+	case *adt.ListLit:
+
+	case *adt.FieldReference:
+		if y, ok := b.Expr().(*adt.FieldReference); ok && x.Label == y.Label {
+			if s.node(a.Env, x.UpCount) == s.node(b.Env, y.UpCount) {
+				return true
+			}
+		}
+
+	case *adt.LabelReference:
+		if y, ok := b.Expr().(*adt.LabelReference); ok {
+			if s.node(a.Env, x.UpCount) == s.node(b.Env, y.UpCount) {
+				return true
+			}
+		}
+
+	case *adt.DynamicReference:
+		if y, ok := b.Expr().(*adt.FieldReference); ok {
+			if s.node(a.Env, x.UpCount) == s.node(b.Env, y.UpCount) {
+				return true
+			}
+		}
+
+	case *adt.ImportReference:
+		if y, ok := b.Expr().(*adt.ImportReference); ok &&
+			x.ImportPath == y.ImportPath {
+			return true
+		}
+
+	case *adt.LetReference:
+		return s.conjunct(s.c(a.Env, x.X), b)
+
+	case *adt.SelectorExpr:
+		if y, ok := a.Expr().(*adt.SelectorExpr); ok &&
+			x.Sel == y.Sel &&
+			s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X)) {
+			return true
+		}
+
+	case *adt.IndexExpr:
+		if y, ok := b.Expr().(*adt.IndexExpr); ok &&
+			s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X)) &&
+			s.conjunct(s.c(a.Env, x.Index), s.c(b.Env, y.Index)) {
+			return true
+		}
+
+	case *adt.SliceExpr:
+		if r, ok := b.Expr().(*adt.SliceExpr); ok &&
+			s.conjunct(s.c(a.Env, x.X), s.c(b.Env, r.X)) &&
+			s.conjunct(s.c(a.Env, x.Lo), s.c(b.Env, r.Lo)) &&
+			s.conjunct(s.c(a.Env, x.Hi), s.c(b.Env, r.Hi)) {
+			return true
+		}
+
+	case *adt.Interpolation:
+		switch y := b.Expr().(type) {
+		case *adt.String:
+			// Be conservative if not ground.
+			s.inexact = true
+
+		case *adt.Interpolation:
+			// structural equivalence
+			if len(x.Parts) != len(y.Parts) {
+				return false
+			}
+			for i, p := range x.Parts {
+				if !s.conjunct(s.c(a.Env, p), s.c(b.Env, y.Parts[i])) {
+					return false
+				}
+			}
+			return true
+		}
+
+	case *adt.BoundExpr:
+		if y, ok := b.Expr().(*adt.BoundExpr); ok && x.Op == y.Op {
+			return s.conjunct(s.c(a.Env, x.Expr), s.c(b.Env, y.Expr))
+		}
+
+	case *adt.UnaryExpr:
+		if y, ok := b.Expr().(*adt.UnaryExpr); ok && x.Op == y.Op {
+			return s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X))
+		}
+
+	case *adt.BinaryExpr:
+		if y, ok := b.Expr().(*adt.BinaryExpr); ok && x.Op == y.Op {
+			return s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X)) &&
+				s.conjunct(s.c(a.Env, x.Y), s.c(b.Env, y.Y))
+		}
+
+	case *adt.CallExpr:
+		if y, ok := b.Expr().(*adt.CallExpr); ok {
+			if len(x.Args) != len(y.Args) {
+				return false
+			}
+			for i, arg := range x.Args {
+				if !s.conjunct(s.c(a.Env, arg), s.c(b.Env, y.Args[i])) {
+					return false
+				}
+			}
+			return s.conjunct(s.c(a.Env, x.Fun), s.c(b.Env, y.Fun))
+		}
+	}
+	return false
+}
+
+func (s *subsumer) structLit(
+	ea *adt.Environment, sa *adt.StructLit,
+	eb *adt.Environment, sb *adt.StructLit) bool {
+
+	// Create index of instance fields.
+	ca := newCollatedDecls()
+	ca.collate(ea, sa)
+
+	if ca.yielders != nil || ca.dynamic != nil {
+		// TODO: we could do structural comparison of comprehensions
+		// in many cases. For instance, an if clause would subsume
+		// structurally if it subsumes any of the if clauses in sb.
+		s.inexact = true
+		return false
+	}
+
+	cb := newCollatedDecls()
+	cb.collate(eb, sb)
+
+	if ca.hasOptional && !s.IgnoreOptional {
+		// TODO: same argument here as for comprehensions. This could
+		// be made to work.
+		if ca.pattern != nil || ca.dynamic != nil {
+			s.inexact = true
+			return false
+		}
+
+		// for f, b := range cb.fields {
+		// 	if !b.required || f.IsDef() {
+		// 		continue
+		// 	}
+		// 	name := ctx.LabelStr(b.Label)
+		// 	arg := &stringLit{x.baseValue, name, nil}
+		// 	u, _ := x.optionals.constraint(ctx, arg)
+		// 	if u != nil && !s.subsumes(u, b.v) {
+		// 		return false
+		// 	}
+		// }
+
+	}
+
+	return false
+
+}
+
+// collatedDecls is used to compute the structural subsumption of two
+// struct literals.
+type collatedDecls struct {
+	fields      map[adt.Feature]field
+	yielders    []adt.Yielder
+	pattern     []*adt.BulkOptionalField
+	dynamic     []*adt.DynamicField
+	values      []adt.Expr
+	additional  []*adt.Ellipsis
+	isOpen      bool
+	hasOptional bool
+}
+
+func newCollatedDecls() *collatedDecls {
+	return &collatedDecls{fields: map[adt.Feature]field{}}
+}
+
+type field struct {
+	required  bool
+	conjuncts []adt.Conjunct
+}
+
+func (c *collatedDecls) collate(env *adt.Environment, s *adt.StructLit) {
+	for _, d := range s.Decls {
+		switch x := d.(type) {
+		case *adt.Field:
+			e := c.fields[x.Label]
+			e.required = true
+			e.conjuncts = append(e.conjuncts, adt.MakeConjunct(env, x))
+			c.fields[x.Label] = e
+
+		case *adt.OptionalField:
+			e := c.fields[x.Label]
+			e.conjuncts = append(e.conjuncts, adt.MakeConjunct(env, x))
+			c.fields[x.Label] = e
+			c.hasOptional = true
+
+		case *adt.BulkOptionalField:
+			c.pattern = append(c.pattern, x)
+			c.hasOptional = true
+
+		case *adt.DynamicField:
+			c.dynamic = append(c.dynamic, x)
+			c.hasOptional = true
+
+		case *adt.Ellipsis:
+			c.isOpen = true
+			c.additional = append(c.additional, x)
+
+		case *adt.ForClause:
+			c.yielders = append(c.yielders, x)
+
+		case *adt.IfClause:
+			c.yielders = append(c.yielders, x)
+
+		case *adt.LetClause:
+		case *adt.ValueClause:
+		}
+	}
+}
diff --git a/internal/core/subsume/structural_test.go b/internal/core/subsume/structural_test.go
new file mode 100644
index 0000000..b22ded7
--- /dev/null
+++ b/internal/core/subsume/structural_test.go
@@ -0,0 +1,495 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package subsume
+
+import (
+	"regexp"
+	"strconv"
+	"strings"
+	"testing"
+
+	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/internal/core/adt"
+	"cuelang.org/go/internal/core/compile"
+	"cuelang.org/go/internal/core/eval"
+	"cuelang.org/go/internal/core/runtime"
+)
+
+func TestStructural(t *testing.T) {
+	// TODO: all these tests should pass for structural subsumption.
+	t.Skip()
+
+	// Do not inline: the named struct is used as a marker in
+	// testdata/gen.go.
+	type subsumeTest struct {
+		// the result of b ⊑ a, where a and b are defined in "in"
+		subsumes bool
+		in       string
+		// mode     subsumeMode
+	}
+	testCases := []subsumeTest{
+		// Top subsumes everything
+		0: {subsumes: true, in: `a: _, b: _ `},
+		1: {subsumes: true, in: `a: _, b: null `},
+		2: {subsumes: true, in: `a: _, b: int `},
+		3: {subsumes: true, in: `a: _, b: 1 `},
+		4: {subsumes: true, in: `a: _, b: float `},
+		5: {subsumes: true, in: `a: _, b: "s" `},
+		6: {subsumes: true, in: `a: _, b: {} `},
+		7: {subsumes: true, in: `a: _, b: []`},
+		8: {subsumes: true, in: `a: _, b: _|_ `},
+
+		// Nothing besides top subsumed top
+		9:  {subsumes: false, in: `a: null,    b: _`},
+		10: {subsumes: false, in: `a: int, b: _`},
+		11: {subsumes: false, in: `a: 1,       b: _`},
+		12: {subsumes: false, in: `a: float, b: _`},
+		13: {subsumes: false, in: `a: "s",     b: _`},
+		14: {subsumes: false, in: `a: {},      b: _`},
+		15: {subsumes: false, in: `a: [],      b: _`},
+		16: {subsumes: false, in: `a: _|_ ,      b: _`},
+
+		// Bottom subsumes nothing except bottom itself.
+		17: {subsumes: false, in: `a: _|_, b: null `},
+		18: {subsumes: false, in: `a: _|_, b: int `},
+		19: {subsumes: false, in: `a: _|_, b: 1 `},
+		20: {subsumes: false, in: `a: _|_, b: float `},
+		21: {subsumes: false, in: `a: _|_, b: "s" `},
+		22: {subsumes: false, in: `a: _|_, b: {} `},
+		23: {subsumes: false, in: `a: _|_, b: [] `},
+		24: {subsumes: true, in: ` a: _|_, b: _|_ `},
+
+		// All values subsume bottom
+		25: {subsumes: true, in: `a: null,    b: _|_`},
+		26: {subsumes: true, in: `a: int, b: _|_`},
+		27: {subsumes: true, in: `a: 1,       b: _|_`},
+		28: {subsumes: true, in: `a: float, b: _|_`},
+		29: {subsumes: true, in: `a: "s",     b: _|_`},
+		30: {subsumes: true, in: `a: {},      b: _|_`},
+		31: {subsumes: true, in: `a: [],      b: _|_`},
+		32: {subsumes: true, in: `a: true,    b: _|_`},
+		33: {subsumes: true, in: `a: _|_,       b: _|_`},
+
+		// null subsumes only null
+		34: {subsumes: true, in: ` a: null, b: null `},
+		35: {subsumes: false, in: `a: null, b: 1 `},
+		36: {subsumes: false, in: `a: 1,    b: null `},
+
+		37: {subsumes: true, in: ` a: true, b: true `},
+		38: {subsumes: false, in: `a: true, b: false `},
+
+		39: {subsumes: true, in: ` a: "a",    b: "a" `},
+		40: {subsumes: false, in: `a: "a",    b: "b" `},
+		41: {subsumes: true, in: ` a: string, b: "a" `},
+		42: {subsumes: false, in: `a: "a",    b: string `},
+
+		// Number typing (TODO)
+		//
+		// In principle, an "int" cannot assume an untyped "1", as "1" may
+		// still by typed as a float. They are two different type aspects. When
+		// considering, keep in mind that:
+		//   Key requirement: if A subsumes B, it must not be possible to
+		//   specialize B further such that A does not subsume B. HOWEVER,
+		//   The type conversion rules for conversion are INDEPENDENT of the
+		//   rules for subsumption!
+		// Consider:
+		// - only having number, but allowing user-defined types.
+		//   Subsumption would still work the same, but it may be somewhat
+		//   less weird.
+		// - making 1 always an int and 1.0 always a float.
+		//   - the int type would subsume any derived type from int.
+		//   - arithmetic would allow implicit conversions, but maybe not for
+		//     types.
+		//
+		// TODO: irrational numbers: allow untyped, but require explicit
+		//       trunking when assigning to float.
+		//
+		// a: number; cue.IsInteger(a) && a > 0
+		// t: (x) -> number; cue.IsInteger(a) && a > 0
+		// type x number: cue.IsInteger(x) && x > 0
+		// x: typeOf(number); cue.IsInteger(x) && x > 0
+		43: {subsumes: true, in: `a: 1, b: 1 `},
+		44: {subsumes: true, in: `a: 1.0, b: 1.0 `},
+		45: {subsumes: true, in: `a: 3.0, b: 3.0 `},
+		46: {subsumes: false, in: `a: 1.0, b: 1 `},
+		47: {subsumes: false, in: `a: 1, b: 1.0 `},
+		48: {subsumes: false, in: `a: 3, b: 3.0`},
+		49: {subsumes: true, in: `a: int, b: 1`},
+		50: {subsumes: true, in: `a: int, b: int & 1`},
+		51: {subsumes: true, in: `a: float, b: 1.0`},
+		52: {subsumes: false, in: `a: float, b: 1`},
+		53: {subsumes: false, in: `a: int, b: 1.0`},
+		54: {subsumes: true, in: `a: int, b: int`},
+		55: {subsumes: true, in: `a: number, b: int`},
+
+		// Structs
+		64: {subsumes: true, in: `a: {}, b: {}`},
+		65: {subsumes: true, in: `a: {}, b: {a: 1}`},
+		66: {subsumes: true, in: `a: {a:1}, b: {a:1, b:1}`},
+		67: {subsumes: true, in: `a: {s: { a:1} }, b: { s: { a:1, b:2 }}`},
+		68: {subsumes: true, in: `a: {}, b: {}`},
+		// TODO: allow subsumption of unevaluated values?
+		// ref not yet evaluated and not structurally equivalent
+		69: {subsumes: true, in: `a: {}, b: {} & c, c: {}`},
+
+		70: {subsumes: false, in: `a: {a:1}, b: {}`},
+		71: {subsumes: false, in: `a: {a:1, b:1}, b: {a:1}`},
+		72: {subsumes: false, in: `a: {s: { a:1} }, b: { s: {}}`},
+
+		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`},
+		88: {subsumes: false, in: `a: int, b: 1 | 2 | 3.1`},
+
+		89: {subsumes: true, in: `a: float | number, b: 1 | 2 | 3.1`},
+
+		90: {subsumes: false, in: `a: int, b: 1 | 2 | 3.1`},
+		91: {subsumes: true, in: `a: 1 | 2, b: 1`},
+		92: {subsumes: true, in: `a: 1 | 2, b: 2`},
+		93: {subsumes: false, in: `a: 1 | 2, b: 3`},
+
+		// Structural
+		94: {subsumes: false, in: `a: int + int, b: int`},
+		95: {subsumes: true, in: `a: int + int, b: int + int`},
+		96: {subsumes: true, in: `a: int + number, b: int + int`},
+		97: {subsumes: true, in: `a: number + number, b: int + int`},
+		// TODO: allow subsumption of unevaluated values?
+		// TODO: may be false if we allow arithmetic on incomplete values.
+		98: {subsumes: false, in: `a: int + int, b: int * int`},
+
+		// TODO: allow subsumption of unevaluated values?
+		// true because both evaluate to bottom
+		99:  {subsumes: true, in: `a: !int, b: !int`},
+		100: {subsumes: true, in: `a: !number, b: !int`},
+		101: {subsumes: true, in: `a: !int, b: !number`},
+
+		// TODO: allow subsumption of unevaluated values?
+		102: {subsumes: false, in: `a: int + int, b: !number`},
+		// TODO: allow subsumption of unevaluated values?
+		103: {subsumes: false, in: `a: !bool, b: bool`},
+		104: {subsumes: true, in: `a: !bool, b: !bool`},
+
+		// Call
+		113: {subsumes: true, in: `
+			a: fn()
+			b: fn()
+			fn: _`,
+		},
+		114: {subsumes: false, in: `
+			a: fn(),
+			b: fn(1)
+			fn: _`,
+		},
+		115: {subsumes: true, in: `
+			a: fn(2)
+			b: fn(2)
+			fn: _`,
+		},
+		116: {subsumes: true, in: `
+			a: fn(number)
+			b: fn(2)
+			fn: _`,
+		},
+		117: {subsumes: false, in: `
+			a: fn(2)
+			b: fn(number)
+			fn: _`,
+		},
+
+		// TODO: allow subsumption of unevaluated values?
+		// TODO: okay, but why false?
+		121: {subsumes: false, in: `a: c + d, b: int, c: int, d: int`},
+		// TODO: allow subsumption of unevaluated values?
+		122: {subsumes: true, in: `a: {}, b: c & {}, c: {}`},
+
+		// references
+		123: {subsumes: true, in: `a: c, b: c, c: {}`},
+		// TODO: allow subsumption of unevaluated values?
+		124: {subsumes: true, in: `a: c, b: d, c: {}, d: {}`},
+		125: {subsumes: false, in: `a: c, b: d, c: {a:1}, d: {}`},
+		// TODO: allow subsumption of unevaluated values?
+		126: {subsumes: true, in: `a: c, b: d, c: {a:1}, d: c & {b:1}`},
+		127: {subsumes: false, in: `a: d, b: c, c: {a:1}, d: c & {b:1}`},
+		128: {subsumes: false, in: `a: c.c, b: c, c: { d: number}`},
+
+		// type unification catches a reference error.
+		129: {subsumes: false, in: `a: c, b: d, c: 1, d: 2`},
+
+		130: {subsumes: true, in: ` a: [1][1], b: [1][1]`},
+		131: {subsumes: true, in: ` a: [1][number], b: [1][1]`},
+		132: {subsumes: true, in: ` a: [number][1], b: [1][1]`},
+		133: {subsumes: true, in: ` a: [number][number], b: [1][1]`},
+		134: {subsumes: false, in: ` a: [1][0], b: [1][number]`},
+		135: {subsumes: false, in: ` a: [1][0], b: [number][0]`},
+		136: {subsumes: true, in: ` a: [number][number], b: [1][number]`},
+		137: {subsumes: true, in: ` a: [number][number], b: [number][1]`},
+		// purely structural:
+		138: {subsumes: false, in: ` a: [number][number], b: number`},
+
+		// interpolations
+		139: {subsumes: true, in: ` a: "\(d)", b: "\(d)", d: _`},
+		// TODO: allow subsumption of unevaluated values?
+		140: {subsumes: true, in: ` a: "\(d)", b: "\(e)", d: _, e: _`},
+
+		141: {subsumes: true, in: ` a: "\(string)", b: "\("foo")"`},
+		// TODO: allow subsumption of unevaluated values?
+		142: {subsumes: true, in: ` a: "\(string)", b: "\(d)", d: "foo"`},
+		143: {subsumes: true, in: ` a: "\("foo")", b: "\("foo")"`},
+		144: {subsumes: false, in: ` a: "\("foo")", b: "\(1) \(2)"`},
+
+		145: {subsumes: false, in: ` a: "s \(d) e", b: "s a e", d: _`},
+		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`},
+		153: {subsumes: false, in: `a: int | *2, b: int | *2.0`},
+		154: {subsumes: true, in: `a: number | *2 | *3, b: number | *2`},
+		155: {subsumes: true, in: `a: number, b: number | *2`},
+
+		// Bounds
+		170: {subsumes: true, in: `a: >=2, b: >=2`},
+		171: {subsumes: true, in: `a: >=1, b: >=2`},
+		172: {subsumes: true, in: `a: >0, b: >=2`},
+		173: {subsumes: true, in: `a: >1, b: >1`},
+		174: {subsumes: true, in: `a: >=1, b: >1`},
+		175: {subsumes: false, in: `a: >1, b: >=1`},
+		176: {subsumes: true, in: `a: >=1, b: >=1`},
+		177: {subsumes: true, in: `a: <1, b: <1`},
+		178: {subsumes: true, in: `a: <=1, b: <1`},
+		179: {subsumes: false, in: `a: <1, b: <=1`},
+		180: {subsumes: true, in: `a: <=1, b: <=1`},
+
+		181: {subsumes: true, in: `a: !=1, b: !=1`},
+		182: {subsumes: false, in: `a: !=1, b: !=2`},
+
+		183: {subsumes: false, in: `a: !=1, b: <=1`},
+		184: {subsumes: true, in: `a: !=1, b: <1`},
+		185: {subsumes: false, in: `a: !=1, b: >=1`},
+		186: {subsumes: true, in: `a: !=1, b: <1`},
+
+		187: {subsumes: true, in: `a: !=1, b: <=0`},
+		188: {subsumes: true, in: `a: !=1, b: >=2`},
+		189: {subsumes: true, in: `a: !=1, b: >1`},
+
+		195: {subsumes: false, in: `a: >=2, b: !=2`},
+		196: {subsumes: false, in: `a: >2, b: !=2`},
+		197: {subsumes: false, in: `a: <2, b: !=2`},
+		198: {subsumes: false, in: `a: <=2, b: !=2`},
+
+		200: {subsumes: true, in: `a: =~"foo", b: =~"foo"`},
+		201: {subsumes: false, in: `a: =~"foo", b: =~"bar"`},
+		202: {subsumes: false, in: `a: =~"foo1", b: =~"foo"`},
+
+		203: {subsumes: true, in: `a: !~"foo", b: !~"foo"`},
+		204: {subsumes: false, in: `a: !~"foo", b: !~"bar"`},
+		205: {subsumes: false, in: `a: !~"foo", b: !~"foo1"`},
+
+		// The following is could be true, but we will not go down the rabbit
+		// hold of trying to prove subsumption of regular expressions.
+		210: {subsumes: false, in: `a: =~"foo", b: =~"foo1"`},
+		211: {subsumes: false, in: `a: !~"foo1", b: !~"foo"`},
+
+		220: {subsumes: true, in: `a: <5, b: 4`},
+		221: {subsumes: false, in: `a: <5, b: 5`},
+		222: {subsumes: true, in: `a: <=5, b: 5`},
+		223: {subsumes: false, in: `a: <=5.0, b: 5.00000001`},
+		224: {subsumes: true, in: `a: >5, b: 6`},
+		225: {subsumes: false, in: `a: >5, b: 5`},
+		226: {subsumes: true, in: `a: >=5, b: 5`},
+		227: {subsumes: false, in: `a: >=5, b: 4`},
+		228: {subsumes: true, in: `a: !=5, b: 6`},
+		229: {subsumes: false, in: `a: !=5, b: 5`},
+		230: {subsumes: false, in: `a: !=5.0, b: 5.0`},
+		231: {subsumes: false, in: `a: !=5.0, b: 5`},
+
+		250: {subsumes: true, in: `a: =~ #"^\d{3}$"#, b: "123"`},
+		251: {subsumes: false, in: `a: =~ #"^\d{3}$"#, b: "1234"`},
+		252: {subsumes: true, in: `a: !~ #"^\d{3}$"#, b: "1234"`},
+		253: {subsumes: false, in: `a: !~ #"^\d{3}$"#, b: "123"`},
+
+		// Conjunctions
+		300: {subsumes: true, in: `a: >0, b: >=2 & <=100`},
+		301: {subsumes: false, in: `a: >0, b: >=0 & <=100`},
+
+		310: {subsumes: true, in: `a: >=0 & <=100, b: 10`},
+		311: {subsumes: true, in: `a: >=0 & <=100, b: >=0 & <=100`},
+		312: {subsumes: false, in: `a: !=2 & !=4, b: >3`},
+		313: {subsumes: true, in: `a: !=2 & !=4, b: >5`},
+
+		314: {subsumes: false, in: `a: >=0 & <=100, b: >=0 & <=150`},
+		315: {subsumes: true, in: `a: >=0 & <=150, b: >=0 & <=100`},
+
+		// Disjunctions
+		330: {subsumes: true, in: `a: >5, b: >10 | 8`},
+		331: {subsumes: false, in: `a: >8, b: >10 | 8`},
+
+		// Optional fields
+		// Optional fields defined constraints on fields that are not yet
+		// defined. So even if such a field is not part of the output, it
+		// influences the lattice structure.
+		// For a given A and B, where A and B unify and where A has an optional
+		// field that is not defined in B, the addition of an incompatible
+		// value of that field in B can cause A and B to no longer unify.
+		//
+		400: {subsumes: false, in: `a: {foo: 1}, b: {}`},
+		401: {subsumes: false, in: `a: {foo?: 1}, b: {}`},
+		402: {subsumes: true, in: `a: {}, b: {foo: 1}`},
+		403: {subsumes: true, in: `a: {}, b: {foo?: 1}`},
+
+		404: {subsumes: true, in: `a: {foo: 1}, b: {foo: 1}`},
+		405: {subsumes: true, in: `a: {foo?: 1}, b: {foo: 1}`},
+		406: {subsumes: true, in: `a: {foo?: 1}, b: {foo?: 1}`},
+		407: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`},
+
+		408: {subsumes: false, in: `a: {foo: 1}, b: {foo: 2}`},
+		409: {subsumes: false, in: `a: {foo?: 1}, b: {foo: 2}`},
+		410: {subsumes: false, in: `a: {foo?: 1}, b: {foo?: 2}`},
+		411: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 2}`},
+
+		412: {subsumes: true, in: `a: {foo: number}, b: {foo: 2}`},
+		413: {subsumes: true, in: `a: {foo?: number}, b: {foo: 2}`},
+		414: {subsumes: true, in: `a: {foo?: number}, b: {foo?: 2}`},
+		415: {subsumes: false, in: `a: {foo: number}, b: {foo?: 2}`},
+
+		416: {subsumes: false, in: `a: {foo: 1}, b: {foo: number}`},
+		417: {subsumes: false, in: `a: {foo?: 1}, b: {foo: number}`},
+		418: {subsumes: false, in: `a: {foo?: 1}, b: {foo?: number}`},
+		419: {subsumes: false, in: `a: {foo: 1}, b: {foo?: number}`},
+
+		// 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}`},
+
+		// 440: {subsumes: true, in: `a: {foo?: 1}, b: {}`, mode: subNoOptional},
+		// 441: {subsumes: true, in: `a: {}, b: {foo?: 1}`, mode: subNoOptional},
+		// 442: {subsumes: true, in: `a: {foo?: 1}, b: {foo: 1}`, mode: subNoOptional},
+		// 443: {subsumes: true, in: `a: {foo?: 1}, b: {foo?: 1}`, mode: subNoOptional},
+		// 444: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`, mode: subNoOptional},
+		// 445: {subsumes: true, in: `a: close({}), b: {foo?: 1}`, mode: subNoOptional},
+		// 446: {subsumes: true, in: `a: close({}), b: close({foo?: 1})`, mode: subNoOptional},
+		// 447: {subsumes: true, in: `a: {}, b: close({})`, mode: subNoOptional},
+		// 448: {subsumes: true, in: `a: {}, b: close({foo?: 1})`, mode: subNoOptional},
+
+		// Lists
+		506: {subsumes: true, in: `a: [], b: [] `},
+		507: {subsumes: true, in: `a: [1], b: [1] `},
+		508: {subsumes: false, in: `a: [1], b: [2] `},
+		509: {subsumes: false, in: `a: [1], b: [2, 3] `},
+		510: {subsumes: true, in: `a: [{b: string}], b: [{b: "foo"}] `},
+		511: {subsumes: true, in: `a: [...{b: string}], b: [{b: "foo"}] `},
+		512: {subsumes: false, in: `a: [{b: "foo"}], b: [{b: string}] `},
+		513: {subsumes: false, in: `a: [{b: string}], b: [{b: "foo"}, ...{b: "foo"}] `},
+		520: {subsumes: false, in: `a: [_, int, ...], b: [int, string, ...string] `},
+
+		// Closed structs.
+		600: {subsumes: false, in: `a: close({}), 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})`},
+		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.
+		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},
+
+		// // Schema values
+		// 800: {subsumes: true, in: `a: close({}), b: {foo: 1}`, mode: subSchema},
+		// // TODO(eval): FIX
+		// // 801: {subsumes: true, in: `a: {[string]: int}, b: {foo: 1}`, mode: subSchema},
+		// 804: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`, mode: subSchema},
+		// 805: {subsumes: true, in: `a: close({}), b: {foo?: 1}`, mode: subSchema},
+		// 806: {subsumes: true, in: `a: close({}), b: close({foo?: 1})`, mode: subSchema},
+		// 807: {subsumes: true, in: `a: {}, b: close({})`, mode: subSchema},
+		// 808: {subsumes: false, in: `a: {[string]: 1}, b: {foo: 2}`, mode: subSchema},
+		// 809: {subsumes: true, in: `a: {}, b: close({foo?: 1})`, mode: subSchema},
+	}
+
+	re := regexp.MustCompile(`a: (.*).*b: ([^\n]*)`)
+	for i, tc := range testCases {
+		if tc.in == "" {
+			continue
+		}
+		m := re.FindStringSubmatch(strings.Join(strings.Split(tc.in, "\n"), ""))
+		const cutset = "\n ,"
+		key := strings.Trim(m[1], cutset) + " ⊑ " + strings.Trim(m[2], cutset)
+
+		r := runtime.New()
+
+		t.Run(strconv.Itoa(i)+"/"+key, func(t *testing.T) {
+
+			file, err := parser.ParseFile("subsume", tc.in)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			root, errs := compile.Files(nil, r, file)
+			if errs != nil {
+				t.Fatal(errs)
+			}
+
+			ctx := eval.NewContext(r, root)
+			root.Finalize(ctx)
+
+			// Use low-level lookup to avoid evaluation.
+			var a, b adt.Value
+			for _, arc := range root.Arcs {
+				switch arc.Label {
+				case ctx.StringLabel("a"):
+					a = arc
+				case ctx.StringLabel("b"):
+					b = arc
+				}
+			}
+
+			err = Value(ctx, a, b)
+			got := err == nil
+
+			if got != tc.subsumes {
+				t.Errorf("got %v; want %v (%v vs %v)", got, tc.subsumes, a.Kind(), b.Kind())
+			}
+		})
+	}
+}
diff --git a/internal/core/subsume/subsume.go b/internal/core/subsume/subsume.go
new file mode 100644
index 0000000..9ce210a
--- /dev/null
+++ b/internal/core/subsume/subsume.go
@@ -0,0 +1,127 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package subsume defines various subsumption relations.
+package subsume
+
+import (
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/internal"
+	"cuelang.org/go/internal/core/adt"
+)
+
+// Profile configures the type of subsumption. One should typically use one
+// of the preconfigured profiles.
+type Profile struct {
+	// Final indicates subsumption should only consider fields that are relevant
+	// to data mode, and ignore definitions, hidden fields, pattern constraints
+	// and additional constraints.
+	Final bool
+
+	// Defaults indicate that default values should be used for the subsumed
+	// value.
+	Defaults bool
+
+	// Ignore optional fields.
+	IgnoreOptional bool
+
+	// IgnoreClosedness ignores closedness of structs and is used for comparing
+	// APIs.
+	IgnoreClosedness bool
+}
+
+var CUE = Profile{}
+
+// Final checks subsumption interpreting the subsumed value as data.
+var Final = Profile{
+	Final:    true,
+	Defaults: true,
+}
+
+// FinalOpen exists as an artifact of the old API. One should probably not use
+// this.
+var FinalOpen = Profile{
+	Final:            true,
+	Defaults:         true,
+	IgnoreClosedness: true,
+}
+
+// API is subsumption used for APIs.
+var API = Profile{
+	IgnoreClosedness: true,
+}
+
+// Value subsumes two values based on their logical (evaluated) values.
+func Value(ctx *adt.OpContext, a, b adt.Value) errors.Error {
+	return CUE.Value(ctx, a, b)
+}
+
+func (p *Profile) Value(ctx *adt.OpContext, a, b adt.Value) errors.Error {
+	s := subsumer{ctx: ctx, Profile: *p}
+	if !s.values(a, b) {
+		return s.getError()
+	}
+	return s.errs
+}
+
+// Check reports whether b is an instance of a.
+func (p *Profile) Check(ctx *adt.OpContext, a, b adt.Value) bool {
+	s := subsumer{ctx: ctx, Profile: *p}
+	return s.values(a, b)
+}
+
+func isBottom(x adt.Node) bool {
+	b, _ := x.(*adt.Bottom)
+	return b != nil
+}
+
+type subsumer struct {
+	ctx  *adt.OpContext
+	errs errors.Error
+
+	Profile
+
+	inexact bool // If true, the result could be a false negative.
+	missing adt.Feature
+	gt      adt.Value
+	lt      adt.Value
+}
+
+func (s *subsumer) errf(msg string, args ...interface{}) {
+	b := s.ctx.NewErrf(msg, args...)
+	s.errs = errors.Append(s.errs, b.Err)
+}
+
+func (s *subsumer) getError() (err errors.Error) {
+	c := s.ctx
+	// src := binSrc(token.NoPos, opUnify, gt, lt)
+	if s.gt != nil && s.lt != nil {
+		// src := binSrc(token.NoPos, opUnify, s.gt, s.lt)
+		if s.missing != 0 {
+			s.errf("missing field %q", s.missing.SelectorString(c))
+		} else if b, ok := adt.BinOp(c, adt.AndOp, s.gt, s.lt).(*adt.Bottom); !ok {
+			s.errf("value not an instance")
+		} else {
+			s.errs = errors.Append(s.errs, b.Err)
+		}
+	}
+	if s.errs == nil {
+		s.errf("value not an instance")
+	}
+	err = s.errs
+	if s.inexact {
+		err = internal.DecorateError(internal.ErrInexact, err)
+	}
+	return err
+}
diff --git a/internal/core/subsume/subsume_test.go b/internal/core/subsume/subsume_test.go
new file mode 100644
index 0000000..757c901
--- /dev/null
+++ b/internal/core/subsume/subsume_test.go
@@ -0,0 +1,61 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package subsume
+
+import (
+	"testing"
+
+	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/internal/core/adt"
+	"cuelang.org/go/internal/core/compile"
+	"cuelang.org/go/internal/core/eval"
+	"cuelang.org/go/internal/core/runtime"
+)
+
+// For debugging purposes, do not remove.
+func TestX(t *testing.T) {
+	t.Skip()
+
+	r := runtime.New()
+	ctx := eval.NewContext(r, nil)
+
+	const gt = `a: *1 | int`
+	const lt = `a: (*1 | int) & 1`
+
+	a := parse(t, ctx, gt)
+	b := parse(t, ctx, lt)
+
+	p := Profile{Defaults: true}
+	err := p.Check(ctx, a, b)
+	t.Error(err)
+}
+
+func parse(t *testing.T, ctx *adt.OpContext, str string) *adt.Vertex {
+	t.Helper()
+
+	file, err := parser.ParseFile("subsume", str)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	root, errs := compile.Files(nil, ctx, file)
+	if errs != nil {
+		t.Fatal(errs)
+	}
+
+	root.Finalize(ctx)
+
+	return root
+}
diff --git a/internal/core/subsume/value.go b/internal/core/subsume/value.go
new file mode 100644
index 0000000..c6e691f
--- /dev/null
+++ b/internal/core/subsume/value.go
@@ -0,0 +1,298 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package subsume
+
+import (
+	"bytes"
+
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/internal/core/adt"
+)
+
+func (s *subsumer) values(a, b adt.Value) (result bool) {
+	defer func() {
+		if !result && s.gt == nil && s.lt == nil {
+			s.gt = a
+			s.lt = b
+		}
+	}()
+
+	if a == b {
+		return true
+	}
+
+	if s.Defaults {
+		a = adt.Default(a)
+		b = adt.Default(b)
+	}
+
+	switch b := b.(type) {
+	case *adt.Bottom:
+		// If the value is incomplete, the error is not final. So either check
+		// structural equivalence or return an error.
+		return !b.IsIncomplete()
+
+	case *adt.Vertex:
+		if a, ok := a.(*adt.Vertex); ok {
+			return s.vertices(a, b)
+		}
+		// Safe to ignore arcs of w.
+		return s.values(a, b.Value)
+
+	case *adt.Conjunction:
+		if _, ok := a.(*adt.Conjunction); ok {
+			break
+		}
+		for _, y := range b.Values {
+			if s.values(a, y) {
+				return true
+			}
+		}
+		return false
+
+	case *adt.Disjunction:
+		if _, ok := a.(*adt.Disjunction); ok {
+			break
+		}
+
+		for _, y := range b.Values {
+			if !s.values(a, y) {
+				return false
+			}
+		}
+		return true
+
+	case *adt.NodeLink:
+		// Do not descend into NodeLinks to avoid processing cycles.
+		// TODO: this would work better if all equal nodes shared the same
+		// node link.
+		return deref(a) == deref(b)
+	}
+
+	switch x := a.(type) {
+	case *adt.Top:
+		return true
+
+	case *adt.Bottom:
+		// isBottom(b) was already tested above.
+		return false
+
+	case *adt.BasicType:
+		k := b.Kind()
+		return x.K&k == k
+
+	case *adt.BoundValue:
+		return s.bound(x, b)
+
+	case *adt.Builtin:
+		return x == b
+
+	case *adt.BuiltinValidator:
+		if y := s.ctx.Validate(x, b); y != nil {
+			s.errs = errors.Append(s.errs, y.Err)
+			return false
+		}
+		return true
+
+	case *adt.Null:
+		return b.Kind() == adt.NullKind
+
+	case *adt.Bool:
+		y, ok := b.(*adt.Bool)
+		return ok && x.B == y.B
+
+	case *adt.Num:
+		y, ok := b.(*adt.Num)
+		return ok && x.K&y.K == y.K && test(s.ctx, x, adt.EqualOp, x, y)
+
+	case *adt.String:
+		y, ok := b.(*adt.String)
+		return ok && x.Str == y.Str
+
+	case *adt.Bytes:
+		y, ok := b.(*adt.Bytes)
+		return ok && bytes.Equal(x.B, y.B)
+
+	case *adt.Vertex:
+		y, ok := b.(*adt.Vertex)
+		if !ok {
+			// 	// Under what conditions can we cast to the value?
+			if len(x.Arcs) == 0 && x.Value != nil {
+				return s.values(x.Value, b)
+			}
+			return false
+		}
+		return s.vertices(x, y)
+
+	case *adt.Conjunction:
+		if y, ok := b.(*adt.Conjunction); ok {
+			// A Conjunction subsumes another Conjunction if for all values a in
+			// x there is a value b in y such that a subsumes b.
+			//
+			// This assumes overlapping ranges in disjunctions are merged.If
+			// this is not the case, subsumes will return a false negative,
+			// which is allowed.
+		outerC:
+			for _, a := range x.Values {
+				for _, b := range y.Values {
+					if s.values(a, b) {
+						continue outerC
+					}
+				}
+				// TODO: should this be marked as inexact?
+				return false
+			}
+			return true
+		}
+		subsumed := true
+		for _, a := range x.Values {
+			subsumed = subsumed && s.values(a, b)
+		}
+		return subsumed
+
+	case *adt.Disjunction:
+		// A Disjunction subsumes another Disjunction if all values of y are
+		// subsumed by any of the values of x, and default values in y 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 y, ok := b.(*adt.Disjunction); ok {
+			// at least one value in x should subsume each value in d.
+		outerD:
+			for i, b := range y.Values {
+				bDefault := i < y.NumDefaults
+				// v is subsumed if any value in x subsumes v.
+				for j, a := range x.Values {
+					aDefault := j < x.NumDefaults
+					if (aDefault || !bDefault) && s.values(a, b) {
+						continue outerD
+					}
+				}
+				return false
+			}
+			return true
+		}
+		// b is subsumed if any value in x subsumes b.
+		for _, a := range x.Values {
+			if s.values(a, b) {
+				return true
+			}
+		}
+		// TODO: should this be marked as inexact?
+		return false
+
+	case *adt.NodeLink:
+		return deref(x) == deref(b)
+	}
+	return false
+}
+
+func deref(v adt.Expr) *adt.Vertex {
+	switch x := v.(type) {
+	case *adt.Vertex:
+		return x
+	case *adt.NodeLink:
+		return x.Node
+	}
+	return nil
+}
+
+func (s *subsumer) bound(x *adt.BoundValue, v adt.Value) bool {
+	ctx := s.ctx
+	if isBottom(v) {
+		return true
+	}
+
+	switch y := v.(type) {
+	case *adt.BoundValue:
+		if !adt.IsConcrete(y.Value) {
+			return false
+		}
+
+		kx := x.Kind()
+		ky := y.Kind()
+		if (kx&ky)&^kx != 0 {
+			return false
+		}
+		// x subsumes y if
+		// x: >= a, y: >= b ==> a <= b
+		// x: >= a, y: >  b ==> a <= b
+		// x: >  a, y: >  b ==> a <= b
+		// x: >  a, y: >= b ==> a < b
+		//
+		// x: <= a, y: <= b ==> a >= b
+		//
+		// x: != a, y: != b ==> a != b
+		//
+		// false if types or op direction doesn't match
+
+		xv := x.Value
+		yv := y.Value
+		switch x.Op {
+		case adt.GreaterThanOp:
+			if y.Op == adt.GreaterEqualOp {
+				return test(ctx, x, adt.LessThanOp, xv, yv)
+			}
+			fallthrough
+		case adt.GreaterEqualOp:
+			if y.Op == adt.GreaterThanOp || y.Op == adt.GreaterEqualOp {
+				return test(ctx, x, adt.LessEqualOp, xv, yv)
+			}
+		case adt.LessThanOp:
+			if y.Op == adt.LessEqualOp {
+				return test(ctx, x, adt.GreaterThanOp, xv, yv)
+			}
+			fallthrough
+		case adt.LessEqualOp:
+			if y.Op == adt.LessThanOp || y.Op == adt.LessEqualOp {
+				return test(ctx, x, adt.GreaterEqualOp, xv, yv)
+			}
+		case adt.NotEqualOp:
+			switch y.Op {
+			case adt.NotEqualOp:
+				return test(ctx, x, adt.EqualOp, xv, yv)
+			case adt.GreaterEqualOp:
+				return test(ctx, x, adt.LessThanOp, xv, yv)
+			case adt.GreaterThanOp:
+				return test(ctx, x, adt.LessEqualOp, xv, yv)
+			case adt.LessThanOp:
+				return test(ctx, x, adt.GreaterEqualOp, xv, yv)
+			case adt.LessEqualOp:
+				return test(ctx, x, adt.GreaterThanOp, xv, yv)
+			}
+
+		case adt.MatchOp, adt.NotMatchOp:
+			// these are just approximations
+			if y.Op == x.Op {
+				return test(ctx, x, adt.EqualOp, xv, yv)
+			}
+
+		default:
+			// adt.NotEqualOp already handled above.
+			panic("cue: undefined bound mode")
+		}
+
+	case *adt.Num, *adt.String, *adt.Bool:
+		return test(ctx, x, x.Op, y, x.Value)
+	}
+	return false
+}
+
+func test(ctx *adt.OpContext, src adt.Node, op adt.Op, gt, lt adt.Value) bool {
+	x := adt.BinOp(ctx, op, gt, lt)
+	b, ok := x.(*adt.Bool)
+	return ok && b.B
+}
diff --git a/internal/core/subsume/value_test.go b/internal/core/subsume/value_test.go
new file mode 100644
index 0000000..80869c4
--- /dev/null
+++ b/internal/core/subsume/value_test.go
@@ -0,0 +1,427 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package subsume
+
+import (
+	"regexp"
+	"strconv"
+	"strings"
+	"testing"
+
+	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/internal/core/adt"
+	"cuelang.org/go/internal/core/compile"
+	"cuelang.org/go/internal/core/eval"
+	"cuelang.org/go/internal/core/runtime"
+)
+
+const (
+	subNone = iota
+	subFinal
+	subNoOptional
+	subSchema
+)
+
+func TestValues(t *testing.T) {
+	// Do not inline: the named struct is used as a marker in
+	// testdata/gen.go.
+	type subsumeTest struct {
+		// the result of b ⊑ a, where a and b are defined in "in"
+		subsumes bool
+		in       string
+		mode     int
+	}
+	testCases := []subsumeTest{
+		// Top subsumes everything
+		0: {subsumes: true, in: `a: _, b: _ `},
+		1: {subsumes: true, in: `a: _, b: null `},
+		2: {subsumes: true, in: `a: _, b: int `},
+		3: {subsumes: true, in: `a: _, b: 1 `},
+		4: {subsumes: true, in: `a: _, b: float `},
+		5: {subsumes: true, in: `a: _, b: "s" `},
+		6: {subsumes: true, in: `a: _, b: {} `},
+		7: {subsumes: true, in: `a: _, b: []`},
+		8: {subsumes: true, in: `a: _, b: _|_ `},
+
+		// Nothing besides top subsumed top
+		9:  {subsumes: false, in: `a: null,    b: _`},
+		10: {subsumes: false, in: `a: int, b: _`},
+		11: {subsumes: false, in: `a: 1,       b: _`},
+		12: {subsumes: false, in: `a: float, b: _`},
+		13: {subsumes: false, in: `a: "s",     b: _`},
+		14: {subsumes: false, in: `a: {},      b: _`},
+		15: {subsumes: false, in: `a: [],      b: _`},
+		16: {subsumes: false, in: `a: _|_ ,      b: _`},
+
+		// Bottom subsumes nothing except bottom itself.
+		17: {subsumes: false, in: `a: _|_, b: null `},
+		18: {subsumes: false, in: `a: _|_, b: int `},
+		19: {subsumes: false, in: `a: _|_, b: 1 `},
+		20: {subsumes: false, in: `a: _|_, b: float `},
+		21: {subsumes: false, in: `a: _|_, b: "s" `},
+		22: {subsumes: false, in: `a: _|_, b: {} `},
+		23: {subsumes: false, in: `a: _|_, b: [] `},
+		24: {subsumes: true, in: ` a: _|_, b: _|_ `},
+
+		// All values subsume bottom
+		25: {subsumes: true, in: `a: null,    b: _|_`},
+		26: {subsumes: true, in: `a: int, b: _|_`},
+		27: {subsumes: true, in: `a: 1,       b: _|_`},
+		28: {subsumes: true, in: `a: float, b: _|_`},
+		29: {subsumes: true, in: `a: "s",     b: _|_`},
+		30: {subsumes: true, in: `a: {},      b: _|_`},
+		31: {subsumes: true, in: `a: [],      b: _|_`},
+		32: {subsumes: true, in: `a: true,    b: _|_`},
+		33: {subsumes: true, in: `a: _|_,       b: _|_`},
+
+		// null subsumes only null
+		34: {subsumes: true, in: ` a: null, b: null `},
+		35: {subsumes: false, in: `a: null, b: 1 `},
+		36: {subsumes: false, in: `a: 1,    b: null `},
+
+		37: {subsumes: true, in: ` a: true, b: true `},
+		38: {subsumes: false, in: `a: true, b: false `},
+
+		39: {subsumes: true, in: ` a: "a",    b: "a" `},
+		40: {subsumes: false, in: `a: "a",    b: "b" `},
+		41: {subsumes: true, in: ` a: string, b: "a" `},
+		42: {subsumes: false, in: `a: "a",    b: string `},
+
+		// Number typing (TODO)
+		//
+		// In principle, an "int" cannot assume an untyped "1", as "1" may
+		// still by typed as a float. They are two different type aspects. When
+		// considering, keep in mind that:
+		//   Key requirement: if A subsumes B, it must not be possible to
+		//   specialize B further such that A does not subsume B. HOWEVER,
+		//   The type conversion rules for conversion are INDEPENDENT of the
+		//   rules for subsumption!
+		// Consider:
+		// - only having number, but allowing user-defined types.
+		//   Subsumption would still work the same, but it may be somewhat
+		//   less weird.
+		// - making 1 always an int and 1.0 always a float.
+		//   - the int type would subsume any derived type from int.
+		//   - arithmetic would allow implicit conversions, but maybe not for
+		//     types.
+		//
+		// TODO: irrational numbers: allow untyped, but require explicit
+		//       trunking when assigning to float.
+		//
+		// a: number; cue.IsInteger(a) && a > 0
+		// t: (x) -> number; cue.IsInteger(a) && a > 0
+		// type x number: cue.IsInteger(x) && x > 0
+		// x: typeOf(number); cue.IsInteger(x) && x > 0
+		43: {subsumes: true, in: `a: 1, b: 1 `},
+		44: {subsumes: true, in: `a: 1.0, b: 1.0 `},
+		45: {subsumes: true, in: `a: 3.0, b: 3.0 `},
+		46: {subsumes: false, in: `a: 1.0, b: 1 `},
+		47: {subsumes: false, in: `a: 1, b: 1.0 `},
+		48: {subsumes: false, in: `a: 3, b: 3.0`},
+		49: {subsumes: true, in: `a: int, b: 1`},
+		50: {subsumes: true, in: `a: int, b: int & 1`},
+		51: {subsumes: true, in: `a: float, b: 1.0`},
+		52: {subsumes: false, in: `a: float, b: 1`},
+		53: {subsumes: false, in: `a: int, b: 1.0`},
+		54: {subsumes: true, in: `a: int, b: int`},
+		55: {subsumes: true, in: `a: number, b: int`},
+
+		// Structs
+		64: {subsumes: true, in: `a: {}, b: {}`},
+		65: {subsumes: true, in: `a: {}, b: {a: 1}`},
+		66: {subsumes: true, in: `a: {a:1}, b: {a:1, b:1}`},
+		67: {subsumes: true, in: `a: {s: { a:1} }, b: { s: { a:1, b:2 }}`},
+		68: {subsumes: true, in: `a: {}, b: {}`},
+		// TODO: allow subsumption of unevaluated values?
+		// ref not yet evaluated and not structurally equivalent
+		69: {subsumes: true, in: `a: {}, b: {} & c, c: {}`},
+
+		70: {subsumes: false, in: `a: {a:1}, b: {}`},
+		71: {subsumes: false, in: `a: {a:1, b:1}, b: {a:1}`},
+		72: {subsumes: false, in: `a: {s: { a:1} }, b: { s: {}}`},
+
+		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`},
+		88: {subsumes: false, in: `a: int, b: 1 | 2 | 3.1`},
+
+		89: {subsumes: true, in: `a: float | number, b: 1 | 2 | 3.1`},
+
+		90: {subsumes: false, in: `a: int, b: 1 | 2 | 3.1`},
+		91: {subsumes: true, in: `a: 1 | 2, b: 1`},
+		92: {subsumes: true, in: `a: 1 | 2, b: 2`},
+		93: {subsumes: false, in: `a: 1 | 2, b: 3`},
+
+		// 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`},
+		153: {subsumes: false, in: `a: int | *2, b: int | *2.0`},
+		154: {subsumes: true, in: `a: number | *2 | *3, b: number | *2`},
+		155: {subsumes: true, in: `a: number, b: number | *2`},
+
+		// Bounds
+		170: {subsumes: true, in: `a: >=2, b: >=2`},
+		171: {subsumes: true, in: `a: >=1, b: >=2`},
+		172: {subsumes: true, in: `a: >0, b: >=2`},
+		173: {subsumes: true, in: `a: >1, b: >1`},
+		174: {subsumes: true, in: `a: >=1, b: >1`},
+		175: {subsumes: false, in: `a: >1, b: >=1`},
+		176: {subsumes: true, in: `a: >=1, b: >=1`},
+		177: {subsumes: true, in: `a: <1, b: <1`},
+		178: {subsumes: true, in: `a: <=1, b: <1`},
+		179: {subsumes: false, in: `a: <1, b: <=1`},
+		180: {subsumes: true, in: `a: <=1, b: <=1`},
+
+		181: {subsumes: true, in: `a: !=1, b: !=1`},
+		182: {subsumes: false, in: `a: !=1, b: !=2`},
+
+		183: {subsumes: false, in: `a: !=1, b: <=1`},
+		184: {subsumes: true, in: `a: !=1, b: <1`},
+		185: {subsumes: false, in: `a: !=1, b: >=1`},
+		186: {subsumes: true, in: `a: !=1, b: <1`},
+
+		187: {subsumes: true, in: `a: !=1, b: <=0`},
+		188: {subsumes: true, in: `a: !=1, b: >=2`},
+		189: {subsumes: true, in: `a: !=1, b: >1`},
+
+		195: {subsumes: false, in: `a: >=2, b: !=2`},
+		196: {subsumes: false, in: `a: >2, b: !=2`},
+		197: {subsumes: false, in: `a: <2, b: !=2`},
+		198: {subsumes: false, in: `a: <=2, b: !=2`},
+
+		200: {subsumes: true, in: `a: =~"foo", b: =~"foo"`},
+		201: {subsumes: false, in: `a: =~"foo", b: =~"bar"`},
+		202: {subsumes: false, in: `a: =~"foo1", b: =~"foo"`},
+
+		203: {subsumes: true, in: `a: !~"foo", b: !~"foo"`},
+		204: {subsumes: false, in: `a: !~"foo", b: !~"bar"`},
+		205: {subsumes: false, in: `a: !~"foo", b: !~"foo1"`},
+
+		// The following is could be true, but we will not go down the rabbit
+		// hold of trying to prove subsumption of regular expressions.
+		210: {subsumes: false, in: `a: =~"foo", b: =~"foo1"`},
+		211: {subsumes: false, in: `a: !~"foo1", b: !~"foo"`},
+
+		220: {subsumes: true, in: `a: <5, b: 4`},
+		221: {subsumes: false, in: `a: <5, b: 5`},
+		222: {subsumes: true, in: `a: <=5, b: 5`},
+		223: {subsumes: false, in: `a: <=5.0, b: 5.00000001`},
+		224: {subsumes: true, in: `a: >5, b: 6`},
+		225: {subsumes: false, in: `a: >5, b: 5`},
+		226: {subsumes: true, in: `a: >=5, b: 5`},
+		227: {subsumes: false, in: `a: >=5, b: 4`},
+		228: {subsumes: true, in: `a: !=5, b: 6`},
+		229: {subsumes: false, in: `a: !=5, b: 5`},
+		230: {subsumes: false, in: `a: !=5.0, b: 5.0`},
+		231: {subsumes: false, in: `a: !=5.0, b: 5`},
+
+		250: {subsumes: true, in: `a: =~ #"^\d{3}$"#, b: "123"`},
+		251: {subsumes: false, in: `a: =~ #"^\d{3}$"#, b: "1234"`},
+		252: {subsumes: true, in: `a: !~ #"^\d{3}$"#, b: "1234"`},
+		253: {subsumes: false, in: `a: !~ #"^\d{3}$"#, b: "123"`},
+
+		// Conjunctions
+		300: {subsumes: true, in: `a: >0, b: >=2 & <=100`},
+		301: {subsumes: false, in: `a: >0, b: >=0 & <=100`},
+
+		310: {subsumes: true, in: `a: >=0 & <=100, b: 10`},
+		311: {subsumes: true, in: `a: >=0 & <=100, b: >=0 & <=100`},
+		312: {subsumes: false, in: `a: !=2 & !=4, b: >3`},
+		313: {subsumes: true, in: `a: !=2 & !=4, b: >5`},
+
+		314: {subsumes: false, in: `a: >=0 & <=100, b: >=0 & <=150`},
+		315: {subsumes: true, in: `a: >=0 & <=150, b: >=0 & <=100`},
+
+		// Disjunctions
+		330: {subsumes: true, in: `a: >5, b: >10 | 8`},
+		331: {subsumes: false, in: `a: >8, b: >10 | 8`},
+
+		// Optional fields
+		// Optional fields defined constraints on fields that are not yet
+		// defined. So even if such a field is not part of the output, it
+		// influences the lattice structure.
+		// For a given A and B, where A and B unify and where A has an optional
+		// field that is not defined in B, the addition of an incompatible
+		// value of that field in B can cause A and B to no longer unify.
+		//
+		400: {subsumes: false, in: `a: {foo: 1}, b: {}`},
+		401: {subsumes: false, in: `a: {foo?: 1}, b: {}`},
+		402: {subsumes: true, in: `a: {}, b: {foo: 1}`},
+		403: {subsumes: true, in: `a: {}, b: {foo?: 1}`},
+
+		404: {subsumes: true, in: `a: {foo: 1}, b: {foo: 1}`},
+		405: {subsumes: true, in: `a: {foo?: 1}, b: {foo: 1}`},
+		406: {subsumes: true, in: `a: {foo?: 1}, b: {foo?: 1}`},
+		407: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`},
+
+		408: {subsumes: false, in: `a: {foo: 1}, b: {foo: 2}`},
+		409: {subsumes: false, in: `a: {foo?: 1}, b: {foo: 2}`},
+		410: {subsumes: false, in: `a: {foo?: 1}, b: {foo?: 2}`},
+		411: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 2}`},
+
+		412: {subsumes: true, in: `a: {foo: number}, b: {foo: 2}`},
+		413: {subsumes: true, in: `a: {foo?: number}, b: {foo: 2}`},
+		414: {subsumes: true, in: `a: {foo?: number}, b: {foo?: 2}`},
+		415: {subsumes: false, in: `a: {foo: number}, b: {foo?: 2}`},
+
+		416: {subsumes: false, in: `a: {foo: 1}, b: {foo: number}`},
+		417: {subsumes: false, in: `a: {foo?: 1}, b: {foo: number}`},
+		418: {subsumes: false, in: `a: {foo?: 1}, b: {foo?: number}`},
+		419: {subsumes: false, in: `a: {foo: 1}, b: {foo?: number}`},
+
+		// 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}`},
+
+		// TODO: the subNoOptional mode used to be used by the equality check.
+		// Now this has its own implementation it is no longer necessary. Keep
+		// around for now in case we still need the more permissive equality
+		// check that can be created by using subsumption.
+		//
+		// 440: {subsumes: true, in: `a: {foo?: 1}, b: {}`, mode: subNoOptional},
+		// 441: {subsumes: true, in: `a: {}, b: {foo?: 1}`, mode: subNoOptional},
+		// 442: {subsumes: true, in: `a: {foo?: 1}, b: {foo: 1}`, mode: subNoOptional},
+		// 443: {subsumes: true, in: `a: {foo?: 1}, b: {foo?: 1}`, mode: subNoOptional},
+		// 444: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`, mode: subNoOptional},
+		// 445: {subsumes: true, in: `a: close({}), b: {foo?: 1}`, mode: subNoOptional},
+		// 446: {subsumes: true, in: `a: close({}), b: close({foo?: 1})`, mode: subNoOptional},
+		// 447: {subsumes: true, in: `a: {}, b: close({})`, mode: subNoOptional},
+		// 448: {subsumes: true, in: `a: {}, b: close({foo?: 1})`, mode: subNoOptional},
+
+		// Lists
+		506: {subsumes: true, in: `a: [], b: [] `},
+		507: {subsumes: true, in: `a: [1], b: [1] `},
+		508: {subsumes: false, in: `a: [1], b: [2] `},
+		509: {subsumes: false, in: `a: [1], b: [2, 3] `},
+		510: {subsumes: true, in: `a: [{b: string}], b: [{b: "foo"}] `},
+		511: {subsumes: true, in: `a: [...{b: string}], b: [{b: "foo"}] `},
+		512: {subsumes: false, in: `a: [{b: "foo"}], b: [{b: string}] `},
+		513: {subsumes: false, in: `a: [{b: string}], b: [{b: "foo"}, ...{b: "foo"}] `},
+		520: {subsumes: false, in: `a: [_, int, ...], b: [int, string, ...string] `},
+
+		// Closed structs.
+		600: {subsumes: false, in: `a: close({}), 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})`},
+		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({})`},
+
+		// New in new evaluator.
+		611: {subsumes: false, in: `a: close({foo?:1}), b: close({bar?: 1})`},
+		612: {subsumes: true, in: `a: {foo?:1}, b: close({bar?: 1})`},
+		613: {subsumes: true, in: `a: {foo?:1}, b: close({bar: 1})`},
+
+		// Definitions are not regular fields.
+		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},
+
+		// Schema values
+		800: {subsumes: true, in: `a: close({}), b: {foo: 1}`, mode: subSchema},
+		// TODO(eval): FIX
+		// 801: {subsumes: true, in: `a: {[string]: int}, b: {foo: 1}`, mode: subSchema},
+		804: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`, mode: subSchema},
+		805: {subsumes: true, in: `a: close({}), b: {foo?: 1}`, mode: subSchema},
+		806: {subsumes: true, in: `a: close({}), b: close({foo?: 1})`, mode: subSchema},
+		807: {subsumes: true, in: `a: {}, b: close({})`, mode: subSchema},
+		808: {subsumes: false, in: `a: {[string]: 1}, b: {foo: 2}`, mode: subSchema},
+		809: {subsumes: true, in: `a: {}, b: close({foo?: 1})`, mode: subSchema},
+	}
+
+	re := regexp.MustCompile(`a: (.*).*b: ([^\n]*)`)
+	for i, tc := range testCases {
+		if tc.in == "" {
+			continue
+		}
+		m := re.FindStringSubmatch(strings.Join(strings.Split(tc.in, "\n"), ""))
+		const cutset = "\n ,"
+		key := strings.Trim(m[1], cutset) + " ⊑ " + strings.Trim(m[2], cutset)
+
+		r := runtime.New()
+
+		t.Run(strconv.Itoa(i)+"/"+key, func(t *testing.T) {
+
+			file, err := parser.ParseFile("subsume", tc.in)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			root, errs := compile.Files(nil, r, file)
+			if errs != nil {
+				t.Fatal(errs)
+			}
+
+			ctx := eval.NewContext(r, root)
+			root.Finalize(ctx)
+
+			// Use low-level lookup to avoid evaluation.
+			var a, b adt.Value
+			for _, arc := range root.Arcs {
+				switch arc.Label {
+				case ctx.StringLabel("a"):
+					a = arc
+				case ctx.StringLabel("b"):
+					b = arc
+				}
+			}
+
+			switch tc.mode {
+			case subNone:
+				err = Value(ctx, a, b)
+			case subSchema:
+				err = API.Value(ctx, a, b)
+			// TODO: see comments above.
+			// case subNoOptional:
+			// 	err = IgnoreOptional.Value(ctx, a, b)
+			case subFinal:
+				err = Final.Value(ctx, a, b)
+			}
+			got := err == nil
+
+			if got != tc.subsumes {
+				t.Errorf("got %v; want %v (%v vs %v)", got, tc.subsumes, a.Kind(), b.Kind())
+			}
+		})
+	}
+}
diff --git a/internal/core/subsume/vertex.go b/internal/core/subsume/vertex.go
new file mode 100644
index 0000000..6ece49e
--- /dev/null
+++ b/internal/core/subsume/vertex.go
@@ -0,0 +1,256 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package subsume
+
+import (
+	"cuelang.org/go/internal/core/adt"
+	"cuelang.org/go/internal/core/export"
+)
+
+// Notes:
+// - Can optional fields of y can always be ignored here? Maybe not in the
+//   schema case.
+// - Definitions of y can be ignored in data mode.
+//
+// TODO(perf): use merge sort where possible.
+func (s *subsumer) vertices(x, y *adt.Vertex) bool {
+	if x == y {
+		return true
+	}
+
+	if s.Defaults {
+		x = x.Default()
+		y = y.Default()
+	}
+
+	if b, _ := y.Value.(*adt.Bottom); b != nil {
+		// If the value is incomplete, the error is not final. So either check
+		// structural equivalence or return an error.
+		return !b.IsIncomplete()
+	}
+
+	ctx := s.ctx
+
+	final := y.IsData() || s.Final
+
+	switch v := x.Value.(type) {
+	case *adt.Bottom:
+		return false
+
+	case *adt.ListMarker:
+		if !y.IsList() {
+			s.errf("list does not subsume %s (type %s)", ctx.Str(y), y.Kind())
+			return false
+		}
+		if !s.listVertices(x, y) {
+			return false
+		}
+		// TODO: allow other arcs alongside list arc.
+		return true
+
+	case *adt.StructMarker:
+		_, ok := y.Value.(*adt.StructMarker)
+		if !ok {
+			return false
+		}
+
+	default:
+		if !s.values(v, y.Value) {
+			return false
+		}
+
+		// Embedded scalars could still have arcs.
+		if final {
+			return true
+		}
+	}
+
+	xClosed := x.IsClosed(ctx) && !s.IgnoreClosedness
+	yClosed := y.IsClosed(ctx) && !s.IgnoreClosedness
+
+	if xClosed && !yClosed && !final {
+		return false
+	}
+
+	types := x.OptionalTypes()
+	if !final && !s.IgnoreOptional && types&(adt.HasPattern|adt.HasAdditional) != 0 {
+		// TODO: there are many cases where pattern constraints can be checked.
+		s.inexact = true
+		return false
+	}
+
+	// All arcs in x must exist in y and its values must subsume.
+	xFeatures := export.VertexFeatures(x)
+	for _, f := range xFeatures {
+		if s.Final && !f.IsRegular() {
+			continue
+		}
+
+		a := x.Lookup(f)
+		aOpt := false
+		if a == nil {
+			// x.f is optional
+			if s.IgnoreOptional {
+				continue
+			}
+
+			a = &adt.Vertex{Label: f}
+			x.MatchAndInsert(ctx, a)
+			a.Finalize(ctx)
+
+			// 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.
+			if a.Kind() == adt.TopKind {
+				continue
+			}
+
+			aOpt = true
+		}
+
+		b := y.Lookup(f)
+		if b == nil {
+			// y.f is optional
+			if !aOpt {
+				s.errf("required field %s is optional in subsumed value",
+					f.SelectorString(ctx))
+				return false
+			}
+
+			// If f is undefined for y and if y 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 y
+			// is invalid, so return false regardless
+			if !y.Accept(ctx, f) || y.IsData() || s.Final {
+				continue
+			}
+
+			b = &adt.Vertex{Label: f}
+			y.MatchAndInsert(ctx, b)
+			b.Finalize(ctx)
+		}
+
+		if s.values(a, b) {
+			continue
+		}
+
+		s.missing = f
+		s.gt = a
+		s.lt = y
+
+		s.errf("field %s not not present in %s",
+			f.SelectorString(ctx), ctx.Str(y))
+		return false
+	}
+
+	if xClosed && !yClosed && !s.Final {
+		s.errf("closed struct does not subsume open struct")
+		return false
+	}
+
+	yFeatures := export.VertexFeatures(y)
+outer:
+	for _, f := range yFeatures {
+		if s.Final && !f.IsRegular() {
+			continue
+		}
+
+		for _, g := range xFeatures {
+			if g == f {
+				// already validated
+				continue outer
+			}
+		}
+
+		b := y.Lookup(f)
+		if b == nil {
+			if s.IgnoreOptional || s.Final {
+				continue
+			}
+
+			b = &adt.Vertex{Label: f}
+			y.MatchAndInsert(ctx, b)
+		}
+
+		if !x.Accept(ctx, f) {
+			if s.Profile.IgnoreClosedness {
+				continue
+			}
+			s.errf("field %s not allowed in closed struct",
+				f.SelectorString(ctx))
+			return false
+		}
+
+		a := &adt.Vertex{Label: f}
+		x.MatchAndInsert(ctx, a)
+		if len(a.Conjuncts) == 0 {
+			// It is accepted and has no further constraints, so all good.
+			continue
+		}
+
+		a.Finalize(ctx)
+		b.Finalize(ctx)
+
+		if !s.vertices(a, b) {
+			return false
+		}
+	}
+
+	return true
+}
+
+func (s *subsumer) listVertices(x, y *adt.Vertex) bool {
+	ctx := s.ctx
+
+	if !y.IsData() && x.IsClosed(ctx) && !y.IsClosed(ctx) {
+		return false
+	}
+
+	xElems := x.Elems()
+	yElems := y.Elems()
+
+	switch {
+	case len(xElems) == len(yElems):
+	case len(xElems) > len(yElems):
+		return false
+	case x.IsClosed(ctx):
+		return false
+	default:
+		a := &adt.Vertex{Label: adt.InvalidLabel}
+		x.MatchAndInsert(ctx, a)
+		a.Finalize(ctx)
+
+		// x must be open
+		for _, b := range yElems[len(xElems):] {
+			if !s.vertices(a, b) {
+				return false
+			}
+		}
+
+		if !y.IsClosed(ctx) {
+			b := &adt.Vertex{Label: adt.InvalidLabel}
+			y.MatchAndInsert(ctx, b)
+			b.Finalize(ctx)
+		}
+	}
+
+	for i, a := range xElems {
+		if !s.vertices(a, yElems[i]) {
+			return false
+		}
+	}
+
+	return true
+}