diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/stencil.go')
-rw-r--r-- | src/cmd/compile/internal/noder/stencil.go | 182 |
1 files changed, 139 insertions, 43 deletions
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index 69461a8190..fb1bbfedc8 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -27,19 +27,37 @@ func (g *irgen) stencil() { // functions calling other generic functions. for i := 0; i < len(g.target.Decls); i++ { decl := g.target.Decls[i] - if decl.Op() != ir.ODCLFUNC || decl.Type().NumTParams() > 0 { - // Skip any non-function declarations and skip generic functions + + // 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 + } + + case ir.OAS: + + case ir.OAS2: + + default: continue } - // For each non-generic function, search for any function calls using - // generic function instantiations. (We don't yet handle generic - // function instantiations that are not immediately called.) - // Then create the needed instantiated function if it hasn't been - // created yet, and change to calling that function directly. - f := decl.(*ir.Func) + // 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 - ir.VisitList(f.Body, func(n ir.Node) { + 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.OCALLFUNC || n.(*ir.CallExpr).X.Op() != ir.OFUNCINST { return } @@ -47,19 +65,7 @@ func (g *irgen) stencil() { // instantiation. call := n.(*ir.CallExpr) inst := call.X.(*ir.InstExpr) - sym := makeInstName(inst) - //fmt.Printf("Found generic func call in %v to %v\n", f, s) - st := g.target.Stencils[sym] - if st == nil { - // If instantiation doesn't exist yet, create it and add - // to the list of decls. - st = genericSubst(sym, inst) - 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) - } - } + st := g.getInstantiation(inst) // Replace the OFUNCINST with a direct reference to the // new stenciled function call.X = st.Nname @@ -76,6 +82,26 @@ func (g *irgen) stencil() { } 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.getInstantiation(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) } @@ -83,18 +109,39 @@ func (g *irgen) stencil() { } -// makeInstName makes the unique name for a stenciled generic function, based on -// the name of the function and the types of the type params. -func makeInstName(inst *ir.InstExpr) *types.Sym { - b := bytes.NewBufferString("#") +// getInstantiation gets the instantiated function corresponding to inst. If the +// instantiated function is not already cached, then it calls genericStub to +// create the new instantiation. +func (g *irgen) getInstantiation(inst *ir.InstExpr) *ir.Func { + var sym *types.Sym if meth, ok := inst.X.(*ir.SelectorExpr); ok { // Write the name of the generic method, including receiver type - b.WriteString(meth.Selection.Nname.Sym().Name) + sym = makeInstName(meth.Selection.Nname.Sym(), inst.Targs) } else { - b.WriteString(inst.X.(*ir.Name).Name().Sym().Name) + sym = makeInstName(inst.X.(*ir.Name).Name().Sym(), inst.Targs) } + //fmt.Printf("Found generic func call in %v to %v\n", f, s) + 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, inst) + 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, based on +// the name of the function and the targs. +func makeInstName(fnsym *types.Sym, targs []ir.Node) *types.Sym { + b := bytes.NewBufferString("#") + b.WriteString(fnsym.Name) b.WriteString("[") - for i, targ := range inst.Targs { + for i, targ := range targs { if i > 0 { b.WriteString(",") } @@ -107,6 +154,7 @@ func makeInstName(inst *ir.InstExpr) *types.Sym { // 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 newf *ir.Func // Func node for the new stenciled function tparams []*types.Field targs []ir.Node @@ -121,7 +169,7 @@ type subster struct { // inst. 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 genericSubst(name *types.Sym, inst *ir.InstExpr) *ir.Func { +func (g *irgen) genericSubst(name *types.Sym, inst *ir.InstExpr) *ir.Func { var nameNode *ir.Name var tparams []*types.Field if selExpr, ok := inst.X.(*ir.SelectorExpr); ok { @@ -148,6 +196,7 @@ func genericSubst(name *types.Sym, inst *ir.InstExpr) *ir.Func { name.Def = newf.Nname subst := &subster{ + g: g, newf: newf, tparams: tparams, targs: inst.Targs, @@ -198,6 +247,9 @@ func (subst *subster) node(n ir.Node) ir.Node { return v } m := ir.NewNameAt(name.Pos(), name.Sym()) + if name.IsClosureVar() { + m.SetIsClosureVar(true) + } t := x.Type() newt := subst.typ(t) m.SetType(newt) @@ -219,10 +271,12 @@ func (subst *subster) node(n ir.Node) ir.Node { // t can be nil only if this is a call that has no // return values, so allow that and otherwise give // an error. - if _, isCallExpr := m.(*ir.CallExpr); !isCallExpr { + _, isCallExpr := m.(*ir.CallExpr) + _, isStructKeyExpr := m.(*ir.StructKeyExpr) + if !isCallExpr && !isStructKeyExpr { base.Fatalf(fmt.Sprintf("Nil type for %v", x)) } - } else { + } else if x.Op() != ir.OCLOSURE { m.SetType(subst.typ(x.Type())) } } @@ -270,14 +324,27 @@ func (subst *subster) node(n ir.Node) ir.Node { if oldfn.ClosureCalled() { newfn.SetClosureCalled(true) } + newfn.SetIsHiddenClosure(true) m.(*ir.ClosureExpr).Func = newfn - newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), oldfn.Nname.Sym()) - newfn.Nname.SetType(oldfn.Nname.Type()) - newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype) + newsym := makeInstName(oldfn.Nname.Sym(), subst.targs) + 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 + subst.newf = newfn + newfn.Dcl = subst.namelist(oldfn.Dcl) + newfn.ClosureVars = subst.namelist(oldfn.ClosureVars) newfn.Body = subst.list(oldfn.Body) - // Make shallow copy of the Dcl and ClosureVar slices - newfn.Dcl = append([]*ir.Name(nil), oldfn.Dcl...) - newfn.ClosureVars = append([]*ir.Name(nil), oldfn.ClosureVars...) + subst.newf = saveNewf + + // Set Ntype for now to be compatible with later parts of compiler + newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype) + typed(subst.typ(oldfn.Nname.Type()), newfn.Nname) + newfn.SetTypecheck(1) + subst.g.target.Decls = append(subst.g.target.Decls, newfn) } return m } @@ -285,6 +352,20 @@ func (subst *subster) node(n ir.Node) ir.Node { 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 { @@ -293,7 +374,9 @@ func (subst *subster) list(l []ir.Node) []ir.Node { return s } -// tstruct substitutes type params in a structure type +// 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. func (subst *subster) tstruct(t *types.Type) *types.Type { if t.NumFields() == 0 { return t @@ -301,7 +384,7 @@ func (subst *subster) tstruct(t *types.Type) *types.Type { var newfields []*types.Field for i, f := range t.Fields().Slice() { t2 := subst.typ(f.Type) - if t2 != f.Type && newfields == nil { + 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) @@ -309,6 +392,12 @@ func (subst *subster) tstruct(t *types.Type) *types.Type { } if newfields != nil { 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 + newfields[i].Nname = subst.vars[f.Nname.(*ir.Name)] + } } } if newfields != nil { @@ -319,14 +408,14 @@ func (subst *subster) tstruct(t *types.Type) *types.Type { } // instTypeName creates a name for an instantiated type, based on the type args -func instTypeName(name string, targs []ir.Node) string { +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.Type().String()) + b.WriteString(targ.String()) } b.WriteByte(']') return b.String() @@ -415,10 +504,17 @@ func (subst *subster) typ(t *types.Type) *types.Type { // Since we've substituted types, we also need to change // the defined name of the type, by removing the old types // (in brackets) from the name, and adding the new types. + + // Translate the type params for this type according to + // the tparam/targs mapping of the function. + neededTargs := make([]*types.Type, len(t.RParams)) + for i, rparam := range t.RParams { + neededTargs[i] = subst.typ(rparam) + } oldname := t.Sym().Name i := strings.Index(oldname, "[") oldname = oldname[:i] - sym := t.Sym().Pkg.Lookup(instTypeName(oldname, subst.targs)) + sym := t.Sym().Pkg.Lookup(instTypeName(oldname, neededTargs)) if sym.Def != nil { // We've already created this instantiated defined type. return sym.Def.Type() |