cue: flx bug for len on non-concrete values

Change-Id: I7925132f01ada119a039cdec7d2a8c891f76fdc3
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3022
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/builtin.go b/cue/builtin.go
index 54367ba..d6716eb 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -27,6 +27,7 @@
 
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/cue/token"
 	"cuelang.org/go/internal"
 	"github.com/cockroachdb/apd/v2"
 )
@@ -118,22 +119,41 @@
 	Result: intKind,
 	Func: func(c *callCtxt) {
 		v := c.value(0)
-		switch v.Kind() {
+		switch k := v.IncompleteKind(); k {
 		case StructKind:
-			s, _ := v.structValData(c.ctx)
+			s, err := v.structValData(c.ctx)
+			if err != nil {
+				c.ret = err
+				break
+			}
 			c.ret = s.Len()
 		case ListKind:
 			i := 0
-			iter, _ := v.List()
+			iter, err := v.List()
+			if err != nil {
+				c.ret = err
+				break
+			}
 			for ; iter.Next(); i++ {
 			}
 			c.ret = i
 		case BytesKind:
-			b, _ := v.Bytes()
+			b, err := v.Bytes()
+			if err != nil {
+				c.ret = err
+				break
+			}
 			c.ret = len(b)
 		case StringKind:
-			s, _ := v.String()
+			s, err := v.String()
+			if err != nil {
+				c.ret = err
+				break
+			}
 			c.ret = len(s)
+		default:
+			c.ret = errors.Newf(token.NoPos,
+				"invalid argument type %v", k)
 		}
 	},
 }
@@ -268,8 +288,11 @@
 		}
 	}()
 	x.Func(&call)
-	if e, ok := call.ret.(value); ok {
-		return e
+	switch v := call.ret.(type) {
+	case value:
+		return v
+	case *valueError:
+		return v.err
 	}
 	return convert(ctx, x, false, call.ret)
 }
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 92c781c..1e5cbd4 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -2182,6 +2182,43 @@
 			`str: string, ` +
 			`s2: strings.ContainsAny ("dd"), ` +
 			`s3: <4>.ContainsAny (<3>.str,"dd")}`,
+	}, {
+		desc: "len of incomplete types",
+		in: `
+		args: *[] | [...string]
+		v1: len(args)
+		v2: len([])
+		v3: len({})
+		v4: len({a: 3})
+		v5: len({a: 3} | {a: 4})
+		v6: len('sf' | 'dd')
+		v7: len([2] | *[1, 2])
+		v8: len([2] | [1, 2])
+		v9: len("😂")
+		v10: len("")
+		`,
+		out: `<0>{` +
+			`args: [], ` +
+			`v1: 0, ` +
+			`v2: 0, ` +
+			`v3: 0, ` +
+			`v4: 1, ` +
+			`v5: len ((<1>{a: 3} | <2>{a: 4})), ` +
+			`v6: len (('sf' | 'dd')), ` +
+			`v7: 2, ` +
+			`v8: len (([2] | [1,2])), ` +
+			`v9: 4, ` +
+			`v10: 0}`,
+	}, {
+		desc: "slice rewrite bug",
+		in: `
+		fn: {
+			arg: [...int] & [1]
+			out: arg[1:]
+		}
+		fn1: fn & {arg: [1]}
+		`,
+		out: `<0>{fn: <1>{arg: [1], out: []}, fn1: <2>{arg: [1], out: []}}`,
 	}}
 	rewriteHelper(t, testCases, evalFull)
 }
@@ -2192,6 +2229,16 @@
 	// Don't remove. For debugging.
 	testCases := []testCase{{
 		in: `
+		fnRec: {nn: [...int], out: (fn & {arg: nn}).out}
+		fn: {
+			arg: [...int]
+
+			out: arg[0] + (fnRec & {nn: arg[1:]}).out if len(arg) > 0
+			out: 0 if len(arg) == 0
+		}
+		fn7: (fn & {arg: [1, 2, 3]}).out
+
+
 		`,
 	}}
 	rewriteHelper(t, testCases, evalFull)
diff --git a/cue/rewrite.go b/cue/rewrite.go
index 06171e0..ff4e7ba 100644
--- a/cue/rewrite.go
+++ b/cue/rewrite.go
@@ -130,8 +130,13 @@
 
 func (x *sliceExpr) rewrite(ctx *context, fn rewriteFunc) value {
 	v := rewrite(ctx, x.x, fn)
-	lo := rewrite(ctx, x.lo, fn)
-	hi := rewrite(ctx, x.hi, fn)
+	var lo, hi value
+	if x.lo != nil {
+		lo = rewrite(ctx, x.lo, fn)
+	}
+	if x.hi != nil {
+		hi = rewrite(ctx, x.hi, fn)
+	}
 	if v == x.x && lo == x.lo && hi == x.hi {
 		return x
 	}