cue: fixes number groundness inference
When a integer was inferred from restricting
comparators with a difference greater than
1.
Also implemented restricting float ranges
when unified with integers.
Fixes #108
Change-Id: Ie64a0670392463cc5340ee9f3771ded9c6f2a15d
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3301
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/binop.go b/cue/binop.go
index 2d0f849..0395753 100644
--- a/cue/binop.go
+++ b/cue/binop.go
@@ -394,8 +394,28 @@
break
}
- var d apd.Decimal
- cond, err := apd.BaseContext.Sub(&d, &b.v, &a.v)
+ var d, lo, hi apd.Decimal
+ lo.Set(&a.v)
+ hi.Set(&b.v)
+ if k&floatKind == 0 {
+ // Readjust bounds for integers.
+ if x.op == opGeq {
+ // >=3.4 ==> >=4
+ _, _ = apd.BaseContext.Ceil(&lo, &a.v)
+ } else {
+ // >3.4 ==> >3
+ _, _ = apd.BaseContext.Floor(&lo, &a.v)
+ }
+ if y.op == opLeq {
+ // <=2.3 ==> <= 2
+ _, _ = apd.BaseContext.Floor(&hi, &b.v)
+ } else {
+ // <2.3 ==> < 3
+ _, _ = apd.BaseContext.Ceil(&hi, &b.v)
+ }
+ }
+
+ cond, err := apd.BaseContext.Sub(&d, &hi, &lo)
if cond.Inexact() || err != nil {
break
}
@@ -432,25 +452,28 @@
case diff == 1:
if k&floatKind == 0 {
if x.op == opGeq && y.op == opLss {
- return a
+ return &numLit{numBase: a.numBase, v: lo}
}
if x.op == opGtr && y.op == opLeq {
- return b
+ return &numLit{numBase: b.numBase, v: hi}
}
}
case diff == 2:
if k&floatKind == 0 && x.op == opGtr && y.op == opLss {
- _, _ = apd.BaseContext.Add(&d, d.SetInt64(1), &a.v)
+ _, _ = apd.BaseContext.Add(&d, d.SetInt64(1), &lo)
n := *a
- n.k = k
- n.v = d
+ n.k = k & numKind
+ n.v.Set(&d)
return &n
}
case diff == 0:
if x.op == opGeq && y.op == opLeq {
- return a
+ n := *a
+ n.k = k & numKind
+ n.v.Set(&lo)
+ return &n
}
fallthrough
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 4697e85..6239022 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -763,6 +763,34 @@
`e8: _|_(conflicting bounds >11 and <=11), ` +
`e9: _|_((>"a" & <1):conflicting values >"a" and <1 (mismatched types string and number))}`,
}, {
+ desc: "bound conversions",
+ in: `
+ r1: int & >0.1 & <1.9
+ r2: int & >=0.1 & <1.9
+ r3: int & >=-1.9 & <=-0.1
+ r4: int & >-1.9 & <=-0.1
+
+ r5: >=1.1 & <=1.1
+ r6: r5 & 1.1
+
+ c1: (1.2 & >1.3) & <2
+ c2: 1.2 & (>1.3 & <2)
+
+ c3: 1.2 & (>=1 & <2)
+ c4: 1.2 & (>=1 & <2 & int)
+ `,
+ out: `<0>{` +
+ `r1: 1, ` +
+ `r2: 1, ` +
+ `r3: -1, ` +
+ `r4: -1, ` +
+ `r5: 1.1, ` +
+ `r6: 1.1, ` +
+ `c1: _|_((>1.3 & 1.2):invalid value 1.2 (out of bound >1.3)), ` +
+ `c2: _|_((>1.3 & 1.2):invalid value 1.2 (out of bound >1.3)), ` +
+ `c3: 1.2, ` +
+ `c4: _|_((1.2 & ((>=1 & <2) & int)):conflicting values 1.2 and ((>=1 & <2) & int) (mismatched types float and int))}`,
+ }, {
desc: "custom validators",
in: `
import "strings"
@@ -1552,7 +1580,7 @@
`s1: "e", ` +
`s2: "ee", ` +
`n1: (>=1 & <=2), ` +
- `n2: (int & >=1.1 & <=1.3), ` +
+ `n2: _|_(conflicting bounds int & >=1.1 and <=1.3), ` +
`n3: 2, ` +
`n4: 0.09999, ` +
`n5: 2.5}`,
@@ -2323,10 +2351,7 @@
// Don't remove. For debugging.
testCases := []testCase{{
- in: `
- g1: 1
- "g\(1)"?: 1
- `,
+ in: ``,
}}
rewriteHelper(t, testCases, evalFull)
}
diff --git a/cue/types_test.go b/cue/types_test.go
index efc384d..02ee635 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -1796,6 +1796,32 @@
// Issue #107
value: `a: 1.0/1`,
json: `{"a":1}`,
+ }, {
+ // Issue #108
+ value: `
+ a: int
+ a: >0
+ a: <2
+
+ b: int
+ b: >=0.9
+ b: <1.1
+
+ c: int
+ c: >1
+ c: <=2
+
+ d: int
+ d: >=1
+ d: <=1.5
+
+ e: int
+ e: >=1
+ e: <=1.32
+
+ f: >=1.1 & <=1.1
+ `,
+ json: `{"a":1,"b":1,"c":2,"d":1,"e":1,"f":1.1}`,
}}
for i, tc := range testCases {
t.Run(fmt.Sprintf("%d/%v", i, tc.value), func(t *testing.T) {