cue: fix premature fixing of some incomplete values

Also tightens structural subsumption (mostly
of cosmetic relevance).

Change-Id: I826d5593907db5a30028a5d99da2f0d255c4f1b8
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2954
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/errors.go b/cue/errors.go
index 5335733..9ab8bb3 100644
--- a/cue/errors.go
+++ b/cue/errors.go
@@ -203,6 +203,7 @@
 	if v, ok := src.(value); ok {
 		e.value = v
 	}
+outer:
 	for i, a := range args {
 		switch x := a.(type) {
 		case errCode:
@@ -219,7 +220,7 @@
 			for i, a := range e.args {
 				e.args[i] = fixArg(idx, a)
 			}
-			return e
+			break outer
 		}
 	}
 	if e.code == codeNone && e.wrapped != nil {
diff --git a/cue/eval.go b/cue/eval.go
index bfb2207..5b4a56c 100644
--- a/cue/eval.go
+++ b/cue/eval.go
@@ -202,6 +202,9 @@
 	}
 	v := x.value.evalPartial(ctx)
 	if isBottom(v) {
+		if isIncomplete(v) {
+			return v
+		}
 		return ctx.mkErr(x, v, "error evaluating bound")
 	}
 	if v == x.value {
@@ -445,15 +448,12 @@
 			d.d = -d.d
 			return &d
 		}
-		fallthrough
+		return ctx.mkErr(src, codeIncomplete, "operand %s of '-' not concrete (was %s)", ctx.str(x), kind)
 
 	case opAdd:
 		if kind&numeric == bottomKind {
 			return ctx.mkErr(src, "invalid operation +%s (+ %s)", ctx.str(x), kind)
 		}
-		if kind&^(numeric|nonGround|referenceKind) == bottomKind {
-			return v
-		}
 		switch v := v.(type) {
 		case *numLit, *durationLit:
 			return v
@@ -462,19 +462,17 @@
 		case *basicType:
 			return &basicType{v.baseValue, (v.k & numeric) | nonGround}
 		}
+		return ctx.mkErr(src, codeIncomplete, "operand %s of '+' not concrete (was %s)", ctx.str(x), kind)
 
 	case opNot:
 		if kind&boolKind == bottomKind {
 			return ctx.mkErr(src, "invalid operation !%s (! %s)", ctx.str(x), kind)
 		}
 		switch v := v.(type) {
-		case *top:
-			return &basicType{v.baseValue, boolKind | nonGround}
-		case *basicType:
-			return v
 		case *boolLit:
 			return &boolLit{src.base(), !v.b}
 		}
+		return ctx.mkErr(src, codeIncomplete, "operand %s of '!' not concrete (was %s)", ctx.str(x), kind)
 	}
 	return ctx.mkErr(src, "invalid operation %s%s (%s %s)", op, ctx.str(x), op, kind)
 }
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 84d63a7..92c781c 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -2145,6 +2145,43 @@
 			`b2: (0 | 1), ` +
 			`c1: (<2>{a: 1, b: 2} | <3>{a: 2, b: 1}), ` +
 			`c2: (<4>{a: 2, b: 1} | <5>{a: 1, b: 2})}`,
+	}, {
+		desc: "don't convert incomplete errors to non-incomplete",
+		in: `
+		import "strings"
+
+		n1: {min: <max, max: >min}
+		n2: -num
+		n3: +num
+		n4: num + num
+		n5: num - num
+		n6: num * num
+		n7: num / num
+
+		b1: !is
+
+		s1: "\(str)"
+		s2: strings.ContainsAny("dd")
+		s3: strings.ContainsAny(str, "dd")
+
+		str: string
+		num: <4
+		is:  bool
+		`,
+		out: `<0>{` +
+			`n1: <1>{min: <<2>.max, max: ><2>.min}, ` +
+			`n2: -<3>.num, num: <4, ` +
+			`n3: +<3>.num, ` +
+			`n4: (<3>.num + <3>.num), ` +
+			`n5: (<3>.num - <3>.num), ` +
+			`n6: (<3>.num * <3>.num), ` +
+			`n7: (<3>.num / <3>.num), ` +
+			`b1: !<3>.is, ` +
+			`is: bool, ` +
+			`s1: ""+<3>.str+"", ` +
+			`str: string, ` +
+			`s2: strings.ContainsAny ("dd"), ` +
+			`s3: <4>.ContainsAny (<3>.str,"dd")}`,
 	}}
 	rewriteHelper(t, testCases, evalFull)
 }
diff --git a/cue/subsume_test.go b/cue/subsume_test.go
index 97dd9eb..176b092 100644
--- a/cue/subsume_test.go
+++ b/cue/subsume_test.go
@@ -159,17 +159,17 @@
 		// TODO: may be false if we allow arithmetic on incomplete values.
 		98: {subsumes: false, in: `a: int + int, b: int * int`},
 
-		99:  {subsumes: true, in: `a: !int, b: !int`},
-		100: {subsumes: true, in: `a: !number, b: !int`},
 		// TODO: allow subsumption of unevaluated values?
 		// true because both evaluate to bottom
+		99:  {subsumes: true, in: `a: !int, b: !int`},
+		100: {subsumes: true, in: `a: !number, b: !int`},
 		101: {subsumes: true, in: `a: !int, b: !number`},
+
 		// TODO: allow subsumption of unevaluated values?
-		// May be true because both evaluate to bottom. false is always allowed.
 		102: {subsumes: false, in: `a: int + int, b: !number`},
 		// TODO: allow subsumption of unevaluated values?
-		// true because both evaluate to bool
-		103: {subsumes: true, in: `a: !bool, b: bool`},
+		103: {subsumes: false, in: `a: !bool, b: bool`},
+		104: {subsumes: true, in: `a: !bool, b: !bool`},
 
 		// Call
 		113: {subsumes: true, in: `