From baeabf3b366fad1eae113f8334451906dac61c0d Mon Sep 17 00:00:00 2001 From: Rob Findley Date: Fri, 16 Jul 2021 20:40:58 -0400 Subject: [PATCH] [dev.typeparams] go/types: cleanups around receiver type checks This is a port of CL 333770 to go/types, adjusted for the error reporting API and to not support compiler error messages. An error message changed (to 'invalid receiver type' from just 'invalid receiver'), so a test had to be adjusted. Change-Id: I166e8831d8c9f98ebfb0270fe5221586fc112825 Reviewed-on: https://go-review.googlesource.com/c/go/+/335079 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/signature.go | 34 ++++++++++++------- src/go/types/testdata/examples/methods.go2 | 17 ++++++++++ .../types/testdata/fixedbugs/issue28251.src | 2 +- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/go/types/signature.go b/src/go/types/signature.go index 85a735120f..da01ec801a 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -199,30 +199,40 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast // spec: "The receiver type must be of the form T or *T where T is a type name." // (ignore invalid types - error was reported before) - if t := rtyp; t != Typ[Invalid] { + if rtyp != Typ[Invalid] { var err string - if T := asNamed(t); T != nil { + switch T := rtyp.(type) { + case *Named: // spec: "The type denoted by T is called the receiver base type; it must not // be a pointer or interface type and it must be declared in the same package // as the method." if T.obj.pkg != check.pkg { err = "type not defined in this package" } else { - switch u := optype(T).(type) { - case *Basic: - // unsafe.Pointer is treated like a regular pointer - if u.kind == UnsafePointer { - err = "unsafe.Pointer" + // The underlying type of a receiver base type can be a type parameter; + // e.g. for methods with a generic receiver T[P] with type T[P any] P. + underIs(T, func(u Type) bool { + switch u := u.(type) { + case *Basic: + // unsafe.Pointer is treated like a regular pointer + if u.kind == UnsafePointer { + err = "unsafe.Pointer" + return false + } + case *Pointer, *Interface: + err = "pointer or interface type" + return false } - case *Pointer, *Interface: - err = "pointer or interface type" - } + return true + }) } - } else { + case *Basic: err = "basic or unnamed type" + default: + check.errorf(recv, _InvalidRecv, "invalid receiver type %s", recv.typ) } if err != "" { - check.errorf(recv, _InvalidRecv, "invalid receiver %s (%s)", recv.typ, err) + check.errorf(recv, _InvalidRecv, "invalid receiver type %s (%s)", recv.typ, err) // ok to continue } } diff --git a/src/go/types/testdata/examples/methods.go2 b/src/go/types/testdata/examples/methods.go2 index 76c6539e1b..4e87041e54 100644 --- a/src/go/types/testdata/examples/methods.go2 +++ b/src/go/types/testdata/examples/methods.go2 @@ -6,6 +6,8 @@ package p +import "unsafe" + // Parameterized types may have methods. type T1[A any] struct{ a A } @@ -94,3 +96,18 @@ func (_ T2[_, _, _]) _() int { return 42 } type T0 struct{} func (T0) _() {} func (T1[A]) _() {} + +// A generic receiver type may constrain its type parameter such +// that it must be a pointer type. Such receiver types are not +// permitted. +type T3a[P interface{ ~int | ~string | ~float64 }] P + +func (T3a[_]) m() {} // this is ok + +type T3b[P interface{ ~unsafe.Pointer }] P + +func (T3b /* ERROR invalid receiver */ [_]) m() {} + +type T3c[P interface{ *int | *string }] P + +func (T3c /* ERROR invalid receiver */ [_]) m() {} diff --git a/src/go/types/testdata/fixedbugs/issue28251.src b/src/go/types/testdata/fixedbugs/issue28251.src index cd79e0e8b5..ef5e61df47 100644 --- a/src/go/types/testdata/fixedbugs/issue28251.src +++ b/src/go/types/testdata/fixedbugs/issue28251.src @@ -60,6 +60,6 @@ type ( T11 = T ) -func (T9 /* ERROR invalid receiver \*\*T */ ) m9() {} +func (T9 /* ERROR invalid receiver type \*\*T */ ) m9() {} func _() { (T{}).m9 /* ERROR has no field or method m9 */ () } func _() { (&T{}).m9 /* ERROR has no field or method m9 */ () }