]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types, types2: improve error messages referencing any
authorRobert Findley <rfindley@google.com>
Mon, 15 Nov 2021 14:42:03 +0000 (09:42 -0500)
committerRobert Findley <rfindley@google.com>
Tue, 16 Nov 2021 15:59:43 +0000 (15:59 +0000)
Because any is an a alias, it is naively formatted as interface{} in
error messages. This is a source of verbosity and potential confusion.

We can improve the situation by looking for pointer equality with the
any type. To avoid churn in the importers, do this all at once across
the compiler, go/types, and go/internal/gcimporter. CL 364194 makes the
corresponding change in x/tools/go/internal/gcimporter, allowing the
x/tools trybots to pass.

Fixes #49583

Change-Id: Ib59570937601308483f6273364cc59338f9b8b3b
Reviewed-on: https://go-review.googlesource.com/c/go/+/363974
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
15 files changed:
src/cmd/compile/internal/importer/support.go
src/cmd/compile/internal/noder/types.go
src/cmd/compile/internal/types2/api_test.go
src/cmd/compile/internal/types2/object.go
src/cmd/compile/internal/types2/object_test.go
src/cmd/compile/internal/types2/testdata/fixedbugs/issue48008.go2
src/cmd/compile/internal/types2/typestring.go
src/cmd/compile/internal/types2/universe.go
src/go/internal/gcimporter/support.go
src/go/types/api_test.go
src/go/types/object.go
src/go/types/object_test.go
src/go/types/testdata/fixedbugs/issue48008.go2
src/go/types/typestring.go
src/go/types/universe.go

index 6ceb413601f0e83abd87cf42619b4b4104b87781..9377d997796190b402536a25f849c75717516fe5 100644 (file)
@@ -118,10 +118,14 @@ var predeclared = []types2.Type{
        types2.Typ[types2.Invalid], // only appears in packages with errors
 
        // used internally by gc; never used by this package or in .a files
+       // not to be confused with the universe any
        anyType{},
 
        // comparable
        types2.Universe.Lookup("comparable").Type(),
+
+       // any
+       types2.Universe.Lookup("any").Type(),
 }
 
 type anyType struct{}
index f035e0da9787dc79d0d0a7540d2b2ec64556449b..fa24ab18442011ac568b5e3f800ac6ab7ce3ed6e 100644 (file)
@@ -26,6 +26,8 @@ func (g *irgen) pkg(pkg *types2.Package) *types.Pkg {
        return types.NewPkg(pkg.Path(), pkg.Name())
 }
 
+var universeAny = types2.Universe.Lookup("any").Type()
+
 // typ converts a types2.Type to a types.Type, including caching of previously
 // translated types.
 func (g *irgen) typ(typ types2.Type) *types.Type {
@@ -53,6 +55,12 @@ func (g *irgen) typ(typ types2.Type) *types.Type {
 // constructed part of a recursive type. Should not be called from outside this
 // file (g.typ is the "external" entry point).
 func (g *irgen) typ1(typ types2.Type) *types.Type {
+       // See issue 49583: the type checker has trouble keeping track of aliases,
+       // but for such a common alias as any we can improve things by preserving a
+       // pointer identity that can be checked when formatting type strings.
+       if typ == universeAny {
+               return types.AnyType
+       }
        // Cache type2-to-type mappings. Important so that each defined generic
        // type (instantiated or not) has a single types.Type representation.
        // Also saves a lot of computation and memory by avoiding re-translating
index a59c9a4eee4d856a9e5d50c8934982a681d7e9e2..866ebb8684fff8395a9d8c67d57aab6b0c7298cf 100644 (file)
@@ -319,16 +319,16 @@ func TestTypesInfo(t *testing.T) {
                {brokenPkg + `x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string]invalid type`},
 
                // parameterized functions
-               {`package p0; func f[T any](T) {}; var _ = f[int]`, `f`, `func[T interface{}](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 p2; func f[T any](T) {}; func _() { f(42) }`, `f`, `func(int)`},
                {`package p3; func f[T any](T) {}; func _() { f[int](42) }`, `f[int]`, `func(int)`},
-               {`package p4; func f[T any](T) {}; func _() { f[int](42) }`, `f`, `func[T interface{}](T)`},
+               {`package p4; func f[T any](T) {}; func _() { f[int](42) }`, `f`, `func[T any](T)`},
                {`package p5; 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 interface{}]`},
+               {`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{}]`},
                {brokenPkg + `t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `broken_t4.t[P, Q interface{m()}]`},
index da3e1a2abcecaf8f87ef37c3d2048754d1038ff9..c7c64ca9d5dd209224e30d49ae80c1d077bfa09a 100644 (file)
@@ -528,6 +528,14 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
                }
        }
 
+       // Special handling for any: because WriteType will format 'any' as 'any',
+       // resulting in the object string `type any = any` rather than `type any =
+       // interface{}`. To avoid this, swap in a different empty interface.
+       if obj == universeAny {
+               assert(Identical(typ, &emptyInterface))
+               typ = &emptyInterface
+       }
+
        buf.WriteByte(' ')
        WriteType(buf, typ, qf)
 }
index 93b3dfb44bfef8810a4a64927c23b4bb3f0f2163..8f0303d4b2457851ec9f1392cca40ee335219ca2 100644 (file)
@@ -101,8 +101,8 @@ var testObjects = []struct {
 
        {"type t struct{f int}", "t", "type p.t struct{f int}"},
        {"type t func(int)", "t", "type p.t func(int)"},
-       {"type t[P any] struct{f P}", "t", "type p.t[P interface{}] struct{f P}"},
-       {"type t[P any] struct{f P}", "t.P", "type parameter P interface{}"},
+       {"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}"},
+       {"type t[P any] struct{f P}", "t.P", "type parameter P any"},
        {"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C"},
 
        {"type t = struct{f int}", "t", "type p.t = struct{f int}"},
@@ -111,8 +111,9 @@ var testObjects = []struct {
        {"var v int", "v", "var p.v int"},
 
        {"func f(int) string", "f", "func p.f(int) string"},
-       {"func g[P any](x P){}", "g", "func p.g[P interface{}](x P)"},
+       {"func g[P any](x P){}", "g", "func p.g[P any](x P)"},
        {"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}"},
+       {"", "any", "type any = interface{}"},
 }
 
 func TestObjectString(t *testing.T) {
@@ -131,7 +132,7 @@ func TestObjectString(t *testing.T) {
                        t.Errorf("%s: invalid object path %s", test.src, test.obj)
                        continue
                }
-               obj := pkg.Scope().Lookup(names[0])
+               _, obj := pkg.Scope().LookupParent(names[0], nopos)
                if obj == nil {
                        t.Errorf("%s: %s not found", test.src, names[0])
                        continue
index 5c9726875c5ffbe6525a1f23cf3ed21724fc9f00..6c14c78e4c9cfc3a56bf285c8af9a1f929f8fa27 100644 (file)
@@ -21,7 +21,7 @@ func _(x interface{}) {
        case map[T[int]] string:
        case chan T[int]:
 
-       case T /* ERROR cannot use generic type T\[P interface{}\] without instantiation */ :
+       case T /* ERROR cannot use generic type T\[P any\] without instantiation */ :
        case []T /* ERROR cannot use generic type */ :
        case [10]T /* ERROR cannot use generic type */ :
        case struct{T /* ERROR cannot use generic type */ }:
index f151f47a5ed4ba7291256c49bc82ac3b1ef1382b..0c93a7e6e483e7af6eb8ea37469d91735c20ce4d 100644 (file)
@@ -197,6 +197,13 @@ func (w *typeWriter) typ(typ Type) {
                }
 
        case *Interface:
+               if t == universeAny.Type() && w.ctxt == nil {
+                       // When not hashing, we can try to improve type strings by writing "any"
+                       // for a type that is pointer-identical to universeAny. This logic should
+                       // be deprecated by more robust handling for aliases.
+                       w.string("any")
+                       break
+               }
                if t.implicit {
                        if len(t.methods) == 0 && len(t.embeddeds) == 1 {
                                w.typ(t.embeddeds[0])
index fccab145f853f55e0cd5257c60f20d3216130c99..c16ae3f63eafd0cc90ec46b6e3a6b94b1ff52cec 100644 (file)
@@ -79,7 +79,10 @@ func defPredeclaredTypes() {
        }
 
        // type any = interface{}
-       def(NewTypeName(nopos, nil, "any", &emptyInterface))
+       // Note: don't use &emptyInterface for the type of any. Using a unique
+       // pointer allows us to detect any and format it as "any" rather than
+       // interface{}, which clarifies user-facing error messages significantly.
+       def(NewTypeName(nopos, nil, "any", &Interface{complete: true, tset: &topTypeSet}))
 
        // type error interface{ Error() string }
        {
index 5aef63ec1e4fc2b4227512492765f8ddda167370..965e5d88388af8e83bc2324a3d6bdc083fb962cf 100644 (file)
@@ -134,10 +134,14 @@ var predeclared = []types.Type{
        types.Typ[types.Invalid], // only appears in packages with errors
 
        // used internally by gc; never used by this package or in .a files
+       // not to be confused with the universe any
        anyType{},
 
        // comparable
        types.Universe.Lookup("comparable").Type(),
+
+       // any
+       types.Universe.Lookup("any").Type(),
 }
 
 type anyType struct{}
index c9127f366aaa6cb7be8eeb39adf005eb85ac3baf..d8ca8ad611d20c2315fdde09e72cdcc6be83c198 100644 (file)
@@ -349,16 +349,16 @@ func TestTypesInfo(t *testing.T) {
                {broken + `x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string]invalid type`},
 
                // parameterized functions
-               {genericPkg + `p0; func f[T any](T) {}; var _ = f[int]`, `f`, `func[T interface{}](T)`},
+               {genericPkg + `p0; func f[T any](T) {}; var _ = f[int]`, `f`, `func[T any](T)`},
                {genericPkg + `p1; func f[T any](T) {}; var _ = f[int]`, `f[int]`, `func(int)`},
                {genericPkg + `p2; func f[T any](T) {}; func _() { f(42) }`, `f`, `func(int)`},
                {genericPkg + `p3; func f[T any](T) {}; func _() { f[int](42) }`, `f[int]`, `func(int)`},
-               {genericPkg + `p4; func f[T any](T) {}; func _() { f[int](42) }`, `f`, `func[T interface{}](T)`},
+               {genericPkg + `p4; func f[T any](T) {}; func _() { f[int](42) }`, `f`, `func[T any](T)`},
                {genericPkg + `p5; func f[T any](T) {}; func _() { f(42) }`, `f(42)`, `()`},
 
                // type parameters
                {genericPkg + `t0; type t[] int; var _ t`, `t`, `generic_t0.t`}, // t[] is a syntax error that is ignored in this test in favor of t
-               {genericPkg + `t1; type t[P any] int; var _ t[int]`, `t`, `generic_t1.t[P interface{}]`},
+               {genericPkg + `t1; type t[P any] int; var _ t[int]`, `t`, `generic_t1.t[P any]`},
                {genericPkg + `t2; type t[P interface{}] int; var _ t[int]`, `t`, `generic_t2.t[P interface{}]`},
                {genericPkg + `t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `generic_t3.t[P, Q interface{}]`},
 
index 9309a529c4a1b2e2a19020343ba97f8e8e6ceb07..cf05384a87ed6d3cf2f77119de64e70c36c34442 100644 (file)
@@ -482,6 +482,14 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
                }
        }
 
+       // Special handling for any: because WriteType will format 'any' as 'any',
+       // resulting in the object string `type any = any` rather than `type any =
+       // interface{}`. To avoid this, swap in a different empty interface.
+       if obj == universeAny {
+               assert(Identical(typ, &emptyInterface))
+               typ = &emptyInterface
+       }
+
        buf.WriteByte(' ')
        WriteType(buf, typ, qf)
 }
index 46b92a4006ebdd5222c486ad4e2c5939667f0615..47c7fcd349c3863d43138fb8e22ec4eb1532e4d8 100644 (file)
@@ -104,8 +104,8 @@ var testObjects = []struct {
 
        {"type t struct{f int}", "t", "type p.t struct{f int}"},
        {"type t func(int)", "t", "type p.t func(int)"},
-       {"type t[P any] struct{f P}", "t", "type p.t[P interface{}] struct{f P}"},
-       {"type t[P any] struct{f P}", "t.P", "type parameter P interface{}"},
+       {"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}"},
+       {"type t[P any] struct{f P}", "t.P", "type parameter P any"},
        {"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C"},
 
        {"type t = struct{f int}", "t", "type p.t = struct{f int}"},
@@ -114,8 +114,9 @@ var testObjects = []struct {
        {"var v int", "v", "var p.v int"},
 
        {"func f(int) string", "f", "func p.f(int) string"},
-       {"func g[P any](x P){}", "g", "func p.g[P interface{}](x P)"},
+       {"func g[P any](x P){}", "g", "func p.g[P any](x P)"},
        {"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}"},
+       {"", "any", "type any = interface{}"},
 }
 
 func TestObjectString(t *testing.T) {
@@ -134,7 +135,7 @@ func TestObjectString(t *testing.T) {
                        t.Errorf("%s: invalid object path %s", test.src, test.obj)
                        continue
                }
-               obj := pkg.Scope().Lookup(names[0])
+               _, obj := pkg.Scope().LookupParent(names[0], token.NoPos)
                if obj == nil {
                        t.Errorf("%s: %s not found", test.src, names[0])
                        continue
index 5c9726875c5ffbe6525a1f23cf3ed21724fc9f00..6c14c78e4c9cfc3a56bf285c8af9a1f929f8fa27 100644 (file)
@@ -21,7 +21,7 @@ func _(x interface{}) {
        case map[T[int]] string:
        case chan T[int]:
 
-       case T /* ERROR cannot use generic type T\[P interface{}\] without instantiation */ :
+       case T /* ERROR cannot use generic type T\[P any\] without instantiation */ :
        case []T /* ERROR cannot use generic type */ :
        case [10]T /* ERROR cannot use generic type */ :
        case struct{T /* ERROR cannot use generic type */ }:
index f33175f97ea93214d47f3b528cf87777d80d50f9..cf86f9f72086fee21a669bd135a7cf404fe8d664 100644 (file)
@@ -202,6 +202,13 @@ func (w *typeWriter) typ(typ Type) {
                }
 
        case *Interface:
+               if t == universeAny.Type() && w.ctxt == nil {
+                       // When not hashing, we can try to improve type strings by writing "any"
+                       // for a type that is pointer-identical to universeAny. This logic should
+                       // be deprecated by more robust handling for aliases.
+                       w.string("any")
+                       break
+               }
                if t.implicit {
                        if len(t.methods) == 0 && len(t.embeddeds) == 1 {
                                w.typ(t.embeddeds[0])
index 519cf0b7078705900a30890bd0ce0eba2599b5e0..e30ab12bc356cab050173e7f4dec541a57fe08ec 100644 (file)
@@ -80,7 +80,10 @@ func defPredeclaredTypes() {
        }
 
        // type any = interface{}
-       def(NewTypeName(token.NoPos, nil, "any", &emptyInterface))
+       // Note: don't use &emptyInterface for the type of any. Using a unique
+       // pointer allows us to detect any and format it as "any" rather than
+       // interface{}, which clarifies user-facing error messages significantly.
+       def(NewTypeName(token.NoPos, nil, "any", &Interface{complete: true, tset: &topTypeSet}))
 
        // type error interface{ Error() string }
        {