mirror of
https://github.com/golang/go
synced 2024-09-15 22:20:06 +00:00
cmd/compile: save selector/inst info for generic method/function calls
In the dict info, we need to save the SelectorExpr of a generic method call when making its sub-dictionary entry. The generic method call will eventually be transformed into a function call on the method shape instantiation, so we may not always have the selector info available when we need it to create a dictionary. We use this SelectorExpr as needed if the relevant call node has already been transformed. Similarly, we save the InstExpr of generic function calls, since the InstExpr will be dropped when the function call is transformed to a call to a shape instantiation. We use this InstExpr if the relevant function call has already been transformed. Added an extra generic function Some2 and a call to it from Some that exercises the generic function case. The existing test already tests the method call case. Fixes #50264 Change-Id: I2c7c7d79a8e33ca36a5e88e64e913c57500c97f9 Reviewed-on: https://go-review.googlesource.com/c/go/+/373754 Reviewed-by: Keith Randall <khr@golang.org> Trust: Dan Scales <danscales@google.com> Run-TryBot: Dan Scales <danscales@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
e39ab9b01c
commit
f154f8b5bb
|
@ -96,6 +96,17 @@ func check2(noders []*noder) {
|
|||
}
|
||||
}
|
||||
|
||||
// Information about sub-dictionary entries in a dictionary
|
||||
type subDictInfo struct {
|
||||
// Call or XDOT node that requires a dictionary.
|
||||
callNode ir.Node
|
||||
// Saved CallExpr.X node (*ir.SelectorExpr or *InstExpr node) for a generic
|
||||
// method or function call, since this node will get dropped when the generic
|
||||
// method/function call is transformed to a call on the instantiated shape
|
||||
// function. Nil for other kinds of calls or XDOTs.
|
||||
savedXNode ir.Node
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -108,7 +119,7 @@ type dictInfo struct {
|
|||
// Nodes in the instantiation that requires a subdictionary. Includes
|
||||
// method and function calls (OCALL), function values (OFUNCINST), method
|
||||
// values/expressions (OXDOT).
|
||||
subDictCalls []ir.Node
|
||||
subDictCalls []subDictInfo
|
||||
// Nodes in the instantiation that are a conversion from a typeparam/derived
|
||||
// type to a specific interface.
|
||||
itabConvs []ir.Node
|
||||
|
|
|
@ -609,7 +609,7 @@ func (g *genInst) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.N
|
|||
if declInfo != nil {
|
||||
entry := -1
|
||||
for i, de := range declInfo.dictInfo.subDictCalls {
|
||||
if n == de {
|
||||
if n == de.callNode {
|
||||
entry = declInfo.dictInfo.startSubDict + i
|
||||
break
|
||||
}
|
||||
|
@ -1570,8 +1570,9 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
|
|||
markTypeUsed(ts, lsym)
|
||||
}
|
||||
// Emit an entry for each subdictionary (after substituting targs)
|
||||
for _, n := range info.subDictCalls {
|
||||
for _, subDictInfo := range info.subDictCalls {
|
||||
var sym *types.Sym
|
||||
n := subDictInfo.callNode
|
||||
switch n.Op() {
|
||||
case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH:
|
||||
call := n.(*ir.CallExpr)
|
||||
|
@ -1618,31 +1619,31 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
|
|||
} else {
|
||||
// This is the case of a normal
|
||||
// method call on a generic type.
|
||||
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)
|
||||
}
|
||||
sym = g.getDictionarySym(nameNode, s2targs, true)
|
||||
assert(subDictInfo.savedXNode == se)
|
||||
sym = g.getSymForMethodCall(se, &subst)
|
||||
}
|
||||
} else {
|
||||
inst := call.X.(*ir.InstExpr)
|
||||
var nameNode *ir.Name
|
||||
var meth *ir.SelectorExpr
|
||||
var isMeth bool
|
||||
if meth, isMeth = inst.X.(*ir.SelectorExpr); isMeth {
|
||||
nameNode = meth.Selection.Nname.(*ir.Name)
|
||||
inst, ok := call.X.(*ir.InstExpr)
|
||||
if ok {
|
||||
// Code hasn't been transformed yet
|
||||
assert(subDictInfo.savedXNode == inst)
|
||||
}
|
||||
// If !ok, then the generic method/function call has
|
||||
// already been transformed to a shape instantiation
|
||||
// call. Either way, use the SelectorExpr/InstExpr
|
||||
// node saved in info.
|
||||
cex := subDictInfo.savedXNode
|
||||
if se, ok := cex.(*ir.SelectorExpr); ok {
|
||||
sym = g.getSymForMethodCall(se, &subst)
|
||||
} else {
|
||||
nameNode = inst.X.(*ir.Name)
|
||||
inst := cex.(*ir.InstExpr)
|
||||
nameNode := inst.X.(*ir.Name)
|
||||
subtargs := typecheck.TypesOf(inst.Targs)
|
||||
for i, t := range subtargs {
|
||||
subtargs[i] = subst.Typ(t)
|
||||
}
|
||||
sym = g.getDictionarySym(nameNode, subtargs, false)
|
||||
}
|
||||
subtargs := typecheck.TypesOf(inst.Targs)
|
||||
for i, t := range subtargs {
|
||||
subtargs[i] = subst.Typ(t)
|
||||
}
|
||||
sym = g.getDictionarySym(nameNode, subtargs, isMeth)
|
||||
}
|
||||
|
||||
case ir.OFUNCINST:
|
||||
|
@ -1655,16 +1656,7 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
|
|||
sym = g.getDictionarySym(nameNode, subtargs, false)
|
||||
|
||||
case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE:
|
||||
selExpr := n.(*ir.SelectorExpr)
|
||||
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 := typecheck.Lookdot1(selExpr, selExpr.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
|
||||
sym = g.getDictionarySym(nameNode, s2targs, true)
|
||||
sym = g.getSymForMethodCall(n.(*ir.SelectorExpr), &subst)
|
||||
|
||||
default:
|
||||
assert(false)
|
||||
|
@ -1692,6 +1684,24 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
|
|||
return sym
|
||||
}
|
||||
|
||||
// getSymForMethodCall gets the dictionary sym for a method call, method value, or method
|
||||
// expression that has selector se. subst gives the substitution from shape types to
|
||||
// concrete types.
|
||||
func (g *genInst) getSymForMethodCall(se *ir.SelectorExpr, subst *typecheck.Tsubster) *types.Sym {
|
||||
// For everything except method expressions, 'recvType = deref(se.X.Type)' would
|
||||
// also give the receiver type. For method expressions with embedded types, we
|
||||
// need to look at the type of the selection to get the final receiver type.
|
||||
recvType := deref(se.Selection.Type.Recv().Type)
|
||||
genRecvType := recvType.OrigSym().Def.Type()
|
||||
nameNode := typecheck.Lookdot1(se, 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)
|
||||
}
|
||||
return g.getDictionarySym(nameNode, s2targs, true)
|
||||
}
|
||||
|
||||
// finalizeSyms finishes up all dictionaries on g.dictSymsToFinalize, by writing out
|
||||
// any needed LSyms for itabs. The itab lsyms create wrappers which need various
|
||||
// dictionaries and method instantiations to be complete, so, to avoid recursive
|
||||
|
@ -1839,7 +1849,7 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
|
|||
case ir.OFUNCINST:
|
||||
if !callMap[n] && 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)
|
||||
info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
|
||||
}
|
||||
case ir.OMETHEXPR, ir.OMETHVALUE:
|
||||
if !callMap[n] && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) &&
|
||||
|
@ -1850,7 +1860,7 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
|
|||
} else {
|
||||
infoPrint(" Closure&subdictionary required at generic meth value %v\n", n)
|
||||
}
|
||||
info.subDictCalls = append(info.subDictCalls, n)
|
||||
info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
|
||||
}
|
||||
case ir.OCALL:
|
||||
ce := n.(*ir.CallExpr)
|
||||
|
@ -1858,14 +1868,18 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
|
|||
callMap[ce.X] = true
|
||||
if hasShapeNodes(ce.X.(*ir.InstExpr).Targs) {
|
||||
infoPrint(" Subdictionary at generic function/method call: %v - %v\n", ce.X.(*ir.InstExpr).X, n)
|
||||
info.subDictCalls = append(info.subDictCalls, n)
|
||||
// Save the instExpr node for the function call,
|
||||
// since we will lose this information when the
|
||||
// generic function call is transformed to a call
|
||||
// on the shape instantiation.
|
||||
info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X})
|
||||
}
|
||||
}
|
||||
if ce.X.Op() == ir.OXDOT &&
|
||||
isShapeDeref(ce.X.(*ir.SelectorExpr).X.Type()) {
|
||||
callMap[ce.X] = true
|
||||
infoPrint(" Optional subdictionary at generic bound call: %v\n", n)
|
||||
info.subDictCalls = append(info.subDictCalls, n)
|
||||
info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
|
||||
}
|
||||
case ir.OCALLMETH:
|
||||
ce := n.(*ir.CallExpr)
|
||||
|
@ -1874,7 +1888,11 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
|
|||
callMap[ce.X] = true
|
||||
if hasShapeTypes(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) {
|
||||
infoPrint(" Subdictionary at generic method call: %v\n", n)
|
||||
info.subDictCalls = append(info.subDictCalls, n)
|
||||
// Save the selector for the method call, since we
|
||||
// will eventually lose this information when the
|
||||
// generic method call is transformed into a
|
||||
// function call on the method shape instantiation.
|
||||
info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X})
|
||||
}
|
||||
}
|
||||
case ir.OCONVIFACE:
|
||||
|
|
45
test/typeparam/issue50264.go
Normal file
45
test/typeparam/issue50264.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
type hello struct{}
|
||||
|
||||
func main() {
|
||||
_ = Some(hello{})
|
||||
res := Applicative2(func(a int, b int) int {
|
||||
return 0
|
||||
})
|
||||
_ = res
|
||||
}
|
||||
|
||||
type NoneType[T any] struct{}
|
||||
|
||||
func (r NoneType[T]) Recover() any {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Func2[A1, A2, R any] func(a1 A1, a2 A2) R
|
||||
|
||||
func Some[T any](v T) any {
|
||||
_ = Some2[T](v)
|
||||
return NoneType[T]{}.Recover()
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func Some2[T any](v T) any {
|
||||
return v
|
||||
}
|
||||
|
||||
type Nil struct{}
|
||||
|
||||
type ApplicativeFunctor2[H, HT, A1, A2, R any] struct {
|
||||
h any
|
||||
}
|
||||
|
||||
func Applicative2[A1, A2, R any](fn Func2[A1, A2, R]) ApplicativeFunctor2[Nil, Nil, A1, A2, R] {
|
||||
return ApplicativeFunctor2[Nil, Nil, A1, A2, R]{Some(Nil{})}
|
||||
}
|
Loading…
Reference in a new issue