cmd/compile: support new fully-inst types referenced during inlining
Modify the phase for creating needed function/method instantiations and
modifying functions to use those instantiations, so that the phase is
self-contained and can be called again after inlining. This is to deal
with the issue that inlining may reveal new fully-instantiated types
whose methods must be instantiated.
With this change, we have an extra phase for instantiation after
inlining, to take care of the new fully-instantiated types that have
shown up during inlining. We call inline.InlineCalls() for any new
instantiated functions that are created.
Change-Id: I4ddf0b1907e5f1f7d45891db7876455a99381133
Reviewed-on: https://go-review.googlesource.com/c/go/+/352870
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Alexander Rakoczy <alex@golang.org>
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 8ddef67..74b2157 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -244,6 +244,11 @@
base.Timer.Start("fe", "inlining")
if base.Flag.LowerL != 0 {
inline.InlinePackage()
+ // If any new fully-instantiated types were referenced during
+ // inlining, we need to create needed instantiations.
+ if len(typecheck.GetInstTypeList()) > 0 {
+ noder.BuildInstantiations(false)
+ }
}
noder.MakeWrappers(typecheck.Target) // must happen after inlining
diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go
index 982e811..e20939d 100644
--- a/src/cmd/compile/internal/noder/irgen.go
+++ b/src/cmd/compile/internal/noder/irgen.go
@@ -158,16 +158,6 @@
// types which we need to finish, by doing g.fillinMethods.
typesToFinalize []*typeDelayInfo
- dnum int // for generating unique dictionary variables
-
- // Map from a name of function that been instantiated to information about
- // its instantiated function (including dictionary format).
- instInfoMap map[*types.Sym]*instInfo
-
- // dictionary syms which we need to finish, by writing out any itabconv
- // entries.
- dictSymsToFinalize []*delayInfo
-
// True when we are compiling a top-level generic function or method. Use to
// avoid adding closures of generic functions/methods to the target.Decls
// list.
@@ -180,6 +170,23 @@
curDecl string
}
+// genInst has the information for creating needed instantiations and modifying
+// functions to use instantiations.
+type genInst struct {
+ dnum int // for generating unique dictionary variables
+
+ // Map from the names of all instantiations to information about the
+ // instantiations.
+ instInfoMap map[*types.Sym]*instInfo
+
+ // Dictionary syms which we need to finish, by writing out any itabconv
+ // entries.
+ dictSymsToFinalize []*delayInfo
+
+ // New instantiations created during this round of buildInstantiations().
+ newInsts []ir.Node
+}
+
func (g *irgen) later(fn func()) {
g.laterFuncs = append(g.laterFuncs, fn)
}
@@ -308,8 +315,9 @@
typecheck.DeclareUniverse()
- // Create any needed stencils of generic functions
- g.stencil()
+ // Create any needed instantiations of generic functions and transform
+ // existing and new functions to use those instantiations.
+ BuildInstantiations(true)
// Remove all generic functions from g.target.Decl, since they have been
// used for stenciling, but don't compile. Generic functions will already
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
index 447fe8a..cfa90e4 100644
--- a/src/cmd/compile/internal/noder/stencil.go
+++ b/src/cmd/compile/internal/noder/stencil.go
@@ -9,6 +9,7 @@
import (
"cmd/compile/internal/base"
+ "cmd/compile/internal/inline"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/reflectdata"
@@ -37,207 +38,54 @@
}
}
-// 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)
+var geninst genInst
+func BuildInstantiations(preinliningMainScan bool) {
+ if geninst.instInfoMap == nil {
+ geninst.instInfoMap = make(map[*types.Sym]*instInfo)
+ }
+ geninst.buildInstantiations(preinliningMainScan)
+}
+
+// buildInstantiations scans functions for generic function calls and methods, and
+// creates the required instantiations. It also creates instantiated methods for all
+// fully-instantiated generic types that have been encountered already or new ones
+// that are encountered during the instantiation process. If preinliningMainScan is
+// true, it scans all declarations in typecheck.Target.Decls first, before scanning
+// any new instantiations created. If preinliningMainScan is false, we do not scan
+// any existing decls - we only scan method instantiations for any new
+// fully-instantiated types that we saw during inlining.
+func (g *genInst) buildInstantiations(preinliningMainScan bool) {
// 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
+ if preinliningMainScan {
+ n := len(typecheck.Target.Decls)
+ for i := 0; i < n; i++ {
+ g.scanForGenCalls(typecheck.Target.Decls[i])
}
+ }
- // 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).fun
- 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, nil)
-
- // 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, nil)
-
- st := g.getInstantiation(gf, targs, true).fun
- 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()
+ // Scan all new instantiations created due to g.instantiateMethods() and the
+ // scan of current decls (if done). This loop purposely runs until no new
+ // instantiations are created.
+ for i := 0; i < len(g.newInsts); i++ {
+ g.scanForGenCalls(g.newInsts[i])
}
g.finalizeSyms()
// All the instantiations and dictionaries have been created. Now go through
- // each instantiation and transform the various operations that need to make
+ // each new instantiation and transform the various operations that need to make
// use of their dictionary.
- l := len(g.instInfoMap)
- for _, info := range g.instInfoMap {
+ l := len(g.newInsts)
+ for _, fun := range g.newInsts {
+ info := g.instInfoMap[fun.Sym()]
g.dictPass(info)
+ if !preinliningMainScan {
+ // Prepare for the round of inlining below.
+ inline.CanInline(fun.(*ir.Func))
+ }
if doubleCheck {
ir.Visit(info.fun, func(n ir.Node) {
if n.Op() != ir.OCONVIFACE {
@@ -255,13 +103,198 @@
ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun)
}
}
- assert(l == len(g.instInfoMap))
+ if !preinliningMainScan {
+ // Extra round of inlining for the new instantiations (only if
+ // preinliningMainScan is false, which means we have already done the
+ // main round of inlining)
+ for _, fun := range g.newInsts {
+ inline.InlineCalls(fun.(*ir.Func))
+ }
+ }
+ assert(l == len(g.newInsts))
+ g.newInsts = nil
+}
+
+// scanForGenCalls scans a single function (or global assignment), looking for
+// references to generic functions/methods. At each such reference, it creates any
+// required instantiation and transforms the reference.
+func (g *genInst) scanForGenCalls(decl ir.Node) {
+ switch decl.Op() {
+ case ir.ODCLFUNC:
+ if decl.Type().HasTParam() {
+ // Skip any generic functions
+ return
+ }
+ // 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.
+ return
+ }
+
+ // Search for any function references using generic function/methods. 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).fun
+ 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, nil)
+
+ // 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, nil)
+
+ st := g.getInstantiation(gf, targs, true).fun
+ 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()
}
// buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR/OMETHVALUE
// 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 {
+func (g *genInst) 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
@@ -423,8 +456,8 @@
rcvrVar.Defn = rcvrAssign
if outer == nil {
rcvrVar.Class = ir.PEXTERN
- g.target.Decls = append(g.target.Decls, rcvrAssign)
- g.target.Externs = append(g.target.Externs, rcvrVar)
+ typecheck.Target.Decls = append(typecheck.Target.Decls, rcvrAssign)
+ typecheck.Target.Externs = append(typecheck.Target.Externs, rcvrVar)
} else {
rcvrVar.Class = ir.PAUTO
rcvrVar.Curfn = outer
@@ -496,7 +529,7 @@
ir.FinishCaptureNames(pos, outer, fn)
// Make a closure referencing our new internal function.
- c := ir.UseClosure(fn.OClosure, g.target)
+ c := ir.UseClosure(fn.OClosure, typecheck.Target)
var init []ir.Node
if outer != nil {
init = append(init, dictAssign)
@@ -510,12 +543,13 @@
// instantiateMethods instantiates all the methods (and associated dictionaries) of
// all fully-instantiated generic types that have been added to typecheck.instTypeList.
// It continues until no more types are added to typecheck.instTypeList.
-func (g *irgen) instantiateMethods() {
+func (g *genInst) instantiateMethods() {
for {
instTypeList := typecheck.GetInstTypeList()
if len(instTypeList) == 0 {
break
}
+ typecheck.ClearInstTypeList()
for _, typ := range instTypeList {
assert(!typ.HasShape())
// Mark runtime type as needed, since this ensures that the
@@ -548,7 +582,7 @@
}
// 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) {
+func (g *genInst) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) {
if meth, ok := inst.X.(*ir.SelectorExpr); ok {
return meth.Selection.Nname.(*ir.Name), true
} else {
@@ -561,7 +595,7 @@
// 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) {
+func (g *genInst) 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 {
@@ -603,7 +637,7 @@
// 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) *instInfo {
+func (g *genInst) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *instInfo {
checkFetchBody(nameNode)
// Convert any non-shape type arguments to their shape, so we can reduce the
@@ -645,7 +679,8 @@
// This ensures that the linker drops duplicates of this instantiation.
// All just works!
st.SetDupok(true)
- g.target.Decls = append(g.target.Decls, st)
+ typecheck.Target.Decls = append(typecheck.Target.Decls, st)
+ g.newInsts = append(g.newInsts, st)
}
return info
}
@@ -653,7 +688,7 @@
// 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
+ g *genInst
isMethod bool // If a method is being instantiated
newf *ir.Func // Func node for the new stenciled function
ts typecheck.Tsubster
@@ -669,7 +704,7 @@
// 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.tparamToBound.
-func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func {
+func (g *genInst) 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
@@ -1170,7 +1205,8 @@
subst.newf = saveNewf
ir.CurFunc = saveNewf
- m = ir.UseClosure(newfn.OClosure, subst.g.target)
+ m = ir.UseClosure(newfn.OClosure, typecheck.Target)
+ subst.g.newInsts = append(subst.g.newInsts, m.(*ir.ClosureExpr).Func)
m.(*ir.ClosureExpr).SetInit(subst.list(x.Init()))
}
@@ -1182,7 +1218,7 @@
// dictPass takes a function instantiation and does the transformations on the
// operations that need to make use of the dictionary param.
-func (g *irgen) dictPass(info *instInfo) {
+func (g *genInst) dictPass(info *instInfo) {
savef := ir.CurFunc
ir.CurFunc = info.fun
@@ -1503,7 +1539,7 @@
// 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 {
+func (g *genInst) 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)
}
@@ -1678,7 +1714,7 @@
// 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() {
+func (g *genInst) finalizeSyms() {
for _, d := range g.dictSymsToFinalize {
infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
@@ -1744,7 +1780,7 @@
g.dictSymsToFinalize = nil
}
-func (g *irgen) getDictionaryValue(gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node {
+func (g *genInst) 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.
@@ -1792,7 +1828,7 @@
// getInstInfo get the dictionary format for a function instantiation- type params, derived
// types, and needed subdictionaries and itabs.
-func (g *irgen) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) {
+func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) {
info := instInfo.dictInfo
info.shapeParams = shapes
@@ -2100,7 +2136,7 @@
//
// The returned closure is fully substituted and has already had any needed
// transformations done.
-func (g *irgen) buildClosure2(info *instInfo, m ir.Node) ir.Node {
+func (g *genInst) buildClosure2(info *instInfo, m ir.Node) ir.Node {
outer := info.fun
pos := m.Pos()
typ := m.Type() // type of the closure
@@ -2155,5 +2191,5 @@
ir.FinishCaptureNames(pos, outer, fn)
// Do final checks on closure and return it.
- return ir.UseClosure(fn.OClosure, g.target)
+ return ir.UseClosure(fn.OClosure, typecheck.Target)
}
diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go
index 6288d15..b3fc745 100644
--- a/src/cmd/compile/internal/typecheck/subr.go
+++ b/src/cmd/compile/internal/typecheck/subr.go
@@ -1007,19 +1007,22 @@
// List of newly fully-instantiated types who should have their methods generated.
var instTypeList []*types.Type
-// NeedInstType adds a new fully-instantied type to instTypeList.
+// NeedInstType adds a new fully-instantiated type to instTypeList.
func NeedInstType(t *types.Type) {
instTypeList = append(instTypeList, t)
}
-// GetInstTypeList returns the current contents of instTypeList, and sets
-// instTypeList to nil.
+// GetInstTypeList returns the current contents of instTypeList.
func GetInstTypeList() []*types.Type {
r := instTypeList
- instTypeList = nil
return r
}
+// ClearInstTypeList clears the contents of instTypeList.
+func ClearInstTypeList() {
+ instTypeList = nil
+}
+
// General type substituter, for replacing typeparams with type args.
type Tsubster struct {
Tparams []*types.Type
diff --git a/test/typeparam/geninline.dir/a.go b/test/typeparam/geninline.dir/a.go
new file mode 100644
index 0000000..fe5ba22
--- /dev/null
+++ b/test/typeparam/geninline.dir/a.go
@@ -0,0 +1,56 @@
+// 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.
+
+package a
+
+type IVal[T comparable] interface {
+ check(want T)
+}
+
+type Val[T comparable] struct {
+ val T
+}
+
+//go:noinline
+func (l *Val[T]) check(want T) {
+ if l.val != want {
+ panic("hi")
+ }
+}
+
+func Test1() {
+ var l Val[int]
+ if l.val != 0 {
+ panic("hi")
+ }
+ _ = IVal[int](&l)
+}
+
+func Test2() {
+ var l Val[float64]
+ l.val = 3.0
+ l.check(float64(3))
+ _ = IVal[float64](&l)
+}
+
+type privateVal[T comparable] struct {
+ val T
+}
+
+//go:noinline
+func (l *privateVal[T]) check(want T) {
+ if l.val != want {
+ panic("hi")
+ }
+}
+
+type Outer struct {
+ val privateVal[string]
+}
+
+func Test3() {
+ var o Outer
+ o.val.check("")
+ _ = IVal[string](&o.val)
+}
diff --git a/test/typeparam/geninline.dir/main.go b/test/typeparam/geninline.dir/main.go
new file mode 100644
index 0000000..6dc36ba
--- /dev/null
+++ b/test/typeparam/geninline.dir/main.go
@@ -0,0 +1,16 @@
+// 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.
+
+package main
+
+import "a"
+
+// Testing inlining of functions that refer to instantiated exported and non-exported
+// generic types.
+
+func main() {
+ a.Test1()
+ a.Test2()
+ a.Test3()
+}
diff --git a/test/typeparam/geninline.go b/test/typeparam/geninline.go
new file mode 100644
index 0000000..76930e5
--- /dev/null
+++ b/test/typeparam/geninline.go
@@ -0,0 +1,7 @@
+// rundir -G=3
+
+// 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.
+
+package ignored