1
0
mirror of https://github.com/golang/go synced 2024-11-18 17:54:57 -07:00

go.tools/go/types: report correct type for untyped conversion arguments

Fixes golang/go#5849.

R=adonovan
CC=golang-dev
https://golang.org/cl/11007043
This commit is contained in:
Robert Griesemer 2013-07-08 13:51:20 -07:00
parent cc52b8b7f8
commit 1aa0484f4b
4 changed files with 78 additions and 13 deletions

View File

@ -18,6 +18,8 @@ import (
// x is marked as invalid (x.mode == invalid).
//
func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type) {
var final Type // declare before gotos
// all conversions have one argument
if len(conv.Args) != 1 {
check.invalidOp(conv.Pos(), "%s conversion requires exactly one argument", conv)
@ -62,9 +64,20 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type) {
x.mode = value
}
// the conversion argument types are final; for now we just use x.typ
// TODO(gri) Fix this. For untyped constants, the type should be typ.
check.updateExprType(x.expr, x.typ, true)
// The conversion argument types are final. For untyped values the
// conversion provides the type, per the spec: "A constant may be
// given a type explicitly by a constant declaration or conversion,...".
final = x.typ
if isUntyped(final) {
final = typ
// For conversions to interfaces, use the argument type's
// default type instead. Keep untyped nil for untyped nil
// arguments.
if _, ok := typ.Underlying().(*Interface); ok {
final = defaultType(x.typ)
}
}
check.updateExprType(x.expr, final, true)
check.conversions[conv] = true // for cap/len checking
x.expr = conv

View File

@ -7,9 +7,12 @@
package types
import (
"go/ast"
"go/parser"
"strings"
"testing"
"code.google.com/p/go.tools/go/exact"
)
func TestIssue5770(t *testing.T) {
@ -26,3 +29,55 @@ func TestIssue5770(t *testing.T) {
t.Errorf("got: %v; want: %s", err, want)
}
}
func TestIssue5849(t *testing.T) {
src := `
package p
var (
s uint
_ = uint8(8)
_ = uint16(16) << s
_ = uint32(32 << s)
_ = uint64(64 << s + s)
_ = (interface{})("foo")
_ = (interface{})(nil)
)`
f, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
t.Error(err)
return
}
ctxt := Context{
Expr: func(x ast.Expr, typ Type, val exact.Value) {
var want Type
switch x := x.(type) {
case *ast.BasicLit:
switch x.Value {
case `8`:
want = Typ[Uint8]
case `16`:
want = Typ[Uint16]
case `32`:
want = Typ[Uint32]
case `64`:
want = Typ[Uint] // because of "+ s", s is of type uint
case `"foo"`:
want = Typ[String]
}
case *ast.Ident:
if x.Name == "nil" {
want = Typ[UntypedNil]
}
}
if want != nil && !IsIdentical(typ, want) {
t.Errorf("got %s; want %s", typ, want)
}
},
}
_, err = ctxt.Check(f.Name.Name, fset, f)
if err != nil {
t.Error(err)
}
}

View File

@ -247,16 +247,13 @@ func identicalMethods(a, b []*Func) bool {
}
// defaultType returns the default "typed" type for an "untyped" type;
// it returns the incoming type for all other types. If there is no
// corresponding untyped type, the result is Typ[Invalid].
// it returns the incoming type for all other types. The default type
// for untyped nil is untyped nil.
//
func defaultType(typ Type) Type {
if t, ok := typ.(*Basic); ok {
k := Invalid
k := t.kind
switch t.kind {
// case UntypedNil:
// There is no default type for nil. For a good error message,
// catch this case before calling this function.
case UntypedBool:
k = Bool
case UntypedInt:

View File

@ -143,13 +143,13 @@ func shifts6() {
_ = make([]int, 1.0)
_ = make([]int, 1.0<<s)
_ = make([]int, 1.1 /* ERROR "integer" */ <<s)
_ = make([]int, 1.1 /* ERROR "must be integer" */ <<s)
_ = float32(1)
_ = float32(1<<s)
_ = float32(1 /* ERROR "must be integer" */ <<s)
_ = float32(1.0)
_ = float32(1.0 /* ERROR "int" */ <<s)
_ = float32(1.1 /* ERROR "int" */ <<s)
_ = float32(1.0 /* ERROR "must be integer" */ <<s)
_ = float32(1.1 /* ERROR "must be integer" */ <<s)
var b []int
_ = append(b, 1<<s)