mirror of https://github.com/golang/go.git
468 lines
12 KiB
C
468 lines
12 KiB
C
// 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.
|
|
|
|
/*
|
|
* function literals aka closures
|
|
*/
|
|
|
|
#include <u.h>
|
|
#include <libc.h>
|
|
#include "go.h"
|
|
|
|
void
|
|
closurehdr(Node *ntype)
|
|
{
|
|
Node *n, *name, *a;
|
|
NodeList *l;
|
|
|
|
n = nod(OCLOSURE, N, N);
|
|
n->ntype = ntype;
|
|
n->funcdepth = funcdepth;
|
|
|
|
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 = ntype->list;
|
|
n->rlist = ntype->rlist;
|
|
ntype->list = nil;
|
|
ntype->rlist = nil;
|
|
for(l=n->list; l; l=l->next) {
|
|
name = l->n->left;
|
|
if(name)
|
|
name = newname(name->sym);
|
|
a = nod(ODCLFIELD, name, l->n->right);
|
|
a->isddd = l->n->isddd;
|
|
if(name)
|
|
name->isddd = a->isddd;
|
|
ntype->list = list(ntype->list, a);
|
|
}
|
|
for(l=n->rlist; l; l=l->next) {
|
|
name = l->n->left;
|
|
if(name)
|
|
name = newname(name->sym);
|
|
ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, name, l->n->right));
|
|
}
|
|
}
|
|
|
|
Node*
|
|
closurebody(NodeList *body)
|
|
{
|
|
Node *func, *v;
|
|
NodeList *l;
|
|
|
|
if(body == nil)
|
|
body = list1(nod(OEMPTY, N, N));
|
|
|
|
func = curfn;
|
|
func->nbody = body;
|
|
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(l=func->cvars; l; l=l->next) {
|
|
v = l->n;
|
|
v->closure->closure = v->outer;
|
|
v->heapaddr = nod(OADDR, oldname(v->sym), N);
|
|
}
|
|
|
|
return func;
|
|
}
|
|
|
|
static Node* makeclosure(Node *func);
|
|
|
|
void
|
|
typecheckclosure(Node *func, int top)
|
|
{
|
|
Node *oldfn;
|
|
NodeList *l;
|
|
Node *v;
|
|
|
|
oldfn = curfn;
|
|
typecheck(&func->ntype, Etype);
|
|
func->type = func->ntype->type;
|
|
|
|
// 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 && func->type != T) {
|
|
curfn = func;
|
|
typechecklist(func->nbody, Etop);
|
|
curfn = oldfn;
|
|
}
|
|
|
|
// type check the & of closed variables outside the closure,
|
|
// so that the outer frame also grabs them and knows they
|
|
// escape.
|
|
func->enter = nil;
|
|
for(l=func->cvars; l; l=l->next) {
|
|
v = l->n;
|
|
if(v->type == T) {
|
|
// 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 = 0;
|
|
continue;
|
|
}
|
|
// For a closure that is called in place, but not
|
|
// inside a go statement, avoid moving variables to the heap.
|
|
if ((top & (Ecall|Eproc)) == Ecall)
|
|
v->heapaddr->etype = 1;
|
|
typecheck(&v->heapaddr, Erv);
|
|
func->enter = list(func->enter, v->heapaddr);
|
|
v->heapaddr = N;
|
|
}
|
|
|
|
// Create top-level function
|
|
xtop = list(xtop, makeclosure(func));
|
|
}
|
|
|
|
static Node*
|
|
makeclosure(Node *func)
|
|
{
|
|
Node *xtype, *v, *addr, *xfunc, *cv;
|
|
NodeList *l, *body;
|
|
static int closgen;
|
|
char *p;
|
|
vlong offset;
|
|
|
|
/*
|
|
* wrap body in external function
|
|
* that begins by reading closure parameters.
|
|
*/
|
|
xtype = nod(OTFUNC, N, N);
|
|
xtype->list = func->list;
|
|
xtype->rlist = func->rlist;
|
|
|
|
// create the function
|
|
xfunc = nod(ODCLFUNC, N, N);
|
|
snprint(namebuf, sizeof namebuf, "func·%.3d", ++closgen);
|
|
xfunc->nname = newname(lookup(namebuf));
|
|
xfunc->nname->sym->flags |= SymExported; // disable export
|
|
xfunc->nname->ntype = xtype;
|
|
xfunc->nname->defn = xfunc;
|
|
declare(xfunc->nname, PFUNC);
|
|
xfunc->nname->funcdepth = func->funcdepth;
|
|
xfunc->funcdepth = func->funcdepth;
|
|
xfunc->endlineno = func->endlineno;
|
|
|
|
// declare variables holding addresses taken from closure
|
|
// and initialize in entry prologue.
|
|
body = nil;
|
|
offset = widthptr;
|
|
xfunc->needctxt = func->cvars != nil;
|
|
for(l=func->cvars; l; l=l->next) {
|
|
v = l->n;
|
|
if(v->op == 0)
|
|
continue;
|
|
addr = nod(ONAME, N, N);
|
|
p = smprint("&%s", v->sym->name);
|
|
addr->sym = lookup(p);
|
|
free(p);
|
|
addr->ntype = nod(OIND, typenod(v->type), N);
|
|
addr->class = PAUTO;
|
|
addr->addable = 1;
|
|
addr->ullman = 1;
|
|
addr->used = 1;
|
|
addr->curfn = xfunc;
|
|
xfunc->dcl = list(xfunc->dcl, addr);
|
|
v->heapaddr = addr;
|
|
cv = nod(OCLOSUREVAR, N, N);
|
|
cv->type = ptrto(v->type);
|
|
cv->xoffset = offset;
|
|
body = list(body, nod(OAS, addr, cv));
|
|
offset += widthptr;
|
|
}
|
|
typechecklist(body, Etop);
|
|
walkstmtlist(body);
|
|
xfunc->enter = body;
|
|
|
|
xfunc->nbody = func->nbody;
|
|
xfunc->dcl = concat(func->dcl, xfunc->dcl);
|
|
if(xfunc->nbody == nil)
|
|
fatal("empty body - won't generate any code");
|
|
typecheck(&xfunc, Etop);
|
|
|
|
xfunc->closure = func;
|
|
func->closure = xfunc;
|
|
|
|
func->nbody = nil;
|
|
func->list = nil;
|
|
func->rlist = nil;
|
|
|
|
return xfunc;
|
|
}
|
|
|
|
Node*
|
|
walkclosure(Node *func, NodeList **init)
|
|
{
|
|
Node *clos, *typ;
|
|
NodeList *l;
|
|
char buf[20];
|
|
int narg;
|
|
|
|
// If no closure vars, don't bother wrapping.
|
|
if(func->cvars == nil)
|
|
return func->closure->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; A0 *int; A1 *string}{func·001, &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.
|
|
|
|
narg = 0;
|
|
typ = nod(OTSTRUCT, N, N);
|
|
typ->list = list1(nod(ODCLFIELD, newname(lookup("F")), typenod(types[TUINTPTR])));
|
|
for(l=func->cvars; l; l=l->next) {
|
|
if(l->n->op == 0)
|
|
continue;
|
|
snprint(buf, sizeof buf, "A%d", narg++);
|
|
typ->list = list(typ->list, nod(ODCLFIELD, newname(lookup(buf)), l->n->heapaddr->ntype));
|
|
}
|
|
|
|
clos = nod(OCOMPLIT, N, nod(OIND, typ, N));
|
|
clos->esc = func->esc;
|
|
clos->right->implicit = 1;
|
|
clos->list = concat(list1(nod(OCFUNC, func->closure->nname, N)), func->enter);
|
|
|
|
// Force type conversion from *struct to the func type.
|
|
clos = nod(OCONVNOP, clos, N);
|
|
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(func->alloc != N) {
|
|
func->alloc->type = clos->left->left->type;
|
|
func->alloc->orig->type = func->alloc->type;
|
|
clos->left->right = func->alloc;
|
|
func->alloc = N;
|
|
}
|
|
walkexpr(&clos, init);
|
|
|
|
return clos;
|
|
}
|
|
|
|
static Node *makepartialcall(Node*, Type*, Node*);
|
|
|
|
void
|
|
typecheckpartialcall(Node *fn, Node *sym)
|
|
{
|
|
switch(fn->op) {
|
|
case ODOTINTER:
|
|
case ODOTMETH:
|
|
break;
|
|
default:
|
|
fatal("invalid typecheckpartialcall");
|
|
}
|
|
|
|
// Create top-level function.
|
|
fn->nname = makepartialcall(fn, fn->type, sym);
|
|
fn->right = sym;
|
|
fn->op = OCALLPART;
|
|
fn->type = fn->nname->type;
|
|
}
|
|
|
|
static Node*
|
|
makepartialcall(Node *fn, Type *t0, Node *meth)
|
|
{
|
|
Node *ptr, *n, *fld, *call, *xtype, *xfunc, *cv, *savecurfn;
|
|
Type *rcvrtype, *basetype, *t;
|
|
NodeList *body, *l, *callargs, *retargs;
|
|
char *p;
|
|
Sym *sym;
|
|
Pkg *spkg;
|
|
static Pkg* gopkg;
|
|
int i, ddd;
|
|
|
|
// TODO: names are not right
|
|
rcvrtype = fn->left->type;
|
|
if(exportname(meth->sym->name))
|
|
p = smprint("%-hT.%s·fm", rcvrtype, meth->sym->name);
|
|
else
|
|
p = smprint("%-hT.(%-S)·fm", rcvrtype, meth->sym);
|
|
basetype = rcvrtype;
|
|
if(isptr[rcvrtype->etype])
|
|
basetype = basetype->type;
|
|
if(basetype->etype != TINTER && basetype->sym == S)
|
|
fatal("missing base type for %T", rcvrtype);
|
|
|
|
spkg = nil;
|
|
if(basetype->sym != S)
|
|
spkg = basetype->sym->pkg;
|
|
if(spkg == nil) {
|
|
if(gopkg == nil)
|
|
gopkg = mkpkg(strlit("go"));
|
|
spkg = gopkg;
|
|
}
|
|
sym = pkglookup(p, spkg);
|
|
free(p);
|
|
if(sym->flags & SymUniq)
|
|
return sym->def;
|
|
sym->flags |= SymUniq;
|
|
|
|
savecurfn = curfn;
|
|
curfn = N;
|
|
|
|
xtype = nod(OTFUNC, N, N);
|
|
i = 0;
|
|
l = nil;
|
|
callargs = nil;
|
|
ddd = 0;
|
|
xfunc = nod(ODCLFUNC, N, N);
|
|
curfn = xfunc;
|
|
for(t = getinargx(t0)->type; t; t = t->down) {
|
|
snprint(namebuf, sizeof namebuf, "a%d", i++);
|
|
n = newname(lookup(namebuf));
|
|
n->class = PPARAM;
|
|
xfunc->dcl = list(xfunc->dcl, n);
|
|
callargs = list(callargs, n);
|
|
fld = nod(ODCLFIELD, n, typenod(t->type));
|
|
if(t->isddd) {
|
|
fld->isddd = 1;
|
|
ddd = 1;
|
|
}
|
|
l = list(l, fld);
|
|
}
|
|
xtype->list = l;
|
|
i = 0;
|
|
l = nil;
|
|
retargs = nil;
|
|
for(t = getoutargx(t0)->type; t; t = t->down) {
|
|
snprint(namebuf, sizeof namebuf, "r%d", i++);
|
|
n = newname(lookup(namebuf));
|
|
n->class = PPARAMOUT;
|
|
xfunc->dcl = list(xfunc->dcl, n);
|
|
retargs = list(retargs, n);
|
|
l = list(l, nod(ODCLFIELD, n, typenod(t->type)));
|
|
}
|
|
xtype->rlist = l;
|
|
|
|
xfunc->dupok = 1;
|
|
xfunc->nname = newname(sym);
|
|
xfunc->nname->sym->flags |= SymExported; // disable export
|
|
xfunc->nname->ntype = xtype;
|
|
xfunc->nname->defn = xfunc;
|
|
declare(xfunc->nname, PFUNC);
|
|
|
|
// Declare and initialize variable holding receiver.
|
|
body = nil;
|
|
xfunc->needctxt = 1;
|
|
cv = nod(OCLOSUREVAR, N, N);
|
|
cv->xoffset = widthptr;
|
|
cv->type = rcvrtype;
|
|
if(cv->type->align > widthptr)
|
|
cv->xoffset = cv->type->align;
|
|
ptr = nod(ONAME, N, N);
|
|
ptr->sym = lookup("rcvr");
|
|
ptr->class = PAUTO;
|
|
ptr->addable = 1;
|
|
ptr->ullman = 1;
|
|
ptr->used = 1;
|
|
ptr->curfn = xfunc;
|
|
xfunc->dcl = list(xfunc->dcl, ptr);
|
|
if(isptr[rcvrtype->etype] || isinter(rcvrtype)) {
|
|
ptr->ntype = typenod(rcvrtype);
|
|
body = list(body, nod(OAS, ptr, cv));
|
|
} else {
|
|
ptr->ntype = typenod(ptrto(rcvrtype));
|
|
body = list(body, nod(OAS, ptr, nod(OADDR, cv, N)));
|
|
}
|
|
|
|
call = nod(OCALL, nod(OXDOT, ptr, meth), N);
|
|
call->list = callargs;
|
|
call->isddd = ddd;
|
|
if(t0->outtuple == 0) {
|
|
body = list(body, call);
|
|
} else {
|
|
n = nod(OAS2, N, N);
|
|
n->list = retargs;
|
|
n->rlist = list1(call);
|
|
body = list(body, n);
|
|
n = nod(ORETURN, N, N);
|
|
body = list(body, n);
|
|
}
|
|
|
|
xfunc->nbody = body;
|
|
|
|
typecheck(&xfunc, Etop);
|
|
sym->def = xfunc;
|
|
xtop = list(xtop, xfunc);
|
|
curfn = savecurfn;
|
|
|
|
return xfunc;
|
|
}
|
|
|
|
Node*
|
|
walkpartialcall(Node *n, NodeList **init)
|
|
{
|
|
Node *clos, *typ;
|
|
|
|
// 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, N, N);
|
|
typ->list = list1(nod(ODCLFIELD, newname(lookup("F")), typenod(types[TUINTPTR])));
|
|
typ->list = list(typ->list, nod(ODCLFIELD, newname(lookup("R")), typenod(n->left->type)));
|
|
|
|
clos = nod(OCOMPLIT, N, nod(OIND, typ, N));
|
|
clos->esc = n->esc;
|
|
clos->right->implicit = 1;
|
|
clos->list = list1(nod(OCFUNC, n->nname->nname, N));
|
|
clos->list = list(clos->list, n->left);
|
|
|
|
// Force type conversion from *struct to the func type.
|
|
clos = nod(OCONVNOP, clos, N);
|
|
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(n->alloc != N) {
|
|
n->alloc->type = clos->left->left->type;
|
|
n->alloc->orig->type = n->alloc->type;
|
|
clos->left->right = n->alloc;
|
|
n->alloc = N;
|
|
}
|
|
walkexpr(&clos, init);
|
|
|
|
return clos;
|
|
}
|