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
This commit is contained in:
Robert Griesemer 2014-03-05 10:23:33 -08:00
parent fccaf8c467
commit e3ab342481
6 changed files with 43 additions and 9 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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