internal/core/subsume: various bug fixes

And making semantics close to that of v0.2.

Fixes #566

Change-Id: I97c5f9733c40d92b40f5e780cd1d6e2632de06ff
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7743
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/cue/types.go b/cue/types.go
index fa0d082..e8f97f2 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -1718,8 +1718,9 @@
 // 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().opCtx
 	p := subsume.Profile{Defaults: true}
-	return p.Check(v.ctx().opCtx, v.v, w.v)
+	return p.Check(ctx, v.v, w.v)
 }
 
 // Unify reports the greatest lower bound of v and w.
diff --git a/cue/types_test.go b/cue/types_test.go
index 87df504..5bda591 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -1159,6 +1159,88 @@
 	}
 }
 
+func TestSubsume(t *testing.T) {
+	a := ParsePath("a")
+	b := ParsePath("b")
+	testCases := []struct {
+		value   string
+		pathA   Path
+		pathB   Path
+		options []Option
+		want    bool
+	}{{
+		value: `4`,
+		want:  true,
+	}, {
+		value: `a: string, b: "foo"`,
+		pathA: a,
+		pathB: b,
+		want:  true,
+	}, {
+		value: `a: string, b: "foo"`,
+		pathA: b,
+		pathB: a,
+		want:  false,
+	}, {
+		value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`,
+		pathA: a,
+		pathB: b,
+		want:  true,
+	}, {
+		value: `a: [string,  4], b: ["foo", 4]`,
+		pathA: a,
+		pathB: b,
+		want:  true,
+	}, {
+		value: `a: [...string], b: ["foo"]`,
+		pathA: a,
+		pathB: b,
+		want:  true,
+	}, {
+		value: `a: [...int], b: ["foo"]`,
+		pathA: a,
+		pathB: b,
+		want:  false,
+	}, {
+		// Issue #566
+		// Closed struct subsuming open struct.
+		value: `
+		#Run: { action: "run", command: [...string] }
+		b: { action: "run", command: ["echo", "hello"] }
+		`,
+		pathA: ParsePath("#Run"),
+		pathB: b,
+
+		// NOTE: this is for v0.2 compatibility. Logically a closed struct
+		// does not subsume an open struct. One could argue that the default
+		// of an open struct is the closed struct with the minimal number
+		// of fields that is an instance of it, though.
+		want: true, // open struct is not subsumed by closed if not final.
+	}, {
+		// Issue #566
+		// Closed struct subsuming open struct.
+		value: `
+			#Run: { action: "run", command: [...string] }
+			b: { action: "run", command: ["echo", "hello"] }
+			`,
+		pathA:   ParsePath("#Run"),
+		pathB:   b,
+		options: []Option{Final()},
+		want:    true,
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			v := getInstance(t, tc.value)
+			a := v.Value().LookupPath(tc.pathA)
+			b := v.Value().LookupPath(tc.pathB)
+			got := a.Subsume(b, tc.options...) == nil
+			if got != tc.want {
+				t.Errorf("got %v (%v); want %v (%v)", got, a, tc.want, b)
+			}
+		})
+	}
+}
+
 func TestSubsumes(t *testing.T) {
 	a := []string{"a"}
 	b := []string{"b"}
@@ -1190,6 +1272,24 @@
 		pathA: a,
 		pathB: b,
 		want:  true,
+	}, {
+		value: `a: [...string], b: ["foo"]`,
+		pathA: a,
+		pathB: b,
+		want:  true,
+	}, {
+		value: `a: [...int], b: ["foo"]`,
+		pathA: a,
+		pathB: b,
+		want:  false,
+	}, {
+		value: `
+		a: { action: "run", command: [...string] }
+		b: { action: "run", command: ["echo", "hello"] }
+		`,
+		pathA: a,
+		pathB: b,
+		want:  true,
 	}}
 	for _, tc := range testCases {
 		t.Run(tc.value, func(t *testing.T) {
diff --git a/internal/core/subsume/value.go b/internal/core/subsume/value.go
index c6e691f..1adb068 100644
--- a/internal/core/subsume/value.go
+++ b/internal/core/subsume/value.go
@@ -34,7 +34,6 @@
 	}
 
 	if s.Defaults {
-		a = adt.Default(a)
 		b = adt.Default(b)
 	}
 
diff --git a/internal/core/subsume/value_test.go b/internal/core/subsume/value_test.go
index e5ed21d..06bea8a 100644
--- a/internal/core/subsume/value_test.go
+++ b/internal/core/subsume/value_test.go
@@ -32,6 +32,7 @@
 	subFinal
 	subNoOptional
 	subSchema
+	subDefaults
 )
 
 func TestValues(t *testing.T) {
@@ -367,6 +368,41 @@
 		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},
+
+		// Lists
+		950: {subsumes: true, in: `a: [], b: []`},
+		951: {subsumes: true, in: `a: [...], b: []`},
+		952: {subsumes: true, in: `a: [...], b: [...]`},
+		953: {subsumes: false, in: `a: [], b: [...]`},
+
+		954: {subsumes: true, in: `a: [2], b: [2]`},
+		955: {subsumes: true, in: `a: [int], b: [2]`},
+		956: {subsumes: false, in: `a: [2], b: [int]`},
+		957: {subsumes: true, in: `a: [int], b: [int]`},
+
+		958: {subsumes: true, in: `a: [...2], b: [2]`},
+		959: {subsumes: true, in: `a: [...int], b: [2]`},
+		960: {subsumes: false, in: `a: [...2], b: [int]`},
+		961: {subsumes: true, in: `a: [...int], b: [int]`},
+
+		962: {subsumes: false, in: `a: [2], b: [...2]`},
+		963: {subsumes: false, in: `a: [int], b: [...2]`},
+		964: {subsumes: false, in: `a: [2], b: [...int]`},
+		965: {subsumes: false, in: `a: [int], b: [...int]`},
+
+		966: {subsumes: false, in: `a: [...int], b: ["foo"]`},
+		967: {subsumes: false, in: `a: ["foo"], b: [...int]`},
+
+		// Defaults:
+		// TODO: for the purpose of v0.2 compatibility these
+		// evaluate to true. Reconsider before making this package
+		// public.
+		970: {subsumes: true, in: `a: [], b: [...int]`, mode: subDefaults},
+		971: {subsumes: true, in: `a: [2], b: [2, ...int]`, mode: subDefaults},
+
+		// Final
+		980: {subsumes: true, in: `a: [], b: [...int]`, mode: subFinal},
+		981: {subsumes: true, in: `a: [2], b: [2, ...int]`, mode: subFinal},
 	}
 
 	re := regexp.MustCompile(`a: (.*).*b: ([^\n]*)`)
@@ -414,6 +450,9 @@
 			// TODO: see comments above.
 			// case subNoOptional:
 			// 	err = IgnoreOptional.Value(ctx, a, b)
+			case subDefaults:
+				p := Profile{Defaults: true}
+				err = p.Value(ctx, a, b)
 			case subFinal:
 				err = Final.Value(ctx, a, b)
 			}
diff --git a/internal/core/subsume/vertex.go b/internal/core/subsume/vertex.go
index 6ece49e..189b1d9 100644
--- a/internal/core/subsume/vertex.go
+++ b/internal/core/subsume/vertex.go
@@ -31,7 +31,6 @@
 	}
 
 	if s.Defaults {
-		x = x.Default()
 		y = y.Default()
 	}
 
@@ -78,7 +77,10 @@
 	}
 
 	xClosed := x.IsClosed(ctx) && !s.IgnoreClosedness
-	yClosed := y.IsClosed(ctx) && !s.IgnoreClosedness
+	// TODO: this should not close for taking defaults. Do a more principled
+	// makeover of this package before making it public, though.
+	yClosed := s.Final || s.Defaults ||
+		(y.IsClosed(ctx) && !s.IgnoreClosedness)
 
 	if xClosed && !yClosed && !final {
 		return false
@@ -228,7 +230,7 @@
 	case x.IsClosed(ctx):
 		return false
 	default:
-		a := &adt.Vertex{Label: adt.InvalidLabel}
+		a := &adt.Vertex{Label: 0}
 		x.MatchAndInsert(ctx, a)
 		a.Finalize(ctx)