}
// 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
+// particular shapes. shapeParams, derivedTypes, subDictCalls, itabConvs, and methodExprClosures
+// 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.
// Nodes in the instantiation that are a conversion from a typeparam/derived
// type to a specific interface.
itabConvs []ir.Node
+ // Method expression closures. For a generic type T with method M(arg1, arg2) res,
+ // these closures are func(rcvr T, arg1, arg2) res.
+ // These closures capture no variables, they are just the generic version of ·f symbols
+ // that live in the dictionary instead of in the readonly globals section.
+ methodExprClosures []methodExprClosure
// Mapping from each shape type that substitutes a type param, to its
// type bound (which is also substituted with shapes if it is parameterized)
// 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
+ startSubDict int // Start of dict entries for subdictionaries
+ startItabConv int // Start of dict entries for itab conversions
+ startMethodExprClosures int // Start of dict entries for closures for method expressions
+ dictLen int // Total number of entries in dictionary
+}
+
+type methodExprClosure struct {
+ idx int // index in list of shape parameters
+ name string // method name
}
// instInfo is information gathered on an shape instantiation of a function.
instInfoMap map[*types.Sym]*instInfo
// Dictionary syms which we need to finish, by writing out any itabconv
- // entries.
+ // or method expression closure entries.
dictSymsToFinalize []*delayInfo
// New instantiations created during this round of buildInstantiations().
return r
}
+// getDictionaryEntryAddr gets the address of the i'th entry in dictionary dict.
+func getDictionaryEntryAddr(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
+ a := ir.NewAddrExpr(pos, getDictionaryEntry(pos, dict, i, size))
+ typed(types.Types[types.TUINTPTR].PtrTo(), a)
+ return a
+}
+
// getDictionaryType returns a *runtime._type from the dictionary entry i (which
// 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.
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.
+ idx := findMethodExprClosure(info.dictInfo, mse)
+ c := getDictionaryEntryAddr(m.Pos(), info.dictParam, info.dictInfo.startMethodExprClosures+idx, info.dictInfo.dictLen)
+ m = ir.NewConvExpr(m.Pos(), ir.OCONVNOP, mse.Type(), c)
+ m.SetTypecheck(1)
} else {
// If we can't find the selected method in the
// AllMethods of the bound, then this must be an access
// dictionaries and method instantiations to be complete, so, to avoid recursive
// dependencies, we finalize the itab lsyms only after all dictionaries syms and
// instantiations have been created.
+// Also handles writing method expression closures into the dictionaries.
func (g *genInst) finalizeSyms() {
for _, d := range g.dictSymsToFinalize {
infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
}
}
+ // Emit an entry for each method expression closure.
+ // Each entry is a (captureless) closure pointing to the method on the instantiating type.
+ // In other words, the entry is a runtime.funcval whose fn field is set to the method
+ // in question, and has no other fields. The address of this dictionary entry can be
+ // cast to a func of the appropriate type.
+ // TODO: do these need to be done when finalizing, or can we do them earlier?
+ for _, bf := range info.methodExprClosures {
+ rcvr := d.targs[bf.idx]
+ rcvr2 := deref(rcvr)
+ found := false
+ typecheck.CalcMethods(rcvr2) // Ensure methods on all instantiating types are computed.
+ for _, f := range rcvr2.AllMethods().Slice() {
+ if f.Sym.Name == bf.name {
+ codePtr := ir.MethodSym(rcvr, f.Sym).Linksym()
+ d.off = objw.SymPtr(lsym, d.off, codePtr, 0)
+ infoPrint(" + MethodExprClosure for %v.%s\n", rcvr, bf.name)
+ found = true
+ break
+ }
+ }
+ if !found {
+ base.Fatalf("method %s on %v not found", bf.name, rcvr)
+ }
+ }
+
objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA)
infoPrint("=== Finalized dictionary %s\n", d.sym.Name)
}
}
// getInstInfo get the dictionary format for a function instantiation- type params, derived
-// types, and needed subdictionaries and itabs.
+// types, and needed subdictionaries, itabs, and method expression closures.
func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) {
info := instInfo.dictInfo
info.shapeParams = shapes
}
case ir.OXDOT:
se := n.(*ir.SelectorExpr)
+ if se.X.Op() == ir.OTYPE && se.X.Type().IsShape() {
+ addMethodExprClosure(info, se)
+ break
+ }
if isBoundMethod(info, se) {
+ // TODO: handle these using method expression closures also.
infoPrint(" Itab for bound call: %v\n", n)
info.itabConvs = append(info.itabConvs, n)
}
}
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)
+ info.startMethodExprClosures = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs)
+ info.dictLen = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs) + len(info.methodExprClosures)
}
// isBoundMethod returns true if the selection indicated by se is a bound method of
return typecheck.Lookdot1(se, se.Sel, bound, bound.AllMethods(), 1) != nil
}
+func shapeIndex(info *dictInfo, t *types.Type) int {
+ for i, s := range info.shapeParams {
+ if s == t {
+ return i
+ }
+ }
+ base.Fatalf("can't find type %v in shape params", t)
+ return -1
+}
+
+// addMethodExprClosure adds the T.M method expression to the list of bound method expressions
+// used in the generic body.
+// isBoundMethod must have returned true on the same arguments.
+func addMethodExprClosure(info *dictInfo, se *ir.SelectorExpr) {
+ idx := shapeIndex(info, se.X.Type())
+ name := se.Sel.Name
+ for _, b := range info.methodExprClosures {
+ if idx == b.idx && name == b.name {
+ return
+ }
+ }
+ info.methodExprClosures = append(info.methodExprClosures, methodExprClosure{idx: idx, name: name})
+}
+
+// findMethodExprClosure finds the entry in the dictionary to use for the T.M
+// method expression encoded in se.
+// isBoundMethod must have returned true on the same arguments.
+func findMethodExprClosure(info *dictInfo, se *ir.SelectorExpr) int {
+ idx := shapeIndex(info, se.X.Type())
+ name := se.Sel.Name
+ for i, b := range info.methodExprClosures {
+ if idx == b.idx && name == b.name {
+ return i
+ }
+ }
+ base.Fatalf("can't find method expression closure for %s %s", se.X.Type(), name)
+ return -1
+}
+
// addType adds t to info.derivedTypes if it is parameterized type (which is not
// just a simple shape) that is different from any existing type on
// info.derivedTypes.
}
return rcvr
}
-
-// buildClosure2 makes a closure to implement a method expression m (generic form x)
-// which has a shape type as receiver. If the receiver is exactly a shape (i.e. from
-// a typeparam), then the body of the closure converts m.X (the receiver) to the
-// interface bound type, and makes an interface call with the remaining arguments.
-//
-// The returned closure is fully substituted and has already had any needed
-// transformations done.
-func (g *genInst) buildClosure2(info *instInfo, m ir.Node) ir.Node {
- outer := info.fun
- pos := m.Pos()
- typ := m.Type() // type of the closure
-
- fn, formalParams, formalResults := startClosure(pos, outer, typ)
-
- // Capture dictionary calculated in the outer function
- dictVar := ir.CaptureName(pos, fn, info.dictParam)
- typed(types.Types[types.TUINTPTR], dictVar)
-
- // Build arguments to call inside the closure.
- var args []ir.Node
- for i := 0; i < typ.NumParams(); i++ {
- args = append(args, formalParams[i].Nname.(*ir.Name))
- }
-
- // Build call itself. This involves converting the first argument to the
- // bound type (an interface) using the dictionary, and then making an
- // interface call with the remaining arguments.
- var innerCall ir.Node
- rcvr := args[0]
- args = args[1:]
- assert(m.(*ir.SelectorExpr).X.Type().IsShape())
- 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, dst)
- } else {
- rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, m, dst, false)
- }
- 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)
-
- typed(dot.Selection.Type, dot)
- innerCall = ir.NewCallExpr(pos, ir.OCALLINTER, dot, args)
- t := m.Type()
- if t.NumResults() == 0 {
- innerCall.SetTypecheck(1)
- } else if t.NumResults() == 1 {
- typed(t.Results().Field(0).Type, innerCall)
- } else {
- typed(t.Results(), innerCall)
- }
- if len(formalResults) > 0 {
- innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall})
- innerCall.SetTypecheck(1)
- }
- fn.Body = []ir.Node{innerCall}
-
- // We're all done with the captured dictionary
- ir.FinishCaptureNames(pos, outer, fn)
-
- // Do final checks on closure and return it.
- return ir.UseClosure(fn.OClosure, typecheck.Target)
-}