]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: completely rewrite method receiver type checking
authorRobert Griesemer <gri@golang.org>
Wed, 26 Jun 2024 00:31:26 +0000 (17:31 -0700)
committerGopher Robot <gobot@golang.org>
Fri, 26 Jul 2024 20:14:26 +0000 (20:14 +0000)
1) Factor out handling of receiver from Checker.funcType into
   Checker.collectRecv. Analyze the receiver parameter "manually"
   without resorting to calling Checker.collectParams.
   The code is more straight-forward and error handling is simpler
   because constructing the receiver type and variable is all handled
   in one function.

2) Change Checker.collectParams to collect parameter names and
   corresponding parameter variables, but do not declare them.
   Instead return two equal-length slices of parameter names
   and variables for later declaration.

3) Streamline Checker.funcType into a sequence of simple steps.
   By declaring the receiver and parameters after type parameters,
   there is no need for a temporary scope and scope squashing anymore.

4) Simplify Checker.unpackRecv some more: don't strip multiple
   *'s from receiver type expression because we don't typecheck
   that expression as a whole later (we don't use collectParams
   for receiver types anymore). If we have a **T receiver, we
   need to use *T (one * stripped) as receiver base type expression
   so that we can report an error later.

5) Remove Checker.recvTParamMap and associated machinery as it is
   not needed anymore.

6) Remove Scope.Squash/squash as it is not needed anymore.

7) Remove the explicit scope parameter from Checker.collectParams
   as it is not needed anymore.

8) Minor adjustments to tests: in some cases, error positions have
   shifted slightly (because we don't use Checker.collectParams to
   typecheck receivers anymore), and in some cases duplicate errors
   don't appear anymore (resolves TODOs).

Fixes #51343.

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

14 files changed:
src/cmd/compile/internal/types2/check.go
src/cmd/compile/internal/types2/resolver.go
src/cmd/compile/internal/types2/scope.go
src/cmd/compile/internal/types2/signature.go
src/cmd/compile/internal/types2/typexpr.go
src/go/types/check.go
src/go/types/generate_test.go
src/go/types/resolver.go
src/go/types/scope.go
src/go/types/signature.go
src/go/types/typexpr.go
src/internal/types/testdata/check/issues0.go
src/internal/types/testdata/fixedbugs/issue47818.go
src/internal/types/testdata/fixedbugs/issue51339.go

index 91ad474e9df3158b5dc9f2b538b6dff595afa2d0..44274b194bc462d1e9e810a59bc4c83b9a82142a 100644 (file)
@@ -139,14 +139,13 @@ type Checker struct {
        // information collected during type-checking of a set of package files
        // (initialized by Files, valid only for the duration of check.Files;
        // maps and lists are allocated on demand)
-       files         []*syntax.File              // list of package files
-       versions      map[*syntax.PosBase]string  // maps files to version strings (each file has an entry); shared with Info.FileVersions if present
-       imports       []*PkgName                  // list of imported packages
-       dotImportMap  map[dotImportKey]*PkgName   // maps dot-imported objects to the package they were dot-imported through
-       recvTParamMap map[*syntax.Name]*TypeParam // maps blank receiver type parameters to their type
-       brokenAliases map[*TypeName]bool          // set of aliases with broken (not yet determined) types
-       unionTypeSets map[*Union]*_TypeSet        // computed type sets for union types
-       mono          monoGraph                   // graph for detecting non-monomorphizable instantiation loops
+       files         []*syntax.File             // list of package files
+       versions      map[*syntax.PosBase]string // maps files to version strings (each file has an entry); shared with Info.FileVersions if present
+       imports       []*PkgName                 // list of imported packages
+       dotImportMap  map[dotImportKey]*PkgName  // maps dot-imported objects to the package they were dot-imported through
+       brokenAliases map[*TypeName]bool         // set of aliases with broken (not yet determined) types
+       unionTypeSets map[*Union]*_TypeSet       // computed type sets for union types
+       mono          monoGraph                  // graph for detecting non-monomorphizable instantiation loops
 
        firstErr error                    // first error encountered
        methods  map[*TypeName][]*Func    // maps package scope type names to associated non-blank (non-interface) methods
@@ -474,7 +473,6 @@ func (check *Checker) checkFiles(files []*syntax.File) {
        check.dotImportMap = nil
        check.pkgPathMap = nil
        check.seenPkgMap = nil
-       check.recvTParamMap = nil
        check.brokenAliases = nil
        check.unionTypeSets = nil
        check.ctxt = nil
index c47672fa1ea397548d82309d533afee7c26b2d6c..b381b541b75785a06588b21e133cd6ff9bb2db92 100644 (file)
@@ -517,27 +517,10 @@ func (check *Checker) collectObjects() {
 // Note that base may not be a *syntax.Name for erroneous programs.
 func (check *Checker) unpackRecv(rtyp syntax.Expr, unpackParams bool) (ptr bool, base syntax.Expr, tparams []*syntax.Name) {
        // unpack receiver type
-       // This accepts invalid receivers such as ***T and does not
-       // work for other invalid receivers, but we don't care. The
-       // validity of receiver expressions is checked elsewhere.
-       base = rtyp
-L:
-       for {
-               switch t := base.(type) {
-               case *syntax.ParenExpr:
-                       base = t.X
-               // case *ast.StarExpr:
-               //      ptr = true
-               //      base = t.X
-               case *syntax.Operation:
-                       if t.Op != syntax.Mul || t.Y != nil {
-                               break
-                       }
-                       ptr = true
-                       base = t.X
-               default:
-                       break L
-               }
+       base = syntax.Unparen(rtyp)
+       if t, _ := base.(*syntax.Operation); t != nil && t.Op == syntax.Mul && t.Y == nil {
+               ptr = true
+               base = syntax.Unparen(t.X)
        }
 
        // unpack type parameters, if any
index f5ad25e81e1c3ef2ddad263e3c83e3d303d99c68..f7a16252f9b6cb8c4bdf0888a37dea0a3c22bf32 100644 (file)
@@ -141,41 +141,6 @@ func (s *Scope) insert(name string, obj Object) {
        s.elems[name] = obj
 }
 
-// Squash merges s with its parent scope p by adding all
-// objects of s to p, adding all children of s to the
-// children of p, and removing s from p's children.
-// The function f is called for each object obj in s which
-// has an object alt in p. s should be discarded after
-// having been squashed.
-func (s *Scope) Squash(err func(obj, alt Object)) {
-       p := s.parent
-       assert(p != nil)
-       for name, obj := range s.elems {
-               obj = resolve(name, obj)
-               obj.setParent(nil)
-               if alt := p.Insert(obj); alt != nil {
-                       err(obj, alt)
-               }
-       }
-
-       j := -1 // index of s in p.children
-       for i, ch := range p.children {
-               if ch == s {
-                       j = i
-                       break
-               }
-       }
-       assert(j >= 0)
-       k := len(p.children) - 1
-       p.children[j] = p.children[k]
-       p.children = p.children[:k]
-
-       p.children = append(p.children, s.children...)
-
-       s.children = nil
-       s.elems = nil
-}
-
 // Pos and End describe the scope's source code extent [pos, end).
 // The results are guaranteed to be valid only if the type-checked
 // AST has complete position information. The extent is undefined
index 5dacd8fa1a32e523b91c28585aaeeb8aae6ce8b0..8d597c9e6cfa71575512208caaa53ff7611240df 100644 (file)
@@ -99,126 +99,140 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
        sig.scope = check.scope
        defer check.closeScope()
 
+       // collect method receiver, if any
+       var recv *Var
+       var rparams *TypeParamList
        if recvPar != nil {
-               // collect generic receiver type parameters, if any
-               // - a receiver type parameter is like any other type parameter, except that it is declared implicitly
-               // - the receiver specification acts as local declaration for its type parameters, which may be blank
-               _, base, rparams := check.unpackRecv(recvPar.Type, true)
-               if len(rparams) > 0 {
-                       // The scope of the type parameter T in "func (r T[T]) f()"
-                       // starts after f, not at "r"; see #52038.
-                       scopePos := ftyp.Pos()
-                       tparams := make([]*TypeParam, len(rparams))
-                       for i, rparam := range rparams {
-                               tparams[i] = check.declareTypeParam(rparam, scopePos)
-                       }
-                       sig.rparams = bindTParams(tparams)
-                       // Blank identifiers don't get declared, so naive type-checking of the
-                       // receiver type expression would fail in Checker.collectParams below,
-                       // when Checker.ident cannot resolve the _ to a type.
-                       //
-                       // Checker.recvTParamMap maps these blank identifiers to their type parameter
-                       // types, so that they may be resolved in Checker.ident when they fail
-                       // lookup in the scope.
-                       for i, p := range rparams {
-                               if p.Value == "_" {
-                                       if check.recvTParamMap == nil {
-                                               check.recvTParamMap = make(map[*syntax.Name]*TypeParam)
-                                       }
-                                       check.recvTParamMap[p] = tparams[i]
-                               }
-                       }
-                       // determine receiver type to get its type parameters
-                       // and the respective type parameter bounds
-                       var recvTParams []*TypeParam
-                       if rname := base.(*syntax.Name); rname != nil {
-                               // recv should be a Named type (otherwise an error is reported elsewhere)
-                               // Also: Don't report an error via genericType since it will be reported
-                               //       again when we type-check the signature.
-                               // TODO(gri) maybe the receiver should be marked as invalid instead?
-                               if recv := asNamed(check.genericType(rname, nil)); recv != nil {
-                                       recvTParams = recv.TypeParams().list()
-                               }
-                       }
-                       // provide type parameter bounds
-                       if len(tparams) == len(recvTParams) {
-                               smap := makeRenameMap(recvTParams, tparams)
-                               for i, tpar := range tparams {
-                                       recvTPar := recvTParams[i]
-                                       check.mono.recordCanon(tpar, recvTPar)
-                                       // recvTPar.bound is (possibly) parameterized in the context of the
-                                       // receiver type declaration. Substitute parameters for the current
-                                       // context.
-                                       tpar.bound = check.subst(tpar.obj.pos, recvTPar.bound, smap, nil, check.context())
-                               }
-                       } else if len(tparams) < len(recvTParams) {
-                               // Reporting an error here is a stop-gap measure to avoid crashes in the
-                               // compiler when a type parameter/argument cannot be inferred later. It
-                               // may lead to follow-on errors (see issues go.dev/issue/51339, go.dev/issue/51343).
-                               // TODO(gri) find a better solution
-                               got := measure(len(tparams), "type parameter")
-                               check.errorf(recvPar, BadRecv, "got %s, but receiver base type declares %d", got, len(recvTParams))
-                       }
-               }
+               // all type parameters' scopes start after the method name
+               scopePos := ftyp.Pos()
+               recv, rparams = check.collectRecv(recvPar, scopePos)
        }
 
+       // collect and declare function type parameters
        if tparams != nil {
                // The parser will complain about invalid type parameters for methods.
                check.collectTypeParams(&sig.tparams, tparams)
        }
 
-       // Use a temporary scope for all parameter declarations and then
-       // squash that scope into the parent scope (and report any
-       // redeclarations at that time).
-       //
-       // TODO(adonovan): now that each declaration has the correct
-       // scopePos, there should be no need for scope squashing.
-       // Audit to ensure all lookups honor scopePos and simplify.
-       scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)")
-       scopePos := syntax.EndPos(ftyp) // all parameters' scopes start after the signature
+       // collect ordinary and result parameters
+       pnames, params, variadic := check.collectParams(ftyp.ParamList, true)
+       rnames, results, _ := check.collectParams(ftyp.ResultList, false)
 
-       // collect and typecheck receiver, incoming parameters, and results
-       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 validateRecv
-               default:
-                       // error reported by parser
-                       check.error(recvList[len(recvList)-1].Pos(), InvalidRecv, "method has multiple receivers")
-                       fallthrough // continue with first receiver
-               case 1:
-                       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)
+       // declare named receiver, ordinary, and result parameters
+       scopePos := syntax.EndPos(ftyp) // all parameter's scopes start after the signature
+       if recv != nil && recv.name != "" {
+               check.declare(check.scope, recvPar.Name, recv, scopePos)
        }
+       check.declareParams(pnames, params, scopePos)
+       check.declareParams(rnames, results, scopePos)
 
-       params, variadic := check.collectParams(scope, ftyp.ParamList, true, scopePos)
-       results, _ := check.collectParams(scope, ftyp.ResultList, false, scopePos)
+       sig.recv = recv
+       sig.rparams = rparams
        sig.params = NewTuple(params...)
        sig.results = NewTuple(results...)
        sig.variadic = variadic
+}
+
+// collectRecv extracts the method receiver and its type parameters (if any) from rparam.
+// It declares the type parameters (but not the receiver) in the the current scope, and
+// returns the receiver variable and its type parameter list (if any).
+func (check *Checker) collectRecv(rparam *syntax.Field, scopePos syntax.Pos) (recv *Var, recvTParamsList *TypeParamList) {
+       // Unpack the receiver parameter which is of the form
+       //
+       //      "(" [rname] ["*"] rbase ["[" rtparams "]"] ")"
+       //
+       // The receiver name rname, the pointer indirection, and the
+       // receiver type parameters rtparams may not be present.
+       rptr, rbase, rtparams := check.unpackRecv(rparam.Type, true)
+
+       // Determine the receiver base type.
+       var recvType Type = Typ[Invalid]
+       if rtparams == nil {
+               // If there are no type parameters, we can simply typecheck rbase.
+               // If rbase denotes a generic type, varType will complain. Further
+               // receiver constraints will be checked later, with validRecv.
+               recvType = check.varType(rbase)
+       } else {
+               // If there are type parameters, rbase must denote a generic base type.
+               var baseType *Named
+               var cause string
+               if t := check.genericType(rbase, &cause); cause == "" {
+                       baseType = asNamed(t)
+               } else {
+                       check.errorf(rbase, InvalidRecv, "%s", cause)
+                       // ok to continue
+               }
 
-       scope.Squash(func(obj, alt Object) {
-               err := check.newError(DuplicateDecl)
-               err.addf(obj, "%s redeclared in this block", obj.Name())
-               err.addAltDecl(alt)
-               err.report()
-       })
+               // Collect the type parameters declared by the receiver (see also
+               // Checker.collectTypeParams). The scope of the type parameter T in
+               // "func (r T[T]) f() {}" starts after f, not at r, so we declare it
+               // after typechecking rbase (see go.dev/issue/52038).
+               recvTParams := make([]*TypeParam, len(rtparams))
+               for i, rparam := range rtparams {
+                       recvTParams[i] = check.declareTypeParam(rparam, scopePos)
+               }
+               recvTParamsList = bindTParams(recvTParams)
+
+               // Get the type parameter bounds from the receiver base type
+               // and set them for the respective (local) receiver type parameters.
+               if baseType != nil {
+                       baseTParams := baseType.TypeParams().list()
+                       if len(recvTParams) == len(baseTParams) {
+                               smap := makeRenameMap(baseTParams, recvTParams)
+                               for i, recvTPar := range recvTParams {
+                                       baseTPar := baseTParams[i]
+                                       check.mono.recordCanon(recvTPar, baseTPar)
+                                       // baseTPar.bound is possibly parameterized by other type parameters
+                                       // defined by the generic base type. Substitute those parameters with
+                                       // the receiver type parameters declared by the current method.
+                                       recvTPar.bound = check.subst(recvTPar.obj.pos, baseTPar.bound, smap, nil, check.context())
+                               }
+                       } else {
+                               got := measure(len(recvTParams), "type parameter")
+                               check.errorf(rbase, BadRecv, "receiver declares %s, but receiver base type declares %d", got, len(baseTParams))
+                       }
+
+                       // The type parameters declared by the receiver also serve as
+                       // type arguments for the receiver type. Instantiate the receiver.
+                       check.verifyVersionf(rbase, go1_18, "type instantiation")
+                       targs := make([]Type, len(recvTParams))
+                       for i, targ := range recvTParams {
+                               targs[i] = targ
+                       }
+                       recvType = check.instance(rparam.Type.Pos(), baseType, targs, nil, check.context())
+                       check.recordInstance(rbase, targs, recvType)
+               }
+       }
+
+       // Reestablish pointerness if needed (but avoid a pointer to an invalid type).
+       if rptr && isValid(recvType) {
+               recvType = NewPointer(recvType)
+       }
+
+       //  Create the receiver parameter.
+       if rname := rparam.Name; rname != nil && rname.Value != "" {
+               // named receiver
+               recv = NewParam(rname.Pos(), check.pkg, rname.Value, recvType)
+               // named receiver is declared by caller
+       } else {
+               // anonymous receiver
+               recv = NewParam(rparam.Pos(), check.pkg, "", recvType)
+               check.recordImplicit(rparam, recv)
+       }
+
+       // Delay validation of receiver type as it may cause premature expansion of types
+       // the receiver type is dependent on (see go.dev/issue/51232, go.dev/issue/51233).
+       check.later(func() {
+               check.validRecv(recv, len(rtparams) != 0)
+       }).describef(recv, "validRecv(%s)", recv)
+
+       return
 }
 
-// collectParams declares the parameters of list in scope and returns the corresponding
-// variable list.
-func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadicOk bool, scopePos syntax.Pos) (params []*Var, variadic bool) {
+// collectParams collects (but does not delare) all parameters of list and returns
+// the list of parameter names, corresponding parameter variables, and whether the
+// parameter list is variadic. Anonymous parameters are recorded with nil names.
+func (check *Checker) collectParams(list []*syntax.Field, variadicOk bool) (names []*syntax.Name, params []*Var, variadic bool) {
        if list == nil {
                return
        }
@@ -253,13 +267,15 @@ func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadic
                                // ok to continue
                        }
                        par := NewParam(field.Name.Pos(), check.pkg, name, typ)
-                       check.declare(scope, field.Name, par, scopePos)
+                       // named parameter is declared by caller
+                       names = append(names, field.Name)
                        params = append(params, par)
                        named = true
                } else {
                        // anonymous parameter
                        par := NewParam(field.Pos(), check.pkg, "", typ)
                        check.recordImplicit(field, par)
+                       names = append(names, nil)
                        params = append(params, par)
                        anonymous = true
                }
@@ -282,6 +298,15 @@ func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadic
        return
 }
 
+// declareParams declares each named parameter in the current scope.
+func (check *Checker) declareParams(names []*syntax.Name, params []*Var, scopePos syntax.Pos) {
+       for i, name := range names {
+               if name != nil && name.Value != "" {
+                       check.declare(check.scope, name, params[i], scopePos)
+               }
+       }
+}
+
 // validRecv verifies that the receiver satisfies its respective spec requirements
 // and reports an error otherwise. If hasTypeParams is set, the receiver declares
 // type parameters.
index 6c121ae05459595e820f6baaeb33770c8f4cad02..3966a21693bc1496f553ef51823848fd37462ba5 100644 (file)
@@ -28,15 +28,7 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *TypeName, wantType
        switch obj {
        case nil:
                if e.Value == "_" {
-                       // Blank identifiers are never declared, but the current identifier may
-                       // be a placeholder for a receiver type parameter. In this case we can
-                       // resolve its type and object from Checker.recvTParamMap.
-                       if tpar := check.recvTParamMap[e]; tpar != nil {
-                               x.mode = typexpr
-                               x.typ = tpar
-                       } else {
-                               check.error(e, InvalidBlank, "cannot use _ as value or type")
-                       }
+                       check.error(e, InvalidBlank, "cannot use _ as value or type")
                } else {
                        check.errorf(e, UndeclaredName, "undefined: %s", e.Value)
                }
index 1a5a41a3bb4b9923310de3f8c2dccbdd7f00e9b9..a4d0ff97d5b0414f7aeda0e10597aac358e33155 100644 (file)
@@ -161,7 +161,6 @@ type Checker struct {
        versions      map[*ast.File]string      // maps files to version strings (each file has an entry); shared with Info.FileVersions if present
        imports       []*PkgName                // list of imported packages
        dotImportMap  map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
-       recvTParamMap map[*ast.Ident]*TypeParam // maps blank receiver type parameters to their type
        brokenAliases map[*TypeName]bool        // set of aliases with broken (not yet determined) types
        unionTypeSets map[*Union]*_TypeSet      // computed type sets for union types
        mono          monoGraph                 // graph for detecting non-monomorphizable instantiation loops
@@ -496,7 +495,6 @@ func (check *Checker) checkFiles(files []*ast.File) {
        check.dotImportMap = nil
        check.pkgPathMap = nil
        check.seenPkgMap = nil
-       check.recvTParamMap = nil
        check.brokenAliases = nil
        check.unionTypeSets = nil
        check.ctxt = nil
index 86b77162961ec8e68bb5c0855c16841d00b9e3ae..79a8e77a75c45b2c7d08224b6c6db9c62222e29a 100644 (file)
@@ -162,7 +162,7 @@ var filemap = map[string]action{
        "package.go":       nil,
        "pointer.go":       nil,
        "predicates.go":    nil,
-       "scope.go":         func(f *ast.File) { fixTokenPos(f); renameIdents(f, "Squash->squash", "InsertLazy->_InsertLazy") },
+       "scope.go":         func(f *ast.File) { fixTokenPos(f); renameIdents(f, "InsertLazy->_InsertLazy") },
        "selection.go":     nil,
        "sizes.go":         func(f *ast.File) { renameIdents(f, "IsSyncAtomicAlign64->_IsSyncAtomicAlign64") },
        "slice.go":         nil,
index 5d6bf7aedac857c8f51399dd746d63d814739236..2953c4fffc86f0a54d86d7efb840ea5c87d8679c 100644 (file)
@@ -507,21 +507,10 @@ func (check *Checker) collectObjects() {
 // Note that base may not be a *ast.Ident for erroneous programs.
 func (check *Checker) unpackRecv(rtyp ast.Expr, unpackParams bool) (ptr bool, base ast.Expr, tparams []*ast.Ident) {
        // unpack receiver type
-       // This accepts invalid receivers such as ***T and does not
-       // work for other invalid receivers, but we don't care. The
-       // validity of receiver expressions is checked elsewhere.
-       base = rtyp
-L:
-       for {
-               switch t := base.(type) {
-               case *ast.ParenExpr:
-                       base = t.X
-               case *ast.StarExpr:
-                       ptr = true
-                       base = t.X
-               default:
-                       break L
-               }
+       base = ast.Unparen(rtyp)
+       if t, _ := base.(*ast.StarExpr); t != nil {
+               ptr = true
+               base = ast.Unparen(t.X)
        }
 
        // unpack type parameters, if any
index 176928eda92a55a6d032276c7a85fe9955fb26e4..b19a36bae198b4ba8e06a1052816efccb7eb1f03 100644 (file)
@@ -144,41 +144,6 @@ func (s *Scope) insert(name string, obj Object) {
        s.elems[name] = obj
 }
 
-// Squash merges s with its parent scope p by adding all
-// objects of s to p, adding all children of s to the
-// children of p, and removing s from p's children.
-// The function f is called for each object obj in s which
-// has an object alt in p. s should be discarded after
-// having been squashed.
-func (s *Scope) squash(err func(obj, alt Object)) {
-       p := s.parent
-       assert(p != nil)
-       for name, obj := range s.elems {
-               obj = resolve(name, obj)
-               obj.setParent(nil)
-               if alt := p.Insert(obj); alt != nil {
-                       err(obj, alt)
-               }
-       }
-
-       j := -1 // index of s in p.children
-       for i, ch := range p.children {
-               if ch == s {
-                       j = i
-                       break
-               }
-       }
-       assert(j >= 0)
-       k := len(p.children) - 1
-       p.children[j] = p.children[k]
-       p.children = p.children[:k]
-
-       p.children = append(p.children, s.children...)
-
-       s.children = nil
-       s.elems = nil
-}
-
 // Pos and End describe the scope's source code extent [pos, end).
 // The results are guaranteed to be valid only if the type-checked
 // AST has complete position information. The extent is undefined
index bcac0da012824c997e481973163e4a74c188d5d9..69266cf4d92ec23768cdbc7c6e130ccdc617d186 100644 (file)
@@ -110,128 +110,158 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
        sig.scope = check.scope
        defer check.closeScope()
 
-       if recvPar != nil && len(recvPar.List) > 0 {
-               // collect generic receiver type parameters, if any
-               // - a receiver type parameter is like any other type parameter, except that it is declared implicitly
-               // - the receiver specification acts as local declaration for its type parameters, which may be blank
-               _, base, rparams := check.unpackRecv(recvPar.List[0].Type, true)
-               if len(rparams) > 0 {
-                       // The scope of the type parameter T in "func (r T[T]) f()"
-                       // starts after f, not at "r"; see #52038.
-                       scopePos := ftyp.Params.Pos()
-                       tparams := check.declareTypeParams(nil, rparams, scopePos)
-                       sig.rparams = bindTParams(tparams)
-                       // Blank identifiers don't get declared, so naive type-checking of the
-                       // receiver type expression would fail in Checker.collectParams below,
-                       // when Checker.ident cannot resolve the _ to a type.
-                       //
-                       // Checker.recvTParamMap maps these blank identifiers to their type parameter
-                       // types, so that they may be resolved in Checker.ident when they fail
-                       // lookup in the scope.
-                       for i, p := range rparams {
-                               if p.Name == "_" {
-                                       if check.recvTParamMap == nil {
-                                               check.recvTParamMap = make(map[*ast.Ident]*TypeParam)
-                                       }
-                                       check.recvTParamMap[p] = tparams[i]
-                               }
-                       }
-                       // determine receiver type to get its type parameters
-                       // and the respective type parameter bounds
-                       var recvTParams []*TypeParam
-                       if rname := base.(*ast.Ident); rname != nil {
-                               // recv should be a Named type (otherwise an error is reported elsewhere)
-                               // Also: Don't report an error via genericType since it will be reported
-                               //       again when we type-check the signature.
-                               // TODO(gri) maybe the receiver should be marked as invalid instead?
-                               if recv := asNamed(check.genericType(rname, nil)); recv != nil {
-                                       recvTParams = recv.TypeParams().list()
-                               }
-                       }
-                       // provide type parameter bounds
-                       if len(tparams) == len(recvTParams) {
-                               smap := makeRenameMap(recvTParams, tparams)
-                               for i, tpar := range tparams {
-                                       recvTPar := recvTParams[i]
-                                       check.mono.recordCanon(tpar, recvTPar)
-                                       // recvTPar.bound is (possibly) parameterized in the context of the
-                                       // receiver type declaration. Substitute parameters for the current
-                                       // context.
-                                       tpar.bound = check.subst(tpar.obj.pos, recvTPar.bound, smap, nil, check.context())
-                               }
-                       } else if len(tparams) < len(recvTParams) {
-                               // Reporting an error here is a stop-gap measure to avoid crashes in the
-                               // compiler when a type parameter/argument cannot be inferred later. It
-                               // may lead to follow-on errors (see issues go.dev/issue/51339, go.dev/issue/51343).
-                               // TODO(gri) find a better solution
-                               got := measure(len(tparams), "type parameter")
-                               check.errorf(recvPar, BadRecv, "got %s, but receiver base type declares %d", got, len(recvTParams))
-                       }
+       // collect method receiver, if any
+       var recv *Var
+       var rparams *TypeParamList
+       if recvPar != nil && recvPar.NumFields() > 0 {
+               // We have at least one receiver; make sure we don't have more than one.
+               if n := len(recvPar.List); n > 1 {
+                       check.error(recvPar.List[n-1], InvalidRecv, "method has multiple receivers")
+                       // continue with first one
                }
+               // all type parameters' scopes start after the method name
+               scopePos := ftyp.Pos()
+               recv, rparams = check.collectRecv(recvPar.List[0], scopePos)
        }
 
+       // collect and declare function type parameters
        if ftyp.TypeParams != nil {
-               check.collectTypeParams(&sig.tparams, ftyp.TypeParams)
                // Always type-check method type parameters but complain that they are not allowed.
                // (A separate check is needed when type-checking interface method signatures because
                // they don't have a receiver specification.)
                if recvPar != nil {
                        check.error(ftyp.TypeParams, InvalidMethodTypeParams, "methods cannot have type parameters")
                }
+               check.collectTypeParams(&sig.tparams, ftyp.TypeParams)
        }
 
-       // Use a temporary scope for all parameter declarations and then
-       // squash that scope into the parent scope (and report any
-       // redeclarations at that time).
-       //
-       // TODO(adonovan): now that each declaration has the correct
-       // scopePos, there should be no need for scope squashing.
-       // Audit to ensure all lookups honor scopePos and simplify.
-       scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)")
-       scopePos := ftyp.End() // all parameters' scopes start after the signature
-
-       // collect and typecheck receiver, incoming parameters, and results
-       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 validateRecv
-               default:
-                       // more than one receiver
-                       check.error(recvList[len(recvList)-1], InvalidRecv, "method has multiple receivers")
-                       fallthrough // continue with first receiver
-               case 1:
-                       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)
+       // collect ordinary and result parameters
+       pnames, params, variadic := check.collectParams(ftyp.Params, true)
+       rnames, results, _ := check.collectParams(ftyp.Results, false)
+
+       // declare named receiver, ordinary, and result parameters
+       scopePos := ftyp.End() // all parameter's scopes start after the signature
+       if recv != nil && recv.name != "" {
+               check.declare(check.scope, recvPar.List[0].Names[0], recv, scopePos)
        }
+       check.declareParams(pnames, params, scopePos)
+       check.declareParams(rnames, results, scopePos)
 
-       params, variadic := check.collectParams(scope, ftyp.Params, true, scopePos)
-       results, _ := check.collectParams(scope, ftyp.Results, false, scopePos)
+       sig.recv = recv
+       sig.rparams = rparams
        sig.params = NewTuple(params...)
        sig.results = NewTuple(results...)
        sig.variadic = variadic
+}
+
+// collectRecv extracts the method receiver and its type parameters (if any) from rparam.
+// It declares the type parameters (but not the receiver) in the the current scope, and
+// returns the receiver variable and its type parameter list (if any).
+func (check *Checker) collectRecv(rparam *ast.Field, scopePos token.Pos) (recv *Var, recvTParamsList *TypeParamList) {
+       // Unpack the receiver parameter which is of the form
+       //
+       //      "(" [rfield] ["*"] rbase ["[" rtparams "]"] ")"
+       //
+       // The receiver name rname, the pointer indirection, and the
+       // receiver type parameters rtparams may not be present.
+       rptr, rbase, rtparams := check.unpackRecv(rparam.Type, true)
+
+       // Determine the receiver base type.
+       var recvType Type = Typ[Invalid]
+       if rtparams == nil {
+               // If there are no type parameters, we can simply typecheck rbase.
+               // If rbase denotes a generic type, varType will complain. Further
+               // receiver constraints will be checked later, with validRecv.
+               recvType = check.varType(rbase)
+       } else {
+               // If there are type parameters, rbase must denote a generic base type.
+               var baseType *Named
+               var cause string
+               if t := check.genericType(rbase, &cause); cause == "" {
+                       baseType = asNamed(t)
+               } else {
+                       check.errorf(rbase, InvalidRecv, "%s", cause)
+                       // ok to continue
+               }
 
-       scope.squash(func(obj, alt Object) {
-               err := check.newError(DuplicateDecl)
-               err.addf(obj, "%s redeclared in this block", obj.Name())
-               err.addAltDecl(alt)
-               err.report()
-       })
+               // Collect the type parameters declared by the receiver (see also
+               // Checker.collectTypeParams). The scope of the type parameter T in
+               // "func (r T[T]) f() {}" starts after f, not at r, so we declare it
+               // after typechecking rbase (see go.dev/issue/52038).
+               recvTParams := check.declareTypeParams(nil, rtparams, scopePos)
+               recvTParamsList = bindTParams(recvTParams)
+
+               // Get the type parameter bounds from the receiver base type
+               // and set them for the respective (local) receiver type parameters.
+               if baseType != nil {
+                       baseTParams := baseType.TypeParams().list()
+                       if len(recvTParams) == len(baseTParams) {
+                               smap := makeRenameMap(baseTParams, recvTParams)
+                               for i, recvTPar := range recvTParams {
+                                       baseTPar := baseTParams[i]
+                                       check.mono.recordCanon(recvTPar, baseTPar)
+                                       // baseTPar.bound is possibly parameterized by other type parameters
+                                       // defined by the generic base type. Substitute those parameters with
+                                       // the receiver type parameters declared by the current method.
+                                       recvTPar.bound = check.subst(recvTPar.obj.pos, baseTPar.bound, smap, nil, check.context())
+                               }
+                       } else {
+                               got := measure(len(recvTParams), "type parameter")
+                               check.errorf(rbase, BadRecv, "receiver declares %s, but receiver base type declares %d", got, len(baseTParams))
+                       }
+
+                       // The type parameters declared by the receiver also serve as
+                       // type arguments for the receiver type. Instantiate the receiver.
+                       check.verifyVersionf(rbase, go1_18, "type instantiation")
+                       targs := make([]Type, len(recvTParams))
+                       for i, targ := range recvTParams {
+                               targs[i] = targ
+                       }
+                       recvType = check.instance(rparam.Type.Pos(), baseType, targs, nil, check.context())
+                       check.recordInstance(rbase, targs, recvType)
+               }
+       }
+
+       // Reestablish pointerness if needed (but avoid a pointer to an invalid type).
+       if rptr && isValid(recvType) {
+               recvType = NewPointer(recvType)
+       }
+
+       // Make sure we have no more than one receiver name.
+       var rname *ast.Ident
+       if n := len(rparam.Names); n >= 1 {
+               if n > 1 {
+                       check.error(rparam.Names[n-1], InvalidRecv, "method has multiple receivers")
+               }
+               rname = rparam.Names[0]
+       }
+
+       //  Create the receiver parameter.
+       if rname != nil && rname.Name != "" {
+               // named receiver
+               recv = NewParam(rname.Pos(), check.pkg, rname.Name, recvType)
+               // In this case, the receiver is declared by the caller
+               // because it must be declared after any type parameters
+               // (otherwise it might shadow one of them).
+       } else {
+               // anonymous receiver
+               recv = NewParam(rparam.Pos(), check.pkg, "", recvType)
+               check.recordImplicit(rparam, recv)
+       }
+
+       // Delay validation of receiver type as it may cause premature expansion of types
+       // the receiver type is dependent on (see go.dev/issue/51232, go.dev/issue/51233).
+       check.later(func() {
+               check.validRecv(recv, len(rtparams) != 0)
+       }).describef(recv, "validRecv(%s)", recv)
+
+       return
 }
 
-// collectParams declares the parameters of list in scope and returns the corresponding
-// variable list.
-func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool, scopePos token.Pos) (params []*Var, variadic bool) {
+// collectParams collects (but does not delare) all parameters of list and returns
+// the list of parameter names, corresponding parameter variables, and whether the
+// parameter list is variadic. Anonymous parameters are recorded with nil names.
+func (check *Checker) collectParams(list *ast.FieldList, variadicOk bool) (names []*ast.Ident, params []*Var, variadic bool) {
        if list == nil {
                return
        }
@@ -259,7 +289,8 @@ func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
                                        // ok to continue
                                }
                                par := NewParam(name.Pos(), check.pkg, name.Name, typ)
-                               check.declare(scope, name, par, scopePos)
+                               // named parameter is declared by caller
+                               names = append(names, name)
                                params = append(params, par)
                        }
                        named = true
@@ -267,6 +298,7 @@ func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
                        // anonymous parameter
                        par := NewParam(ftype.Pos(), check.pkg, "", typ)
                        check.recordImplicit(field, par)
+                       names = append(names, nil)
                        params = append(params, par)
                        anonymous = true
                }
@@ -289,6 +321,15 @@ func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
        return
 }
 
+// declareParams declares each named parameter in the current scope.
+func (check *Checker) declareParams(names []*ast.Ident, params []*Var, scopePos token.Pos) {
+       for i, name := range names {
+               if name != nil && name.Name != "" {
+                       check.declare(check.scope, name, params[i], scopePos)
+               }
+       }
+}
+
 // validRecv verifies that the receiver satisfies its respective spec requirements
 // and reports an error otherwise. If hasTypeParams is set, the receiver declares
 // type parameters.
index b6b6881089de66e9ddc31b6d842867bdba40a100..62d75885a63e4aaff408ed8e270f143ac372e24b 100644 (file)
@@ -29,15 +29,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *TypeName, wantType bo
        switch obj {
        case nil:
                if e.Name == "_" {
-                       // Blank identifiers are never declared, but the current identifier may
-                       // be a placeholder for a receiver type parameter. In this case we can
-                       // resolve its type and object from Checker.recvTParamMap.
-                       if tpar := check.recvTParamMap[e]; tpar != nil {
-                               x.mode = typexpr
-                               x.typ = tpar
-                       } else {
-                               check.error(e, InvalidBlank, "cannot use _ as value or type")
-                       }
+                       check.error(e, InvalidBlank, "cannot use _ as value or type")
                } else {
                        check.errorf(e, UndeclaredName, "undefined: %s", e.Name)
                }
index 3bf4a3144656edb5048aa9a21d65b7af413650ce..d78b65705a9597249e7b41eafccadacbbdfe5705 100644 (file)
@@ -327,7 +327,7 @@ func issue28281c(a, b, c ... /* ERROR "can only use ... with final parameter" */
 func issue28281d(... /* ERROR "can only use ... with final parameter" */ int, int)
 func issue28281e(a, b, c  ... /* ERROR "can only use ... with final parameter" */ int, d int)
 func issue28281f(... /* ERROR "can only use ... with final parameter" */ int, ... /* ERROR "can only use ... with final parameter" */ int, int)
-func (... /* ERROR "can only use ... with final parameter" */ TT) f()
+func (... /* ERROR "invalid use of '...'" */ TT) f()
 func issue28281g() (... /* ERROR "can only use ... with final parameter" */ TT)
 
 // Issue #26234: Make various field/method lookup errors easier to read by matching cmd/compile's output
index 21c85392ab6bfe01c871fdc29ccb5580b216de4e..4750a4fd0404a897f0afe171a43a64e85b39ba77 100644 (file)
@@ -25,7 +25,7 @@ func f[P /* ERROR "type parameter requires go1.18 or later" */ any /* ERROR "pre
        _ = T[ /* ERROR "type instantiation requires go1.18 or later" */ int](struct{}{})
 }
 
-func (T[ /* ERROR "type instantiation requires go1.18 or later" */ P]) g(x int) {
+func (T /* ERROR "type instantiation requires go1.18 or later" */ [P]) g(x int) {
        f[ /* ERROR "function instantiation requires go1.18 or later" */ int](0)     // explicit instantiation
        (f[ /* ERROR "function instantiation requires go1.18 or later" */ int])(0)   // parentheses (different code path)
        f( /* ERROR "implicit function instantiation requires go1.18 or later" */ x) // implicit instantiation
index fd10daa2c24f10d903e71ef1cb293050d721b2f4..933e426715d0578080d9bfc4dae0ba5503fd48ee 100644 (file)
@@ -11,10 +11,10 @@ type T[P any, B *P] struct{}
 
 func (T /* ERROR "cannot use generic type" */) m0() {}
 
-// TODO(rfindley): eliminate the duplicate errors here.
-func ( /* ERROR "got 1 type parameter, but receiver base type declares 2" */ T /* ERROR "not enough type arguments for type" */ [_]) m1() {
+func (T /* ERROR "receiver declares 1 type parameter, but receiver base type declares 2" */ [_]) m1() {
 }
 func (T[_, _]) m2() {}
 
 // TODO(gri) this error is unfortunate (issue #51343)
-func (T /* ERROR "too many type arguments for type" */ [_, _, _]) m3() {}
+func (T /* ERROR "receiver declares 3 type parameters, but receiver base type declares 2" */ [_, _, _]) m3() {
+}