// 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
check.dotImportMap = nil
check.pkgPathMap = nil
check.seenPkgMap = nil
- check.recvTParamMap = nil
check.brokenAliases = nil
check.unionTypeSets = nil
check.ctxt = nil
// 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
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
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
}
// 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
}
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.
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)
}
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
check.dotImportMap = nil
check.pkgPathMap = nil
check.seenPkgMap = nil
- check.recvTParamMap = nil
check.brokenAliases = nil
check.unionTypeSets = nil
check.ctxt = nil
"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,
// 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
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
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
}
// 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
// anonymous parameter
par := NewParam(ftype.Pos(), check.pkg, "", typ)
check.recordImplicit(field, par)
+ names = append(names, nil)
params = append(params, par)
anonymous = true
}
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.
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)
}
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
_ = 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
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() {
+}