// 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 #include #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, int nowrap); 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, func->cvars==nil || (top&Ecall))); } static Node* makeclosure(Node *func, int nowrap) { Node *xtype, *v, *addr, *xfunc, *cv; NodeList *l, *body; static int closgen; char *p; int 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; 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; 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, *call, *xtype, *xfunc, *cv; Type *rcvrtype, *basetype, *t; NodeList *body, *l, *callargs, *retargs; char *p; Sym *sym; int i; // 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->sym == S) fatal("missing base type for %T", rcvrtype); sym = pkglookup(p, basetype->sym->pkg); free(p); if(sym->flags & SymUniq) return sym->def; sym->flags |= SymUniq; xtype = nod(OTFUNC, N, N); i = 0; l = nil; callargs = nil; xfunc = nod(ODCLFUNC, N, N); 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); l = list(l, nod(ODCLFIELD, n, typenod(t->type))); } 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; cv = nod(OCLOSUREVAR, N, N); cv->xoffset = widthptr; cv->type = rcvrtype; 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; 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); 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)) { n->left = cheapexpr(n->left, init); checknotnil(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; walkexpr(&clos, init); return clos; }