| // Copyright 2021 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // This file will evolve, since we plan to do a mix of stenciling and passing |
| // around dictionaries. |
| |
| package noder |
| |
| import ( |
| "bytes" |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| "cmd/compile/internal/typecheck" |
| "cmd/compile/internal/types" |
| "cmd/internal/src" |
| "fmt" |
| "strings" |
| ) |
| |
| // For catching problems as we add more features |
| // TODO(danscales): remove assertions or replace with base.FatalfAt() |
| func assert(p bool) { |
| if !p { |
| panic("assertion failed") |
| } |
| } |
| |
| // stencil scans functions for instantiated generic function calls and creates the |
| // required instantiations for simple generic functions. It also creates |
| // instantiated methods for all fully-instantiated generic types that have been |
| // encountered already or new ones that are encountered during the stenciling |
| // process. |
| func (g *irgen) stencil() { |
| g.target.Stencils = make(map[*types.Sym]*ir.Func) |
| |
| // Instantiate the methods of instantiated generic types that we have seen so far. |
| g.instantiateMethods() |
| |
| // Don't use range(g.target.Decls) - we also want to process any new instantiated |
| // functions that are created during this loop, in order to handle generic |
| // functions calling other generic functions. |
| for i := 0; i < len(g.target.Decls); i++ { |
| decl := g.target.Decls[i] |
| |
| // Look for function instantiations in bodies of non-generic |
| // functions or in global assignments (ignore global type and |
| // constant declarations). |
| switch decl.Op() { |
| case ir.ODCLFUNC: |
| if decl.Type().HasTParam() { |
| // Skip any generic functions |
| continue |
| } |
| // transformCall() below depends on CurFunc being set. |
| ir.CurFunc = decl.(*ir.Func) |
| |
| case ir.OAS, ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV, ir.OASOP: |
| // These are all the various kinds of global assignments, |
| // whose right-hand-sides might contain a function |
| // instantiation. |
| |
| default: |
| // The other possible ops at the top level are ODCLCONST |
| // and ODCLTYPE, which don't have any function |
| // instantiations. |
| continue |
| } |
| |
| // For all non-generic code, search for any function calls using |
| // generic function instantiations. Then create the needed |
| // instantiated function if it hasn't been created yet, and change |
| // to calling that function directly. |
| modified := false |
| foundFuncInst := false |
| ir.Visit(decl, func(n ir.Node) { |
| if n.Op() == ir.OFUNCINST { |
| // We found a function instantiation that is not |
| // immediately called. |
| foundFuncInst = true |
| } |
| if n.Op() != ir.OCALL || n.(*ir.CallExpr).X.Op() != ir.OFUNCINST { |
| return |
| } |
| // We have found a function call using a generic function |
| // instantiation. |
| call := n.(*ir.CallExpr) |
| inst := call.X.(*ir.InstExpr) |
| st := g.getInstantiationForNode(inst) |
| // Replace the OFUNCINST with a direct reference to the |
| // new stenciled function |
| call.X = st.Nname |
| if inst.X.Op() == ir.OCALLPART { |
| // When we create an instantiation of a method |
| // call, we make it a function. So, move the |
| // receiver to be the first arg of the function |
| // call. |
| withRecv := make([]ir.Node, len(call.Args)+1) |
| dot := inst.X.(*ir.SelectorExpr) |
| withRecv[0] = dot.X |
| copy(withRecv[1:], call.Args) |
| call.Args = withRecv |
| } |
| // Transform the Call now, which changes OCALL |
| // to OCALLFUNC and does typecheckaste/assignconvfn. |
| transformCall(call) |
| modified = true |
| }) |
| |
| // If we found an OFUNCINST without a corresponding call in the |
| // above decl, then traverse the nodes of decl again (with |
| // EditChildren rather than Visit), where we actually change the |
| // OFUNCINST node to an ONAME for the instantiated function. |
| // EditChildren is more expensive than Visit, so we only do this |
| // in the infrequent case of an OFUNCINSt without a corresponding |
| // call. |
| if foundFuncInst { |
| var edit func(ir.Node) ir.Node |
| edit = func(x ir.Node) ir.Node { |
| if x.Op() == ir.OFUNCINST { |
| st := g.getInstantiationForNode(x.(*ir.InstExpr)) |
| return st.Nname |
| } |
| ir.EditChildren(x, edit) |
| return x |
| } |
| edit(decl) |
| } |
| if base.Flag.W > 1 && modified { |
| ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl) |
| } |
| ir.CurFunc = nil |
| // We may have seen new fully-instantiated generic types while |
| // instantiating any needed functions/methods in the above |
| // function. If so, instantiate all the methods of those types |
| // (which will then lead to more function/methods to scan in the loop). |
| g.instantiateMethods() |
| } |
| |
| } |
| |
| // instantiateMethods instantiates all the methods of all fully-instantiated |
| // generic types that have been added to g.instTypeList. |
| func (g *irgen) instantiateMethods() { |
| for i := 0; i < len(g.instTypeList); i++ { |
| typ := g.instTypeList[i] |
| // Get the base generic type by looking up the symbol of the |
| // generic (uninstantiated) name. |
| baseSym := typ.Sym().Pkg.Lookup(genericTypeName(typ.Sym())) |
| baseType := baseSym.Def.(*ir.Name).Type() |
| for j, m := range typ.Methods().Slice() { |
| name := m.Nname.(*ir.Name) |
| targs := make([]ir.Node, len(typ.RParams())) |
| for k, targ := range typ.RParams() { |
| targs[k] = ir.TypeNode(targ) |
| } |
| baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name) |
| name.Func = g.getInstantiation(baseNname, targs, true) |
| } |
| } |
| g.instTypeList = nil |
| |
| } |
| |
| // genericSym returns the name of the base generic type for the type named by |
| // sym. It simply returns the name obtained by removing everything after the |
| // first bracket ("["). |
| func genericTypeName(sym *types.Sym) string { |
| return sym.Name[0:strings.Index(sym.Name, "[")] |
| } |
| |
| // getInstantiationForNode returns the function/method instantiation for a |
| // InstExpr node inst. |
| func (g *irgen) getInstantiationForNode(inst *ir.InstExpr) *ir.Func { |
| if meth, ok := inst.X.(*ir.SelectorExpr); ok { |
| return g.getInstantiation(meth.Selection.Nname.(*ir.Name), inst.Targs, true) |
| } else { |
| return g.getInstantiation(inst.X.(*ir.Name), inst.Targs, false) |
| } |
| } |
| |
| // getInstantiation gets the instantiantion of the function or method nameNode |
| // with the type arguments targs. If the instantiated function is not already |
| // cached, then it calls genericSubst to create the new instantiation. |
| func (g *irgen) getInstantiation(nameNode *ir.Name, targs []ir.Node, isMeth bool) *ir.Func { |
| sym := makeInstName(nameNode.Sym(), targs, isMeth) |
| st := g.target.Stencils[sym] |
| if st == nil { |
| // If instantiation doesn't exist yet, create it and add |
| // to the list of decls. |
| st = g.genericSubst(sym, nameNode, targs, isMeth) |
| g.target.Stencils[sym] = st |
| g.target.Decls = append(g.target.Decls, st) |
| if base.Flag.W > 1 { |
| ir.Dump(fmt.Sprintf("\nstenciled %v", st), st) |
| } |
| } |
| return st |
| } |
| |
| // makeInstName makes the unique name for a stenciled generic function or method, |
| // based on the name of the function fy=nsym and the targs. It replaces any |
| // existing bracket type list in the name. makeInstName asserts that fnsym has |
| // brackets in its name if and only if hasBrackets is true. |
| // TODO(danscales): remove the assertions and the hasBrackets argument later. |
| // |
| // Names of declared generic functions have no brackets originally, so hasBrackets |
| // should be false. Names of generic methods already have brackets, since the new |
| // type parameter is specified in the generic type of the receiver (e.g. func |
| // (func (v *value[T]).set(...) { ... } has the original name (*value[T]).set. |
| // |
| // The standard naming is something like: 'genFn[int,bool]' for functions and |
| // '(*genType[int,bool]).methodName' for methods |
| func makeInstName(fnsym *types.Sym, targs []ir.Node, hasBrackets bool) *types.Sym { |
| b := bytes.NewBufferString("") |
| name := fnsym.Name |
| i := strings.Index(name, "[") |
| assert(hasBrackets == (i >= 0)) |
| if i >= 0 { |
| b.WriteString(name[0:i]) |
| } else { |
| b.WriteString(name) |
| } |
| b.WriteString("[") |
| for i, targ := range targs { |
| if i > 0 { |
| b.WriteString(",") |
| } |
| b.WriteString(targ.Type().String()) |
| } |
| b.WriteString("]") |
| if i >= 0 { |
| i2 := strings.Index(name[i:], "]") |
| assert(i2 >= 0) |
| b.WriteString(name[i+i2+1:]) |
| } |
| return typecheck.Lookup(b.String()) |
| } |
| |
| // Struct containing info needed for doing the substitution as we create the |
| // instantiation of a generic function with specified type arguments. |
| type subster struct { |
| g *irgen |
| isMethod bool // If a method is being instantiated |
| newf *ir.Func // Func node for the new stenciled function |
| tparams []*types.Field |
| targs []ir.Node |
| // The substitution map from name nodes in the generic function to the |
| // name nodes in the new stenciled function. |
| vars map[*ir.Name]*ir.Name |
| } |
| |
| // genericSubst returns a new function with name newsym. The function is an |
| // instantiation of a generic function or method specified by namedNode with type |
| // args targs. For a method with a generic receiver, it returns an instantiated |
| // function type where the receiver becomes the first parameter. Otherwise the |
| // instantiated method would still need to be transformed by later compiler |
| // phases. |
| func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []ir.Node, isMethod bool) *ir.Func { |
| var tparams []*types.Field |
| if isMethod { |
| // Get the type params from the method receiver (after skipping |
| // over any pointer) |
| recvType := nameNode.Type().Recv().Type |
| recvType = deref(recvType) |
| tparams = make([]*types.Field, len(recvType.RParams())) |
| for i, rparam := range recvType.RParams() { |
| tparams[i] = types.NewField(src.NoXPos, nil, rparam) |
| } |
| } else { |
| tparams = nameNode.Type().TParams().Fields().Slice() |
| } |
| gf := nameNode.Func |
| // Pos of the instantiated function is same as the generic function |
| newf := ir.NewFunc(gf.Pos()) |
| newf.Pragma = gf.Pragma // copy over pragmas from generic function to stenciled implementation. |
| newf.Nname = ir.NewNameAt(gf.Pos(), newsym) |
| newf.Nname.Func = newf |
| newf.Nname.Defn = newf |
| newsym.Def = newf.Nname |
| savef := ir.CurFunc |
| // transformCall/transformReturn (called during stenciling of the body) |
| // depend on ir.CurFunc being set. |
| ir.CurFunc = newf |
| |
| assert(len(tparams) == len(targs)) |
| |
| subst := &subster{ |
| g: g, |
| isMethod: isMethod, |
| newf: newf, |
| tparams: tparams, |
| targs: targs, |
| vars: make(map[*ir.Name]*ir.Name), |
| } |
| |
| newf.Dcl = make([]*ir.Name, len(gf.Dcl)) |
| for i, n := range gf.Dcl { |
| newf.Dcl[i] = subst.node(n).(*ir.Name) |
| } |
| |
| // Ugly: we have to insert the Name nodes of the parameters/results into |
| // the function type. The current function type has no Nname fields set, |
| // because it came via conversion from the types2 type. |
| oldt := nameNode.Type() |
| // We also transform a generic method type to the corresponding |
| // instantiated function type where the receiver is the first parameter. |
| newt := types.NewSignature(oldt.Pkg(), nil, nil, |
| subst.fields(ir.PPARAM, append(oldt.Recvs().FieldSlice(), oldt.Params().FieldSlice()...), newf.Dcl), |
| subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl)) |
| |
| newf.Nname.SetType(newt) |
| ir.MarkFunc(newf.Nname) |
| newf.SetTypecheck(1) |
| newf.Nname.SetTypecheck(1) |
| |
| // Make sure name/type of newf is set before substituting the body. |
| newf.Body = subst.list(gf.Body) |
| ir.CurFunc = savef |
| |
| return newf |
| } |
| |
| // node is like DeepCopy(), but creates distinct ONAME nodes, and also descends |
| // into closures. It substitutes type arguments for type parameters in all the new |
| // nodes. |
| func (subst *subster) node(n ir.Node) ir.Node { |
| // Use closure to capture all state needed by the ir.EditChildren argument. |
| var edit func(ir.Node) ir.Node |
| edit = func(x ir.Node) ir.Node { |
| switch x.Op() { |
| case ir.OTYPE: |
| return ir.TypeNode(subst.typ(x.Type())) |
| |
| case ir.ONAME: |
| name := x.(*ir.Name) |
| if v := subst.vars[name]; v != nil { |
| return v |
| } |
| m := ir.NewNameAt(name.Pos(), name.Sym()) |
| if name.IsClosureVar() { |
| m.SetIsClosureVar(true) |
| } |
| t := x.Type() |
| if t == nil { |
| assert(name.BuiltinOp != 0) |
| } else { |
| newt := subst.typ(t) |
| m.SetType(newt) |
| } |
| m.BuiltinOp = name.BuiltinOp |
| m.Curfn = subst.newf |
| m.Class = name.Class |
| m.Func = name.Func |
| subst.vars[name] = m |
| m.SetTypecheck(1) |
| return m |
| case ir.OLITERAL, ir.ONIL: |
| if x.Sym() != nil { |
| return x |
| } |
| } |
| m := ir.Copy(x) |
| if _, isExpr := m.(ir.Expr); isExpr { |
| t := x.Type() |
| if t == nil { |
| // t can be nil only if this is a call that has no |
| // return values, so allow that and otherwise give |
| // an error. |
| _, isCallExpr := m.(*ir.CallExpr) |
| _, isStructKeyExpr := m.(*ir.StructKeyExpr) |
| if !isCallExpr && !isStructKeyExpr && x.Op() != ir.OPANIC && |
| x.Op() != ir.OCLOSE { |
| base.Fatalf(fmt.Sprintf("Nil type for %v", x)) |
| } |
| } else if x.Op() != ir.OCLOSURE { |
| m.SetType(subst.typ(x.Type())) |
| } |
| } |
| ir.EditChildren(m, edit) |
| |
| if x.Typecheck() == 3 { |
| // These are nodes whose transforms were delayed until |
| // their instantiated type was known. |
| m.SetTypecheck(1) |
| if typecheck.IsCmp(x.Op()) { |
| transformCompare(m.(*ir.BinaryExpr)) |
| } else { |
| switch x.Op() { |
| case ir.OSLICE, ir.OSLICE3: |
| transformSlice(m.(*ir.SliceExpr)) |
| |
| case ir.OADD: |
| m = transformAdd(m.(*ir.BinaryExpr)) |
| |
| case ir.OINDEX: |
| transformIndex(m.(*ir.IndexExpr)) |
| |
| case ir.OAS2: |
| as2 := m.(*ir.AssignListStmt) |
| transformAssign(as2, as2.Lhs, as2.Rhs) |
| |
| case ir.OAS: |
| as := m.(*ir.AssignStmt) |
| lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y} |
| transformAssign(as, lhs, rhs) |
| |
| case ir.OASOP: |
| as := m.(*ir.AssignOpStmt) |
| transformCheckAssign(as, as.X) |
| |
| case ir.ORETURN: |
| transformReturn(m.(*ir.ReturnStmt)) |
| |
| case ir.OSEND: |
| transformSend(m.(*ir.SendStmt)) |
| |
| default: |
| base.Fatalf("Unexpected node with Typecheck() == 3") |
| } |
| } |
| } |
| |
| switch x.Op() { |
| case ir.OLITERAL: |
| t := m.Type() |
| if t != x.Type() { |
| // types2 will give us a constant with a type T, |
| // if an untyped constant is used with another |
| // operand of type T (in a provably correct way). |
| // When we substitute in the type args during |
| // stenciling, we now know the real type of the |
| // constant. We may then need to change the |
| // BasicLit.val to be the correct type (e.g. |
| // convert an int64Val constant to a floatVal |
| // constant). |
| m.SetType(types.UntypedInt) // use any untyped type for DefaultLit to work |
| m = typecheck.DefaultLit(m, t) |
| } |
| |
| case ir.OXDOT: |
| // A method value/call via a type param will have been |
| // left as an OXDOT. When we see this during stenciling, |
| // finish the transformation, now that we have the |
| // instantiated receiver type. We need to do this now, |
| // since the access/selection to the method for the real |
| // type is very different from the selection for the type |
| // param. m will be transformed to an OCALLPART node. It |
| // will be transformed to an ODOTMETH or ODOTINTER node if |
| // we find in the OCALL case below that the method value |
| // is actually called. |
| transformDot(m.(*ir.SelectorExpr), false) |
| m.SetTypecheck(1) |
| |
| case ir.OCALL: |
| call := m.(*ir.CallExpr) |
| switch call.X.Op() { |
| case ir.OTYPE: |
| // Transform the conversion, now that we know the |
| // type argument. |
| m = transformConvCall(m.(*ir.CallExpr)) |
| |
| case ir.OCALLPART: |
| // Redo the transformation of OXDOT, now that we |
| // know the method value is being called. Then |
| // transform the call. |
| call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT) |
| transformDot(call.X.(*ir.SelectorExpr), true) |
| transformCall(call) |
| |
| case ir.ODOT, ir.ODOTPTR: |
| // An OXDOT for a generic receiver was resolved to |
| // an access to a field which has a function |
| // value. Transform the call to that function, now |
| // that the OXDOT was resolved. |
| transformCall(call) |
| |
| case ir.ONAME: |
| name := call.X.Name() |
| if name.BuiltinOp != ir.OXXX { |
| switch name.BuiltinOp { |
| case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OLEN, ir.OCAP, ir.OAPPEND: |
| // Transform these builtins now that we |
| // know the type of the args. |
| m = transformBuiltin(call) |
| default: |
| base.FatalfAt(call.Pos(), "Unexpected builtin op") |
| } |
| } else { |
| // This is the case of a function value that was a |
| // type parameter (implied to be a function via a |
| // structural constraint) which is now resolved. |
| transformCall(call) |
| } |
| |
| case ir.OCLOSURE: |
| transformCall(call) |
| |
| case ir.OFUNCINST: |
| // A call with an OFUNCINST will get transformed |
| // in stencil() once we have created & attached the |
| // instantiation to be called. |
| |
| default: |
| base.FatalfAt(call.Pos(), fmt.Sprintf("Unexpected op with CALL during stenciling: %v", call.X.Op())) |
| } |
| |
| case ir.OCLOSURE: |
| x := x.(*ir.ClosureExpr) |
| // Need to duplicate x.Func.Nname, x.Func.Dcl, x.Func.ClosureVars, and |
| // x.Func.Body. |
| oldfn := x.Func |
| newfn := ir.NewFunc(oldfn.Pos()) |
| if oldfn.ClosureCalled() { |
| newfn.SetClosureCalled(true) |
| } |
| newfn.SetIsHiddenClosure(true) |
| m.(*ir.ClosureExpr).Func = newfn |
| // Closure name can already have brackets, if it derives |
| // from a generic method |
| newsym := makeInstName(oldfn.Nname.Sym(), subst.targs, subst.isMethod) |
| newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), newsym) |
| newfn.Nname.Func = newfn |
| newfn.Nname.Defn = newfn |
| ir.MarkFunc(newfn.Nname) |
| newfn.OClosure = m.(*ir.ClosureExpr) |
| |
| saveNewf := subst.newf |
| ir.CurFunc = newfn |
| subst.newf = newfn |
| newfn.Dcl = subst.namelist(oldfn.Dcl) |
| newfn.ClosureVars = subst.namelist(oldfn.ClosureVars) |
| |
| typed(subst.typ(oldfn.Nname.Type()), newfn.Nname) |
| typed(newfn.Nname.Type(), m) |
| newfn.SetTypecheck(1) |
| |
| // Make sure type of closure function is set before doing body. |
| newfn.Body = subst.list(oldfn.Body) |
| subst.newf = saveNewf |
| ir.CurFunc = saveNewf |
| |
| subst.g.target.Decls = append(subst.g.target.Decls, newfn) |
| } |
| return m |
| } |
| |
| return edit(n) |
| } |
| |
| func (subst *subster) namelist(l []*ir.Name) []*ir.Name { |
| s := make([]*ir.Name, len(l)) |
| for i, n := range l { |
| s[i] = subst.node(n).(*ir.Name) |
| if n.Defn != nil { |
| s[i].Defn = subst.node(n.Defn) |
| } |
| if n.Outer != nil { |
| s[i].Outer = subst.node(n.Outer).(*ir.Name) |
| } |
| } |
| return s |
| } |
| |
| func (subst *subster) list(l []ir.Node) []ir.Node { |
| s := make([]ir.Node, len(l)) |
| for i, n := range l { |
| s[i] = subst.node(n) |
| } |
| return s |
| } |
| |
| // tstruct substitutes type params in types of the fields of a structure type. For |
| // each field, if Nname is set, tstruct also translates the Nname using |
| // subst.vars, if Nname is in subst.vars. To always force the creation of a new |
| // (top-level) struct, regardless of whether anything changed with the types or |
| // names of the struct's fields, set force to true. |
| func (subst *subster) tstruct(t *types.Type, force bool) *types.Type { |
| if t.NumFields() == 0 { |
| if t.HasTParam() { |
| // For an empty struct, we need to return a new type, |
| // since it may now be fully instantiated (HasTParam |
| // becomes false). |
| return types.NewStruct(t.Pkg(), nil) |
| } |
| return t |
| } |
| var newfields []*types.Field |
| if force { |
| newfields = make([]*types.Field, t.NumFields()) |
| } |
| for i, f := range t.Fields().Slice() { |
| t2 := subst.typ(f.Type) |
| if (t2 != f.Type || f.Nname != nil) && newfields == nil { |
| newfields = make([]*types.Field, t.NumFields()) |
| for j := 0; j < i; j++ { |
| newfields[j] = t.Field(j) |
| } |
| } |
| if newfields != nil { |
| // TODO(danscales): make sure this works for the field |
| // names of embedded types (which should keep the name of |
| // the type param, not the instantiated type). |
| newfields[i] = types.NewField(f.Pos, f.Sym, t2) |
| if f.Nname != nil { |
| // f.Nname may not be in subst.vars[] if this is |
| // a function name or a function instantiation type |
| // that we are translating |
| v := subst.vars[f.Nname.(*ir.Name)] |
| // Be careful not to put a nil var into Nname, |
| // since Nname is an interface, so it would be a |
| // non-nil interface. |
| if v != nil { |
| newfields[i].Nname = v |
| } |
| } |
| } |
| } |
| if newfields != nil { |
| return types.NewStruct(t.Pkg(), newfields) |
| } |
| return t |
| |
| } |
| |
| // tinter substitutes type params in types of the methods of an interface type. |
| func (subst *subster) tinter(t *types.Type) *types.Type { |
| if t.Methods().Len() == 0 { |
| return t |
| } |
| var newfields []*types.Field |
| for i, f := range t.Methods().Slice() { |
| t2 := subst.typ(f.Type) |
| if (t2 != f.Type || f.Nname != nil) && newfields == nil { |
| newfields = make([]*types.Field, t.Methods().Len()) |
| for j := 0; j < i; j++ { |
| newfields[j] = t.Methods().Index(j) |
| } |
| } |
| if newfields != nil { |
| newfields[i] = types.NewField(f.Pos, f.Sym, t2) |
| } |
| } |
| if newfields != nil { |
| return types.NewInterface(t.Pkg(), newfields) |
| } |
| return t |
| } |
| |
| // instTypeName creates a name for an instantiated type, based on the name of the |
| // generic type and the type args |
| func instTypeName(name string, targs []*types.Type) string { |
| b := bytes.NewBufferString(name) |
| b.WriteByte('[') |
| for i, targ := range targs { |
| if i > 0 { |
| b.WriteByte(',') |
| } |
| b.WriteString(targ.String()) |
| } |
| b.WriteByte(']') |
| return b.String() |
| } |
| |
| // typ computes the type obtained by substituting any type parameter in t with the |
| // corresponding type argument in subst. If t contains no type parameters, the |
| // result is t; otherwise the result is a new type. It deals with recursive types |
| // by using TFORW types and finding partially or fully created types via sym.Def. |
| func (subst *subster) typ(t *types.Type) *types.Type { |
| if !t.HasTParam() && t.Kind() != types.TFUNC { |
| // Note: function types need to be copied regardless, as the |
| // types of closures may contain declarations that need |
| // to be copied. See #45738. |
| return t |
| } |
| |
| if t.Kind() == types.TTYPEPARAM { |
| for i, tp := range subst.tparams { |
| if tp.Type == t { |
| return subst.targs[i].Type() |
| } |
| } |
| // If t is a simple typeparam T, then t has the name/symbol 'T' |
| // and t.Underlying() == t. |
| // |
| // However, consider the type definition: 'type P[T any] T'. We |
| // might use this definition so we can have a variant of type T |
| // that we can add new methods to. Suppose t is a reference to |
| // P[T]. t has the name 'P[T]', but its kind is TTYPEPARAM, |
| // because P[T] is defined as T. If we look at t.Underlying(), it |
| // is different, because the name of t.Underlying() is 'T' rather |
| // than 'P[T]'. But the kind of t.Underlying() is also TTYPEPARAM. |
| // In this case, we do the needed recursive substitution in the |
| // case statement below. |
| if t.Underlying() == t { |
| // t is a simple typeparam that didn't match anything in tparam |
| return t |
| } |
| // t is a more complex typeparam (e.g. P[T], as above, whose |
| // definition is just T). |
| assert(t.Sym() != nil) |
| } |
| |
| var newsym *types.Sym |
| var neededTargs []*types.Type |
| var forw *types.Type |
| |
| if t.Sym() != nil { |
| // Translate the type params for this type according to |
| // the tparam/targs mapping from subst. |
| neededTargs = make([]*types.Type, len(t.RParams())) |
| for i, rparam := range t.RParams() { |
| neededTargs[i] = subst.typ(rparam) |
| } |
| // For a named (defined) type, we have to change the name of the |
| // type as well. We do this first, so we can look up if we've |
| // already seen this type during this substitution or other |
| // definitions/substitutions. |
| genName := genericTypeName(t.Sym()) |
| newsym = t.Sym().Pkg.Lookup(instTypeName(genName, neededTargs)) |
| if newsym.Def != nil { |
| // We've already created this instantiated defined type. |
| return newsym.Def.Type() |
| } |
| |
| // In order to deal with recursive generic types, create a TFORW |
| // type initially and set the Def field of its sym, so it can be |
| // found if this type appears recursively within the type. |
| forw = newIncompleteNamedType(t.Pos(), newsym) |
| //println("Creating new type by sub", newsym.Name, forw.HasTParam()) |
| forw.SetRParams(neededTargs) |
| } |
| |
| var newt *types.Type |
| |
| switch t.Kind() { |
| case types.TTYPEPARAM: |
| if t.Sym() == newsym { |
| // The substitution did not change the type. |
| return t |
| } |
| // Substitute the underlying typeparam (e.g. T in P[T], see |
| // the example describing type P[T] above). |
| newt = subst.typ(t.Underlying()) |
| assert(newt != t) |
| |
| case types.TARRAY: |
| elem := t.Elem() |
| newelem := subst.typ(elem) |
| if newelem != elem { |
| newt = types.NewArray(newelem, t.NumElem()) |
| } |
| |
| case types.TPTR: |
| elem := t.Elem() |
| newelem := subst.typ(elem) |
| if newelem != elem { |
| newt = types.NewPtr(newelem) |
| } |
| |
| case types.TSLICE: |
| elem := t.Elem() |
| newelem := subst.typ(elem) |
| if newelem != elem { |
| newt = types.NewSlice(newelem) |
| } |
| |
| case types.TSTRUCT: |
| newt = subst.tstruct(t, false) |
| if newt == t { |
| newt = nil |
| } |
| |
| case types.TFUNC: |
| newrecvs := subst.tstruct(t.Recvs(), false) |
| newparams := subst.tstruct(t.Params(), false) |
| newresults := subst.tstruct(t.Results(), false) |
| if newrecvs != t.Recvs() || newparams != t.Params() || newresults != t.Results() { |
| // If any types have changed, then the all the fields of |
| // of recv, params, and results must be copied, because they have |
| // offset fields that are dependent, and so must have an |
| // independent copy for each new signature. |
| var newrecv *types.Field |
| if newrecvs.NumFields() > 0 { |
| if newrecvs == t.Recvs() { |
| newrecvs = subst.tstruct(t.Recvs(), true) |
| } |
| newrecv = newrecvs.Field(0) |
| } |
| if newparams == t.Params() { |
| newparams = subst.tstruct(t.Params(), true) |
| } |
| if newresults == t.Results() { |
| newresults = subst.tstruct(t.Results(), true) |
| } |
| newt = types.NewSignature(t.Pkg(), newrecv, t.TParams().FieldSlice(), newparams.FieldSlice(), newresults.FieldSlice()) |
| } |
| |
| case types.TINTER: |
| newt = subst.tinter(t) |
| if newt == t { |
| newt = nil |
| } |
| |
| case types.TMAP: |
| newkey := subst.typ(t.Key()) |
| newval := subst.typ(t.Elem()) |
| if newkey != t.Key() || newval != t.Elem() { |
| newt = types.NewMap(newkey, newval) |
| } |
| |
| case types.TCHAN: |
| elem := t.Elem() |
| newelem := subst.typ(elem) |
| if newelem != elem { |
| newt = types.NewChan(newelem, t.ChanDir()) |
| if !newt.HasTParam() { |
| // TODO(danscales): not sure why I have to do this |
| // only for channels..... |
| types.CheckSize(newt) |
| } |
| } |
| } |
| if newt == nil { |
| // Even though there were typeparams in the type, there may be no |
| // change if this is a function type for a function call (which will |
| // have its own tparams/targs in the function instantiation). |
| return t |
| } |
| |
| if t.Sym() == nil { |
| // Not a named type, so there was no forwarding type and there are |
| // no methods to substitute. |
| assert(t.Methods().Len() == 0) |
| return newt |
| } |
| |
| forw.SetUnderlying(newt) |
| newt = forw |
| |
| if t.Kind() != types.TINTER && t.Methods().Len() > 0 { |
| // Fill in the method info for the new type. |
| var newfields []*types.Field |
| newfields = make([]*types.Field, t.Methods().Len()) |
| for i, f := range t.Methods().Slice() { |
| t2 := subst.typ(f.Type) |
| oldsym := f.Nname.Sym() |
| newsym := makeInstName(oldsym, subst.targs, true) |
| var nname *ir.Name |
| if newsym.Def != nil { |
| nname = newsym.Def.(*ir.Name) |
| } else { |
| nname = ir.NewNameAt(f.Pos, newsym) |
| nname.SetType(t2) |
| newsym.Def = nname |
| } |
| newfields[i] = types.NewField(f.Pos, f.Sym, t2) |
| newfields[i].Nname = nname |
| } |
| newt.Methods().Set(newfields) |
| if !newt.HasTParam() { |
| // Generate all the methods for a new fully-instantiated type. |
| subst.g.instTypeList = append(subst.g.instTypeList, newt) |
| } |
| } |
| return newt |
| } |
| |
| // fields sets the Nname field for the Field nodes inside a type signature, based |
| // on the corresponding in/out parameters in dcl. It depends on the in and out |
| // parameters being in order in dcl. |
| func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field { |
| // Find the starting index in dcl of declarations of the class (either |
| // PPARAM or PPARAMOUT). |
| var i int |
| for i = range dcl { |
| if dcl[i].Class == class { |
| break |
| } |
| } |
| |
| // Create newfields nodes that are copies of the oldfields nodes, but |
| // with substitution for any type params, and with Nname set to be the node in |
| // Dcl for the corresponding PPARAM or PPARAMOUT. |
| newfields := make([]*types.Field, len(oldfields)) |
| for j := range oldfields { |
| newfields[j] = oldfields[j].Copy() |
| newfields[j].Type = subst.typ(oldfields[j].Type) |
| // A param field will be missing from dcl if its name is |
| // unspecified or specified as "_". So, we compare the dcl sym |
| // with the field sym. If they don't match, this dcl (if there is |
| // one left) must apply to a later field. |
| if i < len(dcl) && dcl[i].Sym() == oldfields[j].Sym { |
| newfields[j].Nname = dcl[i] |
| i++ |
| } |
| } |
| return newfields |
| } |
| |
| // defer does a single defer of type t, if it is a pointer type. |
| func deref(t *types.Type) *types.Type { |
| if t.IsPtr() { |
| return t.Elem() |
| } |
| return t |
| } |
| |
| // newIncompleteNamedType returns a TFORW type t with name specified by sym, such |
| // that t.nod and sym.Def are set correctly. |
| func newIncompleteNamedType(pos src.XPos, sym *types.Sym) *types.Type { |
| name := ir.NewDeclNameAt(pos, ir.OTYPE, sym) |
| forw := types.NewNamed(name) |
| name.SetType(forw) |
| sym.Def = name |
| return forw |
| } |