From: Robert Griesemer Date: Tue, 18 Jun 2024 18:32:18 +0000 (-0700) Subject: go/types, types2: factor out method receiver validation X-Git-Tag: go1.24rc1~1382 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=1b7285d1ab38212433a78dc32a0421250a35c921;p=gostls13.git go/types, types2: factor out method receiver validation 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 Auto-Submit: Robert Griesemer Reviewed-by: Robert Griesemer LUCI-TryBot-Result: Go LUCI --- diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index 5050212e15..ed4ca1f08d 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -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) + } +} diff --git a/src/go/types/signature.go b/src/go/types/signature.go index 9d83dd4fd5..651a333e24 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -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) + } +}