go.tools/go/exact: more consistent handling of unknown values

R=adonovan
CC=golang-dev
https://golang.org/cl/10431046
This commit is contained in:
Robert Griesemer 2013-06-21 19:56:40 -07:00
parent 78d364c43e
commit cf8ec1591f
1 changed files with 42 additions and 17 deletions

View File

@ -48,9 +48,6 @@ type Value interface {
implementsValue() implementsValue()
} }
// TODO(gri) Handle unknown values more consistently. Some operations
// accept unknowns, some don't.
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Implementations // Implementations
@ -195,37 +192,61 @@ func MakeFromLiteral(lit string, tok token.Token) Value {
// Accessors // Accessors
// BoolVal returns the Go boolean value of x, which must be a Bool. // BoolVal returns the Go boolean value of x, which must be a Bool.
func BoolVal(x Value) bool { return bool(x.(boolVal)) } // The result is false for unknown values.
func BoolVal(x Value) bool {
switch x := x.(type) {
case boolVal:
return bool(x)
case unknownVal:
return false
}
panic(fmt.Sprintf("invalid BoolVal(%v)", x))
}
// StringVal returns the Go string value of x, which must be a String. // StringVal returns the Go string value of x, which must be a String.
func StringVal(x Value) string { return string(x.(stringVal)) } // The result is "" for unknown values.
func StringVal(x Value) string {
switch x := x.(type) {
case stringVal:
return string(x)
case unknownVal:
return ""
}
panic(fmt.Sprintf("invalidStringVal(%v)", x))
}
// Int64Val returns the Go int64 value of x and whether the result is exact; // Int64Val returns the Go int64 value of x and whether the result is exact;
// x must be an Int. If the result is not exact, its value is undefined. // x must be an Int. If the result is not exact, its value is undefined.
// The result is (0, false) for unknown values.
func Int64Val(x Value) (int64, bool) { func Int64Val(x Value) (int64, bool) {
switch x := x.(type) { switch x := x.(type) {
case int64Val: case int64Val:
return int64(x), true return int64(x), true
case intVal: case intVal:
return x.val.Int64(), x.val.BitLen() <= 63 return x.val.Int64(), x.val.BitLen() <= 63
case unknownVal:
return 0, false
} }
panic(fmt.Sprintf("invalid Int64Val(%v)", x)) panic(fmt.Sprintf("invalid Int64Val(%v)", x))
} }
// Uint64Val returns the Go uint64 value of x and whether the result is exact; // Uint64Val returns the Go uint64 value of x and whether the result is exact;
// x must be an Int. If the result is not exact, its value is undefined. // x must be an Int. If the result is not exact, its value is undefined.
// The result is (0, false) for unknown values.
func Uint64Val(x Value) (uint64, bool) { func Uint64Val(x Value) (uint64, bool) {
switch x := x.(type) { switch x := x.(type) {
case int64Val: case int64Val:
return uint64(x), x >= 0 return uint64(x), x >= 0
case intVal: case intVal:
return x.val.Uint64(), x.val.Sign() >= 0 && x.val.BitLen() <= 64 return x.val.Uint64(), x.val.Sign() >= 0 && x.val.BitLen() <= 64
case unknownVal:
return 0, false
} }
panic(fmt.Sprintf("invalid Uint64Val(%v)", x)) panic(fmt.Sprintf("invalid Uint64Val(%v)", x))
} }
// Float64Val returns the nearest Go float64 value of x and whether the result is exact; // Float64Val returns the nearest Go float64 value of x and whether the result is exact;
// x must be numeric but not Complex. // x must be numeric but not Complex. The result is (0, false) for unknown values.
func Float64Val(x Value) (float64, bool) { func Float64Val(x Value) (float64, bool) {
switch x := x.(type) { switch x := x.(type) {
case int64Val: case int64Val:
@ -235,18 +256,23 @@ func Float64Val(x Value) (float64, bool) {
return new(big.Rat).SetFrac(x.val, int1).Float64() return new(big.Rat).SetFrac(x.val, int1).Float64()
case floatVal: case floatVal:
return x.val.Float64() return x.val.Float64()
case unknownVal:
return 0, false
} }
panic(fmt.Sprintf("invalid Float64Val(%v)", x)) panic(fmt.Sprintf("invalid Float64Val(%v)", x))
} }
// BitLen() returns the number of bits required to represent // BitLen() returns the number of bits required to represent
// the absolute value x in binary representation; x must be an Int. // the absolute value x in binary representation; x must be an Int.
// The result is 0 for unknown values.
func BitLen(x Value) int { func BitLen(x Value) int {
switch x := x.(type) { switch x := x.(type) {
case int64Val: case int64Val:
return new(big.Int).SetInt64(int64(x)).BitLen() return new(big.Int).SetInt64(int64(x)).BitLen()
case intVal: case intVal:
return x.val.BitLen() return x.val.BitLen()
case unknownVal:
return 0
} }
panic(fmt.Sprintf("invalid BitLen(%v)", x)) panic(fmt.Sprintf("invalid BitLen(%v)", x))
} }
@ -254,14 +280,9 @@ func BitLen(x Value) int {
// Sign returns -1, 0, or 1 depending on whether // Sign returns -1, 0, or 1 depending on whether
// x < 0, x == 0, or x > 0. For complex values z, // x < 0, x == 0, or x > 0. For complex values z,
// the sign is 0 if z == 0, otherwise it is != 0. // the sign is 0 if z == 0, otherwise it is != 0.
// For unknown values, the sign is always 1 (this // The result is 0 for unknown values.
// helps avoiding "division by zero errors"). For
// all other values, Sign panics.
//
func Sign(x Value) int { func Sign(x Value) int {
switch x := x.(type) { switch x := x.(type) {
case unknownVal:
return 1
case int64Val: case int64Val:
switch { switch {
case x < 0: case x < 0:
@ -276,6 +297,8 @@ func Sign(x Value) int {
return x.val.Sign() return x.val.Sign()
case complexVal: case complexVal:
return x.re.Sign() | x.im.Sign() return x.re.Sign() | x.im.Sign()
case unknownVal:
return 0
} }
panic(fmt.Sprintf("invalid Sign(%v)", x)) panic(fmt.Sprintf("invalid Sign(%v)", x))
} }
@ -285,18 +308,18 @@ func Sign(x Value) int {
// MakeImag returns the numeric value x*i (possibly 0); // MakeImag returns the numeric value x*i (possibly 0);
// x must be numeric but not Complex. // x must be numeric but not Complex.
// If x is unknown, the result is unknown. // The result is unknown for unknown values.
func MakeImag(x Value) Value { func MakeImag(x Value) Value {
var im *big.Rat var im *big.Rat
switch x := x.(type) { switch x := x.(type) {
case unknownVal:
return x
case int64Val: case int64Val:
im = big.NewRat(int64(x), 1) im = big.NewRat(int64(x), 1)
case intVal: case intVal:
im = new(big.Rat).SetFrac(x.val, int1) im = new(big.Rat).SetFrac(x.val, int1)
case floatVal: case floatVal:
im = x.val im = x.val
case unknownVal:
return x
default: default:
panic(fmt.Sprintf("invalid MakeImag(%v)", x)) panic(fmt.Sprintf("invalid MakeImag(%v)", x))
} }
@ -304,20 +327,22 @@ func MakeImag(x Value) Value {
} }
// Real returns the real part of x, which must be a numeric value. // Real returns the real part of x, which must be a numeric value.
// If x is unknown, the result is unknown. // The result is unknown for unknown values.
func Real(x Value) Value { func Real(x Value) Value {
if z, ok := x.(complexVal); ok { if z, ok := x.(complexVal); ok {
return normFloat(z.re) return normFloat(z.re)
} }
// TODO(gri) should we check explicit for unknownVal and disallow all others?
return x return x
} }
// Imag returns the imaginary part of x, which must be a numeric value. // Imag returns the imaginary part of x, which must be a numeric value.
// If x is unknown, the result is 0. // The result is 0 for unknown values.
func Imag(x Value) Value { func Imag(x Value) Value {
if z, ok := x.(complexVal); ok { if z, ok := x.(complexVal); ok {
return normFloat(z.im) return normFloat(z.im)
} }
// TODO(gri) should we check explicit for unknownVal and disallow all others?
return int64Val(0) return int64Val(0)
} }