cue: fix type checking for numeric arithmetic ops
Addresses issue cuelang/cue#16 (numbers only)
Change-Id: I8b873dc1362e3a6432032a5282b3803f8c491c52
diff --git a/cue/kind.go b/cue/kind.go
index 57b9928..a6c3e2e 100644
--- a/cue/kind.go
+++ b/cue/kind.go
@@ -173,7 +173,7 @@
// - keep type compatibility mapped at a central place
// - reduce the amount op type switching.
// - simplifies testing
-func matchBinOpKind(op op, a, b kind) (k kind, invert bool) {
+func matchBinOpKind(op op, a, b kind) (k kind, swap bool) {
if op == opDisjunction {
return a | b, false
}
@@ -185,14 +185,16 @@
a = a & typeKinds
b = b & typeKinds
if valBits == bottomKind {
- if op == opEql || op == opNeq || op == opUnify {
- // Set invert for better error messages
- // invert = aGround && !bGround
+ k := nullKind
+ switch op {
+ case opEql, opNeq:
+ fallthrough
+ case opUnify:
if a&nullKind != 0 {
- return boolKind, false
+ return k, false
}
if b&nullKind != 0 {
- return boolKind, true
+ return k, true
}
return bottomKind, false
}
@@ -219,12 +221,6 @@
}
case op.isCmp():
return boolKind, false
- case op.allowImplicitNumCast():
- if a.isAnyOf(intKind) ||
- (a.isAnyOf(floatKind) && !b.isAnyOf(intKind)) {
- return b | catBits, false
- }
- return a | catBits, false
}
return bottomKind, false
}
@@ -235,17 +231,17 @@
case op != opUnify && op != opLand && op != opLor && op != opNeq:
default:
- invert = aGround && !bGround
+ swap = aGround && !bGround
}
// a and b have overlapping types.
switch op {
case opUnify:
// Increase likelihood of unification succeeding on first try.
- return u, invert
+ return u, swap
case opLand, opLor:
if u.isAnyOf(boolKind) {
- return boolKind | catBits, invert
+ return boolKind | catBits, swap
}
case opEql, opNeq, opMat, opNMat:
if u.isAnyOf(fixedKinds) {
@@ -265,11 +261,11 @@
return u&scalarKinds | catBits, false
}
case opRem:
- if u.isAnyOf(durationKind | intKind) {
- return u&(durationKind|intKind) | catBits, false
+ if u.isAnyOf(floatKind) {
+ return floatKind | catBits, false
}
case opQuo:
- if u.isAnyOf(durationKind | numKind) {
+ if u.isAnyOf(floatKind) {
return floatKind | catBits, false
}
case opIRem, opIMod:
diff --git a/cue/kind_test.go b/cue/kind_test.go
index 0deccfd..6483a90 100644
--- a/cue/kind_test.go
+++ b/cue/kind_test.go
@@ -28,10 +28,20 @@
}{{
op: opMul,
a: floatKind,
- b: intKind,
+ b: numKind,
want: floatKind,
}, {
op: opMul,
+ a: intKind,
+ b: numKind,
+ want: intKind,
+ }, {
+ op: opMul,
+ a: floatKind,
+ b: intKind,
+ want: bottomKind,
+ }, {
+ op: opMul,
a: listKind,
b: intKind,
want: listKind,
diff --git a/cue/op.go b/cue/op.go
index 258343a..7d020aa 100644
--- a/cue/op.go
+++ b/cue/op.go
@@ -30,13 +30,6 @@
return opEql <= op && op <= opGeq
}
-// allowImplicitNumCast returns whether an operator is allowed between two
-// different kind of numeric types without an explicit cast.
-// TODO: remove
-func (op op) allowImplicitNumCast() bool {
- return opAdd <= op && op <= opQuo
-}
-
type op uint16
const (
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index b6fbaa6..18b5c6a 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -143,15 +143,54 @@
}, {
desc: "arithmetic",
in: `
+ i1: 1 & int
+ i2: 2 & int
+
sum: -1 + +2 // 1
str: "foo" + "bar" // "foobar"
div1: 2.0 / 3 * 6 // 4
div2: 2 / 3 * 6 // 4
rem: 2 % 3 // 2
- e: 2 + "a" // _|_: unsupported op +(int, string))
b: 1 != 4
+
+ v1: 1.0T/2.0
+ v2: 2.0 == 2
+ v3: 2.0/3.0
+ v4: 2.0%3.0
+ v5: i1 div i2
+
+ e0: 2 + "a"
+ e1: 2.0 / i1
+ e2: i1 / 2.0
+ e3: 3.0 % i2
+ e4: i1 % 2.0
+ e5: 1.0 div 2
+ e6: 2 rem 2.0
+ e7: 2 quo 2.0
+ e8: 1.0 mod 1
`,
- out: `<0>{sum: 1, str: "foobar", div1: 4.00000000000000000000000, div2: 4.00000000000000000000000, rem: 2, e: _|_((2 + "a"):unsupported op +(number, string)), b: true}`,
+ out: `<0>{i1: 1, i2: 2, ` +
+ `sum: 1, ` +
+ `str: "foobar", ` +
+ `div1: 4.00000000000000000000000, ` +
+ `div2: 4.00000000000000000000000, ` +
+ `rem: 2, ` +
+ `b: true, ` +
+ `v1: 5e+11, ` +
+ `v2: true, ` +
+ `v3: 0.666666666666666666666667, ` +
+ `v4: 2.0, ` +
+ `v5: 0, ` +
+
+ `e0: _|_((2 + "a"):unsupported op +(number, string)), ` +
+ `e1: _|_((2.0 / 1):unsupported op /(float, int)), ` +
+ `e2: _|_((1 / 2.0):unsupported op /(int, float)), ` +
+ `e3: _|_((3.0 % 2):unsupported op %(float, int)), ` +
+ `e4: _|_((1 % 2.0):unsupported op %(int, float)), ` +
+ `e5: _|_((1.0 div 2):unsupported op div(float, number)), ` +
+ `e6: _|_((2 rem 2.0):unsupported op rem(number, float)), ` +
+ `e7: _|_((2 quo 2.0):unsupported op quo(number, float)), ` +
+ `e8: _|_((1.0 mod 1):unsupported op mod(float, number))}`,
}, {
desc: "integer-specific arithmetic",
in: `
@@ -482,14 +521,14 @@
}, {
desc: "arithmetic",
in: `
- v1: 1.0T/2.0 //
+ v1: 1.0T/2.0
v2: 2.0 == 2
- i1: 1
- v5: 2.0 / i1 // TODO: should probably fail
- e1: 2.0 % 3
+ n1: 1
+ v5: 2.0 / n1
+ e1: 2.0 % (3&int)
e2: int & 4.0/2.0
`,
- out: `<0>{v1: 5e+11, v2: true, i1: 1, v5: 2, e1: _|_((2.0 % 3):unsupported op %(float, number)), e2: _|_((int & 2):unsupported op &((int)*, float))}`,
+ out: `<0>{v1: 5e+11, v2: true, n1: 1, v5: 2, e1: _|_((2.0 % 3):unsupported op %(float, int)), e2: _|_((int & 2):unsupported op &((int)*, float))}`,
}, {
desc: "inequality",
in: `