aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/compile/internal/noder/irgen.go57
-rw-r--r--src/cmd/compile/internal/noder/stencil.go534
-rw-r--r--src/cmd/compile/internal/reflectdata/reflect.go2
-rw-r--r--src/cmd/compile/internal/typecheck/subr.go59
-rw-r--r--src/cmd/compile/internal/types/identity.go31
5 files changed, 375 insertions, 308 deletions
diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go
index 414875615f..4f1b4e6bfd 100644
--- a/src/cmd/compile/internal/noder/irgen.go
+++ b/src/cmd/compile/internal/noder/irgen.go
@@ -97,39 +97,42 @@ func check2(noders []*noder) {
}
}
-// gfInfo is information gathered on a generic function.
-type gfInfo struct {
- tparams []*types.Type
+// dictInfo is the dictionary format for an instantiation of a generic function with
+// particular shapes. shapeParams, derivedTypes, subDictCalls, and itabConvs describe
+// the actual dictionary entries in order, and the remaining fields are other info
+// needed in doing dictionary processing during compilation.
+type dictInfo struct {
+ // Types substituted for the type parameters, which are shape types.
+ shapeParams []*types.Type
+ // All types derived from those typeparams used in the instantiation.
derivedTypes []*types.Type
- // Nodes in generic function that requires a subdictionary. Includes
+ // Nodes in the instantiation that requires a subdictionary. Includes
// method and function calls (OCALL), function values (OFUNCINST), method
// values/expressions (OXDOT).
subDictCalls []ir.Node
- // Nodes in generic functions that are a conversion from a typeparam/derived
+ // Nodes in the instantiation that are a conversion from a typeparam/derived
// type to a specific interface.
itabConvs []ir.Node
+
+ // Mapping from each shape type that substitutes a type param, to its
+ // type bound (which is also substitued with shapes if it is parameterized)
+ shapeToBound map[*types.Type]*types.Type
+
// For type switches on nonempty interfaces, a map from OTYPE entries of
- // HasTParam type, to the interface type we're switching from.
- // TODO: what if the type we're switching from is a shape type?
+ // HasShape type, to the interface type we're switching from.
type2switchType map[ir.Node]*types.Type
+
+ startSubDict int // Start of dict entries for subdictionaries
+ startItabConv int // Start of dict entries for itab conversions
+ dictLen int // Total number of entries in dictionary
}
-// instInfo is information gathered on an gcshape (or fully concrete)
-// instantiation of a function.
+// instInfo is information gathered on an shape instantiation of a function.
type instInfo struct {
fun *ir.Func // The instantiated function (with body)
dictParam *ir.Name // The node inside fun that refers to the dictionary param
- gf *ir.Name // The associated generic function
- gfInfo *gfInfo
-
- startSubDict int // Start of dict entries for subdictionaries
- startItabConv int // Start of dict entries for itab conversions
- dictLen int // Total number of entries in dictionary
-
- // Map from nodes in instantiated fun (OCALL, OCALLMETHOD, OFUNCINST, and
- // OMETHEXPR) to the associated dictionary entry for a sub-dictionary
- dictEntryMap map[ir.Node]int
+ dictInfo *dictInfo
}
type irgen struct {
@@ -155,13 +158,8 @@ type irgen struct {
dnum int // for generating unique dictionary variables
- // Map from generic function to information about its type params, derived
- // types, and subdictionaries.
- gfInfoMap map[*types.Sym]*gfInfo
-
// Map from a name of function that been instantiated to information about
- // its instantiated function, associated generic function/method, and the
- // mapping from IR nodes to dictionary entries.
+ // its instantiated function (including dictionary format).
instInfoMap map[*types.Sym]*instInfo
// dictionary syms which we need to finish, by writing out any itabconv
@@ -179,10 +177,11 @@ func (g *irgen) later(fn func()) {
}
type delayInfo struct {
- gf *ir.Name
- targs []*types.Type
- sym *types.Sym
- off int
+ gf *ir.Name
+ targs []*types.Type
+ sym *types.Sym
+ off int
+ isMeth bool
}
type typeDelayInfo struct {
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
index 6c990c1828..e60383f4e0 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"
@@ -544,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
}
}
@@ -577,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
@@ -586,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]
}
@@ -605,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
@@ -647,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 {
@@ -744,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
@@ -870,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)
@@ -936,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)
@@ -1019,36 +1027,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)
@@ -1103,7 +1084,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:
@@ -1111,6 +1092,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()))
}
@@ -1142,7 +1124,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
@@ -1154,16 +1136,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
@@ -1182,117 +1159,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 {
- return info.gfInfo.findDictType(t)
-}
-
-func (gfInfo *gfInfo) findDictType(t *types.Type) int {
- for i, dt := range gfInfo.tparams {
+ for i, dt := range info.dictInfo.shapeParams {
if dt == t {
return i
}
}
- for i, dt := range gfInfo.derivedTypes {
- if types.Identical(dt, t) {
- return i + len(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() {
@@ -1305,8 +1341,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)
@@ -1328,7 +1363,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}
@@ -1344,16 +1379,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)
@@ -1469,8 +1504,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).
@@ -1480,8 +1513,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)
@@ -1496,18 +1533,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
@@ -1526,8 +1578,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)
@@ -1560,14 +1614,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:
@@ -1584,11 +1640,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
@@ -1604,10 +1662,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,
}
@@ -1615,10 +1674,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 {
@@ -1649,6 +1708,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)
@@ -1687,66 +1749,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
- }
+// 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
- checkFetchBody(gn)
- var info gfInfo
- gf := gn.Func
- recv := gf.Type().Recv()
- if recv != nil {
- info.tparams = deref(recv.Type).RParams()
- } else {
- tparams := gn.Type().TParams().FieldSlice()
- info.tparams = make([]*types.Type, len(tparams))
- for i, f := range tparams {
- info.tparams[i] = f.Type
- }
- }
-
- for _, t := range info.tparams {
- b := t.Bound()
- if b.HasTParam() {
+ 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())
- n.DictIndex = uint16(info.findDictType(n.Type()) + 1)
+ 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)
}
}
@@ -1754,14 +1800,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 {
@@ -1772,34 +1818,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)
}
@@ -1816,13 +1861,13 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
ir.Visit(n1, visitFunc)
}
for _, n := range cfunc.Dcl {
- n.DictIndex = uint16(info.findDictType(n.Type()) + 1)
+ 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)
@@ -1834,41 +1879,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() {
@@ -1877,7 +1929,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
}
}
@@ -1903,8 +1955,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 {
@@ -1914,6 +1965,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)
@@ -2004,17 +2057,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
}
@@ -2026,9 +2079,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
@@ -2051,24 +2103,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 {
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index 183ede789e..f42bb338d0 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -1921,7 +1921,7 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
// Target method uses shaped names.
targs2 := make([]*types.Type, len(targs))
for i, t := range targs {
- targs2[i] = typecheck.Shapify(t)
+ targs2[i] = typecheck.Shapify(t, i)
}
targs = targs2
diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go
index 5854e3c458..64d30eeb5a 100644
--- a/src/cmd/compile/internal/typecheck/subr.go
+++ b/src/cmd/compile/internal/typecheck/subr.go
@@ -1019,10 +1019,11 @@ type Tsubster struct {
SubstForwFunc func(*types.Type) *types.Type
}
-// Typ computes the type obtained by substituting any type parameter in t with the
-// corresponding type argument in subst. If t contains no type parameters, the
-// result is t; otherwise the result is a new type. It deals with recursive types
-// by using TFORW types and finding partially or fully created types via sym.Def.
+// Typ computes the type obtained by substituting any type parameter or shape in t
+// that appears in subst.Tparams with the corresponding type argument in subst.Targs.
+// If t contains no type parameters, the result is t; otherwise the result is a new
+// type. It deals with recursive types by using TFORW types and finding partially or
+// fully created types via sym.Def.
func (ts *Tsubster) Typ(t *types.Type) *types.Type {
// Defer the CheckSize calls until we have fully-defined
// (possibly-recursive) top-level type.
@@ -1033,14 +1034,14 @@ func (ts *Tsubster) Typ(t *types.Type) *types.Type {
}
func (ts *Tsubster) typ1(t *types.Type) *types.Type {
- if !t.HasTParam() && t.Kind() != types.TFUNC {
+ if !t.HasTParam() && !t.HasShape() && t.Kind() != types.TFUNC {
// Note: function types need to be copied regardless, as the
// types of closures may contain declarations that need
// to be copied. See #45738.
return t
}
- if t.IsTypeParam() {
+ if t.IsTypeParam() || t.IsShape() {
for i, tp := range ts.Tparams {
if tp == t {
return ts.Targs[i]
@@ -1072,14 +1073,14 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type {
var targsChanged bool
var forw *types.Type
- if t.Sym() != nil && t.HasTParam() {
+ if t.Sym() != nil && (t.HasTParam() || t.HasShape()) {
// Need to test for t.HasTParam() again because of special TFUNC case above.
// Translate the type params for this type according to
// the tparam/targs mapping from subst.
neededTargs = make([]*types.Type, len(t.RParams()))
for i, rparam := range t.RParams() {
neededTargs[i] = ts.typ1(rparam)
- if !types.Identical(neededTargs[i], rparam) {
+ if !types.IdenticalStrict(neededTargs[i], rparam) {
targsChanged = true
}
}
@@ -1286,7 +1287,7 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type {
// fields, set force to true.
func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type {
if t.NumFields() == 0 {
- if t.HasTParam() {
+ if t.HasTParam() || t.HasShape() {
// For an empty struct, we need to return a new type,
// since it may now be fully instantiated (HasTParam
// becomes false).
@@ -1388,19 +1389,20 @@ func genericTypeName(sym *types.Sym) string {
return sym.Name[0:strings.Index(sym.Name, "[")]
}
-// Shapify takes a concrete type and returns a GCshape type that can
+// Shapify takes a concrete type and a type param index, and returns a GCshape type that can
// be used in place of the input type and still generate identical code.
// No methods are added - all methods calls directly on a shape should
// be done by converting to an interface using the dictionary.
//
-// TODO: this could take the generic function and base its decisions
-// on how that generic function uses this type argument. For instance,
-// if it doesn't use it as a function argument/return value, then
-// we don't need to distinguish int64 and float64 (because they only
-// differ in how they get passed as arguments). For now, we only
-// unify two different types if they are identical in every possible way.
-func Shapify(t *types.Type) *types.Type {
- assert(!t.HasShape())
+// For now, we only consider two types to have the same shape, if they have exactly
+// the same underlying type or they are both pointer types.
+//
+// Shape types are also distinguished by the index of the type in a type param/arg
+// list. We need to do this so we can distinguish and substitute properly for two
+// type params in the same function that have the same shape for a particular
+// instantiation.
+func Shapify(t *types.Type, index int) *types.Type {
+ assert(!t.IsShape())
// Map all types with the same underlying type to the same shape.
u := t.Underlying()
@@ -1410,15 +1412,24 @@ func Shapify(t *types.Type) *types.Type {
u = types.Types[types.TUINT8].PtrTo()
}
- if s := shaped[u]; s != nil {
+ if shapeMap == nil {
+ shapeMap = map[int]map[*types.Type]*types.Type{}
+ }
+ submap := shapeMap[index]
+ if submap == nil {
+ submap = map[*types.Type]*types.Type{}
+ shapeMap[index] = submap
+ }
+ if s := submap[u]; s != nil {
return s
}
- sym := types.ShapePkg.Lookup(u.LinkString())
+ nm := fmt.Sprintf("%s_%d", u.LinkString(), index)
+ sym := types.ShapePkg.Lookup(nm)
if sym.Def != nil {
// Use any existing type with the same name
- shaped[u] = sym.Def.Type()
- return shaped[u]
+ submap[u] = sym.Def.Type()
+ return submap[u]
}
name := ir.NewDeclNameAt(u.Pos(), ir.OTYPE, sym)
s := types.NewNamed(name)
@@ -1428,8 +1439,8 @@ func Shapify(t *types.Type) *types.Type {
s.SetHasShape(true)
name.SetType(s)
name.SetTypecheck(1)
- shaped[u] = s
+ submap[u] = s
return s
}
-var shaped = map[*types.Type]*types.Type{}
+var shapeMap map[int]map[*types.Type]*types.Type
diff --git a/src/cmd/compile/internal/types/identity.go b/src/cmd/compile/internal/types/identity.go
index 2e9e2f4fd8..dce7d29143 100644
--- a/src/cmd/compile/internal/types/identity.go
+++ b/src/cmd/compile/internal/types/identity.go
@@ -4,19 +4,30 @@
package types
+const (
+ identIgnoreTags = 1 << iota
+ identStrict
+)
+
// Identical reports whether t1 and t2 are identical types, following the spec rules.
// Receiver parameter types are ignored. Named (defined) types are only equal if they
// are pointer-equal - i.e. there must be a unique types.Type for each specific named
// type. Also, a type containing a shape type is considered identical to another type
// (shape or not) if their underlying types are the same, or they are both pointers.
func Identical(t1, t2 *Type) bool {
- return identical(t1, t2, true, nil)
+ return identical(t1, t2, 0, nil)
}
// IdenticalIgnoreTags is like Identical, but it ignores struct tags
// for struct identity.
func IdenticalIgnoreTags(t1, t2 *Type) bool {
- return identical(t1, t2, false, nil)
+ return identical(t1, t2, identIgnoreTags, nil)
+}
+
+// IdenticalStrict is like Identical, but matches types exactly, without the
+// exception for shapes.
+func IdenticalStrict(t1, t2 *Type) bool {
+ return identical(t1, t2, identStrict, nil)
}
type typePair struct {
@@ -24,7 +35,7 @@ type typePair struct {
t2 *Type
}
-func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) bool {
+func identical(t1, t2 *Type, flags int, assumedEqual map[typePair]struct{}) bool {
if t1 == t2 {
return true
}
@@ -32,7 +43,7 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
return false
}
if t1.sym != nil || t2.sym != nil {
- if t1.HasShape() || t2.HasShape() {
+ if flags&identStrict == 0 && (t1.HasShape() || t2.HasShape()) {
switch t1.kind {
case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TINT, TUINT, TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64, TBOOL, TSTRING, TPTR, TUNSAFEPTR:
return true
@@ -78,7 +89,7 @@ cont:
}
for i, f1 := range t1.AllMethods().Slice() {
f2 := t2.AllMethods().Index(i)
- if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+ if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, flags, assumedEqual) {
return false
}
}
@@ -90,10 +101,10 @@ cont:
}
for i, f1 := range t1.FieldSlice() {
f2 := t2.Field(i)
- if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+ if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, flags, assumedEqual) {
return false
}
- if cmpTags && f1.Note != f2.Note {
+ if (flags&identIgnoreTags) == 0 && f1.Note != f2.Note {
return false
}
}
@@ -111,7 +122,7 @@ cont:
}
for i, f1 := range fs1 {
f2 := fs2[i]
- if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+ if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, flags, assumedEqual) {
return false
}
}
@@ -129,10 +140,10 @@ cont:
}
case TMAP:
- if !identical(t1.Key(), t2.Key(), cmpTags, assumedEqual) {
+ if !identical(t1.Key(), t2.Key(), flags, assumedEqual) {
return false
}
}
- return identical(t1.Elem(), t2.Elem(), cmpTags, assumedEqual)
+ return identical(t1.Elem(), t2.Elem(), flags, assumedEqual)
}