]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.regabi] go/types: consistently report nil type as "untyped nil"
authorRob Findley <rfindley@google.com>
Thu, 4 Feb 2021 16:44:21 +0000 (11:44 -0500)
committerRobert Findley <rfindley@google.com>
Mon, 8 Feb 2021 23:25:48 +0000 (23:25 +0000)
This is a port of CL 284052 to go/types. The port is not entirely
faithful, as untyped conversion has been refactored in go/types.
Additionally, a comment was added to reference issue #13061 in the
implicitType method.

For #13061

Change-Id: Iec17611f6432c988624023d1d74121ff34eb0c83
Reviewed-on: https://go-review.googlesource.com/c/go/+/289715
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/go/types/api_test.go
src/go/types/conversions.go
src/go/types/expr.go

index 75cebc98261124891abf04af07bf933f868ad6c8..dde451ee3cf76e70a96f892c9e0049a46726bfc0 100644 (file)
@@ -42,7 +42,7 @@ func mustTypecheck(t *testing.T, path, source string, info *Info) string {
        return pkg.Name()
 }
 
-func mayTypecheck(t *testing.T, path, source string, info *Info) string {
+func mayTypecheck(t *testing.T, path, source string, info *Info) (string, error) {
        fset := token.NewFileSet()
        f, err := parser.ParseFile(fset, path, source, 0)
        if f == nil { // ignore errors unless f is nil
@@ -52,8 +52,8 @@ func mayTypecheck(t *testing.T, path, source string, info *Info) string {
                Error:    func(err error) {},
                Importer: importer.Default(),
        }
-       pkg, _ := conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
-       return pkg.Name()
+       pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
+       return pkg.Name(), err
 }
 
 func TestValuesInfo(t *testing.T) {
@@ -175,6 +175,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
@@ -187,6 +190,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)`,
@@ -268,17 +304,27 @@ 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`},
        }
 
        for _, test := range tests {
                info := Info{Types: make(map[ast.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
index 1cab1cc70f0e8bfc4206422136b1f5cb65b9f66a..c634d27aa9499ea402ace950cf6813149b793580 100644 (file)
@@ -55,8 +55,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
                }
index eb2056125a7b71aecf3efb362cd98ec5146a09a1..f7fb0caeddb0e39bb5d0d2538425666cce6b0572 100644 (file)
@@ -579,6 +579,8 @@ func (check *Checker) implicitType(x *operand, target Type) Type {
                        if !hasNil(target) {
                                return nil
                        }
+                       // Preserve the type of nil as UntypedNil: see #13061.
+                       return Typ[UntypedNil]
                default:
                        return nil
                }