mirror of
https://github.com/golang/go
synced 2024-11-26 02:47:58 -07:00
go/constant: switch to floating-point representation when fractions become too large
Use two internal representations for Float values (similar to what is done for Int values). Transparently switch to a big.Float representation when big.Rat values become unwieldy. This is almost never needed for real-world programs but it is trivial to create test cases that cannot be handled with rational arithmetic alone. As a consequence, the go/constant API semantics changes slightly: Until now, a value could always be represented in its "smallest" form (e.g., float values that happened to be integers would be represented as integers). Now, constant Kind depends on how the value was created, rather than its actual value. (The reason why we cannot automatically "normalize" values to their smallest form anymore is because floating-point numbers are not exact in general; and thus normalization is often not possible in the first place, or would throw away precision when it is not desired.) This has repercussions as to how constant Values are used go/types and required corresponding adjustments. Details of the changes: go/constant package: - use big.Rat and big.Float values to represent floating-point values (internal change) - changed semantic of Value.Kind accordingly - String now returns a short, human-readable form of a value (this leads to better error messages in go/types) - added ToInt, ToFloat, and ToComplex conversion functions - added ExactString to obtain an exact string form of a value go/types: - adjusted and simplified implementation of representableConst - adjusted various places where Value.Kind was expected to be "smallest" by calling the respective ToInt/Float/Complex conversion functions - enabled 5 disabled tests in stdlib_test.go that now work api checker: - print all constant values in a short human-readable form (floats are printed in floating-point form), but also print an exact form if it is different from the short form - adjusted test golden file and go.1.1.text reference file Fixes #11327. Change-Id: I492b704aae5b0238e5b7cee13e18ffce61193587 Reviewed-on: https://go-review.googlesource.com/17360 Reviewed-by: Alan Donovan <adonovan@google.com> Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
e568a0180a
commit
d0c17461a7
@ -1983,13 +1983,13 @@ pkg log/syslog (openbsd-amd64-cgo), const LOG_SYSLOG = 40
|
||||
pkg log/syslog (openbsd-amd64-cgo), const LOG_USER = 8
|
||||
pkg log/syslog (openbsd-amd64-cgo), const LOG_UUCP = 64
|
||||
pkg log/syslog (openbsd-amd64-cgo), const LOG_WARNING = 4
|
||||
pkg math, const E = 271828182845904523536028747135266249775724709369995957496696763/100000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const Ln10 = 23025850929940456840179914546843642076011014886287729760333279/10000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const Ln2 = 693147180559945309417232121458176568075500134360255254120680009/1000000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const Log10E = 10000000000000000000000000000000000000000000000000000000000000/23025850929940456840179914546843642076011014886287729760333279
|
||||
pkg math, const Log2E = 1000000000000000000000000000000000000000000000000000000000000000/693147180559945309417232121458176568075500134360255254120680009
|
||||
pkg math, const MaxFloat32 = 340282346638528859811704183484516925440
|
||||
pkg math, const MaxFloat64 = 179769313486231570814527423731704356798100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const E = 2.71828 // 271828182845904523536028747135266249775724709369995957496696763/100000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const Ln10 = 2.30259 // 23025850929940456840179914546843642076011014886287729760333279/10000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const Ln2 = 0.693147 // 693147180559945309417232121458176568075500134360255254120680009/1000000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const Log10E = 0.434294 // 10000000000000000000000000000000000000000000000000000000000000/23025850929940456840179914546843642076011014886287729760333279
|
||||
pkg math, const Log2E = 1.4427 // 1000000000000000000000000000000000000000000000000000000000000000/693147180559945309417232121458176568075500134360255254120680009
|
||||
pkg math, const MaxFloat32 = 3.40282e+38 // 340282346638528859811704183484516925440
|
||||
pkg math, const MaxFloat64 = 1.79769e+308 // 179769313486231570814527423731704356798100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const MaxInt16 = 32767
|
||||
pkg math, const MaxInt32 = 2147483647
|
||||
pkg math, const MaxInt64 = 9223372036854775807
|
||||
@ -2002,14 +2002,14 @@ pkg math, const MinInt16 = -32768
|
||||
pkg math, const MinInt32 = -2147483648
|
||||
pkg math, const MinInt64 = -9223372036854775808
|
||||
pkg math, const MinInt8 = -128
|
||||
pkg math, const Phi = 80901699437494742410229341718281905886015458990288143106772431/50000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const Pi = 314159265358979323846264338327950288419716939937510582097494459/100000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const SmallestNonzeroFloat32 = 17516230804060213386546619791123951641/12500000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const SmallestNonzeroFloat64 = 4940656458412465441765687928682213723651/1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const Sqrt2 = 70710678118654752440084436210484903928483593768847403658833987/50000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const SqrtE = 164872127070012814684865078781416357165377610071014801157507931/100000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const SqrtPhi = 63600982475703448212621123086874574585780402092004812430832019/50000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const SqrtPi = 177245385090551602729816748334114518279754945612238712821380779/100000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const Phi = 1.61803 // 80901699437494742410229341718281905886015458990288143106772431/50000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const Pi = 3.14159 // 314159265358979323846264338327950288419716939937510582097494459/100000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const SmallestNonzeroFloat32 = 1.4013e-45 // 17516230804060213386546619791123951641/12500000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const SmallestNonzeroFloat64 = 4.94066e-324 // 4940656458412465441765687928682213723651/1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const Sqrt2 = 1.41421 // 70710678118654752440084436210484903928483593768847403658833987/50000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const SqrtE = 1.64872 // 164872127070012814684865078781416357165377610071014801157507931/100000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const SqrtPhi = 1.27202 // 63600982475703448212621123086874574585780402092004812430832019/50000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math, const SqrtPi = 1.77245 // 177245385090551602729816748334114518279754945612238712821380779/100000000000000000000000000000000000000000000000000000000000000
|
||||
pkg math/big, const MaxBase = 36
|
||||
pkg math/big, method (*Int) MarshalJSON() ([]uint8, error)
|
||||
pkg math/big, method (*Int) SetUint64(uint64) *Int
|
||||
|
@ -680,7 +680,14 @@ func (w *Walker) emitObj(obj types.Object) {
|
||||
switch obj := obj.(type) {
|
||||
case *types.Const:
|
||||
w.emitf("const %s %s", obj.Name(), w.typeString(obj.Type()))
|
||||
w.emitf("const %s = %s", obj.Name(), obj.Val())
|
||||
x := obj.Val()
|
||||
short := x.String()
|
||||
exact := x.ExactString()
|
||||
if short == exact {
|
||||
w.emitf("const %s = %s", obj.Name(), short)
|
||||
} else {
|
||||
w.emitf("const %s = %s // %s", obj.Name(), short, exact)
|
||||
}
|
||||
case *types.Var:
|
||||
w.emitf("var %s %s", obj.Name(), w.typeString(obj.Type()))
|
||||
case *types.TypeName:
|
||||
|
2
src/cmd/api/testdata/src/pkg/p1/golden.txt
vendored
2
src/cmd/api/testdata/src/pkg/p1/golden.txt
vendored
@ -10,7 +10,7 @@ pkg p1, const ConstChase2 = 11
|
||||
pkg p1, const ConstChase2 ideal-int
|
||||
pkg p1, const ConversionConst = 5
|
||||
pkg p1, const ConversionConst MyInt
|
||||
pkg p1, const FloatConst = 3/2
|
||||
pkg p1, const FloatConst = 1.5 // 3/2
|
||||
pkg p1, const FloatConst ideal-float
|
||||
pkg p1, const StrConst = "foo"
|
||||
pkg p1, const StrConst ideal-string
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -176,12 +176,17 @@ func TestOps(t *testing.T) {
|
||||
want := val(a[i+3])
|
||||
if !eql(got, want) {
|
||||
t.Errorf("%s: got %s; want %s", test, got, want)
|
||||
continue
|
||||
}
|
||||
|
||||
if x0 != nil && !eql(x, x0) {
|
||||
t.Errorf("%s: x changed to %s", test, x)
|
||||
continue
|
||||
}
|
||||
|
||||
if !eql(y, y0) {
|
||||
t.Errorf("%s: y changed to %s", test, y)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -195,6 +200,68 @@ func eql(x, y Value) bool {
|
||||
return Compare(x, token.EQL, y)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// String tests
|
||||
|
||||
var xxx = strings.Repeat("x", 68)
|
||||
|
||||
var stringTests = []struct {
|
||||
input, short, exact string
|
||||
}{
|
||||
// Unknown
|
||||
{"", "unknown", "unknown"},
|
||||
{"0x", "unknown", "unknown"},
|
||||
{"'", "unknown", "unknown"},
|
||||
{"1f0", "unknown", "unknown"},
|
||||
{"unknown", "unknown", "unknown"},
|
||||
|
||||
// Bool
|
||||
{"true", "true", "true"},
|
||||
{"false", "false", "false"},
|
||||
|
||||
// String
|
||||
{`""`, `""`, `""`},
|
||||
{`"foo"`, `"foo"`, `"foo"`},
|
||||
{`"` + xxx + `xx"`, `"` + xxx + `xx"`, `"` + xxx + `xx"`},
|
||||
{`"` + xxx + `xxx"`, `"` + xxx + `...`, `"` + xxx + `xxx"`},
|
||||
{`"` + xxx + xxx + `xxx"`, `"` + xxx + `...`, `"` + xxx + xxx + `xxx"`},
|
||||
|
||||
// Int
|
||||
{"0", "0", "0"},
|
||||
{"-1", "-1", "-1"},
|
||||
{"12345", "12345", "12345"},
|
||||
{"-12345678901234567890", "-12345678901234567890", "-12345678901234567890"},
|
||||
{"12345678901234567890", "12345678901234567890", "12345678901234567890"},
|
||||
|
||||
// Float
|
||||
{"0.", "0", "0"},
|
||||
{"-0.0", "0", "0"},
|
||||
{"10.0", "10", "10"},
|
||||
{"2.1", "2.1", "21/10"},
|
||||
{"-2.1", "-2.1", "-21/10"},
|
||||
{"1e9999", "1e+9999", "0x.f8d4a9da224650a8cb2959e10d985ad92adbd44c62917e608b1f24c0e1b76b6f61edffeb15c135a4b601637315f7662f325f82325422b244286a07663c9415d2p+33216"},
|
||||
{"2.71828182845904523536028747135266249775724709369995957496696763", "2.71828", "271828182845904523536028747135266249775724709369995957496696763/100000000000000000000000000000000000000000000000000000000000000"},
|
||||
|
||||
// Complex
|
||||
{"0i", "(0 + 0i)", "(0 + 0i)"},
|
||||
{"-0i", "(0 + 0i)", "(0 + 0i)"},
|
||||
{"10i", "(0 + 10i)", "(0 + 10i)"},
|
||||
{"-10i", "(0 + -10i)", "(0 + -10i)"},
|
||||
{"1e9999i", "(0 + 1e+9999i)", "(0 + 0x.f8d4a9da224650a8cb2959e10d985ad92adbd44c62917e608b1f24c0e1b76b6f61edffeb15c135a4b601637315f7662f325f82325422b244286a07663c9415d2p+33216i)"},
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
for _, test := range stringTests {
|
||||
x := val(test.input)
|
||||
if got := x.String(); got != test.short {
|
||||
t.Errorf("%s: got %q; want %q as short string", test.input, got, test.short)
|
||||
}
|
||||
if got := x.ExactString(); got != test.exact {
|
||||
t.Errorf("%s: got %q; want %q as exact string", test.input, got, test.exact)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support functions
|
||||
|
||||
@ -212,6 +279,13 @@ func val(lit string) Value {
|
||||
return MakeBool(false)
|
||||
}
|
||||
|
||||
if i := strings.IndexByte(lit, '/'); i >= 0 {
|
||||
// assume fraction
|
||||
a := MakeFromLiteral(lit[:i], token.INT, 0)
|
||||
b := MakeFromLiteral(lit[i+1:], token.INT, 0)
|
||||
return BinaryOp(a, token.QUO, b)
|
||||
}
|
||||
|
||||
tok := token.INT
|
||||
switch first, last := lit[0], lit[len(lit)-1]; {
|
||||
case first == '"' || first == '`':
|
||||
@ -290,32 +364,29 @@ func doOp(x Value, op token.Token, y Value) (z Value) {
|
||||
// Other tests
|
||||
|
||||
var fracTests = []string{
|
||||
"0 0 1",
|
||||
"1 1 1",
|
||||
"-1 -1 1",
|
||||
"1.2 6 5",
|
||||
"-0.991 -991 1000",
|
||||
"1e100 1e100 1",
|
||||
"0",
|
||||
"1",
|
||||
"-1",
|
||||
"1.2",
|
||||
"-0.991",
|
||||
"2.718281828",
|
||||
"3.14159265358979323e-10",
|
||||
"1e100",
|
||||
"1e1000",
|
||||
}
|
||||
|
||||
func TestFractions(t *testing.T) {
|
||||
for _, test := range fracTests {
|
||||
a := strings.Split(test, " ")
|
||||
if len(a) != 3 {
|
||||
t.Errorf("invalid test case: %s", test)
|
||||
continue
|
||||
}
|
||||
|
||||
x := val(a[0])
|
||||
n := val(a[1])
|
||||
d := val(a[2])
|
||||
|
||||
if got := Num(x); !eql(got, n) {
|
||||
t.Errorf("%s: got num = %s; want %s", test, got, n)
|
||||
}
|
||||
|
||||
if got := Denom(x); !eql(got, d) {
|
||||
t.Errorf("%s: got denom = %s; want %s", test, got, d)
|
||||
x := val(test)
|
||||
// We don't check the actual numerator and denominator because they
|
||||
// are unlikely to be 100% correct due to floatVal rounding errors.
|
||||
// Instead, we compute the fraction again and compare the rounded
|
||||
// result.
|
||||
q := BinaryOp(Num(x), token.QUO, Denom(x))
|
||||
got := q.String()
|
||||
want := x.String()
|
||||
if got != want {
|
||||
t.Errorf("%s: got quotient %s, want %s", x, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,10 +91,10 @@ func runImporterTest(t *testing.T, imp Importer, initmap map[*types.Package]Init
|
||||
|
||||
var importerTests = [...]importerTest{
|
||||
{pkgpath: "pointer", name: "Int8Ptr", want: "type Int8Ptr *int8"},
|
||||
{pkgpath: "complexnums", name: "NN", want: "const NN untyped complex", wantval: "(-1/1 + -1/1i)"},
|
||||
{pkgpath: "complexnums", name: "NP", want: "const NP untyped complex", wantval: "(-1/1 + 1/1i)"},
|
||||
{pkgpath: "complexnums", name: "PN", want: "const PN untyped complex", wantval: "(1/1 + -1/1i)"},
|
||||
{pkgpath: "complexnums", name: "PP", want: "const PP untyped complex", wantval: "(1/1 + 1/1i)"},
|
||||
{pkgpath: "complexnums", name: "NN", want: "const NN untyped complex", wantval: "(-1 + -1i)"},
|
||||
{pkgpath: "complexnums", name: "NP", want: "const NP untyped complex", wantval: "(-1 + 1i)"},
|
||||
{pkgpath: "complexnums", name: "PN", want: "const PN untyped complex", wantval: "(1 + -1i)"},
|
||||
{pkgpath: "complexnums", name: "PP", want: "const PP untyped complex", wantval: "(1 + 1i)"},
|
||||
// TODO: enable this entry once bug has been tracked down
|
||||
//{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}},
|
||||
}
|
||||
|
@ -54,14 +54,14 @@ func TestValuesInfo(t *testing.T) {
|
||||
{`package a1; const _ = 0`, `0`, `untyped int`, `0`},
|
||||
{`package a2; const _ = 'A'`, `'A'`, `untyped rune`, `65`},
|
||||
{`package a3; const _ = 0.`, `0.`, `untyped float`, `0`},
|
||||
{`package a4; const _ = 0i`, `0i`, `untyped complex`, `0`},
|
||||
{`package a4; const _ = 0i`, `0i`, `untyped complex`, `(0 + 0i)`},
|
||||
{`package a5; const _ = "foo"`, `"foo"`, `untyped string`, `"foo"`},
|
||||
|
||||
{`package b0; var _ = false`, `false`, `bool`, `false`},
|
||||
{`package b1; var _ = 0`, `0`, `int`, `0`},
|
||||
{`package b2; var _ = 'A'`, `'A'`, `rune`, `65`},
|
||||
{`package b3; var _ = 0.`, `0.`, `float64`, `0`},
|
||||
{`package b4; var _ = 0i`, `0i`, `complex128`, `0`},
|
||||
{`package b4; var _ = 0i`, `0i`, `complex128`, `(0 + 0i)`},
|
||||
{`package b5; var _ = "foo"`, `"foo"`, `string`, `"foo"`},
|
||||
|
||||
{`package c0a; var _ = bool(false)`, `false`, `bool`, `false`},
|
||||
@ -80,9 +80,9 @@ func TestValuesInfo(t *testing.T) {
|
||||
{`package c3b; var _ = float32(0.)`, `float32(0.)`, `float32`, `0`},
|
||||
{`package c3c; type T float32; var _ = T(0.)`, `T(0.)`, `c3c.T`, `0`},
|
||||
|
||||
{`package c4a; var _ = complex64(0i)`, `0i`, `complex64`, `0`},
|
||||
{`package c4b; var _ = complex64(0i)`, `complex64(0i)`, `complex64`, `0`},
|
||||
{`package c4c; type T complex64; var _ = T(0i)`, `T(0i)`, `c4c.T`, `0`},
|
||||
{`package c4a; var _ = complex64(0i)`, `0i`, `complex64`, `(0 + 0i)`},
|
||||
{`package c4b; var _ = complex64(0i)`, `complex64(0i)`, `complex64`, `(0 + 0i)`},
|
||||
{`package c4c; type T complex64; var _ = T(0i)`, `T(0i)`, `c4c.T`, `(0 + 0i)`},
|
||||
|
||||
{`package c5a; var _ = string("foo")`, `"foo"`, `string`, `"foo"`},
|
||||
{`package c5b; var _ = string("foo")`, `string("foo")`, `string`, `"foo"`},
|
||||
@ -97,10 +97,10 @@ func TestValuesInfo(t *testing.T) {
|
||||
{`package e1; const _ = float32(-1e-200)`, `float32(-1e-200)`, `float32`, `0`},
|
||||
{`package e2; const _ = float64( 1e-2000)`, `float64(1e-2000)`, `float64`, `0`},
|
||||
{`package e3; const _ = float64(-1e-2000)`, `float64(-1e-2000)`, `float64`, `0`},
|
||||
{`package e4; const _ = complex64( 1e-200)`, `complex64(1e-200)`, `complex64`, `0`},
|
||||
{`package e5; const _ = complex64(-1e-200)`, `complex64(-1e-200)`, `complex64`, `0`},
|
||||
{`package e6; const _ = complex128( 1e-2000)`, `complex128(1e-2000)`, `complex128`, `0`},
|
||||
{`package e7; const _ = complex128(-1e-2000)`, `complex128(-1e-2000)`, `complex128`, `0`},
|
||||
{`package e4; const _ = complex64( 1e-200)`, `complex64(1e-200)`, `complex64`, `(0 + 0i)`},
|
||||
{`package e5; const _ = complex64(-1e-200)`, `complex64(-1e-200)`, `complex64`, `(0 + 0i)`},
|
||||
{`package e6; const _ = complex128( 1e-2000)`, `complex128(1e-2000)`, `complex128`, `(0 + 0i)`},
|
||||
{`package e7; const _ = complex128(-1e-2000)`, `complex128(-1e-2000)`, `complex128`, `(0 + 0i)`},
|
||||
|
||||
{`package f0 ; var _ float32 = 1e-200`, `1e-200`, `float32`, `0`},
|
||||
{`package f1 ; var _ float32 = -1e-200`, `-1e-200`, `float32`, `0`},
|
||||
@ -108,12 +108,12 @@ func TestValuesInfo(t *testing.T) {
|
||||
{`package f3a; var _ float64 = -1e-2000`, `-1e-2000`, `float64`, `0`},
|
||||
{`package f2b; var _ = 1e-2000`, `1e-2000`, `float64`, `0`},
|
||||
{`package f3b; var _ = -1e-2000`, `-1e-2000`, `float64`, `0`},
|
||||
{`package f4 ; var _ complex64 = 1e-200 `, `1e-200`, `complex64`, `0`},
|
||||
{`package f5 ; var _ complex64 = -1e-200 `, `-1e-200`, `complex64`, `0`},
|
||||
{`package f6a; var _ complex128 = 1e-2000i`, `1e-2000i`, `complex128`, `0`},
|
||||
{`package f7a; var _ complex128 = -1e-2000i`, `-1e-2000i`, `complex128`, `0`},
|
||||
{`package f6b; var _ = 1e-2000i`, `1e-2000i`, `complex128`, `0`},
|
||||
{`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `0`},
|
||||
{`package f4 ; var _ complex64 = 1e-200 `, `1e-200`, `complex64`, `(0 + 0i)`},
|
||||
{`package f5 ; var _ complex64 = -1e-200 `, `-1e-200`, `complex64`, `(0 + 0i)`},
|
||||
{`package f6a; var _ complex128 = 1e-2000i`, `1e-2000i`, `complex128`, `(0 + 0i)`},
|
||||
{`package f7a; var _ complex128 = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`},
|
||||
{`package f6b; var _ = 1e-2000i`, `1e-2000i`, `complex128`, `(0 + 0i)`},
|
||||
{`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@ -143,7 +143,7 @@ func TestValuesInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
// check that value is correct
|
||||
if got := tv.Value.String(); got != test.val {
|
||||
if got := tv.Value.ExactString(); got != test.val {
|
||||
t.Errorf("package %s: got value %s; want %s", name, got, test.val)
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
|
||||
// if both arguments are constants, the result is a constant
|
||||
if x.mode == constant_ && y.mode == constant_ {
|
||||
x.val = constant.BinaryOp(x.val, token.ADD, constant.MakeImag(y.val))
|
||||
x.val = constant.BinaryOp(constant.ToFloat(x.val), token.ADD, constant.MakeImag(constant.ToFloat(y.val)))
|
||||
} else {
|
||||
x.mode = value
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ func (check *Checker) conversion(x *operand, T Type) {
|
||||
case constArg && isConstType(T):
|
||||
// constant conversion
|
||||
switch t := T.Underlying().(*Basic); {
|
||||
case representableConst(x.val, check.conf, t.kind, &x.val):
|
||||
case representableConst(x.val, check.conf, t, &x.val):
|
||||
ok = true
|
||||
case isInteger(x.typ) && isString(t):
|
||||
codepoint := int64(-1)
|
||||
|
@ -48,7 +48,7 @@ func testEval(t *testing.T, fset *token.FileSet, pkg *Package, pos token.Pos, ex
|
||||
// compare values
|
||||
gotStr := ""
|
||||
if gotTv.Value != nil {
|
||||
gotStr = gotTv.Value.String()
|
||||
gotStr = gotTv.Value.ExactString()
|
||||
}
|
||||
if gotStr != valStr {
|
||||
t.Errorf("Eval(%q) got value %s, want %s", expr, gotStr, valStr)
|
||||
|
@ -180,25 +180,27 @@ func roundFloat64(x constant.Value) constant.Value {
|
||||
}
|
||||
|
||||
// representableConst reports whether x can be represented as
|
||||
// value of the given basic type kind and for the configuration
|
||||
// value of the given basic type and for the configuration
|
||||
// provided (only needed for int/uint sizes).
|
||||
//
|
||||
// If rounded != nil, *rounded is set to the rounded value of x for
|
||||
// representable floating-point values; it is left alone otherwise.
|
||||
// It is ok to provide the addressof the first argument for rounded.
|
||||
func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *constant.Value) bool {
|
||||
switch x.Kind() {
|
||||
case constant.Unknown:
|
||||
return true
|
||||
func representableConst(x constant.Value, conf *Config, typ *Basic, rounded *constant.Value) bool {
|
||||
if x.Kind() == constant.Unknown {
|
||||
return true // avoid follow-up errors
|
||||
}
|
||||
|
||||
case constant.Bool:
|
||||
return as == Bool || as == UntypedBool
|
||||
|
||||
case constant.Int:
|
||||
switch {
|
||||
case isInteger(typ):
|
||||
x := constant.ToInt(x)
|
||||
if x.Kind() != constant.Int {
|
||||
return false
|
||||
}
|
||||
if x, ok := constant.Int64Val(x); ok {
|
||||
switch as {
|
||||
switch typ.kind {
|
||||
case Int:
|
||||
var s = uint(conf.sizeof(Typ[as])) * 8
|
||||
var s = uint(conf.sizeof(typ)) * 8
|
||||
return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
|
||||
case Int8:
|
||||
const s = 8
|
||||
@ -209,10 +211,10 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c
|
||||
case Int32:
|
||||
const s = 32
|
||||
return -1<<(s-1) <= x && x <= 1<<(s-1)-1
|
||||
case Int64:
|
||||
case Int64, UntypedInt:
|
||||
return true
|
||||
case Uint, Uintptr:
|
||||
if s := uint(conf.sizeof(Typ[as])) * 8; s < 64 {
|
||||
if s := uint(conf.sizeof(typ)) * 8; s < 64 {
|
||||
return 0 <= x && x <= int64(1)<<s-1
|
||||
}
|
||||
return 0 <= x
|
||||
@ -227,20 +229,28 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c
|
||||
return 0 <= x && x <= 1<<s-1
|
||||
case Uint64:
|
||||
return 0 <= x
|
||||
case Float32, Float64, Complex64, Complex128,
|
||||
UntypedInt, UntypedFloat, UntypedComplex:
|
||||
return true
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
}
|
||||
|
||||
n := constant.BitLen(x)
|
||||
switch as {
|
||||
// x does not fit into int64
|
||||
switch n := constant.BitLen(x); typ.kind {
|
||||
case Uint, Uintptr:
|
||||
var s = uint(conf.sizeof(Typ[as])) * 8
|
||||
var s = uint(conf.sizeof(typ)) * 8
|
||||
return constant.Sign(x) >= 0 && n <= int(s)
|
||||
case Uint64:
|
||||
return constant.Sign(x) >= 0 && n <= 64
|
||||
case Float32, Complex64:
|
||||
case UntypedInt:
|
||||
return true
|
||||
}
|
||||
|
||||
case isFloat(typ):
|
||||
x := constant.ToFloat(x)
|
||||
if x.Kind() != constant.Float {
|
||||
return false
|
||||
}
|
||||
switch typ.kind {
|
||||
case Float32:
|
||||
if rounded == nil {
|
||||
return fitsFloat32(x)
|
||||
}
|
||||
@ -249,7 +259,7 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c
|
||||
*rounded = r
|
||||
return true
|
||||
}
|
||||
case Float64, Complex128:
|
||||
case Float64:
|
||||
if rounded == nil {
|
||||
return fitsFloat64(x)
|
||||
}
|
||||
@ -258,36 +268,18 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c
|
||||
*rounded = r
|
||||
return true
|
||||
}
|
||||
case UntypedInt, UntypedFloat, UntypedComplex:
|
||||
case UntypedFloat:
|
||||
return true
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
|
||||
case constant.Float:
|
||||
switch as {
|
||||
case Float32, Complex64:
|
||||
if rounded == nil {
|
||||
return fitsFloat32(x)
|
||||
}
|
||||
r := roundFloat32(x)
|
||||
if r != nil {
|
||||
*rounded = r
|
||||
return true
|
||||
}
|
||||
case Float64, Complex128:
|
||||
if rounded == nil {
|
||||
return fitsFloat64(x)
|
||||
}
|
||||
r := roundFloat64(x)
|
||||
if r != nil {
|
||||
*rounded = r
|
||||
return true
|
||||
}
|
||||
case UntypedFloat, UntypedComplex:
|
||||
return true
|
||||
case isComplex(typ):
|
||||
x := constant.ToComplex(x)
|
||||
if x.Kind() != constant.Complex {
|
||||
return false
|
||||
}
|
||||
|
||||
case constant.Complex:
|
||||
switch as {
|
||||
switch typ.kind {
|
||||
case Complex64:
|
||||
if rounded == nil {
|
||||
return fitsFloat32(constant.Real(x)) && fitsFloat32(constant.Imag(x))
|
||||
@ -310,13 +302,15 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c
|
||||
}
|
||||
case UntypedComplex:
|
||||
return true
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
|
||||
case constant.String:
|
||||
return as == String || as == UntypedString
|
||||
case isString(typ):
|
||||
return x.Kind() == constant.String
|
||||
|
||||
default:
|
||||
unreachable()
|
||||
case isBoolean(typ):
|
||||
return x.Kind() == constant.Bool
|
||||
}
|
||||
|
||||
return false
|
||||
@ -325,7 +319,7 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c
|
||||
// representable checks that a constant operand is representable in the given basic type.
|
||||
func (check *Checker) representable(x *operand, typ *Basic) {
|
||||
assert(x.mode == constant_)
|
||||
if !representableConst(x.val, check.conf, typ.kind, &x.val) {
|
||||
if !representableConst(x.val, check.conf, typ, &x.val) {
|
||||
var msg string
|
||||
if isNumeric(x.typ) && isNumeric(typ) {
|
||||
// numeric conversion : error msg
|
||||
@ -498,8 +492,6 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
|
||||
return
|
||||
}
|
||||
// expression value may have been rounded - update if needed
|
||||
// TODO(gri) A floating-point value may silently underflow to
|
||||
// zero. If it was negative, the sign is lost. See issue 6898.
|
||||
check.updateExprVal(x.expr, x.val)
|
||||
} else {
|
||||
// Non-constant untyped values may appear as the
|
||||
@ -621,9 +613,16 @@ func (check *Checker) comparison(x, y *operand, op token.Token) {
|
||||
func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
|
||||
untypedx := isUntyped(x.typ)
|
||||
|
||||
// The lhs must be of integer type or be representable
|
||||
// as an integer; otherwise the shift has no chance.
|
||||
if !x.isInteger() {
|
||||
var xval constant.Value
|
||||
if x.mode == constant_ {
|
||||
xval = constant.ToInt(x.val)
|
||||
}
|
||||
|
||||
if isInteger(x.typ) || untypedx && xval != nil && xval.Kind() == constant.Int {
|
||||
// The lhs is of integer type or an untyped constant representable
|
||||
// as an integer. Nothing to do.
|
||||
} else {
|
||||
// shift has no chance
|
||||
check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
|
||||
x.mode = invalid
|
||||
return
|
||||
@ -633,7 +632,7 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
|
||||
// integer type or be an untyped constant that can be converted to
|
||||
// unsigned integer type."
|
||||
switch {
|
||||
case isInteger(y.typ) && isUnsigned(y.typ):
|
||||
case isUnsigned(y.typ):
|
||||
// nothing to do
|
||||
case isUntyped(y.typ):
|
||||
check.convertUntyped(y, Typ[UntypedInt])
|
||||
@ -650,14 +649,15 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
|
||||
if x.mode == constant_ {
|
||||
if y.mode == constant_ {
|
||||
// rhs must be an integer value
|
||||
if !y.isInteger() {
|
||||
yval := constant.ToInt(y.val)
|
||||
if yval.Kind() != constant.Int {
|
||||
check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y)
|
||||
x.mode = invalid
|
||||
return
|
||||
}
|
||||
// rhs must be within reasonable bounds
|
||||
const stupidShift = 1023 - 1 + 52 // so we can express smallestFloat64
|
||||
s, ok := constant.Uint64Val(y.val)
|
||||
s, ok := constant.Uint64Val(yval)
|
||||
if !ok || s > stupidShift {
|
||||
check.invalidOp(y.pos(), "stupid shift count %s", y)
|
||||
x.mode = invalid
|
||||
@ -670,7 +670,8 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
|
||||
if !isInteger(x.typ) {
|
||||
x.typ = Typ[UntypedInt]
|
||||
}
|
||||
x.val = constant.Shift(x.val, op, uint(s))
|
||||
// x is a constant so xval != nil and it must be of Int kind.
|
||||
x.val = constant.Shift(xval, op, uint(s))
|
||||
// Typed constants must be representable in
|
||||
// their type after each constant operation.
|
||||
if isTyped(x.typ) {
|
||||
@ -802,12 +803,16 @@ func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, o
|
||||
}
|
||||
|
||||
if x.mode == constant_ && y.mode == constant_ {
|
||||
xval := x.val
|
||||
yval := y.val
|
||||
typ := x.typ.Underlying().(*Basic)
|
||||
// force integer division of integer operands
|
||||
if op == token.QUO && isInteger(typ) {
|
||||
xval = constant.ToInt(xval)
|
||||
yval = constant.ToInt(yval)
|
||||
op = token.QUO_ASSIGN
|
||||
}
|
||||
x.val = constant.BinaryOp(x.val, op, y.val)
|
||||
x.val = constant.BinaryOp(xval, op, yval)
|
||||
// Typed constants must be representable in
|
||||
// their type after each constant operation.
|
||||
if isTyped(typ) {
|
||||
@ -851,7 +856,7 @@ func (check *Checker) index(index ast.Expr, max int64) (i int64, valid bool) {
|
||||
check.invalidArg(x.pos(), "index %s must not be negative", &x)
|
||||
return
|
||||
}
|
||||
i, valid = constant.Int64Val(x.val)
|
||||
i, valid = constant.Int64Val(constant.ToInt(x.val))
|
||||
if !valid || max >= 0 && i >= max {
|
||||
check.errorf(x.pos(), "index %s is out of bounds", &x)
|
||||
return i, false
|
||||
|
@ -166,13 +166,6 @@ 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 := constant.MakeFromLiteral(lit, tok, 0)
|
||||
if val == nil {
|
||||
// TODO(gri) Should we make it an unknown constant instead?
|
||||
x.mode = invalid
|
||||
return
|
||||
}
|
||||
|
||||
var kind BasicKind
|
||||
switch tok {
|
||||
case token.INT:
|
||||
@ -185,11 +178,13 @@ func (x *operand) setConst(tok token.Token, lit string) {
|
||||
kind = UntypedRune
|
||||
case token.STRING:
|
||||
kind = UntypedString
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
|
||||
x.mode = constant_
|
||||
x.typ = Typ[kind]
|
||||
x.val = val
|
||||
x.val = constant.MakeFromLiteral(lit, tok, 0)
|
||||
}
|
||||
|
||||
// isNil reports whether x is the nil value.
|
||||
@ -229,7 +224,7 @@ func (x *operand) assignableTo(conf *Config, T Type, reason *string) bool {
|
||||
return true
|
||||
}
|
||||
if x.mode == constant_ {
|
||||
return representableConst(x.val, conf, t.kind, nil)
|
||||
return representableConst(x.val, conf, t, nil)
|
||||
}
|
||||
// The result of a comparison is an untyped boolean,
|
||||
// but may not be a constant.
|
||||
@ -276,11 +271,3 @@ func (x *operand) assignableTo(conf *Config, T Type, reason *string) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// isInteger reports whether x is value of integer type
|
||||
// or an untyped constant representable as an integer.
|
||||
func (x *operand) isInteger() bool {
|
||||
return x.mode == invalid ||
|
||||
isInteger(x.typ) ||
|
||||
isUntyped(x.typ) && x.mode == constant_ && representableConst(x.val, nil, UntypedInt, nil) // no *Config required for UntypedInt
|
||||
}
|
||||
|
@ -144,14 +144,9 @@ func TestStdFixed(t *testing.T) {
|
||||
|
||||
testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"),
|
||||
"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
|
||||
"bug459.go", // possibly incorrect test - see issue 6703 (pending spec clarification)
|
||||
"issue3924.go", // possibly incorrect test - see issue 6671 (pending spec clarification)
|
||||
"issue6889.go", // gc-specific test
|
||||
"issue7746.go", // large constants - consumes too much memory
|
||||
"issue11326.go", // large constants
|
||||
"issue11326b.go", // large constants
|
||||
"issue11362.go", // canonical import path check
|
||||
"issue13471.go", // large constants - remove once issue 11327 is fixed
|
||||
"issue6889.go", // gc-specific test
|
||||
"issue7746.go", // large constants - consumes too much memory
|
||||
"issue11362.go", // canonical import path check
|
||||
)
|
||||
}
|
||||
|
||||
|
13
src/go/types/testdata/const0.src
vendored
13
src/go/types/testdata/const0.src
vendored
@ -280,3 +280,16 @@ func _() {
|
||||
var y = iota
|
||||
_ = y
|
||||
}
|
||||
|
||||
// constant arithmetic precision and rounding must lead to expected (integer) results
|
||||
var _ = []int64{
|
||||
0.0005 * 1e9,
|
||||
0.001 * 1e9,
|
||||
0.005 * 1e9,
|
||||
0.01 * 1e9,
|
||||
0.05 * 1e9,
|
||||
0.1 * 1e9,
|
||||
0.5 * 1e9,
|
||||
1 * 1e9,
|
||||
5 * 1e9,
|
||||
}
|
||||
|
5
src/go/types/testdata/decls0.src
vendored
5
src/go/types/testdata/decls0.src
vendored
@ -50,7 +50,10 @@ func _() { var init int; _ = init }
|
||||
// invalid array types
|
||||
type (
|
||||
iA0 [... /* ERROR "invalid use of '...'" */ ]byte
|
||||
iA1 [1 /* ERROR "invalid array length" */ <<100]int
|
||||
// The error message below could be better. At the moment
|
||||
// we believe an integer that is too large is not an integer.
|
||||
// But at least we get an error.
|
||||
iA1 [1 /* ERROR "must be integer" */ <<100]int
|
||||
iA2 [- /* ERROR "invalid array length" */ 1]complex128
|
||||
iA3 ["foo" /* ERROR "must be integer" */ ]string
|
||||
iA4 [float64 /* ERROR "must be integer" */ (0)]int
|
||||
|
4
src/go/types/testdata/errors.src
vendored
4
src/go/types/testdata/errors.src
vendored
@ -21,8 +21,8 @@ func f(x int, m map[string]int) {
|
||||
const c2 float32 = 0.5
|
||||
0 /* ERROR "0 \(untyped int constant\) is not used" */
|
||||
c1 /* ERROR "c1 \(untyped int constant 991\) is not used" */
|
||||
c2 /* ERROR "c2 \(constant 1/2 of type float32\) is not used" */
|
||||
c1 /* ERROR "c1 \+ c2 \(constant 1983/2 of type float32\) is not used" */ + c2
|
||||
c2 /* ERROR "c2 \(constant 0.5 of type float32\) is not used" */
|
||||
c1 /* ERROR "c1 \+ c2 \(constant 991.5 of type float32\) is not used" */ + c2
|
||||
|
||||
// variables
|
||||
x /* ERROR "x \(variable of type int\) is not used" */
|
||||
|
@ -373,16 +373,19 @@ func (check *Checker) arrayLength(e ast.Expr) int64 {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
if !x.isInteger() {
|
||||
check.errorf(x.pos(), "array length %s must be integer", &x)
|
||||
return 0
|
||||
if isUntyped(x.typ) || isInteger(x.typ) {
|
||||
if val := constant.ToInt(x.val); val.Kind() == constant.Int {
|
||||
if representableConst(val, check.conf, Typ[Int], nil) {
|
||||
if n, ok := constant.Int64Val(val); ok && n >= 0 {
|
||||
return n
|
||||
}
|
||||
check.errorf(x.pos(), "invalid array length %s", &x)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
n, ok := constant.Int64Val(x.val)
|
||||
if !ok || n < 0 {
|
||||
check.errorf(x.pos(), "invalid array length %s", &x)
|
||||
return 0
|
||||
}
|
||||
return n
|
||||
check.errorf(x.pos(), "array length %s must be integer", &x)
|
||||
return 0
|
||||
}
|
||||
|
||||
func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {
|
||||
|
Loading…
Reference in New Issue
Block a user