xtype := p.typeExpr(expr.Type)
ntype := p.typeExpr(expr.Type)
- xfunc := p.nod(expr, ODCLFUNC, nil, nil)
- xfunc.Func.SetIsHiddenClosure(Curfn != nil)
- xfunc.Func.Nname = newfuncnamel(p.pos(expr), nblank.Sym) // filled in by typecheckclosure
- xfunc.Func.Nname.Name.Param.Ntype = xtype
- xfunc.Func.Nname.Name.Defn = xfunc
+ dcl := p.nod(expr, ODCLFUNC, nil, nil)
+ fn := dcl.Func
+ fn.SetIsHiddenClosure(Curfn != nil)
+ fn.Nname = newfuncnamel(p.pos(expr), nblank.Sym, fn) // filled in by typecheckclosure
+ fn.Nname.Name.Param.Ntype = xtype
+ fn.Nname.Name.Defn = dcl
clo := p.nod(expr, OCLOSURE, nil, nil)
- clo.Func.Ntype = ntype
+ clo.Func = fn
+ fn.ClosureType = ntype
+ fn.OClosure = clo
- xfunc.Func.Closure = clo
- clo.Func.Closure = xfunc
-
- p.funcBody(xfunc, expr.Body)
+ p.funcBody(dcl, expr.Body)
// 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 xfunc.Func.Cvars.Slice() {
+ for _, v := range fn.ClosureVars.Slice() {
// Unlink from v1; see comment in syntax.go type Param for these fields.
v1 := v.Name.Defn
v1.Name.Param.Innermost = v.Name.Param.Outer
// TODO: This creation of the named function should probably really be done in a
// separate pass from type-checking.
func typecheckclosure(clo *Node, top int) {
- xfunc := clo.Func.Closure
+ fn := clo.Func
+ dcl := fn.Decl
// Set current associated iota value, so iota can be used inside
// function in ConstSpec, see issue #22344
if x := getIotaValue(); x >= 0 {
- xfunc.SetIota(x)
+ dcl.SetIota(x)
}
- clo.Func.Ntype = typecheck(clo.Func.Ntype, ctxType)
- clo.Type = clo.Func.Ntype.Type
- clo.Func.Top = top
+ fn.ClosureType = typecheck(fn.ClosureType, ctxType)
+ clo.Type = fn.ClosureType.Type
+ fn.ClosureCalled = top&ctxCallee != 0
- // Do not typecheck xfunc twice, otherwise, we will end up pushing
- // xfunc to xtop multiple times, causing initLSym called twice.
+ // Do not typecheck dcl twice, otherwise, we will end up pushing
+ // dcl to xtop multiple times, causing initLSym called twice.
// See #30709
- if xfunc.Typecheck() == 1 {
+ if dcl.Typecheck() == 1 {
return
}
- for _, ln := range xfunc.Func.Cvars.Slice() {
+ for _, ln := range fn.ClosureVars.Slice() {
n := ln.Name.Defn
if !n.Name.Captured() {
n.Name.SetCaptured(true)
}
}
- xfunc.Func.Nname.Sym = closurename(Curfn)
- setNodeNameFunc(xfunc.Func.Nname)
- xfunc = typecheck(xfunc, ctxStmt)
+ fn.Nname.Sym = closurename(Curfn)
+ setNodeNameFunc(fn.Nname)
+ dcl = typecheck(dcl, ctxStmt)
// 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
// underlying closure function we create is added to xtop.
if Curfn != nil && clo.Type != nil {
oldfn := Curfn
- Curfn = xfunc
+ Curfn = dcl
olddd := decldepth
decldepth = 1
- typecheckslice(xfunc.Nbody.Slice(), ctxStmt)
+ typecheckslice(dcl.Nbody.Slice(), ctxStmt)
decldepth = olddd
Curfn = oldfn
}
- xtop = append(xtop, xfunc)
+ xtop = append(xtop, dcl)
}
// globClosgen is like Func.Closgen, but for the global scope.
gen := &globClosgen
if outerfunc != nil {
- if outerfunc.Func.Closure != nil {
+ if outerfunc.Func.OClosure != nil {
prefix = ""
}
// 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) {
+func capturevars(dcl *Node) {
lno := lineno
- lineno = xfunc.Pos
-
- clo := xfunc.Func.Closure
- cvars := xfunc.Func.Cvars.Slice()
+ lineno = dcl.Pos
+ fn := dcl.Func
+ cvars := fn.ClosureVars.Slice()
out := cvars[:0]
for _, v := range cvars {
if v.Type == nil {
}
outer = typecheck(outer, ctxExpr)
- clo.Func.Enter.Append(outer)
+ fn.ClosureEnter.Append(outer)
}
- xfunc.Func.Cvars.Set(out)
+ fn.ClosureVars.Set(out)
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) {
+func transformclosure(dcl *Node) {
lno := lineno
- lineno = xfunc.Pos
- clo := xfunc.Func.Closure
+ lineno = dcl.Pos
+ fn := dcl.Func
- if clo.Func.Top&ctxCallee != 0 {
+ if fn.ClosureCalled {
// 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)
// }(byval, &byref, 42)
// f is ONAME of the actual function.
- f := xfunc.Func.Nname
+ f := fn.Nname
// We are going to insert captured variables before input args.
var params []*types.Field
var decls []*Node
- for _, v := range xfunc.Func.Cvars.Slice() {
+ for _, v := range fn.ClosureVars.Slice() {
if !v.Name.Byval() {
// If v of type T is captured by reference,
// we introduce function param &v *T
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...)
+ fn.Dcl = append(decls, fn.Dcl...)
}
dowidth(f.Type)
- xfunc.Type = f.Type // update type of ODCLFUNC
+ dcl.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)
- for _, v := range xfunc.Func.Cvars.Slice() {
+ for _, v := range fn.ClosureVars.Slice() {
// cv refers to the field inside of closure OSTRUCTLIT.
cv := nod(OCLOSUREVAR, nil, nil)
if v.Name.Byval() && v.Type.Width <= int64(2*Widthptr) {
// If it is a small variable captured by value, downgrade it to PAUTO.
v.SetClass(PAUTO)
- xfunc.Func.Dcl = append(xfunc.Func.Dcl, v)
+ fn.Dcl = append(fn.Dcl, v)
body = append(body, nod(OAS, v, cv))
} else {
// Declare variable holding addresses taken from closure
addr.Type = types.NewPtr(v.Type)
addr.SetClass(PAUTO)
addr.Name.SetUsed(true)
- addr.Name.Curfn = xfunc
- xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr)
+ addr.Name.Curfn = dcl
+ fn.Dcl = append(fn.Dcl, addr)
v.Name.Param.Heapaddr = addr
if v.Name.Byval() {
cv = nod(OADDR, cv, nil)
if len(body) > 0 {
typecheckslice(body, ctxStmt)
- xfunc.Func.Enter.Set(body)
- xfunc.Func.SetNeedctxt(true)
+ fn.Enter.Set(body)
+ fn.SetNeedctxt(true)
}
}
// hasemptycvars reports whether closure clo has an
// empty list of captured vars.
func hasemptycvars(clo *Node) bool {
- xfunc := clo.Func.Closure
- return xfunc.Func.Cvars.Len() == 0
+ return clo.Func.ClosureVars.Len() == 0
}
// closuredebugruntimecheck applies boilerplate checks for debug flags
// and compiling runtime
func closuredebugruntimecheck(clo *Node) {
if Debug_closure > 0 {
- xfunc := clo.Func.Closure
if clo.Esc == EscHeap {
- Warnl(clo.Pos, "heap closure, captured vars = %v", xfunc.Func.Cvars)
+ Warnl(clo.Pos, "heap closure, captured vars = %v", clo.Func.ClosureVars)
} else {
- Warnl(clo.Pos, "stack closure, captured vars = %v", xfunc.Func.Cvars)
+ Warnl(clo.Pos, "stack closure, captured vars = %v", clo.Func.ClosureVars)
}
}
if compiling_runtime && clo.Esc == EscHeap {
fields := []*Node{
namedfield(".F", types.Types[TUINTPTR]),
}
- for _, v := range clo.Func.Closure.Func.Cvars.Slice() {
+ for _, v := range clo.Func.ClosureVars.Slice() {
typ := v.Type
if !v.Name.Byval() {
typ = types.NewPtr(typ)
}
func walkclosure(clo *Node, init *Nodes) *Node {
- xfunc := clo.Func.Closure
+ fn := clo.Func
// If no closure vars, don't bother wrapping.
if hasemptycvars(clo) {
if Debug_closure > 0 {
Warnl(clo.Pos, "closure converted to global")
}
- return xfunc.Func.Nname
+ return fn.Nname
}
closuredebugruntimecheck(clo)
clos := nod(OCOMPLIT, nil, typenod(typ))
clos.Esc = clo.Esc
- clos.List.Set(append([]*Node{nod(OCFUNC, xfunc.Func.Nname, nil)}, clo.Func.Enter.Slice()...))
+ clos.List.Set(append([]*Node{nod(OCFUNC, fn.Nname, nil)}, fn.ClosureEnter.Slice()...))
clos = nod(OADDR, clos, nil)
clos.Esc = clo.Esc
return walkexpr(clos, init)
}
-func typecheckpartialcall(fn *Node, sym *types.Sym) {
- switch fn.Op {
+func typecheckpartialcall(dot *Node, sym *types.Sym) {
+ switch dot.Op {
case ODOTINTER, ODOTMETH:
break
}
// Create top-level function.
- xfunc := makepartialcall(fn, fn.Type, sym)
- fn.Func = xfunc.Func
- fn.Func.SetWrapper(true)
- fn.Right = newname(sym)
- fn.Op = OCALLPART
- fn.Type = xfunc.Type
- fn.SetOpt(nil) // clear types.Field from ODOTMETH
+ dcl := makepartialcall(dot, dot.Type, sym)
+ dcl.Func.SetWrapper(true)
+ dot.Op = OCALLPART
+ dot.Right = newname(sym)
+ dot.Type = dcl.Type
+ dot.Func = dcl.Func
+ dot.SetOpt(nil) // clear types.Field from ODOTMETH
}
// makepartialcall returns a DCLFUNC node representing the wrapper function (*-fm) needed
// for partial calls.
-func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
- rcvrtype := fn.Left.Type
+func makepartialcall(dot *Node, t0 *types.Type, meth *types.Sym) *Node {
+ rcvrtype := dot.Left.Type
sym := methodSymSuffix(rcvrtype, meth, "-fm")
if sym.Uniq() {
tfn.List.Set(structargs(t0.Params(), true))
tfn.Rlist.Set(structargs(t0.Results(), false))
- xfunc := dclfunc(sym, tfn)
- xfunc.Func.SetDupok(true)
- xfunc.Func.SetNeedctxt(true)
+ dcl := dclfunc(sym, tfn)
+ fn := dcl.Func
+ fn.SetDupok(true)
+ fn.SetNeedctxt(true)
tfn.Type.SetPkg(t0.Pkg())
}
body = append(body, call)
- xfunc.Nbody.Set(body)
+ dcl.Nbody.Set(body)
funcbody()
- xfunc = typecheck(xfunc, ctxStmt)
+ dcl = typecheck(dcl, ctxStmt)
// Need to typecheck the body of the just-generated wrapper.
// typecheckslice() requires that Curfn is set when processing an ORETURN.
- Curfn = xfunc
- typecheckslice(xfunc.Nbody.Slice(), ctxStmt)
- sym.Def = asTypesNode(xfunc)
- xtop = append(xtop, xfunc)
+ Curfn = dcl
+ typecheckslice(dcl.Nbody.Slice(), ctxStmt)
+ sym.Def = asTypesNode(dcl)
+ xtop = append(xtop, dcl)
Curfn = savecurfn
lineno = saveLineNo
- return xfunc
+ return dcl
}
// partialCallType returns the struct type used to hold all the information
}
// newfuncnamel generates a new name node for a function or method.
-// TODO(rsc): Use an ODCLFUNC node instead. See comment in CL 7360.
-func newfuncnamel(pos src.XPos, s *types.Sym) *Node {
+func newfuncnamel(pos src.XPos, s *types.Sym, fn *Func) *Node {
+ if fn.Nname != nil {
+ Fatalf("newfuncnamel - already have name")
+ }
n := newnamel(pos, s)
- n.Func = new(Func)
- n.Func.SetIsHiddenClosure(Curfn != nil)
+ n.Func = fn
+ fn.Nname = n
return n
}
c.Name.Param.Outer = n.Name.Param.Innermost
n.Name.Param.Innermost = c
- Curfn.Func.Cvars.Append(c)
+ Curfn.Func.ClosureVars.Append(c)
}
// return ref to closure var, not original
types.Markdcl()
- if n.Func.Nname != nil {
+ if n.Func.Nname != nil && n.Func.Nname.Name.Param.Ntype != nil {
funcargs(n.Func.Nname.Name.Param.Ntype)
- } else if n.Func.Ntype != nil {
- funcargs(n.Func.Ntype)
} else {
funcargs2(n.Type)
}
}
fn := nod(ODCLFUNC, nil, nil)
- fn.Func.Nname = newfuncnamel(lineno, sym)
+ fn.Func.Nname = newfuncnamel(lineno, sym, fn.Func)
fn.Func.Nname.Name.Defn = fn
fn.Func.Nname.Name.Param.Ntype = tfn
setNodeNameFunc(fn.Func.Nname)
case ONAME:
callee = arg.Name.Defn
case OCLOSURE:
- callee = arg.Func.Closure
+ callee = arg.Func.Decl
default:
Fatalf("expected ONAME or OCLOSURE node, got %+v", arg)
}
// heap in f, not in the inner closure. Flip over to f before calling moveToHeap.
oldfn := Curfn
Curfn = n.Name.Curfn
- if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
- Curfn = Curfn.Func.Closure
+ if Curfn.Op == OCLOSURE {
+ Curfn = Curfn.Func.Decl
+ panic("can't happen")
}
ln := lineno
lineno = Curfn.Pos
k = e.spill(k, n)
// Link addresses of captured variables to closure.
- for _, v := range n.Func.Closure.Func.Cvars.Slice() {
+ for _, v := range n.Func.ClosureVars.Slice() {
if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs
continue
}
case v.Op == ONAME && v.Class() == PFUNC:
fn = v
case v.Op == OCLOSURE:
- fn = v.Func.Closure.Func.Nname
+ fn = v.Func.Nname
}
case OCALLMETH:
fn = call.Left.MethodName()
//
// var u int // okay to stack allocate
// *(func() *int { return &u }()) = 42
- if containsClosure(other.curfn, l.curfn) && l.curfn.Func.Closure.Func.Top&ctxCallee != 0 {
+ if containsClosure(other.curfn, l.curfn) && l.curfn.Func.ClosureCalled {
return false
}
mode.Fprintf(s, "%v { %v }", n.Type, n.Nbody)
return
}
- mode.Fprintf(s, "%v { %v }", n.Type, n.Func.Closure.Nbody)
+ mode.Fprintf(s, "%v { %v }", n.Type, n.Func.Decl.Nbody)
case OCOMPLIT:
if mode == FErr {
}
}
- if n.Op == OCLOSURE && n.Func.Closure != nil && n.Func.Closure.Func.Nname.Sym != nil {
- mode.Fprintf(s, " fnName %v", n.Func.Closure.Func.Nname.Sym)
+ if n.Op == OCLOSURE && n.Func.Decl != nil && n.Func.Nname.Sym != nil {
+ mode.Fprintf(s, " fnName %v", n.Func.Nname.Sym)
}
if n.Sym != nil && n.Op != ONAME {
mode.Fprintf(s, " %v", n.Sym)
if n.Right != nil {
mode.Fprintf(s, "%v", n.Right)
}
- if n.Func != nil && n.Func.Closure != nil && n.Func.Closure.Nbody.Len() != 0 {
+ if n.Op == OCLOSURE && n.Func != nil && n.Func.Decl != nil && n.Func.Decl.Nbody.Len() != 0 {
indent(s)
// The function associated with a closure
- mode.Fprintf(s, "%v-clofunc%v", n.Op, n.Func.Closure)
+ mode.Fprintf(s, "%v-clofunc%v", n.Op, n.Func.Decl)
}
- if n.Func != nil && n.Func.Dcl != nil && len(n.Func.Dcl) != 0 {
+ if n.Op == ODCLFUNC && n.Func != nil && n.Func.Dcl != nil && len(n.Func.Dcl) != 0 {
indent(s)
// The dcls for a func or closure
mode.Fprintf(s, "%v-dcl%v", n.Op, asNodes(n.Func.Dcl))
if curfn == nil {
Fatalf("no curfn for tempAt")
}
- if curfn.Func.Closure != nil && curfn.Op == OCLOSURE {
+ if curfn.Op == OCLOSURE {
Dump("tempAt", curfn)
Fatalf("adding tempAt to wrong closure function")
}
recv := r.param()
mtyp := r.signature(recv)
- m := newfuncnamel(mpos, methodSym(recv.Type, msym))
+ m := newfuncnamel(mpos, methodSym(recv.Type, msym), new(Func))
m.Type = mtyp
m.SetClass(PFUNC)
// methodSym already marked m.Sym as a function.
}
case OCLOSURE:
- d.inspectList(n.Func.Closure.Nbody)
+ d.inspectList(n.Func.Decl.Nbody)
case ODOTMETH, OCALLPART:
d.foundDep(n.MethodName())
}
return fn
case fn.Op == OCLOSURE:
- c := fn.Func.Closure
+ c := fn.Func.Decl
caninl(c)
return c.Func.Nname
}
// We need to walk the function body to check for reassignments so we follow the
// linkage to the ODCLFUNC node as that is where body is held.
if f.Op == OCLOSURE {
- f = f.Func.Closure
+ f = f.Func.Decl
}
v := reassignVisitor{name: n}
a := v.visitList(f.Nbody)
// Handle captured variables when inlining closures.
if fn.Name.Defn != nil {
- if c := fn.Name.Defn.Func.Closure; c != nil {
- for _, v := range c.Func.Closure.Func.Cvars.Slice() {
+ if c := fn.Name.Defn.Func.OClosure; c != nil {
+ for _, v := range c.Func.ClosureVars.Slice() {
if v.Op == OXXX {
continue
}
// NB: if we enabled inlining of functions containing OCLOSURE or refined
// the reassigned check via some sort of copy propagation this would most
// likely need to be changed to a loop to walk up to the correct Param
- if o == nil || (o.Name.Curfn != Curfn && o.Name.Curfn.Func.Closure != Curfn) {
+ if o == nil || (o.Name.Curfn != Curfn && o.Name.Curfn.Func.OClosure != Curfn) {
Fatalf("%v: unresolvable capture %v %v\n", n.Line(), fn, v)
}
// because variables captured by value do not escape.
timings.Start("fe", "capturevars")
for _, n := range xtop {
- if n.Op == ODCLFUNC && n.Func.Closure != nil {
+ if n.Op == ODCLFUNC && n.Func.OClosure != nil {
Curfn = n
capturevars(n)
}
// before walk reaches a call of a closure.
timings.Start("fe", "xclosures")
for _, n := range xtop {
- if n.Op == ODCLFUNC && n.Func.Closure != nil {
+ if n.Op == ODCLFUNC && n.Func.OClosure != nil {
Curfn = n
transformclosure(n)
}
func numNonClosures(list []*Node) int {
count := 0
for _, n := range list {
- if n.Func.Closure == nil {
+ if n.Func.OClosure == nil {
count++
}
}
name = nblank.Sym // filled in by typecheckfunc
}
- f.Func.Nname = newfuncnamel(p.pos(fun.Name), name)
+ f.Func.Nname = newfuncnamel(p.pos(fun.Name), name, f.Func)
f.Func.Nname.Name.Defn = f
f.Func.Nname.Name.Param.Ntype = t
}
case OCLOSURE:
- if n.Transient() && n.Func.Closure.Func.Cvars.Len() > 0 {
+ if n.Transient() && n.Func.ClosureVars.Len() > 0 {
prealloc[n] = o.newTemp(closureType(n), false)
}
}
}
+ // Back when there were two different *Funcs for a function, this code
+ // was not consistent about whether a particular *Node being processed
+ // was an ODCLFUNC or ONAME node. Partly this is because inlined function
+ // bodies have no ODCLFUNC node, which was it's own inconsistency.
+ // In any event, the handling of the two different nodes for DWARF purposes
+ // was subtly different, likely in unintended ways. CL 272253 merged the
+ // two nodes' Func fields, so that code sees the same *Func whether it is
+ // holding the ODCLFUNC or the ONAME. This resulted in changes in the
+ // DWARF output. To preserve the existing DWARF output and leave an
+ // intentional change for a future CL, this code does the following when
+ // fn.Op == ONAME:
+ //
+ // 1. Disallow use of createComplexVars in createDwarfVars.
+ // It was not possible to reach that code for an ONAME before,
+ // because the DebugInfo was set only on the ODCLFUNC Func.
+ // Calling into it in the ONAME case causes an index out of bounds panic.
+ //
+ // 2. Do not populate apdecls. fn.Func.Dcl was in the ODCLFUNC Func,
+ // not the ONAME Func. Populating apdecls for the ONAME case results
+ // in selected being populated after createSimpleVars is called in
+ // createDwarfVars, and then that causes the loop to skip all the entries
+ // in dcl, meaning that the RecordAutoType calls don't happen.
+ //
+ // These two adjustments keep toolstash -cmp working for now.
+ // Deciding the right answer is, as they say, future work.
+ isODCLFUNC := fn.Op == ODCLFUNC
+
var apdecls []*Node
// Populate decls for fn.
- for _, n := range fn.Func.Dcl {
- if n.Op != ONAME { // might be OTYPE or OLITERAL
- continue
- }
- switch n.Class() {
- case PAUTO:
- if !n.Name.Used() {
- // Text == nil -> generating abstract function
- if fnsym.Func().Text != nil {
- Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
+ if isODCLFUNC {
+ for _, n := range fn.Func.Dcl {
+ if n.Op != ONAME { // might be OTYPE or OLITERAL
+ continue
+ }
+ switch n.Class() {
+ case PAUTO:
+ if !n.Name.Used() {
+ // Text == nil -> generating abstract function
+ if fnsym.Func().Text != nil {
+ Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
+ }
+ continue
}
+ case PPARAM, PPARAMOUT:
+ default:
continue
}
- case PPARAM, PPARAMOUT:
- default:
- continue
+ apdecls = append(apdecls, n)
+ fnsym.Func().RecordAutoType(ngotype(n).Linksym())
}
- apdecls = append(apdecls, n)
- fnsym.Func().RecordAutoType(ngotype(n).Linksym())
}
- decls, dwarfVars := createDwarfVars(fnsym, fn.Func, apdecls)
+ decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn.Func, apdecls)
// For each type referenced by the functions auto vars but not
// already referenced by a dwarf var, attach a dummy relocation to
// createDwarfVars process fn, returning a list of DWARF variables and the
// Nodes they represent.
-func createDwarfVars(fnsym *obj.LSym, fn *Func, apDecls []*Node) ([]*Node, []*dwarf.Var) {
+func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *Func, apDecls []*Node) ([]*Node, []*dwarf.Var) {
// Collect a raw list of DWARF vars.
var vars []*dwarf.Var
var decls []*Node
var selected map[*Node]bool
- if Ctxt.Flag_locationlists && Ctxt.Flag_optimize && fn.DebugInfo != nil {
+ if Ctxt.Flag_locationlists && Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK {
decls, vars, selected = createComplexVars(fnsym, fn)
} else {
decls, vars, selected = createSimpleVars(fnsym, apDecls)
}
}
case OCLOSURE:
- if m := v.visit(n.Func.Closure); m < min {
+ if m := v.visit(n.Func.Decl); m < min {
min = m
}
}
}
// Closures with no captured variables are globals,
// so the assignment can be done at link time.
- pfuncsym(l, r.Func.Closure.Func.Nname)
+ pfuncsym(l, r.Func.Nname)
return true
}
closuredebugruntimecheck(r)
_32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms
}{
- {Func{}, 124, 224},
+ {Func{}, 132, 240},
{Name{}, 32, 56},
{Param{}, 24, 48},
{Node{}, 76, 128},
func nodl(pos src.XPos, op Op, nleft, nright *Node) *Node {
var n *Node
switch op {
- case OCLOSURE, ODCLFUNC:
+ case ODCLFUNC:
var x struct {
n Node
f Func
}
n = &x.n
n.Func = &x.f
+ n.Func.Decl = n
case ONAME:
Fatalf("use newname instead")
case OLABEL, OPACK:
*(*p.Extra).(*embedFileList) = list
}
-// Functions
+// A Func corresponds to a single function in a Go program
+// (and vice versa: each function is denoted by exactly one *Func).
//
-// A simple function declaration is represented as an ODCLFUNC node f
-// and an ONAME node n. They're linked to one another through
-// f.Func.Nname == n and n.Name.Defn == f. When functions are
-// referenced by name in an expression, the function's ONAME node is
-// used directly.
+// There are multiple nodes that represent a Func in the IR.
//
-// Function names have n.Class() == PFUNC. This distinguishes them
-// from variables of function type.
+// The ONAME node (Func.Name) is used for plain references to it.
+// The ODCLFUNC node (Func.Decl) is used for its declaration code.
+// The OCLOSURE node (Func.Closure) is used for a reference to a
+// function literal.
//
-// Confusingly, n.Func and f.Func both exist, but commonly point to
-// different Funcs. (Exception: an OCALLPART's Func does point to its
-// ODCLFUNC's Func.)
+// A Func for an imported function will have only an ONAME node.
+// A declared function or method has an ONAME and an ODCLFUNC.
+// A function literal is represented directly by an OCLOSURE, but it also
+// has an ODCLFUNC (and a matching ONAME) representing the compiled
+// underlying form of the closure, which accesses the captured variables
+// using a special data structure passed in a register.
//
-// A method declaration is represented like functions, except n.Sym
+// A method declaration is represented like functions, except f.Sym
// will be the qualified method name (e.g., "T.m") and
// f.Func.Shortname is the bare method name (e.g., "m").
//
-// Method expressions are represented as ONAME/PFUNC nodes like
-// function names, but their Left and Right fields still point to the
-// type and method, respectively. They can be distinguished from
-// normal functions with isMethodExpression. Also, unlike function
-// name nodes, method expression nodes exist for each method
-// expression. The declaration ONAME can be accessed with
-// x.Type.Nname(), where x is the method expression ONAME node.
+// A method expression (T.M) is represented as an ONAME node
+// like a function name would be, but n.Left and n.Right point to
+// the type and method, respectively. A method expression can
+// be distinguished from a normal function ONAME by checking
+// n.IsMethodExpression. Unlike ordinary ONAME nodes, each
+// distinct mention of a method expression in the source code
+// constructs a fresh ONAME node.
+// TODO(rsc): Method expressions deserve their own opcode
+// instead of violating invariants of ONAME.
//
-// Method values are represented by ODOTMETH/ODOTINTER when called
-// immediately, and OCALLPART otherwise. They are like method
-// expressions, except that for ODOTMETH/ODOTINTER the method name is
-// stored in Sym instead of Right.
-//
-// Closures are represented by OCLOSURE node c. They link back and
-// forth with the ODCLFUNC via Func.Closure; that is, c.Func.Closure
-// == f and f.Func.Closure == c.
-//
-// Function bodies are stored in f.Nbody, and inline function bodies
-// are stored in n.Func.Inl. Pragmas are stored in f.Func.Pragma.
-//
-// Imported functions skip the ODCLFUNC, so n.Name.Defn is nil. They
-// also use Dcl instead of Inldcl.
-
-// Func holds Node fields used only with function-like nodes.
+// A method value (t.M) is represented by ODOTMETH/ODOTINTER
+// when it is called directly and by OCALLPART otherwise.
+// These are like method expressions, except that for ODOTMETH/ODOTINTER,
+// the method name is stored in Sym instead of Right.
+// Each OCALLPART ends up being implemented as a new
+// function, a bit like a closure, with its own ODCLFUNC.
+// The OCALLPART has uses n.Func to record the linkage to
+// the generated ODCLFUNC (as n.Func.Decl), but there is no
+// pointer from the Func back to the OCALLPART.
type Func struct {
+ Nname *Node // ONAME node
+ Decl *Node // ODCLFUNC node
+ OClosure *Node // OCLOSURE node
+
Shortname *types.Sym
+
// Extra entry code for the function. For example, allocate and initialize
- // memory for escaping parameters. However, just for OCLOSURE, Enter is a
- // list of ONAME nodes of captured variables
+ // memory for escaping parameters.
Enter Nodes
Exit Nodes
- // ONAME nodes for closure params, each should have closurevar set
- Cvars Nodes
// ONAME nodes for all params/locals for this func/closure, does NOT
// include closurevars until transformclosure runs.
Dcl []*Node
+ ClosureEnter Nodes // list of ONAME nodes of captured variables
+ ClosureType *Node // closure representation type
+ ClosureCalled bool // closure is only immediately called
+ ClosureVars Nodes // closure params; each has closurevar set
+
// Parents records the parent scope of each scope within a
// function. The root scope (0) has no parent, so the i'th
// scope's parent is stored at Parents[i-1].
FieldTrack map[*types.Sym]struct{}
DebugInfo *ssa.FuncDebug
- Ntype *Node // signature
- Top int // top context (ctxCallee, etc)
- Closure *Node // OCLOSURE <-> ODCLFUNC (see header comment above)
- Nname *Node // The ONAME node associated with an ODCLFUNC (both have same Type)
lsym *obj.LSym
Inl *Inline
// transformclosure already did all preparation work.
// Prepend captured variables to argument list.
- n.List.Prepend(n.Left.Func.Enter.Slice()...)
-
- n.Left.Func.Enter.Set(nil)
+ n.List.Prepend(n.Left.Func.ClosureEnter.Slice()...)
+ n.Left.Func.ClosureEnter.Set(nil)
// Replace OCLOSURE with ONAME/PFUNC.
- n.Left = n.Left.Func.Closure.Func.Nname
+ n.Left = n.Left.Func.Nname
// Update type of OCALLFUNC node.
// Output arguments had not changed, but their offsets could.