diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go index 81fc1243e96..9fcbfc469f7 100644 --- a/src/cmd/compile/internal/types2/api_test.go +++ b/src/cmd/compile/internal/types2/api_test.go @@ -182,6 +182,9 @@ func TestValuesInfo(t *testing.T) { } func TestTypesInfo(t *testing.T) { + // Test sources that are not expected to typecheck must start with the broken prefix. + const broken = "package broken_" + var tests = []struct { src string expr string // expression @@ -194,6 +197,39 @@ func TestTypesInfo(t *testing.T) { {`package b3; var x interface{} = 0i`, `0i`, `complex128`}, {`package b4; var x interface{} = "foo"`, `"foo"`, `string`}, + // uses of nil + {`package n0; var _ *int = nil`, `nil`, `untyped nil`}, + {`package n1; var _ func() = nil`, `nil`, `untyped nil`}, + {`package n2; var _ []byte = nil`, `nil`, `untyped nil`}, + {`package n3; var _ map[int]int = nil`, `nil`, `untyped nil`}, + {`package n4; var _ chan int = nil`, `nil`, `untyped nil`}, + {`package n5; var _ interface{} = nil`, `nil`, `untyped nil`}, + {`package n6; import "unsafe"; var _ unsafe.Pointer = nil`, `nil`, `untyped nil`}, + + {`package n10; var (x *int; _ = x == nil)`, `nil`, `untyped nil`}, + {`package n11; var (x func(); _ = x == nil)`, `nil`, `untyped nil`}, + {`package n12; var (x []byte; _ = x == nil)`, `nil`, `untyped nil`}, + {`package n13; var (x map[int]int; _ = x == nil)`, `nil`, `untyped nil`}, + {`package n14; var (x chan int; _ = x == nil)`, `nil`, `untyped nil`}, + {`package n15; var (x interface{}; _ = x == nil)`, `nil`, `untyped nil`}, + {`package n15; import "unsafe"; var (x unsafe.Pointer; _ = x == nil)`, `nil`, `untyped nil`}, + + {`package n20; var _ = (*int)(nil)`, `nil`, `untyped nil`}, + {`package n21; var _ = (func())(nil)`, `nil`, `untyped nil`}, + {`package n22; var _ = ([]byte)(nil)`, `nil`, `untyped nil`}, + {`package n23; var _ = (map[int]int)(nil)`, `nil`, `untyped nil`}, + {`package n24; var _ = (chan int)(nil)`, `nil`, `untyped nil`}, + {`package n25; var _ = (interface{})(nil)`, `nil`, `untyped nil`}, + {`package n26; import "unsafe"; var _ = unsafe.Pointer(nil)`, `nil`, `untyped nil`}, + + {`package n30; func f(*int) { f(nil) }`, `nil`, `untyped nil`}, + {`package n31; func f(func()) { f(nil) }`, `nil`, `untyped nil`}, + {`package n32; func f([]byte) { f(nil) }`, `nil`, `untyped nil`}, + {`package n33; func f(map[int]int) { f(nil) }`, `nil`, `untyped nil`}, + {`package n34; func f(chan int) { f(nil) }`, `nil`, `untyped nil`}, + {`package n35; func f(interface{}) { f(nil) }`, `nil`, `untyped nil`}, + {`package n35; import "unsafe"; func f(unsafe.Pointer) { f(nil) }`, `nil`, `untyped nil`}, + // comma-ok expressions {`package p0; var x interface{}; var _, _ = x.(int)`, `x.(int)`, @@ -275,25 +311,25 @@ func TestTypesInfo(t *testing.T) { }, // tests for broken code that doesn't parse or type-check - {`package x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`}, - {`package x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`}, - {`package x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a, f: b,}}`, `b`, `string`}, - {`package x3; var x = panic("");`, `panic`, `func(interface{})`}, + {broken + `x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`}, + {broken + `x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`}, + {broken + `x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a, f: b,}}`, `b`, `string`}, + {broken + `x3; var x = panic("");`, `panic`, `func(interface{})`}, {`package x4; func _() { panic("") }`, `panic`, `func(interface{})`}, - {`package x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string][-1]int`}, + {broken + `x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string][-1]int`}, // parameterized functions {`package p0; func f[T any](T); var _ = f[int]`, `f`, `func[T₁ any](T₁)`}, {`package p1; func f[T any](T); var _ = f[int]`, `f[int]`, `func(int)`}, - {`package p2; func f[T any](T); var _ = f(42)`, `f`, `func[T₁ any](T₁)`}, - {`package p2; func f[T any](T); var _ = f(42)`, `f(42)`, `()`}, + {`package p2; func f[T any](T); func _() { f(42) }`, `f`, `func[T₁ any](T₁)`}, + {`package p3; func f[T any](T); func _() { f(42) }`, `f(42)`, `()`}, // type parameters {`package t0; type t[] int; var _ t`, `t`, `t0.t`}, // t[] is a syntax error that is ignored in this test in favor of t {`package t1; type t[P any] int; var _ t[int]`, `t`, `t1.t[P₁ any]`}, {`package t2; type t[P interface{}] int; var _ t[int]`, `t`, `t2.t[P₁ interface{}]`}, {`package t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `t3.t[P₁, Q₂ interface{}]`}, - {`package t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `t4.t[P₁, Q₂ interface{m()}]`}, + {broken + `t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `broken_t4.t[P₁, Q₂ interface{m()}]`}, // instantiated types must be sanitized {`package g0; type t[P any] int; var x struct{ f t[int] }; var _ = x.f`, `x.f`, `g0.t[int]`}, @@ -301,7 +337,17 @@ func TestTypesInfo(t *testing.T) { for _, test := range tests { info := Info{Types: make(map[syntax.Expr]TypeAndValue)} - name, _ := mayTypecheck(t, "TypesInfo", test.src, &info) + var name string + if strings.HasPrefix(test.src, broken) { + var err error + name, err = mayTypecheck(t, "TypesInfo", test.src, &info) + if err == nil { + t.Errorf("package %s: expected to fail but passed", name) + continue + } + } else { + name = mustTypecheck(t, "TypesInfo", test.src, &info) + } // look for expression type var typ Type diff --git a/src/cmd/compile/internal/types2/conversions.go b/src/cmd/compile/internal/types2/conversions.go index 0f6a9909353..2a7b54a49c0 100644 --- a/src/cmd/compile/internal/types2/conversions.go +++ b/src/cmd/compile/internal/types2/conversions.go @@ -56,8 +56,8 @@ func (check *Checker) conversion(x *operand, T Type) { // - 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) + if IsInterface(T) || constArg && !isConstType(T) || x.isNil() { + final = Default(x.typ) // default type of untyped nil is untyped nil } else if isInteger(x.typ) && isString(T) { final = x.typ } diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index 736d3bfaccc..7fca5db7d7d 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -667,6 +667,7 @@ func (check *Checker) convertUntypedInternal(x *operand, target Type) { if !hasNil(target) { goto Error } + target = Typ[UntypedNil] default: goto Error }