blob: d29e8cbc287ab17344dd754ebe736ff04617a805 [file] [log] [blame]
// 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;
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;
}
void
typecheckclosure(Node *func, int top)
{
Node *oldfn;
NodeList *l;
Node *v;
oldfn = curfn;
typecheck(&func->ntype, Etype);
func->type = func->ntype->type;
if(curfn == nil) {
xtop = list(xtop, func);
return;
}
if(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;
}
}
static Node*
makeclosure(Node *func, NodeList **init, int nowrap)
{
Node *xtype, *v, *addr, *xfunc;
NodeList *l;
static int closgen;
char *p;
USED(init);
/*
* wrap body in external function
* with extra closure parameters.
*/
xtype = nod(OTFUNC, N, N);
// each closure variable has a corresponding
// address parameter.
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 = PPARAM;
addr->addable = 1;
addr->ullman = 1;
v->heapaddr = addr;
xtype->list = list(xtype->list, nod(ODCLFIELD, addr, addr->ntype));
}
// then a dummy arg where the closure's caller pc sits
if (!nowrap)
xtype->list = list(xtype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
// then the function arguments
xtype->list = concat(xtype->list, func->list);
xtype->rlist = concat(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->ntype = xtype;
xfunc->nname->defn = xfunc;
declare(xfunc->nname, PFUNC);
xfunc->nname->funcdepth = func->funcdepth;
xfunc->funcdepth = func->funcdepth;
xfunc->nbody = func->nbody;
xfunc->dcl = func->dcl;
if(xfunc->nbody == nil)
fatal("empty body - won't generate any code");
typecheck(&xfunc, Etop);
closures = list(closures, xfunc);
return xfunc;
}
Node*
walkclosure(Node *func, NodeList **init)
{
int narg;
Node *xtype, *xfunc, *call, *clos;
NodeList *l, *in;
/*
* wrap body in external function
* with extra closure parameters.
*/
// create the function
xfunc = makeclosure(func, init, 0);
xtype = xfunc->nname->ntype;
// prepare call of sys.closure that turns external func into func literal value.
clos = syslook("closure", 1);
clos->type = T;
clos->ntype = nod(OTFUNC, N, N);
in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // siz
in = list(in, nod(ODCLFIELD, N, xtype));
narg = 0;
for(l=func->cvars; l; l=l->next) {
if(l->n->op == 0)
continue;
narg++;
in = list(in, nod(ODCLFIELD, N, l->n->heapaddr->ntype));
}
clos->ntype->list = in;
clos->ntype->rlist = list1(nod(ODCLFIELD, N, typenod(func->type)));
typecheck(&clos, Erv);
call = nod(OCALL, clos, N);
if(narg*widthptr > 100)
yyerror("closure needs too many variables; runtime will reject it");
in = list1(nodintconst(narg*widthptr));
in = list(in, xfunc->nname);
in = concat(in, func->enter);
call->list = in;
typecheck(&call, Erv);
walkexpr(&call, init);
return call;
}
// Special case for closures that get called in place.
// Optimize runtime.closure(X, __func__xxxx_, .... ) away
// to __func__xxxx_(Y ....).
// On entry, expect n->op == OCALL, n->left->op == OCLOSURE.
void
walkcallclosure(Node *n, NodeList **init)
{
if (n->op != OCALLFUNC || n->left->op != OCLOSURE) {
dump("walkcallclosure", n);
fatal("abuse of walkcallclosure");
}
// New arg list for n. First the closure-args
// and then the original parameter list.
n->list = concat(n->left->enter, n->list);
n->left = makeclosure(n->left, init, 1)->nname;
dowidth(n->left->type);
n->type = getoutargx(n->left->type);
// for a single valued function, pull the field type out of the struct
if (n->type && n->type->type && !n->type->type->down)
n->type = n->type->type->type;
}