go/exact: future-proof API: permit setting precision limit
Added a prec parameter to MakeFromLiteral (which currently must
always be 0). This will permit go/types to provide an upper limit
for the precision of constant values, eventually. Overflows can be
returned with a special Overflow value (very much like the current
Unknown values).
This is a minimal change that should prevent the need for future
backward-incompatible API changes.
Change-Id: I6c9390d7cc4810375e26c53ed3bde5a383392330
Reviewed-on: https://go-review.googlesource.com/9168
Run-TryBot: Robert Griesemer <gri@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/src/go/exact/exact.go b/src/go/exact/exact.go
index e3ceff33..f0510ce 100644
--- a/src/go/exact/exact.go
+++ b/src/go/exact/exact.go
@@ -145,9 +145,15 @@
}
// MakeFromLiteral returns the corresponding integer, floating-point,
-// imaginary, character, or string value for a Go literal string. The
-// result is nil if the literal string is invalid.
-func MakeFromLiteral(lit string, tok token.Token) Value {
+// imaginary, character, or string value for a Go literal string.
+// If prec > 0, prec specifies an upper limit for the precision of
+// a numeric value. If the literal string is invalid, the result is
+// nil.
+// BUG(gri) Only prec == 0 is supported at the moment.
+func MakeFromLiteral(lit string, tok token.Token, prec uint) Value {
+ if prec != 0 {
+ panic("limited precision not supported")
+ }
switch tok {
case token.INT:
if x, err := strconv.ParseInt(lit, 0, 64); err == nil {
@@ -489,10 +495,10 @@
// UnaryOp returns the result of the unary expression op y.
// The operation must be defined for the operand.
-// If size >= 0 it specifies the ^ (xor) result size in bytes.
+// If prec > 0 it specifies the ^ (xor) result size in bits.
// If y is Unknown, the result is Unknown.
//
-func UnaryOp(op token.Token, y Value, size int) Value {
+func UnaryOp(op token.Token, y Value, prec uint) Value {
switch op {
case token.ADD:
switch y.(type) {
@@ -530,11 +536,10 @@
goto Error
}
// For unsigned types, the result will be negative and
- // thus "too large": We must limit the result size to
- // the type's size.
- if size >= 0 {
- s := uint(size) * 8
- z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)<<s
+ // thus "too large": We must limit the result precision
+ // to the type's precision.
+ if prec > 0 {
+ z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), prec)) // z &^= (-1)<<prec
}
return normInt(&z)
diff --git a/src/go/exact/exact_test.go b/src/go/exact/exact_test.go
index aa38a89..0f17014 100644
--- a/src/go/exact/exact_test.go
+++ b/src/go/exact/exact_test.go
@@ -227,7 +227,7 @@
}
}
- return MakeFromLiteral(lit, tok)
+ return MakeFromLiteral(lit, tok, 0)
}
var optab = map[string]token.Token{
@@ -272,7 +272,7 @@
defer panicHandler(&z)
if x == nil {
- return UnaryOp(op, y, -1)
+ return UnaryOp(op, y, 0)
}
switch op {
@@ -354,7 +354,7 @@
MakeBool(false), // token.ADD ok below, operation is never considered
MakeString(""),
MakeInt64(1),
- MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT),
+ MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT, 0),
MakeFloat64(1.2),
MakeImag(MakeFloat64(1.2)),
}
diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go
index b7e2bab..523edb0 100644
--- a/src/go/internal/gcimporter/gcimporter.go
+++ b/src/go/internal/gcimporter/gcimporter.go
@@ -706,7 +706,7 @@
//
func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) {
// mantissa
- mant := exact.MakeFromLiteral(p.parseInt(), token.INT)
+ mant := exact.MakeFromLiteral(p.parseInt(), token.INT, 0)
if mant == nil {
panic("invalid mantissa")
}
@@ -793,13 +793,13 @@
case scanner.Char:
// rune_lit
typ = types.Typ[types.UntypedRune]
- val = exact.MakeFromLiteral(p.lit, token.CHAR)
+ val = exact.MakeFromLiteral(p.lit, token.CHAR, 0)
p.next()
case scanner.String:
// string_lit
typ = types.Typ[types.UntypedString]
- val = exact.MakeFromLiteral(p.lit, token.STRING)
+ val = exact.MakeFromLiteral(p.lit, token.STRING, 0)
p.next()
default:
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index 2b60a1b..14674a9 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -117,11 +117,11 @@
if x.mode == constant {
typ := x.typ.Underlying().(*Basic)
- size := -1
+ var prec uint
if isUnsigned(typ) {
- size = int(check.conf.sizeof(typ))
+ prec = uint(check.conf.sizeof(typ) * 8)
}
- x.val = exact.UnaryOp(op, x.val, size)
+ x.val = exact.UnaryOp(op, x.val, prec)
// Typed constants must be representable in
// their type after each constant operation.
if isTyped(typ) {
diff --git a/src/go/types/operand.go b/src/go/types/operand.go
index 6df72be..2714c38 100644
--- a/src/go/types/operand.go
+++ b/src/go/types/operand.go
@@ -166,7 +166,7 @@
// setConst sets x to the untyped constant for literal lit.
func (x *operand) setConst(tok token.Token, lit string) {
- val := exact.MakeFromLiteral(lit, tok)
+ val := exact.MakeFromLiteral(lit, tok, 0)
if val == nil {
// TODO(gri) Should we make it an unknown constant instead?
x.mode = invalid