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:
parent
78d364c43e
commit
cf8ec1591f
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue