]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: start using sub-dictionary entries where needed
authorDan Scales <danscales@google.com>
Tue, 29 Jun 2021 01:04:58 +0000 (18:04 -0700)
committerDan Scales <danscales@google.com>
Fri, 2 Jul 2021 19:07:06 +0000 (19:07 +0000)
Added new struct instInfo for information about an instantiation (of a
generic function/method with gcshapes or concrete types). We use this to
remember the dictionary param node, the nodes where sub-dictionaries
need to be used, etc. The instInfo map replaces the Stencil map in
Package.

Added code to access sub-dictionary entries at the appropriate call
sites. We are currently still calculating the corresponding main
dictionary, even when we really only need a sub-dictionary. I'll clean
that up in a follow-up CL.

Added code to deal with "generic" closures (closures that reference some
generic variables/types). We decided that closures will share the same
dictionary as the containing function (accessing the dictionary via a
closure variable). So, the getGfInfo function now traverses all the
nodes of each closure in a function that it is analyzing, so that a
function's dictionary has all the entries needed for all its closures as
well. Also, the instInfo of a closure is largely shared with its
containing function. A good test for generic closures already exists
with orderedmap.go.

Other improvements:
 - Only create sub-dictionary entries when the function/method
   call/value or closure actually has type params in it. Added new test
   file subdict.go with an example where a generic method has an
   instantiated method call that does not depend not have type params.

Change-Id: I691b9dc024a89d2305fcf1d8ba8540e53c9d103f
Reviewed-on: https://go-review.googlesource.com/c/go/+/331516
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/compile/internal/ir/package.go
src/cmd/compile/internal/noder/irgen.go
src/cmd/compile/internal/noder/stencil.go
test/typeparam/subdict.go [new file with mode: 0644]

index e4b93d113eda9c89b44512e2e5957c977df15a03..3896e2b91b1177dbc41977960d7fe26648e88d6e 100644 (file)
@@ -32,7 +32,4 @@ type Package struct {
 
        // Exported (or re-exported) symbols.
        Exports []*Name
-
-       // Map from function names of stencils to already-created stencils.
-       Stencils map[*types.Sym]*Func
 }
index 8f39061250675105c1015671e0fa077d0d07b7d3..299d468a15f9a71b14e6fa918c47c587ba8baaa6 100644 (file)
@@ -100,11 +100,34 @@ func check2(noders []*noder) {
 type gfInfo struct {
        tparams      []*types.Type
        derivedTypes []*types.Type
-       // Node in generic function that requires a subdictionary. Some of these
-       // are not function/method values (not strictly calls).
+       // Nodes in generic function that requires a subdictionary. Includes
+       // method and function calls (OCALL), function values (OFUNCINST), method
+       // values/expressions (OXDOT).
        subDictCalls []ir.Node
 }
 
+// instInfo is information gathered on an gcshape (or fully concrete)
+// 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
+       // Addr of static dictionary associated with this instantiation. This is the
+       // dictionary you should pass if all the type args are concreate. Soon to be
+       // removed, when creating static dictionary and instantiated function are
+       // separated.
+       dictAddr ir.Node
+
+       gf     *ir.Name // The associated generic function
+       gfInfo *gfInfo
+
+       startSubDict int // Start of dict entries for subdictionaries
+       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
+}
+
 type irgen struct {
        target *ir.Package
        self   *types2.Package
@@ -123,6 +146,11 @@ type irgen struct {
        // 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.
+       instInfoMap map[*types.Sym]*instInfo
 }
 
 func (g *irgen) generate(noders []*noder) {
index c04300a165461cc9b4ba5f597f44f1032e024c4a..9d70e0e299e408a434c459b2e7b073f2a2988a40 100644 (file)
@@ -44,7 +44,7 @@ func infoPrint(format string, a ...interface{}) {
 // encountered already or new ones that are encountered during the stenciling
 // process.
 func (g *irgen) stencil() {
-       g.target.Stencils = make(map[*types.Sym]*ir.Func)
+       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.
@@ -86,6 +86,9 @@ func (g *irgen) stencil() {
                // to calling that function directly.
                modified := false
                closureRequired := false
+               // declInfo will be non-nil exactly if we are scanning an instantiated function
+               declInfo := g.instInfoMap[decl.Sym()]
+
                ir.Visit(decl, func(n ir.Node) {
                        if n.Op() == ir.OFUNCINST {
                                // generic F, not immediately called
@@ -103,11 +106,24 @@ func (g *irgen) stencil() {
                                call := n.(*ir.CallExpr)
                                inst := call.X.(*ir.InstExpr)
                                st, dict := g.getInstantiationForNode(inst)
-                               if infoPrintMode && g.target.Stencils[decl.Sym()] == nil {
+                               dictkind := "Main dictionary"
+                               if declInfo != nil {
+                                       // Get the dictionary arg via sub-dictionary reference
+                                       entry, ok := declInfo.dictEntryMap[n]
+                                       // If the entry is not found, it must be that
+                                       // this node was 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)
+                                               dictkind = "Sub-dictionary"
+                                       }
+                               }
+                               if infoPrintMode {
                                        if inst.X.Op() == ir.OMETHVALUE {
-                                               fmt.Printf("Main dictionary in %v at generic method call: %v - %v\n", decl, inst.X, call)
+                                               fmt.Printf("%s in %v at generic method call: %v - %v\n", dictkind, decl, inst.X, call)
                                        } else {
-                                               fmt.Printf("Main dictionary in %v at generic function call: %v - %v\n", decl, inst.X, call)
+                                               fmt.Printf("%s in %v at generic function call: %v - %v\n", dictkind, decl, inst.X, call)
                                        }
                                }
                                // Replace the OFUNCINST with a direct reference to the
@@ -147,6 +163,17 @@ func (g *irgen) stencil() {
                                }
 
                                st, dict := g.getInstantiation(gf, targs, true)
+                               entry, ok := declInfo.dictEntryMap[n]
+                               // TODO: Not creating sub-dictionary entry for
+                               // absDifference in absdiff.go yet. Unusual case,
+                               // where there are different generic method
+                               // implementations of Abs in absDifference.
+                               if ok {
+                                       if infoPrintMode {
+                                               fmt.Printf("Sub-dictionary in %v at generic method call: %v\n", decl, call)
+                                       }
+                                       dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictLen)
+                               }
                                call.SetOp(ir.OCALL)
                                call.X = st.Nname
                                call.Args.Prepend(dict, meth.X)
@@ -175,8 +202,6 @@ func (g *irgen) stencil() {
                                ir.EditChildren(x, edit)
                                switch {
                                case x.Op() == ir.OFUNCINST:
-                                       // TODO: only set outer!=nil if this instantiation uses
-                                       // a type parameter from outer. See comment in buildClosure.
                                        return g.buildClosure(outer, x)
                                case x.Op() == ir.OMETHEXPR && len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0 &&
                                        !types.IsInterfaceMethod(x.(*ir.SelectorExpr).Selection.Type): // TODO: test for ptr-to-method case
@@ -208,6 +233,11 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
        var dictValue ir.Node // dictionary to use
        var rcvrValue ir.Node // receiver, if a method value
        typ := x.Type()       // type of the closure
+       var outerInfo *instInfo
+       if outer != nil {
+               outerInfo = g.instInfoMap[outer.Sym()]
+       }
+       usingSubdict := false
        if x.Op() == ir.OFUNCINST {
                inst := x.(*ir.InstExpr)
 
@@ -234,11 +264,20 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
                // as its first two arguments.
                // dictValue is the value to use for the dictionary argument.
                target, dictValue = g.getInstantiation(gf, targs, rcvrValue != nil)
-               if infoPrintMode && (outer == nil || g.target.Stencils[outer.Sym()] == nil) {
+               dictkind := "Main dictionary"
+               if outerInfo != nil {
+                       entry, ok := outerInfo.dictEntryMap[x]
+                       if ok {
+                               dictValue = getDictionaryEntry(x.Pos(), outerInfo.dictParam, entry, outerInfo.dictLen)
+                               dictkind = "Sub-dictionary"
+                               usingSubdict = true
+                       }
+               }
+               if infoPrintMode {
                        if rcvrValue == nil {
-                               fmt.Printf("Main dictionary in %v for function value %v\n", outer, inst.X)
+                               fmt.Printf("%s in %v for generic function value %v\n", dictkind, outer, inst.X)
                        } else {
-                               fmt.Printf("Main dictionary in %v for method value %v\n", outer, inst.X)
+                               fmt.Printf("%s in %v for generic method value %v\n", dictkind, outer, inst.X)
                        }
                }
        } else { // ir.OMETHEXPR
@@ -270,8 +309,17 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
                        }
                }
                target, dictValue = g.getInstantiation(gf, targs, true)
-               if infoPrintMode && (outer == nil || g.target.Stencils[outer.Sym()] == nil) {
-                       fmt.Printf("Main dictionary in %v for method expression %v\n", outer, x)
+               dictkind := "Main dictionary"
+               if outerInfo != nil {
+                       entry, ok := outerInfo.dictEntryMap[x]
+                       if ok {
+                               dictValue = getDictionaryEntry(x.Pos(), outerInfo.dictParam, entry, outerInfo.dictLen)
+                               dictkind = "Sub-dictionary"
+                               usingSubdict = true
+                       }
+               }
+               if infoPrintMode {
+                       fmt.Printf("%s in %v for method expression %v\n", dictkind, outer, x)
                }
        }
 
@@ -386,14 +434,12 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
 
        // First, figure out the dictionary argument.
        var dict2Var ir.Node
-       if outer != nil {
-               // If there's an outer function, the dictionary value will be read from
-               // the dictionary of the outer function.
-               // TODO: only use a subdictionary if any of the instantiating types
-               // depend on the type params of the outer function.
+       if usingSubdict {
+               // Capture sub-dictionary calculated in the outer function
                dict2Var = ir.CaptureName(pos, fn, dictVar)
+               typed(types.Types[types.TUINTPTR], dict2Var)
        } else {
-               // No outer function, instantiating types are known concrete types.
+               // Static dictionary, so can be used directly in the closure
                dict2Var = dictValue
        }
        // Also capture the receiver variable.
@@ -695,8 +741,8 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, targs []*types.Type, isMeth
                nameNode.Func.Dcl = nameNode.Func.Inl.Dcl
        }
        sym := typecheck.MakeInstName(nameNode.Sym(), targs, isMeth)
-       st := g.target.Stencils[sym]
-       if st == nil {
+       info := g.instInfoMap[sym]
+       if info == nil {
                if false {
                        // Testing out gcshapeType() and gcshapeName()
                        for i, t := range targs {
@@ -706,27 +752,38 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, targs []*types.Type, isMeth
                }
                // If instantiation doesn't exist yet, create it and add
                // to the list of decls.
-               st = g.genericSubst(sym, nameNode, targs, isMeth)
+               gfInfo := g.getGfInfo(nameNode)
+               info = &instInfo{
+                       gf:           nameNode,
+                       gfInfo:       gfInfo,
+                       startSubDict: len(targs) + len(gfInfo.derivedTypes),
+                       dictLen:      len(targs) + len(gfInfo.derivedTypes) + len(gfInfo.subDictCalls),
+                       dictEntryMap: make(map[ir.Node]int),
+               }
+               // genericSubst fills in info.dictParam and info.dictEntryMap.
+               st := g.genericSubst(sym, nameNode, targs, isMeth, info)
+               info.fun = st
+               info.dictAddr = g.getDictionaryValue(nameNode, targs, isMeth)
+               g.instInfoMap[sym] = info
                // This ensures that the linker drops duplicates of this instantiation.
                // All just works!
                st.SetDupok(true)
-               g.target.Stencils[sym] = st
                g.target.Decls = append(g.target.Decls, st)
                if base.Flag.W > 1 {
                        ir.Dump(fmt.Sprintf("\nstenciled %v", st), st)
                }
        }
-       return st, g.getDictionaryValue(nameNode, targs, isMeth)
+       return info.fun, info.dictAddr
 }
 
 // Struct containing info needed for doing the substitution as we create the
 // instantiation of a generic function with specified type arguments.
 type subster struct {
-       g          *irgen
-       isMethod   bool     // If a method is being instantiated
-       newf       *ir.Func // Func node for the new stenciled function
-       ts         typecheck.Tsubster
-       dictionary *ir.Name // Name of dictionary variable
+       g        *irgen
+       isMethod bool     // If a method is being instantiated
+       newf     *ir.Func // Func node for the new stenciled function
+       ts       typecheck.Tsubster
+       info     *instInfo // Place to put extra info in the instantiation
 }
 
 // genericSubst returns a new function with name newsym. The function is an
@@ -734,8 +791,8 @@ type subster struct {
 // args targs. For a method with a generic receiver, it returns an instantiated
 // function type where the receiver becomes the first parameter. Otherwise the
 // instantiated method would still need to be transformed by later compiler
-// phases.
-func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []*types.Type, isMethod bool) *ir.Func {
+// phases.  genericSubst fills in info.dictParam and info.dictEntryMap.
+func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []*types.Type, isMethod bool, info *instInfo) *ir.Func {
        var tparams []*types.Type
        if isMethod {
                // Get the type params from the method receiver (after skipping
@@ -769,6 +826,7 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []*type
                g:        g,
                isMethod: isMethod,
                newf:     newf,
+               info:     info,
                ts: typecheck.Tsubster{
                        Tparams: tparams,
                        Targs:   targs,
@@ -778,13 +836,7 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []*type
 
        newf.Dcl = make([]*ir.Name, 0, len(gf.Dcl)+1)
 
-       // Replace the types in the function signature.
-       // Ugly: also, we have to insert the Name nodes of the parameters/results into
-       // the function type. The current function type has no Nname fields set,
-       // because it came via conversion from the types2 type.
-       oldt := nameNode.Type()
-       // We also transform a generic method type to the corresponding
-       // instantiated function type where the dictionary is the first parameter.
+       // Create the needed dictionary param
        dictionarySym := newsym.Pkg.Lookup(".dict")
        dictionaryType := types.Types[types.TUINTPTR]
        dictionaryName := ir.NewNameAt(gf.Pos(), dictionarySym)
@@ -800,11 +852,21 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []*type
        }
        dictionaryArg := types.NewField(gf.Pos(), dictionarySym, dictionaryType)
        dictionaryArg.Nname = dictionaryName
-       subst.dictionary = dictionaryName
+       info.dictParam = dictionaryName
+
+       // We add the dictionary as the first parameter in the function signature.
+       // We also transform a method type to the corresponding function type
+       // (make the receiver be the next parameter after the dictionary).
+       oldt := nameNode.Type()
        var args []*types.Field
        args = append(args, dictionaryArg)
        args = append(args, oldt.Recvs().FieldSlice()...)
        args = append(args, oldt.Params().FieldSlice()...)
+
+       // Replace the types in the function signature via subst.fields.
+       // Ugly: also, we have to insert the Name nodes of the parameters/results into
+       // the function type. The current function type has no Nname fields set,
+       // because it came via conversion from the types2 type.
        newt := types.NewSignature(oldt.Pkg(), nil, nil,
                subst.fields(ir.PPARAM, args, newf.Dcl),
                subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl))
@@ -884,6 +946,26 @@ func (g *irgen) checkDictionary(name *ir.Name, targs []*types.Type) (code []ir.N
        return
 }
 
+// getDictionaryEntry gets the i'th entry in the dictionary dict.
+func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
+       // Convert dictionary to *[N]uintptr
+       // All entries in the dictionary are pointers. They all point to static data, though, so we
+       // treat them as uintptrs so the GC doesn't need to keep track of them.
+       d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], dict)
+       d.SetTypecheck(1)
+       d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(size)).PtrTo(), d)
+       d.SetTypecheck(1)
+
+       // Load entry i out of the dictionary.
+       deref := ir.NewStarExpr(pos, d)
+       typed(d.Type().Elem(), deref)
+       idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), dict) // TODO: what to set orig to?
+       typed(types.Types[types.TUINTPTR], idx)
+       r := ir.NewIndexExpr(pos, deref, idx)
+       typed(types.Types[types.TUINTPTR], r)
+       return r
+}
+
 // getDictionaryType returns a *runtime._type from the dictionary corresponding to the input type.
 // The input type must be a type parameter (TODO: or a local derived type).
 func (subst *subster) getDictionaryType(pos src.XPos, t *types.Type) ir.Node {
@@ -898,21 +980,10 @@ func (subst *subster) getDictionaryType(pos src.XPos, t *types.Type) ir.Node {
                base.Fatalf(fmt.Sprintf("couldn't find type param %+v", t))
        }
 
-       // Convert dictionary to *[N]uintptr
-       // All entries in the dictionary are pointers. They all point to static data, though, so we
-       // treat them as uintptrs so the GC doesn't need to keep track of them.
-       d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], subst.dictionary)
-       d.SetTypecheck(1)
-       d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(len(tparams))).PtrTo(), d)
-       d.SetTypecheck(1)
-
-       // Load entry i out of the dictionary.
-       deref := ir.NewStarExpr(pos, d)
-       typed(d.Type().Elem(), deref)
-       idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), subst.dictionary) // TODO: what to set orig to?
-       typed(types.Types[types.TUINTPTR], idx)
-       r := ir.NewIndexExpr(pos, deref, idx)
-       typed(types.Types[types.TUINT8].PtrTo(), r) // standard typing of a *runtime._type in the compiler is *byte
+       r := getDictionaryEntry(pos, subst.info.dictParam, i, len(tparams))
+       // 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)
        return r
 }
 
@@ -957,6 +1028,18 @@ func (subst *subster) node(n ir.Node) ir.Node {
                                m.SetType(subst.ts.Typ(x.Type()))
                        }
                }
+
+               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)
@@ -1109,7 +1192,26 @@ func (subst *subster) node(n ir.Node) ir.Node {
                        ir.CurFunc = newfn
                        subst.newf = newfn
                        newfn.Dcl = subst.namelist(oldfn.Dcl)
-                       newfn.ClosureVars = subst.namelist(oldfn.ClosureVars)
+
+                       // Make a closure variable for the dictionary of the
+                       // containing function.
+                       cdict := ir.CaptureName(oldfn.Pos(), newfn, subst.info.dictParam)
+                       typed(types.Types[types.TUINTPTR], cdict)
+                       ir.FinishCaptureNames(oldfn.Pos(), saveNewf, newfn)
+                       newfn.ClosureVars = append(newfn.ClosureVars, subst.namelist(oldfn.ClosureVars)...)
+
+                       // 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.
+                       cinfo := &instInfo{
+                               fun:          newfn,
+                               dictParam:    cdict,
+                               startSubDict: subst.info.startSubDict,
+                               dictLen:      subst.info.dictLen,
+                               dictEntryMap: subst.info.dictEntryMap,
+                       }
+                       subst.g.instInfoMap[newfn.Nname.Sym()] = cinfo
 
                        typed(subst.ts.Typ(oldfn.Nname.Type()), newfn.Nname)
                        typed(newfn.Nname.Type(), newfn.OClosure)
@@ -1257,7 +1359,7 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
 
        // Initialize the dictionary, if we haven't yet already.
        if lsym := sym.Linksym(); len(lsym.P) == 0 {
-               infoPrint("Creating dictionary %v\n", sym.Name)
+               infoPrint("=== Creating dictionary %v\n", sym.Name)
                off := 0
                // Emit an entry for each targ (concrete type or gcshape).
                for _, t := range targs {
@@ -1279,7 +1381,8 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
                // Emit an entry for each subdictionary (after substituting targs)
                for _, n := range info.subDictCalls {
                        var sym *types.Sym
-                       if n.Op() == ir.OCALL {
+                       switch n.Op() {
+                       case ir.OCALL:
                                call := n.(*ir.CallExpr)
                                if call.X.Op() == ir.OXDOT {
                                        subtargs := deref(call.X.(*ir.SelectorExpr).X.Type()).RParams()
@@ -1304,11 +1407,9 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
                                                subtargs[i] = subst.Typ(t)
                                        }
                                        sym = g.getDictionarySym(nameNode, subtargs, isMeth)
-                                       // TODO: This can actually be a static
-                                       // main dictionary, if all of the subtargs
-                                       // are concrete types (!HasTParam)
                                }
-                       } else if n.Op() == ir.OFUNCINST {
+
+                       case ir.OFUNCINST:
                                inst := n.(*ir.InstExpr)
                                nameNode := inst.X.(*ir.Name)
                                subtargs := typecheck.TypesOf(inst.Targs)
@@ -1316,10 +1417,8 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
                                        subtargs[i] = subst.Typ(t)
                                }
                                sym = g.getDictionarySym(nameNode, subtargs, false)
-                               // TODO: This can actually be a static
-                               // main dictionary, if all of the subtargs
-                               // are concrete types (!HasTParam)
-                       } else if n.Op() == ir.OXDOT {
+
+                       case ir.OXDOT:
                                selExpr := n.(*ir.SelectorExpr)
                                subtargs := selExpr.X.Type().RParams()
                                s2targs := make([]*types.Type, len(subtargs))
@@ -1328,14 +1427,16 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
                                }
                                nameNode := selExpr.Selection.Nname.(*ir.Name)
                                sym = g.getDictionarySym(nameNode, s2targs, true)
+
+                       default:
+                               assert(false)
                        }
-                       // TODO: handle closure cases that need sub-dictionaries, get rid of conditional
-                       if sym != nil {
-                               off = objw.SymPtr(lsym, off, sym.Linksym(), 0)
-                               infoPrint(" - Subdict %v\n", sym.Name)
-                       }
+
+                       off = objw.SymPtr(lsym, off, sym.Linksym(), 0)
+                       infoPrint(" - Subdict %v\n", sym.Name)
                }
                objw.Global(lsym, int32(off), obj.DUPOK|obj.RODATA)
+               infoPrint("=== Done dictionary\n")
 
                // Add any new, fully instantiated types seen during the substitution to g.instTypeList.
                g.instTypeList = append(g.instTypeList, subst.InstTypeList...)
@@ -1363,6 +1464,26 @@ 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 {
+       for _, n := range targs {
+               if n.Type().HasTParam() {
+                       return true
+               }
+       }
+       return false
+}
+
+// hasTParamNodes returns true if any type in targs has a typeparam.
+func hasTParamTypes(targs []*types.Type) bool {
+       for _, t := range targs {
+               if t.HasTParam() {
+                       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 {
@@ -1377,8 +1498,9 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
        if recv != nil {
                info.tparams = deref(recv.Type).RParams()
        } else {
-               info.tparams = make([]*types.Type, len(gn.Type().TParams().FieldSlice()))
-               for i, f := range gn.Type().TParams().FieldSlice() {
+               tparams := gn.Type().TParams().FieldSlice()
+               info.tparams = make([]*types.Type, len(tparams))
+               for i, f := range tparams {
                        info.tparams[i] = f.Type
                }
        }
@@ -1387,23 +1509,28 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
        }
 
        if infoPrintMode {
-               fmt.Printf(">>> Info for %v\n", gn)
+               fmt.Printf(">>> GfInfo for %v\n", gn)
                for _, t := range info.tparams {
                        fmt.Printf("  Typeparam %v\n", t)
                }
-               for _, t := range info.derivedTypes {
-                       fmt.Printf("  Derived type %v\n", t)
-               }
        }
 
-       for _, stmt := range gf.Body {
-               ir.Visit(stmt, func(n ir.Node) {
-                       if n.Op() == ir.OFUNCINST && !n.(*ir.InstExpr).Implicit() {
+       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) {
                                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 &&
-                               len(n.(*ir.SelectorExpr).X.Type().RParams()) > 0 {
+                       }
+               } else if n.Op() == ir.OXDOT && !n.(*ir.SelectorExpr).Implicit() &&
+                       n.(*ir.SelectorExpr).Selection != nil &&
+                       len(n.(*ir.SelectorExpr).X.Type().RParams()) > 0 {
+                       if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE {
+                               infoPrint("  Closure&subdictionary required at generic meth expr %v\n", n)
+                       } else {
+                               infoPrint("  Closure&subdictionary required at generic meth value %v\n", n)
+                       }
+                       if hasTParamTypes(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 {
@@ -1411,40 +1538,43 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
                                }
                                info.subDictCalls = append(info.subDictCalls, n)
                        }
-                       if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST {
-                               infoPrint("  Subdictionary at generic function call: %v - %v\n", n.(*ir.CallExpr).X.(*ir.InstExpr).X, n)
-                               n.(*ir.CallExpr).X.(*ir.InstExpr).SetImplicit(true)
+               }
+               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) {
+                               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 &&
-                               len(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 {
+               }
+               if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT &&
+                       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()) {
                                infoPrint("  Subdictionary at generic method call: %v\n", n)
-                               n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true)
                                info.subDictCalls = append(info.subDictCalls, n)
                        }
-                       if n.Op() == ir.OCLOSURE {
-                               oldfn := n.(*ir.ClosureExpr).Func
-                               needDict := false
-                               if oldfn.Nname.Type().HasTParam() {
-                                       needDict = true
-                                       infoPrint("  Subdictionary for closure that has generic params: %v\n", oldfn)
-                               } else {
-                                       for _, cv := range oldfn.ClosureVars {
-                                               if cv.Type().HasTParam() {
-                                                       needDict = true
-                                                       infoPrint("  Subdictionary for closure that has generic capture: %v\n", oldfn)
-                                                       break
-                                               }
-                                       }
-                               }
-                               if needDict {
-                                       info.subDictCalls = append(info.subDictCalls, n)
-                               }
+               }
+               if n.Op() == ir.OCLOSURE {
+                       // 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 {
+                               ir.Visit(n1, visitFunc)
                        }
+               }
 
-                       addType(&info, n, n.Type())
-               })
+               addType(&info, n, n.Type())
+       }
+
+       for _, stmt := range gf.Body {
+               ir.Visit(stmt, visitFunc)
+       }
+       if infoPrintMode {
+               for _, t := range info.derivedTypes {
+                       fmt.Printf("  Derived type %v\n", t)
+               }
+               fmt.Printf(">>> Done Gfinfo\n")
        }
        g.gfInfoMap[gn.Sym()] = &info
        return &info
diff --git a/test/typeparam/subdict.go b/test/typeparam/subdict.go
new file mode 100644 (file)
index 0000000..c899af6
--- /dev/null
@@ -0,0 +1,42 @@
+// run -gcflags=-G=3
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test cases where a main dictionary is needed inside a generic function/method, because 
+// we are calling a method on a fully-instantiated type or a fully-instantiated function.
+// (probably not common situations, of course)
+
+package main
+
+import (
+       "fmt"
+)
+
+type value[T comparable] struct {
+       val T
+}
+
+func (v *value[T]) test(def T) bool {
+       return (v.val == def)
+}
+
+func (v *value[T]) get(def T) T {
+       var c value[int]
+       if c.test(32) {
+               return def
+       } else if v.test(def) {
+               return def
+       } else {
+               return v.val
+       }
+}
+
+
+func main() {
+       var s value[string]
+       if got, want := s.get("ab"), ""; got != want {
+               panic(fmt.Sprintf("get() == %d, want %d", got, want))
+       }
+}