Commit d0c17461 authored by Robert Griesemer's avatar Robert Griesemer

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/17360Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent e568a018
...@@ -1983,13 +1983,13 @@ pkg log/syslog (openbsd-amd64-cgo), const LOG_SYSLOG = 40 ...@@ -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_USER = 8
pkg log/syslog (openbsd-amd64-cgo), const LOG_UUCP = 64 pkg log/syslog (openbsd-amd64-cgo), const LOG_UUCP = 64
pkg log/syslog (openbsd-amd64-cgo), const LOG_WARNING = 4 pkg log/syslog (openbsd-amd64-cgo), const LOG_WARNING = 4
pkg math, const E = 271828182845904523536028747135266249775724709369995957496696763/100000000000000000000000000000000000000000000000000000000000000 pkg math, const E = 2.71828 // 271828182845904523536028747135266249775724709369995957496696763/100000000000000000000000000000000000000000000000000000000000000
pkg math, const Ln10 = 23025850929940456840179914546843642076011014886287729760333279/10000000000000000000000000000000000000000000000000000000000000 pkg math, const Ln10 = 2.30259 // 23025850929940456840179914546843642076011014886287729760333279/10000000000000000000000000000000000000000000000000000000000000
pkg math, const Ln2 = 693147180559945309417232121458176568075500134360255254120680009/1000000000000000000000000000000000000000000000000000000000000000 pkg math, const Ln2 = 0.693147 // 693147180559945309417232121458176568075500134360255254120680009/1000000000000000000000000000000000000000000000000000000000000000
pkg math, const Log10E = 10000000000000000000000000000000000000000000000000000000000000/23025850929940456840179914546843642076011014886287729760333279 pkg math, const Log10E = 0.434294 // 10000000000000000000000000000000000000000000000000000000000000/23025850929940456840179914546843642076011014886287729760333279
pkg math, const Log2E = 1000000000000000000000000000000000000000000000000000000000000000/693147180559945309417232121458176568075500134360255254120680009 pkg math, const Log2E = 1.4427 // 1000000000000000000000000000000000000000000000000000000000000000/693147180559945309417232121458176568075500134360255254120680009
pkg math, const MaxFloat32 = 340282346638528859811704183484516925440 pkg math, const MaxFloat32 = 3.40282e+38 // 340282346638528859811704183484516925440
pkg math, const MaxFloat64 = 179769313486231570814527423731704356798100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 pkg math, const MaxFloat64 = 1.79769e+308 // 179769313486231570814527423731704356798100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
pkg math, const MaxInt16 = 32767 pkg math, const MaxInt16 = 32767
pkg math, const MaxInt32 = 2147483647 pkg math, const MaxInt32 = 2147483647
pkg math, const MaxInt64 = 9223372036854775807 pkg math, const MaxInt64 = 9223372036854775807
...@@ -2002,14 +2002,14 @@ pkg math, const MinInt16 = -32768 ...@@ -2002,14 +2002,14 @@ pkg math, const MinInt16 = -32768
pkg math, const MinInt32 = -2147483648 pkg math, const MinInt32 = -2147483648
pkg math, const MinInt64 = -9223372036854775808 pkg math, const MinInt64 = -9223372036854775808
pkg math, const MinInt8 = -128 pkg math, const MinInt8 = -128
pkg math, const Phi = 80901699437494742410229341718281905886015458990288143106772431/50000000000000000000000000000000000000000000000000000000000000 pkg math, const Phi = 1.61803 // 80901699437494742410229341718281905886015458990288143106772431/50000000000000000000000000000000000000000000000000000000000000
pkg math, const Pi = 314159265358979323846264338327950288419716939937510582097494459/100000000000000000000000000000000000000000000000000000000000000 pkg math, const Pi = 3.14159 // 314159265358979323846264338327950288419716939937510582097494459/100000000000000000000000000000000000000000000000000000000000000
pkg math, const SmallestNonzeroFloat32 = 17516230804060213386546619791123951641/12500000000000000000000000000000000000000000000000000000000000000000000000000000000 pkg math, const SmallestNonzeroFloat32 = 1.4013e-45 // 17516230804060213386546619791123951641/12500000000000000000000000000000000000000000000000000000000000000000000000000000000
pkg math, const SmallestNonzeroFloat64 = 4940656458412465441765687928682213723651/1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 pkg math, const SmallestNonzeroFloat64 = 4.94066e-324 // 4940656458412465441765687928682213723651/1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
pkg math, const Sqrt2 = 70710678118654752440084436210484903928483593768847403658833987/50000000000000000000000000000000000000000000000000000000000000 pkg math, const Sqrt2 = 1.41421 // 70710678118654752440084436210484903928483593768847403658833987/50000000000000000000000000000000000000000000000000000000000000
pkg math, const SqrtE = 164872127070012814684865078781416357165377610071014801157507931/100000000000000000000000000000000000000000000000000000000000000 pkg math, const SqrtE = 1.64872 // 164872127070012814684865078781416357165377610071014801157507931/100000000000000000000000000000000000000000000000000000000000000
pkg math, const SqrtPhi = 63600982475703448212621123086874574585780402092004812430832019/50000000000000000000000000000000000000000000000000000000000000 pkg math, const SqrtPhi = 1.27202 // 63600982475703448212621123086874574585780402092004812430832019/50000000000000000000000000000000000000000000000000000000000000
pkg math, const SqrtPi = 177245385090551602729816748334114518279754945612238712821380779/100000000000000000000000000000000000000000000000000000000000000 pkg math, const SqrtPi = 1.77245 // 177245385090551602729816748334114518279754945612238712821380779/100000000000000000000000000000000000000000000000000000000000000
pkg math/big, const MaxBase = 36 pkg math/big, const MaxBase = 36
pkg math/big, method (*Int) MarshalJSON() ([]uint8, error) pkg math/big, method (*Int) MarshalJSON() ([]uint8, error)
pkg math/big, method (*Int) SetUint64(uint64) *Int pkg math/big, method (*Int) SetUint64(uint64) *Int
...@@ -680,7 +680,14 @@ func (w *Walker) emitObj(obj types.Object) { ...@@ -680,7 +680,14 @@ func (w *Walker) emitObj(obj types.Object) {
switch obj := obj.(type) { switch obj := obj.(type) {
case *types.Const: case *types.Const:
w.emitf("const %s %s", obj.Name(), w.typeString(obj.Type())) 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: case *types.Var:
w.emitf("var %s %s", obj.Name(), w.typeString(obj.Type())) w.emitf("var %s %s", obj.Name(), w.typeString(obj.Type()))
case *types.TypeName: case *types.TypeName:
......
...@@ -10,7 +10,7 @@ pkg p1, const ConstChase2 = 11 ...@@ -10,7 +10,7 @@ pkg p1, const ConstChase2 = 11
pkg p1, const ConstChase2 ideal-int pkg p1, const ConstChase2 ideal-int
pkg p1, const ConversionConst = 5 pkg p1, const ConversionConst = 5
pkg p1, const ConversionConst MyInt 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 FloatConst ideal-float
pkg p1, const StrConst = "foo" pkg p1, const StrConst = "foo"
pkg p1, const StrConst ideal-string pkg p1, const StrConst ideal-string
......
This diff is collapsed.
...@@ -176,12 +176,17 @@ func TestOps(t *testing.T) { ...@@ -176,12 +176,17 @@ func TestOps(t *testing.T) {
want := val(a[i+3]) want := val(a[i+3])
if !eql(got, want) { if !eql(got, want) {
t.Errorf("%s: got %s; want %s", test, got, want) t.Errorf("%s: got %s; want %s", test, got, want)
continue
} }
if x0 != nil && !eql(x, x0) { if x0 != nil && !eql(x, x0) {
t.Errorf("%s: x changed to %s", test, x) t.Errorf("%s: x changed to %s", test, x)
continue
} }
if !eql(y, y0) { if !eql(y, y0) {
t.Errorf("%s: y changed to %s", test, y) t.Errorf("%s: y changed to %s", test, y)
continue
} }
} }
} }
...@@ -195,6 +200,68 @@ func eql(x, y Value) bool { ...@@ -195,6 +200,68 @@ func eql(x, y Value) bool {
return Compare(x, token.EQL, y) 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 // Support functions
...@@ -212,6 +279,13 @@ func val(lit string) Value { ...@@ -212,6 +279,13 @@ func val(lit string) Value {
return MakeBool(false) 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 tok := token.INT
switch first, last := lit[0], lit[len(lit)-1]; { switch first, last := lit[0], lit[len(lit)-1]; {
case first == '"' || first == '`': case first == '"' || first == '`':
...@@ -290,32 +364,29 @@ func doOp(x Value, op token.Token, y Value) (z Value) { ...@@ -290,32 +364,29 @@ func doOp(x Value, op token.Token, y Value) (z Value) {
// Other tests // Other tests
var fracTests = []string{ var fracTests = []string{
"0 0 1", "0",
"1 1 1", "1",
"-1 -1 1", "-1",
"1.2 6 5", "1.2",
"-0.991 -991 1000", "-0.991",
"1e100 1e100 1", "2.718281828",
"3.14159265358979323e-10",
"1e100",
"1e1000",
} }
func TestFractions(t *testing.T) { func TestFractions(t *testing.T) {
for _, test := range fracTests { for _, test := range fracTests {
a := strings.Split(test, " ") x := val(test)
if len(a) != 3 { // We don't check the actual numerator and denominator because they
t.Errorf("invalid test case: %s", test) // are unlikely to be 100% correct due to floatVal rounding errors.
continue // Instead, we compute the fraction again and compare the rounded
} // result.
q := BinaryOp(Num(x), token.QUO, Denom(x))
x := val(a[0]) got := q.String()
n := val(a[1]) want := x.String()
d := val(a[2]) if got != want {
t.Errorf("%s: got quotient %s, want %s", x, got, want)
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)
} }
} }
} }
......
...@@ -91,10 +91,10 @@ func runImporterTest(t *testing.T, imp Importer, initmap map[*types.Package]Init ...@@ -91,10 +91,10 @@ func runImporterTest(t *testing.T, imp Importer, initmap map[*types.Package]Init
var importerTests = [...]importerTest{ var importerTests = [...]importerTest{
{pkgpath: "pointer", name: "Int8Ptr", want: "type Int8Ptr *int8"}, {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: "NN", want: "const NN untyped complex", wantval: "(-1 + -1i)"},
{pkgpath: "complexnums", name: "NP", want: "const NP untyped complex", wantval: "(-1/1 + 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/1 + -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/1 + 1/1i)"}, {pkgpath: "complexnums", name: "PP", want: "const PP untyped complex", wantval: "(1 + 1i)"},
// TODO: enable this entry once bug has been tracked down // TODO: enable this entry once bug has been tracked down
//{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}}, //{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}},
} }
......
...@@ -54,14 +54,14 @@ func TestValuesInfo(t *testing.T) { ...@@ -54,14 +54,14 @@ func TestValuesInfo(t *testing.T) {
{`package a1; const _ = 0`, `0`, `untyped int`, `0`}, {`package a1; const _ = 0`, `0`, `untyped int`, `0`},
{`package a2; const _ = 'A'`, `'A'`, `untyped rune`, `65`}, {`package a2; const _ = 'A'`, `'A'`, `untyped rune`, `65`},
{`package a3; const _ = 0.`, `0.`, `untyped float`, `0`}, {`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 a5; const _ = "foo"`, `"foo"`, `untyped string`, `"foo"`},
{`package b0; var _ = false`, `false`, `bool`, `false`}, {`package b0; var _ = false`, `false`, `bool`, `false`},
{`package b1; var _ = 0`, `0`, `int`, `0`}, {`package b1; var _ = 0`, `0`, `int`, `0`},
{`package b2; var _ = 'A'`, `'A'`, `rune`, `65`}, {`package b2; var _ = 'A'`, `'A'`, `rune`, `65`},
{`package b3; var _ = 0.`, `0.`, `float64`, `0`}, {`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 b5; var _ = "foo"`, `"foo"`, `string`, `"foo"`},
{`package c0a; var _ = bool(false)`, `false`, `bool`, `false`}, {`package c0a; var _ = bool(false)`, `false`, `bool`, `false`},
...@@ -80,9 +80,9 @@ func TestValuesInfo(t *testing.T) { ...@@ -80,9 +80,9 @@ func TestValuesInfo(t *testing.T) {
{`package c3b; var _ = float32(0.)`, `float32(0.)`, `float32`, `0`}, {`package c3b; var _ = float32(0.)`, `float32(0.)`, `float32`, `0`},
{`package c3c; type T float32; var _ = T(0.)`, `T(0.)`, `c3c.T`, `0`}, {`package c3c; type T float32; var _ = T(0.)`, `T(0.)`, `c3c.T`, `0`},
{`package c4a; var _ = complex64(0i)`, `0i`, `complex64`, `0`}, {`package c4a; var _ = complex64(0i)`, `0i`, `complex64`, `(0 + 0i)`},
{`package c4b; var _ = complex64(0i)`, `complex64(0i)`, `complex64`, `0`}, {`package c4b; var _ = complex64(0i)`, `complex64(0i)`, `complex64`, `(0 + 0i)`},
{`package c4c; type T complex64; var _ = T(0i)`, `T(0i)`, `c4c.T`, `0`}, {`package c4c; type T complex64; var _ = T(0i)`, `T(0i)`, `c4c.T`, `(0 + 0i)`},
{`package c5a; var _ = string("foo")`, `"foo"`, `string`, `"foo"`}, {`package c5a; var _ = string("foo")`, `"foo"`, `string`, `"foo"`},
{`package c5b; var _ = string("foo")`, `string("foo")`, `string`, `"foo"`}, {`package c5b; var _ = string("foo")`, `string("foo")`, `string`, `"foo"`},
...@@ -97,10 +97,10 @@ func TestValuesInfo(t *testing.T) { ...@@ -97,10 +97,10 @@ func TestValuesInfo(t *testing.T) {
{`package e1; const _ = float32(-1e-200)`, `float32(-1e-200)`, `float32`, `0`}, {`package e1; const _ = float32(-1e-200)`, `float32(-1e-200)`, `float32`, `0`},
{`package e2; const _ = float64( 1e-2000)`, `float64(1e-2000)`, `float64`, `0`}, {`package e2; const _ = float64( 1e-2000)`, `float64(1e-2000)`, `float64`, `0`},
{`package e3; 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 e4; const _ = complex64( 1e-200)`, `complex64(1e-200)`, `complex64`, `(0 + 0i)`},
{`package e5; const _ = complex64(-1e-200)`, `complex64(-1e-200)`, `complex64`, `0`}, {`package e5; const _ = complex64(-1e-200)`, `complex64(-1e-200)`, `complex64`, `(0 + 0i)`},
{`package e6; const _ = complex128( 1e-2000)`, `complex128(1e-2000)`, `complex128`, `0`}, {`package e6; const _ = complex128( 1e-2000)`, `complex128(1e-2000)`, `complex128`, `(0 + 0i)`},
{`package e7; const _ = complex128(-1e-2000)`, `complex128(-1e-2000)`, `complex128`, `0`}, {`package e7; const _ = complex128(-1e-2000)`, `complex128(-1e-2000)`, `complex128`, `(0 + 0i)`},
{`package f0 ; var _ float32 = 1e-200`, `1e-200`, `float32`, `0`}, {`package f0 ; var _ float32 = 1e-200`, `1e-200`, `float32`, `0`},
{`package f1 ; 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) { ...@@ -108,12 +108,12 @@ func TestValuesInfo(t *testing.T) {
{`package f3a; var _ float64 = -1e-2000`, `-1e-2000`, `float64`, `0`}, {`package f3a; var _ float64 = -1e-2000`, `-1e-2000`, `float64`, `0`},
{`package f2b; var _ = 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 f3b; var _ = -1e-2000`, `-1e-2000`, `float64`, `0`},
{`package f4 ; var _ complex64 = 1e-200 `, `1e-200`, `complex64`, `0`}, {`package f4 ; var _ complex64 = 1e-200 `, `1e-200`, `complex64`, `(0 + 0i)`},
{`package f5 ; var _ complex64 = -1e-200 `, `-1e-200`, `complex64`, `0`}, {`package f5 ; var _ complex64 = -1e-200 `, `-1e-200`, `complex64`, `(0 + 0i)`},
{`package f6a; var _ complex128 = 1e-2000i`, `1e-2000i`, `complex128`, `0`}, {`package f6a; var _ complex128 = 1e-2000i`, `1e-2000i`, `complex128`, `(0 + 0i)`},
{`package f7a; var _ complex128 = -1e-2000i`, `-1e-2000i`, `complex128`, `0`}, {`package f7a; var _ complex128 = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`},
{`package f6b; var _ = 1e-2000i`, `1e-2000i`, `complex128`, `0`}, {`package f6b; var _ = 1e-2000i`, `1e-2000i`, `complex128`, `(0 + 0i)`},
{`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `0`}, {`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`},
} }
for _, test := range tests { for _, test := range tests {
...@@ -143,7 +143,7 @@ func TestValuesInfo(t *testing.T) { ...@@ -143,7 +143,7 @@ func TestValuesInfo(t *testing.T) {
} }
// check that value is correct // 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) 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 ...@@ -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 both arguments are constants, the result is a constant
if x.mode == constant_ && y.mode == 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 { } else {
x.mode = value x.mode = value
} }
......
...@@ -18,7 +18,7 @@ func (check *Checker) conversion(x *operand, T Type) { ...@@ -18,7 +18,7 @@ func (check *Checker) conversion(x *operand, T Type) {
case constArg && isConstType(T): case constArg && isConstType(T):
// constant conversion // constant conversion
switch t := T.Underlying().(*Basic); { 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 ok = true
case isInteger(x.typ) && isString(t): case isInteger(x.typ) && isString(t):
codepoint := int64(-1) codepoint := int64(-1)
......
...@@ -48,7 +48,7 @@ func testEval(t *testing.T, fset *token.FileSet, pkg *Package, pos token.Pos, ex ...@@ -48,7 +48,7 @@ func testEval(t *testing.T, fset *token.FileSet, pkg *Package, pos token.Pos, ex
// compare values // compare values
gotStr := "" gotStr := ""
if gotTv.Value != nil { if gotTv.Value != nil {
gotStr = gotTv.Value.String() gotStr = gotTv.Value.ExactString()
} }
if gotStr != valStr { if gotStr != valStr {
t.Errorf("Eval(%q) got value %s, want %s", expr, 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 { ...@@ -180,25 +180,27 @@ func roundFloat64(x constant.Value) constant.Value {
} }
// representableConst reports whether x can be represented as // 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). // provided (only needed for int/uint sizes).
// //
// If rounded != nil, *rounded is set to the rounded value of x for // If rounded != nil, *rounded is set to the rounded value of x for
// representable floating-point values; it is left alone otherwise. // representable floating-point values; it is left alone otherwise.
// It is ok to provide the addressof the first argument for rounded. // 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 { func representableConst(x constant.Value, conf *Config, typ *Basic, rounded *constant.Value) bool {
switch x.Kind() { if x.Kind() == constant.Unknown {
case constant.Unknown: return true // avoid follow-up errors
return true }
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 { if x, ok := constant.Int64Val(x); ok {
switch as { switch typ.kind {
case Int: 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 return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
case Int8: case Int8:
const s = 8 const s = 8
...@@ -209,10 +211,10 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c ...@@ -209,10 +211,10 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c
case Int32: case Int32:
const s = 32 const s = 32
return -1<<(s-1) <= x && x <= 1<<(s-1)-1 return -1<<(s-1) <= x && x <= 1<<(s-1)-1
case Int64: case Int64, UntypedInt:
return true return true
case Uint, Uintptr: 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 && x <= int64(1)<<s-1
} }
return 0 <= x return 0 <= x
...@@ -227,44 +229,28 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c ...@@ -227,44 +229,28 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c
return 0 <= x && x <= 1<<s-1 return 0 <= x && x <= 1<<s-1
case Uint64: case Uint64:
return 0 <= x return 0 <= x
case Float32, Float64, Complex64, Complex128, default:
UntypedInt, UntypedFloat, UntypedComplex: unreachable()
return true
} }
} }
// x does not fit into int64
n := constant.BitLen(x) switch n := constant.BitLen(x); typ.kind {
switch as {
case Uint, Uintptr: 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) return constant.Sign(x) >= 0 && n <= int(s)
case Uint64: case Uint64:
return constant.Sign(x) >= 0 && n <= 64 return constant.Sign(x) >= 0 && n <= 64
case Float32, Complex64: case UntypedInt:
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 UntypedInt, UntypedFloat, UntypedComplex:
return true return true
} }
case constant.Float: case isFloat(typ):
switch as { x := constant.ToFloat(x)
case Float32, Complex64: if x.Kind() != constant.Float {
return false
}
switch typ.kind {
case Float32:
if rounded == nil { if rounded == nil {
return fitsFloat32(x) return fitsFloat32(x)
} }
...@@ -273,7 +259,7 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c ...@@ -273,7 +259,7 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c
*rounded = r *rounded = r
return true return true
} }
case Float64, Complex128: case Float64:
if rounded == nil { if rounded == nil {
return fitsFloat64(x) return fitsFloat64(x)
} }
...@@ -282,12 +268,18 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c ...@@ -282,12 +268,18 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c
*rounded = r *rounded = r
return true return true
} }
case UntypedFloat, UntypedComplex: case UntypedFloat:
return true return true
default:
unreachable()
} }
case constant.Complex: case isComplex(typ):
switch as { x := constant.ToComplex(x)
if x.Kind() != constant.Complex {
return false
}
switch typ.kind {
case Complex64: case Complex64:
if rounded == nil { if rounded == nil {
return fitsFloat32(constant.Real(x)) && fitsFloat32(constant.Imag(x)) return fitsFloat32(constant.Real(x)) && fitsFloat32(constant.Imag(x))
...@@ -310,13 +302,15 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c ...@@ -310,13 +302,15 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c
} }
case UntypedComplex: case UntypedComplex:
return true return true
default:
unreachable()
} }
case constant.String: case isString(typ):
return as == String || as == UntypedString return x.Kind() == constant.String
default: case isBoolean(typ):
unreachable() return x.Kind() == constant.Bool
} }
return false return false
...@@ -325,7 +319,7 @@ func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *c ...@@ -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. // representable checks that a constant operand is representable in the given basic type.
func (check *Checker) representable(x *operand, typ *Basic) { func (check *Checker) representable(x *operand, typ *Basic) {
assert(x.mode == constant_) 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 var msg string
if isNumeric(x.typ) && isNumeric(typ) { if isNumeric(x.typ) && isNumeric(typ) {
// numeric conversion : error msg // numeric conversion : error msg
...@@ -498,8 +492,6 @@ func (check *Checker) convertUntyped(x *operand, target Type) { ...@@ -498,8 +492,6 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
return return
} }
// expression value may have been rounded - update if needed // 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) check.updateExprVal(x.expr, x.val)
} else { } else {
// Non-constant untyped values may appear as the // Non-constant untyped values may appear as the
...@@ -621,9 +613,16 @@ func (check *Checker) comparison(x, y *operand, op token.Token) { ...@@ -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) { func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
untypedx := isUntyped(x.typ) untypedx := isUntyped(x.typ)
// The lhs must be of integer type or be representable var xval constant.Value
// as an integer; otherwise the shift has no chance. if x.mode == constant_ {
if !x.isInteger() { 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) check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
x.mode = invalid x.mode = invalid
return return
...@@ -633,7 +632,7 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) { ...@@ -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 // integer type or be an untyped constant that can be converted to
// unsigned integer type." // unsigned integer type."
switch { switch {
case isInteger(y.typ) && isUnsigned(y.typ): case isUnsigned(y.typ):
// nothing to do // nothing to do
case isUntyped(y.typ): case isUntyped(y.typ):
check.convertUntyped(y, Typ[UntypedInt]) check.convertUntyped(y, Typ[UntypedInt])
...@@ -650,14 +649,15 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) { ...@@ -650,14 +649,15 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
if x.mode == constant_ { if x.mode == constant_ {
if y.mode == constant_ { if y.mode == constant_ {
// rhs must be an integer value // 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) check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y)
x.mode = invalid x.mode = invalid
return return
} }
// rhs must be within reasonable bounds // rhs must be within reasonable bounds
const stupidShift = 1023 - 1 + 52 // so we can express smallestFloat64 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 { if !ok || s > stupidShift {
check.invalidOp(y.pos(), "stupid shift count %s", y) check.invalidOp(y.pos(), "stupid shift count %s", y)
x.mode = invalid x.mode = invalid
...@@ -670,7 +670,8 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) { ...@@ -670,7 +670,8 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
if !isInteger(x.typ) { if !isInteger(x.typ) {
x.typ = Typ[UntypedInt] 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 // Typed constants must be representable in
// their type after each constant operation. // their type after each constant operation.
if isTyped(x.typ) { if isTyped(x.typ) {
...@@ -802,12 +803,16 @@ func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, o ...@@ -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_ { if x.mode == constant_ && y.mode == constant_ {
xval := x.val
yval := y.val
typ := x.typ.Underlying().(*Basic) typ := x.typ.Underlying().(*Basic)
// force integer division of integer operands // force integer division of integer operands
if op == token.QUO && isInteger(typ) { if op == token.QUO && isInteger(typ) {
xval = constant.ToInt(xval)
yval = constant.ToInt(yval)
op = token.QUO_ASSIGN 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 // Typed constants must be representable in
// their type after each constant operation. // their type after each constant operation.
if isTyped(typ) { if isTyped(typ) {
...@@ -851,7 +856,7 @@ func (check *Checker) index(index ast.Expr, max int64) (i int64, valid bool) { ...@@ -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) check.invalidArg(x.pos(), "index %s must not be negative", &x)
return return
} }
i, valid = constant.Int64Val(x.val) i, valid = constant.Int64Val(constant.ToInt(x.val))
if !valid || max >= 0 && i >= max { if !valid || max >= 0 && i >= max {
check.errorf(x.pos(), "index %s is out of bounds", &x) check.errorf(x.pos(), "index %s is out of bounds", &x)
return i, false return i, false
......
...@@ -166,13 +166,6 @@ func (x *operand) String() string { ...@@ -166,13 +166,6 @@ 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 := 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 var kind BasicKind
switch tok { switch tok {
case token.INT: case token.INT:
...@@ -185,11 +178,13 @@ func (x *operand) setConst(tok token.Token, lit string) { ...@@ -185,11 +178,13 @@ func (x *operand) setConst(tok token.Token, lit string) {
kind = UntypedRune kind = UntypedRune
case token.STRING: case token.STRING:
kind = UntypedString kind = UntypedString
default:
unreachable()
} }
x.mode = constant_ x.mode = constant_
x.typ = Typ[kind] x.typ = Typ[kind]
x.val = val x.val = constant.MakeFromLiteral(lit, tok, 0)
} }
// isNil reports whether x is the nil value. // isNil reports whether x is the nil value.
...@@ -229,7 +224,7 @@ func (x *operand) assignableTo(conf *Config, T Type, reason *string) bool { ...@@ -229,7 +224,7 @@ func (x *operand) assignableTo(conf *Config, T Type, reason *string) bool {
return true return true
} }
if x.mode == constant_ { 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, // The result of a comparison is an untyped boolean,
// but may not be a constant. // but may not be a constant.
...@@ -276,11 +271,3 @@ func (x *operand) assignableTo(conf *Config, T Type, reason *string) bool { ...@@ -276,11 +271,3 @@ func (x *operand) assignableTo(conf *Config, T Type, reason *string) bool {
return false 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) { ...@@ -144,14 +144,9 @@ func TestStdFixed(t *testing.T) {
testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"), testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"),
"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore "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 "issue6889.go", // gc-specific test
"issue7746.go", // large constants - consumes too much memory "issue7746.go", // large constants - consumes too much memory
"issue11326.go", // large constants
"issue11326b.go", // large constants
"issue11362.go", // canonical import path check "issue11362.go", // canonical import path check
"issue13471.go", // large constants - remove once issue 11327 is fixed
) )
} }
......
...@@ -280,3 +280,16 @@ func _() { ...@@ -280,3 +280,16 @@ func _() {
var y = iota var y = iota
_ = y _ = 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,
}
...@@ -50,7 +50,10 @@ func _() { var init int; _ = init } ...@@ -50,7 +50,10 @@ func _() { var init int; _ = init }
// invalid array types // invalid array types
type ( type (
iA0 [... /* ERROR "invalid use of '...'" */ ]byte 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 iA2 [- /* ERROR "invalid array length" */ 1]complex128
iA3 ["foo" /* ERROR "must be integer" */ ]string iA3 ["foo" /* ERROR "must be integer" */ ]string
iA4 [float64 /* ERROR "must be integer" */ (0)]int iA4 [float64 /* ERROR "must be integer" */ (0)]int
......
...@@ -21,8 +21,8 @@ func f(x int, m map[string]int) { ...@@ -21,8 +21,8 @@ func f(x int, m map[string]int) {
const c2 float32 = 0.5 const c2 float32 = 0.5
0 /* ERROR "0 \(untyped int constant\) is not used" */ 0 /* ERROR "0 \(untyped int constant\) is not used" */
c1 /* ERROR "c1 \(untyped int constant 991\) 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" */ c2 /* ERROR "c2 \(constant 0.5 of type float32\) is not used" */
c1 /* ERROR "c1 \+ c2 \(constant 1983/2 of type float32\) is not used" */ + c2 c1 /* ERROR "c1 \+ c2 \(constant 991.5 of type float32\) is not used" */ + c2
// variables // variables
x /* ERROR "x \(variable of type int\) is not used" */ x /* ERROR "x \(variable of type int\) is not used" */
......
...@@ -373,16 +373,19 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { ...@@ -373,16 +373,19 @@ func (check *Checker) arrayLength(e ast.Expr) int64 {
} }
return 0 return 0
} }
if !x.isInteger() { if isUntyped(x.typ) || isInteger(x.typ) {
check.errorf(x.pos(), "array length %s must be integer", &x) if val := constant.ToInt(x.val); val.Kind() == constant.Int {
return 0 if representableConst(val, check.conf, Typ[Int], nil) {
if n, ok := constant.Int64Val(val); ok && n >= 0 {
return n
} }
n, ok := constant.Int64Val(x.val)
if !ok || n < 0 {
check.errorf(x.pos(), "invalid array length %s", &x) check.errorf(x.pos(), "invalid array length %s", &x)
return 0 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) { func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {
......
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