aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/noder/stencil.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/noder/stencil.go')
-rw-r--r--src/cmd/compile/internal/noder/stencil.go556
1 files changed, 314 insertions, 242 deletions
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
index 1c22fc2ac0..7fca674132 100644
--- a/src/cmd/compile/internal/noder/stencil.go
+++ b/src/cmd/compile/internal/noder/stencil.go
@@ -44,7 +44,6 @@ func infoPrint(format string, a ...interface{}) {
// 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()
@@ -106,7 +105,7 @@ func (g *irgen) stencil() {
inst := call.X.(*ir.InstExpr)
nameNode, isMeth := g.getInstNameNode(inst)
targs := typecheck.TypesOf(inst.Targs)
- st := g.getInstantiation(nameNode, targs, isMeth)
+ st := g.getInstantiation(nameNode, targs, isMeth).fun
dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth)
if infoPrintMode {
dictkind := "Main dictionary"
@@ -165,7 +164,7 @@ func (g *irgen) stencil() {
// to OCALLFUNC and does typecheckaste/assignconvfn.
transformCall(call)
- st := g.getInstantiation(gf, targs, true)
+ 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.
@@ -232,6 +231,31 @@ func (g *irgen) stencil() {
}
g.finalizeSyms()
+
+ // All the instantiations and dictionaries have been created. Now go through
+ // each instantiation and transform the various operations that need to make
+ // use of their dictionary.
+ l := len(g.instInfoMap)
+ for _, info := range g.instInfoMap {
+ g.dictPass(info)
+ if doubleCheck {
+ ir.Visit(info.fun, func(n ir.Node) {
+ if n.Op() != ir.OCONVIFACE {
+ return
+ }
+ c := n.(*ir.ConvExpr)
+ if c.X.Type().HasShape() && !c.X.Type().IsInterface() {
+ ir.Dump("BAD FUNCTION", info.fun)
+ ir.Dump("BAD CONVERSION", c)
+ base.Fatalf("converting shape type to interface")
+ }
+ })
+ }
+ if base.Flag.W > 1 {
+ ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun)
+ }
+ }
+ assert(l == len(g.instInfoMap))
}
// buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR
@@ -274,7 +298,7 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
// 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)
+ target = g.getInstantiation(gf, targs, rcvrValue != nil).fun
dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, rcvrValue != nil)
if infoPrintMode {
dictkind := "Main dictionary"
@@ -321,7 +345,7 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
// Remember if value method, so we can detect (*T).M case.
valueMethod = true
}
- target = g.getInstantiation(gf, targs, true)
+ target = g.getInstantiation(gf, targs, true).fun
dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true)
if infoPrintMode {
dictkind := "Main dictionary"
@@ -396,13 +420,19 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) 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)
+ if outer == nil {
+ rcvrVar.Class = ir.PEXTERN
+ g.target.Decls = append(g.target.Decls, rcvrAssign)
+ g.target.Externs = append(g.target.Externs, rcvrVar)
+ } else {
+ rcvrVar.Class = ir.PAUTO
+ rcvrVar.Curfn = outer
+ outer.Dcl = append(outer.Dcl, rcvrVar)
+ }
}
// Build body of closure. This involves just calling the wrapped function directly
@@ -538,13 +568,18 @@ func (g *irgen) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Nam
var dict ir.Node
usingSubdict := false
if declInfo != nil {
- // Get the dictionary arg via sub-dictionary reference
- entry, ok := declInfo.dictEntryMap[n]
+ entry := -1
+ for i, de := range declInfo.dictInfo.subDictCalls {
+ if n == de {
+ entry = declInfo.dictInfo.startSubDict + i
+ break
+ }
+ }
// 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)
+ if entry >= 0 {
+ dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictInfo.dictLen)
usingSubdict = true
}
}
@@ -571,7 +606,7 @@ func checkFetchBody(nameNode *ir.Name) {
// 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 {
+func (g *irgen) 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
@@ -580,12 +615,12 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth
// specified concrete type args.
var s1 []*types.Type
for i, t := range shapes {
- if !t.HasShape() {
+ if !t.IsShape() {
if s1 == nil {
s1 = make([]*types.Type, len(shapes))
copy(s1[0:i], shapes[0:i])
}
- s1[i] = typecheck.Shapify(t)
+ s1[i] = typecheck.Shapify(t, i)
} else if s1 != nil {
s1[i] = shapes[i]
}
@@ -599,28 +634,28 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth
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.
+ dictInfo: &dictInfo{},
+ }
+ info.dictInfo.shapeToBound = make(map[*types.Type]*types.Type)
+
+ // genericSubst fills in info.dictParam and info.tparamToBound.
st := g.genericSubst(sym, nameNode, shapes, isMeth, info)
info.fun = st
g.instInfoMap[sym] = info
+
+ // getInstInfo fills in info.dictInfo.
+ g.getInstInfo(st, shapes, info)
+ if base.Flag.W > 1 {
+ ir.Dump(fmt.Sprintf("\nstenciled %v", st), st)
+ }
+
// 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
+ return info
}
// Struct containing info needed for doing the substitution as we create the
@@ -641,7 +676,7 @@ type subster struct {
// 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.
+// 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 {
var tparams []*types.Type
if isMethod {
@@ -738,23 +773,13 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*typ
base.Fatalf("defnMap is not empty")
}
- ir.CurFunc = savef
-
- if doubleCheck {
- ir.Visit(newf, func(n ir.Node) {
- if n.Op() != ir.OCONVIFACE {
- return
- }
- c := n.(*ir.ConvExpr)
- if c.X.Type().HasShape() && !c.X.Type().IsInterface() {
- ir.Dump("BAD FUNCTION", newf)
- ir.Dump("BAD CONVERSION", c)
- base.Fatalf("converting shape type to interface")
- }
- })
+ for i, tp := range tparams {
+ info.dictInfo.shapeToBound[shapes[i]] = subst.ts.Typ(tp.Bound())
}
- return newf
+ ir.CurFunc = savef
+
+ return subst.newf
}
// localvar creates a new name node for the specified local variable and enters it
@@ -773,6 +798,7 @@ func (subst *subster) localvar(name *ir.Name) *ir.Name {
m.Func = name.Func
subst.ts.Vars[name] = m
m.SetTypecheck(1)
+ m.DictIndex = name.DictIndex
if name.Defn != nil {
if name.Defn.Op() == ir.ONAME {
// This is a closure variable, so its Defn is the outer
@@ -863,11 +889,11 @@ func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
// 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 {
+ if i < 0 || i >= info.dictInfo.startSubDict {
base.Fatalf(fmt.Sprintf("bad dict index %d", i))
}
- r := getDictionaryEntry(pos, info.dictParam, i, info.startSubDict)
+ r := getDictionaryEntry(pos, info.dictParam, i, info.dictInfo.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)
@@ -929,17 +955,6 @@ func (subst *subster) node(n ir.Node) ir.Node {
}
}
- 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)
@@ -980,6 +995,9 @@ func (subst *subster) node(n ir.Node) ir.Node {
case ir.OSEND:
transformSend(m.(*ir.SendStmt))
+ case ir.OSELECT:
+ transformSelect(m.(*ir.SelectStmt))
+
}
}
@@ -1012,36 +1030,9 @@ func (subst *subster) node(n ir.Node) ir.Node {
// 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 {
+ if src := mse.X.Type(); !src.IsShape() {
transformDot(mse, false)
}
- m.SetTypecheck(1)
case ir.OCALL:
call := m.(*ir.CallExpr)
@@ -1096,7 +1087,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
// or channel receive to compute function value.
transformCall(call)
- case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.ODYNAMICDOTTYPE:
+ case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
transformCall(call)
case ir.OFUNCINST:
@@ -1104,6 +1095,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
// in stencil() once we have created & attached the
// instantiation to be called.
+ case ir.OXDOT, ir.ODOTTYPE, ir.ODOTTYPE2:
default:
base.FatalfAt(call.Pos(), fmt.Sprintf("Unexpected op with CALL during stenciling: %v", call.X.Op()))
}
@@ -1135,7 +1127,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
// 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"))
+ ldict := ir.NewNameAt(x.Pos(), newfn.Sym().Pkg.Lookup(".dict"))
typed(types.Types[types.TUINTPTR], ldict)
ldict.Class = ir.PAUTO
ldict.Curfn = newfn
@@ -1147,16 +1139,11 @@ func (subst *subster) node(n ir.Node) ir.Node {
// 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.
+ // same dictInfo.
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,
+ fun: newfn,
+ dictParam: ldict,
+ dictInfo: subst.info.dictInfo,
}
subst.g.instInfoMap[newfn.Nname.Sym()] = cinfo
@@ -1175,107 +1162,176 @@ func (subst *subster) node(n ir.Node) ir.Node {
m = ir.UseClosure(newfn.OClosure, subst.g.target)
m.(*ir.ClosureExpr).SetInit(subst.list(x.Init()))
+ }
+ return m
+ }
+
+ return edit(n)
+}
+
+// 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) {
+ savef := ir.CurFunc
+ ir.CurFunc = info.fun
+
+ var edit func(ir.Node) ir.Node
+ edit = func(m ir.Node) ir.Node {
+ ir.EditChildren(m, edit)
+
+ switch m.Op() {
+ case ir.OCLOSURE:
+ newf := m.(*ir.ClosureExpr).Func
+ ir.CurFunc = newf
+ outerinfo := info
+ info = g.instInfoMap[newf.Nname.Sym()]
+
+ body := newf.Body
+ for i, n := range body {
+ body[i] = edit(n)
+ }
+
+ info = outerinfo
+ ir.CurFunc = info.fun
+
+ case ir.OXDOT:
+ mse := m.(*ir.SelectorExpr)
+ src := mse.X.Type()
+ assert(src.IsShape())
+
+ // The only dot on a shape type value are methods.
+ if mse.X.Op() == ir.OTYPE {
+ // Method expression T.M
+ m = g.buildClosure2(info, m)
+ // 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
+ dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()]
+ if src.IsInterface() {
+ // If type arg is an interface (unusual case),
+ // we do a type assert to the type bound.
+ mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst)
+ } else {
+ mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst)
+ }
+ transformDot(mse, false)
+ }
+ case ir.OCALL:
+ op := m.(*ir.CallExpr).X.Op()
+ if op != ir.OFUNCINST {
+ assert(op == ir.OMETHVALUE || op == ir.OCLOSURE || op == ir.ODYNAMICDOTTYPE || op == ir.ODYNAMICDOTTYPE2)
+ transformCall(m.(*ir.CallExpr))
+ }
+
case ir.OCONVIFACE:
- x := x.(*ir.ConvExpr)
+ if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() {
+ // Was T->interface{}, after stenciling it is now interface{}->interface{}.
+ // No longer need the conversion. See issue 48276.
+ m.(*ir.ConvExpr).SetOp(ir.OCONVNOP)
+ break
+ }
+ mce := m.(*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() || (x.X.Type().IsInterface() && x.Type().HasTParam()) {
- m = convertUsingDictionary(subst.info, subst.info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, x, m.Type(), x.X.Type())
+ if mce.X.Type().HasShape() || (mce.X.Type().IsInterface() && m.Type().HasShape()) {
+ m = convertUsingDictionary(info, info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, m, m.Type())
}
case ir.ODOTTYPE, ir.ODOTTYPE2:
- if !x.Type().HasTParam() {
+ if !m.Type().HasShape() {
break
}
dt := m.(*ir.TypeAssertExpr)
var rt ir.Node
if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() {
- ix := findDictType(subst.info, x.Type())
+ ix := findDictType(info, m.Type())
assert(ix >= 0)
- rt = getDictionaryType(subst.info, subst.info.dictParam, dt.Pos(), ix)
+ rt = getDictionaryType(info, 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
+ for i, ic := range info.dictInfo.itabConvs {
+ if ic == m {
+ ix = info.dictInfo.startItabConv + i
break
}
}
assert(ix >= 0)
- rt = getDictionaryEntry(dt.Pos(), subst.info.dictParam, ix, subst.info.dictLen)
+ rt = getDictionaryEntry(dt.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
}
op := ir.ODYNAMICDOTTYPE
- if x.Op() == ir.ODOTTYPE2 {
+ if m.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 {
+ if _, ok := m.(*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() {
+ for i, c := range m.List {
+ if c.Op() == ir.OTYPE && c.Type().HasShape() {
// Use a *runtime._type for the dynamic type.
- ix := findDictType(subst.info, c.Type())
+ ix := findDictType(info, m.List[i].Type())
assert(ix >= 0)
- dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), subst.info.dictParam, ix, subst.info.dictLen))
+ dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.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 {
+ if _, ok := info.dictInfo.type2switchType[m.List[i]]; 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
+ for i, ic := range info.dictInfo.itabConvs {
+ if ic == m.List[i] {
+ ix = info.dictInfo.startItabConv + i
break
}
}
assert(ix >= 0)
- dt.ITab = getDictionaryEntry(c.Pos(), subst.info.dictParam, ix, subst.info.dictLen)
+ dt.ITab = getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
}
}
typed(m.List[i].Type(), dt)
m.List[i] = dt
}
}
+
}
return m
}
-
- return edit(n)
+ edit(info.fun)
+ ir.CurFunc = savef
}
// 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 {
+ for i, dt := range info.dictInfo.shapeParams {
if dt == t {
return i
}
}
- for i, dt := range info.gfInfo.derivedTypes {
- if types.Identical(dt, t) {
- return i + len(info.gfInfo.tparams)
+ for i, dt := range info.dictInfo.derivedTypes {
+ if types.IdenticalStrict(dt, t) {
+ return i + len(info.dictInfo.shapeParams)
}
}
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
+// convertUsingDictionary converts instantiated value v (type v.Type()) to an interface
+// type dst, by returning a new set of nodes that make use of a dictionary entry. in is the
+// instantiated 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() || src.IsInterface() && gn.Type().HasTParam())
+func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type) ir.Node {
+ assert(v.Type().HasShape() || v.Type().IsInterface() && in.Type().HasShape())
assert(dst.IsInterface())
if v.Type().IsInterface() {
@@ -1288,8 +1344,7 @@ func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v
v.SetTypecheck(1)
return v
}
- gdst := gn.Type() // pre-stenciled destination type
- if !gdst.HasTParam() {
+ if !in.Type().HasShape() {
// Regular OCONVIFACE works if the destination isn't parameterized.
v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v)
v.SetTypecheck(1)
@@ -1311,7 +1366,7 @@ func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v
types.CalcSize(fn.Type())
call := ir.NewCallExpr(pos, ir.OCALLFUNC, fn, nil)
typed(types.Types[types.TUINT8].PtrTo(), call)
- ix := findDictType(info, gdst)
+ ix := findDictType(info, in.Type())
assert(ix >= 0)
inter := getDictionaryType(info, dictParam, pos, ix)
call.Args = []ir.Node{inter, itab}
@@ -1327,16 +1382,16 @@ func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v
// 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
+ for i, ic := range info.dictInfo.itabConvs {
+ if ic == in {
+ ix = info.dictInfo.startItabConv + i
break
}
}
assert(ix >= 0)
- rt = getDictionaryEntry(pos, dictParam, ix, info.dictLen)
+ rt = getDictionaryEntry(pos, dictParam, ix, info.dictInfo.dictLen)
} else {
- ix := findDictType(info, src)
+ ix := findDictType(info, v.Type())
assert(ix >= 0)
// Load the actual runtime._type of the type parameter from the dictionary.
rt = getDictionaryType(info, dictParam, pos, ix)
@@ -1424,6 +1479,7 @@ func markTypeUsed(t *types.Type, lsym *obj.LSym) {
} else {
// TODO: This is somewhat overkill, we really only need it
// for types that are put into interfaces.
+ // Note: this relocation is also used in cmd/link/internal/ld/dwarf.go
reflectdata.MarkTypeUsedInInterface(t, lsym)
}
}
@@ -1452,8 +1508,6 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
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).
@@ -1463,8 +1517,12 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
off = objw.SymPtr(lsym, off, s, 0)
markTypeUsed(t, lsym)
}
+
+ instInfo := g.getInstantiation(gf, targs, isMeth)
+ info := instInfo.dictInfo
+
subst := typecheck.Tsubster{
- Tparams: info.tparams,
+ Tparams: info.shapeParams,
Targs: targs,
}
// Emit an entry for each derived type (after substituting targs)
@@ -1479,18 +1537,33 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
for _, n := range info.subDictCalls {
var sym *types.Sym
switch n.Op() {
- case ir.OCALL:
+ case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH:
call := n.(*ir.CallExpr)
- if call.X.Op() == ir.OXDOT {
+ if call.X.Op() == ir.OXDOT || call.X.Op() == ir.ODOTMETH {
var nameNode *ir.Name
se := call.X.(*ir.SelectorExpr)
- if types.IsInterfaceMethod(se.Selection.Type) {
+ if se.X.Type().IsShape() {
// This is a method call enabled by a type bound.
+
+ // We need this extra check for type expressions, which
+ // don't add in the implicit XDOTs.
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 !tparam.IsShape() {
+ // The method expression is not
+ // really on a typeparam.
+ break
+ }
+ ix := -1
+ for i, shape := range info.shapeParams {
+ if shape == tparam {
+ ix = i
+ break
+ }
+ }
+ assert(ix >= 0)
+ recvType := targs[ix]
if recvType.IsInterface() || len(recvType.RParams()) == 0 {
// No sub-dictionary entry is
// actually needed, since the
@@ -1509,8 +1582,10 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
} 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()
+ recvType := deref(call.X.(*ir.SelectorExpr).X.Type())
+ genRecvType := recvType.OrigSym().Def.Type()
+ nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
+ subtargs := recvType.RParams()
s2targs := make([]*types.Type, len(subtargs))
for i, t := range subtargs {
s2targs[i] = subst.Typ(t)
@@ -1543,14 +1618,16 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
}
sym = g.getDictionarySym(nameNode, subtargs, false)
- case ir.OXDOT:
+ case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE:
selExpr := n.(*ir.SelectorExpr)
- subtargs := deref(selExpr.X.Type()).RParams()
+ recvType := deref(selExpr.Selection.Type.Recv().Type)
+ genRecvType := recvType.OrigSym().Def.Type()
+ subtargs := recvType.RParams()
s2targs := make([]*types.Type, len(subtargs))
for i, t := range subtargs {
s2targs[i] = subst.Typ(t)
}
- nameNode := selExpr.Selection.Nname.(*ir.Name)
+ nameNode := typecheck.Lookdot1(selExpr, selExpr.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
sym = g.getDictionarySym(nameNode, s2targs, true)
default:
@@ -1567,11 +1644,13 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
}
}
+ g.instantiateMethods()
delay := &delayInfo{
- gf: gf,
- targs: targs,
- sym: sym,
- off: off,
+ gf: gf,
+ targs: targs,
+ sym: sym,
+ off: off,
+ isMeth: isMeth,
}
g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay)
return sym
@@ -1587,10 +1666,11 @@ func (g *irgen) finalizeSyms() {
infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
lsym := d.sym.Linksym()
- info := g.getGfInfo(d.gf)
+ instInfo := g.getInstantiation(d.gf, d.targs, d.isMeth)
+ info := instInfo.dictInfo
subst := typecheck.Tsubster{
- Tparams: info.tparams,
+ Tparams: info.shapeParams,
Targs: d.targs,
}
@@ -1598,10 +1678,10 @@ func (g *irgen) finalizeSyms() {
for _, n := range info.itabConvs {
var srctype, dsttype *types.Type
switch n.Op() {
- case ir.OXDOT:
+ case ir.OXDOT, ir.OMETHVALUE:
se := n.(*ir.SelectorExpr)
srctype = subst.Typ(se.X.Type())
- dsttype = subst.Typ(se.X.Type().Bound())
+ dsttype = subst.Typ(info.shapeToBound[se.X.Type()])
found := false
for i, m := range dsttype.AllMethods().Slice() {
if se.Sel == m.Sym {
@@ -1632,6 +1712,9 @@ func (g *irgen) finalizeSyms() {
d.off = objw.Uintptr(lsym, d.off, 0)
infoPrint(" + Unused itab entry for %v\n", srctype)
} else {
+ // Make sure all new fully-instantiated types have
+ // their methods created before generating any itabs.
+ g.instantiateMethods()
itabLsym := reflectdata.ITabLsym(srctype, dsttype)
d.off = objw.SymPtr(lsym, d.off, itabLsym, 0)
infoPrint(" + Itab for (%v,%v)\n", srctype, dsttype)
@@ -1670,65 +1753,50 @@ func (g *irgen) getDictionaryValue(gf *ir.Name, targs []*types.Type, isMeth bool
return np
}
-// hasTParamNodes returns true if the type of any node in targs has a typeparam.
-func hasTParamNodes(targs []ir.Node) bool {
+// hasShapeNodes returns true if the type of any node in targs has a shape.
+func hasShapeNodes(targs []ir.Node) bool {
for _, n := range targs {
- if n.Type().HasTParam() {
+ if n.Type().HasShape() {
return true
}
}
return false
}
-// hasTParamNodes returns true if any type in targs has a typeparam.
-func hasTParamTypes(targs []*types.Type) bool {
+// hasShapeTypes returns true if any type in targs has a shape.
+func hasShapeTypes(targs []*types.Type) bool {
for _, t := range targs {
- if t.HasTParam() {
+ if t.HasShape() {
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
- }
- }
+// 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) {
+ info := instInfo.dictInfo
+ info.shapeParams = shapes
- for _, t := range info.tparams {
- b := t.Bound()
- if b.HasTParam() {
+ for _, t := range info.shapeParams {
+ b := info.shapeToBound[t]
+ if b.HasShape() {
// 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)
+ addType(info, nil, b)
}
}
- for _, n := range gf.Dcl {
- addType(&info, n, n.Type())
+ for _, n := range st.Dcl {
+ addType(info, n, n.Type())
+ n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
}
if infoPrintMode {
- fmt.Printf(">>> GfInfo for %v\n", gn)
- for _, t := range info.tparams {
+ fmt.Printf(">>> InstInfo for %v\n", st)
+ for _, t := range info.shapeParams {
fmt.Printf(" Typeparam %v\n", t)
}
}
@@ -1736,14 +1804,14 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
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) {
+ if hasShapeNodes(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 &&
+ } else if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && !n.(*ir.SelectorExpr).Implicit() &&
+ !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) &&
len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 {
- if hasTParamTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) {
+ if hasShapeTypes(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 {
@@ -1754,34 +1822,33 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
}
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) {
+ if hasShapeNodes(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 &&
+ if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH &&
+ //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()) {
+ if hasShapeTypes(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() {
+ isShapeDeref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()) {
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() {
+ n.(*ir.ConvExpr).X.Type().HasShape() {
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() {
+ if n.Op() == ir.OXDOT && n.(*ir.SelectorExpr).X.Type().IsShape() {
infoPrint(" Itab for bound call: %v\n", n)
info.itabConvs = append(info.itabConvs, n)
}
@@ -1793,14 +1860,18 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
// 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 {
+ cfunc := n.(*ir.ClosureExpr).Func
+ for _, n1 := range cfunc.Body {
ir.Visit(n1, visitFunc)
}
+ for _, n := range cfunc.Dcl {
+ n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
+ }
}
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() {
+ if c.Op() == ir.OTYPE && c.Type().HasShape() {
// 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)
@@ -1812,41 +1883,48 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
}
}
}
- addType(&info, n, n.Type())
+ addType(info, n, n.Type())
}
- for _, stmt := range gf.Body {
+ for _, stmt := range st.Body {
ir.Visit(stmt, visitFunc)
}
if infoPrintMode {
for _, t := range info.derivedTypes {
fmt.Printf(" Derived type %v\n", t)
}
- fmt.Printf(">>> Done Gfinfo\n")
+ fmt.Printf(">>> Done Instinfo\n")
}
- g.gfInfoMap[gn.Sym()] = &info
- return &info
+ info.startSubDict = len(info.shapeParams) + len(info.derivedTypes)
+ info.startItabConv = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls)
+ info.dictLen = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs)
+}
+
+// isShapeDeref returns true if t is either a shape or a pointer to a shape. (We
+// can't just use deref(t).IsShape(), since a shape type is a complex type and may
+// have a pointer as part of its shape.)
+func isShapeDeref(t *types.Type) bool {
+ return t.IsShape() || t.IsPtr() && t.Elem().IsShape()
}
// 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
+// just a simple shape) 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() {
+func addType(info *dictInfo, n ir.Node, t *types.Type) {
+ if t == nil || !t.HasShape() {
return
}
- if t.IsTypeParam() && t.Underlying() == t {
+ if t.IsShape() {
return
}
if t.Kind() == types.TFUNC && n != nil &&
- (t.Recv() != nil ||
- n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) {
+ (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) {
+ if doubleCheck && !parameterizedBy(t, info.shapeParams) {
base.Fatalf("adding type with invalid parameters %+v", t)
}
if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() {
@@ -1855,7 +1933,7 @@ func addType(info *gfInfo, n ir.Node, t *types.Type) {
}
// Ignore a derived type we've already added.
for _, et := range info.derivedTypes {
- if types.Identical(t, et) {
+ if types.IdenticalStrict(t, et) {
return
}
}
@@ -1881,8 +1959,7 @@ func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Ty
}
return true
}
- switch t.Kind() {
- case types.TTYPEPARAM:
+ if t.IsShape() {
// Check if t is one of the allowed parameters in scope.
for _, p := range params {
if p == t {
@@ -1892,6 +1969,8 @@ func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Ty
// Couldn't find t in the list of allowed parameters.
return false
+ }
+ switch t.Kind() {
case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN:
return parameterizedBy1(t.Elem(), params, visited)
@@ -1982,17 +2061,17 @@ func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*t
}
// 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)
+// the 'dst' interface type.
+func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, dst *types.Type) ir.Node {
+ if dst.HasShape() {
+ ix := findDictType(info, dst)
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)
+ typed(dst, rcvr)
}
return rcvr
}
@@ -2004,9 +2083,8 @@ 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
// transformations done.
-func (g *irgen) buildClosure2(subst *subster, m, x ir.Node) ir.Node {
- outer := subst.newf
- info := subst.info
+func (g *irgen) buildClosure2(info *instInfo, m ir.Node) ir.Node {
+ outer := info.fun
pos := m.Pos()
typ := m.Type() // type of the closure
@@ -2029,24 +2107,18 @@ func (g *irgen) buildClosure2(subst *subster, m, x ir.Node) 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)
- }
+ dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()]
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)
+ rcvr = assertToBound(info, dictVar, pos, rcvr, dst)
} else {
- rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, x, dst, gsrc)
+ rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, m, dst)
}
- dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, x.(*ir.SelectorExpr).Sel)
+ dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, m.(*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)
+ typed(dot.Selection.Type, dot)
innerCall = ir.NewCallExpr(pos, ir.OCALLINTER, dot, args)
t := m.Type()
if t.NumResults() == 0 {