diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index ab08a2669d..4f54f684b8 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -87,6 +87,10 @@ func TestValuesInfo(t *testing.T) { {`package c5a; var _ = string("foo")`, `"foo"`, `string`, `"foo"`}, {`package c5b; var _ = string("foo")`, `string("foo")`, `string`, `"foo"`}, {`package c5c; type T string; var _ = T("foo")`, `T("foo")`, `c5c.T`, `"foo"`}, + {`package c5d; var _ = string(65)`, `65`, `untyped int`, `65`}, + {`package c5e; var _ = string('A')`, `'A'`, `untyped rune`, `65`}, + {`package c5f; type T string; var _ = T('A')`, `'A'`, `untyped rune`, `65`}, + {`package c5g; var s uint; var _ = string(1 << s)`, `1 << s`, `untyped int`, ``}, {`package d0; var _ = []byte("foo")`, `"foo"`, `string`, `"foo"`}, {`package d1; var _ = []byte(string("foo"))`, `"foo"`, `string`, `"foo"`}, @@ -122,7 +126,7 @@ func TestValuesInfo(t *testing.T) { } name := mustTypecheck(t, "ValuesInfo", test.src, &info) - // look for constant expression + // look for expression var expr ast.Expr for e := range info.Types { if ExprString(e) == test.expr { @@ -142,9 +146,15 @@ func TestValuesInfo(t *testing.T) { continue } - // check that value is correct - if got := tv.Value.ExactString(); got != test.val { - t.Errorf("package %s: got value %s; want %s", name, got, test.val) + // if we have a constant, check that value is correct + if tv.Value != nil { + if got := tv.Value.ExactString(); got != test.val { + t.Errorf("package %s: got value %s; want %s", name, got, test.val) + } + } else { + if test.val != "" { + t.Errorf("package %s: no constant found; want %s", name, test.val) + } } } } diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go index 2bf1e2d5e3..81a65838fe 100644 --- a/src/go/types/conversions.go +++ b/src/go/types/conversions.go @@ -46,16 +46,19 @@ func (check *Checker) conversion(x *operand, T Type) { // 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(x.typ) { - final = T + final := T // - For conversions to interfaces, use the argument's default type. // - For conversions of untyped constants to non-constant types, also // use the default type (e.g., []byte("foo") should report string // not []byte as type for the constant "foo"). // - Keep untyped nil for untyped nil arguments. + // - For integer to string conversions, keep the argument type. + // (See also the TODO below.) if IsInterface(T) || constArg && !isConstType(T) { final = Default(x.typ) + } else if isInteger(x.typ) && isString(T) { + final = x.typ } check.updateExprType(x.expr, final, true) } @@ -63,6 +66,16 @@ func (check *Checker) conversion(x *operand, T Type) { x.typ = T } +// TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type +// of x is fully known, but that's not the case for say string(1<> 4 } + +func issue21727() { + var s uint + var a = make([]int, 1<