diff --git a/go/types/conversions.go b/go/types/conversions.go index 3675c143..6d999df2 100644 --- a/go/types/conversions.go +++ b/go/types/conversions.go @@ -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 diff --git a/go/types/issues_test.go b/go/types/issues_test.go index 21682358..71a8326b 100644 --- a/go/types/issues_test.go +++ b/go/types/issues_test.go @@ -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) + } +} diff --git a/go/types/predicates.go b/go/types/predicates.go index b86f1f42..ffdcb926 100644 --- a/go/types/predicates.go +++ b/go/types/predicates.go @@ -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: diff --git a/go/types/testdata/shifts.src b/go/types/testdata/shifts.src index c6d49e18..7389fc22 100644 --- a/go/types/testdata/shifts.src +++ b/go/types/testdata/shifts.src @@ -143,13 +143,13 @@ func shifts6() { _ = make([]int, 1.0) _ = make([]int, 1.0<