1
0
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:
Robert Griesemer 2015-12-02 17:41:39 -08:00
parent e568a0180a
commit d0c17461a7
17 changed files with 775 additions and 350 deletions

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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"}},
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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
)
}

View File

@ -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,
}

View File

@ -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

View File

@ -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" */

View File

@ -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) {