cue: simplify numLit

- more convenient helper functions
- only one composite literal of numLit (simplifies checking)

Change-Id: I1fb224f1bf7770710428b5e3e923489400d00658
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/4421
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/binop.go b/cue/binop.go
index 38dcdb1..a0e9d4f 100644
--- a/cue/binop.go
+++ b/cue/binop.go
@@ -268,9 +268,7 @@
 			if k == y.k {
 				return y
 			}
-			i := *y
-			i.k = k
-			return &i
+			return y.specialize(k)
 		}
 		src = mkBin(ctx, src.Pos(), op, x, other)
 		return ctx.mkErr(src, codeIncomplete, "%s with incomplete values", op)
@@ -446,40 +444,30 @@
 				//     a+1 if b-a == 2
 				//     _|_ if b <= a
 
+				n := newNum(src, k&numKind, a.rep|b.rep)
 				switch diff, err := d.Int64(); {
 				case err != nil:
 
 				case diff == 1:
 					if k&floatKind == 0 {
 						if x.op == opGeq && y.op == opLss {
-							n := *a
-							n.k = k & numKind
-							n.v.Set(&lo)
-							return &n
+							return n.set(&lo)
 						}
 						if x.op == opGtr && y.op == opLeq {
-							n := *b
-							n.k = k & numKind
-							n.v.Set(&hi)
-							return &n
+							return n.set(&hi)
 						}
 					}
 
 				case diff == 2:
 					if k&floatKind == 0 && x.op == opGtr && y.op == opLss {
 						_, _ = apd.BaseContext.Add(&d, d.SetInt64(1), &lo)
-						n := *a
-						n.k = k & numKind
-						n.v.Set(&d)
-						return &n
+						return n.set(&d)
+
 					}
 
 				case diff == 0:
 					if x.op == opGeq && y.op == opLeq {
-						n := *a
-						n.k = k & numKind
-						n.v.Set(&lo)
-						return &n
+						return n.set(&lo)
 					}
 					fallthrough
 
@@ -506,9 +494,7 @@
 			}
 			// Narrow down number type.
 			if y.k != k {
-				n := *y
-				n.k = k
-				return &n
+				return y.specialize(k)
 			}
 			return other
 
@@ -552,9 +538,7 @@
 			}
 			// Narrow down number type.
 			if y.k != k {
-				n := *y
-				n.k = k
-				return &n
+				return y.specialize(k)
 			}
 			return other
 
@@ -1024,7 +1008,14 @@
 		}
 	case *numLit:
 		k, _, _ := matchBinOpKind(op, x.kind(), y.kind())
-		n := newNumBin(k, x, y)
+		if k == bottomKind {
+			break
+		}
+		switch op {
+		case opLss, opLeq, opEql, opNeq, opGeq, opGtr:
+			return cmpTonode(src, op, x.v.Cmp(&y.v))
+		}
+		n := newNum(src.base(), k, x.rep|y.rep)
 		switch op {
 		case opUnify, opUnifyUnchecked:
 			if x.v.Cmp(&y.v) != 0 {
@@ -1037,8 +1028,6 @@
 				return n
 			}
 			return x
-		case opLss, opLeq, opEql, opNeq, opGeq, opGtr:
-			return cmpTonode(src, op, x.v.Cmp(&y.v))
 		case opAdd:
 			_, _ = ctx.Add(&n.v, &x.v, &y.v)
 		case opSub:
@@ -1140,19 +1129,13 @@
 		case opSub:
 			return &durationLit{binSrc(src.Pos(), op, x, other), x.d - y.d}
 		case opQuo:
-			n := &numLit{
-				numBase: newNumBase(nil, newNumInfo(floatKind, 0, 10, false)),
-			}
-			n.v.SetInt64(int64(x.d))
+			n := newFloat(src.base(), base10).setInt64(int64(x.d))
 			d := apd.New(int64(y.d), 0)
 			// TODO: check result if this code becomes undead.
 			_, _ = ctx.Quo(&n.v, &n.v, d)
 			return n
 		case opIRem:
-			n := &numLit{
-				numBase: newNumBase(nil, newNumInfo(intKind, 0, 10, false)),
-			}
-			n.v.SetInt64(int64(x.d % y.d))
+			n := newInt(src.base(), base10).setInt64(int64(x.d % y.d))
 			n.v.Exponent = -9
 			return n
 		}
@@ -1254,9 +1237,7 @@
 		switch v := y.len.(type) {
 		case *numLit:
 			// Closed list
-			ln := &numLit{numBase: v.numBase}
-			ln.v.SetInt64(int64(len(arcs)))
-			n.len = ln
+			n.len = newInt(v.base(), v.rep).setInt(len(arcs))
 		default:
 			// Open list
 			n.len = y.len // TODO: add length of x?
@@ -1290,9 +1271,7 @@
 		switch v := x.len.(type) {
 		case *numLit:
 			// Closed list
-			ln := &numLit{numBase: v.numBase}
-			ln.v.SetInt64(int64(len(arcs)))
-			n.len = ln
+			n.len = newInt(v.base(), v.rep).setInt(len(arcs))
 		default:
 			// Open list
 			n.len = x.len // TODO: multiply length?
diff --git a/cue/go.go b/cue/go.go
index 09c1ae6..468369b 100644
--- a/cue/go.go
+++ b/cue/go.go
@@ -210,7 +210,7 @@
 		return ctx.manifest(x.walk(v))
 
 	case *big.Int:
-		n := newNum(src, intKind)
+		n := newInt(src.base(), 0)
 		n.v.Coeff.Set(v)
 		if v.Sign() < 0 {
 			n.v.Coeff.Neg(&n.v.Coeff)
@@ -220,7 +220,7 @@
 
 	case *big.Rat:
 		// should we represent this as a binary operation?
-		n := newNum(src, numKind)
+		n := newNum(src, numKind, 0)
 		_, err := ctx.Quo(&n.v, apd.NewWithBigInt(v.Num(), 0), apd.NewWithBigInt(v.Denom(), 0))
 		if err != nil {
 			return ctx.mkErr(src, err)
@@ -231,13 +231,10 @@
 		return n
 
 	case *big.Float:
-		n := newNum(src, floatKind)
-		_, _, _ = n.v.SetString(v.String())
-		return n
+		return newFloat(src, 0).setString(v.String())
 
 	case *apd.Decimal:
-		n := newNum(src, floatKind|intKind)
-		n.v.Set(v)
+		n := newNum(src, numKind, 0).set(v)
 		if !n.isInt(ctx) {
 			n.k = floatKind
 		}
@@ -293,13 +290,9 @@
 	case uintptr:
 		return toUint(ctx, src, uint64(v))
 	case float64:
-		r := newNum(src, floatKind)
-		_, _, _ = r.v.SetString(fmt.Sprintf("%g", v))
-		return r
+		return newFloat(src, 0).setString(fmt.Sprintf("%g", v))
 	case float32:
-		r := newNum(src, floatKind)
-		_, _, _ = r.v.SetString(fmt.Sprintf("%g", v))
-		return r
+		return newFloat(src, 0).setString(fmt.Sprintf("%g", v))
 
 	case reflect.Value:
 		if v.CanInterface() {
@@ -446,15 +439,11 @@
 }
 
 func toInt(ctx *context, src source, x int64) evaluated {
-	n := newNum(src, intKind)
-	n.v.SetInt64(x)
-	return n
+	return newInt(src, 0).setInt64(x)
 }
 
 func toUint(ctx *context, src source, x uint64) evaluated {
-	n := newNum(src, intKind)
-	n.v.Coeff.SetUint64(x)
-	return n
+	return newInt(src, 0).setUInt64(x)
 }
 
 func convertGoType(r *Runtime, t reflect.Type) value {
diff --git a/cue/go_test.go b/cue/go_test.go
index b9d749e..b311a97 100644
--- a/cue/go_test.go
+++ b/cue/go_test.go
@@ -25,6 +25,8 @@
 	"github.com/cockroachdb/apd/v2"
 )
 
+func mkBigInt(a int64) (v apd.Decimal) { v.SetInt64(a); return }
+
 func TestConvert(t *testing.T) {
 	i34 := big.NewInt(34)
 	d34 := mkBigInt(34)
diff --git a/cue/kind.go b/cue/kind.go
index a2a7235..1f95d00 100644
--- a/cue/kind.go
+++ b/cue/kind.go
@@ -264,7 +264,11 @@
 		if u.isAnyOf(boolKind) {
 			return boolKind | catBits, swap, ""
 		}
-	case opEql, opNeq, opMat, opNMat:
+	case opMat, opNMat:
+		if u.isAnyOf(stringKind | bytesKind) {
+			return boolKind | catBits, false, ""
+		}
+	case opEql, opNeq:
 		if u.isAnyOf(fixedKinds) {
 			return boolKind | catBits, false, ""
 		}
diff --git a/cue/lit.go b/cue/lit.go
index 325f9e5..6f88baf 100644
--- a/cue/lit.go
+++ b/cue/lit.go
@@ -22,33 +22,24 @@
 	"github.com/cockroachdb/apd/v2"
 )
 
-type numInfo struct {
-	rep multiplier
-	k   kind
-}
-
-func newNumInfo(k kind, m multiplier, base int, sep bool) numInfo {
+func newRepresentation(m multiplier, base int, sep bool) multiplier {
 	switch base {
 	case 10:
 		m |= base10
 	case 2:
 		m |= base2
-		k = intKind
 	case 8:
 		m |= base8
-		k = intKind
 	case 16:
 		m |= base16
-		k = intKind
 	}
 	if sep {
 		m |= hasSeparators
 	}
-	return numInfo{m, k}
+	return m
 }
 
-func (n numInfo) isValid() bool          { return n.k != bottomKind }
-func (n numInfo) multiplier() multiplier { return n.rep & (hasSeparators - 1) }
+func (m multiplier) multiplier() multiplier { return m & (hasSeparators - 1) }
 
 type multiplier uint16
 
@@ -307,9 +298,7 @@
 		} else {
 			mul |= mulDec
 		}
-		n := &numLit{
-			numBase: newNumBase(p.node, newNumInfo(intKind, mul, 10, p.useSep)),
-		}
+		n := newInt(newExpr(p.node), newRepresentation(mul, 10, p.useSep))
 		n.v.UnmarshalText(p.buf)
 		p.ctx.Mul(&n.v, &n.v, mulToRat[mul])
 		cond, _ := p.ctx.RoundToIntegralExact(&n.v, &n.v)
@@ -331,13 +320,11 @@
 
 exit:
 	if isFloat {
-		f := &numLit{
-			numBase: newNumBase(p.node, newNumInfo(floatKind, 0, 10, p.useSep)),
-		}
+		f := newFloat(newExpr(p.node), newRepresentation(0, 10, p.useSep))
 		f.v.UnmarshalText(p.buf)
 		return f
 	}
-	i := &numLit{numBase: newNumBase(p.node, newNumInfo(intKind, 0, base, p.useSep))}
+	i := newInt(newExpr(p.node), newRepresentation(0, base, p.useSep))
 	i.v.Coeff.SetString(string(p.buf), base)
 	return i
 }
diff --git a/cue/lit_test.go b/cue/lit_test.go
index 41c1238..ff0d357 100644
--- a/cue/lit_test.go
+++ b/cue/lit_test.go
@@ -25,27 +25,17 @@
 	"github.com/google/go-cmp/cmp/cmpopts"
 )
 
-var defIntBase = newNumBase(&ast.BasicLit{}, newNumInfo(intKind, 0, 10, false))
-var defRatBase = newNumBase(&ast.BasicLit{}, newNumInfo(floatKind, 0, 10, false))
+var testBase = newExpr(&ast.BasicLit{})
 
 func mkInt(a int64) *numLit {
-	x := &numLit{numBase: defIntBase}
-	x.v.SetInt64(a)
-	return x
+	return newInt(testBase, base10).setInt64(a)
 }
 func mkIntString(a string) *numLit {
-	x := &numLit{numBase: defIntBase}
-	x.v.SetString(a)
-	return x
+	return newInt(testBase, base10).setString(a)
 }
 func mkFloat(a string) *numLit {
-	x := &numLit{numBase: defRatBase}
-	x.v.SetString(a)
-	return x
+	return newFloat(testBase, base10).setString(a)
 }
-func mkBigInt(a int64) (v apd.Decimal) { v.SetInt64(a); return }
-
-func mkBigFloat(a string) (v apd.Decimal) { v.SetString(a); return }
 
 var diffOpts = []cmp.Option{
 	cmp.Comparer(func(x, y big.Rat) bool {
@@ -60,8 +50,6 @@
 		stringLit{},
 		bytesLit{},
 		numLit{},
-		numBase{},
-		numInfo{},
 	),
 	cmpopts.IgnoreUnexported(
 		bottom{},
@@ -78,15 +66,9 @@
 
 func TestLiterals(t *testing.T) {
 	mkMul := func(x int64, m multiplier, base int) *numLit {
-		return &numLit{
-			newNumBase(&ast.BasicLit{}, newNumInfo(intKind, m, base, false)),
-			mkBigInt(x),
-		}
+		return newInt(testBase, newRepresentation(m, base, false)).setInt64(x)
 	}
-	hk := &numLit{
-		newNumBase(&ast.BasicLit{}, newNumInfo(intKind, 0, 10, true)),
-		mkBigInt(100000),
-	}
+	hk := newInt(testBase, newRepresentation(0, 10, true)).setInt64(100000)
 	testCases := []struct {
 		lit  string
 		node value
diff --git a/cue/types.go b/cue/types.go
index c019e11..9976180 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -985,10 +985,7 @@
 }
 
 func makeInt(v Value, x int64) Value {
-	n := &numLit{numBase: numBase{baseValue: v.path.v.base()}}
-	n.v.SetInt64(x)
-	n.k = intKind
-	return remakeValue(v, n)
+	return remakeValue(v, newInt(v.path.v.base(), base10).setInt64(x))
 }
 
 // Len returns the number of items of the underlying value.
diff --git a/cue/value.go b/cue/value.go
index 59c0ef1..f6e612e 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -216,9 +216,7 @@
 		return ctx.mkErr(x, "index %d out of bounds", i)
 	}
 	// TODO: this is incorrect.
-	n := newNum(x, intKind)
-	n.v.SetInt64(int64(x.b[i]))
-	return n
+	return newInt(x, 0).setUInt64(uint64(x.b[i]))
 }
 
 func (x *bytesLit) len() int { return len(x.b) }
@@ -302,28 +300,59 @@
 	return &stringLit{x.baseValue, string(runes[lox:hix]), nil}
 }
 
-type numBase struct {
+type numLit struct {
 	baseValue
-	numInfo
+	rep multiplier
+	k   kind
+	v   apd.Decimal
 }
 
-func newNumBase(n ast.Expr, info numInfo) numBase {
-	return numBase{newExpr(n), info}
-}
-
-func newNumBin(k kind, a, b *numLit) *numLit {
-	n := &numLit{
-		numBase: numBase{
-			baseValue: a.baseValue,
-			numInfo:   numInfo{a.rep | b.rep, k},
-		},
+func newNum(src source, k kind, rep multiplier) *numLit {
+	if rep&base2|base8|base10|base16 == 0 {
+		rep |= base10
 	}
+	if k&numKind == 0 {
+		panic("not a number")
+	}
+	return &numLit{baseValue: src.base(), rep: rep, k: k}
+}
+
+func newInt(src source, rep multiplier) *numLit {
+	return newNum(src, intKind, rep)
+}
+
+func newFloat(src source, rep multiplier) *numLit {
+	return newNum(src, floatKind, rep)
+}
+
+func (n numLit) specialize(k kind) *numLit {
+	n.k = k
+	return &n
+}
+
+func (n *numLit) set(d *apd.Decimal) *numLit {
+	n.v.Set(d)
 	return n
 }
 
-type numLit struct {
-	numBase
-	v apd.Decimal
+func (n *numLit) setInt(x int) *numLit {
+	n.v.SetInt64(int64(x))
+	return n
+}
+
+func (n *numLit) setInt64(x int64) *numLit {
+	n.v.SetInt64(x)
+	return n
+}
+
+func (n *numLit) setUInt64(x uint64) *numLit {
+	n.v.Coeff.SetUint64(x)
+	return n
+}
+
+func (n *numLit) setString(s string) *numLit {
+	_, _, _ = n.v.SetString(s)
+	return n
 }
 
 func (n *numLit) String() string {
@@ -342,7 +371,7 @@
 		Kind:  token.INT,
 		Value: s,
 	}
-	num := newNum(newExpr(n), k)
+	num := newInt(newExpr(n), 0)
 	_, _, err := num.v.SetString(s)
 	if err != nil {
 		panic(err)
@@ -355,7 +384,7 @@
 		Kind:  token.FLOAT,
 		Value: s,
 	}
-	num := newNum(newExpr(n), floatKind)
+	num := newFloat(newExpr(n), 0)
 	_, _, err := num.v.SetString(s)
 	if err != nil {
 		panic(err)
@@ -363,12 +392,6 @@
 	return num
 }
 
-func newNum(src source, k kind) *numLit {
-	n := &numLit{numBase: numBase{baseValue: src.base()}}
-	n.k = k
-	return n
-}
-
 var ten = big.NewInt(10)
 
 var one = parseInt(intKind, "1")
@@ -508,9 +531,7 @@
 
 // initLit initializes a literal list.
 func (x *list) initLit() {
-	n := newNum(x, intKind)
-	n.v.SetInt64(int64(len(x.elem.arcs)))
-	x.len = n
+	x.len = newInt(x, 0).setInt(len(x.elem.arcs))
 	x.typ = &top{x.baseValue}
 }
 
@@ -520,8 +541,7 @@
 	}
 	// A list is ground if its length is ground, or if the current length
 	// meets matches the cap.
-	n := newNum(x, intKind)
-	n.v.SetInt64(int64(len(x.elem.arcs)))
+	n := newInt(x, 0).setInt(len(x.elem.arcs))
 	if n := binOp(ctx, x, opUnify, n, x.len.evalPartial(ctx)); !isBottom(n) {
 		return &list{
 			baseValue: x.baseValue,
@@ -1908,8 +1928,7 @@
 
 	case *list:
 		for i := range src.elem.arcs {
-			idx := newNum(x, intKind)
-			idx.v.SetInt64(int64(i))
+			idx := newInt(x, 0).setInt(i)
 			v := fn.call(ctx, x, idx, src.at(ctx, i))
 			if err, ok := v.(*bottom); ok {
 				return err
diff --git a/cuego/examples_test.go b/cuego/examples_test.go
index 9e160a9..b56a323 100644
--- a/cuego/examples_test.go
+++ b/cuego/examples_test.go
@@ -42,7 +42,7 @@
 	//Output:
 	// completed: cuego_test.Sum{A:1, B:5, C:6} (err: <nil>)
 	// completed: cuego_test.Sum{A:2, B:6, C:8} (err: <nil>)
-	// empty disjunction: invalid operation null & {A: 2, B: 3, C: 8} (mismatched types null and struct)
+	// empty disjunction: conflicting values 5 and 2
 }
 
 func ExampleConstrain() {