cue: support list addition

Change-Id: Iba4c4c83c6b4c55af8743199d1e9fa01be86182c
Reviewed-on: https://cue-review.googlesource.com/c/1563
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cue/binop.go b/cue/binop.go
index b3fe6c7..a6976ba 100644
--- a/cue/binop.go
+++ b/cue/binop.go
@@ -956,6 +956,25 @@
 		}
 		return &list{baseValue: binSrc(src.Pos(), op, x, other), a: a, typ: typ, len: n}
 
+	case opAdd:
+		y, ok := other.(*list)
+		if !ok {
+			break
+		}
+		n := &list{baseValue: binSrc(src.Pos(), op, x, other), typ: y.typ}
+		n.a = append(x.a, y.a...)
+		switch v := y.len.(type) {
+		case *numLit:
+			// Closed list
+			ln := &numLit{numBase: v.numBase}
+			ln.v.SetInt64(int64(len(n.a)))
+			n.len = ln
+		default:
+			// Open list
+			n.len = y.len
+		}
+		return n
+
 	case opMul:
 		k := other.kind()
 		if !k.isAnyOf(intKind) {
diff --git a/cue/kind.go b/cue/kind.go
index c5d281f..fdc4aed 100644
--- a/cue/kind.go
+++ b/cue/kind.go
@@ -55,6 +55,7 @@
 	referenceKind
 
 	atomKind     = (listKind - 1) &^ unknownKind
+	addableKind  = (structKind - 1) &^ unknownKind
 	concreteKind = (lambdaKind - 1) &^ unknownKind
 
 	// doneKind indicates a value can not further develop on its own (i.e. not a
@@ -257,9 +258,8 @@
 			return boolKind | catBits, false
 		}
 	case opAdd:
-		if u.isAnyOf(atomKind) {
-			return u&(atomKind) | catBits, false
-			// u&(durationKind|intKind)
+		if u.isAnyOf(addableKind) {
+			return u&(addableKind) | catBits, false
 		}
 	case opSub:
 		if u.isAnyOf(scalarKinds) {
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index ba47879..5780db3 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -279,6 +279,10 @@
 			b0: 'foo' + 'bar'
 			b1: 3 * 'abc'
 			b2: 'abc' * 2
+
+			// TODO: consider the semantics of this and perhaps allow this.
+			e0: "a" + ''
+			e1: 'b' + "c"
 		`,
 		out: `<0>{` +
 			`s0: "foobar", ` +
@@ -286,7 +290,10 @@
 			`s2: "abcabc", ` +
 			`b0: 'foobar', ` +
 			`b1: 'abcabcabc', ` +
-			`b2: 'abcabc'` +
+			`b2: 'abcabc', ` +
+
+			`e0: _|_(("a" + ''):unsupported op +(string, bytes)), ` +
+			`e1: _|_(('b' + "c"):unsupported op +(bytes, string))` +
 			`}`,
 	}, {
 		desc: "escaping",
@@ -834,6 +841,44 @@
 			l6: 3*[...int]
 			l7: 3*[1, ...int]
 			l8: 3*[1, 2, ...int]
+
+			s0: [] + []
+			s1: [1] + []
+			s2: [] + [2]
+			s3: [1] + [2]
+			s4: [1,2] + []
+			s5: [] + [1,2]
+			s6: [1] + [1,2]
+			s7: [1,2] + [1]
+			s8: [1,2] + [1,2]
+			s9: [] + [...]
+			s10: [1] + [...]
+			s11: [] + [2, ...]
+			s12: [1] + [2, ...]
+			s13: [1,2] + [...]
+			s14: [] + [1,2, ...]
+			s15: [1] + [1,2, ...]
+			s16: [1,2] + [1, ...]
+			s17: [1,2] + [1,2, ...]
+
+			s18: [...] + []
+			s19: [1, ...] + []
+			s20: [...] + [2]
+			s21: [1, ...] + [2]
+			s22: [1,2, ...] + []
+			s23: [...] + [1,2]
+			s24: [1, ...] + [1,2]
+			s25: [1,2, ...] + [1]
+			s26: [1,2, ...] + [1,2]
+			s27: [...] + [...]
+			s28: [1, ...] + [...]
+			s29: [...] + [2, ...]
+			s30: [1, ...] + [2, ...]
+			s31: [1,2, ...] + [...]
+			s32: [...] + [1,2, ...]
+			s33: [1, ...] + [1,2, ...]
+			s34: [1,2, ...] + [1, ...]
+			s35: [1,2, ...] + [1,2, ...]
 			`,
 		out: `<0>{l0: [1,2,3,1,2,3,1,2,3], ` +
 			`l1: [], ` +
@@ -843,7 +888,46 @@
 			`l5: (<=2 * (int * [int])), ` +
 			`l6: [, ...int], ` +
 			`l7: [1,1,1, ...int], ` +
-			`l8: [1,2,1,2,1,2, ...int]` +
+			`l8: [1,2,1,2,1,2, ...int], ` +
+
+			`s0: [], ` +
+			`s1: [1], ` +
+			`s2: [2], ` +
+			`s3: [1,2], ` +
+			`s4: [1,2], ` +
+			`s5: [1,2], ` +
+			`s6: [1,1,2], ` +
+			`s7: [1,2,1], ` +
+			`s8: [1,2,1,2], ` +
+			`s9: [, ...], ` +
+			`s10: [1, ...], ` +
+			`s11: [2, ...], ` +
+			`s12: [1,2, ...], ` +
+			`s13: [1,2, ...], ` +
+			`s14: [1,2, ...], ` +
+			`s15: [1,1,2, ...], ` +
+			`s16: [1,2,1, ...], ` +
+			`s17: [1,2,1,2, ...], ` +
+
+			`s18: [], ` +
+			`s19: [1], ` +
+			`s20: [2], ` +
+			`s21: [1,2], ` +
+			`s22: [1,2], ` +
+			`s23: [1,2], ` +
+			`s24: [1,1,2], ` +
+			`s25: [1,2,1], ` +
+			`s26: [1,2,1,2], ` +
+			`s27: [, ...], ` +
+			`s28: [1, ...], ` +
+			`s29: [2, ...], ` +
+			`s30: [1,2, ...], ` +
+			`s31: [1,2, ...], ` +
+			`s32: [1,2, ...], ` +
+			`s33: [1,1,2, ...], ` +
+			`s34: [1,2,1, ...], ` +
+			`s35: [1,2,1,2, ...]` +
+
 			`}`,
 	}, {
 		desc: "correct error messages",
diff --git a/doc/ref/spec.md b/doc/ref/spec.md
index 09b4015..94228ec 100644
--- a/doc/ref/spec.md
+++ b/doc/ref/spec.md
@@ -1544,6 +1544,7 @@
 [ 1, 2 ]      + [ 3, 4 ]       // [ 1, 2, 3, 4 ]
 [ 1, 2, ... ] + [ 3, 4 ]       // [ 1, 2, 3, 4 ]
 [ 1, 2 ]      + [ 3, 4, ... ]  // [ 1, 2, 3, 4, ... ]
+[ 1, 2, ... ] + [ 3, 4, ... ]  // [ 1, 2, 3, 4, ... ]
 ```
 
 Lists can be multiplied with a non-negative`int` using the `*` operator