Newer
Older
kv := ir.NewKeyExpr(r.pos(), r.expr(), nil)
*elemp, elemp = kv, &kv.Value
}
*elemp = wrapName(r.pos(), r.expr())
}
lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, typ, elems))
if rtype != nil {
lit := lit.(*ir.CompLitExpr)
lit.RType = rtype
if typ0.IsPtr() {
lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit))
lit.SetType(typ0)
}
return lit
}
func wrapName(pos src.XPos, x ir.Node) ir.Node {
// These nodes do not carry line numbers.
// Introduce a wrapper node to give them the correct line.
switch ir.Orig(x).Op() {
case ir.OTYPE, ir.OLITERAL:
if x.Sym() == nil {
break
}
fallthrough
case ir.ONAME, ir.ONONAME, ir.ONIL:
p := ir.NewParenExpr(pos, x)
p.SetImplicit(true)
return p
}
return x
}
func (r *reader) funcLit() ir.Node {
r.Sync(pkgbits.SyncFuncLit)
// The underlying function declaration (including its parameters'
// positions, if any) need to remain the original, uninlined
// positions. This is because we track inlining-context on nodes so
// we can synthesize the extra implied stack frames dynamically when
// generating tracebacks, whereas those stack frames don't make
// sense *within* the function literal. (Any necessary inlining
// adjustments will have been applied to the call expression
// instead.)
//
// This is subtle, and getting it wrong leads to cycles in the
// inlining tree, which lead to infinite loops during stack
// unwinding (#46234, #54625).
//
// Note that we *do* want the inline-adjusted position for the
// OCLOSURE node, because that position represents where any heap
// allocation of the closure is credited (#49171).
r.suppressInlPos++
pos := r.pos()
xtype2 := r.signature(types.LocalPkg, nil)
r.suppressInlPos--
fn := ir.NewClosureFunc(pos, r.curfn != nil)
clo := fn.OClosure
clo.SetPos(r.inlPos(pos)) // see comment above
ir.NameClosure(clo, r.curfn)
setType(fn.Nname, xtype2)
typecheck.Func(fn)
setType(clo, fn.Type())
fn.ClosureVars = make([]*ir.Name, 0, r.Len())
for len(fn.ClosureVars) < cap(fn.ClosureVars) {
ir.NewClosureVar(r.pos(), fn, r.useLocal())
if param := r.dictParam; param != nil {
// If we have a dictionary parameter, capture it too. For
// simplicity, we capture it last and unconditionally.
ir.NewClosureVar(param.Pos(), fn, param)
}
r.addBody(fn, nil)
// TODO(mdempsky): Remove hard-coding of typecheck.Target.
return ir.UseClosure(clo, typecheck.Target)
}
func (r *reader) exprList() []ir.Node {
r.Sync(pkgbits.SyncExprList)
return r.exprs()
}
func (r *reader) exprs() []ir.Node {
r.Sync(pkgbits.SyncExprs)
nodes := make([]ir.Node, r.Len())
if len(nodes) == 0 {
return nil // TODO(mdempsky): Unclear if this matters.
}
for i := range nodes {
nodes[i] = r.expr()
}
return nodes
}
// dictWord returns an expression to return the specified
// uintptr-typed word from the dictionary parameter.
func (r *reader) dictWord(pos src.XPos, idx int) ir.Node {
base.AssertfAt(r.dictParam != nil, pos, "expected dictParam in %v", r.curfn)
return typecheck.Expr(ir.NewIndexExpr(pos, r.dictParam, ir.NewBasicLit(pos, constant.MakeInt64(int64(idx)))))
}
// rttiWord is like dictWord, but converts it to *byte (the type used
// internally to represent *runtime._type and *runtime.itab).
func (r *reader) rttiWord(pos src.XPos, idx int) ir.Node {
return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, types.NewPtr(types.Types[types.TUINT8]), r.dictWord(pos, idx)))
}
// rtype reads a type reference from the element bitstream, and
// returns an expression of type *runtime._type representing that
// type.
func (r *reader) rtype(pos src.XPos) ir.Node {
_, rtype := r.rtype0(pos)
return rtype
func (r *reader) rtype0(pos src.XPos) (typ *types.Type, rtype ir.Node) {
r.Sync(pkgbits.SyncRType)
if r.Bool() { // derived type
idx := r.Len()
info := r.dict.rtypes[idx]
typ = r.p.typIdx(info, r.dict, true)
rtype = r.rttiWord(pos, r.dict.rtypesOffset()+idx)
return
typ = r.typ()
rtype = reflectdata.TypePtrAt(pos, typ)
return
}
// varDictIndex populates name.DictIndex if name is a derived type.
func (r *reader) varDictIndex(name *ir.Name) {
if r.Bool() {
idx := 1 + r.dict.rtypesOffset() + r.Len()
if int(uint16(idx)) != idx {
base.FatalfAt(name.Pos(), "DictIndex overflow for %v: %v", name, idx)
}
name.DictIndex = uint16(idx)
}
}
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
func (r *reader) itab(pos src.XPos) (typ *types.Type, typRType ir.Node, iface *types.Type, ifaceRType ir.Node, itab ir.Node) {
if r.Bool() { // derived types
idx := r.Len()
info := r.dict.itabs[idx]
typ = r.p.typIdx(info.typ, r.dict, true)
typRType = r.rttiWord(pos, r.dict.itabsOffset()+3*idx)
iface = r.p.typIdx(info.iface, r.dict, true)
ifaceRType = r.rttiWord(pos, r.dict.itabsOffset()+3*idx+1)
itab = r.rttiWord(pos, r.dict.itabsOffset()+3*idx+2)
return
}
typ = r.typ()
iface = r.typ()
if iface.IsInterface() {
typRType = reflectdata.TypePtrAt(pos, typ)
ifaceRType = reflectdata.TypePtrAt(pos, iface)
if !typ.IsInterface() && !iface.IsEmptyInterface() {
lsym := reflectdata.ITabLsym(typ, iface)
itab = typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8])
}
}
return
}
// convRTTI returns expressions appropriate for populating an
// ir.ConvExpr's TypeWord and SrcRType fields, respectively.
func (r *reader) convRTTI(pos src.XPos) (typeWord, srcRType ir.Node) {
r.Sync(pkgbits.SyncConvRTTI)
src, srcRType0, dst, dstRType, itab := r.itab(pos)
if !dst.IsInterface() {
return
}
// See reflectdata.ConvIfaceTypeWord.
switch {
case dst.IsEmptyInterface():
if !src.IsInterface() {
typeWord = srcRType0 // direct eface construction
}
case !src.IsInterface():
typeWord = itab // direct iface construction
default:
typeWord = dstRType // convI2I
}
// See reflectdata.ConvIfaceSrcRType.
if !src.IsInterface() {
srcRType = srcRType0
}
return
}
func (r *reader) exprType() ir.Node {
r.Sync(pkgbits.SyncExprType)
pos := r.pos()
var typ *types.Type
var rtype, itab ir.Node
typ, rtype, _, _, itab = r.itab(pos)
if typ.IsInterface() {
itab = nil
} else {
rtype = nil // TODO(mdempsky): Leave set?
typ, rtype = r.rtype0(pos)
if !r.Bool() { // not derived
// TODO(mdempsky): ir.TypeNode should probably return a typecheck'd node.
n := ir.TypeNode(typ)
n.SetTypecheck(1)
return n
}
}
dt := ir.NewDynamicType(pos, rtype)
dt.ITab = itab
return typed(typ, dt)
}
func (r *reader) op() ir.Op {
r.Sync(pkgbits.SyncOp)
return ir.Op(r.Len())
}
// @@@ Package initialization
func (r *reader) pkgInit(self *types.Pkg, target *ir.Package) {
cgoPragmas := make([][]string, r.Len())
for i := range cgoPragmas {
cgoPragmas[i] = r.Strings()
}
target.CgoPragmas = cgoPragmas
r.pkgDecls(target)
r.Sync(pkgbits.SyncEOF)
}
func (r *reader) pkgDecls(target *ir.Package) {
r.Sync(pkgbits.SyncDecls)
switch code := codeDecl(r.Code(pkgbits.SyncDecl)); code {
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
default:
panic(fmt.Sprintf("unhandled decl: %v", code))
case declEnd:
return
case declFunc:
names := r.pkgObjs(target)
assert(len(names) == 1)
target.Decls = append(target.Decls, names[0].Func)
case declMethod:
typ := r.typ()
_, sym := r.selector()
method := typecheck.Lookdot1(nil, sym, typ, typ.Methods(), 0)
target.Decls = append(target.Decls, method.Nname.(*ir.Name).Func)
case declVar:
pos := r.pos()
names := r.pkgObjs(target)
values := r.exprList()
if len(names) > 1 && len(values) == 1 {
as := ir.NewAssignListStmt(pos, ir.OAS2, nil, values)
for _, name := range names {
as.Lhs.Append(name)
name.Defn = as
}
target.Decls = append(target.Decls, as)
} else {
for i, name := range names {
as := ir.NewAssignStmt(pos, name, nil)
if i < len(values) {
as.Y = values[i]
}
name.Defn = as
target.Decls = append(target.Decls, as)
}
}
if n := r.Len(); n > 0 {
assert(len(names) == 1)
embeds := make([]ir.Embed, n)
for i := range embeds {
embeds[i] = ir.Embed{Pos: r.pos(), Patterns: r.Strings()}
}
names[0].Embed = &embeds
target.Embeds = append(target.Embeds, names[0])
}
case declOther:
r.pkgObjs(target)
}
}
}
func (r *reader) pkgObjs(target *ir.Package) []*ir.Name {
r.Sync(pkgbits.SyncDeclNames)
nodes := make([]*ir.Name, r.Len())
for i := range nodes {
r.Sync(pkgbits.SyncDeclName)
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
name := r.obj().(*ir.Name)
nodes[i] = name
sym := name.Sym()
if sym.IsBlank() {
continue
}
switch name.Class {
default:
base.FatalfAt(name.Pos(), "unexpected class: %v", name.Class)
case ir.PEXTERN:
target.Externs = append(target.Externs, name)
case ir.PFUNC:
assert(name.Type().Recv() == nil)
// TODO(mdempsky): Cleaner way to recognize init?
if strings.HasPrefix(sym.Name, "init.") {
target.Inits = append(target.Inits, name.Func)
}
}
if types.IsExported(sym.Name) {
assert(!sym.OnExportList())
target.Exports = append(target.Exports, name)
sym.SetOnExportList(true)
}
if base.Flag.AsmHdr != "" {
assert(!sym.Asm())
target.Asms = append(target.Asms, name)
sym.SetAsm(true)
}
}
return nodes
}
// @@@ Inlining
var inlgen = 0
// InlineCall implements inline.NewInline by re-reading the function
// body from its Unified IR export data.
func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
// TODO(mdempsky): Turn callerfn into an explicit parameter.
callerfn := ir.CurFunc
pri, ok := bodyReaderFor(fn)
// TODO(mdempsky): Reconsider this diagnostic's wording, if it's
// to be included in Go 1.20.
if base.Flag.LowerM != 0 {
base.WarnfAt(call.Pos(), "cannot inline call to %v: missing inline body", fn)
}
return nil
}
if fn.Inl.Body == nil {
expandInline(fn, pri)
}
r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody)
// TODO(mdempsky): This still feels clumsy. Can we do better?
tmpfn := ir.NewFunc(fn.Pos())
tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), callerfn.Sym())
tmpfn.Closgen = callerfn.Closgen
defer func() { callerfn.Closgen = tmpfn.Closgen }()
setType(tmpfn.Nname, fn.Type())
r.curfn = tmpfn
r.inlCaller = callerfn
r.inlCall = call
r.inlFunc = fn
r.inlTreeIndex = inlIndex
r.inlPosBases = make(map[*src.PosBase]*src.PosBase)
r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars))
for i, cv := range r.inlFunc.ClosureVars {
r.closureVars[i] = cv.Outer
if len(r.closureVars) != 0 && r.hasTypeParams() {
r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit
}
r.funcargs(fn)
r.delayResults = fn.Inl.CanDelayResults
r.retlabel = typecheck.AutoLabel(".i")
inlgen++
init := ir.TakeInit(call)
// For normal function calls, the function callee expression
// may contain side effects. Make sure to preserve these,
// if necessary (#42703).
if call.Op() == ir.OCALLFUNC {
inline.CalleeEffects(&init, call.X)
}
var args ir.Nodes
if call.Op() == ir.OCALLMETH {
base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
}
args.Append(call.Args...)
// Create assignment to declare and initialize inlvars.
as2 := ir.NewAssignListStmt(call.Pos(), ir.OAS2, r.inlvars, args)
as2.Def = true
var as2init ir.Nodes
for _, name := range r.inlvars {
if ir.IsBlank(name) {
continue
}
// TODO(mdempsky): Use inlined position of name.Pos() instead?
name := name.(*ir.Name)
as2init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
name.Defn = as2
}
as2.SetInit(as2init)
init.Append(typecheck.Stmt(as2))
if !r.delayResults {
// If not delaying retvars, declare and zero initialize the
// result variables now.
for _, name := range r.retvars {
// TODO(mdempsky): Use inlined position of name.Pos() instead?
name := name.(*ir.Name)
init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
ras := ir.NewAssignStmt(call.Pos(), name, nil)
init.Append(typecheck.Stmt(ras))
}
}
// Add an inline mark just before the inlined body.
// This mark is inline in the code so that it's a reasonable spot
// to put a breakpoint. Not sure if that's really necessary or not
// (in which case it could go at the end of the function instead).
// Note issue 28603.
init.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(r.inlTreeIndex)))
nparams := len(r.curfn.Dcl)
ir.WithFunc(r.curfn, func() {
if !r.syntheticBody(call.Pos()) {
assert(r.Bool()) // have body
r.curfn.Body = r.stmts()
r.curfn.Endlineno = r.pos()
}
// TODO(mdempsky): This shouldn't be necessary. Inlining might
// read in new function/method declarations, which could
// potentially be recursively inlined themselves; but we shouldn't
// need to read in the non-inlined bodies for the declarations
// themselves. But currently it's an easy fix to #50552.
readBodies(typecheck.Target)
deadcode.Func(r.curfn)
// Replace any "return" statements within the function body.
var edit func(ir.Node) ir.Node
edit = func(n ir.Node) ir.Node {
if ret, ok := n.(*ir.ReturnStmt); ok {
n = typecheck.Stmt(r.inlReturn(ret))
}
ir.EditChildren(n, edit)
return n
}
edit(r.curfn)
})
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
body := ir.Nodes(r.curfn.Body)
// Quirkish: We need to eagerly prune variables added during
// inlining, but removed by deadcode.FuncBody above. Unused
// variables will get removed during stack frame layout anyway, but
// len(fn.Dcl) ends up influencing things like autotmp naming.
used := usedLocals(body)
for i, name := range r.curfn.Dcl {
if i < nparams || used.Has(name) {
name.Curfn = callerfn
callerfn.Dcl = append(callerfn.Dcl, name)
// Quirkish. TODO(mdempsky): Document why.
if name.AutoTemp() {
name.SetEsc(ir.EscUnknown)
if base.Flag.GenDwarfInl != 0 {
name.SetInlLocal(true)
} else {
name.SetPos(r.inlCall.Pos())
}
}
}
}
body.Append(ir.NewLabelStmt(call.Pos(), r.retlabel))
res := ir.NewInlinedCallExpr(call.Pos(), body, append([]ir.Node(nil), r.retvars...))
res.SetInit(init)
res.SetType(call.Type())
res.SetTypecheck(1)
// Inlining shouldn't add any functions to todoBodies.
assert(len(todoBodies) == 0)
return res
}
// inlReturn returns a statement that can substitute for the given
// return statement when inlining.
func (r *reader) inlReturn(ret *ir.ReturnStmt) *ir.BlockStmt {
pos := r.inlCall.Pos()
block := ir.TakeInit(ret)
if results := ret.Results; len(results) != 0 {
assert(len(r.retvars) == len(results))
as2 := ir.NewAssignListStmt(pos, ir.OAS2, append([]ir.Node(nil), r.retvars...), ret.Results)
if r.delayResults {
for _, name := range r.retvars {
// TODO(mdempsky): Use inlined position of name.Pos() instead?
name := name.(*ir.Name)
block.Append(ir.NewDecl(pos, ir.ODCL, name))
name.Defn = as2
}
}
block.Append(as2)
}
block.Append(ir.NewBranchStmt(pos, ir.OGOTO, r.retlabel))
return ir.NewBlockStmt(pos, block)
}
// expandInline reads in an extra copy of IR to populate
// fn.Inl.{Dcl,Body}.
func expandInline(fn *ir.Func, pri pkgReaderIndex) {
// TODO(mdempsky): Remove this function. It's currently needed by
// dwarfgen/dwarf.go:preInliningDcls, which requires fn.Inl.Dcl to
// create abstract function DIEs. But we should be able to provide it
// with the same information some other way.
fndcls := len(fn.Dcl)
topdcls := len(typecheck.Target.Decls)
tmpfn := ir.NewFunc(fn.Pos())
tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), fn.Sym())
tmpfn.ClosureVars = fn.ClosureVars
{
r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody)
setType(tmpfn.Nname, fn.Type())
// Don't change parameter's Sym/Nname fields.
r.funarghack = true
r.funcBody(tmpfn)
ir.WithFunc(tmpfn, func() {
deadcode.Func(tmpfn)
})
}
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
used := usedLocals(tmpfn.Body)
for _, name := range tmpfn.Dcl {
if name.Class != ir.PAUTO || used.Has(name) {
name.Curfn = fn
fn.Inl.Dcl = append(fn.Inl.Dcl, name)
}
}
fn.Inl.Body = tmpfn.Body
// Double check that we didn't change fn.Dcl by accident.
assert(fndcls == len(fn.Dcl))
// typecheck.Stmts may have added function literals to
// typecheck.Target.Decls. Remove them again so we don't risk trying
// to compile them multiple times.
typecheck.Target.Decls = typecheck.Target.Decls[:topdcls]
}
// usedLocals returns a set of local variables that are used within body.
func usedLocals(body []ir.Node) ir.NameSet {
var used ir.NameSet
ir.VisitList(body, func(n ir.Node) {
if n, ok := n.(*ir.Name); ok && n.Op() == ir.ONAME && n.Class == ir.PAUTO {
used.Add(n)
}
})
return used
}
// @@@ Method wrappers
// needWrapperTypes lists types for which we may need to generate
// method wrappers.
var needWrapperTypes []*types.Type
Matthew Dempsky
committed
// haveWrapperTypes lists types for which we know we already have
// method wrappers, because we found the type in an imported package.
var haveWrapperTypes []*types.Type
// needMethodValueWrappers lists methods for which we may need to
// generate method value wrappers.
var needMethodValueWrappers []methodValueWrapper
// haveMethodValueWrappers lists methods for which we know we already
// have method value wrappers, because we found it in an imported
// package.
var haveMethodValueWrappers []methodValueWrapper
type methodValueWrapper struct {
rcvr *types.Type
method *types.Field
}
func (r *reader) needWrapper(typ *types.Type) {
if typ.IsPtr() {
return
}
// If a type was found in an imported package, then we can assume
// that package (or one of its transitive dependencies) already
// generated method wrappers for it.
if r.importedDef() {
haveWrapperTypes = append(haveWrapperTypes, typ)
} else {
needWrapperTypes = append(needWrapperTypes, typ)
// importedDef reports whether r is reading from an imported and
// non-generic element.
//
// If a type was found in an imported package, then we can assume that
// package (or one of its transitive dependencies) already generated
// method wrappers for it.
//
// Exception: If we're instantiating an imported generic type or
// function, we might be instantiating it with type arguments not
// previously seen before.
//
// TODO(mdempsky): Distinguish when a generic function or type was
// instantiated in an imported package so that we can add types to
// haveWrapperTypes instead.
func (r *reader) importedDef() bool {
return r.p != localPkgReader && !r.hasTypeParams()
}
func MakeWrappers(target *ir.Package) {
// Only unified IR emits its own wrappers.
if base.Debug.Unified == 0 {
return
}
// always generate a wrapper for error.Error (#29304)
needWrapperTypes = append(needWrapperTypes, types.ErrorType)
seen := make(map[string]*types.Type)
Matthew Dempsky
committed
for _, typ := range haveWrapperTypes {
wrapType(typ, target, seen, false)
Matthew Dempsky
committed
}
haveWrapperTypes = nil
for _, typ := range needWrapperTypes {
wrapType(typ, target, seen, true)
Matthew Dempsky
committed
}
needWrapperTypes = nil
for _, wrapper := range haveMethodValueWrappers {
wrapMethodValue(wrapper.rcvr, wrapper.method, target, false)
}
haveMethodValueWrappers = nil
for _, wrapper := range needMethodValueWrappers {
wrapMethodValue(wrapper.rcvr, wrapper.method, target, true)
}
needMethodValueWrappers = nil
}
func wrapType(typ *types.Type, target *ir.Package, seen map[string]*types.Type, needed bool) {
key := typ.LinkString()
if prev := seen[key]; prev != nil {
if !types.Identical(typ, prev) {
base.Fatalf("collision: types %v and %v have link string %q", typ, prev, key)
}
return
}
seen[key] = typ
if !needed {
// Only called to add to 'seen'.
return
}
if !typ.IsInterface() {
typecheck.CalcMethods(typ)
}
for _, meth := range typ.AllMethods().Slice() {
if meth.Sym.IsBlank() || !meth.IsMethod() {
base.FatalfAt(meth.Pos, "invalid method: %v", meth)
}
methodWrapper(0, typ, meth, target)
// For non-interface types, we also want *T wrappers.
if !typ.IsInterface() {
methodWrapper(1, typ, meth, target)
// For not-in-heap types, *T is a scalar, not pointer shaped,
// so the interface wrappers use **T.
if typ.NotInHeap() {
methodWrapper(2, typ, meth, target)
}
}
}
}
func methodWrapper(derefs int, tbase *types.Type, method *types.Field, target *ir.Package) {
wrapper := tbase
for i := 0; i < derefs; i++ {
wrapper = types.NewPtr(wrapper)
}
sym := ir.MethodSym(wrapper, method.Sym)
base.Assertf(!sym.Siggen(), "already generated wrapper %v", sym)
sym.SetSiggen(true)
wrappee := method.Type.Recv().Type
if types.Identical(wrapper, wrappee) ||
!types.IsMethodApplicable(wrapper, method) ||
!reflectdata.NeedEmit(tbase) {
return
}
// TODO(mdempsky): Use method.Pos instead?
pos := base.AutogeneratedPos
fn := newWrapperFunc(pos, sym, wrapper, method)
var recv ir.Node = fn.Nname.Type().Recv().Nname.(*ir.Name)
// For simple *T wrappers around T methods, panicwrap produces a
// nicer panic message.
if wrapper.IsPtr() && types.Identical(wrapper.Elem(), wrappee) {
cond := ir.NewBinaryExpr(pos, ir.OEQ, recv, types.BuiltinPkg.Lookup("nil").Def.(ir.Node))
then := []ir.Node{ir.NewCallExpr(pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)}
fn.Body.Append(ir.NewIfStmt(pos, cond, then, nil))
}
// typecheck will add one implicit deref, if necessary,
// but not-in-heap types require more for their **T wrappers.
for i := 1; i < derefs; i++ {
recv = Implicit(ir.NewStarExpr(pos, recv))
}
addTailCall(pos, fn, recv, method)
Matthew Dempsky
committed
finishWrapperFunc(fn, target)
}
func wrapMethodValue(recvType *types.Type, method *types.Field, target *ir.Package, needed bool) {
sym := ir.MethodSymSuffix(recvType, method.Sym, "-fm")
if sym.Uniq() {
return
}
sym.SetUniq(true)
// TODO(mdempsky): Use method.Pos instead?
pos := base.AutogeneratedPos
fn := newWrapperFunc(pos, sym, nil, method)
Matthew Dempsky
committed
sym.Def = fn.Nname
// Declare and initialize variable holding receiver.
recv := ir.NewHiddenParam(pos, fn, typecheck.Lookup(".this"), recvType)
if !needed {
Matthew Dempsky
committed
typecheck.Func(fn)
return
}
addTailCall(pos, fn, recv, method)
Matthew Dempsky
committed
finishWrapperFunc(fn, target)
}
func newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field) *ir.Func {
fn := ir.NewFunc(pos)
fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers?
name := ir.NewNameAt(pos, sym)
ir.MarkFunc(name)
name.Func = fn
name.Defn = fn
fn.Nname = name
sig := newWrapperType(wrapper, method)
setType(name, sig)
// TODO(mdempsky): De-duplicate with similar logic in funcargs.
defParams := func(class ir.Class, params *types.Type) {
for _, param := range params.FieldSlice() {
name := ir.NewNameAt(param.Pos, param.Sym)
name.Class = class
setType(name, param.Type)
name.Curfn = fn
fn.Dcl = append(fn.Dcl, name)
param.Nname = name
}
}
defParams(ir.PPARAM, sig.Recvs())
defParams(ir.PPARAM, sig.Params())
defParams(ir.PPARAMOUT, sig.Results())
return fn
}
func finishWrapperFunc(fn *ir.Func, target *ir.Package) {
Matthew Dempsky
committed
typecheck.Func(fn)
ir.WithFunc(fn, func() {
typecheck.Stmts(fn.Body)
})
// We generate wrappers after the global inlining pass,
// so we're responsible for applying inlining ourselves here.
inline.InlineCalls(fn)
// The body of wrapper function after inlining may reveal new ir.OMETHVALUE node,
// we don't know whether wrapper function has been generated for it or not, so
// generate one immediately here.
ir.VisitList(fn.Body, func(n ir.Node) {
if n, ok := n.(*ir.SelectorExpr); ok && n.Op() == ir.OMETHVALUE {
wrapMethodValue(n.X.Type(), n.Selection, target, true)
}
})
Matthew Dempsky
committed
target.Decls = append(target.Decls, fn)
}
// newWrapperType returns a copy of the given signature type, but with
// the receiver parameter type substituted with recvType.
// If recvType is nil, newWrapperType returns a signature
// without a receiver parameter.
func newWrapperType(recvType *types.Type, method *types.Field) *types.Type {
clone := func(params []*types.Field) []*types.Field {
res := make([]*types.Field, len(params))
for i, param := range params {
sym := param.Sym
if sym == nil || sym.Name == "_" {
sym = typecheck.LookupNum(".anon", i)
}
res[i] = types.NewField(param.Pos, sym, param.Type)
res[i].SetIsDDD(param.IsDDD())
}
return res
}
sig := method.Type
var recv *types.Field
if recvType != nil {
recv = types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), recvType)
}
params := clone(sig.Params().FieldSlice())
results := clone(sig.Results().FieldSlice())
return types.NewSignature(types.NoPkg, recv, nil, params, results)
}
func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
sig := fn.Nname.Type()
args := make([]ir.Node, sig.NumParams())
for i, param := range sig.Params().FieldSlice() {
args[i] = param.Nname.(*ir.Name)
}
// TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper.
// Not urgent though, because tail calls are currently incompatible with regabi anyway.
fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls?
dot := ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym)
call := typecheck.Call(pos, dot, args, method.Type.IsVariadic()).(*ir.CallExpr)
if method.Type.NumResults() == 0 {
fn.Body.Append(call)
return
}
ret := ir.NewReturnStmt(pos, nil)
ret.Results = []ir.Node{call}
fn.Body.Append(ret)
Cuong Manh Le
committed
func setBasePos(pos src.XPos) {
// Set the position for any error messages we might print (e.g. too large types).
base.Pos = pos
}
// dictParamName is the name of the synthetic dictionary parameter
// added to shaped functions.
//
// N.B., this variable name is known to Delve:
// https://github.com/go-delve/delve/blob/cb91509630529e6055be845688fd21eb89ae8714/pkg/proc/eval.go#L28
const dictParamName = ".dict"
// shapeSig returns a copy of fn's signature, except adding a
// dictionary parameter and promoting the receiver parameter (if any)
// to a normal parameter.
//
// The parameter types.Fields are all copied too, so their Nname
// fields can be initialized for use by the shape function.
func shapeSig(fn *ir.Func, dict *readerDict) *types.Type {
sig := fn.Nname.Type()
oldRecv := sig.Recv()
var recv *types.Field
if oldRecv != nil {
recv = types.NewField(oldRecv.Pos, oldRecv.Sym, oldRecv.Type)
params := make([]*types.Field, 1+sig.Params().Fields().Len())
params[0] = types.NewField(fn.Pos(), fn.Sym().Pkg.Lookup(dictParamName), types.NewPtr(dict.varType()))
for i, param := range sig.Params().Fields().Slice() {
d := types.NewField(param.Pos, param.Sym, param.Type)
d.SetIsDDD(param.IsDDD())
params[1+i] = d
}
results := make([]*types.Field, sig.Results().Fields().Len())
for i, result := range sig.Results().Fields().Slice() {
results[i] = types.NewField(result.Pos, result.Sym, result.Type)
}
return types.NewSignature(types.LocalPkg, recv, nil, params, results)