1
0
mirror of https://github.com/golang/go synced 2024-11-17 13:54:46 -07:00

[dev.typeparams] cmd/compile/internal/importer: adjust importer to match compiler importer

The compiler chooses the literal value export format by type
not by constant.Kind. That is, a floating-point constant is
always exported as a (big) float value, not a (big) rational
value, even though the internal representation may be that
of a rational number. (This is a possibility now that the
compiler also uses the go/constant package.)

Naturally, during import, a floating-point value is read as
a float and represented as a (big) float in go/constant.

The types2 importer (based on the go/types importer) read
the floating-point number elements (mantissa, exponent) but
then constructed the float go/constant value through a series
of elementary operations, typically leading to a rational,
but sometimes even an integer number (e.g. for math.MaxFloat64).

There is no problem with that (the value is the same) but if
we want to impose bitsize limits on overlarge integer values
we quickly run into trouble with large floats represented as
integers.

This change matches the code importing float literals with
the code used by the compiler.

Note: At some point we may want to relax the import/export code
for constant values and export them by representation rather than
by type. As is, we lose accuracy since all floating-point point
values, even the ones internally represented as rational numbers
end up being exported as floating-point numbers.

Change-Id: Ic751b2046a0fd047f751da3d35cbef0a1b5fea3e
Reviewed-on: https://go-review.googlesource.com/c/go/+/288632
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-02-01 15:39:42 -08:00
parent 3f845b3b45
commit bb53a5ad43

View File

@ -17,6 +17,7 @@ import (
"go/constant"
"go/token"
"io"
"math/big"
"sort"
)
@ -324,7 +325,9 @@ func (r *importReader) value() (typ types2.Type, val constant.Value) {
val = constant.MakeString(r.string())
case types2.IsInteger:
val = r.mpint(b)
var x big.Int
r.mpint(&x, b)
val = constant.Make(&x)
case types2.IsFloat:
val = r.mpfloat(b)
@ -369,8 +372,8 @@ func intSize(b *types2.Basic) (signed bool, maxBytes uint) {
return
}
func (r *importReader) mpint(b *types2.Basic) constant.Value {
signed, maxBytes := intSize(b)
func (r *importReader) mpint(x *big.Int, typ *types2.Basic) {
signed, maxBytes := intSize(typ)
maxSmall := 256 - maxBytes
if signed {
@ -389,7 +392,8 @@ func (r *importReader) mpint(b *types2.Basic) constant.Value {
v = ^v
}
}
return constant.MakeInt64(v)
x.SetInt64(v)
return
}
v := -n
@ -399,39 +403,23 @@ func (r *importReader) mpint(b *types2.Basic) constant.Value {
if v < 1 || uint(v) > maxBytes {
errorf("weird decoding: %v, %v => %v", n, signed, v)
}
buf := make([]byte, v)
io.ReadFull(&r.declReader, buf)
// convert to little endian
// TODO(gri) go/constant should have a more direct conversion function
// (e.g., once it supports a big.Float based implementation)
for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
buf[i], buf[j] = buf[j], buf[i]
}
x := constant.MakeFromBytes(buf)
b := make([]byte, v)
io.ReadFull(&r.declReader, b)
x.SetBytes(b)
if signed && n&1 != 0 {
x = constant.UnaryOp(token.SUB, x, 0)
x.Neg(x)
}
return x
}
func (r *importReader) mpfloat(b *types2.Basic) constant.Value {
x := r.mpint(b)
if constant.Sign(x) == 0 {
return x
func (r *importReader) mpfloat(typ *types2.Basic) constant.Value {
var mant big.Int
r.mpint(&mant, typ)
var f big.Float
f.SetInt(&mant)
if f.Sign() != 0 {
f.SetMantExp(&f, int(r.int64()))
}
exp := r.int64()
switch {
case exp > 0:
x = constant.Shift(x, token.SHL, uint(exp))
case exp < 0:
d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
x = constant.BinaryOp(x, token.QUO, d)
}
return x
return constant.Make(&f)
}
func (r *importReader) ident() string {