Add IntRange.TryFoo methods
diff --git a/lang/check/bounds.go b/lang/check/bounds.go
index 7275ea9..266093d 100644
--- a/lang/check/bounds.go
+++ b/lang/check/bounds.go
@@ -1384,7 +1384,7 @@
return bounds{}, fmt.Errorf("check: divide/modulus op argument %q is possibly non-positive", rhs.Str(q.tm))
}
if op == t.IDXBinarySlash {
- nb, _ := lb.Quo(rb)
+ nb, _ := lb.TryQuo(rb)
return nb, nil
}
return bounds{
@@ -1413,14 +1413,14 @@
switch op {
case t.IDXBinaryShiftL:
- nb, _ := lb.Lsh(rb)
+ nb, _ := lb.TryLsh(rb)
return nb, nil
case t.IDXBinaryTildeModShiftL:
- nb, _ := lb.Lsh(rb)
+ nb, _ := lb.TryLsh(rb)
nb[1] = min(nb[1], typeBounds[1])
return nb, nil
case t.IDXBinaryShiftR:
- nb, _ := lb.Rsh(rb)
+ nb, _ := lb.TryRsh(rb)
return nb, nil
}
@@ -1435,11 +1435,9 @@
}
switch op {
case t.IDXBinaryAmp:
- nb, _ := lb.And(rb)
- return nb, nil
+ return lb.And(rb), nil
case t.IDXBinaryPipe:
- nb, _ := lb.Or(rb)
- return nb, nil
+ return lb.Or(rb), nil
case t.IDXBinaryHat:
z := max(lb[1], rb[1])
// Return [0, z rounded up to the next power-of-2-minus-1]. This is
diff --git a/lib/interval/interval.go b/lib/interval/interval.go
index a8d5f32..b1d2ea4 100644
--- a/lib/interval/interval.go
+++ b/lib/interval/interval.go
@@ -189,6 +189,13 @@
// *big.Int pointer values. Specifically, after "z = x.Add(y)", mutating
// "*z[0]" will not affect "*x[0]", "*x[1]", "*y[0]" or "*y[1]".
//
+// Those operator-like methods come in two forms: Foo and TryFoo. The TryFoo
+// forms return (z IntRange, ok bool). The bool indicates success, as
+// operations like dividing by zero or shifting by a negative value can fail.
+// When TryFoo can never fail, there is also a Foo method that omits the
+// always-true bool. For example, there are Add, TryAdd and TryLsh methods, but
+// no Lsh method.
+//
// A subtle point is that an interval's minimum or maximum can be infinite, but
// if an integer value i is known to be within such an interval, i's possible
// values are arbitrarily large but not infinite. Specifically, 0*i is
@@ -413,6 +420,11 @@
return z
}
+// TryUnite returns (x.Unite(y), true).
+func (x IntRange) TryUnite(y IntRange) (z IntRange, ok bool) {
+ return x.Unite(y), true
+}
+
func (x *IntRange) inPlaceUnite(y IntRange) {
if y.Empty() {
return
@@ -478,6 +490,11 @@
return z
}
+// TryIntersect returns (x.Intersect(y), true).
+func (x IntRange) TryIntersect(y IntRange) (z IntRange, ok bool) {
+ return x.Intersect(y), true
+}
+
// Add returns z = x + y.
func (x IntRange) Add(y IntRange) (z IntRange) {
if x.Empty() || y.Empty() {
@@ -492,6 +509,11 @@
return z
}
+// TryAdd returns (x.Add(y), true).
+func (x IntRange) TryAdd(y IntRange) (z IntRange, ok bool) {
+ return x.Add(y), true
+}
+
// Sub returns z = x - y.
func (x IntRange) Sub(y IntRange) (z IntRange) {
if x.Empty() || y.Empty() {
@@ -506,17 +528,27 @@
return z
}
+// TrySub returns (x.Sub(y), true).
+func (x IntRange) TrySub(y IntRange) (z IntRange, ok bool) {
+ return x.Sub(y), true
+}
+
// Mul returns z = x * y.
func (x IntRange) Mul(y IntRange) (z IntRange) {
return x.mulLsh(y, false)
}
-// Lsh returns z = x << y.
+// TryMul returns (x.Mul(y), true).
+func (x IntRange) TryMul(y IntRange) (z IntRange, ok bool) {
+ return x.Mul(y), true
+}
+
+// TryLsh returns z = x << y.
//
// ok is false (and z will be IntRange{nil, nil}) if x is non-empty and y
// contains at least one negative value, as it's invalid to shift by a negative
// number. Otherwise, ok is true.
-func (x IntRange) Lsh(y IntRange) (z IntRange, ok bool) {
+func (x IntRange) TryLsh(y IntRange) (z IntRange, ok bool) {
if !x.Empty() && y.ContainsNegative() {
return IntRange{}, false
}
@@ -602,12 +634,12 @@
return ret.toIntRange()
}
-// Quo returns z = x / y. Like the big.Int.Quo method (and unlike the
+// TryQuo returns z = x / y. Like the big.Int.Quo method (and unlike the
// big.Int.Div method), it truncates towards zero.
//
// ok is false (and z will be IntRange{nil, nil}) if x is non-empty and y
// contains zero, as it's invalid to divide by zero. Otherwise, ok is true.
-func (x IntRange) Quo(y IntRange) (z IntRange, ok bool) {
+func (x IntRange) TryQuo(y IntRange) (z IntRange, ok bool) {
if x.Empty() || y.Empty() {
return makeEmptyRange(), true
}
@@ -692,12 +724,12 @@
return ret.toIntRange(), true
}
-// Rsh returns z = x >> y.
+// TryRsh returns z = x >> y.
//
// ok is false (and z will be IntRange{nil, nil}) if x is non-empty and y
// contains at least one negative value, as it's invalid to shift by a negative
// number. Otherwise, ok is true.
-func (x IntRange) Rsh(y IntRange) (z IntRange, ok bool) {
+func (x IntRange) TryRsh(y IntRange) (z IntRange, ok bool) {
if x.Empty() || y.Empty() {
return makeEmptyRange(), true
}
@@ -750,15 +782,12 @@
}
// And returns z = x & y.
-//
-// ok is false (and z will be IntRange{nil, nil}) if x or y contains at least
-// one negative value. Otherwise, ok is true.
-func (x IntRange) And(y IntRange) (z IntRange, ok bool) {
+func (x IntRange) And(y IntRange) (z IntRange) {
if x.Empty() || y.Empty() {
- return makeEmptyRange(), true
+ return makeEmptyRange()
}
if !x.ContainsNegative() && !y.ContainsNegative() {
- return andBothNonNeg(x, y), true
+ return andBothNonNeg(x, y)
}
negX, nonX, hasNegX, hasNonX := x.split2Ways()
@@ -791,7 +820,12 @@
z.inPlaceUnite(andBothNonNeg(nonX, nonY))
}
}
- return z, true
+ return z
+}
+
+// TryAnd returns (x.And(y), true).
+func (x IntRange) TryAnd(y IntRange) (z IntRange, ok bool) {
+ return x.And(y), true
}
func andBothNonNeg(x IntRange, y IntRange) (z IntRange) {
@@ -855,15 +889,12 @@
}
// Or returns z = x | y.
-//
-// ok is false (and z will be IntRange{nil, nil}) if x or y contains at least
-// one negative value. Otherwise, ok is true.
-func (x IntRange) Or(y IntRange) (z IntRange, ok bool) {
+func (x IntRange) Or(y IntRange) (z IntRange) {
if x.Empty() || y.Empty() {
- return makeEmptyRange(), true
+ return makeEmptyRange()
}
if !x.ContainsNegative() && !y.ContainsNegative() {
- return orBothNonNeg(x, y), true
+ return orBothNonNeg(x, y)
}
negX, nonX, hasNegX, hasNonX := x.split2Ways()
@@ -896,7 +927,12 @@
z.inPlaceUnite(orBothNonNeg(nonX, nonY))
}
}
- return z, true
+ return z
+}
+
+// TryOr returns (x.Or(y), true).
+func (x IntRange) TryOr(y IntRange) (z IntRange, ok bool) {
+ return x.Or(y), true
}
func orBothNonNeg(x IntRange, y IntRange) (z IntRange) {
diff --git a/lib/interval/interval_test.go b/lib/interval/interval_test.go
index b3cd1ec..1727195 100644
--- a/lib/interval/interval_test.go
+++ b/lib/interval/interval_test.go
@@ -441,16 +441,16 @@
}
var intOperators = map[rune]func(IntRange, IntRange) (IntRange, bool){
- '∪': func(x IntRange, y IntRange) (z IntRange, ok bool) { return x.Unite(y), true },
- '∩': func(x IntRange, y IntRange) (z IntRange, ok bool) { return x.Intersect(y), true },
- '+': func(x IntRange, y IntRange) (z IntRange, ok bool) { return x.Add(y), true },
- '-': func(x IntRange, y IntRange) (z IntRange, ok bool) { return x.Sub(y), true },
- '*': func(x IntRange, y IntRange) (z IntRange, ok bool) { return x.Mul(y), true },
- '/': IntRange.Quo,
- '«': IntRange.Lsh,
- '»': IntRange.Rsh,
- '&': IntRange.And,
- '|': IntRange.Or,
+ '∪': IntRange.TryUnite,
+ '∩': IntRange.TryIntersect,
+ '+': IntRange.TryAdd,
+ '-': IntRange.TrySub,
+ '*': IntRange.TryMul,
+ '/': IntRange.TryQuo,
+ '«': IntRange.TryLsh,
+ '»': IntRange.TryRsh,
+ '&': IntRange.TryAnd,
+ '|': IntRange.TryOr,
}
var intOperatorsKeys []rune
@@ -913,10 +913,10 @@
x := IntRange{x0, x1}
want := x
- if got, _ := x.And(minusOne); !got.Eq(want) {
+ if got := x.And(minusOne); !got.Eq(want) {
tt.Fatalf("%v & -1: got %v, want %v", x, got, want)
}
- if got, _ := minusOne.And(x); !got.Eq(want) {
+ if got := minusOne.And(x); !got.Eq(want) {
tt.Fatalf("-1 & %v: got %v, want %v", x, got, want)
}
}
@@ -933,10 +933,10 @@
want = sharedEmptyRange
}
- if got, _ := x.And(zero); !got.Eq(want) {
+ if got := x.And(zero); !got.Eq(want) {
tt.Fatalf("%v & +0: got %v, want %v", x, got, want)
}
- if got, _ := zero.And(x); !got.Eq(want) {
+ if got := zero.And(x); !got.Eq(want) {
tt.Fatalf("+0 & %v: got %v, want %v", x, got, want)
}
}
@@ -953,10 +953,10 @@
want = sharedEmptyRange
}
- if got, _ := x.Or(minusOne); !got.Eq(want) {
+ if got := x.Or(minusOne); !got.Eq(want) {
tt.Fatalf("%v | -1: got %v, want %v", x, got, want)
}
- if got, _ := minusOne.Or(x); !got.Eq(want) {
+ if got := minusOne.Or(x); !got.Eq(want) {
tt.Fatalf("-1 | %v: got %v, want %v", x, got, want)
}
}
@@ -970,10 +970,10 @@
x := IntRange{x0, x1}
want := x
- if got, _ := x.Or(zero); !got.Eq(want) {
+ if got := x.Or(zero); !got.Eq(want) {
tt.Fatalf("%v | +0: got %v, want %v", x, got, want)
}
- if got, _ := zero.Or(x); !got.Eq(want) {
+ if got := zero.Or(x); !got.Eq(want) {
tt.Fatalf("+0 | %v: got %v, want %v", x, got, want)
}
}