mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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>
This commit is contained in:
parent
723f86537c
commit
3f91a017f8
5 changed files with 25 additions and 20 deletions
|
|
@ -145,9 +145,15 @@ func MakeFloat64(x float64) Value {
|
|||
}
|
||||
|
||||
// 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 @@ func is63bit(x int64) bool {
|
|||
|
||||
// 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 @@ func UnaryOp(op token.Token, y Value, size int) Value {
|
|||
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ func val(lit string) Value {
|
|||
}
|
||||
}
|
||||
|
||||
return MakeFromLiteral(lit, tok)
|
||||
return MakeFromLiteral(lit, tok, 0)
|
||||
}
|
||||
|
||||
var optab = map[string]token.Token{
|
||||
|
|
@ -272,7 +272,7 @@ func doOp(x Value, op token.Token, y Value) (z Value) {
|
|||
defer panicHandler(&z)
|
||||
|
||||
if x == nil {
|
||||
return UnaryOp(op, y, -1)
|
||||
return UnaryOp(op, y, 0)
|
||||
}
|
||||
|
||||
switch op {
|
||||
|
|
@ -354,7 +354,7 @@ func TestUnknown(t *testing.T) {
|
|||
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)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -706,7 +706,7 @@ func (p *parser) parseInt() string {
|
|||
//
|
||||
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 @@ func (p *parser) parseConstDecl() {
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -117,11 +117,11 @@ func (check *Checker) unary(x *operand, op token.Token) {
|
|||
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ func (x *operand) String() string {
|
|||
|
||||
// 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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue