From e3ab34248152006cc023dbb5bb4109dccee04ea4 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 5 Mar 2014 10:23:33 -0800 Subject: [PATCH] go.tools/go/types: report correctly rounded constant values Also: - better documentation of exact.Float64Val - minor rearrangement in go/importer (unrelated) LGTM=adonovan R=adonovan CC=golang-codereviews https://golang.org/cl/70200047 --- go/exact/exact.go | 4 +++- go/importer/export.go | 6 +++--- go/importer/import.go | 4 ++-- go/types/api_test.go | 24 ++++++++++++++++++++++-- go/types/conversions.go | 2 +- go/types/expr.go | 12 ++++++++++++ 6 files changed, 43 insertions(+), 9 deletions(-) diff --git a/go/exact/exact.go b/go/exact/exact.go index 05662bf1..f5c3565e 100644 --- a/go/exact/exact.go +++ b/go/exact/exact.go @@ -244,7 +244,9 @@ func Uint64Val(x Value) (uint64, bool) { } // Float64Val returns the nearest Go float64 value of x and whether the result is exact; -// x must be numeric but not Complex, or Unknown. +// x must be numeric but not Complex, or Unknown. For values too small (too close to 0) +// to represent as float64, Float64Val silently underflows to 0. The result sign always +// matches the sign of x, even for 0. // If x is Unknown, the result is (0, false). func Float64Val(x Value) (float64, bool) { switch x := x.(type) { diff --git a/go/importer/export.go b/go/importer/export.go index 75fa04ee..c8f5e1cc 100644 --- a/go/importer/export.go +++ b/go/importer/export.go @@ -137,9 +137,6 @@ func (p *exporter) value(x exact.Value) { tag = trueTag } p.int(tag) - case exact.String: - p.int(stringTag) - p.string(exact.StringVal(x)) case exact.Int: if i, ok := exact.Int64Val(x); ok { p.int(int64Tag) @@ -155,6 +152,9 @@ func (p *exporter) value(x exact.Value) { p.int(complexTag) p.fraction(exact.Real(x)) p.fraction(exact.Imag(x)) + case exact.String: + p.int(stringTag) + p.string(exact.StringVal(x)) default: panic(fmt.Sprintf("unexpected value kind %d", kind)) } diff --git a/go/importer/import.go b/go/importer/import.go index 5e5b320e..d00419ab 100644 --- a/go/importer/import.go +++ b/go/importer/import.go @@ -129,8 +129,6 @@ func (p *importer) value() exact.Value { return exact.MakeBool(false) case trueTag: return exact.MakeBool(true) - case stringTag: - return exact.MakeString(p.string()) case int64Tag: return exact.MakeInt64(p.int64()) case floatTag: @@ -141,6 +139,8 @@ func (p *importer) value() exact.Value { re := p.fraction() im := p.fraction() return exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) + case stringTag: + return exact.MakeString(p.string()) default: panic(fmt.Sprintf("unexpected value kind %d", kind)) } diff --git a/go/types/api_test.go b/go/types/api_test.go index 147282bb..209fc6b1 100644 --- a/go/types/api_test.go +++ b/go/types/api_test.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// TODO(gri) This file needs to be expanded significantly. - package types_test import ( @@ -89,6 +87,28 @@ func TestValuesInfo(t *testing.T) { {`package d1; var _ = []byte(string("foo"))`, `"foo"`, `string`, `"foo"`}, {`package d2; var _ = []byte(string("foo"))`, `string("foo")`, `string`, `"foo"`}, {`package d3; type T []byte; var _ = T("foo")`, `"foo"`, `string`, `"foo"`}, + + {`package e0; 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 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 f0 ; var _ float32 = 1e-200`, `1e-200`, `float32`, `0`}, + {`package f1 ; var _ float32 = -1e-200`, `-1e-200`, `float32`, `0`}, + {`package f2a; 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 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`}, } for _, test := range tests { diff --git a/go/types/conversions.go b/go/types/conversions.go index 6469bc6d..15fb0543 100644 --- a/go/types/conversions.go +++ b/go/types/conversions.go @@ -57,10 +57,10 @@ func (check *checker) conversion(x *operand, T Type) { if isInterface(T) || constArg && !isConstType(T) { final = defaultType(x.typ) } + check.updateExprType(x.expr, final, true) } x.typ = T - check.updateExprType(x.expr, final, true) } func (x *operand) convertibleTo(conf *Config, T Type) bool { diff --git a/go/types/expr.go b/go/types/expr.go index f0e0c0db..83d83192 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -462,6 +462,14 @@ func (check *checker) updateExprType(x ast.Expr, typ Type, final bool) { check.recordTypeAndValue(x, typ, old.val) } +// updateExprVal updates the value of x to val. +func (check *checker) updateExprVal(x ast.Expr, val exact.Value) { + if info, ok := check.untyped[x]; ok { + info.val = val + check.untyped[x] = info + } +} + // convertUntyped attempts to set the type of an untyped value to the target type. func (check *checker) convertUntyped(x *operand, target Type) { if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] { @@ -494,6 +502,10 @@ func (check *checker) convertUntyped(x *operand, target Type) { if x.mode == invalid { 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 // result of comparisons (untyped bool), intermediate