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