// 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 ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/objw" "cmd/compile/internal/reflectdata" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/obj" "cmd/internal/src" "fmt" "go/constant" ) // Enable extra consistency checks. const doubleCheck = true func assert(p bool) { base.Assert(p) } // Temporary - for outputting information on derived types, dictionaries, sub-dictionaries. // Turn off when running tests. var infoPrintMode = false func infoPrint(format string, a ...interface{}) { if infoPrintMode { fmt.Printf(format, a...) } } // 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.instInfoMap = make(map[*types.Sym]*instInfo) g.gfInfoMap = make(map[*types.Sym]*gfInfo) // 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 closureRequired := false // declInfo will be non-nil exactly if we are scanning an instantiated function declInfo := g.instInfoMap[decl.Sym()] ir.Visit(decl, func(n ir.Node) { if n.Op() == ir.OFUNCINST { // generic F, not immediately called closureRequired = true } if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) { // T.M or x.M, where T or x is generic, but not immediately // called. Not necessary if the method selected is // actually for an embedded interface field. closureRequired = true } if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST { // We have found a function call using a generic function // instantiation. call := n.(*ir.CallExpr) inst := call.X.(*ir.InstExpr) nameNode, isMeth := g.getInstNameNode(inst) targs := typecheck.TypesOf(inst.Targs) st := g.getInstantiation(nameNode, targs, isMeth) dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth) if infoPrintMode { dictkind := "Main dictionary" if usingSubdict { dictkind = "Sub-dictionary" } if inst.X.Op() == ir.OMETHVALUE { fmt.Printf("%s in %v at generic method call: %v - %v\n", dictkind, decl, inst.X, call) } else { fmt.Printf("%s in %v at generic function call: %v - %v\n", dictkind, decl, inst.X, call) } } // Transform the Call now, which changes OCALL to // OCALLFUNC and does typecheckaste/assignconvfn. Do // it before installing the instantiation, so we are // checking against non-shape param types in // typecheckaste. transformCall(call) // Replace the OFUNCINST with a direct reference to the // new stenciled function call.X = st.Nname if inst.X.Op() == ir.OMETHVALUE { // 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. call.Args.Prepend(inst.X.(*ir.SelectorExpr).X) } // Add dictionary to argument list. call.Args.Prepend(dictValue) modified = true } if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH && len(deref(n.(*ir.CallExpr).X.Type().Recv().Type).RParams()) > 0 { // Method call on a generic type, which was instantiated by stenciling. // Method calls on explicitly instantiated types will have an OFUNCINST // and are handled above. call := n.(*ir.CallExpr) meth := call.X.(*ir.SelectorExpr) targs := deref(meth.Type().Recv().Type).RParams() t := meth.X.Type() baseSym := deref(t).OrigSym baseType := baseSym.Def.(*ir.Name).Type() var gf *ir.Name for _, m := range baseType.Methods().Slice() { if meth.Sel == m.Sym { gf = m.Nname.(*ir.Name) break } } // Transform the Call now, which changes OCALL // to OCALLFUNC and does typecheckaste/assignconvfn. transformCall(call) st := g.getInstantiation(gf, targs, true) dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true) // We have to be using a subdictionary, since this is // a generic method call. assert(usingSubdict) // Transform to a function call, by appending the // dictionary and the receiver to the args. call.SetOp(ir.OCALLFUNC) call.X = st.Nname call.Args.Prepend(dictValue, meth.X) modified = true } }) // If we found a reference to a generic instantiation that wasn't an // immediate call, then traverse the nodes of decl again (with // EditChildren rather than Visit), where we actually change the // reference to the instantiation to a closure that captures the // dictionary, then does a direct call. // EditChildren is more expensive than Visit, so we only do this // in the infrequent case of an OFUNCINST without a corresponding // call. if closureRequired { modified = true var edit func(ir.Node) ir.Node var outer *ir.Func if f, ok := decl.(*ir.Func); ok { outer = f } edit = func(x ir.Node) ir.Node { if x.Op() == ir.OFUNCINST { child := x.(*ir.InstExpr).X if child.Op() == ir.OMETHEXPR || child.Op() == ir.OMETHVALUE { // Call EditChildren on child (x.X), // not x, so that we don't do // buildClosure() on the // METHEXPR/METHVALUE nodes as well. ir.EditChildren(child, edit) return g.buildClosure(outer, x) } } ir.EditChildren(x, edit) switch { case x.Op() == ir.OFUNCINST: return g.buildClosure(outer, x) case (x.Op() == ir.OMETHEXPR || x.Op() == ir.OMETHVALUE) && len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && !types.IsInterfaceMethod(x.(*ir.SelectorExpr).Selection.Type): return g.buildClosure(outer, x) } 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() } g.finalizeSyms() } // buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR // of generic type. outer is the containing function (or nil if closure is // in a global assignment instead of a function). func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node { pos := x.Pos() var target *ir.Func // target instantiated function/method var dictValue ir.Node // dictionary to use var rcvrValue ir.Node // receiver, if a method value typ := x.Type() // type of the closure var outerInfo *instInfo if outer != nil { outerInfo = g.instInfoMap[outer.Sym()] } usingSubdict := false valueMethod := false if x.Op() == ir.OFUNCINST { inst := x.(*ir.InstExpr) // Type arguments we're instantiating with. targs := typecheck.TypesOf(inst.Targs) // Find the generic function/method. var gf *ir.Name if inst.X.Op() == ir.ONAME { // Instantiating a generic function call. gf = inst.X.(*ir.Name) } else if inst.X.Op() == ir.OMETHVALUE { // Instantiating a method value x.M. se := inst.X.(*ir.SelectorExpr) rcvrValue = se.X gf = se.Selection.Nname.(*ir.Name) } else { panic("unhandled") } // target is the instantiated function we're trying to call. // For functions, the target expects a dictionary as its first argument. // For method values, the target expects a dictionary and the receiver // as its first two arguments. // dictValue is the value to use for the dictionary argument. target = g.getInstantiation(gf, targs, rcvrValue != nil) dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, rcvrValue != nil) if infoPrintMode { dictkind := "Main dictionary" if usingSubdict { dictkind = "Sub-dictionary" } if rcvrValue == nil { fmt.Printf("%s in %v for generic function value %v\n", dictkind, outer, inst.X) } else { fmt.Printf("%s in %v for generic method value %v\n", dictkind, outer, inst.X) } } } else { // ir.OMETHEXPR or ir.METHVALUE // Method expression T.M where T is a generic type. se := x.(*ir.SelectorExpr) targs := deref(se.X.Type()).RParams() if len(targs) == 0 { panic("bad") } if x.Op() == ir.OMETHVALUE { rcvrValue = se.X } // se.X.Type() is the top-level type of the method expression. To // correctly handle method expressions involving embedded fields, // look up the generic method below using the type of the receiver // of se.Selection, since that will be the type that actually has // the method. recv := deref(se.Selection.Type.Recv().Type) if len(recv.RParams()) == 0 { // The embedded type that actually has the method is not // actually generic, so no need to build a closure. return x } baseType := recv.OrigSym.Def.Type() var gf *ir.Name for _, m := range baseType.Methods().Slice() { if se.Sel == m.Sym { gf = m.Nname.(*ir.Name) break } } if !gf.Type().Recv().Type.IsPtr() { // Remember if value method, so we can detect (*T).M case. valueMethod = true } target = g.getInstantiation(gf, targs, true) dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true) if infoPrintMode { dictkind := "Main dictionary" if usingSubdict { dictkind = "Sub-dictionary" } fmt.Printf("%s in %v for method expression %v\n", dictkind, outer, x) } } // Build a closure to implement a function instantiation. // // func f[T any] (int, int) (int, int) { ...whatever... } // // Then any reference to f[int] not directly called gets rewritten to // // .dictN := ... dictionary to use ... // func(a0, a1 int) (r0, r1 int) { // return .inst.f[int](.dictN, a0, a1) // } // // Similarly for method expressions, // // type g[T any] .... // func (rcvr g[T]) f(a0, a1 int) (r0, r1 int) { ... } // // Any reference to g[int].f not directly called gets rewritten to // // .dictN := ... dictionary to use ... // func(rcvr g[int], a0, a1 int) (r0, r1 int) { // return .inst.g[int].f(.dictN, rcvr, a0, a1) // } // // Also method values // // var x g[int] // // Any reference to x.f not directly called gets rewritten to // // .dictN := ... dictionary to use ... // x2 := x // func(a0, a1 int) (r0, r1 int) { // return .inst.g[int].f(.dictN, x2, a0, a1) // } // Make a new internal function. fn, formalParams, formalResults := startClosure(pos, outer, typ) // This is the dictionary we want to use. // It may be a constant, or it may be a dictionary acquired from the outer function's dictionary. // For the latter, dictVar is a variable in the outer function's scope, set to the subdictionary // read from the outer function's dictionary. var dictVar *ir.Name var dictAssign *ir.AssignStmt if outer != nil { // Note: for now this is a compile-time constant, so we don't really need a closure // to capture it (a wrapper function would work just as well). But eventually it // will be a read of a subdictionary from the parent dictionary. dictVar = ir.NewNameAt(pos, typecheck.LookupNum(".dict", g.dnum)) g.dnum++ dictVar.Class = ir.PAUTO typed(types.Types[types.TUINTPTR], dictVar) dictVar.Curfn = outer dictAssign = ir.NewAssignStmt(pos, dictVar, dictValue) dictAssign.SetTypecheck(1) dictVar.Defn = dictAssign outer.Dcl = append(outer.Dcl, dictVar) } // assign the receiver to a temporary. var rcvrVar *ir.Name var rcvrAssign ir.Node if rcvrValue != nil { rcvrVar = ir.NewNameAt(pos, typecheck.LookupNum(".rcvr", g.dnum)) g.dnum++ rcvrVar.Class = ir.PAUTO typed(rcvrValue.Type(), rcvrVar) rcvrVar.Curfn = outer rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue) rcvrAssign.SetTypecheck(1) rcvrVar.Defn = rcvrAssign outer.Dcl = append(outer.Dcl, rcvrVar) } // Build body of closure. This involves just calling the wrapped function directly // with the additional dictionary argument. // First, figure out the dictionary argument. var dict2Var ir.Node if usingSubdict { // Capture sub-dictionary calculated in the outer function dict2Var = ir.CaptureName(pos, fn, dictVar) typed(types.Types[types.TUINTPTR], dict2Var) } else { // Static dictionary, so can be used directly in the closure dict2Var = dictValue } // Also capture the receiver variable. var rcvr2Var *ir.Name if rcvrValue != nil { rcvr2Var = ir.CaptureName(pos, fn, rcvrVar) } // Build arguments to call inside the closure. var args []ir.Node // First the dictionary argument. args = append(args, dict2Var) // Then the receiver. if rcvrValue != nil { args = append(args, rcvr2Var) } // Then all the other arguments (including receiver for method expressions). for i := 0; i < typ.NumParams(); i++ { if x.Op() == ir.OMETHEXPR && i == 0 { // If we are doing a method expression, we need to // explicitly traverse any embedded fields in the receiver // argument in order to call the method instantiation. arg0 := formalParams[0].Nname.(ir.Node) arg0 = typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, arg0, x.(*ir.SelectorExpr).Sel)).X if valueMethod && arg0.Type().IsPtr() { // For handling the (*T).M case: if we have a pointer // receiver after following all the embedded fields, // but it's a value method, add a star operator. arg0 = ir.NewStarExpr(arg0.Pos(), arg0) } args = append(args, arg0) } else { args = append(args, formalParams[i].Nname.(*ir.Name)) } } // Build call itself. var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args) if len(formalResults) > 0 { innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall}) } // Finish building body of closure. ir.CurFunc = fn // TODO: set types directly here instead of using typecheck.Stmt typecheck.Stmt(innerCall) ir.CurFunc = nil fn.Body = []ir.Node{innerCall} // We're all done with the captured dictionary (and receiver, for method values). ir.FinishCaptureNames(pos, outer, fn) // Make a closure referencing our new internal function. c := ir.UseClosure(fn.OClosure, g.target) var init []ir.Node if outer != nil { init = append(init, dictAssign) } if rcvrValue != nil { init = append(init, rcvrAssign) } return ir.InitExpr(init, c) } // instantiateMethods instantiates all the methods (and associated dictionaries) 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] assert(!typ.HasShape()) // Mark runtime type as needed, since this ensures that the // compiler puts out the needed DWARF symbols, when this // instantiated type has a different package from the local // package. typecheck.NeedRuntimeType(typ) // Lookup the method on the base generic type, since methods may // not be set on imported instantiated types. baseSym := typ.OrigSym baseType := baseSym.Def.(*ir.Name).Type() for j, _ := range typ.Methods().Slice() { if baseType.Methods().Slice()[j].Nointerface() { typ.Methods().Slice()[j].SetNointerface(true) } baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name) // Eagerly generate the instantiations and dictionaries that implement these methods. // We don't use the instantiations here, just generate them (and any // further instantiations those generate, etc.). // Note that we don't set the Func for any methods on instantiated // types. Their signatures don't match so that would be confusing. // Direct method calls go directly to the instantiations, implemented above. // Indirect method calls use wrappers generated in reflectcall. Those wrappers // will use these instantiations if they are needed (for interface tables or reflection). _ = g.getInstantiation(baseNname, typ.RParams(), true) _ = g.getDictionarySym(baseNname, typ.RParams(), true) } } g.instTypeList = nil } // getInstNameNode returns the name node for the method or function being instantiated, and a bool which is true if a method is being instantiated. func (g *irgen) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) { if meth, ok := inst.X.(*ir.SelectorExpr); ok { return meth.Selection.Nname.(*ir.Name), true } else { return inst.X.(*ir.Name), false } } // getDictOrSubdict returns, for a method/function call or reference (node n) in an // instantiation (described by instInfo), a node which is accessing a sub-dictionary // or main/static dictionary, as needed, and also returns a boolean indicating if a // sub-dictionary was accessed. nameNode is the particular function or method being // called/referenced, and targs are the type arguments. func (g *irgen) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Name, targs []*types.Type, isMeth bool) (ir.Node, bool) { var dict ir.Node usingSubdict := false if declInfo != nil { // Get the dictionary arg via sub-dictionary reference entry, ok := declInfo.dictEntryMap[n] // If the entry is not found, it may be that this node did not have // any type args that depend on type params, so we need a main // dictionary, not a sub-dictionary. if ok { dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictLen) usingSubdict = true } } if !usingSubdict { dict = g.getDictionaryValue(nameNode, targs, isMeth) } return dict, usingSubdict } // checkFetchBody checks if a generic body can be fetched, but hasn't been loaded // yet. If so, it imports the body. func checkFetchBody(nameNode *ir.Name) { if nameNode.Func.Body == nil && nameNode.Func.Inl != nil { // If there is no body yet but Func.Inl exists, then we can can // import the whole generic body. assert(nameNode.Func.Inl.Cost == 1 && nameNode.Sym().Pkg != types.LocalPkg) typecheck.ImportBody(nameNode.Func) assert(nameNode.Func.Inl.Body != nil) nameNode.Func.Body = nameNode.Func.Inl.Body nameNode.Func.Dcl = nameNode.Func.Inl.Dcl } } // getInstantiation gets the instantiantion and dictionary of the function or method nameNode // with the type arguments shapes. If the instantiated function is not already // cached, then it calls genericSubst to create the new instantiation. func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *ir.Func { checkFetchBody(nameNode) // Convert any non-shape type arguments to their shape, so we can reduce the // number of instantiations we have to generate. You can actually have a mix // of shape and non-shape arguments, because of inferred or explicitly // specified concrete type args. var s1 []*types.Type for i, t := range shapes { if !t.HasShape() { if s1 == nil { s1 = make([]*types.Type, len(shapes)) copy(s1[0:i], shapes[0:i]) } s1[i] = typecheck.Shapify(t) } else if s1 != nil { s1[i] = shapes[i] } } if s1 != nil { shapes = s1 } sym := typecheck.MakeFuncInstSym(nameNode.Sym(), shapes, isMeth) info := g.instInfoMap[sym] if info == nil { // If instantiation doesn't exist yet, create it and add // to the list of decls. gfInfo := g.getGfInfo(nameNode) info = &instInfo{ gf: nameNode, gfInfo: gfInfo, startSubDict: len(shapes) + len(gfInfo.derivedTypes), startItabConv: len(shapes) + len(gfInfo.derivedTypes) + len(gfInfo.subDictCalls), dictLen: len(shapes) + len(gfInfo.derivedTypes) + len(gfInfo.subDictCalls) + len(gfInfo.itabConvs), dictEntryMap: make(map[ir.Node]int), } // genericSubst fills in info.dictParam and info.dictEntryMap. st := g.genericSubst(sym, nameNode, shapes, isMeth, info) info.fun = st g.instInfoMap[sym] = info // This ensures that the linker drops duplicates of this instantiation. // All just works! st.SetDupok(true) g.target.Decls = append(g.target.Decls, st) if base.Flag.W > 1 { ir.Dump(fmt.Sprintf("\nstenciled %v", st), st) } } return info.fun } // 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 ts typecheck.Tsubster info *instInfo // Place to put extra info in the instantiation } // 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 shapes. For a method with a generic receiver, it returns an instantiated // function type where the receiver becomes the first parameter. For either a generic // method or function, a dictionary parameter is the added as the very first // parameter. genericSubst fills in info.dictParam and info.dictEntryMap. func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func { var tparams []*types.Type if isMethod { // Get the type params from the method receiver (after skipping // over any pointer) recvType := nameNode.Type().Recv().Type recvType = deref(recvType) tparams = recvType.RParams() } else { fields := nameNode.Type().TParams().Fields().Slice() tparams = make([]*types.Type, len(fields)) for i, f := range fields { tparams[i] = f.Type } } 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(shapes)) subst := &subster{ g: g, isMethod: isMethod, newf: newf, info: info, ts: typecheck.Tsubster{ Tparams: tparams, Targs: shapes, Vars: make(map[*ir.Name]*ir.Name), }, } newf.Dcl = make([]*ir.Name, 0, len(gf.Dcl)+1) // Create the needed dictionary param dictionarySym := newsym.Pkg.Lookup(".dict") dictionaryType := types.Types[types.TUINTPTR] dictionaryName := ir.NewNameAt(gf.Pos(), dictionarySym) typed(dictionaryType, dictionaryName) dictionaryName.Class = ir.PPARAM dictionaryName.Curfn = newf newf.Dcl = append(newf.Dcl, dictionaryName) for _, n := range gf.Dcl { if n.Sym().Name == ".dict" { panic("already has dictionary") } newf.Dcl = append(newf.Dcl, subst.localvar(n)) } dictionaryArg := types.NewField(gf.Pos(), dictionarySym, dictionaryType) dictionaryArg.Nname = dictionaryName info.dictParam = dictionaryName // We add the dictionary as the first parameter in the function signature. // We also transform a method type to the corresponding function type // (make the receiver be the next parameter after the dictionary). oldt := nameNode.Type() var args []*types.Field args = append(args, dictionaryArg) args = append(args, oldt.Recvs().FieldSlice()...) args = append(args, oldt.Params().FieldSlice()...) // Replace the types in the function signature via subst.fields. // Ugly: also, 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. newt := types.NewSignature(oldt.Pkg(), nil, nil, subst.fields(ir.PPARAM, args, newf.Dcl), subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl)) typed(newt, newf.Nname) ir.MarkFunc(newf.Nname) newf.SetTypecheck(1) // Make sure name/type of newf is set before substituting the body. newf.Body = subst.list(gf.Body) // Add code to check that the dictionary is correct. // TODO: must be adjusted to deal with shapes, but will go away soon when we move // to many->1 shape to concrete mapping. // newf.Body.Prepend(subst.checkDictionary(dictionaryName, shapes)...) ir.CurFunc = savef // Add any new, fully instantiated types seen during the substitution to // g.instTypeList. g.instTypeList = append(g.instTypeList, subst.ts.InstTypeList...) if doubleCheck { ir.Visit(newf, func(n ir.Node) { if n.Op() != ir.OCONVIFACE { return } c := n.(*ir.ConvExpr) if c.X.Type().HasShape() { ir.Dump("BAD FUNCTION", newf) ir.Dump("BAD CONVERSION", c) base.Fatalf("converting shape type to interface") } }) } return newf } // localvar creates a new name node for the specified local variable and enters it // in subst.vars. It substitutes type arguments for type parameters in the type of // name as needed. func (subst *subster) localvar(name *ir.Name) *ir.Name { m := ir.NewNameAt(name.Pos(), name.Sym()) if name.IsClosureVar() { m.SetIsClosureVar(true) } m.SetType(subst.ts.Typ(name.Type())) m.BuiltinOp = name.BuiltinOp m.Curfn = subst.newf m.Class = name.Class assert(name.Class != ir.PEXTERN && name.Class != ir.PFUNC) m.Func = name.Func subst.ts.Vars[name] = m m.SetTypecheck(1) return m } // checkDictionary returns code that does runtime consistency checks // between the dictionary and the types it should contain. func (subst *subster) checkDictionary(name *ir.Name, targs []*types.Type) (code []ir.Node) { if false { return // checking turned off } // TODO: when moving to GCshape, this test will become harder. Call into // runtime to check the expected shape is correct? pos := name.Pos() // Convert dictionary to *[N]uintptr d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], name) d.SetTypecheck(1) d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(len(targs))).PtrTo(), d) d.SetTypecheck(1) types.CheckSize(d.Type().Elem()) // Check that each type entry in the dictionary is correct. for i, t := range targs { if t.HasShape() { // Check the concrete type, not the shape type. base.Fatalf("shape type in dictionary %s %+v\n", name.Sym().Name, t) } want := reflectdata.TypePtr(t) typed(types.Types[types.TUINTPTR], want) deref := ir.NewStarExpr(pos, d) typed(d.Type().Elem(), deref) idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), name) // TODO: what to set orig to? typed(types.Types[types.TUINTPTR], idx) got := ir.NewIndexExpr(pos, deref, idx) typed(types.Types[types.TUINTPTR], got) cond := ir.NewBinaryExpr(pos, ir.ONE, want, got) typed(types.Types[types.TBOOL], cond) panicArg := ir.NewNilExpr(pos) typed(types.NewInterface(types.LocalPkg, nil), panicArg) then := ir.NewUnaryExpr(pos, ir.OPANIC, panicArg) then.SetTypecheck(1) x := ir.NewIfStmt(pos, cond, []ir.Node{then}, nil) x.SetTypecheck(1) code = append(code, x) } return } // getDictionaryEntry gets the i'th entry in the dictionary dict. func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node { // Convert dictionary to *[N]uintptr // All entries in the dictionary are pointers. They all point to static data, though, so we // treat them as uintptrs so the GC doesn't need to keep track of them. d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], dict) d.SetTypecheck(1) d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(size)).PtrTo(), d) d.SetTypecheck(1) types.CheckSize(d.Type().Elem()) // Load entry i out of the dictionary. deref := ir.NewStarExpr(pos, d) typed(d.Type().Elem(), deref) idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), dict) // TODO: what to set orig to? typed(types.Types[types.TUINTPTR], idx) r := ir.NewIndexExpr(pos, deref, idx) typed(types.Types[types.TUINTPTR], r) return r } // getDictionaryType returns a *runtime._type from the dictionary entry i (which // refers to a type param or a derived type that uses type params). It uses the // specified dictionary dictParam, rather than the one in info.dictParam. func getDictionaryType(info *instInfo, dictParam *ir.Name, pos src.XPos, i int) ir.Node { if i < 0 || i >= info.startSubDict { base.Fatalf(fmt.Sprintf("bad dict index %d", i)) } r := getDictionaryEntry(pos, info.dictParam, i, info.startSubDict) // change type of retrieved dictionary entry to *byte, which is the // standard typing of a *runtime._type in the compiler typed(types.Types[types.TUINT8].PtrTo(), r) return r } // node is like DeepCopy(), but substitutes ONAME nodes based on subst.ts.vars, 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.ts.Typ(x.Type())) case ir.ONAME: if v := subst.ts.Vars[x.(*ir.Name)]; v != nil { return v } return x case ir.ONONAME: // This handles the identifier in a type switch guard fallthrough 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) _, isKeyExpr := m.(*ir.KeyExpr) if !isCallExpr && !isStructKeyExpr && !isKeyExpr && 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.ts.Typ(x.Type())) } } for i, de := range subst.info.gfInfo.subDictCalls { if de == x { // Remember the dictionary entry associated with this // node in the instantiated function // TODO: make sure this remains correct with respect to the // transformations below. subst.info.dictEntryMap[m] = subst.info.startSubDict + i break } } ir.EditChildren(m, edit) 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) if as.Y != nil { // transformAssign doesn't handle the case // of zeroing assignment of a dcl (rhs[0] is nil). 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)) } } 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 OMETHVALUE 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. mse := m.(*ir.SelectorExpr) if src := mse.X.Type(); src.IsShape() { // The only dot on a shape type value are methods. if mse.X.Op() == ir.OTYPE { // Method expression T.M m = subst.g.buildClosure2(subst, m, x) // No need for transformDot - buildClosure2 has already // transformed to OCALLINTER/ODOTINTER. } else { // Implement x.M as a conversion-to-bound-interface // 1) convert x to the bound interface // 2) call M on that interface gsrc := x.(*ir.SelectorExpr).X.Type() bound := gsrc.Bound() dst := bound if dst.HasTParam() { dst = subst.ts.Typ(dst) } if src.IsInterface() { // If type arg is an interface (unusual case), // we do a type assert to the type bound. mse.X = assertToBound(subst.info, subst.info.dictParam, m.Pos(), mse.X, bound, dst) } else { mse.X = convertUsingDictionary(subst.info, subst.info.dictParam, m.Pos(), mse.X, x, dst, gsrc) } transformDot(mse, false) } } else { transformDot(mse, 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(call) // CONVIFACE transformation was already done in node2 assert(m.Op() != ir.OCONVIFACE) case ir.OMETHVALUE, ir.OMETHEXPR: // 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.OAPPEND, ir.ODELETE, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: // 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.ODEREF, ir.OINDEX, ir.OINDEXMAP, ir.ORECV: // Transform a call that was delayed because of the // use of typeparam inside an expression that required // a pointer dereference, array indexing, map indexing, // or channel receive to compute function value. 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: // We're going to create a new closure from scratch, so clear m // to avoid using the ir.Copy by accident until we reassign it. m = nil 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.NewClosureFunc(oldfn.Pos(), subst.newf != nil) ir.NameClosure(newfn.OClosure, subst.newf) saveNewf := subst.newf ir.CurFunc = newfn subst.newf = newfn newfn.Dcl = subst.namelist(oldfn.Dcl) // Make a closure variable for the dictionary of the // containing function. cdict := ir.CaptureName(oldfn.Pos(), newfn, subst.info.dictParam) typed(types.Types[types.TUINTPTR], cdict) ir.FinishCaptureNames(oldfn.Pos(), saveNewf, newfn) newfn.ClosureVars = append(newfn.ClosureVars, subst.namelist(oldfn.ClosureVars)...) // Copy that closure variable to a local one. // Note: this allows the dictionary to be captured by child closures. // See issue 47723. ldict := ir.NewNameAt(x.Pos(), subst.info.gf.Sym().Pkg.Lookup(".dict")) typed(types.Types[types.TUINTPTR], ldict) ldict.Class = ir.PAUTO ldict.Curfn = newfn newfn.Dcl = append(newfn.Dcl, ldict) as := ir.NewAssignStmt(x.Pos(), ldict, cdict) as.SetTypecheck(1) newfn.Body.Append(as) // Create inst info for the instantiated closure. The dict // param is the closure variable for the dictionary of the // outer function. Since the dictionary is shared, use the // same entries for startSubDict, dictLen, dictEntryMap. cinfo := &instInfo{ fun: newfn, dictParam: ldict, gf: subst.info.gf, gfInfo: subst.info.gfInfo, startSubDict: subst.info.startSubDict, startItabConv: subst.info.startItabConv, dictLen: subst.info.dictLen, dictEntryMap: subst.info.dictEntryMap, } subst.g.instInfoMap[newfn.Nname.Sym()] = cinfo typed(subst.ts.Typ(oldfn.Nname.Type()), newfn.Nname) typed(newfn.Nname.Type(), newfn.OClosure) newfn.SetTypecheck(1) outerinfo := subst.info subst.info = cinfo // Make sure type of closure function is set before doing body. newfn.Body.Append(subst.list(oldfn.Body)...) subst.info = outerinfo subst.newf = saveNewf ir.CurFunc = saveNewf m = ir.UseClosure(newfn.OClosure, subst.g.target) m.(*ir.ClosureExpr).SetInit(subst.list(x.Init())) case ir.OCONVIFACE: x := x.(*ir.ConvExpr) // Note: x's argument is still typed as a type parameter. // m's argument now has an instantiated type. if x.X.Type().HasTParam() { m = convertUsingDictionary(subst.info, subst.info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, x, m.Type(), x.X.Type()) } case ir.ODOTTYPE, ir.ODOTTYPE2: if !x.Type().HasTParam() { break } dt := m.(*ir.TypeAssertExpr) var rt ir.Node if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() { ix := findDictType(subst.info, x.Type()) assert(ix >= 0) rt = getDictionaryType(subst.info, subst.info.dictParam, dt.Pos(), ix) } else { // nonempty interface to noninterface. Need an itab. ix := -1 for i, ic := range subst.info.gfInfo.itabConvs { if ic == x { ix = subst.info.startItabConv + i break } } assert(ix >= 0) rt = getDictionaryEntry(dt.Pos(), subst.info.dictParam, ix, subst.info.dictLen) } op := ir.ODYNAMICDOTTYPE if x.Op() == ir.ODOTTYPE2 { op = ir.ODYNAMICDOTTYPE2 } m = ir.NewDynamicTypeAssertExpr(dt.Pos(), op, dt.X, rt) m.SetType(dt.Type()) m.SetTypecheck(1) case ir.OCASE: if _, ok := x.(*ir.CommClause); ok { // This is not a type switch. TODO: Should we use an OSWITCH case here instead of OCASE? break } x := x.(*ir.CaseClause) m := m.(*ir.CaseClause) for i, c := range x.List { if c.Op() == ir.OTYPE && c.Type().HasTParam() { // Use a *runtime._type for the dynamic type. ix := findDictType(subst.info, c.Type()) assert(ix >= 0) dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), subst.info.dictParam, ix, subst.info.dictLen)) // For type switch from nonempty interfaces to non-interfaces, we need an itab as well. if !m.List[i].Type().IsInterface() { if _, ok := subst.info.gfInfo.type2switchType[c]; ok { // Type switch from nonempty interface. We need a *runtime.itab // for the dynamic type. ix := -1 for i, ic := range subst.info.gfInfo.itabConvs { if ic == c { ix = subst.info.startItabConv + i break } } assert(ix >= 0) dt.ITab = getDictionaryEntry(c.Pos(), subst.info.dictParam, ix, subst.info.dictLen) } } typed(m.List[i].Type(), dt) m.List[i] = dt } } } return m } return edit(n) } // findDictType looks for type t in the typeparams or derived types in the generic // function info.gfInfo. This will indicate the dictionary entry with the // correct concrete type for the associated instantiated function. func findDictType(info *instInfo, t *types.Type) int { for i, dt := range info.gfInfo.tparams { if dt == t { return i } } for i, dt := range info.gfInfo.derivedTypes { if types.Identical(dt, t) { return i + len(info.gfInfo.tparams) } } return -1 } // convertUsingDictionary converts value v from instantiated type src to an interface // type dst, by returning a new set of nodes that make use of a dictionary entry. src // is the generic (not shape) type, and gn is the original generic node of the // CONVIFACE node or XDOT node (for a bound method call) that is causing the // conversion. func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, gn ir.Node, dst, src *types.Type) ir.Node { assert(src.HasTParam()) assert(dst.IsInterface()) var rt ir.Node if !dst.IsEmptyInterface() { // We should have an itab entry in the dictionary. Using this itab // will be more efficient than converting to an empty interface first // and then type asserting to dst. ix := -1 for i, ic := range info.gfInfo.itabConvs { if ic == gn { ix = info.startItabConv + i break } } assert(ix >= 0) rt = getDictionaryEntry(pos, dictParam, ix, info.dictLen) } else if v.Type().IsInterface() { ta := ir.NewTypeAssertExpr(pos, v, nil) ta.SetType(dst) ta.SetTypecheck(1) return ta } else { ix := findDictType(info, src) assert(ix >= 0) // Load the actual runtime._type of the type parameter from the dictionary. rt = getDictionaryType(info, dictParam, pos, ix) } // Figure out what the data field of the interface will be. var data ir.Node if v.Type().IsInterface() { data = ir.NewUnaryExpr(pos, ir.OIDATA, v) } else { data = ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v) } typed(types.Types[types.TUNSAFEPTR], data) // Build an interface from the type and data parts. var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data) typed(dst, i) return i } func (subst *subster) namelist(l []*ir.Name) []*ir.Name { s := make([]*ir.Name, len(l)) for i, n := range l { s[i] = subst.localvar(n) 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 } // 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.ts.Typ(oldfields[j].Type) // A PPARAM field will be missing from dcl if its name is // unspecified or specified as "_". So, we compare the dcl sym // with the field sym (or sym of the field's Nname node). (Unnamed // results still have a name like ~r2 in their Nname node.) 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 || (oldfields[j].Nname != nil && dcl[i].Sym() == oldfields[j].Nname.Sym())) { newfields[j].Nname = dcl[i] i++ } } return newfields } // deref does a single deref of type t, if it is a pointer type. func deref(t *types.Type) *types.Type { if t.IsPtr() { return t.Elem() } return t } // markTypeUsed marks type t as used in order to help avoid dead-code elimination of // needed methods. func markTypeUsed(t *types.Type, lsym *obj.LSym) { if t.IsInterface() { // Mark all the methods of the interface as used. // TODO: we should really only mark the interface methods // that are actually called in the application. for i, _ := range t.AllMethods().Slice() { reflectdata.MarkUsedIfaceMethodIndex(lsym, t, i) } } else { // TODO: This is somewhat overkill, we really only need it // for types that are put into interfaces. reflectdata.MarkTypeUsedInInterface(t, lsym) } } // getDictionarySym returns the dictionary for the named generic function gf, which // is instantiated with the type arguments targs. func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) *types.Sym { if len(targs) == 0 { base.Fatalf("%s should have type arguments", gf.Sym().Name) } // Enforce that only concrete types can make it to here. for _, t := range targs { if t.HasShape() { panic(fmt.Sprintf("shape %+v in dictionary for %s", t, gf.Sym().Name)) } } // Get a symbol representing the dictionary. sym := typecheck.MakeDictSym(gf.Sym(), targs, isMeth) // Initialize the dictionary, if we haven't yet already. lsym := sym.Linksym() if len(lsym.P) > 0 { // We already started creating this dictionary and its lsym. return sym } info := g.getGfInfo(gf) infoPrint("=== Creating dictionary %v\n", sym.Name) off := 0 // Emit an entry for each targ (concrete type or gcshape). for _, t := range targs { infoPrint(" * %v\n", t) s := reflectdata.TypeLinksym(t) off = objw.SymPtr(lsym, off, s, 0) markTypeUsed(t, lsym) } subst := typecheck.Tsubster{ Tparams: info.tparams, Targs: targs, } // Emit an entry for each derived type (after substituting targs) for _, t := range info.derivedTypes { ts := subst.Typ(t) infoPrint(" - %v\n", ts) s := reflectdata.TypeLinksym(ts) off = objw.SymPtr(lsym, off, s, 0) markTypeUsed(ts, lsym) } // Emit an entry for each subdictionary (after substituting targs) for _, n := range info.subDictCalls { var sym *types.Sym switch n.Op() { case ir.OCALL: call := n.(*ir.CallExpr) if call.X.Op() == ir.OXDOT { var nameNode *ir.Name se := call.X.(*ir.SelectorExpr) if types.IsInterfaceMethod(se.Selection.Type) { // This is a method call enabled by a type bound. tmpse := ir.NewSelectorExpr(base.Pos, ir.OXDOT, se.X, se.Sel) tmpse = typecheck.AddImplicitDots(tmpse) tparam := tmpse.X.Type() assert(tparam.IsTypeParam()) recvType := targs[tparam.Index()] if recvType.IsInterface() || len(recvType.RParams()) == 0 { // No sub-dictionary entry is // actually needed, since the // type arg is not an // instantiated type that // will have generic methods. break } // This is a method call for an // instantiated type, so we need a // sub-dictionary. targs := recvType.RParams() genRecvType := recvType.OrigSym.Def.Type() nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name) sym = g.getDictionarySym(nameNode, targs, true) } else { // This is the case of a normal // method call on a generic type. nameNode = call.X.(*ir.SelectorExpr).Selection.Nname.(*ir.Name) subtargs := deref(call.X.(*ir.SelectorExpr).X.Type()).RParams() s2targs := make([]*types.Type, len(subtargs)) for i, t := range subtargs { s2targs[i] = subst.Typ(t) } sym = g.getDictionarySym(nameNode, s2targs, true) } } else { inst := call.X.(*ir.InstExpr) var nameNode *ir.Name var meth *ir.SelectorExpr var isMeth bool if meth, isMeth = inst.X.(*ir.SelectorExpr); isMeth { nameNode = meth.Selection.Nname.(*ir.Name) } else { nameNode = inst.X.(*ir.Name) } subtargs := typecheck.TypesOf(inst.Targs) for i, t := range subtargs { subtargs[i] = subst.Typ(t) } sym = g.getDictionarySym(nameNode, subtargs, isMeth) } case ir.OFUNCINST: inst := n.(*ir.InstExpr) nameNode := inst.X.(*ir.Name) subtargs := typecheck.TypesOf(inst.Targs) for i, t := range subtargs { subtargs[i] = subst.Typ(t) } sym = g.getDictionarySym(nameNode, subtargs, false) case ir.OXDOT: selExpr := n.(*ir.SelectorExpr) subtargs := deref(selExpr.X.Type()).RParams() s2targs := make([]*types.Type, len(subtargs)) for i, t := range subtargs { s2targs[i] = subst.Typ(t) } nameNode := selExpr.Selection.Nname.(*ir.Name) sym = g.getDictionarySym(nameNode, s2targs, true) default: assert(false) } if sym == nil { // Unused sub-dictionary entry, just emit 0. off = objw.Uintptr(lsym, off, 0) infoPrint(" - Unused subdict entry\n") } else { off = objw.SymPtr(lsym, off, sym.Linksym(), 0) infoPrint(" - Subdict %v\n", sym.Name) } } delay := &delayInfo{ gf: gf, targs: targs, sym: sym, off: off, } g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay) g.instTypeList = append(g.instTypeList, subst.InstTypeList...) return sym } // finalizeSyms finishes up all dictionaries on g.dictSymsToFinalize, by writing out // any needed LSyms for itabs. The itab lsyms create wrappers which need various // dictionaries and method instantiations to be complete, so, to avoid recursive // dependencies, we finalize the itab lsyms only after all dictionaries syms and // instantiations have been created. func (g *irgen) finalizeSyms() { for _, d := range g.dictSymsToFinalize { infoPrint("=== Finalizing dictionary %s\n", d.sym.Name) lsym := d.sym.Linksym() info := g.getGfInfo(d.gf) subst := typecheck.Tsubster{ Tparams: info.tparams, Targs: d.targs, } // Emit an entry for each itab for _, n := range info.itabConvs { var srctype, dsttype *types.Type switch n.Op() { case ir.OXDOT: se := n.(*ir.SelectorExpr) srctype = subst.Typ(se.X.Type()) dsttype = subst.Typ(se.X.Type().Bound()) found := false for i, m := range dsttype.AllMethods().Slice() { if se.Sel == m.Sym { // Mark that this method se.Sel is // used for the dsttype interface, so // it won't get deadcoded. reflectdata.MarkUsedIfaceMethodIndex(lsym, dsttype, i) found = true break } } assert(found) case ir.ODOTTYPE, ir.ODOTTYPE2: srctype = subst.Typ(n.(*ir.TypeAssertExpr).Type()) dsttype = subst.Typ(n.(*ir.TypeAssertExpr).X.Type()) case ir.OCONVIFACE: srctype = subst.Typ(n.(*ir.ConvExpr).X.Type()) dsttype = subst.Typ(n.Type()) case ir.OTYPE: srctype = subst.Typ(n.Type()) dsttype = subst.Typ(info.type2switchType[n]) default: base.Fatalf("itab entry with unknown op %s", n.Op()) } if srctype.IsInterface() { // No itab is wanted if src type is an interface. We // will use a type assert instead. d.off = objw.Uintptr(lsym, d.off, 0) infoPrint(" + Unused itab entry for %v\n", srctype) } else { itabLsym := reflectdata.ITabLsym(srctype, dsttype) d.off = objw.SymPtr(lsym, d.off, itabLsym, 0) infoPrint(" + Itab for (%v,%v)\n", srctype, dsttype) } } objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA) infoPrint("=== Finalized dictionary %s\n", d.sym.Name) g.instTypeList = append(g.instTypeList, subst.InstTypeList...) } g.dictSymsToFinalize = nil } func (g *irgen) getDictionaryValue(gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node { sym := g.getDictionarySym(gf, targs, isMeth) // Make (or reuse) a node referencing the dictionary symbol. var n *ir.Name if sym.Def != nil { n = sym.Def.(*ir.Name) } else { n = typecheck.NewName(sym) n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter n.SetTypecheck(1) n.Class = ir.PEXTERN sym.Def = n } // Return the address of the dictionary. np := typecheck.NodAddr(n) // Note: treat dictionary pointers as uintptrs, so they aren't pointers // with respect to GC. That saves on stack scanning work, write barriers, etc. // We can get away with it because dictionaries are global variables. // TODO: use a cast, or is typing directly ok? np.SetType(types.Types[types.TUINTPTR]) np.SetTypecheck(1) return np } // hasTParamNodes returns true if the type of any node in targs has a typeparam. func hasTParamNodes(targs []ir.Node) bool { for _, n := range targs { if n.Type().HasTParam() { return true } } return false } // hasTParamNodes returns true if any type in targs has a typeparam. func hasTParamTypes(targs []*types.Type) bool { for _, t := range targs { if t.HasTParam() { return true } } return false } // getGfInfo get information for a generic function - type params, derived generic // types, and subdictionaries. func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo { infop := g.gfInfoMap[gn.Sym()] if infop != nil { return infop } checkFetchBody(gn) var info gfInfo gf := gn.Func recv := gf.Type().Recv() if recv != nil { info.tparams = deref(recv.Type).RParams() } else { tparams := gn.Type().TParams().FieldSlice() info.tparams = make([]*types.Type, len(tparams)) for i, f := range tparams { info.tparams[i] = f.Type } } for _, t := range info.tparams { b := t.Bound() if b.HasTParam() { // If a type bound is parameterized (unusual case), then we // may need its derived type to do a type assert when doing a // bound call for a type arg that is an interface. addType(&info, nil, b) } } for _, n := range gf.Dcl { addType(&info, n, n.Type()) } if infoPrintMode { fmt.Printf(">>> GfInfo for %v\n", gn) for _, t := range info.tparams { fmt.Printf(" Typeparam %v\n", t) } } var visitFunc func(ir.Node) visitFunc = func(n ir.Node) { if n.Op() == ir.OFUNCINST && !n.(*ir.InstExpr).Implicit() { if hasTParamNodes(n.(*ir.InstExpr).Targs) { infoPrint(" Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X) info.subDictCalls = append(info.subDictCalls, n) } } else if n.Op() == ir.OXDOT && !n.(*ir.SelectorExpr).Implicit() && n.(*ir.SelectorExpr).Selection != nil && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 { if hasTParamTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) { if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE { infoPrint(" Closure&subdictionary required at generic meth expr %v\n", n) } else { infoPrint(" Closure&subdictionary required at generic meth value %v\n", n) } info.subDictCalls = append(info.subDictCalls, n) } } if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST { n.(*ir.CallExpr).X.(*ir.InstExpr).SetImplicit(true) if hasTParamNodes(n.(*ir.CallExpr).X.(*ir.InstExpr).Targs) { infoPrint(" Subdictionary at generic function/method call: %v - %v\n", n.(*ir.CallExpr).X.(*ir.InstExpr).X, n) info.subDictCalls = append(info.subDictCalls, n) } } if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT && n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil && len(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 { n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true) if hasTParamTypes(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) { infoPrint(" Subdictionary at generic method call: %v\n", n) info.subDictCalls = append(info.subDictCalls, n) } } if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT && n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil && deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).IsTypeParam() { n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true) infoPrint(" Optional subdictionary at generic bound call: %v\n", n) info.subDictCalls = append(info.subDictCalls, n) } if n.Op() == ir.OCONVIFACE && n.Type().IsInterface() && !n.Type().IsEmptyInterface() && n.(*ir.ConvExpr).X.Type().HasTParam() { infoPrint(" Itab for interface conv: %v\n", n) info.itabConvs = append(info.itabConvs, n) } if n.Op() == ir.OXDOT && n.(*ir.SelectorExpr).X.Type().IsTypeParam() { infoPrint(" Itab for bound call: %v\n", n) info.itabConvs = append(info.itabConvs, n) } if (n.Op() == ir.ODOTTYPE || n.Op() == ir.ODOTTYPE2) && !n.(*ir.TypeAssertExpr).Type().IsInterface() && !n.(*ir.TypeAssertExpr).X.Type().IsEmptyInterface() { infoPrint(" Itab for dot type: %v\n", n) info.itabConvs = append(info.itabConvs, n) } if n.Op() == ir.OCLOSURE { // Visit the closure body and add all relevant entries to the // dictionary of the outer function (closure will just use // the dictionary of the outer function). for _, n1 := range n.(*ir.ClosureExpr).Func.Body { ir.Visit(n1, visitFunc) } } if n.Op() == ir.OSWITCH && n.(*ir.SwitchStmt).Tag != nil && n.(*ir.SwitchStmt).Tag.Op() == ir.OTYPESW && !n.(*ir.SwitchStmt).Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() { for _, cc := range n.(*ir.SwitchStmt).Cases { for _, c := range cc.List { if c.Op() == ir.OTYPE && c.Type().HasTParam() { // Type switch from a non-empty interface - might need an itab. infoPrint(" Itab for type switch: %v\n", c) info.itabConvs = append(info.itabConvs, c) if info.type2switchType == nil { info.type2switchType = map[ir.Node]*types.Type{} } info.type2switchType[c] = n.(*ir.SwitchStmt).Tag.(*ir.TypeSwitchGuard).X.Type() } } } } addType(&info, n, n.Type()) } for _, stmt := range gf.Body { ir.Visit(stmt, visitFunc) } if infoPrintMode { for _, t := range info.derivedTypes { fmt.Printf(" Derived type %v\n", t) } fmt.Printf(">>> Done Gfinfo\n") } g.gfInfoMap[gn.Sym()] = &info return &info } // addType adds t to info.derivedTypes if it is parameterized type (which is not // just a simple type param) that is different from any existing type on // info.derivedTypes. func addType(info *gfInfo, n ir.Node, t *types.Type) { if t == nil || !t.HasTParam() { return } if t.IsTypeParam() && t.Underlying() == t { return } if t.Kind() == types.TFUNC && n != nil && (t.Recv() != nil || n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) { // Don't use the type of a named generic function or method, // since that is parameterized by other typeparams. // (They all come from arguments of a FUNCINST node.) return } if doubleCheck && !parameterizedBy(t, info.tparams) { base.Fatalf("adding type with invalid parameters %+v", t) } if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() { // Multiple return values are not a relevant new type (?). return } // Ignore a derived type we've already added. for _, et := range info.derivedTypes { if types.Identical(t, et) { return } } info.derivedTypes = append(info.derivedTypes, t) } // parameterizedBy returns true if t is parameterized by (at most) params. func parameterizedBy(t *types.Type, params []*types.Type) bool { return parameterizedBy1(t, params, map[*types.Type]bool{}) } func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Type]bool) bool { if visited[t] { return true } visited[t] = true if t.Sym() != nil && len(t.RParams()) > 0 { // This defined type is instantiated. Check the instantiating types. for _, r := range t.RParams() { if !parameterizedBy1(r, params, visited) { return false } } return true } switch t.Kind() { case types.TTYPEPARAM: // Check if t is one of the allowed parameters in scope. for _, p := range params { if p == t { return true } } // Couldn't find t in the list of allowed parameters. return false case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN: return parameterizedBy1(t.Elem(), params, visited) case types.TMAP: return parameterizedBy1(t.Key(), params, visited) && parameterizedBy1(t.Elem(), params, visited) case types.TFUNC: return parameterizedBy1(t.TParams(), params, visited) && parameterizedBy1(t.Recvs(), params, visited) && parameterizedBy1(t.Params(), params, visited) && parameterizedBy1(t.Results(), params, visited) case types.TSTRUCT: for _, f := range t.Fields().Slice() { if !parameterizedBy1(f.Type, params, visited) { return false } } return true case types.TINTER: for _, f := range t.Methods().Slice() { if !parameterizedBy1(f.Type, params, visited) { return false } } return true case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64, types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64, types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128: return true case types.TUNION: for i := 0; i < t.NumTerms(); i++ { tt, _ := t.Term(i) if !parameterizedBy1(tt, params, visited) { return false } } return true default: base.Fatalf("bad type kind %+v", t) return true } } // startClosures starts creation of a closure that has the function type typ. It // creates all the formal params and results according to the type typ. On return, // the body and closure variables of the closure must still be filled in, and // ir.UseClosure() called. func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*types.Field, []*types.Field) { // Make a new internal function. fn := ir.NewClosureFunc(pos, outer != nil) ir.NameClosure(fn.OClosure, outer) // Build formal argument and return lists. var formalParams []*types.Field // arguments of closure var formalResults []*types.Field // returns of closure for i := 0; i < typ.NumParams(); i++ { t := typ.Params().Field(i).Type arg := ir.NewNameAt(pos, typecheck.LookupNum("a", i)) arg.Class = ir.PPARAM typed(t, arg) arg.Curfn = fn fn.Dcl = append(fn.Dcl, arg) f := types.NewField(pos, arg.Sym(), t) f.Nname = arg formalParams = append(formalParams, f) } for i := 0; i < typ.NumResults(); i++ { t := typ.Results().Field(i).Type result := ir.NewNameAt(pos, typecheck.LookupNum("r", i)) // TODO: names not needed? result.Class = ir.PPARAMOUT typed(t, result) result.Curfn = fn fn.Dcl = append(fn.Dcl, result) f := types.NewField(pos, result.Sym(), t) f.Nname = result formalResults = append(formalResults, f) } // Build an internal function with the right signature. closureType := types.NewSignature(typ.Pkg(), nil, nil, formalParams, formalResults) typed(closureType, fn.Nname) typed(typ, fn.OClosure) fn.SetTypecheck(1) return fn, formalParams, formalResults } // assertToBound returns a new node that converts a node rcvr with interface type to // the 'dst' interface type. bound is the unsubstituted form of dst. func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, bound, dst *types.Type) ir.Node { if bound.HasTParam() { ix := findDictType(info, bound) assert(ix >= 0) rt := getDictionaryType(info, dictVar, pos, ix) rcvr = ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, rcvr, rt) typed(dst, rcvr) } else { rcvr = ir.NewTypeAssertExpr(pos, rcvr, nil) typed(bound, rcvr) } return rcvr } // buildClosure2 makes a closure to implement a method expression m (generic form x) // which has a shape type as receiver. If the receiver is exactly a shape (i.e. from // a typeparam), then the body of the closure converts m.X (the receiver) to the // interface bound type, and makes an interface call with the remaining arguments. // // The returned closure is fully substituted and has already had any needed // transformations done. func (g *irgen) buildClosure2(subst *subster, m, x ir.Node) ir.Node { outer := subst.newf info := subst.info pos := m.Pos() typ := m.Type() // type of the closure fn, formalParams, formalResults := startClosure(pos, outer, typ) // Capture dictionary calculated in the outer function dictVar := ir.CaptureName(pos, fn, info.dictParam) typed(types.Types[types.TUINTPTR], dictVar) // Build arguments to call inside the closure. var args []ir.Node for i := 0; i < typ.NumParams(); i++ { args = append(args, formalParams[i].Nname.(*ir.Name)) } // Build call itself. This involves converting the first argument to the // bound type (an interface) using the dictionary, and then making an // interface call with the remaining arguments. var innerCall ir.Node rcvr := args[0] args = args[1:] assert(m.(*ir.SelectorExpr).X.Type().IsShape()) gsrc := x.(*ir.SelectorExpr).X.Type() bound := gsrc.Bound() dst := bound if dst.HasTParam() { dst = subst.ts.Typ(bound) } if m.(*ir.SelectorExpr).X.Type().IsInterface() { // If type arg is an interface (unusual case), we do a type assert to // the type bound. rcvr = assertToBound(info, dictVar, pos, rcvr, bound, dst) } else { rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, x, dst, gsrc) } dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, x.(*ir.SelectorExpr).Sel) dot.Selection = typecheck.Lookdot1(dot, dot.Sel, dot.X.Type(), dot.X.Type().AllMethods(), 1) // Do a type substitution on the generic bound, in case it is parameterized. typed(subst.ts.Typ(x.(*ir.SelectorExpr).Selection.Type), dot) innerCall = ir.NewCallExpr(pos, ir.OCALLINTER, dot, args) t := m.Type() if t.NumResults() == 0 { innerCall.SetTypecheck(1) } else if t.NumResults() == 1 { typed(t.Results().Field(0).Type, innerCall) } else { typed(t.Results(), innerCall) } if len(formalResults) > 0 { innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall}) innerCall.SetTypecheck(1) } fn.Body = []ir.Node{innerCall} // We're all done with the captured dictionary ir.FinishCaptureNames(pos, outer, fn) // Do final checks on closure and return it. return ir.UseClosure(fn.OClosure, g.target) }