internal/core/adt: do list artihmetic with comprehensions

Change-Id: I60cf6a2ced7def18a1e0196d7a70a8e52ace9b12
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7761
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/internal/core/adt/binop.go b/internal/core/adt/binop.go
index b43bea6..73670c2 100644
--- a/internal/core/adt/binop.go
+++ b/internal/core/adt/binop.go
@@ -183,20 +183,36 @@
 			return c.newBytes(b)
 
 		case leftKind == ListKind && rightKind == ListKind:
-			a := c.Elems(left)
-			b := c.Elems(right)
+			// TODO: get rid of list addition. Semantically it is somewhat
+			// unclear and, as it turns out, it is also hard to get right.
+			// Simulate addition with comprehensions now.
 			if err := c.Err(); err != nil {
 				return err
 			}
-			n := c.newList(c.src, nil)
-			if err := n.appendListArcs(a); err != nil {
-				return err
+
+			x := MakeIdentLabel(c, "x", "")
+
+			forClause := func(src Expr) *ForClause {
+				return &ForClause{
+					Value: x,
+					Src:   src,
+					Dst: &ValueClause{&StructLit{Decls: []Decl{
+						&FieldReference{UpCount: 1, Label: x},
+					}}},
+				}
 			}
-			if err := n.appendListArcs(b); err != nil {
-				return err
+
+			list := &ListLit{
+				Elems: []Elem{
+					forClause(left),
+					forClause(right),
+				},
 			}
-			// n.isList = true
-			// n.IsClosed = true
+
+			n := &Vertex{}
+			n.AddConjunct(MakeRootConjunct(c.Env(0), list))
+			n.Finalize(c)
+
 			return n
 		}
 
@@ -230,21 +246,30 @@
 			fallthrough
 
 		case leftKind == IntKind && rightKind == ListKind:
-			a := c.Elems(right)
-			n := c.newList(c.src, nil)
-			// n.IsClosed = true
-			index := int64(0)
+			// TODO: get rid of list multiplication.
+
+			list := &ListLit{}
+			x := MakeIdentLabel(c, "x", "")
+
 			for i := c.uint64(left, "list multiplier"); i > 0; i-- {
-				for _, a := range a {
-					f, _ := MakeLabel(a.Source(), index, IntLabel)
-					n.Arcs = append(n.Arcs, &Vertex{
-						Parent:    n,
-						Label:     f,
-						Conjuncts: a.Conjuncts,
-					})
-					index++
-				}
+				list.Elems = append(list.Elems,
+					&ForClause{
+						Value: x,
+						Src:   right,
+						Dst: &ValueClause{&StructLit{Decls: []Decl{
+							&FieldReference{UpCount: 1, Label: x},
+						}}},
+					},
+				)
 			}
+			if err := c.Err(); err != nil {
+				return err
+			}
+
+			n := &Vertex{}
+			n.AddConjunct(MakeRootConjunct(c.Env(0), list))
+			n.Finalize(c)
+
 			return n
 		}
 
diff --git a/internal/core/adt/composite.go b/internal/core/adt/composite.go
index 6d160d9..00f0737 100644
--- a/internal/core/adt/composite.go
+++ b/internal/core/adt/composite.go
@@ -572,23 +572,6 @@
 	return a
 }
 
-func (v *Vertex) appendListArcs(arcs []*Vertex) (err *Bottom) {
-	for _, a := range arcs {
-		// TODO(list): BUG this only works if lists do not have definitions
-		// fields.
-		label, err := MakeLabel(a.Source(), int64(len(v.Arcs)), IntLabel)
-		if err != nil {
-			return &Bottom{Src: a.Source(), Err: err}
-		}
-		v.Arcs = append(v.Arcs, &Vertex{
-			Parent:    v,
-			Label:     label,
-			Conjuncts: a.Conjuncts,
-		})
-	}
-	return nil
-}
-
 // An Conjunct is an Environment-Expr pair. The Environment is the starting
 // point for reference lookup for any reference contained in X.
 type Conjunct struct {
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
index a2dd75f..bd4a02c 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -875,11 +875,7 @@
 		return err
 	}
 
-	value := BinOp(c, x.Op, left, right)
-	if n, ok := value.(*Vertex); ok && n.IsList() {
-		n.UpdateStatus(Partial)
-	}
-	return value
+	return BinOp(c, x.Op, left, right)
 }
 
 // A CallExpr represents a call to a builtin.
diff --git a/internal/core/eval/eval.go b/internal/core/eval/eval.go
index 8ab1974..30499e1 100644
--- a/internal/core/eval/eval.go
+++ b/internal/core/eval/eval.go
@@ -1731,6 +1731,9 @@
 	// TODO: disallow adding conjuncts when cache set?
 	arc.AddConjunct(x)
 
+	adt.Assert("invalid adt.ID",
+		x.CloseID == 0 || int(x.CloseID) < len(closedInfo(n.node).Canopy))
+
 	if isNew {
 		closedInfo(n.node).visitAllFieldSets(func(o *fieldSet) {
 			o.MatchAndInsert(ctx, arc)