]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: factor out method receiver validation
authorRobert Griesemer <gri@golang.org>
Tue, 18 Jun 2024 18:32:18 +0000 (11:32 -0700)
committerGopher Robot <gobot@golang.org>
Fri, 26 Jul 2024 20:11:50 +0000 (20:11 +0000)
While at it, slightly regroup surounding code for clarity.

For #51343.

Change-Id: Ibb3a58c2ea138afae0be5315d98f698d7633b22a
Reviewed-on: https://go-review.googlesource.com/c/go/+/593455
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/compile/internal/types2/signature.go
src/go/types/signature.go

index 5050212e15dced2d62fd6a948efe7ba5a548b29c..ed4ca1f08df6d987770f6d3aebe37be69aae1435 100644 (file)
@@ -178,15 +178,15 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
        scopePos := syntax.EndPos(ftyp) // all parameters' scopes start after the signature
 
        // collect and typecheck receiver, incoming parameters, and results
-       var recv *Var
        if recvPar != nil {
                // spec: "The receiver is specified via an extra parameter section preceding the
                // method name. That parameter section must declare a single parameter, the receiver."
                recvList, _ := check.collectParams(scope, []*syntax.Field{recvPar}, false, scopePos) // use rewritten receiver type, if any
+               var recv *Var
                switch len(recvList) {
                case 0:
                        // error reported by parser
-                       recv = NewParam(nopos, nil, "", Typ[Invalid]) // use invalid type so it's ignored by check.later code below
+                       recv = NewParam(nopos, nil, "", Typ[Invalid]) // use invalid type so it's ignored by validateRecv
                default:
                        // error reported by parser
                        check.error(recvList[len(recvList)-1].Pos(), InvalidRecv, "method has multiple receivers")
@@ -195,9 +195,18 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
                        recv = recvList[0]
                }
                sig.recv = recv
+               // Delay validation of receiver type as it may cause premature expansion
+               // of types the receiver type is dependent on (see issues go.dev/issue/51232, go.dev/issue/51233).
+               check.later(func() {
+                       check.validRecv(recv, sig.RecvTypeParams() != nil)
+               }).describef(recv, "validRecv(%s)", recv)
        }
+
        params, variadic := check.collectParams(scope, ftyp.ParamList, true, scopePos)
        results, _ := check.collectParams(scope, ftyp.ResultList, false, scopePos)
+       sig.params = NewTuple(params...)
+       sig.results = NewTuple(results...)
+       sig.variadic = variadic
 
        scope.Squash(func(obj, alt Object) {
                err := check.newError(DuplicateDecl)
@@ -205,60 +214,6 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
                err.addAltDecl(alt)
                err.report()
        })
-
-       if recv != nil {
-               // Delay validation of receiver type as it may cause premature expansion
-               // of types the receiver type is dependent on (see issues go.dev/issue/51232, go.dev/issue/51233).
-               check.later(func() {
-                       // spec: "The receiver type must be of the form T or *T where T is a type name."
-                       rtyp, _ := deref(recv.typ)
-                       atyp := Unalias(rtyp)
-                       if !isValid(atyp) {
-                               return // error was reported before
-                       }
-                       // 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."
-                       switch T := atyp.(type) {
-                       case *Named:
-                               // The receiver type may be an instantiated type referred to
-                               // by an alias (which cannot have receiver parameters for now).
-                               if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
-                                       check.errorf(recv, InvalidRecv, "cannot define new methods on instantiated type %s", rtyp)
-                                       break
-                               }
-                               if T.obj.pkg != check.pkg {
-                                       check.errorf(recv, InvalidRecv, "cannot define new methods on non-local type %s", rtyp)
-                                       break
-                               }
-                               var cause string
-                               switch u := T.under().(type) {
-                               case *Basic:
-                                       // unsafe.Pointer is treated like a regular pointer
-                                       if u.kind == UnsafePointer {
-                                               cause = "unsafe.Pointer"
-                                       }
-                               case *Pointer, *Interface:
-                                       cause = "pointer or interface type"
-                               case *TypeParam:
-                                       // The underlying type of a receiver base type cannot be a
-                                       // type parameter: "type T[P any] P" is not a valid declaration.
-                                       panic("unreachable")
-                               }
-                               if cause != "" {
-                                       check.errorf(recv, InvalidRecv, "invalid receiver type %s (%s)", rtyp, cause)
-                               }
-                       case *Basic:
-                               check.errorf(recv, InvalidRecv, "cannot define new methods on non-local type %s", rtyp)
-                       default:
-                               check.errorf(recv, InvalidRecv, "invalid receiver type %s", recv.typ)
-                       }
-               }).describef(recv, "validate receiver %s", recv)
-       }
-
-       sig.params = NewTuple(params...)
-       sig.results = NewTuple(results...)
-       sig.variadic = variadic
 }
 
 // collectParams declares the parameters of list in scope and returns the corresponding
@@ -326,3 +281,54 @@ func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadic
 
        return
 }
+
+// validRecv verifies that the receiver satisfies its respective spec requirements
+// and reports an error otherwise. If hasTypeParams is set, the receiver declares
+// type parameters.
+func (check *Checker) validRecv(recv *Var, hasTypeParams bool) {
+       // spec: "The receiver type must be of the form T or *T where T is a type name."
+       rtyp, _ := deref(recv.typ)
+       atyp := Unalias(rtyp)
+       if !isValid(atyp) {
+               return // error was reported before
+       }
+       // 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."
+       switch T := atyp.(type) {
+       case *Named:
+               // The receiver type may be an instantiated type referred to
+               // by an alias (which cannot have receiver parameters for now).
+               // TODO(gri) revisit this logic since alias types can have
+               //           type parameters in 1.24
+               if T.TypeArgs() != nil && !hasTypeParams {
+                       check.errorf(recv, InvalidRecv, "cannot define new methods on instantiated type %s", rtyp)
+                       break
+               }
+               if T.obj.pkg != check.pkg {
+                       check.errorf(recv, InvalidRecv, "cannot define new methods on non-local type %s", rtyp)
+                       break
+               }
+               var cause string
+               switch u := T.under().(type) {
+               case *Basic:
+                       // unsafe.Pointer is treated like a regular pointer
+                       if u.kind == UnsafePointer {
+                               cause = "unsafe.Pointer"
+                       }
+               case *Pointer, *Interface:
+                       cause = "pointer or interface type"
+               case *TypeParam:
+                       // The underlying type of a receiver base type cannot be a
+                       // type parameter: "type T[P any] P" is not a valid declaration.
+                       panic("unreachable")
+               }
+               if cause != "" {
+                       check.errorf(recv, InvalidRecv, "invalid receiver type %s (%s)", rtyp, cause)
+               }
+       case *Basic:
+               check.errorf(recv, InvalidRecv, "cannot define new methods on non-local type %s", rtyp)
+       default:
+               check.errorf(recv, InvalidRecv, "invalid receiver type %s", recv.typ)
+       }
+}
index 9d83dd4fd534030b3c56ff693f63ee10ebb95417..651a333e24f8bd0a2560ef75afec9a0c0e0b2d2f 100644 (file)
@@ -191,15 +191,15 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
        scopePos := ftyp.End() // all parameters' scopes start after the signature
 
        // collect and typecheck receiver, incoming parameters, and results
-       var recv *Var
        if recvPar != nil {
                // spec: "The receiver is specified via an extra parameter section preceding the
                // method name. That parameter section must declare a single parameter, the receiver."
                recvList, _ := check.collectParams(scope, recvPar, false, scopePos) // use rewritten receiver type, if any
+               var recv *Var
                switch len(recvList) {
                case 0:
                        // error reported by resolver
-                       recv = NewParam(nopos, nil, "", Typ[Invalid]) // use invalid type so it's ignored by check.later code below
+                       recv = NewParam(nopos, nil, "", Typ[Invalid]) // use invalid type so it's ignored by validateRecv
                default:
                        // more than one receiver
                        check.error(recvList[len(recvList)-1], InvalidRecv, "method has multiple receivers")
@@ -208,9 +208,18 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
                        recv = recvList[0]
                }
                sig.recv = recv
+               // Delay validation of receiver type as it may cause premature expansion
+               // of types the receiver type is dependent on (see issues go.dev/issue/51232, go.dev/issue/51233).
+               check.later(func() {
+                       check.validRecv(recv, sig.RecvTypeParams() != nil)
+               }).describef(recv, "validRecv(%s)", recv)
        }
+
        params, variadic := check.collectParams(scope, ftyp.Params, true, scopePos)
        results, _ := check.collectParams(scope, ftyp.Results, false, scopePos)
+       sig.params = NewTuple(params...)
+       sig.results = NewTuple(results...)
+       sig.variadic = variadic
 
        scope.squash(func(obj, alt Object) {
                err := check.newError(DuplicateDecl)
@@ -218,60 +227,6 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
                err.addAltDecl(alt)
                err.report()
        })
-
-       if recv != nil {
-               // Delay validation of receiver type as it may cause premature expansion
-               // of types the receiver type is dependent on (see issues go.dev/issue/51232, go.dev/issue/51233).
-               check.later(func() {
-                       // spec: "The receiver type must be of the form T or *T where T is a type name."
-                       rtyp, _ := deref(recv.typ)
-                       atyp := Unalias(rtyp)
-                       if !isValid(atyp) {
-                               return // error was reported before
-                       }
-                       // 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."
-                       switch T := atyp.(type) {
-                       case *Named:
-                               // The receiver type may be an instantiated type referred to
-                               // by an alias (which cannot have receiver parameters for now).
-                               if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
-                                       check.errorf(recv, InvalidRecv, "cannot define new methods on instantiated type %s", rtyp)
-                                       break
-                               }
-                               if T.obj.pkg != check.pkg {
-                                       check.errorf(recv, InvalidRecv, "cannot define new methods on non-local type %s", rtyp)
-                                       break
-                               }
-                               var cause string
-                               switch u := T.under().(type) {
-                               case *Basic:
-                                       // unsafe.Pointer is treated like a regular pointer
-                                       if u.kind == UnsafePointer {
-                                               cause = "unsafe.Pointer"
-                                       }
-                               case *Pointer, *Interface:
-                                       cause = "pointer or interface type"
-                               case *TypeParam:
-                                       // The underlying type of a receiver base type cannot be a
-                                       // type parameter: "type T[P any] P" is not a valid declaration.
-                                       panic("unreachable")
-                               }
-                               if cause != "" {
-                                       check.errorf(recv, InvalidRecv, "invalid receiver type %s (%s)", rtyp, cause)
-                               }
-                       case *Basic:
-                               check.errorf(recv, InvalidRecv, "cannot define new methods on non-local type %s", rtyp)
-                       default:
-                               check.errorf(recv, InvalidRecv, "invalid receiver type %s", recv.typ)
-                       }
-               }).describef(recv, "validate receiver %s", recv)
-       }
-
-       sig.params = NewTuple(params...)
-       sig.results = NewTuple(results...)
-       sig.variadic = variadic
 }
 
 // collectParams declares the parameters of list in scope and returns the corresponding
@@ -333,3 +288,54 @@ func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
 
        return
 }
+
+// validRecv verifies that the receiver satisfies its respective spec requirements
+// and reports an error otherwise. If hasTypeParams is set, the receiver declares
+// type parameters.
+func (check *Checker) validRecv(recv *Var, hasTypeParams bool) {
+       // spec: "The receiver type must be of the form T or *T where T is a type name."
+       rtyp, _ := deref(recv.typ)
+       atyp := Unalias(rtyp)
+       if !isValid(atyp) {
+               return // error was reported before
+       }
+       // 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."
+       switch T := atyp.(type) {
+       case *Named:
+               // The receiver type may be an instantiated type referred to
+               // by an alias (which cannot have receiver parameters for now).
+               // TODO(gri) revisit this logic since alias types can have
+               //           type parameters in 1.24
+               if T.TypeArgs() != nil && !hasTypeParams {
+                       check.errorf(recv, InvalidRecv, "cannot define new methods on instantiated type %s", rtyp)
+                       break
+               }
+               if T.obj.pkg != check.pkg {
+                       check.errorf(recv, InvalidRecv, "cannot define new methods on non-local type %s", rtyp)
+                       break
+               }
+               var cause string
+               switch u := T.under().(type) {
+               case *Basic:
+                       // unsafe.Pointer is treated like a regular pointer
+                       if u.kind == UnsafePointer {
+                               cause = "unsafe.Pointer"
+                       }
+               case *Pointer, *Interface:
+                       cause = "pointer or interface type"
+               case *TypeParam:
+                       // The underlying type of a receiver base type cannot be a
+                       // type parameter: "type T[P any] P" is not a valid declaration.
+                       panic("unreachable")
+               }
+               if cause != "" {
+                       check.errorf(recv, InvalidRecv, "invalid receiver type %s (%s)", rtyp, cause)
+               }
+       case *Basic:
+               check.errorf(recv, InvalidRecv, "cannot define new methods on non-local type %s", rtyp)
+       default:
+               check.errorf(recv, InvalidRecv, "invalid receiver type %s", recv.typ)
+       }
+}