Commit 3f91a017 authored by Robert Griesemer's avatar Robert Griesemer Committed by Alan Donovan

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: 's avatarAlan Donovan <adonovan@google.com>
parent 723f8653
...@@ -145,9 +145,15 @@ func MakeFloat64(x float64) Value { ...@@ -145,9 +145,15 @@ func MakeFloat64(x float64) Value {
} }
// MakeFromLiteral returns the corresponding integer, floating-point, // MakeFromLiteral returns the corresponding integer, floating-point,
// imaginary, character, or string value for a Go literal string. The // imaginary, character, or string value for a Go literal string.
// result is nil if the literal string is invalid. // If prec > 0, prec specifies an upper limit for the precision of
func MakeFromLiteral(lit string, tok token.Token) Value { // 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 { switch tok {
case token.INT: case token.INT:
if x, err := strconv.ParseInt(lit, 0, 64); err == nil { if x, err := strconv.ParseInt(lit, 0, 64); err == nil {
...@@ -489,10 +495,10 @@ func is63bit(x int64) bool { ...@@ -489,10 +495,10 @@ func is63bit(x int64) bool {
// UnaryOp returns the result of the unary expression op y. // UnaryOp returns the result of the unary expression op y.
// The operation must be defined for the operand. // 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. // 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 { switch op {
case token.ADD: case token.ADD:
switch y.(type) { switch y.(type) {
...@@ -530,11 +536,10 @@ func UnaryOp(op token.Token, y Value, size int) Value { ...@@ -530,11 +536,10 @@ func UnaryOp(op token.Token, y Value, size int) Value {
goto Error goto Error
} }
// For unsigned types, the result will be negative and // For unsigned types, the result will be negative and
// thus "too large": We must limit the result size to // thus "too large": We must limit the result precision
// the type's size. // to the type's precision.
if size >= 0 { if prec > 0 {
s := uint(size) * 8 z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), prec)) // z &^= (-1)<<prec
z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)<<s
} }
return normInt(&z) return normInt(&z)
......
...@@ -227,7 +227,7 @@ func val(lit string) Value { ...@@ -227,7 +227,7 @@ func val(lit string) Value {
} }
} }
return MakeFromLiteral(lit, tok) return MakeFromLiteral(lit, tok, 0)
} }
var optab = map[string]token.Token{ var optab = map[string]token.Token{
...@@ -272,7 +272,7 @@ func doOp(x Value, op token.Token, y Value) (z Value) { ...@@ -272,7 +272,7 @@ func doOp(x Value, op token.Token, y Value) (z Value) {
defer panicHandler(&z) defer panicHandler(&z)
if x == nil { if x == nil {
return UnaryOp(op, y, -1) return UnaryOp(op, y, 0)
} }
switch op { switch op {
...@@ -354,7 +354,7 @@ func TestUnknown(t *testing.T) { ...@@ -354,7 +354,7 @@ func TestUnknown(t *testing.T) {
MakeBool(false), // token.ADD ok below, operation is never considered MakeBool(false), // token.ADD ok below, operation is never considered
MakeString(""), MakeString(""),
MakeInt64(1), MakeInt64(1),
MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT), MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT, 0),
MakeFloat64(1.2), MakeFloat64(1.2),
MakeImag(MakeFloat64(1.2)), MakeImag(MakeFloat64(1.2)),
} }
......
...@@ -706,7 +706,7 @@ func (p *parser) parseInt() string { ...@@ -706,7 +706,7 @@ func (p *parser) parseInt() string {
// //
func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) { func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) {
// mantissa // mantissa
mant := exact.MakeFromLiteral(p.parseInt(), token.INT) mant := exact.MakeFromLiteral(p.parseInt(), token.INT, 0)
if mant == nil { if mant == nil {
panic("invalid mantissa") panic("invalid mantissa")
} }
...@@ -793,13 +793,13 @@ func (p *parser) parseConstDecl() { ...@@ -793,13 +793,13 @@ func (p *parser) parseConstDecl() {
case scanner.Char: case scanner.Char:
// rune_lit // rune_lit
typ = types.Typ[types.UntypedRune] typ = types.Typ[types.UntypedRune]
val = exact.MakeFromLiteral(p.lit, token.CHAR) val = exact.MakeFromLiteral(p.lit, token.CHAR, 0)
p.next() p.next()
case scanner.String: case scanner.String:
// string_lit // string_lit
typ = types.Typ[types.UntypedString] typ = types.Typ[types.UntypedString]
val = exact.MakeFromLiteral(p.lit, token.STRING) val = exact.MakeFromLiteral(p.lit, token.STRING, 0)
p.next() p.next()
default: default:
......
...@@ -117,11 +117,11 @@ func (check *Checker) unary(x *operand, op token.Token) { ...@@ -117,11 +117,11 @@ func (check *Checker) unary(x *operand, op token.Token) {
if x.mode == constant { if x.mode == constant {
typ := x.typ.Underlying().(*Basic) typ := x.typ.Underlying().(*Basic)
size := -1 var prec uint
if isUnsigned(typ) { 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 // Typed constants must be representable in
// their type after each constant operation. // their type after each constant operation.
if isTyped(typ) { if isTyped(typ) {
......
...@@ -166,7 +166,7 @@ func (x *operand) String() string { ...@@ -166,7 +166,7 @@ func (x *operand) String() string {
// setConst sets x to the untyped constant for literal lit. // setConst sets x to the untyped constant for literal lit.
func (x *operand) setConst(tok token.Token, lit string) { func (x *operand) setConst(tok token.Token, lit string) {
val := exact.MakeFromLiteral(lit, tok) val := exact.MakeFromLiteral(lit, tok, 0)
if val == nil { if val == nil {
// TODO(gri) Should we make it an unknown constant instead? // TODO(gri) Should we make it an unknown constant instead?
x.mode = invalid x.mode = invalid
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment