mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Fix the cons.go missing method error. Mark all the methods of instantiated interface types as used. We could try to record all the exact methods used for generic interface types, but for now, just mark all the methods as used so that their methods are not dead-code eliminated. Change-Id: I35685eda82476244371379b97691a1b8506ef0f7 Reviewed-on: https://go-review.googlesource.com/c/go/+/337349 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>
1984 lines
65 KiB
Go
1984 lines
65 KiB
Go
// 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.
|
|
|
|
// This file will evolve, since we plan to do a mix of stenciling and passing
|
|
// around dictionaries.
|
|
|
|
package noder
|
|
|
|
import (
|
|
"bytes"
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/ir"
|
|
"cmd/compile/internal/objw"
|
|
"cmd/compile/internal/reflectdata"
|
|
"cmd/compile/internal/typecheck"
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/obj"
|
|
"cmd/internal/src"
|
|
"fmt"
|
|
"go/constant"
|
|
"strconv"
|
|
)
|
|
|
|
// Enable extra consistency checks.
|
|
const doubleCheck = true
|
|
|
|
func assert(p bool) {
|
|
base.Assert(p)
|
|
}
|
|
|
|
// Temporary - for outputting information on derived types, dictionaries, sub-dictionaries.
|
|
// Turn off when running tests.
|
|
var infoPrintMode = false
|
|
|
|
func infoPrint(format string, a ...interface{}) {
|
|
if infoPrintMode {
|
|
fmt.Printf(format, a...)
|
|
}
|
|
}
|
|
|
|
// stencil scans functions for instantiated generic function calls and creates the
|
|
// required instantiations for simple generic functions. It also creates
|
|
// instantiated methods for all fully-instantiated generic types that have been
|
|
// encountered already or new ones that are encountered during the stenciling
|
|
// process.
|
|
func (g *irgen) stencil() {
|
|
g.instInfoMap = make(map[*types.Sym]*instInfo)
|
|
g.gfInfoMap = make(map[*types.Sym]*gfInfo)
|
|
|
|
// Instantiate the methods of instantiated generic types that we have seen so far.
|
|
g.instantiateMethods()
|
|
|
|
// Don't use range(g.target.Decls) - we also want to process any new instantiated
|
|
// functions that are created during this loop, in order to handle generic
|
|
// functions calling other generic functions.
|
|
for i := 0; i < len(g.target.Decls); i++ {
|
|
decl := g.target.Decls[i]
|
|
|
|
// Look for function instantiations in bodies of non-generic
|
|
// functions or in global assignments (ignore global type and
|
|
// constant declarations).
|
|
switch decl.Op() {
|
|
case ir.ODCLFUNC:
|
|
if decl.Type().HasTParam() {
|
|
// Skip any generic functions
|
|
continue
|
|
}
|
|
// transformCall() below depends on CurFunc being set.
|
|
ir.CurFunc = decl.(*ir.Func)
|
|
|
|
case ir.OAS, ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV, ir.OASOP:
|
|
// These are all the various kinds of global assignments,
|
|
// whose right-hand-sides might contain a function
|
|
// instantiation.
|
|
|
|
default:
|
|
// The other possible ops at the top level are ODCLCONST
|
|
// and ODCLTYPE, which don't have any function
|
|
// instantiations.
|
|
continue
|
|
}
|
|
|
|
// For all non-generic code, search for any function calls using
|
|
// generic function instantiations. Then create the needed
|
|
// instantiated function if it hasn't been created yet, and change
|
|
// 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
|
|
closureRequired = true
|
|
}
|
|
if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) {
|
|
// T.M or x.M, where T or x is generic, but not immediately
|
|
// called. Not necessary if the method selected is
|
|
// actually for an embedded interface field.
|
|
closureRequired = true
|
|
}
|
|
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)
|
|
nameNode, isMeth := g.getInstNameNode(inst)
|
|
targs := typecheck.TypesOf(inst.Targs)
|
|
st := g.getInstantiation(nameNode, targs, isMeth)
|
|
dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth)
|
|
if infoPrintMode {
|
|
dictkind := "Main dictionary"
|
|
if usingSubdict {
|
|
dictkind = "Sub-dictionary"
|
|
}
|
|
if inst.X.Op() == ir.OMETHVALUE {
|
|
fmt.Printf("%s in %v at generic method call: %v - %v\n", dictkind, decl, inst.X, call)
|
|
} else {
|
|
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
|
|
// new stenciled function
|
|
call.X = st.Nname
|
|
if inst.X.Op() == ir.OMETHVALUE {
|
|
// 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.
|
|
call.Args.Prepend(dictValue)
|
|
// 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)
|
|
dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true)
|
|
// We have to be using a subdictionary, since this is
|
|
// a generic method call.
|
|
assert(usingSubdict)
|
|
|
|
call.SetOp(ir.OCALL)
|
|
call.X = st.Nname
|
|
call.Args.Prepend(dictValue, meth.X)
|
|
// Transform the Call now, which changes OCALL
|
|
// to OCALLFUNC and does typecheckaste/assignconvfn.
|
|
transformCall(call)
|
|
modified = true
|
|
}
|
|
})
|
|
|
|
// 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
|
|
// 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 closureRequired {
|
|
modified = true
|
|
var edit func(ir.Node) ir.Node
|
|
var outer *ir.Func
|
|
if f, ok := decl.(*ir.Func); ok {
|
|
outer = f
|
|
}
|
|
edit = func(x ir.Node) ir.Node {
|
|
if x.Op() == ir.OFUNCINST {
|
|
child := x.(*ir.InstExpr).X
|
|
if child.Op() == ir.OMETHEXPR || child.Op() == ir.OMETHVALUE {
|
|
// Call EditChildren on child (x.X),
|
|
// not x, so that we don't do
|
|
// buildClosure() on the
|
|
// METHEXPR/METHVALUE nodes as well.
|
|
ir.EditChildren(child, edit)
|
|
return g.buildClosure(outer, x)
|
|
}
|
|
}
|
|
ir.EditChildren(x, edit)
|
|
switch {
|
|
case x.Op() == ir.OFUNCINST:
|
|
return g.buildClosure(outer, x)
|
|
case (x.Op() == ir.OMETHEXPR || x.Op() == ir.OMETHVALUE) &&
|
|
len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0 &&
|
|
!types.IsInterfaceMethod(x.(*ir.SelectorExpr).Selection.Type):
|
|
return g.buildClosure(outer, x)
|
|
}
|
|
return x
|
|
}
|
|
edit(decl)
|
|
}
|
|
if base.Flag.W > 1 && modified {
|
|
ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl)
|
|
}
|
|
ir.CurFunc = nil
|
|
// We may have seen new fully-instantiated generic types while
|
|
// instantiating any needed functions/methods in the above
|
|
// function. If so, instantiate all the methods of those types
|
|
// (which will then lead to more function/methods to scan in the loop).
|
|
g.instantiateMethods()
|
|
}
|
|
|
|
g.finalizeSyms()
|
|
}
|
|
|
|
// buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR
|
|
// of generic type. outer is the containing function (or nil if closure is
|
|
// in a global assignment instead of a 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
|
|
var outerInfo *instInfo
|
|
if outer != nil {
|
|
outerInfo = g.instInfoMap[outer.Sym()]
|
|
}
|
|
usingSubdict := false
|
|
valueMethod := false
|
|
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.OMETHVALUE {
|
|
// 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.
|
|
// dictValue is the value to use for the dictionary argument.
|
|
target = g.getInstantiation(gf, targs, rcvrValue != nil)
|
|
dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, rcvrValue != nil)
|
|
if infoPrintMode {
|
|
dictkind := "Main dictionary"
|
|
if usingSubdict {
|
|
dictkind = "Sub-dictionary"
|
|
}
|
|
if rcvrValue == nil {
|
|
fmt.Printf("%s in %v for generic function value %v\n", dictkind, outer, inst.X)
|
|
} else {
|
|
fmt.Printf("%s in %v for generic method value %v\n", dictkind, outer, inst.X)
|
|
}
|
|
}
|
|
} else { // ir.OMETHEXPR or ir.METHVALUE
|
|
// Method expression T.M where T is a generic type.
|
|
se := x.(*ir.SelectorExpr)
|
|
targs := deref(se.X.Type()).RParams()
|
|
if len(targs) == 0 {
|
|
panic("bad")
|
|
}
|
|
if x.Op() == ir.OMETHVALUE {
|
|
rcvrValue = se.X
|
|
}
|
|
|
|
// se.X.Type() is the top-level type of the method expression. To
|
|
// correctly handle method expressions involving embedded fields,
|
|
// look up the generic method below using the type of the receiver
|
|
// of se.Selection, since that will be the type that actually has
|
|
// the method.
|
|
recv := deref(se.Selection.Type.Recv().Type)
|
|
baseType := recv.OrigSym.Def.Type()
|
|
var gf *ir.Name
|
|
for _, m := range baseType.Methods().Slice() {
|
|
if se.Sel == m.Sym {
|
|
gf = m.Nname.(*ir.Name)
|
|
break
|
|
}
|
|
}
|
|
if !gf.Type().Recv().Type.IsPtr() {
|
|
// Remember if value method, so we can detect (*T).M case.
|
|
valueMethod = true
|
|
}
|
|
target = g.getInstantiation(gf, targs, true)
|
|
dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true)
|
|
if infoPrintMode {
|
|
dictkind := "Main dictionary"
|
|
if usingSubdict {
|
|
dictkind = "Sub-dictionary"
|
|
}
|
|
fmt.Printf("%s in %v for method expression %v\n", dictkind, outer, x)
|
|
}
|
|
}
|
|
|
|
// 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.NewClosureFunc(pos, outer != nil)
|
|
ir.NameClosure(fn.OClosure, outer)
|
|
|
|
// This is the dictionary we want to use.
|
|
// It may be a constant, or it may be a dictionary acquired from the outer function's dictionary.
|
|
// For the latter, dictVar is a variable in the outer function's scope, set to the subdictionary
|
|
// read from the outer function's dictionary.
|
|
var dictVar *ir.Name
|
|
var dictAssign *ir.AssignStmt
|
|
if outer != nil {
|
|
// 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)
|
|
typed(closureType, fn.Nname)
|
|
typed(x.Type(), fn.OClosure)
|
|
fn.SetTypecheck(1)
|
|
|
|
// Build body of closure. This involves just calling the wrapped function directly
|
|
// with the additional dictionary argument.
|
|
|
|
// First, figure out the dictionary argument.
|
|
var dict2Var ir.Node
|
|
if usingSubdict {
|
|
// Capture sub-dictionary calculated in the outer function
|
|
dict2Var = ir.CaptureName(pos, fn, dictVar)
|
|
typed(types.Types[types.TUINTPTR], dict2Var)
|
|
} else {
|
|
// Static dictionary, so can be used directly in the closure
|
|
dict2Var = dictValue
|
|
}
|
|
// 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++ {
|
|
if x.Op() == ir.OMETHEXPR && i == 0 {
|
|
// If we are doing a method expression, we need to
|
|
// explicitly traverse any embedded fields in the receiver
|
|
// argument in order to call the method instantiation.
|
|
arg0 := formalParams[0].Nname.(ir.Node)
|
|
arg0 = typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, arg0, x.(*ir.SelectorExpr).Sel)).X
|
|
if valueMethod && arg0.Type().IsPtr() {
|
|
// For handling the (*T).M case: if we have a pointer
|
|
// receiver after following all the embedded fields,
|
|
// but it's a value method, add a star operator.
|
|
arg0 = ir.NewStarExpr(arg0.Pos(), arg0)
|
|
}
|
|
args = append(args, arg0)
|
|
} else {
|
|
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.UseClosure(fn.OClosure, g.target)
|
|
var init []ir.Node
|
|
if outer != nil {
|
|
init = append(init, dictAssign)
|
|
}
|
|
if rcvrValue != nil {
|
|
init = append(init, rcvrAssign)
|
|
}
|
|
return ir.InitExpr(init, c)
|
|
}
|
|
|
|
// instantiateMethods instantiates all the methods (and associated dictionaries) of
|
|
// all fully-instantiated generic types that have been added to g.instTypeList.
|
|
func (g *irgen) instantiateMethods() {
|
|
for i := 0; i < len(g.instTypeList); i++ {
|
|
typ := g.instTypeList[i]
|
|
if typ.HasShape() {
|
|
// Shape types should not have any methods.
|
|
continue
|
|
}
|
|
// Mark runtime type as needed, since this ensures that the
|
|
// compiler puts out the needed DWARF symbols, when this
|
|
// instantiated type has a different package from the local
|
|
// package.
|
|
typecheck.NeedRuntimeType(typ)
|
|
// Lookup the method on the base generic type, since methods may
|
|
// not be set on imported instantiated types.
|
|
baseSym := typ.OrigSym
|
|
baseType := baseSym.Def.(*ir.Name).Type()
|
|
for j, _ := range typ.Methods().Slice() {
|
|
baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name)
|
|
// Eagerly generate the instantiations and dictionaries 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.getDictionarySym(baseNname, typ.RParams(), true)
|
|
}
|
|
}
|
|
g.instTypeList = nil
|
|
|
|
}
|
|
|
|
// getInstNameNode returns the name node for the method or function being instantiated, and a bool which is true if a method is being instantiated.
|
|
func (g *irgen) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) {
|
|
if meth, ok := inst.X.(*ir.SelectorExpr); ok {
|
|
return meth.Selection.Nname.(*ir.Name), true
|
|
} else {
|
|
return inst.X.(*ir.Name), false
|
|
}
|
|
}
|
|
|
|
// getDictOrSubdict returns, for a method/function call or reference (node n) in an
|
|
// instantiation (described by instInfo), a node which is accessing a sub-dictionary
|
|
// or main/static dictionary, as needed, and also returns a boolean indicating if a
|
|
// sub-dictionary was accessed. nameNode is the particular function or method being
|
|
// called/referenced, and targs are the type arguments.
|
|
func (g *irgen) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Name, targs []*types.Type, isMeth bool) (ir.Node, bool) {
|
|
var dict ir.Node
|
|
usingSubdict := false
|
|
if declInfo != nil {
|
|
// Get the dictionary arg via sub-dictionary reference
|
|
entry, ok := declInfo.dictEntryMap[n]
|
|
// If the entry is not found, it may be that this node did not have
|
|
// any type args that depend on type params, so we need a main
|
|
// dictionary, not a sub-dictionary.
|
|
if ok {
|
|
dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictLen)
|
|
usingSubdict = true
|
|
}
|
|
}
|
|
if !usingSubdict {
|
|
dict = g.getDictionaryValue(nameNode, targs, isMeth)
|
|
}
|
|
return dict, usingSubdict
|
|
}
|
|
|
|
func addGcType(fl []*types.Field, t *types.Type) []*types.Field {
|
|
return append(fl, types.NewField(base.Pos, typecheck.Lookup("F"+strconv.Itoa(len(fl))), t))
|
|
}
|
|
|
|
const INTTYPE = types.TINT64 // XX fix for 32-bit arch
|
|
const UINTTYPE = types.TUINT64 // XX fix for 32-bit arch
|
|
const INTSTRING = "i8" // XX fix for 32-bit arch
|
|
const UINTSTRING = "u8" // XX fix for 32-bit arch
|
|
|
|
// accumGcshape adds fields to fl resulting from the GCshape transformation of
|
|
// type t. The string associated with the GCshape transformation of t is added to
|
|
// buf. fieldSym is the sym of the field associated with type t, if it is in a
|
|
// struct. fieldSym could be used to have special naming for blank fields, etc.
|
|
func accumGcshape(fl []*types.Field, buf *bytes.Buffer, t *types.Type, fieldSym *types.Sym) []*types.Field {
|
|
// t.Kind() is already the kind of the underlying type, so no need to
|
|
// reference t.Underlying() to reference the underlying type.
|
|
assert(t.Kind() == t.Underlying().Kind())
|
|
|
|
switch t.Kind() {
|
|
case types.TINT8:
|
|
fl = addGcType(fl, types.Types[types.TINT8])
|
|
buf.WriteString("i1")
|
|
|
|
case types.TUINT8:
|
|
fl = addGcType(fl, types.Types[types.TUINT8])
|
|
buf.WriteString("u1")
|
|
|
|
case types.TINT16:
|
|
fl = addGcType(fl, types.Types[types.TINT16])
|
|
buf.WriteString("i2")
|
|
|
|
case types.TUINT16:
|
|
fl = addGcType(fl, types.Types[types.TUINT16])
|
|
buf.WriteString("u2")
|
|
|
|
case types.TINT32:
|
|
fl = addGcType(fl, types.Types[types.TINT32])
|
|
buf.WriteString("i4")
|
|
|
|
case types.TUINT32:
|
|
fl = addGcType(fl, types.Types[types.TUINT32])
|
|
buf.WriteString("u4")
|
|
|
|
case types.TINT64:
|
|
fl = addGcType(fl, types.Types[types.TINT64])
|
|
buf.WriteString("i8")
|
|
|
|
case types.TUINT64:
|
|
fl = addGcType(fl, types.Types[types.TUINT64])
|
|
buf.WriteString("u8")
|
|
|
|
case types.TINT:
|
|
fl = addGcType(fl, types.Types[INTTYPE])
|
|
buf.WriteString(INTSTRING)
|
|
|
|
case types.TUINT, types.TUINTPTR:
|
|
fl = addGcType(fl, types.Types[UINTTYPE])
|
|
buf.WriteString(UINTSTRING)
|
|
|
|
case types.TCOMPLEX64:
|
|
fl = addGcType(fl, types.Types[types.TFLOAT32])
|
|
fl = addGcType(fl, types.Types[types.TFLOAT32])
|
|
buf.WriteString("f4")
|
|
buf.WriteString("f4")
|
|
|
|
case types.TCOMPLEX128:
|
|
fl = addGcType(fl, types.Types[types.TFLOAT64])
|
|
fl = addGcType(fl, types.Types[types.TFLOAT64])
|
|
buf.WriteString("f8")
|
|
buf.WriteString("f8")
|
|
|
|
case types.TFLOAT32:
|
|
fl = addGcType(fl, types.Types[types.TFLOAT32])
|
|
buf.WriteString("f4")
|
|
|
|
case types.TFLOAT64:
|
|
fl = addGcType(fl, types.Types[types.TFLOAT64])
|
|
buf.WriteString("f8")
|
|
|
|
case types.TBOOL:
|
|
fl = addGcType(fl, types.Types[types.TINT8])
|
|
buf.WriteString("i1")
|
|
|
|
case types.TPTR:
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
buf.WriteString("p")
|
|
|
|
case types.TFUNC:
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
buf.WriteString("p")
|
|
|
|
case types.TSLICE:
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
fl = addGcType(fl, types.Types[INTTYPE])
|
|
fl = addGcType(fl, types.Types[INTTYPE])
|
|
buf.WriteString("p")
|
|
buf.WriteString(INTSTRING)
|
|
buf.WriteString(INTSTRING)
|
|
|
|
case types.TARRAY:
|
|
n := t.NumElem()
|
|
if n == 1 {
|
|
fl = accumGcshape(fl, buf, t.Elem(), nil)
|
|
} else if n > 0 {
|
|
// Represent an array with more than one element as its
|
|
// unique type, since it must be treated differently for
|
|
// regabi.
|
|
fl = addGcType(fl, t)
|
|
buf.WriteByte('[')
|
|
buf.WriteString(strconv.Itoa(int(n)))
|
|
buf.WriteString("](")
|
|
var ignore []*types.Field
|
|
// But to determine its gcshape name, we must call
|
|
// accumGcShape() on t.Elem().
|
|
accumGcshape(ignore, buf, t.Elem(), nil)
|
|
buf.WriteByte(')')
|
|
}
|
|
|
|
case types.TSTRUCT:
|
|
nfields := t.NumFields()
|
|
for i, f := range t.Fields().Slice() {
|
|
fl = accumGcshape(fl, buf, f.Type, f.Sym)
|
|
|
|
// Check if we need to add an alignment field.
|
|
var pad int64
|
|
if i < nfields-1 {
|
|
pad = t.Field(i+1).Offset - f.Offset - f.Type.Width
|
|
} else {
|
|
pad = t.Width - f.Offset - f.Type.Width
|
|
}
|
|
if pad > 0 {
|
|
// There is padding between fields or at end of
|
|
// struct. Add an alignment field.
|
|
fl = addGcType(fl, types.NewArray(types.Types[types.TUINT8], pad))
|
|
buf.WriteString("a")
|
|
buf.WriteString(strconv.Itoa(int(pad)))
|
|
}
|
|
}
|
|
|
|
case types.TCHAN:
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
buf.WriteString("p")
|
|
|
|
case types.TMAP:
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
buf.WriteString("p")
|
|
|
|
case types.TINTER:
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
buf.WriteString("pp")
|
|
|
|
case types.TFORW, types.TANY:
|
|
assert(false)
|
|
|
|
case types.TSTRING:
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
fl = addGcType(fl, types.Types[INTTYPE])
|
|
buf.WriteString("p")
|
|
buf.WriteString(INTSTRING)
|
|
|
|
case types.TUNSAFEPTR:
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
buf.WriteString("p")
|
|
|
|
default: // Everything TTYPEPARAM and below in list of Kinds
|
|
assert(false)
|
|
}
|
|
|
|
return fl
|
|
}
|
|
|
|
// gcshapeType returns the GCshape type and name corresponding to type t.
|
|
func gcshapeType(t *types.Type) (*types.Type, string) {
|
|
var fl []*types.Field
|
|
buf := bytes.NewBufferString("")
|
|
|
|
// Call CallSize so type sizes and field offsets are available.
|
|
types.CalcSize(t)
|
|
|
|
instType := t.Sym() != nil && t.IsFullyInstantiated()
|
|
if instType {
|
|
// We distinguish the gcshape of all top-level instantiated type from
|
|
// normal concrete types, even if they have the exact same underlying
|
|
// "shape", because in a function instantiation, any method call on
|
|
// this type arg will be a generic method call (requiring a
|
|
// dictionary), rather than a direct method call on the underlying
|
|
// type (no dictionary). So, we add the instshape prefix to the
|
|
// normal gcshape name, and will make it a defined type with that
|
|
// name below.
|
|
buf.WriteString("instshape-")
|
|
}
|
|
fl = accumGcshape(fl, buf, t, nil)
|
|
|
|
// TODO: Should gcshapes be in a global package, so we don't have to
|
|
// duplicate in each package? Or at least in the specified source package
|
|
// of a function/method instantiation?
|
|
gcshape := types.NewStruct(types.LocalPkg, fl)
|
|
gcname := buf.String()
|
|
if instType {
|
|
// Lookup or create type with name 'gcname' (with instshape prefix).
|
|
newsym := t.Sym().Pkg.Lookup(gcname)
|
|
if newsym.Def != nil {
|
|
gcshape = newsym.Def.Type()
|
|
} else {
|
|
newt := typecheck.NewIncompleteNamedType(t.Pos(), newsym)
|
|
newt.SetUnderlying(gcshape.Underlying())
|
|
gcshape = newt
|
|
}
|
|
}
|
|
assert(gcshape.Size() == t.Size())
|
|
return gcshape, buf.String()
|
|
}
|
|
|
|
// checkFetchBody checks if a generic body can be fetched, but hasn't been loaded
|
|
// yet. If so, it imports the body.
|
|
func checkFetchBody(nameNode *ir.Name) {
|
|
if nameNode.Func.Body == nil && nameNode.Func.Inl != nil {
|
|
// If there is no body yet but Func.Inl exists, then we can can
|
|
// import the whole generic body.
|
|
assert(nameNode.Func.Inl.Cost == 1 && nameNode.Sym().Pkg != types.LocalPkg)
|
|
typecheck.ImportBody(nameNode.Func)
|
|
assert(nameNode.Func.Inl.Body != nil)
|
|
nameNode.Func.Body = nameNode.Func.Inl.Body
|
|
nameNode.Func.Dcl = nameNode.Func.Inl.Dcl
|
|
}
|
|
}
|
|
|
|
// getInstantiation gets the instantiantion and dictionary of the function or method nameNode
|
|
// with the type arguments targs. If the instantiated function is not already
|
|
// cached, then it calls genericSubst to create the new instantiation.
|
|
func (g *irgen) getInstantiation(nameNode *ir.Name, targs []*types.Type, isMeth bool) *ir.Func {
|
|
checkFetchBody(nameNode)
|
|
|
|
// Convert type arguments to their shape, so we can reduce the number
|
|
// of instantiations we have to generate.
|
|
shapes := typecheck.ShapifyList(targs)
|
|
|
|
sym := typecheck.MakeInstName(nameNode.Sym(), shapes, isMeth)
|
|
info := g.instInfoMap[sym]
|
|
if info == nil {
|
|
if false {
|
|
// Testing out gcshapeType() and gcshapeName()
|
|
for i, t := range targs {
|
|
gct, gcs := gcshapeType(t)
|
|
fmt.Printf("targ %d: %v %v %v\n", i, gcs, gct, gct.Underlying())
|
|
}
|
|
}
|
|
// If instantiation doesn't exist yet, create it and add
|
|
// to the list of decls.
|
|
gfInfo := g.getGfInfo(nameNode)
|
|
info = &instInfo{
|
|
gf: nameNode,
|
|
gfInfo: gfInfo,
|
|
startSubDict: len(targs) + len(gfInfo.derivedTypes),
|
|
startItabConv: len(targs) + len(gfInfo.derivedTypes) + len(gfInfo.subDictCalls),
|
|
dictLen: len(targs) + len(gfInfo.derivedTypes) + len(gfInfo.subDictCalls) + len(gfInfo.itabConvs),
|
|
dictEntryMap: make(map[ir.Node]int),
|
|
}
|
|
// genericSubst fills in info.dictParam and info.dictEntryMap.
|
|
st := g.genericSubst(sym, nameNode, shapes, targs, isMeth, info)
|
|
info.fun = st
|
|
g.instInfoMap[sym] = info
|
|
// This ensures that the linker drops duplicates of this instantiation.
|
|
// All just works!
|
|
st.SetDupok(true)
|
|
g.target.Decls = append(g.target.Decls, st)
|
|
if base.Flag.W > 1 {
|
|
ir.Dump(fmt.Sprintf("\nstenciled %v", st), st)
|
|
}
|
|
}
|
|
return info.fun
|
|
}
|
|
|
|
// 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
|
|
info *instInfo // Place to put extra info in the instantiation
|
|
|
|
// unshapeify maps from shape types to the concrete types they represent.
|
|
// TODO: remove when we no longer need it.
|
|
unshapify typecheck.Tsubster
|
|
|
|
// TODO: some sort of map from <shape type, interface type> to index in the
|
|
// dictionary where a *runtime.itab for the corresponding <concrete type,
|
|
// interface type> pair resides.
|
|
}
|
|
|
|
// genericSubst returns a new function with name newsym. The function is an
|
|
// instantiation of a generic function or method specified by namedNode with type
|
|
// 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. genericSubst fills in info.dictParam and info.dictEntryMap.
|
|
func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes, 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
|
|
// over any pointer)
|
|
recvType := nameNode.Type().Recv().Type
|
|
recvType = deref(recvType)
|
|
tparams = recvType.RParams()
|
|
} else {
|
|
fields := nameNode.Type().TParams().Fields().Slice()
|
|
tparams = make([]*types.Type, len(fields))
|
|
for i, f := range fields {
|
|
tparams[i] = f.Type
|
|
}
|
|
}
|
|
for i := range targs {
|
|
if targs[i].HasShape() {
|
|
base.Fatalf("generiSubst shape %s %+v %+v\n", newsym.Name, shapes[i], targs[i])
|
|
}
|
|
}
|
|
gf := nameNode.Func
|
|
// Pos of the instantiated function is same as the generic function
|
|
newf := ir.NewFunc(gf.Pos())
|
|
newf.Pragma = gf.Pragma // copy over pragmas from generic function to stenciled implementation.
|
|
newf.Nname = ir.NewNameAt(gf.Pos(), newsym)
|
|
newf.Nname.Func = newf
|
|
newf.Nname.Defn = newf
|
|
newsym.Def = newf.Nname
|
|
savef := ir.CurFunc
|
|
// transformCall/transformReturn (called during stenciling of the body)
|
|
// depend on ir.CurFunc being set.
|
|
ir.CurFunc = newf
|
|
|
|
assert(len(tparams) == len(shapes))
|
|
assert(len(tparams) == len(targs))
|
|
|
|
subst := &subster{
|
|
g: g,
|
|
isMethod: isMethod,
|
|
newf: newf,
|
|
info: info,
|
|
ts: typecheck.Tsubster{
|
|
Tparams: tparams,
|
|
Targs: shapes,
|
|
Vars: make(map[*ir.Name]*ir.Name),
|
|
},
|
|
unshapify: typecheck.Tsubster{
|
|
Tparams: shapes,
|
|
Targs: targs,
|
|
Vars: make(map[*ir.Name]*ir.Name),
|
|
},
|
|
}
|
|
|
|
newf.Dcl = make([]*ir.Name, 0, len(gf.Dcl)+1)
|
|
|
|
// Create the needed dictionary param
|
|
dictionarySym := newsym.Pkg.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
|
|
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))
|
|
|
|
typed(newt, newf.Nname)
|
|
ir.MarkFunc(newf.Nname)
|
|
newf.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.
|
|
// TODO: must go away when we move to many->1 shape to concrete mapping.
|
|
newf.Body.Prepend(subst.checkDictionary(dictionaryName, targs)...)
|
|
|
|
ir.CurFunc = savef
|
|
// Add any new, fully instantiated types seen during the substitution to
|
|
// g.instTypeList.
|
|
g.instTypeList = append(g.instTypeList, subst.ts.InstTypeList...)
|
|
g.instTypeList = append(g.instTypeList, subst.unshapify.InstTypeList...)
|
|
|
|
if doubleCheck {
|
|
okConvs := map[ir.Node]bool{}
|
|
ir.Visit(newf, func(n ir.Node) {
|
|
if n.Op() == ir.OIDATA {
|
|
// IDATA(OCONVIFACE(x)) is ok, as we don't use the type of x.
|
|
// TODO: use some other op besides OCONVIFACE. ONEW might work
|
|
// (with appropriate direct vs. indirect interface cases).
|
|
okConvs[n.(*ir.UnaryExpr).X] = true
|
|
}
|
|
if n.Op() == ir.OCONVIFACE && !okConvs[n] {
|
|
c := n.(*ir.ConvExpr)
|
|
if c.X.Type().HasShape() {
|
|
ir.Dump("BAD FUNCTION", newf)
|
|
ir.Dump("BAD CONVERSION", c)
|
|
base.Fatalf("converting shape type to interface")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
return newf
|
|
}
|
|
|
|
func (subst *subster) unshapifyTyp(t *types.Type) *types.Type {
|
|
res := subst.unshapify.Typ(t)
|
|
types.CheckSize(res)
|
|
return res
|
|
}
|
|
|
|
// localvar creates a new name node for the specified local variable and enters it
|
|
// in subst.vars. It substitutes type arguments for type parameters in the type of
|
|
// name as needed.
|
|
func (subst *subster) localvar(name *ir.Name) *ir.Name {
|
|
m := ir.NewNameAt(name.Pos(), name.Sym())
|
|
if name.IsClosureVar() {
|
|
m.SetIsClosureVar(true)
|
|
}
|
|
m.SetType(subst.ts.Typ(name.Type()))
|
|
m.BuiltinOp = name.BuiltinOp
|
|
m.Curfn = subst.newf
|
|
m.Class = name.Class
|
|
assert(name.Class != ir.PEXTERN && name.Class != ir.PFUNC)
|
|
m.Func = name.Func
|
|
subst.ts.Vars[name] = m
|
|
m.SetTypecheck(1)
|
|
return m
|
|
}
|
|
|
|
// checkDictionary returns code that does runtime consistency checks
|
|
// between the dictionary and the types it should contain.
|
|
func (subst *subster) 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 {
|
|
if t.HasShape() {
|
|
// Check the concrete type, not the shape type.
|
|
// TODO: can this happen?
|
|
//t = subst.unshapify.Typ(t)
|
|
base.Fatalf("shape type in dictionary %s %+v\n", name.Sym().Name, t)
|
|
continue
|
|
}
|
|
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
|
|
}
|
|
|
|
// 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 entry i
|
|
// (which refers to a type param or a derived type that uses type params).
|
|
func (subst *subster) getDictionaryType(pos src.XPos, i int) ir.Node {
|
|
if i < 0 || i >= subst.info.startSubDict {
|
|
base.Fatalf(fmt.Sprintf("bad dict index %d", i))
|
|
}
|
|
|
|
r := getDictionaryEntry(pos, subst.info.dictParam, i, subst.info.startSubDict)
|
|
// change type of retrieved dictionary entry to *byte, which is the
|
|
// standard typing of a *runtime._type in the compiler
|
|
typed(types.Types[types.TUINT8].PtrTo(), r)
|
|
return r
|
|
}
|
|
|
|
// node is like DeepCopy(), but substitutes ONAME nodes based on subst.ts.vars, and
|
|
// also descends into closures. It substitutes type arguments for type parameters
|
|
// in all the new nodes.
|
|
func (subst *subster) node(n ir.Node) ir.Node {
|
|
// Use closure to capture all state needed by the ir.EditChildren argument.
|
|
var edit func(ir.Node) ir.Node
|
|
edit = func(x ir.Node) ir.Node {
|
|
switch x.Op() {
|
|
case ir.OTYPE:
|
|
return ir.TypeNode(subst.ts.Typ(x.Type()))
|
|
|
|
case ir.ONAME:
|
|
if v := subst.ts.Vars[x.(*ir.Name)]; v != nil {
|
|
return v
|
|
}
|
|
return x
|
|
case ir.ONONAME:
|
|
// This handles the identifier in a type switch guard
|
|
fallthrough
|
|
case ir.OLITERAL, ir.ONIL:
|
|
if x.Sym() != nil {
|
|
return x
|
|
}
|
|
}
|
|
m := ir.Copy(x)
|
|
if _, isExpr := m.(ir.Expr); isExpr {
|
|
t := x.Type()
|
|
if t == nil {
|
|
// t can be nil only if this is a call that has no
|
|
// return values, so allow that and otherwise give
|
|
// an error.
|
|
_, isCallExpr := m.(*ir.CallExpr)
|
|
_, isStructKeyExpr := m.(*ir.StructKeyExpr)
|
|
_, isKeyExpr := m.(*ir.KeyExpr)
|
|
if !isCallExpr && !isStructKeyExpr && !isKeyExpr && x.Op() != ir.OPANIC &&
|
|
x.Op() != ir.OCLOSE {
|
|
base.Fatalf(fmt.Sprintf("Nil type for %v", x))
|
|
}
|
|
} else if x.Op() != ir.OCLOSURE {
|
|
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)
|
|
if typecheck.IsCmp(x.Op()) {
|
|
transformCompare(m.(*ir.BinaryExpr))
|
|
} else {
|
|
switch x.Op() {
|
|
case ir.OSLICE, ir.OSLICE3:
|
|
transformSlice(m.(*ir.SliceExpr))
|
|
|
|
case ir.OADD:
|
|
m = transformAdd(m.(*ir.BinaryExpr))
|
|
|
|
case ir.OINDEX:
|
|
transformIndex(m.(*ir.IndexExpr))
|
|
|
|
case ir.OAS2:
|
|
as2 := m.(*ir.AssignListStmt)
|
|
transformAssign(as2, as2.Lhs, as2.Rhs)
|
|
|
|
case ir.OAS:
|
|
as := m.(*ir.AssignStmt)
|
|
if as.Y != nil {
|
|
// transformAssign doesn't handle the case
|
|
// of zeroing assignment of a dcl (rhs[0] is nil).
|
|
lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y}
|
|
transformAssign(as, lhs, rhs)
|
|
}
|
|
|
|
case ir.OASOP:
|
|
as := m.(*ir.AssignOpStmt)
|
|
transformCheckAssign(as, as.X)
|
|
|
|
case ir.ORETURN:
|
|
transformReturn(m.(*ir.ReturnStmt))
|
|
|
|
case ir.OSEND:
|
|
transformSend(m.(*ir.SendStmt))
|
|
|
|
}
|
|
}
|
|
|
|
switch x.Op() {
|
|
case ir.OLITERAL:
|
|
t := m.Type()
|
|
if t != x.Type() {
|
|
// types2 will give us a constant with a type T,
|
|
// if an untyped constant is used with another
|
|
// operand of type T (in a provably correct way).
|
|
// When we substitute in the type args during
|
|
// stenciling, we now know the real type of the
|
|
// constant. We may then need to change the
|
|
// BasicLit.val to be the correct type (e.g.
|
|
// convert an int64Val constant to a floatVal
|
|
// constant).
|
|
m.SetType(types.UntypedInt) // use any untyped type for DefaultLit to work
|
|
m = typecheck.DefaultLit(m, t)
|
|
}
|
|
|
|
case ir.OXDOT:
|
|
// A method value/call via a type param will have been
|
|
// left as an OXDOT. When we see this during stenciling,
|
|
// finish the transformation, now that we have the
|
|
// instantiated receiver type. We need to do this now,
|
|
// since the access/selection to the method for the real
|
|
// type is very different from the selection for the type
|
|
// param. m will be transformed to an OMETHVALUE node. It
|
|
// will be transformed to an ODOTMETH or ODOTINTER node if
|
|
// we find in the OCALL case below that the method value
|
|
// is actually called.
|
|
mse := m.(*ir.SelectorExpr)
|
|
if src := mse.X.Type(); src.IsShape() {
|
|
// The only dot on a shape type value are methods.
|
|
if mse.X.Op() == ir.OTYPE {
|
|
// Method expression T.M
|
|
// Fall back from shape type to concrete type.
|
|
src = subst.unshapifyTyp(src)
|
|
mse.X = ir.TypeNode(src)
|
|
} else {
|
|
// Implement x.M as a conversion-to-bound-interface
|
|
// 1) convert x to the bound interface
|
|
// 2) call M on that interface
|
|
gsrc := x.(*ir.SelectorExpr).X.Type()
|
|
dst := gsrc.Bound()
|
|
if dst.HasTParam() {
|
|
dst = subst.ts.Typ(dst)
|
|
}
|
|
mse.X = subst.convertUsingDictionary(m.Pos(), mse.X, x, dst, gsrc)
|
|
}
|
|
}
|
|
transformDot(mse, false)
|
|
if mse.Op() == ir.OMETHEXPR && mse.X.Type().HasShape() {
|
|
mse.X = ir.TypeNodeAt(mse.X.Pos(), subst.unshapifyTyp(mse.X.Type()))
|
|
}
|
|
m.SetTypecheck(1)
|
|
|
|
case ir.OCALL:
|
|
call := m.(*ir.CallExpr)
|
|
switch call.X.Op() {
|
|
case ir.OTYPE:
|
|
// Transform the conversion, now that we know the
|
|
// type argument.
|
|
m = transformConvCall(call)
|
|
// CONVIFACE transformation was already done in node2
|
|
assert(m.Op() != ir.OCONVIFACE)
|
|
|
|
case ir.OMETHVALUE, ir.OMETHEXPR:
|
|
// Redo the transformation of OXDOT, now that we
|
|
// know the method value is being called. Then
|
|
// transform the call.
|
|
call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
|
|
transformDot(call.X.(*ir.SelectorExpr), true)
|
|
call.X.SetType(subst.unshapifyTyp(call.X.Type()))
|
|
transformCall(call)
|
|
|
|
case ir.ODOT, ir.ODOTPTR:
|
|
// An OXDOT for a generic receiver was resolved to
|
|
// an access to a field which has a function
|
|
// value. Transform the call to that function, now
|
|
// that the OXDOT was resolved.
|
|
transformCall(call)
|
|
|
|
case ir.ONAME:
|
|
name := call.X.Name()
|
|
if name.BuiltinOp != ir.OXXX {
|
|
switch name.BuiltinOp {
|
|
case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE:
|
|
// Transform these builtins now that we
|
|
// know the type of the args.
|
|
m = transformBuiltin(call)
|
|
default:
|
|
base.FatalfAt(call.Pos(), "Unexpected builtin op")
|
|
}
|
|
} else {
|
|
// This is the case of a function value that was a
|
|
// type parameter (implied to be a function via a
|
|
// structural constraint) which is now resolved.
|
|
transformCall(call)
|
|
}
|
|
|
|
case ir.OCLOSURE:
|
|
transformCall(call)
|
|
|
|
case ir.OFUNCINST:
|
|
// A call with an OFUNCINST will get transformed
|
|
// in stencil() once we have created & attached the
|
|
// instantiation to be called.
|
|
|
|
default:
|
|
base.FatalfAt(call.Pos(), fmt.Sprintf("Unexpected op with CALL during stenciling: %v", call.X.Op()))
|
|
}
|
|
|
|
case ir.OCLOSURE:
|
|
// We're going to create a new closure from scratch, so clear m
|
|
// to avoid using the ir.Copy by accident until we reassign it.
|
|
m = nil
|
|
|
|
x := x.(*ir.ClosureExpr)
|
|
// Need to duplicate x.Func.Nname, x.Func.Dcl, x.Func.ClosureVars, and
|
|
// x.Func.Body.
|
|
oldfn := x.Func
|
|
newfn := ir.NewClosureFunc(oldfn.Pos(), subst.newf != nil)
|
|
ir.NameClosure(newfn.OClosure, subst.newf)
|
|
|
|
saveNewf := subst.newf
|
|
ir.CurFunc = newfn
|
|
subst.newf = newfn
|
|
newfn.Dcl = subst.namelist(oldfn.Dcl)
|
|
|
|
// 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)
|
|
newfn.SetTypecheck(1)
|
|
|
|
// Make sure type of closure function is set before doing body.
|
|
newfn.Body = subst.list(oldfn.Body)
|
|
subst.newf = saveNewf
|
|
ir.CurFunc = saveNewf
|
|
|
|
m = ir.UseClosure(newfn.OClosure, subst.g.target)
|
|
m.(*ir.ClosureExpr).SetInit(subst.list(x.Init()))
|
|
|
|
case ir.OCONVIFACE:
|
|
x := x.(*ir.ConvExpr)
|
|
// Note: x's argument is still typed as a type parameter.
|
|
// m's argument now has an instantiated type.
|
|
if x.X.Type().HasTParam() {
|
|
m = subst.convertUsingDictionary(m.Pos(), m.(*ir.ConvExpr).X, x, m.Type(), x.X.Type())
|
|
}
|
|
case ir.ODOTTYPE, ir.ODOTTYPE2:
|
|
m.SetType(subst.unshapifyTyp(m.Type()))
|
|
|
|
case ir.OMETHEXPR:
|
|
se := m.(*ir.SelectorExpr)
|
|
se.X = ir.TypeNodeAt(se.X.Pos(), subst.unshapifyTyp(se.X.Type()))
|
|
case ir.OFUNCINST:
|
|
inst := m.(*ir.InstExpr)
|
|
targs2 := make([]ir.Node, len(inst.Targs))
|
|
for i, n := range inst.Targs {
|
|
targs2[i] = ir.TypeNodeAt(n.Pos(), subst.unshapifyTyp(n.Type()))
|
|
// TODO: need an ir.Name node?
|
|
}
|
|
inst.Targs = targs2
|
|
}
|
|
return m
|
|
}
|
|
|
|
return edit(n)
|
|
}
|
|
|
|
// findDictType looks for type t in the typeparams or derived types in the generic
|
|
// function info subst.info.gfInfo. This will indicate the dictionary entry with the
|
|
// correct concrete type for the associated instantiated function.
|
|
func (subst *subster) findDictType(t *types.Type) int {
|
|
for i, dt := range subst.info.gfInfo.tparams {
|
|
if dt == t {
|
|
return i
|
|
}
|
|
}
|
|
for i, dt := range subst.info.gfInfo.derivedTypes {
|
|
if types.Identical(dt, t) {
|
|
return i + len(subst.info.gfInfo.tparams)
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// convertUsingDictionary converts value v from instantiated type src to an interface
|
|
// type dst, by returning a new set of nodes that make use of a dictionary entry. src
|
|
// is the generic (not shape) type, and gn is the original generic node of the
|
|
// CONVIFACE node or XDOT node (for a bound method call) that is causing the
|
|
// conversion.
|
|
func (subst *subster) convertUsingDictionary(pos src.XPos, v ir.Node, gn ir.Node, dst, src *types.Type) ir.Node {
|
|
assert(src.HasTParam())
|
|
assert(dst.IsInterface())
|
|
|
|
var rt ir.Node
|
|
if !dst.IsEmptyInterface() {
|
|
// We should have an itab entry in the dictionary. Using this itab
|
|
// will be more efficient than converting to an empty interface first
|
|
// and then type asserting to dst.
|
|
ix := -1
|
|
for i, ic := range subst.info.gfInfo.itabConvs {
|
|
if ic == gn {
|
|
ix = subst.info.startItabConv + i
|
|
break
|
|
}
|
|
}
|
|
assert(ix >= 0)
|
|
rt = getDictionaryEntry(pos, subst.info.dictParam, ix, subst.info.dictLen)
|
|
} else {
|
|
ix := subst.findDictType(src)
|
|
assert(ix >= 0)
|
|
// Load the actual runtime._type of the type parameter from the dictionary.
|
|
rt = subst.getDictionaryType(pos, ix)
|
|
}
|
|
|
|
// Figure out what the data field of the interface will be.
|
|
var data ir.Node
|
|
if v.Type().IsInterface() {
|
|
data = ir.NewUnaryExpr(pos, ir.OIDATA, v)
|
|
} else {
|
|
data = ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v)
|
|
}
|
|
typed(types.Types[types.TUNSAFEPTR], data)
|
|
|
|
// Build an interface from the type and data parts.
|
|
var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data)
|
|
typed(dst, i)
|
|
return i
|
|
|
|
}
|
|
|
|
func (subst *subster) namelist(l []*ir.Name) []*ir.Name {
|
|
s := make([]*ir.Name, len(l))
|
|
for i, n := range l {
|
|
s[i] = subst.localvar(n)
|
|
if n.Defn != nil {
|
|
s[i].Defn = subst.node(n.Defn)
|
|
}
|
|
if n.Outer != nil {
|
|
s[i].Outer = subst.node(n.Outer).(*ir.Name)
|
|
}
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (subst *subster) list(l []ir.Node) []ir.Node {
|
|
s := make([]ir.Node, len(l))
|
|
for i, n := range l {
|
|
s[i] = subst.node(n)
|
|
}
|
|
return s
|
|
}
|
|
|
|
// fields sets the Nname field for the Field nodes inside a type signature, based
|
|
// on the corresponding in/out parameters in dcl. It depends on the in and out
|
|
// parameters being in order in dcl.
|
|
func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field {
|
|
// Find the starting index in dcl of declarations of the class (either
|
|
// PPARAM or PPARAMOUT).
|
|
var i int
|
|
for i = range dcl {
|
|
if dcl[i].Class == class {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Create newfields nodes that are copies of the oldfields nodes, but
|
|
// with substitution for any type params, and with Nname set to be the node in
|
|
// Dcl for the corresponding PPARAM or PPARAMOUT.
|
|
newfields := make([]*types.Field, len(oldfields))
|
|
for j := range oldfields {
|
|
newfields[j] = oldfields[j].Copy()
|
|
newfields[j].Type = subst.ts.Typ(oldfields[j].Type)
|
|
// A PPARAM field will be missing from dcl if its name is
|
|
// unspecified or specified as "_". So, we compare the dcl sym
|
|
// with the field sym (or sym of the field's Nname node). (Unnamed
|
|
// results still have a name like ~r2 in their Nname node.) If
|
|
// they don't match, this dcl (if there is one left) must apply to
|
|
// a later field.
|
|
if i < len(dcl) && (dcl[i].Sym() == oldfields[j].Sym ||
|
|
(oldfields[j].Nname != nil && dcl[i].Sym() == oldfields[j].Nname.Sym())) {
|
|
newfields[j].Nname = dcl[i]
|
|
i++
|
|
}
|
|
}
|
|
return newfields
|
|
}
|
|
|
|
// deref does a single deref of type t, if it is a pointer type.
|
|
func deref(t *types.Type) *types.Type {
|
|
if t.IsPtr() {
|
|
return t.Elem()
|
|
}
|
|
return t
|
|
}
|
|
|
|
// markTypeUsed marks type t as used in order to help avoid dead-code elimination of
|
|
// needed methods.
|
|
func markTypeUsed(t *types.Type, lsym *obj.LSym) {
|
|
if t.IsInterface() {
|
|
// Mark all the methods of the interface as used.
|
|
// TODO: we should really only mark the interface methods
|
|
// that are actually called in the application.
|
|
for i, _ := range t.AllMethods().Slice() {
|
|
reflectdata.MarkUsedIfaceMethodIndex(lsym, t, i)
|
|
}
|
|
} else {
|
|
// TODO: This is somewhat overkill, we really only need it
|
|
// for types that are put into interfaces.
|
|
reflectdata.MarkTypeUsedInInterface(t, lsym)
|
|
}
|
|
}
|
|
|
|
// getDictionarySym returns the dictionary for the named generic function gf, which
|
|
// is instantiated with the type arguments targs.
|
|
func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) *types.Sym {
|
|
if len(targs) == 0 {
|
|
base.Fatalf("%s should have type arguments", gf.Sym().Name)
|
|
}
|
|
|
|
// Enforce that only concrete types can make it to here.
|
|
for _, t := range targs {
|
|
if t.HasShape() {
|
|
panic(fmt.Sprintf("shape %+v in dictionary for %s", t, gf.Sym().Name))
|
|
}
|
|
}
|
|
|
|
// Get a symbol representing the dictionary.
|
|
sym := typecheck.MakeDictName(gf.Sym(), targs, isMeth)
|
|
|
|
// Initialize the dictionary, if we haven't yet already.
|
|
if lsym := sym.Linksym(); len(lsym.P) == 0 {
|
|
info := g.getGfInfo(gf)
|
|
|
|
infoPrint("=== Creating dictionary %v\n", sym.Name)
|
|
off := 0
|
|
// Emit an entry for each targ (concrete type or gcshape).
|
|
for _, t := range targs {
|
|
infoPrint(" * %v\n", t)
|
|
s := reflectdata.TypeLinksym(t)
|
|
off = objw.SymPtr(lsym, off, s, 0)
|
|
markTypeUsed(t, lsym)
|
|
}
|
|
subst := typecheck.Tsubster{
|
|
Tparams: info.tparams,
|
|
Targs: targs,
|
|
}
|
|
// Emit an entry for each derived type (after substituting targs)
|
|
for _, t := range info.derivedTypes {
|
|
ts := subst.Typ(t)
|
|
infoPrint(" - %v\n", ts)
|
|
s := reflectdata.TypeLinksym(ts)
|
|
off = objw.SymPtr(lsym, off, s, 0)
|
|
markTypeUsed(ts, lsym)
|
|
}
|
|
// Emit an entry for each subdictionary (after substituting targs)
|
|
for _, n := range info.subDictCalls {
|
|
var sym *types.Sym
|
|
switch n.Op() {
|
|
case ir.OCALL:
|
|
call := n.(*ir.CallExpr)
|
|
if call.X.Op() == ir.OXDOT {
|
|
var nameNode *ir.Name
|
|
se := call.X.(*ir.SelectorExpr)
|
|
if types.IsInterfaceMethod(se.Selection.Type) {
|
|
// This is a method call enabled by a type bound.
|
|
tmpse := ir.NewSelectorExpr(base.Pos, ir.OXDOT, se.X, se.Sel)
|
|
tmpse = typecheck.AddImplicitDots(tmpse)
|
|
tparam := tmpse.X.Type()
|
|
assert(tparam.IsTypeParam())
|
|
recvType := targs[tparam.Index()]
|
|
if len(recvType.RParams()) == 0 {
|
|
// No sub-dictionary entry is
|
|
// actually needed, since the
|
|
// typeparam is not an
|
|
// instantiated type that
|
|
// will have generic methods.
|
|
break
|
|
}
|
|
// This is a method call for an
|
|
// instantiated type, so we need a
|
|
// sub-dictionary.
|
|
targs := recvType.RParams()
|
|
genRecvType := recvType.OrigSym.Def.Type()
|
|
nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
|
|
sym = g.getDictionarySym(nameNode, targs, true)
|
|
} else {
|
|
// This is the case of a normal
|
|
// method call on a generic type.
|
|
nameNode = call.X.(*ir.SelectorExpr).Selection.Nname.(*ir.Name)
|
|
subtargs := deref(call.X.(*ir.SelectorExpr).X.Type()).RParams()
|
|
s2targs := make([]*types.Type, len(subtargs))
|
|
for i, t := range subtargs {
|
|
s2targs[i] = subst.Typ(t)
|
|
}
|
|
sym = g.getDictionarySym(nameNode, s2targs, true)
|
|
}
|
|
} 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)
|
|
} else {
|
|
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, isMeth)
|
|
}
|
|
|
|
case ir.OFUNCINST:
|
|
inst := n.(*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)
|
|
|
|
case ir.OXDOT:
|
|
selExpr := n.(*ir.SelectorExpr)
|
|
subtargs := selExpr.X.Type().RParams()
|
|
s2targs := make([]*types.Type, len(subtargs))
|
|
for i, t := range subtargs {
|
|
s2targs[i] = subst.Typ(t)
|
|
}
|
|
nameNode := selExpr.Selection.Nname.(*ir.Name)
|
|
sym = g.getDictionarySym(nameNode, s2targs, true)
|
|
|
|
default:
|
|
assert(false)
|
|
}
|
|
|
|
if sym == nil {
|
|
// Unused sub-dictionary entry, just emit 0.
|
|
off = objw.Uintptr(lsym, off, 0)
|
|
infoPrint(" - Unused subdict entry\n")
|
|
} else {
|
|
off = objw.SymPtr(lsym, off, sym.Linksym(), 0)
|
|
infoPrint(" - Subdict %v\n", sym.Name)
|
|
}
|
|
}
|
|
|
|
delay := &delayInfo{
|
|
gf: gf,
|
|
targs: targs,
|
|
sym: sym,
|
|
off: off,
|
|
}
|
|
g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay)
|
|
g.instTypeList = append(g.instTypeList, subst.InstTypeList...)
|
|
}
|
|
return sym
|
|
}
|
|
|
|
// 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
|
|
// dependencies, we finalize the itab lsyms only after all dictionaries syms and
|
|
// instantiations have been created.
|
|
func (g *irgen) finalizeSyms() {
|
|
for _, d := range g.dictSymsToFinalize {
|
|
lsym := d.sym.Linksym()
|
|
info := g.getGfInfo(d.gf)
|
|
|
|
subst := typecheck.Tsubster{
|
|
Tparams: info.tparams,
|
|
Targs: d.targs,
|
|
}
|
|
|
|
// Emit an entry for each itab
|
|
for _, n := range info.itabConvs {
|
|
var srctype, dsttype *types.Type
|
|
if n.Op() == ir.OXDOT {
|
|
se := n.(*ir.SelectorExpr)
|
|
srctype = subst.Typ(se.X.Type())
|
|
dsttype = subst.Typ(se.X.Type().Bound())
|
|
found := false
|
|
for i, m := range dsttype.AllMethods().Slice() {
|
|
if se.Sel == m.Sym {
|
|
// Mark that this method se.Sel is
|
|
// used for the dsttype interface, so
|
|
// it won't get deadcoded.
|
|
reflectdata.MarkUsedIfaceMethodIndex(lsym, dsttype, i)
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert(found)
|
|
} else {
|
|
assert(n.Op() == ir.OCONVIFACE)
|
|
srctype = subst.Typ(n.(*ir.ConvExpr).X.Type())
|
|
dsttype = subst.Typ(n.Type())
|
|
}
|
|
itabLsym := reflectdata.ITabLsym(srctype, dsttype)
|
|
d.off = objw.SymPtr(lsym, d.off, itabLsym, 0)
|
|
}
|
|
|
|
objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA)
|
|
infoPrint("=== Finalized dictionary %s\n", d.sym.Name)
|
|
|
|
g.instTypeList = append(g.instTypeList, subst.InstTypeList...)
|
|
}
|
|
g.dictSymsToFinalize = nil
|
|
}
|
|
|
|
func (g *irgen) getDictionaryValue(gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node {
|
|
sym := g.getDictionarySym(gf, targs, isMeth)
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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 {
|
|
infop := g.gfInfoMap[gn.Sym()]
|
|
if infop != nil {
|
|
return infop
|
|
}
|
|
|
|
checkFetchBody(gn)
|
|
var info gfInfo
|
|
gf := gn.Func
|
|
recv := gf.Type().Recv()
|
|
if recv != nil {
|
|
info.tparams = deref(recv.Type).RParams()
|
|
} else {
|
|
tparams := gn.Type().TParams().FieldSlice()
|
|
info.tparams = make([]*types.Type, len(tparams))
|
|
for i, f := range tparams {
|
|
info.tparams[i] = f.Type
|
|
}
|
|
}
|
|
for _, n := range gf.Dcl {
|
|
addType(&info, n, n.Type())
|
|
}
|
|
|
|
if infoPrintMode {
|
|
fmt.Printf(">>> GfInfo for %v\n", gn)
|
|
for _, t := range info.tparams {
|
|
fmt.Printf(" Typeparam %v\n", t)
|
|
}
|
|
}
|
|
|
|
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 {
|
|
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 {
|
|
infoPrint(" Closure&subdictionary required at generic meth value %v\n", n)
|
|
}
|
|
info.subDictCalls = append(info.subDictCalls, n)
|
|
}
|
|
}
|
|
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 {
|
|
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)
|
|
info.subDictCalls = append(info.subDictCalls, n)
|
|
}
|
|
}
|
|
if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT &&
|
|
n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil &&
|
|
deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).IsTypeParam() {
|
|
n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true)
|
|
infoPrint(" Optional subdictionary at generic bound call: %v\n", n)
|
|
info.subDictCalls = append(info.subDictCalls, n)
|
|
}
|
|
if n.Op() == ir.OCONVIFACE && n.Type().IsInterface() &&
|
|
!n.Type().IsEmptyInterface() &&
|
|
n.(*ir.ConvExpr).X.Type().HasTParam() {
|
|
infoPrint(" Itab for interface conv: %v\n", n)
|
|
info.itabConvs = append(info.itabConvs, n)
|
|
}
|
|
if n.Op() == ir.OXDOT && n.(*ir.SelectorExpr).X.Type().IsTypeParam() {
|
|
infoPrint(" Itab for interface conv: %v\n", n)
|
|
info.itabConvs = append(info.itabConvs, 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())
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// addType adds t to info.derivedTypes if it is parameterized type (which is not
|
|
// just a simple type param) that is different from any existing type on
|
|
// info.derivedTypes.
|
|
func addType(info *gfInfo, n ir.Node, t *types.Type) {
|
|
if t == nil || !t.HasTParam() {
|
|
return
|
|
}
|
|
if t.IsTypeParam() && t.Underlying() == t {
|
|
return
|
|
}
|
|
if t.Kind() == types.TFUNC && n != nil &&
|
|
(t.Recv() != nil ||
|
|
n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) {
|
|
// Don't use the type of a named generic function or method,
|
|
// since that is parameterized by other typeparams.
|
|
// (They all come from arguments of a FUNCINST node.)
|
|
return
|
|
}
|
|
if doubleCheck && !parameterizedBy(t, info.tparams) {
|
|
base.Fatalf("adding type with invalid parameters %+v", t)
|
|
}
|
|
if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() {
|
|
// Multiple return values are not a relevant new type (?).
|
|
return
|
|
}
|
|
// Ignore a derived type we've already added.
|
|
for _, et := range info.derivedTypes {
|
|
if types.Identical(t, et) {
|
|
return
|
|
}
|
|
}
|
|
info.derivedTypes = append(info.derivedTypes, t)
|
|
}
|
|
|
|
// parameterizedBy returns true if t is parameterized by (at most) params.
|
|
func parameterizedBy(t *types.Type, params []*types.Type) bool {
|
|
return parameterizedBy1(t, params, map[*types.Type]bool{})
|
|
}
|
|
func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Type]bool) bool {
|
|
if visited[t] {
|
|
return true
|
|
}
|
|
visited[t] = true
|
|
|
|
if t.Sym() != nil && len(t.RParams()) > 0 {
|
|
// This defined type is instantiated. Check the instantiating types.
|
|
for _, r := range t.RParams() {
|
|
if !parameterizedBy1(r, params, visited) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
switch t.Kind() {
|
|
case types.TTYPEPARAM:
|
|
// Check if t is one of the allowed parameters in scope.
|
|
for _, p := range params {
|
|
if p == t {
|
|
return true
|
|
}
|
|
}
|
|
// Couldn't find t in the list of allowed parameters.
|
|
return false
|
|
|
|
case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN:
|
|
return parameterizedBy1(t.Elem(), params, visited)
|
|
|
|
case types.TMAP:
|
|
return parameterizedBy1(t.Key(), params, visited) && parameterizedBy1(t.Elem(), params, visited)
|
|
|
|
case types.TFUNC:
|
|
return parameterizedBy1(t.TParams(), params, visited) && parameterizedBy1(t.Recvs(), params, visited) && parameterizedBy1(t.Params(), params, visited) && parameterizedBy1(t.Results(), params, visited)
|
|
|
|
case types.TSTRUCT:
|
|
for _, f := range t.Fields().Slice() {
|
|
if !parameterizedBy1(f.Type, params, visited) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
|
|
case types.TINTER:
|
|
for _, f := range t.Methods().Slice() {
|
|
if !parameterizedBy1(f.Type, params, visited) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
|
|
case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64,
|
|
types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64,
|
|
types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128:
|
|
return true
|
|
|
|
default:
|
|
base.Fatalf("bad type kind %+v", t)
|
|
return true
|
|
}
|
|
}
|