root/src/cmd/gc/closure.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. closurehdr
  2. closurebody
  3. typecheckclosure
  4. makeclosure
  5. walkclosure
  6. typecheckpartialcall
  7. makepartialcall
  8. walkpartialcall

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

/* [<][>][^][v][top][bottom][index][help] */