1
0
mirror of https://github.com/golang/go synced 2024-11-26 12:09:04 -07:00

[dev.typeparams] cmd/compile/internal/types2: consistently report nil type as "untyped nil"

This fixes an inconsistency where the type for nil in code such as

	var x unsafe.Pointer = nil

and in conversions of the form

	T(nil)

(where T is a pointer, function, slice, map, channel, interface, or
unsafe.Pointer) was reported as (converted to) the respective type.
For all other operations that accept a nil value, we don't do this
conversion for nil.

(We never change the type of the untyped nil value, in contrast to
other untyped values where we give the values context-specific types.)

It may still be useful to change this behavior and - consistently -
report a converted nil type like we do for any other type, but for
now this CL simply fixes the existing inconsistency.

Added tests and fixed existing test harness.

Updates #13061.

Change-Id: Ia82832845c096e3cbc4a239ba3d6c8b9a9d274c9
Reviewed-on: https://go-review.googlesource.com/c/go/+/284052
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
Robert Griesemer 2021-01-14 17:34:38 -08:00
parent 82c3f0a358
commit 502198c8dc
3 changed files with 58 additions and 11 deletions

View File

@ -182,6 +182,9 @@ func TestValuesInfo(t *testing.T) {
} }
func TestTypesInfo(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 { var tests = []struct {
src string src string
expr string // expression expr string // expression
@ -194,6 +197,39 @@ func TestTypesInfo(t *testing.T) {
{`package b3; var x interface{} = 0i`, `0i`, `complex128`}, {`package b3; var x interface{} = 0i`, `0i`, `complex128`},
{`package b4; var x interface{} = "foo"`, `"foo"`, `string`}, {`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 // comma-ok expressions
{`package p0; var x interface{}; var _, _ = x.(int)`, {`package p0; var x interface{}; var _, _ = x.(int)`,
`x.(int)`, `x.(int)`,
@ -275,25 +311,25 @@ func TestTypesInfo(t *testing.T) {
}, },
// tests for broken code that doesn't parse or type-check // 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`}, {broken + `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`}, {broken + `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`}, {broken + `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 + `x3; var x = panic("");`, `panic`, `func(interface{})`},
{`package x4; func _() { 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 // parameterized functions
{`package p0; func f[T any](T); var _ = f[int]`, `f`, `func[T₁ any](T₁)`}, {`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 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); func _() { f(42) }`, `f`, `func[T₁ any](T₁)`},
{`package p2; func f[T any](T); var _ = f(42)`, `f(42)`, `()`}, {`package p3; func f[T any](T); func _() { f(42) }`, `f(42)`, `()`},
// type parameters // 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 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 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 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 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 // 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]`}, {`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 { for _, test := range tests {
info := Info{Types: make(map[syntax.Expr]TypeAndValue)} 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 // look for expression type
var typ Type var typ Type

View File

@ -56,8 +56,8 @@ func (check *Checker) conversion(x *operand, T Type) {
// - Keep untyped nil for untyped nil arguments. // - Keep untyped nil for untyped nil arguments.
// - For integer to string conversions, keep the argument type. // - For integer to string conversions, keep the argument type.
// (See also the TODO below.) // (See also the TODO below.)
if IsInterface(T) || constArg && !isConstType(T) { if IsInterface(T) || constArg && !isConstType(T) || x.isNil() {
final = Default(x.typ) final = Default(x.typ) // default type of untyped nil is untyped nil
} else if isInteger(x.typ) && isString(T) { } else if isInteger(x.typ) && isString(T) {
final = x.typ final = x.typ
} }

View File

@ -667,6 +667,7 @@ func (check *Checker) convertUntypedInternal(x *operand, target Type) {
if !hasNil(target) { if !hasNil(target) {
goto Error goto Error
} }
target = Typ[UntypedNil]
default: default:
goto Error goto Error
} }