pkg/internal: fix decimal and string list errors

Fixes regression introduced in 0ffde15.
Problem was bug that converted an incomplete error to a
permanent error.

The solution is to not use the public API, but rather
the internal one, and pass errors up more directly.

This also fixes a bug with slice, which masked
unevaluated arcs with a finalized one. This issue
did not surface before as the API would proactively
evaluate arcs.  For this reason (and because slices
are officially not supported), the lower level
implementations now defensively still proactively
evaluate, even if this should not be necessary.

Fixes #776

Change-Id: I76f9e36cf28d5bde0f0a629958c7d07b909d48f5
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8742
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/testdata/builtins/incomplete.txtar b/cue/testdata/builtins/incomplete.txtar
index 20a2703..3d3eea9 100644
--- a/cue/testdata/builtins/incomplete.txtar
+++ b/cue/testdata/builtins/incomplete.txtar
@@ -1,5 +1,8 @@
 -- in.cue --
-import "list"
+import (
+  "list"
+  "strings"
+)
 
 
 list1: {
@@ -40,36 +43,90 @@
     len('sf' | 'dd')
 }
 
+incompleteArgDecimalList: {
+    a: #a & {param: 123}
+    #a: {
+        param: int
+        transformed: +param
+        max: list.Max([transformed])
+    }
+}
+
+incompleteArgStringList: {
+    a: #a & {param: "123"}
+    #a: {
+        param: string
+        transformed: param+""
+        joined: strings.Join([transformed], "-")
+    }
+}
+
+incompleteList: {
+    x: _
+    decimal: list.Max(x)
+    str:     strings.Join(x, "")
+}
+
+incompleteListError: {
+    x: y + []
+    y: _
+    decimal: list.Max(x)
+    str:     strings.Join(x, "")
+}
+
+badListType: {
+    x: 2
+    decimal: list.Max(x)
+    str:     strings.Join(x, "")
+}
+
+badListError: {
+    x: 2 + y
+    y: "foo"
+    decimal: list.Max(x)
+    str:     strings.Join(x, "")
+}
+
 -- out/eval --
-(struct){
+Errors:
+badListType.decimal: cannot use 2 (type int) as list in argument 1 to list.Max:
+    ./in.cue:77:8
+badListType.str: cannot use 2 (type int) as list in argument 1 to strings.Join:
+    ./in.cue:77:8
+badListError.x: invalid operands 2 and "foo" to '+' (type int and string):
+    ./in.cue:83:8
+
+Result:
+(_|_){
+  // [eval]
   list1: (struct){
     Out1: (#list){
       0: (_|_){
         // [cycle] cycle error:
-        //     ./in.cue:13:23
+        //     ./in.cue:16:23
       }
     }
     Out2: (#list){
       0: (_|_){
         // [cycle] cycle error:
-        //     ./in.cue:13:23
+        //     ./in.cue:16:23
       }
     }
     Out3: (#list){
       0: (_|_){
         // [cycle] cycle error:
-        //     ./in.cue:13:23
+        //     ./in.cue:16:23
       }
     }
     Top: (#list){
       0: (_|_){
         // [cycle] cycle error:
-        //     ./in.cue:13:23
+        //     ./in.cue:16:23
       }
     }
     _Sub: (_|_){
       // [incomplete] list1._Sub: undefined field b:
-      //     ./in.cue:16:13
+      //     ./in.cue:19:13
     }
     a: (struct){
     }
@@ -77,23 +134,23 @@
   list2: (struct){
     Out1: (_|_){
       // [cycle] cycle error:
-      //     ./in.cue:27:21
+      //     ./in.cue:30:21
     }
     Out2: (_|_){
       // [cycle] cycle error:
-      //     ./in.cue:27:21
+      //     ./in.cue:30:21
     }
     Out3: (_|_){
       // [cycle] cycle error:
-      //     ./in.cue:27:21
+      //     ./in.cue:30:21
     }
     _Top: (_|_){
       // [cycle] cycle error:
-      //     ./in.cue:27:21
+      //     ./in.cue:30:21
     }
     #Sub: (_|_){
       // [incomplete] list2.#Sub: undefined field b:
-      //     ./in.cue:30:13
+      //     ./in.cue:33:13
     }
     a: (struct){
     }
@@ -101,12 +158,102 @@
   value1: (struct){
     a: (_|_){
       // [incomplete] value1.a: unresolved disjunction 'sf' | 'dd' (type bytes):
-      //     ./in.cue:35:8
+      //     ./in.cue:38:8
     }
   }
   value2: (_|_){
     // [incomplete] value2: unresolved disjunction 'sf' | 'dd' (type bytes):
-    //     ./in.cue:39:5
+    //     ./in.cue:42:5
+  }
+  incompleteArgDecimalList: (struct){
+    a: (#struct){
+      param: (int){ 123 }
+      transformed: (int){ 123 }
+      max: (int){ 123 }
+    }
+    #a: (#struct){
+      param: (int){ int }
+      transformed: (_|_){
+        // [incomplete] incompleteArgDecimalList.#a.transformed: operand param of '+' not concrete (was int):
+        //     ./in.cue:49:23
+      }
+      max: (_|_){
+        // [incomplete] 0: operand param of '+' not concrete (was int):
+        //     ./in.cue:49:23
+      }
+    }
+  }
+  incompleteArgStringList: (struct){
+    a: (#struct){
+      param: (string){ "123" }
+      transformed: (string){ "123" }
+      joined: (string){ "123" }
+    }
+    #a: (#struct){
+      param: (string){ string }
+      transformed: (_|_){
+        // [incomplete] incompleteArgStringList.#a.transformed: non-concrete value string in operand to +:
+        //     ./in.cue:58:22
+      }
+      joined: (_|_){
+        // [incomplete] 0: non-concrete value string in operand to +:
+        //     ./in.cue:58:22
+      }
+    }
+  }
+  incompleteList: (struct){
+    x: (_){ _ }
+    decimal: (_|_){
+      // [incomplete] incompleteList.decimal: non-concrete list for argument 0:
+      //     ./in.cue:65:14
+    }
+    str: (_|_){
+      // [incomplete] incompleteList.str: non-concrete list for argument 0:
+      //     ./in.cue:66:14
+    }
+  }
+  incompleteListError: (struct){
+    x: (_|_){
+      // [incomplete] incompleteListError.x: non-concrete value _ in operand to +:
+      //     ./in.cue:70:8
+    }
+    y: (_){ _ }
+    decimal: (_|_){
+      // [incomplete] incompleteListError.x: non-concrete value _ in operand to +:
+      //     ./in.cue:70:8
+    }
+    str: (_|_){
+      // [incomplete] incompleteListError.x: non-concrete value _ in operand to +:
+      //     ./in.cue:70:8
+    }
+  }
+  badListType: (_|_){
+    // [eval]
+    x: (int){ 2 }
+    decimal: (_|_){
+      // [eval] badListType.decimal: cannot use 2 (type int) as list in argument 1 to list.Max:
+      //     ./in.cue:77:8
+    }
+    str: (_|_){
+      // [eval] badListType.str: cannot use 2 (type int) as list in argument 1 to strings.Join:
+      //     ./in.cue:77:8
+    }
+  }
+  badListError: (_|_){
+    // [eval]
+    x: (_|_){
+      // [eval] badListError.x: invalid operands 2 and "foo" to '+' (type int and string):
+      //     ./in.cue:83:8
+    }
+    y: (string){ "foo" }
+    decimal: (_|_){
+      // [eval] badListError.x: invalid operands 2 and "foo" to '+' (type int and string):
+      //     ./in.cue:83:8
+    }
+    str: (_|_){
+      // [eval] badListError.x: invalid operands 2 and "foo" to '+' (type int and string):
+      //     ./in.cue:83:8
+    }
   }
 }
 -- out/compile --
@@ -152,4 +299,50 @@
   value2: {
     len(('sf'|'dd'))
   }
+  incompleteArgDecimalList: {
+    a: (〈0;#a〉 & {
+      param: 123
+    })
+    #a: {
+      param: int
+      transformed: +〈0;param〉
+      max: 〈import;list〉.Max([
+        〈0;transformed〉,
+      ])
+    }
+  }
+  incompleteArgStringList: {
+    a: (〈0;#a〉 & {
+      param: "123"
+    })
+    #a: {
+      param: string
+      transformed: (〈0;param〉 + "")
+      joined: 〈import;strings〉.Join([
+        〈0;transformed〉,
+      ], "-")
+    }
+  }
+  incompleteList: {
+    x: _
+    decimal: 〈import;list〉.Max(〈0;x〉)
+    str: 〈import;strings〉.Join(〈0;x〉, "")
+  }
+  incompleteListError: {
+    x: (〈0;y〉 + [])
+    y: _
+    decimal: 〈import;list〉.Max(〈0;x〉)
+    str: 〈import;strings〉.Join(〈0;x〉, "")
+  }
+  badListType: {
+    x: 2
+    decimal: 〈import;list〉.Max(〈0;x〉)
+    str: 〈import;strings〉.Join(〈0;x〉, "")
+  }
+  badListError: {
+    x: (2 + 〈0;y〉)
+    y: "foo"
+    decimal: 〈import;list〉.Max(〈0;x〉)
+    str: 〈import;strings〉.Join(〈0;x〉, "")
+  }
 }
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
index 5c40ae3..3e46997 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -951,10 +951,10 @@
 				c.AddBottom(&Bottom{Src: a.Source(), Err: err})
 				return nil
 			}
-			n.Arcs = append(n.Arcs, &Vertex{
-				Label:     label,
-				Conjuncts: a.Conjuncts,
-			})
+			arc := *a
+			arc.Parent = n
+			arc.Label = label
+			n.Arcs = append(n.Arcs, &arc)
 		}
 		n.status = Finalized
 		return n
diff --git a/pkg/internal/context.go b/pkg/internal/context.go
index c428b5a..e08a152 100644
--- a/pkg/internal/context.go
+++ b/pkg/internal/context.go
@@ -228,70 +228,98 @@
 	return v
 }
 
-func (c *CallCtxt) DecimalList(i int) (a []*apd.Decimal) {
-	arg := c.args[i]
-	x := cue.MakeValue(c.ctx, arg)
-	v, err := x.List()
-	if err != nil {
+func (c *CallCtxt) getList(i int) *adt.Vertex {
+	x := c.args[i]
+	switch v, ok := x.(*adt.Vertex); {
+	case ok && v.IsList():
+		v.Finalize(c.ctx)
+		return v
+
+	case v != nil:
+		x = v.Value()
+	}
+	if x.Kind()&adt.ListKind == 0 {
+		var err error
+		if b, ok := x.(*adt.Bottom); ok {
+			err = &callError{b}
+		}
 		c.invalidArgType(c.args[i], i, "list", err)
+	} else {
+		err := c.ctx.NewErrf("non-concrete list for argument %d", i)
+		err.Code = adt.IncompleteError
+		c.Err = &callError{err}
+	}
+	return nil
+}
+
+func (c *CallCtxt) DecimalList(i int) (a []*apd.Decimal) {
+	v := c.getList(i)
+	if v == nil {
 		return nil
 	}
-	for j := 0; v.Next(); j++ {
-		w := v.Value()
-		if k := w.IncompleteKind(); k&adt.NumKind == 0 {
-			err := c.ctx.NewErrf(
-				"invalid type element %d (%s) of number list argument %d", j, k, i)
-			c.Err = &callError{err}
-			break
-		}
-		if !w.IsConcrete() {
+
+	for j, w := range v.Elems() {
+		w.Finalize(c.ctx) // defensive
+		switch x := w.Value().(type) {
+		case *adt.Num:
+			a = append(a, &x.X)
+
+		case *adt.Bottom:
+			if x.IsIncomplete() {
+				c.Err = x
+				return nil
+			}
+
+		default:
+			if k := w.Kind(); k&adt.NumKind == 0 {
+				err := c.ctx.NewErrf(
+					"invalid type element %d (%s) of number list argument %d", j, k, i)
+				c.Err = &callError{err}
+				return a
+			}
+
 			err := c.ctx.NewErrf(
 				"non-concrete number value for element %d of number list argument %d", j, i)
 			err.Code = adt.IncompleteError
 			c.Err = &callError{err}
-			break
+			return nil
 		}
-		num, err := w.Decimal()
-		if err != nil {
-			c.errf(c.src, err, "invalid list element %d in argument %d to %s: %v",
-				j, i, c.Name(), err)
-			break
-		}
-		a = append(a, num)
 	}
 	return a
 }
 
 func (c *CallCtxt) StringList(i int) (a []string) {
-	arg := c.args[i]
-	x := cue.MakeValue(c.ctx, arg)
-	v, err := x.List()
-	if err != nil {
-		c.invalidArgType(c.args[i], i, "list", err)
+	v := c.getList(i)
+	if v == nil {
 		return nil
 	}
-	for j := 0; v.Next(); j++ {
-		w := v.Value()
-		if k := w.IncompleteKind(); k&adt.StringKind == 0 {
-			err := c.ctx.NewErrf(
-				"invalid type element %d (%s) of string list argument %d", j, k, i)
-			c.Err = &callError{err}
-			break
-		}
-		if !w.IsConcrete() {
+
+	for j, w := range v.Elems() {
+		w.Finalize(c.ctx) // defensive
+		switch x := w.Value().(type) {
+		case *adt.String:
+			a = append(a, x.Str)
+
+		case *adt.Bottom:
+			if x.IsIncomplete() {
+				c.Err = x
+				return nil
+			}
+
+		default:
+			if k := w.Kind(); k&adt.StringKind == 0 {
+				err := c.ctx.NewErrf(
+					"invalid type element %d (%s) of string list argument %d", j, k, i)
+				c.Err = &callError{err}
+				return a
+			}
+
 			err := c.ctx.NewErrf(
 				"non-concrete string value for element %d of string list argument %d", j, i)
 			err.Code = adt.IncompleteError
 			c.Err = &callError{err}
-			break
+			return nil
 		}
-		str, err := w.String()
-		if err != nil {
-			// TODO: expose wrapping
-			c.Err = &callError{c.ctx.NewErrf("element %d of list argument %d: %v", j, i, err)}
-			break
-		}
-		a = append(a, str)
 	}
 	return a
 }