| // 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; |
| } |