From a7d3a0e971818c754217829c8bd10b1e167d3481 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 8 Oct 2021 09:49:22 -0700 Subject: [PATCH] cmd/compile/internal/types2: use an identifier map rather than isubst for recv type params This is a port of CL 354643 from go/types to types2 with adjustments: - use of syntax rather than go/ast package as needed - adjustments due to the different code for type parameter declarations - rename of Checker.rparamMap to Checker.recvTParamMap, which seems clearer Change-Id: I5311a0c05a13c6b87ea1422b250b90c3d05c5dce Reviewed-on: https://go-review.googlesource.com/c/go/+/354693 Trust: Robert Griesemer Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/check.go | 8 +- src/cmd/compile/internal/types2/signature.go | 114 ++++--------------- src/cmd/compile/internal/types2/typexpr.go | 10 +- 3 files changed, 36 insertions(+), 96 deletions(-) diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go index 5957518c17..d89ec3d29f 100644 --- a/src/cmd/compile/internal/types2/check.go +++ b/src/cmd/compile/internal/types2/check.go @@ -100,9 +100,10 @@ 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 - imports []*PkgName // list of imported packages - dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through + files []*syntax.File // list of package files + 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 firstErr error // first error encountered methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods @@ -292,6 +293,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) { check.dotImportMap = nil check.pkgPathMap = nil check.seenPkgMap = nil + check.recvTParamMap = nil // TODO(gri) There's more memory we should release at this point. diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index 5ea3a056a0..604d0c9dbd 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -4,10 +4,7 @@ package types2 -import ( - "cmd/compile/internal/syntax" - "fmt" -) +import "cmd/compile/internal/syntax" // ---------------------------------------------------------------------------- // API @@ -105,39 +102,33 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] sig.scope = check.scope defer check.closeScope() - var recvTyp syntax.Expr // rewritten receiver type; valid if != nil 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 _, rname, rparams := check.unpackRecv(recvPar.Type, true) if len(rparams) > 0 { - // Blank identifiers don't get declared and regular type-checking of the instantiated - // parameterized receiver type expression fails in Checker.collectParams of receiver. - // Identify blank type parameters and substitute each with a unique new identifier named - // "n_" (where n is the parameter index) and which cannot conflict with any user-defined - // name. - var smap map[*syntax.Name]*syntax.Name // substitution map from "_" to "!n" identifiers + tparams := make([]*TypeParam, len(rparams)) + for i, rparam := range rparams { + tparams[i] = check.declareTypeParam(rparam) + } + 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 == "_" { - new := *p - new.Value = fmt.Sprintf("%d_", i) - rparams[i] = &new // use n_ identifier instead of _ so it can be looked up - if smap == nil { - smap = make(map[*syntax.Name]*syntax.Name) + tpar := sig.rparams.At(i) + if check.recvTParamMap == nil { + check.recvTParamMap = make(map[*syntax.Name]*TypeParam) } - smap[p] = &new + check.recvTParamMap[p] = tpar } } - if smap != nil { - // blank identifiers were found => use rewritten receiver type - recvTyp = isubst(recvPar.Type, smap) - } - rlist := make([]*TypeParam, len(rparams)) - for i, rparam := range rparams { - rlist[i] = check.declareTypeParam(rparam) - } - sig.rparams = bindTParams(rlist) // determine receiver type to get its type parameters // and the respective type parameter bounds var recvTParams []*TypeParam @@ -186,10 +177,10 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)") var recvList []*Var // TODO(gri) remove the need for making a list here if recvPar != nil { - recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, recvTyp, false) // use rewritten receiver type, if any + recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, false) // use rewritten receiver type, if any } - params, variadic := check.collectParams(scope, ftyp.ParamList, nil, true) - results, _ := check.collectParams(scope, ftyp.ResultList, nil, false) + params, variadic := check.collectParams(scope, ftyp.ParamList, true) + results, _ := check.collectParams(scope, ftyp.ResultList, false) scope.Squash(func(obj, alt Object) { var err error_ err.errorf(obj, "%s redeclared in this block", obj.Name()) @@ -281,8 +272,8 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] } // collectParams declares the parameters of list in scope and returns the corresponding -// variable list. If type0 != nil, it is used instead of the first type in list. -func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, type0 syntax.Expr, variadicOk bool) (params []*Var, variadic bool) { +// variable list. +func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadicOk bool) (params []*Var, variadic bool) { if list == nil { return } @@ -296,9 +287,6 @@ func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, type0 sy // type-check type of grouped fields only once if ftype != prev { prev = ftype - if i == 0 && type0 != nil { - ftype = type0 - } if t, _ := ftype.(*syntax.DotsType); t != nil { ftype = t.Elem if variadicOk && i == len(list)-1 { @@ -348,61 +336,3 @@ func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, type0 sy return } - -// isubst returns an x with identifiers substituted per the substitution map smap. -// isubst only handles the case of (valid) method receiver type expressions correctly. -func isubst(x syntax.Expr, smap map[*syntax.Name]*syntax.Name) syntax.Expr { - switch n := x.(type) { - case *syntax.Name: - if alt := smap[n]; alt != nil { - return alt - } - // case *syntax.StarExpr: - // X := isubst(n.X, smap) - // if X != n.X { - // new := *n - // new.X = X - // return &new - // } - case *syntax.Operation: - if n.Op == syntax.Mul && n.Y == nil { - X := isubst(n.X, smap) - if X != n.X { - new := *n - new.X = X - return &new - } - } - case *syntax.IndexExpr: - Index := isubst(n.Index, smap) - if Index != n.Index { - new := *n - new.Index = Index - return &new - } - case *syntax.ListExpr: - var elems []syntax.Expr - for i, elem := range n.ElemList { - new := isubst(elem, smap) - if new != elem { - if elems == nil { - elems = make([]syntax.Expr, len(n.ElemList)) - copy(elems, n.ElemList) - } - elems[i] = new - } - } - if elems != nil { - new := *n - new.ElemList = elems - return &new - } - case *syntax.ParenExpr: - return isubst(n.X, smap) // no need to keep parentheses - default: - // Other receiver type expressions are invalid. - // It's fine to ignore those here as they will - // be checked elsewhere. - } - return x -} diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index 62cfda825f..746fe78b38 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -28,7 +28,15 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo switch obj { case nil: if e.Value == "_" { - check.error(e, "cannot use _ as value or type") + // 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, "cannot use _ as value or type") + } } else { if check.conf.CompilerErrorMessages { check.errorf(e, "undefined: %s", e.Value) -- 2.48.1