mirror of
https://github.com/golang/go
synced 2024-09-04 15:34:21 +00:00
[dev.typeparams] cmd/compile: add dictionary argument to generic functions
When converting from a generic function to a concrete implementation, add a dictionary argument to the generic function (both an actual argument at each callsite, and a formal argument of each implementation). The dictionary argument comes before all other arguments (including any receiver). The dictionary argument is checked for validity, but is otherwise unused. Subsequent CLs will start using the dictionary for, e.g., converting a value of generic type to interface{}. Import/export required adding support for LINKSYMOFFSET, which is used by the dictionary checking code. Change-Id: I16a7a8d23c7bd6a897e0da87c69f273be9103bd7 Reviewed-on: https://go-review.googlesource.com/c/go/+/323272 Trust: Keith Randall <khr@golang.org> Trust: Dan Scales <danscales@google.com> Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Dan Scales <danscales@google.com>
This commit is contained in:
parent
aa9cfdf775
commit
7b876def6c
|
@ -105,6 +105,8 @@ type irgen struct {
|
|||
|
||||
// Fully-instantiated generic types whose methods should be instantiated
|
||||
instTypeList []*types.Type
|
||||
|
||||
dnum int // for generating unique dictionary variables
|
||||
}
|
||||
|
||||
func (g *irgen) generate(noders []*noder) {
|
||||
|
|
|
@ -10,9 +10,11 @@ package noder
|
|||
import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/reflectdata"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -70,72 +72,89 @@ func (g *irgen) stencil() {
|
|||
// instantiated function if it hasn't been created yet, and change
|
||||
// to calling that function directly.
|
||||
modified := false
|
||||
foundFuncInst := false
|
||||
closureRequired := false
|
||||
ir.Visit(decl, func(n ir.Node) {
|
||||
if n.Op() == ir.OFUNCINST {
|
||||
// We found a function instantiation that is not
|
||||
// immediately called.
|
||||
foundFuncInst = true
|
||||
// generic F, not immediately called
|
||||
closureRequired = true
|
||||
}
|
||||
if n.Op() != ir.OCALL || n.(*ir.CallExpr).X.Op() != ir.OFUNCINST {
|
||||
return
|
||||
if n.Op() == ir.OMETHEXPR && len(n.(*ir.SelectorExpr).X.Type().RParams()) > 0 {
|
||||
// T.M, T a type which is generic, not immediately called
|
||||
closureRequired = true
|
||||
}
|
||||
// We have found a function call using a generic function
|
||||
// instantiation.
|
||||
call := n.(*ir.CallExpr)
|
||||
inst := call.X.(*ir.InstExpr)
|
||||
// Replace the OFUNCINST with a direct reference to the
|
||||
// new stenciled function
|
||||
st := g.getInstantiationForNode(inst)
|
||||
call.X = st.Nname
|
||||
if inst.X.Op() == ir.OCALLPART {
|
||||
// When we create an instantiation of a method
|
||||
// call, we make it a function. So, move the
|
||||
// receiver to be the first arg of the function
|
||||
// call.
|
||||
withRecv := make([]ir.Node, len(call.Args)+1)
|
||||
dot := inst.X.(*ir.SelectorExpr)
|
||||
withRecv[0] = dot.X
|
||||
copy(withRecv[1:], call.Args)
|
||||
call.Args = withRecv
|
||||
if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST {
|
||||
// We have found a function call using a generic function
|
||||
// instantiation.
|
||||
call := n.(*ir.CallExpr)
|
||||
inst := call.X.(*ir.InstExpr)
|
||||
st := g.getInstantiationForNode(inst)
|
||||
// Replace the OFUNCINST with a direct reference to the
|
||||
// new stenciled function
|
||||
call.X = st.Nname
|
||||
if inst.X.Op() == ir.OCALLPART {
|
||||
// When we create an instantiation of a method
|
||||
// call, we make it a function. So, move the
|
||||
// receiver to be the first arg of the function
|
||||
// call.
|
||||
call.Args.Prepend(inst.X.(*ir.SelectorExpr).X)
|
||||
}
|
||||
// Add dictionary to argument list.
|
||||
dict := reflectdata.GetDictionaryForInstantiation(inst)
|
||||
call.Args.Prepend(dict)
|
||||
// Transform the Call now, which changes OCALL
|
||||
// to OCALLFUNC and does typecheckaste/assignconvfn.
|
||||
transformCall(call)
|
||||
modified = true
|
||||
}
|
||||
if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH && len(deref(n.(*ir.CallExpr).X.Type().Recv().Type).RParams()) > 0 {
|
||||
// Method call on a generic type, which was instantiated by stenciling.
|
||||
// Method calls on explicitly instantiated types will have an OFUNCINST
|
||||
// and are handled above.
|
||||
call := n.(*ir.CallExpr)
|
||||
meth := call.X.(*ir.SelectorExpr)
|
||||
targs := deref(meth.Type().Recv().Type).RParams()
|
||||
|
||||
t := meth.X.Type()
|
||||
baseSym := deref(t).OrigSym
|
||||
baseType := baseSym.Def.(*ir.Name).Type()
|
||||
var gf *ir.Name
|
||||
for _, m := range baseType.Methods().Slice() {
|
||||
if meth.Sel == m.Sym {
|
||||
gf = m.Nname.(*ir.Name)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
st := g.getInstantiation(gf, targs, true)
|
||||
call.SetOp(ir.OCALL)
|
||||
call.X = st.Nname
|
||||
dict := reflectdata.GetDictionaryForMethod(gf, targs)
|
||||
call.Args.Prepend(dict, meth.X)
|
||||
// Transform the Call now, which changes OCALL
|
||||
// to OCALLFUNC and does typecheckaste/assignconvfn.
|
||||
transformCall(call)
|
||||
modified = true
|
||||
}
|
||||
// Transform the Call now, which changes OCALL
|
||||
// to OCALLFUNC and does typecheckaste/assignconvfn.
|
||||
transformCall(call)
|
||||
modified = true
|
||||
})
|
||||
|
||||
// If we found an OFUNCINST without a corresponding call in the
|
||||
// above decl, then traverse the nodes of decl again (with
|
||||
// If we found a reference to a generic instantiation that wasn't an
|
||||
// immediate call, then traverse the nodes of decl again (with
|
||||
// EditChildren rather than Visit), where we actually change the
|
||||
// OFUNCINST node to an ONAME for the instantiated function.
|
||||
// reference to the instantiation to a closure that captures the
|
||||
// dictionary, then does a direct call.
|
||||
// EditChildren is more expensive than Visit, so we only do this
|
||||
// in the infrequent case of an OFUNCINST without a corresponding
|
||||
// call.
|
||||
if foundFuncInst {
|
||||
if closureRequired {
|
||||
var edit func(ir.Node) ir.Node
|
||||
edit = func(x ir.Node) ir.Node {
|
||||
if x.Op() == ir.OFUNCINST {
|
||||
// inst.X is either a function name node
|
||||
// or a selector expression for a method.
|
||||
inst := x.(*ir.InstExpr)
|
||||
st := g.getInstantiationForNode(inst)
|
||||
modified = true
|
||||
if inst.X.Op() == ir.ONAME {
|
||||
return st.Nname
|
||||
}
|
||||
assert(inst.X.Op() == ir.OCALLPART)
|
||||
|
||||
// Return a new selector expression referring
|
||||
// to the newly stenciled function.
|
||||
oldse := inst.X.(*ir.SelectorExpr)
|
||||
newse := ir.NewSelectorExpr(oldse.Pos(), ir.OCALLPART, oldse.X, oldse.Sel)
|
||||
newse.Selection = types.NewField(oldse.Pos(), st.Sym(), st.Type())
|
||||
newse.Selection.Nname = st
|
||||
typed(inst.Type(), newse)
|
||||
return newse
|
||||
}
|
||||
ir.EditChildren(x, edit)
|
||||
switch {
|
||||
case x.Op() == ir.OFUNCINST:
|
||||
return g.buildClosure(decl.(*ir.Func), x)
|
||||
case x.Op() == ir.OMETHEXPR && len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0: // TODO: test for ptr-to-method case
|
||||
return g.buildClosure(decl.(*ir.Func), x)
|
||||
}
|
||||
return x
|
||||
}
|
||||
edit(decl)
|
||||
|
@ -153,6 +172,228 @@ func (g *irgen) stencil() {
|
|||
|
||||
}
|
||||
|
||||
// buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR
|
||||
// of generic type. outer is the containing function.
|
||||
func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||
pos := x.Pos()
|
||||
var target *ir.Func // target instantiated function/method
|
||||
var dictValue ir.Node // dictionary to use
|
||||
var rcvrValue ir.Node // receiver, if a method value
|
||||
typ := x.Type() // type of the closure
|
||||
if x.Op() == ir.OFUNCINST {
|
||||
inst := x.(*ir.InstExpr)
|
||||
|
||||
// Type arguments we're instantiating with.
|
||||
targs := typecheck.TypesOf(inst.Targs)
|
||||
|
||||
// Find the generic function/method.
|
||||
var gf *ir.Name
|
||||
if inst.X.Op() == ir.ONAME {
|
||||
// Instantiating a generic function call.
|
||||
gf = inst.X.(*ir.Name)
|
||||
} else if inst.X.Op() == ir.OCALLPART {
|
||||
// Instantiating a method value x.M.
|
||||
se := inst.X.(*ir.SelectorExpr)
|
||||
rcvrValue = se.X
|
||||
gf = se.Selection.Nname.(*ir.Name)
|
||||
} else {
|
||||
panic("unhandled")
|
||||
}
|
||||
|
||||
// target is the instantiated function we're trying to call.
|
||||
// For functions, the target expects a dictionary as its first argument.
|
||||
// For method values, the target expects a dictionary and the receiver
|
||||
// as its first two arguments.
|
||||
target = g.getInstantiation(gf, targs, rcvrValue != nil)
|
||||
|
||||
// The value to use for the dictionary argument.
|
||||
if rcvrValue == nil {
|
||||
dictValue = reflectdata.GetDictionaryForFunc(gf, targs)
|
||||
} else {
|
||||
dictValue = reflectdata.GetDictionaryForMethod(gf, targs)
|
||||
}
|
||||
} else { // ir.OMETHEXPR
|
||||
// Method expression T.M where T is a generic type.
|
||||
// TODO: Is (*T).M right?
|
||||
se := x.(*ir.SelectorExpr)
|
||||
targs := se.X.Type().RParams()
|
||||
if len(targs) == 0 {
|
||||
if se.X.Type().IsPtr() {
|
||||
targs = se.X.Type().Elem().RParams()
|
||||
if len(targs) == 0 {
|
||||
panic("bad")
|
||||
}
|
||||
}
|
||||
}
|
||||
t := se.X.Type()
|
||||
baseSym := t.OrigSym
|
||||
baseType := baseSym.Def.(*ir.Name).Type()
|
||||
var gf *ir.Name
|
||||
for _, m := range baseType.Methods().Slice() {
|
||||
if se.Sel == m.Sym {
|
||||
gf = m.Nname.(*ir.Name)
|
||||
break
|
||||
}
|
||||
}
|
||||
target = g.getInstantiation(gf, targs, true)
|
||||
dictValue = reflectdata.GetDictionaryForMethod(gf, targs)
|
||||
}
|
||||
|
||||
// Build a closure to implement a function instantiation.
|
||||
//
|
||||
// func f[T any] (int, int) (int, int) { ...whatever... }
|
||||
//
|
||||
// Then any reference to f[int] not directly called gets rewritten to
|
||||
//
|
||||
// .dictN := ... dictionary to use ...
|
||||
// func(a0, a1 int) (r0, r1 int) {
|
||||
// return .inst.f[int](.dictN, a0, a1)
|
||||
// }
|
||||
//
|
||||
// Similarly for method expressions,
|
||||
//
|
||||
// type g[T any] ....
|
||||
// func (rcvr g[T]) f(a0, a1 int) (r0, r1 int) { ... }
|
||||
//
|
||||
// Any reference to g[int].f not directly called gets rewritten to
|
||||
//
|
||||
// .dictN := ... dictionary to use ...
|
||||
// func(rcvr g[int], a0, a1 int) (r0, r1 int) {
|
||||
// return .inst.g[int].f(.dictN, rcvr, a0, a1)
|
||||
// }
|
||||
//
|
||||
// Also method values
|
||||
//
|
||||
// var x g[int]
|
||||
//
|
||||
// Any reference to x.f not directly called gets rewritten to
|
||||
//
|
||||
// .dictN := ... dictionary to use ...
|
||||
// x2 := x
|
||||
// func(a0, a1 int) (r0, r1 int) {
|
||||
// return .inst.g[int].f(.dictN, x2, a0, a1)
|
||||
// }
|
||||
|
||||
// Make a new internal function.
|
||||
fn := ir.NewFunc(pos)
|
||||
fn.SetIsHiddenClosure(true)
|
||||
|
||||
// This is the dictionary we want to use.
|
||||
// Note: for now this is a compile-time constant, so we don't really need a closure
|
||||
// to capture it (a wrapper function would work just as well). But eventually it
|
||||
// will be a read of a subdictionary from the parent dictionary.
|
||||
dictVar := ir.NewNameAt(pos, typecheck.LookupNum(".dict", g.dnum))
|
||||
g.dnum++
|
||||
dictVar.Class = ir.PAUTO
|
||||
typed(types.Types[types.TUINTPTR], dictVar)
|
||||
dictVar.Curfn = outer
|
||||
dictAssign := ir.NewAssignStmt(pos, dictVar, dictValue)
|
||||
dictAssign.SetTypecheck(1)
|
||||
dictVar.Defn = dictAssign
|
||||
outer.Dcl = append(outer.Dcl, dictVar)
|
||||
|
||||
// assign the receiver to a temporary.
|
||||
var rcvrVar *ir.Name
|
||||
var rcvrAssign ir.Node
|
||||
if rcvrValue != nil {
|
||||
rcvrVar = ir.NewNameAt(pos, typecheck.LookupNum(".rcvr", g.dnum))
|
||||
g.dnum++
|
||||
rcvrVar.Class = ir.PAUTO
|
||||
typed(rcvrValue.Type(), rcvrVar)
|
||||
rcvrVar.Curfn = outer
|
||||
rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue)
|
||||
rcvrAssign.SetTypecheck(1)
|
||||
rcvrVar.Defn = rcvrAssign
|
||||
outer.Dcl = append(outer.Dcl, rcvrVar)
|
||||
}
|
||||
|
||||
// Build formal argument and return lists.
|
||||
var formalParams []*types.Field // arguments of closure
|
||||
var formalResults []*types.Field // returns of closure
|
||||
for i := 0; i < typ.NumParams(); i++ {
|
||||
t := typ.Params().Field(i).Type
|
||||
arg := ir.NewNameAt(pos, typecheck.LookupNum("a", i))
|
||||
arg.Class = ir.PPARAM
|
||||
typed(t, arg)
|
||||
arg.Curfn = fn
|
||||
fn.Dcl = append(fn.Dcl, arg)
|
||||
f := types.NewField(pos, arg.Sym(), t)
|
||||
f.Nname = arg
|
||||
formalParams = append(formalParams, f)
|
||||
}
|
||||
for i := 0; i < typ.NumResults(); i++ {
|
||||
t := typ.Results().Field(i).Type
|
||||
result := ir.NewNameAt(pos, typecheck.LookupNum("r", i)) // TODO: names not needed?
|
||||
result.Class = ir.PPARAMOUT
|
||||
typed(t, result)
|
||||
result.Curfn = fn
|
||||
fn.Dcl = append(fn.Dcl, result)
|
||||
f := types.NewField(pos, result.Sym(), t)
|
||||
f.Nname = result
|
||||
formalResults = append(formalResults, f)
|
||||
}
|
||||
|
||||
// Build an internal function with the right signature.
|
||||
closureType := types.NewSignature(x.Type().Pkg(), nil, nil, formalParams, formalResults)
|
||||
sym := typecheck.ClosureName(outer)
|
||||
sym.SetFunc(true)
|
||||
fn.Nname = ir.NewNameAt(pos, sym)
|
||||
fn.Nname.Func = fn
|
||||
fn.Nname.Defn = fn
|
||||
typed(closureType, fn.Nname)
|
||||
fn.SetTypecheck(1)
|
||||
|
||||
// Build body of closure. This involves just calling the wrapped function directly
|
||||
// with the additional dictionary argument.
|
||||
|
||||
// First, capture the dictionary variable for use in the closure.
|
||||
dict2Var := ir.CaptureName(pos, fn, dictVar)
|
||||
// Also capture the receiver variable.
|
||||
var rcvr2Var *ir.Name
|
||||
if rcvrValue != nil {
|
||||
rcvr2Var = ir.CaptureName(pos, fn, rcvrVar)
|
||||
}
|
||||
|
||||
// Build arguments to call inside the closure.
|
||||
var args []ir.Node
|
||||
|
||||
// First the dictionary argument.
|
||||
args = append(args, dict2Var)
|
||||
// Then the receiver.
|
||||
if rcvrValue != nil {
|
||||
args = append(args, rcvr2Var)
|
||||
}
|
||||
// Then all the other arguments (including receiver for method expressions).
|
||||
for i := 0; i < typ.NumParams(); i++ {
|
||||
args = append(args, formalParams[i].Nname.(*ir.Name))
|
||||
}
|
||||
|
||||
// Build call itself.
|
||||
var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args)
|
||||
if len(formalResults) > 0 {
|
||||
innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall})
|
||||
}
|
||||
// Finish building body of closure.
|
||||
ir.CurFunc = fn
|
||||
// TODO: set types directly here instead of using typecheck.Stmt
|
||||
typecheck.Stmt(innerCall)
|
||||
ir.CurFunc = nil
|
||||
fn.Body = []ir.Node{innerCall}
|
||||
|
||||
// We're all done with the captured dictionary (and receiver, for method values).
|
||||
ir.FinishCaptureNames(pos, outer, fn)
|
||||
|
||||
// Make a closure referencing our new internal function.
|
||||
c := ir.NewClosureExpr(pos, fn)
|
||||
init := []ir.Node{dictAssign}
|
||||
if rcvrValue != nil {
|
||||
init = append(init, rcvrAssign)
|
||||
}
|
||||
c.SetInit(init)
|
||||
typed(x.Type(), c)
|
||||
return c
|
||||
}
|
||||
|
||||
// instantiateMethods instantiates all the methods of all fully-instantiated
|
||||
// generic types that have been added to g.instTypeList.
|
||||
func (g *irgen) instantiateMethods() {
|
||||
|
@ -167,14 +408,17 @@ func (g *irgen) instantiateMethods() {
|
|||
// not be set on imported instantiated types.
|
||||
baseSym := typ.OrigSym
|
||||
baseType := baseSym.Def.(*ir.Name).Type()
|
||||
for j, m := range typ.Methods().Slice() {
|
||||
name := m.Nname.(*ir.Name)
|
||||
for j, _ := range typ.Methods().Slice() {
|
||||
baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name)
|
||||
// Note: we are breaking an invariant here:
|
||||
// m.Nname is now not equal m.Nname.Func.Nname.
|
||||
// m.Nname has the type of a method, whereas m.Nname.Func.Nname has
|
||||
// the type of a function, since it is an function instantiation.
|
||||
name.Func = g.getInstantiation(baseNname, typ.RParams(), true)
|
||||
// Eagerly generate the instantiations that implement these methods.
|
||||
// We don't use the instantiations here, just generate them (and any
|
||||
// further instantiations those generate, etc.).
|
||||
// Note that we don't set the Func for any methods on instantiated
|
||||
// types. Their signatures don't match so that would be confusing.
|
||||
// Direct method calls go directly to the instantiations, implemented above.
|
||||
// Indirect method calls use wrappers generated in reflectcall. Those wrappers
|
||||
// will use these instantiations if they are needed (for interface tables or reflection).
|
||||
_ = g.getInstantiation(baseNname, typ.RParams(), true)
|
||||
}
|
||||
}
|
||||
g.instTypeList = nil
|
||||
|
@ -287,10 +531,7 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []*type
|
|||
vars: make(map[*ir.Name]*ir.Name),
|
||||
}
|
||||
|
||||
newf.Dcl = make([]*ir.Name, len(gf.Dcl))
|
||||
for i, n := range gf.Dcl {
|
||||
newf.Dcl[i] = subst.localvar(n)
|
||||
}
|
||||
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
|
||||
|
@ -298,18 +539,40 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []*type
|
|||
// 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 receiver is the first parameter.
|
||||
// instantiated function type where the dictionary is the first parameter.
|
||||
dictionarySym := types.LocalPkg.Lookup(".dict")
|
||||
dictionaryType := types.Types[types.TUINTPTR]
|
||||
dictionaryName := ir.NewNameAt(gf.Pos(), dictionarySym)
|
||||
typed(dictionaryType, dictionaryName)
|
||||
dictionaryName.Class = ir.PPARAM
|
||||
dictionaryName.Curfn = newf
|
||||
newf.Dcl = append(newf.Dcl, dictionaryName)
|
||||
for _, n := range gf.Dcl {
|
||||
if n.Sym().Name == ".dict" {
|
||||
panic("already has dictionary")
|
||||
}
|
||||
newf.Dcl = append(newf.Dcl, subst.localvar(n))
|
||||
}
|
||||
dictionaryArg := types.NewField(gf.Pos(), dictionarySym, dictionaryType)
|
||||
dictionaryArg.Nname = dictionaryName
|
||||
var args []*types.Field
|
||||
args = append(args, dictionaryArg)
|
||||
args = append(args, oldt.Recvs().FieldSlice()...)
|
||||
args = append(args, oldt.Params().FieldSlice()...)
|
||||
newt := types.NewSignature(oldt.Pkg(), nil, nil,
|
||||
subst.fields(ir.PPARAM, append(oldt.Recvs().FieldSlice(), oldt.Params().FieldSlice()...), newf.Dcl),
|
||||
subst.fields(ir.PPARAM, args, newf.Dcl),
|
||||
subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl))
|
||||
|
||||
newf.Nname.SetType(newt)
|
||||
typed(newt, newf.Nname)
|
||||
ir.MarkFunc(newf.Nname)
|
||||
newf.SetTypecheck(1)
|
||||
newf.Nname.SetTypecheck(1)
|
||||
|
||||
// Make sure name/type of newf is set before substituting the body.
|
||||
newf.Body = subst.list(gf.Body)
|
||||
|
||||
// Add code to check that the dictionary is correct.
|
||||
newf.Body.Prepend(g.checkDictionary(dictionaryName, targs)...)
|
||||
|
||||
ir.CurFunc = savef
|
||||
|
||||
return newf
|
||||
|
@ -334,6 +597,44 @@ func (subst *subster) localvar(name *ir.Name) *ir.Name {
|
|||
return m
|
||||
}
|
||||
|
||||
// checkDictionary returns code that does runtime consistency checks
|
||||
// between the dictionary and the types it should contain.
|
||||
func (g *irgen) checkDictionary(name *ir.Name, targs []*types.Type) (code []ir.Node) {
|
||||
if false {
|
||||
return // checking turned off
|
||||
}
|
||||
// TODO: when moving to GCshape, this test will become harder. Call into
|
||||
// runtime to check the expected shape is correct?
|
||||
pos := name.Pos()
|
||||
// Convert dictionary to *[N]uintptr
|
||||
d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], name)
|
||||
d.SetTypecheck(1)
|
||||
d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(len(targs))).PtrTo(), d)
|
||||
d.SetTypecheck(1)
|
||||
|
||||
// Check that each type entry in the dictionary is correct.
|
||||
for i, t := range targs {
|
||||
want := reflectdata.TypePtr(t)
|
||||
typed(types.Types[types.TUINTPTR], want)
|
||||
deref := ir.NewStarExpr(pos, d)
|
||||
typed(d.Type().Elem(), deref)
|
||||
idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), name) // TODO: what to set orig to?
|
||||
typed(types.Types[types.TUINTPTR], idx)
|
||||
got := ir.NewIndexExpr(pos, deref, idx)
|
||||
typed(types.Types[types.TUINTPTR], got)
|
||||
cond := ir.NewBinaryExpr(pos, ir.ONE, want, got)
|
||||
typed(types.Types[types.TBOOL], cond)
|
||||
panicArg := ir.NewNilExpr(pos)
|
||||
typed(types.NewInterface(types.LocalPkg, nil), panicArg)
|
||||
then := ir.NewUnaryExpr(pos, ir.OPANIC, panicArg)
|
||||
then.SetTypecheck(1)
|
||||
x := ir.NewIfStmt(pos, cond, []ir.Node{then}, nil)
|
||||
x.SetTypecheck(1)
|
||||
code = append(code, x)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// node is like DeepCopy(), but substitutes ONAME nodes based on subst.vars, and
|
||||
// also descends into closures. It substitutes type arguments for type parameters
|
||||
// in all the new nodes.
|
||||
|
@ -837,13 +1138,14 @@ func (subst *subster) typ(t *types.Type) *types.Type {
|
|||
t2 := subst.typ(f.Type)
|
||||
oldsym := f.Nname.Sym()
|
||||
newsym := typecheck.MakeInstName(oldsym, subst.targs, true)
|
||||
// TODO: use newsym?
|
||||
var nname *ir.Name
|
||||
if newsym.Def != nil {
|
||||
nname = newsym.Def.(*ir.Name)
|
||||
} else {
|
||||
nname = ir.NewNameAt(f.Pos, newsym)
|
||||
nname = ir.NewNameAt(f.Pos, oldsym)
|
||||
nname.SetType(t2)
|
||||
newsym.Def = nname
|
||||
oldsym.Def = nname
|
||||
}
|
||||
newfields[i] = types.NewField(f.Pos, f.Sym, t2)
|
||||
newfields[i].Nname = nname
|
||||
|
|
|
@ -340,12 +340,12 @@ assignOK:
|
|||
}
|
||||
}
|
||||
|
||||
// Corresponds to typecheck.typecheckargs.
|
||||
// Corresponds to, but slightly more general than, typecheck.typecheckargs.
|
||||
func transformArgs(n ir.InitNode) {
|
||||
var list []ir.Node
|
||||
switch n := n.(type) {
|
||||
default:
|
||||
base.Fatalf("typecheckargs %+v", n.Op())
|
||||
base.Fatalf("transformArgs %+v", n.Op())
|
||||
case *ir.CallExpr:
|
||||
list = n.Args
|
||||
if n.IsDDD {
|
||||
|
@ -354,25 +354,31 @@ func transformArgs(n ir.InitNode) {
|
|||
case *ir.ReturnStmt:
|
||||
list = n.Results
|
||||
}
|
||||
if len(list) != 1 {
|
||||
|
||||
// Look to see if we have any multi-return functions as arguments.
|
||||
extra := 0
|
||||
for _, arg := range list {
|
||||
t := arg.Type()
|
||||
if t.IsFuncArgStruct() {
|
||||
num := t.Fields().Len()
|
||||
if num <= 1 {
|
||||
base.Fatalf("multi-return type with only %d parts", num)
|
||||
}
|
||||
extra += num - 1
|
||||
}
|
||||
}
|
||||
// If not, nothing to do.
|
||||
if extra == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
t := list[0].Type()
|
||||
if t == nil || !t.IsFuncArgStruct() {
|
||||
return
|
||||
}
|
||||
|
||||
// Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
|
||||
// Rewrite f(..., g(), ...) into t1, ..., tN = g(); f(..., t1, ..., tN, ...).
|
||||
|
||||
// Save n as n.Orig for fmt.go.
|
||||
if ir.Orig(n) == n {
|
||||
n.(ir.OrigNode).SetOrig(ir.SepCopy(n))
|
||||
}
|
||||
|
||||
as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
|
||||
as.Rhs.Append(list...)
|
||||
|
||||
// If we're outside of function context, then this call will
|
||||
// be executed during the generated init function. However,
|
||||
// init.go hasn't yet created it. Instead, associate the
|
||||
|
@ -382,27 +388,42 @@ func transformArgs(n ir.InitNode) {
|
|||
if static {
|
||||
ir.CurFunc = typecheck.InitTodoFunc
|
||||
}
|
||||
list = nil
|
||||
for _, f := range t.FieldSlice() {
|
||||
t := typecheck.Temp(f.Type)
|
||||
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, t))
|
||||
as.Lhs.Append(t)
|
||||
list = append(list, t)
|
||||
|
||||
// Expand multi-return function calls.
|
||||
// The spec only allows a multi-return function as an argument
|
||||
// if it is the only argument. This code must handle calls to
|
||||
// stenciled generic functions which have extra arguments
|
||||
// (like the dictionary) so it must handle a slightly more general
|
||||
// cases, like f(n, g()) where g is multi-return.
|
||||
newList := make([]ir.Node, 0, len(list)+extra)
|
||||
for _, arg := range list {
|
||||
t := arg.Type()
|
||||
if t.IsFuncArgStruct() {
|
||||
as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, []ir.Node{arg})
|
||||
for _, f := range t.FieldSlice() {
|
||||
t := typecheck.Temp(f.Type)
|
||||
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, t))
|
||||
as.Lhs.Append(t)
|
||||
newList = append(newList, t)
|
||||
}
|
||||
transformAssign(as, as.Lhs, as.Rhs)
|
||||
as.SetTypecheck(1)
|
||||
n.PtrInit().Append(as)
|
||||
} else {
|
||||
newList = append(newList, arg)
|
||||
}
|
||||
}
|
||||
|
||||
if static {
|
||||
ir.CurFunc = nil
|
||||
}
|
||||
|
||||
switch n := n.(type) {
|
||||
case *ir.CallExpr:
|
||||
n.Args = list
|
||||
n.Args = newList
|
||||
case *ir.ReturnStmt:
|
||||
n.Results = list
|
||||
n.Results = newList
|
||||
}
|
||||
|
||||
transformAssign(as, as.Lhs, as.Rhs)
|
||||
as.SetTypecheck(1)
|
||||
n.PtrInit().Append(as)
|
||||
}
|
||||
|
||||
// assignconvfn converts node n for assignment to type t. Corresponds to
|
||||
|
@ -562,6 +583,11 @@ func transformDot(n *ir.SelectorExpr, isCall bool) ir.Node {
|
|||
|
||||
if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && !isCall {
|
||||
n.SetOp(ir.OCALLPART)
|
||||
if len(n.X.Type().RParams()) > 0 || n.X.Type().IsPtr() && len(n.X.Type().Elem().RParams()) > 0 {
|
||||
// TODO: MethodValueWrapper needed for generics?
|
||||
// Or did we successfully desugar all that at stencil time?
|
||||
return n
|
||||
}
|
||||
n.SetType(typecheck.MethodValueWrapper(n).Type())
|
||||
}
|
||||
return n
|
||||
|
|
|
@ -321,13 +321,6 @@ func methods(t *types.Type) []*typeSig {
|
|||
}
|
||||
typecheck.CalcMethods(mt)
|
||||
|
||||
// type stored in interface word
|
||||
it := t
|
||||
|
||||
if !types.IsDirectIface(it) {
|
||||
it = types.NewPtr(t)
|
||||
}
|
||||
|
||||
// make list of methods for t,
|
||||
// generating code if necessary.
|
||||
var ms []*typeSig
|
||||
|
@ -355,8 +348,8 @@ func methods(t *types.Type) []*typeSig {
|
|||
|
||||
sig := &typeSig{
|
||||
name: f.Sym,
|
||||
isym: methodWrapper(it, f),
|
||||
tsym: methodWrapper(t, f),
|
||||
isym: methodWrapper(t, f, true),
|
||||
tsym: methodWrapper(t, f, false),
|
||||
type_: typecheck.NewMethodType(f.Type, t),
|
||||
mtype: typecheck.NewMethodType(f.Type, nil),
|
||||
}
|
||||
|
@ -394,7 +387,7 @@ func imethods(t *types.Type) []*typeSig {
|
|||
// IfaceType.Method is not in the reflect data.
|
||||
// Generate the method body, so that compiled
|
||||
// code can refer to it.
|
||||
methodWrapper(t, f)
|
||||
methodWrapper(t, f, false)
|
||||
}
|
||||
|
||||
return methods
|
||||
|
@ -1765,7 +1758,28 @@ func CollectPTabs() {
|
|||
//
|
||||
// rcvr - U
|
||||
// method - M func (t T)(), a TFIELD type struct
|
||||
func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
|
||||
//
|
||||
// Also wraps methods on instantiated generic types for use in itab entries.
|
||||
// For an instantiated generic type G[int], we generate wrappers like:
|
||||
// G[int] pointer shaped:
|
||||
// func (x G[int]) f(arg) {
|
||||
// .inst.G[int].f(dictionary, x, arg)
|
||||
// }
|
||||
// G[int] not pointer shaped:
|
||||
// func (x *G[int]) f(arg) {
|
||||
// .inst.G[int].f(dictionary, *x, arg)
|
||||
// }
|
||||
// These wrappers are always fully stenciled.
|
||||
func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSym {
|
||||
orig := rcvr
|
||||
if forItab && !types.IsDirectIface(rcvr) {
|
||||
rcvr = rcvr.PtrTo()
|
||||
}
|
||||
generic := false
|
||||
if !rcvr.IsInterface() && len(rcvr.RParams()) > 0 || rcvr.IsPtr() && len(rcvr.Elem().RParams()) > 0 { // TODO: right detection?
|
||||
// TODO: check that we do the right thing when rcvr.IsInterface().
|
||||
generic = true
|
||||
}
|
||||
newnam := ir.MethodSym(rcvr, method.Sym)
|
||||
lsym := newnam.Linksym()
|
||||
if newnam.Siggen() {
|
||||
|
@ -1773,7 +1787,7 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
|
|||
}
|
||||
newnam.SetSiggen(true)
|
||||
|
||||
if types.Identical(rcvr, method.Type.Recv().Type) {
|
||||
if !generic && types.Identical(rcvr, method.Type.Recv().Type) {
|
||||
return lsym
|
||||
}
|
||||
|
||||
|
@ -1808,9 +1822,10 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
|
|||
nthis := ir.AsNode(tfn.Type().Recv().Nname)
|
||||
|
||||
methodrcvr := method.Type.Recv().Type
|
||||
indirect := rcvr.IsPtr() && rcvr.Elem() == methodrcvr
|
||||
|
||||
// generate nil pointer check for better error
|
||||
if rcvr.IsPtr() && rcvr.Elem() == methodrcvr {
|
||||
if indirect {
|
||||
// generating wrapper from *T to T.
|
||||
n := ir.NewIfStmt(base.Pos, nil, nil, nil)
|
||||
n.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, nthis, typecheck.NodNil())
|
||||
|
@ -1832,7 +1847,7 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
|
|||
// Disable tailcall for RegabiArgs for now. The IR does not connect the
|
||||
// arguments with the OTAILCALL node, and the arguments are not marshaled
|
||||
// correctly.
|
||||
if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !buildcfg.Experiment.RegabiArgs {
|
||||
if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !buildcfg.Experiment.RegabiArgs && !generic {
|
||||
// generate tail call: adjust pointer receiver and jump to embedded method.
|
||||
left := dot.X // skip final .M
|
||||
if !left.Type().IsPtr() {
|
||||
|
@ -1843,8 +1858,44 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
|
|||
fn.Body.Append(ir.NewTailCallStmt(base.Pos, method.Nname.(*ir.Name)))
|
||||
} else {
|
||||
fn.SetWrapper(true) // ignore frame for panic+recover matching
|
||||
call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
|
||||
call.Args = ir.ParamNames(tfn.Type())
|
||||
var call *ir.CallExpr
|
||||
if generic {
|
||||
var args []ir.Node
|
||||
var targs []*types.Type
|
||||
if rcvr.IsPtr() { // TODO: correct condition?
|
||||
targs = rcvr.Elem().RParams()
|
||||
} else {
|
||||
targs = rcvr.RParams()
|
||||
}
|
||||
if strings.HasPrefix(ir.MethodSym(orig, method.Sym).Name, ".inst.") {
|
||||
fmt.Printf("%s\n", ir.MethodSym(orig, method.Sym).Name)
|
||||
panic("multiple .inst.")
|
||||
}
|
||||
args = append(args, getDictionary(".inst."+ir.MethodSym(orig, method.Sym).Name, targs)) // TODO: remove .inst.
|
||||
if indirect {
|
||||
args = append(args, ir.NewStarExpr(base.Pos, nthis))
|
||||
} else {
|
||||
args = append(args, nthis)
|
||||
}
|
||||
args = append(args, ir.ParamNames(tfn.Type())...)
|
||||
|
||||
// TODO: Once we enter the gcshape world, we'll need a way to look up
|
||||
// the stenciled implementation to use for this concrete type. Essentially,
|
||||
// erase the concrete types and replace them with gc shape representatives.
|
||||
sym := typecheck.MakeInstName(ir.MethodSym(methodrcvr, method.Sym), targs, true)
|
||||
if sym.Def == nil {
|
||||
// Currently we make sure that we have all the instantiations
|
||||
// we need by generating them all in ../noder/stencil.go:instantiateMethods
|
||||
// TODO: maybe there's a better, more incremental way to generate
|
||||
// only the instantiations we need?
|
||||
base.Fatalf("instantiation %s not found", sym.Name)
|
||||
}
|
||||
target := ir.AsNode(sym.Def)
|
||||
call = ir.NewCallExpr(base.Pos, ir.OCALL, target, args)
|
||||
} else {
|
||||
call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
|
||||
call.Args = ir.ParamNames(tfn.Type())
|
||||
}
|
||||
call.IsDDD = tfn.Type().IsVariadic()
|
||||
if method.Type.NumResults() > 0 {
|
||||
ret := ir.NewReturnStmt(base.Pos, nil)
|
||||
|
@ -1909,3 +1960,71 @@ func MarkUsedIfaceMethod(n *ir.CallExpr) {
|
|||
r.Add = InterfaceMethodOffset(ityp, midx)
|
||||
r.Type = objabi.R_USEIFACEMETHOD
|
||||
}
|
||||
|
||||
// getDictionaryForInstantiation returns the dictionary that should be used for invoking
|
||||
// the concrete instantiation described by inst.
|
||||
func GetDictionaryForInstantiation(inst *ir.InstExpr) ir.Node {
|
||||
targs := typecheck.TypesOf(inst.Targs)
|
||||
if meth, ok := inst.X.(*ir.SelectorExpr); ok {
|
||||
return GetDictionaryForMethod(meth.Selection.Nname.(*ir.Name), targs)
|
||||
}
|
||||
return GetDictionaryForFunc(inst.X.(*ir.Name), targs)
|
||||
}
|
||||
|
||||
func GetDictionaryForFunc(fn *ir.Name, targs []*types.Type) ir.Node {
|
||||
return getDictionary(typecheck.MakeInstName(fn.Sym(), targs, false).Name, targs)
|
||||
}
|
||||
func GetDictionaryForMethod(meth *ir.Name, targs []*types.Type) ir.Node {
|
||||
return getDictionary(typecheck.MakeInstName(meth.Sym(), targs, true).Name, targs)
|
||||
}
|
||||
|
||||
// getDictionary returns the dictionary for the given named generic function
|
||||
// or method, with the given type arguments.
|
||||
// TODO: pass a reference to the generic function instead? We might need
|
||||
// that to look up protodictionaries.
|
||||
func getDictionary(name string, targs []*types.Type) ir.Node {
|
||||
if len(targs) == 0 {
|
||||
base.Fatalf("%s should have type arguments", name)
|
||||
}
|
||||
|
||||
// The dictionary for this instantiation is named after the function
|
||||
// and concrete types it is instantiated with.
|
||||
// TODO: decouple this naming from the instantiation naming. The instantiation
|
||||
// naming will be based on GC shapes, this naming must be fully stenciled.
|
||||
if !strings.HasPrefix(name, ".inst.") {
|
||||
base.Fatalf("%s should start in .inst.", name)
|
||||
}
|
||||
name = ".dict." + name[6:]
|
||||
|
||||
// Get a symbol representing the dictionary.
|
||||
sym := typecheck.Lookup(name)
|
||||
|
||||
// Initialize the dictionary, if we haven't yet already.
|
||||
if lsym := sym.Linksym(); len(lsym.P) == 0 {
|
||||
off := 0
|
||||
// Emit an entry for each concrete type.
|
||||
for _, t := range targs {
|
||||
s := TypeLinksym(t)
|
||||
off = objw.SymPtr(lsym, off, s, 0)
|
||||
}
|
||||
// TODO: subdictionaries
|
||||
objw.Global(lsym, int32(off), obj.DUPOK|obj.RODATA)
|
||||
}
|
||||
|
||||
// Make a node referencing the dictionary symbol.
|
||||
n := typecheck.NewName(sym)
|
||||
n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter
|
||||
n.SetTypecheck(1)
|
||||
n.Class = ir.PEXTERN
|
||||
sym.Def = n
|
||||
|
||||
// Return the address of the dictionary.
|
||||
np := typecheck.NodAddr(n)
|
||||
// Note: treat dictionary pointers as uintptrs, so they aren't pointers
|
||||
// with respect to GC. That saves on stack scanning work, write barriers, etc.
|
||||
// We can get away with it because dictionaries are global variables.
|
||||
// TODO: use a cast, or is typing directly ok?
|
||||
np.SetType(types.Types[types.TUINTPTR])
|
||||
np.SetTypecheck(1)
|
||||
return np
|
||||
}
|
||||
|
|
|
@ -1904,6 +1904,14 @@ func (w *exportWriter) expr(n ir.Node) {
|
|||
w.op(ir.OEND)
|
||||
}
|
||||
|
||||
case ir.OLINKSYMOFFSET:
|
||||
n := n.(*ir.LinksymOffsetExpr)
|
||||
w.op(ir.OLINKSYMOFFSET)
|
||||
w.pos(n.Pos())
|
||||
w.string(n.Linksym.Name)
|
||||
w.uint64(uint64(n.Offset_))
|
||||
w.typ(n.Type())
|
||||
|
||||
// unary expressions
|
||||
case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV:
|
||||
n := n.(*ir.UnaryExpr)
|
||||
|
@ -2068,7 +2076,7 @@ func (w *exportWriter) localIdent(s *types.Sym) {
|
|||
}
|
||||
|
||||
// TODO(mdempsky): Fix autotmp hack.
|
||||
if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, ".autotmp_") {
|
||||
if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, ".autotmp_") && !strings.HasPrefix(name, ".dict") { // TODO: just use autotmp names for dictionaries?
|
||||
base.Fatalf("unexpected dot in identifier: %v", name)
|
||||
}
|
||||
|
||||
|
|
|
@ -1467,6 +1467,13 @@ func (r *importReader) node() ir.Node {
|
|||
n.Args.Append(r.exprList()...)
|
||||
return n
|
||||
|
||||
case ir.OLINKSYMOFFSET:
|
||||
pos := r.pos()
|
||||
name := r.string()
|
||||
off := r.uint64()
|
||||
typ := r.typ()
|
||||
return ir.NewLinksymOffsetExpr(pos, Lookup(name).Linksym(), int64(off), typ)
|
||||
|
||||
// unary expressions
|
||||
case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV:
|
||||
n := ir.NewUnaryExpr(r.pos(), op, r.expr())
|
||||
|
|
|
@ -901,6 +901,14 @@ func TypesOf(x []ir.Node) []*types.Type {
|
|||
// '(*genType[int,bool]).methodName' for methods
|
||||
func MakeInstName(fnsym *types.Sym, targs []*types.Type, hasBrackets bool) *types.Sym {
|
||||
b := bytes.NewBufferString("")
|
||||
|
||||
// marker to distinguish generic instantiations from fully stenciled wrapper functions.
|
||||
// Once we move to GC shape implementations, this prefix will not be necessary as the
|
||||
// GC shape naming will distinguish them.
|
||||
// e.g. f[8bytenonpointer] vs. f[int].
|
||||
// For now, we use .inst.f[int] vs. f[int].
|
||||
b.WriteString(".inst.")
|
||||
|
||||
name := fnsym.Name
|
||||
i := strings.Index(name, "[")
|
||||
assert(hasBrackets == (i >= 0))
|
||||
|
@ -924,10 +932,13 @@ func MakeInstName(fnsym *types.Sym, targs []*types.Type, hasBrackets bool) *type
|
|||
}
|
||||
b.WriteString("]")
|
||||
if i >= 0 {
|
||||
i2 := strings.Index(name[i:], "]")
|
||||
i2 := strings.LastIndex(name[i:], "]")
|
||||
assert(i2 >= 0)
|
||||
b.WriteString(name[i+i2+1:])
|
||||
}
|
||||
if strings.HasPrefix(b.String(), ".inst..inst.") {
|
||||
panic(fmt.Sprintf("multiple .inst. prefix in %s", b.String()))
|
||||
}
|
||||
return fnsym.Pkg.Lookup(b.String())
|
||||
}
|
||||
|
||||
|
|
|
@ -958,7 +958,7 @@ func (t *Type) FuncArgs() *Type {
|
|||
return t.Extra.(FuncArgs).T
|
||||
}
|
||||
|
||||
// IsFuncArgStruct reports whether t is a struct representing function parameters.
|
||||
// IsFuncArgStruct reports whether t is a struct representing function parameters or results.
|
||||
func (t *Type) IsFuncArgStruct() bool {
|
||||
return t.kind == TSTRUCT && t.Extra.(*Struct).Funarg != FunargNone
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
package atomic
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
"internal/cpu"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
100
test/typeparam/dictionaryCapture.go
Normal file
100
test/typeparam/dictionaryCapture.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
// 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 situations where functions/methods are not
|
||||
// immediately called and we need to capture the dictionary
|
||||
// required for later invocation.
|
||||
|
||||
// TODO: copy this test file, add -l to gcflags.
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
functions()
|
||||
methodExpressions()
|
||||
methodValues()
|
||||
interfaceMethods()
|
||||
}
|
||||
|
||||
func g0[T any](x T) {
|
||||
}
|
||||
func g1[T any](x T) T {
|
||||
return x
|
||||
}
|
||||
func g2[T any](x T) (T, T) {
|
||||
return x, x
|
||||
}
|
||||
|
||||
func functions() {
|
||||
f0 := g0[int]
|
||||
f0(7)
|
||||
f1 := g1[int]
|
||||
is7(f1(7))
|
||||
f2 := g2[int]
|
||||
is77(f2(7))
|
||||
}
|
||||
|
||||
func is7(x int) {
|
||||
if x != 7 {
|
||||
println(x)
|
||||
panic("assertion failed")
|
||||
}
|
||||
}
|
||||
func is77(x, y int) {
|
||||
if x != 7 || y != 7 {
|
||||
println(x,y)
|
||||
panic("assertion failed")
|
||||
}
|
||||
}
|
||||
|
||||
type s[T any] struct {
|
||||
a T
|
||||
}
|
||||
|
||||
func (x s[T]) g0() {
|
||||
}
|
||||
func (x s[T]) g1() T {
|
||||
return x.a
|
||||
}
|
||||
func (x s[T]) g2() (T, T) {
|
||||
return x.a, x.a
|
||||
}
|
||||
|
||||
func methodExpressions() {
|
||||
x := s[int]{a:7}
|
||||
f0 := s[int].g0
|
||||
f0(x)
|
||||
f1 := s[int].g1
|
||||
is7(f1(x))
|
||||
f2 := s[int].g2
|
||||
is77(f2(x))
|
||||
}
|
||||
|
||||
func methodValues() {
|
||||
x := s[int]{a:7}
|
||||
f0 := x.g0
|
||||
f0()
|
||||
f1 := x.g1
|
||||
is7(f1())
|
||||
f2 := x.g2
|
||||
is77(f2())
|
||||
}
|
||||
|
||||
var x interface{
|
||||
g0()
|
||||
g1()int
|
||||
g2()(int,int)
|
||||
} = s[int]{a:7}
|
||||
var y interface{} = s[int]{a:7}
|
||||
|
||||
func interfaceMethods() {
|
||||
x.g0()
|
||||
is7(x.g1())
|
||||
is77(x.g2())
|
||||
y.(interface{g0()}).g0()
|
||||
is7(y.(interface{g1()int}).g1())
|
||||
is77(y.(interface{g2()(int,int)}).g2())
|
||||
}
|
Loading…
Reference in a new issue