mirror of
https://github.com/golang/go.git
synced 2025-05-23 00:11:26 +00:00
Allows removing a bunch of unnecessary fields. Passes toolstash/buildall. Change-Id: Iec2492920e1c3ef352a9bf4296c74a55d9cc9ad6 Reviewed-on: https://go-review.googlesource.com/20677 Reviewed-by: Robert Griesemer <gri@golang.org> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
670 lines
17 KiB
Go
670 lines
17 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gc
|
|
|
|
import (
|
|
"cmd/internal/obj"
|
|
"fmt"
|
|
)
|
|
|
|
// function literals aka closures
|
|
func closurehdr(ntype *Node) {
|
|
var name *Node
|
|
var a *Node
|
|
|
|
n := Nod(OCLOSURE, nil, nil)
|
|
n.Func.Ntype = ntype
|
|
n.Func.Depth = Funcdepth
|
|
n.Func.Outerfunc = Curfn
|
|
|
|
funchdr(n)
|
|
|
|
// steal ntype's argument names and
|
|
// leave a fresh copy in their place.
|
|
// references to these variables need to
|
|
// refer to the variables in the external
|
|
// function declared below; see walkclosure.
|
|
n.List.Set(ntype.List.Slice())
|
|
|
|
n.Rlist.Set(ntype.Rlist.Slice())
|
|
ntype.List.Set(nil)
|
|
ntype.Rlist.Set(nil)
|
|
for _, n1 := range n.List.Slice() {
|
|
name = n1.Left
|
|
if name != nil {
|
|
name = newname(name.Sym)
|
|
}
|
|
a = Nod(ODCLFIELD, name, n1.Right)
|
|
a.Isddd = n1.Isddd
|
|
if name != nil {
|
|
name.Isddd = a.Isddd
|
|
}
|
|
ntype.List.Append(a)
|
|
}
|
|
for _, n2 := range n.Rlist.Slice() {
|
|
name = n2.Left
|
|
if name != nil {
|
|
name = newname(name.Sym)
|
|
}
|
|
ntype.Rlist.Append(Nod(ODCLFIELD, name, n2.Right))
|
|
}
|
|
}
|
|
|
|
func closurebody(body []*Node) *Node {
|
|
if len(body) == 0 {
|
|
body = []*Node{Nod(OEMPTY, nil, nil)}
|
|
}
|
|
|
|
func_ := Curfn
|
|
func_.Nbody.Set(body)
|
|
func_.Func.Endlineno = lineno
|
|
funcbody(func_)
|
|
|
|
// closure-specific variables are hanging off the
|
|
// ordinary ones in the symbol table; see oldname.
|
|
// unhook them.
|
|
// make the list of pointers for the closure call.
|
|
for _, v := range func_.Func.Cvars.Slice() {
|
|
v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer
|
|
v.Name.Param.Outerexpr = oldname(v.Sym)
|
|
}
|
|
|
|
return func_
|
|
}
|
|
|
|
func typecheckclosure(func_ *Node, top int) {
|
|
for _, ln := range func_.Func.Cvars.Slice() {
|
|
n := ln.Name.Param.Closure
|
|
if !n.Name.Captured {
|
|
n.Name.Captured = true
|
|
if n.Name.Decldepth == 0 {
|
|
Fatalf("typecheckclosure: var %v does not have decldepth assigned", Nconv(n, obj.FmtShort))
|
|
}
|
|
|
|
// Ignore assignments to the variable in straightline code
|
|
// preceding the first capturing by a closure.
|
|
if n.Name.Decldepth == decldepth {
|
|
n.Assigned = false
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, ln := range func_.Func.Dcl {
|
|
if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) {
|
|
ln.Name.Decldepth = 1
|
|
}
|
|
}
|
|
|
|
oldfn := Curfn
|
|
typecheck(&func_.Func.Ntype, Etype)
|
|
func_.Type = func_.Func.Ntype.Type
|
|
func_.Func.Top = top
|
|
|
|
// Type check the body now, but only if we're inside a function.
|
|
// At top level (in a variable initialization: curfn==nil) we're not
|
|
// ready to type check code yet; we'll check it later, because the
|
|
// underlying closure function we create is added to xtop.
|
|
if Curfn != nil && func_.Type != nil {
|
|
Curfn = func_
|
|
olddd := decldepth
|
|
decldepth = 1
|
|
typechecklist(func_.Nbody.Slice(), Etop)
|
|
decldepth = olddd
|
|
Curfn = oldfn
|
|
}
|
|
|
|
// Create top-level function
|
|
xtop = append(xtop, makeclosure(func_))
|
|
}
|
|
|
|
// closurename returns name for OCLOSURE n.
|
|
// It is not as simple as it ought to be, because we typecheck nested closures
|
|
// starting from the innermost one. So when we check the inner closure,
|
|
// we don't yet have name for the outer closure. This function uses recursion
|
|
// to generate names all the way up if necessary.
|
|
|
|
var closurename_closgen int
|
|
|
|
func closurename(n *Node) *Sym {
|
|
if n.Sym != nil {
|
|
return n.Sym
|
|
}
|
|
gen := 0
|
|
outer := ""
|
|
prefix := ""
|
|
if n.Func.Outerfunc == nil {
|
|
// Global closure.
|
|
outer = "glob"
|
|
|
|
prefix = "func"
|
|
closurename_closgen++
|
|
gen = closurename_closgen
|
|
} else if n.Func.Outerfunc.Op == ODCLFUNC {
|
|
// The outermost closure inside of a named function.
|
|
outer = n.Func.Outerfunc.Func.Nname.Sym.Name
|
|
|
|
prefix = "func"
|
|
|
|
// Yes, functions can be named _.
|
|
// Can't use function closgen in such case,
|
|
// because it would lead to name clashes.
|
|
if !isblank(n.Func.Outerfunc.Func.Nname) {
|
|
n.Func.Outerfunc.Func.Closgen++
|
|
gen = n.Func.Outerfunc.Func.Closgen
|
|
} else {
|
|
closurename_closgen++
|
|
gen = closurename_closgen
|
|
}
|
|
} else if n.Func.Outerfunc.Op == OCLOSURE {
|
|
// Nested closure, recurse.
|
|
outer = closurename(n.Func.Outerfunc).Name
|
|
|
|
prefix = ""
|
|
n.Func.Outerfunc.Func.Closgen++
|
|
gen = n.Func.Outerfunc.Func.Closgen
|
|
} else {
|
|
Fatalf("closurename called for %v", Nconv(n, obj.FmtShort))
|
|
}
|
|
n.Sym = Lookupf("%s.%s%d", outer, prefix, gen)
|
|
return n.Sym
|
|
}
|
|
|
|
func makeclosure(func_ *Node) *Node {
|
|
// wrap body in external function
|
|
// that begins by reading closure parameters.
|
|
xtype := Nod(OTFUNC, nil, nil)
|
|
|
|
xtype.List.Set(func_.List.Slice())
|
|
xtype.Rlist.Set(func_.Rlist.Slice())
|
|
|
|
// create the function
|
|
xfunc := Nod(ODCLFUNC, nil, nil)
|
|
|
|
xfunc.Func.Nname = newfuncname(closurename(func_))
|
|
xfunc.Func.Nname.Sym.Flags |= SymExported // disable export
|
|
xfunc.Func.Nname.Name.Param.Ntype = xtype
|
|
xfunc.Func.Nname.Name.Defn = xfunc
|
|
declare(xfunc.Func.Nname, PFUNC)
|
|
xfunc.Func.Nname.Name.Funcdepth = func_.Func.Depth
|
|
xfunc.Func.Depth = func_.Func.Depth
|
|
xfunc.Func.Endlineno = func_.Func.Endlineno
|
|
makefuncsym(xfunc.Func.Nname.Sym)
|
|
|
|
xfunc.Nbody.Set(func_.Nbody.Slice())
|
|
xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...)
|
|
func_.Func.Dcl = nil
|
|
if len(xfunc.Nbody.Slice()) == 0 {
|
|
Fatalf("empty body - won't generate any code")
|
|
}
|
|
typecheck(&xfunc, Etop)
|
|
|
|
xfunc.Func.Closure = func_
|
|
func_.Func.Closure = xfunc
|
|
|
|
func_.Nbody.Set(nil)
|
|
func_.List.Set(nil)
|
|
func_.Rlist.Set(nil)
|
|
|
|
return xfunc
|
|
}
|
|
|
|
// capturevars is called in a separate phase after all typechecking is done.
|
|
// It decides whether each variable captured by a closure should be captured
|
|
// by value or by reference.
|
|
// We use value capturing for values <= 128 bytes that are never reassigned
|
|
// after capturing (effectively constant).
|
|
func capturevars(xfunc *Node) {
|
|
var outer *Node
|
|
|
|
lno := lineno
|
|
lineno = xfunc.Lineno
|
|
|
|
func_ := xfunc.Func.Closure
|
|
func_.Func.Enter.Set(nil)
|
|
for _, v := range func_.Func.Cvars.Slice() {
|
|
if v.Type == nil {
|
|
// if v->type is nil, it means v looked like it was
|
|
// going to be used in the closure but wasn't.
|
|
// this happens because when parsing a, b, c := f()
|
|
// the a, b, c gets parsed as references to older
|
|
// a, b, c before the parser figures out this is a
|
|
// declaration.
|
|
v.Op = OXXX
|
|
|
|
continue
|
|
}
|
|
|
|
// type check the & of closed variables outside the closure,
|
|
// so that the outer frame also grabs them and knows they escape.
|
|
dowidth(v.Type)
|
|
|
|
outer = v.Name.Param.Outerexpr
|
|
v.Name.Param.Outerexpr = nil
|
|
|
|
// out parameters will be assigned to implicitly upon return.
|
|
if outer.Class != PPARAMOUT && !v.Name.Param.Closure.Addrtaken && !v.Name.Param.Closure.Assigned && v.Type.Width <= 128 {
|
|
v.Name.Byval = true
|
|
} else {
|
|
v.Name.Param.Closure.Addrtaken = true
|
|
outer = Nod(OADDR, outer, nil)
|
|
}
|
|
|
|
if Debug['m'] > 1 {
|
|
var name *Sym
|
|
if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil {
|
|
name = v.Name.Curfn.Func.Nname.Sym
|
|
}
|
|
how := "ref"
|
|
if v.Name.Byval {
|
|
how = "value"
|
|
}
|
|
Warnl(v.Lineno, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, v.Name.Param.Closure.Addrtaken, v.Name.Param.Closure.Assigned, int32(v.Type.Width))
|
|
}
|
|
|
|
typecheck(&outer, Erv)
|
|
func_.Func.Enter.Append(outer)
|
|
}
|
|
|
|
lineno = lno
|
|
}
|
|
|
|
// transformclosure is called in a separate phase after escape analysis.
|
|
// It transform closure bodies to properly reference captured variables.
|
|
func transformclosure(xfunc *Node) {
|
|
lno := lineno
|
|
lineno = xfunc.Lineno
|
|
func_ := xfunc.Func.Closure
|
|
|
|
if func_.Func.Top&Ecall != 0 {
|
|
// If the closure is directly called, we transform it to a plain function call
|
|
// with variables passed as args. This avoids allocation of a closure object.
|
|
// Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE)
|
|
// will complete the transformation later.
|
|
// For illustration, the following closure:
|
|
// func(a int) {
|
|
// println(byval)
|
|
// byref++
|
|
// }(42)
|
|
// becomes:
|
|
// func(a int, byval int, &byref *int) {
|
|
// println(byval)
|
|
// (*&byref)++
|
|
// }(byval, &byref, 42)
|
|
|
|
// f is ONAME of the actual function.
|
|
f := xfunc.Func.Nname
|
|
|
|
// We are going to insert captured variables before input args.
|
|
var params []*Field
|
|
var decls []*Node
|
|
for _, v := range func_.Func.Cvars.Slice() {
|
|
if v.Op == OXXX {
|
|
continue
|
|
}
|
|
fld := newField()
|
|
fld.Funarg = true
|
|
if v.Name.Byval {
|
|
// If v is captured by value, we merely downgrade it to PPARAM.
|
|
v.Class = PPARAM
|
|
|
|
v.Ullman = 1
|
|
fld.Nname = v
|
|
} else {
|
|
// If v of type T is captured by reference,
|
|
// we introduce function param &v *T
|
|
// and v remains PPARAMREF with &v heapaddr
|
|
// (accesses will implicitly deref &v).
|
|
addr := newname(Lookupf("&%s", v.Sym.Name))
|
|
addr.Type = Ptrto(v.Type)
|
|
addr.Class = PPARAM
|
|
v.Name.Heapaddr = addr
|
|
fld.Nname = addr
|
|
}
|
|
|
|
fld.Type = fld.Nname.Type
|
|
fld.Sym = fld.Nname.Sym
|
|
|
|
params = append(params, fld)
|
|
decls = append(decls, fld.Nname)
|
|
}
|
|
|
|
if len(params) > 0 {
|
|
// Prepend params and decls.
|
|
f.Type.Params().SetFields(append(params, f.Type.Params().FieldSlice()...))
|
|
xfunc.Func.Dcl = append(decls, xfunc.Func.Dcl...)
|
|
}
|
|
|
|
// Recalculate param offsets.
|
|
if f.Type.Width > 0 {
|
|
Fatalf("transformclosure: width is already calculated")
|
|
}
|
|
dowidth(f.Type)
|
|
xfunc.Type = f.Type // update type of ODCLFUNC
|
|
} else {
|
|
// The closure is not called, so it is going to stay as closure.
|
|
var body []*Node
|
|
offset := int64(Widthptr)
|
|
var addr *Node
|
|
var cv *Node
|
|
for _, v := range func_.Func.Cvars.Slice() {
|
|
if v.Op == OXXX {
|
|
continue
|
|
}
|
|
|
|
// cv refers to the field inside of closure OSTRUCTLIT.
|
|
cv = Nod(OCLOSUREVAR, nil, nil)
|
|
|
|
cv.Type = v.Type
|
|
if !v.Name.Byval {
|
|
cv.Type = Ptrto(v.Type)
|
|
}
|
|
offset = Rnd(offset, int64(cv.Type.Align))
|
|
cv.Xoffset = offset
|
|
offset += cv.Type.Width
|
|
|
|
if v.Name.Byval && v.Type.Width <= int64(2*Widthptr) {
|
|
// If it is a small variable captured by value, downgrade it to PAUTO.
|
|
v.Class = PAUTO
|
|
v.Ullman = 1
|
|
xfunc.Func.Dcl = append(xfunc.Func.Dcl, v)
|
|
body = append(body, Nod(OAS, v, cv))
|
|
} else {
|
|
// Declare variable holding addresses taken from closure
|
|
// and initialize in entry prologue.
|
|
addr = newname(Lookupf("&%s", v.Sym.Name))
|
|
addr.Name.Param.Ntype = Nod(OIND, typenod(v.Type), nil)
|
|
addr.Class = PAUTO
|
|
addr.Used = true
|
|
addr.Name.Curfn = xfunc
|
|
xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr)
|
|
v.Name.Heapaddr = addr
|
|
if v.Name.Byval {
|
|
cv = Nod(OADDR, cv, nil)
|
|
}
|
|
body = append(body, Nod(OAS, addr, cv))
|
|
}
|
|
}
|
|
|
|
if len(body) > 0 {
|
|
typecheckslice(body, Etop)
|
|
walkstmtlist(body)
|
|
xfunc.Func.Enter.Set(body)
|
|
xfunc.Func.Needctxt = true
|
|
}
|
|
}
|
|
|
|
lineno = lno
|
|
}
|
|
|
|
func walkclosure(func_ *Node, init *Nodes) *Node {
|
|
// If no closure vars, don't bother wrapping.
|
|
if len(func_.Func.Cvars.Slice()) == 0 {
|
|
return func_.Func.Closure.Func.Nname
|
|
}
|
|
|
|
// Create closure in the form of a composite literal.
|
|
// supposing the closure captures an int i and a string s
|
|
// and has one float64 argument and no results,
|
|
// the generated code looks like:
|
|
//
|
|
// clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s}
|
|
//
|
|
// The use of the struct provides type information to the garbage
|
|
// collector so that it can walk the closure. We could use (in this case)
|
|
// [3]unsafe.Pointer instead, but that would leave the gc in the dark.
|
|
// The information appears in the binary in the form of type descriptors;
|
|
// the struct is unnamed so that closures in multiple packages with the
|
|
// same struct type can share the descriptor.
|
|
|
|
typ := Nod(OTSTRUCT, nil, nil)
|
|
|
|
typ.List.Set1(Nod(ODCLFIELD, newname(Lookup(".F")), typenod(Types[TUINTPTR])))
|
|
var typ1 *Node
|
|
for _, v := range func_.Func.Cvars.Slice() {
|
|
if v.Op == OXXX {
|
|
continue
|
|
}
|
|
typ1 = typenod(v.Type)
|
|
if !v.Name.Byval {
|
|
typ1 = Nod(OIND, typ1, nil)
|
|
}
|
|
typ.List.Append(Nod(ODCLFIELD, newname(v.Sym), typ1))
|
|
}
|
|
|
|
clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil))
|
|
clos.Esc = func_.Esc
|
|
clos.Right.Implicit = true
|
|
clos.List.Set(append([]*Node{Nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)}, func_.Func.Enter.Slice()...))
|
|
|
|
// Force type conversion from *struct to the func type.
|
|
clos = Nod(OCONVNOP, clos, nil)
|
|
|
|
clos.Type = func_.Type
|
|
|
|
typecheck(&clos, Erv)
|
|
|
|
// typecheck will insert a PTRLIT node under CONVNOP,
|
|
// tag it with escape analysis result.
|
|
clos.Left.Esc = func_.Esc
|
|
|
|
// non-escaping temp to use, if any.
|
|
// orderexpr did not compute the type; fill it in now.
|
|
if x := prealloc[func_]; x != nil {
|
|
x.Type = clos.Left.Left.Type
|
|
x.Orig.Type = x.Type
|
|
clos.Left.Right = x
|
|
delete(prealloc, func_)
|
|
}
|
|
|
|
walkexpr(&clos, init)
|
|
|
|
return clos
|
|
}
|
|
|
|
func typecheckpartialcall(fn *Node, sym *Node) {
|
|
switch fn.Op {
|
|
case ODOTINTER, ODOTMETH:
|
|
break
|
|
|
|
default:
|
|
Fatalf("invalid typecheckpartialcall")
|
|
}
|
|
|
|
// Create top-level function.
|
|
xfunc := makepartialcall(fn, fn.Type, sym)
|
|
fn.Func = xfunc.Func
|
|
fn.Right = sym
|
|
fn.Op = OCALLPART
|
|
fn.Type = xfunc.Type
|
|
}
|
|
|
|
var makepartialcall_gopkg *Pkg
|
|
|
|
func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node {
|
|
var p string
|
|
|
|
rcvrtype := fn.Left.Type
|
|
if exportname(meth.Sym.Name) {
|
|
p = fmt.Sprintf("(%v).%s-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), meth.Sym.Name)
|
|
} else {
|
|
p = fmt.Sprintf("(%v).(%v)-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), Sconv(meth.Sym, obj.FmtLeft))
|
|
}
|
|
basetype := rcvrtype
|
|
if Isptr[rcvrtype.Etype] {
|
|
basetype = basetype.Type
|
|
}
|
|
if basetype.Etype != TINTER && basetype.Sym == nil {
|
|
Fatalf("missing base type for %v", rcvrtype)
|
|
}
|
|
|
|
var spkg *Pkg
|
|
if basetype.Sym != nil {
|
|
spkg = basetype.Sym.Pkg
|
|
}
|
|
if spkg == nil {
|
|
if makepartialcall_gopkg == nil {
|
|
makepartialcall_gopkg = mkpkg("go")
|
|
}
|
|
spkg = makepartialcall_gopkg
|
|
}
|
|
|
|
sym := Pkglookup(p, spkg)
|
|
|
|
if sym.Flags&SymUniq != 0 {
|
|
return sym.Def
|
|
}
|
|
sym.Flags |= SymUniq
|
|
|
|
savecurfn := Curfn
|
|
Curfn = nil
|
|
|
|
xtype := Nod(OTFUNC, nil, nil)
|
|
i := 0
|
|
var l []*Node
|
|
var callargs []*Node
|
|
ddd := false
|
|
xfunc := Nod(ODCLFUNC, nil, nil)
|
|
Curfn = xfunc
|
|
var fld *Node
|
|
var n *Node
|
|
for t, it := IterFields(t0.Params()); t != nil; t = it.Next() {
|
|
n = newname(Lookupf("a%d", i))
|
|
i++
|
|
n.Class = PPARAM
|
|
xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
|
|
callargs = append(callargs, n)
|
|
fld = Nod(ODCLFIELD, n, typenod(t.Type))
|
|
if t.Isddd {
|
|
fld.Isddd = true
|
|
ddd = true
|
|
}
|
|
|
|
l = append(l, fld)
|
|
}
|
|
|
|
xtype.List.Set(l)
|
|
i = 0
|
|
l = nil
|
|
var retargs []*Node
|
|
for t, it := IterFields(t0.Results()); t != nil; t = it.Next() {
|
|
n = newname(Lookupf("r%d", i))
|
|
i++
|
|
n.Class = PPARAMOUT
|
|
xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
|
|
retargs = append(retargs, n)
|
|
l = append(l, Nod(ODCLFIELD, n, typenod(t.Type)))
|
|
}
|
|
|
|
xtype.Rlist.Set(l)
|
|
|
|
xfunc.Func.Dupok = true
|
|
xfunc.Func.Nname = newfuncname(sym)
|
|
xfunc.Func.Nname.Sym.Flags |= SymExported // disable export
|
|
xfunc.Func.Nname.Name.Param.Ntype = xtype
|
|
xfunc.Func.Nname.Name.Defn = xfunc
|
|
declare(xfunc.Func.Nname, PFUNC)
|
|
|
|
// Declare and initialize variable holding receiver.
|
|
|
|
xfunc.Func.Needctxt = true
|
|
cv := Nod(OCLOSUREVAR, nil, nil)
|
|
cv.Xoffset = int64(Widthptr)
|
|
cv.Type = rcvrtype
|
|
if int(cv.Type.Align) > Widthptr {
|
|
cv.Xoffset = int64(cv.Type.Align)
|
|
}
|
|
ptr := Nod(ONAME, nil, nil)
|
|
ptr.Sym = Lookup("rcvr")
|
|
ptr.Class = PAUTO
|
|
ptr.Addable = true
|
|
ptr.Ullman = 1
|
|
ptr.Used = true
|
|
ptr.Name.Curfn = xfunc
|
|
ptr.Xoffset = 0
|
|
xfunc.Func.Dcl = append(xfunc.Func.Dcl, ptr)
|
|
var body []*Node
|
|
if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) {
|
|
ptr.Name.Param.Ntype = typenod(rcvrtype)
|
|
body = append(body, Nod(OAS, ptr, cv))
|
|
} else {
|
|
ptr.Name.Param.Ntype = typenod(Ptrto(rcvrtype))
|
|
body = append(body, Nod(OAS, ptr, Nod(OADDR, cv, nil)))
|
|
}
|
|
|
|
call := Nod(OCALL, Nod(OXDOT, ptr, meth), nil)
|
|
call.List.Set(callargs)
|
|
call.Isddd = ddd
|
|
if t0.Outtuple == 0 {
|
|
body = append(body, call)
|
|
} else {
|
|
n := Nod(OAS2, nil, nil)
|
|
n.List.Set(retargs)
|
|
n.Rlist.Set1(call)
|
|
body = append(body, n)
|
|
n = Nod(ORETURN, nil, nil)
|
|
body = append(body, n)
|
|
}
|
|
|
|
xfunc.Nbody.Set(body)
|
|
|
|
typecheck(&xfunc, Etop)
|
|
sym.Def = xfunc
|
|
xtop = append(xtop, xfunc)
|
|
Curfn = savecurfn
|
|
|
|
return xfunc
|
|
}
|
|
|
|
func walkpartialcall(n *Node, init *Nodes) *Node {
|
|
// Create closure in the form of a composite literal.
|
|
// For x.M with receiver (x) type T, the generated code looks like:
|
|
//
|
|
// clos = &struct{F uintptr; R T}{M.T·f, x}
|
|
//
|
|
// Like walkclosure above.
|
|
|
|
if Isinter(n.Left.Type) {
|
|
// Trigger panic for method on nil interface now.
|
|
// Otherwise it happens in the wrapper and is confusing.
|
|
n.Left = cheapexpr(n.Left, init)
|
|
|
|
checknil(n.Left, init)
|
|
}
|
|
|
|
typ := Nod(OTSTRUCT, nil, nil)
|
|
typ.List.Set1(Nod(ODCLFIELD, newname(Lookup("F")), typenod(Types[TUINTPTR])))
|
|
typ.List.Append(Nod(ODCLFIELD, newname(Lookup("R")), typenod(n.Left.Type)))
|
|
|
|
clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil))
|
|
clos.Esc = n.Esc
|
|
clos.Right.Implicit = true
|
|
clos.List.Set1(Nod(OCFUNC, n.Func.Nname, nil))
|
|
clos.List.Append(n.Left)
|
|
|
|
// Force type conversion from *struct to the func type.
|
|
clos = Nod(OCONVNOP, clos, nil)
|
|
|
|
clos.Type = n.Type
|
|
|
|
typecheck(&clos, Erv)
|
|
|
|
// typecheck will insert a PTRLIT node under CONVNOP,
|
|
// tag it with escape analysis result.
|
|
clos.Left.Esc = n.Esc
|
|
|
|
// non-escaping temp to use, if any.
|
|
// orderexpr did not compute the type; fill it in now.
|
|
if x := prealloc[n]; x != nil {
|
|
x.Type = clos.Left.Left.Type
|
|
x.Orig.Type = x.Type
|
|
clos.Left.Right = x
|
|
delete(prealloc, n)
|
|
}
|
|
|
|
walkexpr(&clos, init)
|
|
|
|
return clos
|
|
}
|