mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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>
This commit is contained in:
parent
fad4a16fd4
commit
a80e53ec43
7 changed files with 355 additions and 224 deletions
|
|
@ -244,6 +244,11 @@ func Main(archInit func(*ssagen.ArchInfo)) {
|
||||||
base.Timer.Start("fe", "inlining")
|
base.Timer.Start("fe", "inlining")
|
||||||
if base.Flag.LowerL != 0 {
|
if base.Flag.LowerL != 0 {
|
||||||
inline.InlinePackage()
|
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
|
noder.MakeWrappers(typecheck.Target) // must happen after inlining
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -158,16 +158,6 @@ type irgen struct {
|
||||||
// types which we need to finish, by doing g.fillinMethods.
|
// types which we need to finish, by doing g.fillinMethods.
|
||||||
typesToFinalize []*typeDelayInfo
|
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
|
// 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
|
// avoid adding closures of generic functions/methods to the target.Decls
|
||||||
// list.
|
// list.
|
||||||
|
|
@ -180,6 +170,23 @@ type irgen struct {
|
||||||
curDecl string
|
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()) {
|
func (g *irgen) later(fn func()) {
|
||||||
g.laterFuncs = append(g.laterFuncs, fn)
|
g.laterFuncs = append(g.laterFuncs, fn)
|
||||||
}
|
}
|
||||||
|
|
@ -308,8 +315,9 @@ Outer:
|
||||||
|
|
||||||
typecheck.DeclareUniverse()
|
typecheck.DeclareUniverse()
|
||||||
|
|
||||||
// Create any needed stencils of generic functions
|
// Create any needed instantiations of generic functions and transform
|
||||||
g.stencil()
|
// existing and new functions to use those instantiations.
|
||||||
|
BuildInstantiations(true)
|
||||||
|
|
||||||
// Remove all generic functions from g.target.Decl, since they have been
|
// Remove all generic functions from g.target.Decl, since they have been
|
||||||
// used for stenciling, but don't compile. Generic functions will already
|
// used for stenciling, but don't compile. Generic functions will already
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ package noder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmd/compile/internal/base"
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/inline"
|
||||||
"cmd/compile/internal/ir"
|
"cmd/compile/internal/ir"
|
||||||
"cmd/compile/internal/objw"
|
"cmd/compile/internal/objw"
|
||||||
"cmd/compile/internal/reflectdata"
|
"cmd/compile/internal/reflectdata"
|
||||||
|
|
@ -37,207 +38,54 @@ func infoPrint(format string, a ...interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stencil scans functions for instantiated generic function calls and creates the
|
var geninst genInst
|
||||||
// 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)
|
|
||||||
|
|
||||||
|
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.
|
// Instantiate the methods of instantiated generic types that we have seen so far.
|
||||||
g.instantiateMethods()
|
g.instantiateMethods()
|
||||||
|
|
||||||
// Don't use range(g.target.Decls) - we also want to process any new instantiated
|
if preinliningMainScan {
|
||||||
// functions that are created during this loop, in order to handle generic
|
n := len(typecheck.Target.Decls)
|
||||||
// functions calling other generic functions.
|
for i := 0; i < n; i++ {
|
||||||
for i := 0; i < len(g.target.Decls); i++ {
|
g.scanForGenCalls(typecheck.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
|
// Scan all new instantiations created due to g.instantiateMethods() and the
|
||||||
// generic function instantiations. Then create the needed
|
// scan of current decls (if done). This loop purposely runs until no new
|
||||||
// instantiated function if it hasn't been created yet, and change
|
// instantiations are created.
|
||||||
// to calling that function directly.
|
for i := 0; i < len(g.newInsts); i++ {
|
||||||
modified := false
|
g.scanForGenCalls(g.newInsts[i])
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g.finalizeSyms()
|
g.finalizeSyms()
|
||||||
|
|
||||||
// All the instantiations and dictionaries have been created. Now go through
|
// 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.
|
// use of their dictionary.
|
||||||
l := len(g.instInfoMap)
|
l := len(g.newInsts)
|
||||||
for _, info := range g.instInfoMap {
|
for _, fun := range g.newInsts {
|
||||||
|
info := g.instInfoMap[fun.Sym()]
|
||||||
g.dictPass(info)
|
g.dictPass(info)
|
||||||
|
if !preinliningMainScan {
|
||||||
|
// Prepare for the round of inlining below.
|
||||||
|
inline.CanInline(fun.(*ir.Func))
|
||||||
|
}
|
||||||
if doubleCheck {
|
if doubleCheck {
|
||||||
ir.Visit(info.fun, func(n ir.Node) {
|
ir.Visit(info.fun, func(n ir.Node) {
|
||||||
if n.Op() != ir.OCONVIFACE {
|
if n.Op() != ir.OCONVIFACE {
|
||||||
|
|
@ -255,13 +103,198 @@ func (g *irgen) stencil() {
|
||||||
ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun)
|
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
|
// 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
|
// of generic type. outer is the containing function (or nil if closure is
|
||||||
// in a global assignment instead of a function).
|
// 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()
|
pos := x.Pos()
|
||||||
var target *ir.Func // target instantiated function/method
|
var target *ir.Func // target instantiated function/method
|
||||||
var dictValue ir.Node // dictionary to use
|
var dictValue ir.Node // dictionary to use
|
||||||
|
|
@ -423,8 +456,8 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||||
rcvrVar.Defn = rcvrAssign
|
rcvrVar.Defn = rcvrAssign
|
||||||
if outer == nil {
|
if outer == nil {
|
||||||
rcvrVar.Class = ir.PEXTERN
|
rcvrVar.Class = ir.PEXTERN
|
||||||
g.target.Decls = append(g.target.Decls, rcvrAssign)
|
typecheck.Target.Decls = append(typecheck.Target.Decls, rcvrAssign)
|
||||||
g.target.Externs = append(g.target.Externs, rcvrVar)
|
typecheck.Target.Externs = append(typecheck.Target.Externs, rcvrVar)
|
||||||
} else {
|
} else {
|
||||||
rcvrVar.Class = ir.PAUTO
|
rcvrVar.Class = ir.PAUTO
|
||||||
rcvrVar.Curfn = outer
|
rcvrVar.Curfn = outer
|
||||||
|
|
@ -496,7 +529,7 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||||
ir.FinishCaptureNames(pos, outer, fn)
|
ir.FinishCaptureNames(pos, outer, fn)
|
||||||
|
|
||||||
// Make a closure referencing our new internal function.
|
// 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
|
var init []ir.Node
|
||||||
if outer != nil {
|
if outer != nil {
|
||||||
init = append(init, dictAssign)
|
init = append(init, dictAssign)
|
||||||
|
|
@ -510,12 +543,13 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||||
// instantiateMethods instantiates all the methods (and associated dictionaries) of
|
// instantiateMethods instantiates all the methods (and associated dictionaries) of
|
||||||
// all fully-instantiated generic types that have been added to typecheck.instTypeList.
|
// all fully-instantiated generic types that have been added to typecheck.instTypeList.
|
||||||
// It continues until no more types are added to typecheck.instTypeList.
|
// It continues until no more types are added to typecheck.instTypeList.
|
||||||
func (g *irgen) instantiateMethods() {
|
func (g *genInst) instantiateMethods() {
|
||||||
for {
|
for {
|
||||||
instTypeList := typecheck.GetInstTypeList()
|
instTypeList := typecheck.GetInstTypeList()
|
||||||
if len(instTypeList) == 0 {
|
if len(instTypeList) == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
typecheck.ClearInstTypeList()
|
||||||
for _, typ := range instTypeList {
|
for _, typ := range instTypeList {
|
||||||
assert(!typ.HasShape())
|
assert(!typ.HasShape())
|
||||||
// Mark runtime type as needed, since this ensures that the
|
// Mark runtime type as needed, since this ensures that the
|
||||||
|
|
@ -548,7 +582,7 @@ func (g *irgen) instantiateMethods() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInstNameNode returns the name node for the method or function being instantiated, and a bool which is true if a method is being instantiated.
|
// 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 {
|
if meth, ok := inst.X.(*ir.SelectorExpr); ok {
|
||||||
return meth.Selection.Nname.(*ir.Name), true
|
return meth.Selection.Nname.(*ir.Name), true
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -561,7 +595,7 @@ func (g *irgen) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) {
|
||||||
// or main/static dictionary, as needed, and also returns a boolean indicating if a
|
// 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
|
// sub-dictionary was accessed. nameNode is the particular function or method being
|
||||||
// called/referenced, and targs are the type arguments.
|
// 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
|
var dict ir.Node
|
||||||
usingSubdict := false
|
usingSubdict := false
|
||||||
if declInfo != nil {
|
if declInfo != nil {
|
||||||
|
|
@ -603,7 +637,7 @@ func checkFetchBody(nameNode *ir.Name) {
|
||||||
// getInstantiation gets the instantiantion and dictionary of the function or method nameNode
|
// getInstantiation gets the instantiantion and dictionary of the function or method nameNode
|
||||||
// with the type arguments shapes. If the instantiated function is not already
|
// with the type arguments shapes. If the instantiated function is not already
|
||||||
// cached, then it calls genericSubst to create the new instantiation.
|
// 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)
|
checkFetchBody(nameNode)
|
||||||
|
|
||||||
// Convert any non-shape type arguments to their shape, so we can reduce the
|
// Convert any non-shape type arguments to their shape, so we can reduce the
|
||||||
|
|
@ -645,7 +679,8 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth
|
||||||
// This ensures that the linker drops duplicates of this instantiation.
|
// This ensures that the linker drops duplicates of this instantiation.
|
||||||
// All just works!
|
// All just works!
|
||||||
st.SetDupok(true)
|
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
|
return info
|
||||||
}
|
}
|
||||||
|
|
@ -653,7 +688,7 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth
|
||||||
// Struct containing info needed for doing the substitution as we create the
|
// Struct containing info needed for doing the substitution as we create the
|
||||||
// instantiation of a generic function with specified type arguments.
|
// instantiation of a generic function with specified type arguments.
|
||||||
type subster struct {
|
type subster struct {
|
||||||
g *irgen
|
g *genInst
|
||||||
isMethod bool // If a method is being instantiated
|
isMethod bool // If a method is being instantiated
|
||||||
newf *ir.Func // Func node for the new stenciled function
|
newf *ir.Func // Func node for the new stenciled function
|
||||||
ts typecheck.Tsubster
|
ts typecheck.Tsubster
|
||||||
|
|
@ -669,7 +704,7 @@ type subster struct {
|
||||||
// function type where the receiver becomes the first parameter. For either a generic
|
// 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
|
// method or function, a dictionary parameter is the added as the very first
|
||||||
// parameter. genericSubst fills in info.dictParam and info.tparamToBound.
|
// 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
|
var tparams []*types.Type
|
||||||
if isMethod {
|
if isMethod {
|
||||||
// Get the type params from the method receiver (after skipping
|
// Get the type params from the method receiver (after skipping
|
||||||
|
|
@ -1170,7 +1205,8 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
||||||
subst.newf = saveNewf
|
subst.newf = saveNewf
|
||||||
ir.CurFunc = 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()))
|
m.(*ir.ClosureExpr).SetInit(subst.list(x.Init()))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1182,7 +1218,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
||||||
|
|
||||||
// dictPass takes a function instantiation and does the transformations on the
|
// dictPass takes a function instantiation and does the transformations on the
|
||||||
// operations that need to make use of the dictionary param.
|
// 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
|
savef := ir.CurFunc
|
||||||
ir.CurFunc = info.fun
|
ir.CurFunc = info.fun
|
||||||
|
|
||||||
|
|
@ -1503,7 +1539,7 @@ func markTypeUsed(t *types.Type, lsym *obj.LSym) {
|
||||||
|
|
||||||
// getDictionarySym returns the dictionary for the named generic function gf, which
|
// getDictionarySym returns the dictionary for the named generic function gf, which
|
||||||
// is instantiated with the type arguments targs.
|
// 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 {
|
if len(targs) == 0 {
|
||||||
base.Fatalf("%s should have type arguments", gf.Sym().Name)
|
base.Fatalf("%s should have type arguments", gf.Sym().Name)
|
||||||
}
|
}
|
||||||
|
|
@ -1678,7 +1714,7 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
|
||||||
// dictionaries and method instantiations to be complete, so, to avoid recursive
|
// dictionaries and method instantiations to be complete, so, to avoid recursive
|
||||||
// dependencies, we finalize the itab lsyms only after all dictionaries syms and
|
// dependencies, we finalize the itab lsyms only after all dictionaries syms and
|
||||||
// instantiations have been created.
|
// instantiations have been created.
|
||||||
func (g *irgen) finalizeSyms() {
|
func (g *genInst) finalizeSyms() {
|
||||||
for _, d := range g.dictSymsToFinalize {
|
for _, d := range g.dictSymsToFinalize {
|
||||||
infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
|
infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
|
||||||
|
|
||||||
|
|
@ -1744,7 +1780,7 @@ func (g *irgen) finalizeSyms() {
|
||||||
g.dictSymsToFinalize = nil
|
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)
|
sym := g.getDictionarySym(gf, targs, isMeth)
|
||||||
|
|
||||||
// Make (or reuse) a node referencing the dictionary symbol.
|
// Make (or reuse) a node referencing the dictionary symbol.
|
||||||
|
|
@ -1792,7 +1828,7 @@ func hasShapeTypes(targs []*types.Type) bool {
|
||||||
|
|
||||||
// getInstInfo get the dictionary format for a function instantiation- type params, derived
|
// getInstInfo get the dictionary format for a function instantiation- type params, derived
|
||||||
// types, and needed subdictionaries and itabs.
|
// 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 := instInfo.dictInfo
|
||||||
info.shapeParams = shapes
|
info.shapeParams = shapes
|
||||||
|
|
||||||
|
|
@ -2100,7 +2136,7 @@ func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node,
|
||||||
//
|
//
|
||||||
// The returned closure is fully substituted and has already had any needed
|
// The returned closure is fully substituted and has already had any needed
|
||||||
// transformations done.
|
// 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
|
outer := info.fun
|
||||||
pos := m.Pos()
|
pos := m.Pos()
|
||||||
typ := m.Type() // type of the closure
|
typ := m.Type() // type of the closure
|
||||||
|
|
@ -2155,5 +2191,5 @@ func (g *irgen) buildClosure2(info *instInfo, m ir.Node) ir.Node {
|
||||||
ir.FinishCaptureNames(pos, outer, fn)
|
ir.FinishCaptureNames(pos, outer, fn)
|
||||||
|
|
||||||
// Do final checks on closure and return it.
|
// Do final checks on closure and return it.
|
||||||
return ir.UseClosure(fn.OClosure, g.target)
|
return ir.UseClosure(fn.OClosure, typecheck.Target)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1007,19 +1007,22 @@ func assert(p bool) {
|
||||||
// List of newly fully-instantiated types who should have their methods generated.
|
// List of newly fully-instantiated types who should have their methods generated.
|
||||||
var instTypeList []*types.Type
|
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) {
|
func NeedInstType(t *types.Type) {
|
||||||
instTypeList = append(instTypeList, t)
|
instTypeList = append(instTypeList, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInstTypeList returns the current contents of instTypeList, and sets
|
// GetInstTypeList returns the current contents of instTypeList.
|
||||||
// instTypeList to nil.
|
|
||||||
func GetInstTypeList() []*types.Type {
|
func GetInstTypeList() []*types.Type {
|
||||||
r := instTypeList
|
r := instTypeList
|
||||||
instTypeList = nil
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearInstTypeList clears the contents of instTypeList.
|
||||||
|
func ClearInstTypeList() {
|
||||||
|
instTypeList = nil
|
||||||
|
}
|
||||||
|
|
||||||
// General type substituter, for replacing typeparams with type args.
|
// General type substituter, for replacing typeparams with type args.
|
||||||
type Tsubster struct {
|
type Tsubster struct {
|
||||||
Tparams []*types.Type
|
Tparams []*types.Type
|
||||||
|
|
|
||||||
56
test/typeparam/geninline.dir/a.go
Normal file
56
test/typeparam/geninline.dir/a.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
16
test/typeparam/geninline.dir/main.go
Normal file
16
test/typeparam/geninline.dir/main.go
Normal file
|
|
@ -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()
|
||||||
|
}
|
||||||
7
test/typeparam/geninline.go
Normal file
7
test/typeparam/geninline.go
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue