The Go Programming Language

Text file src/cmd/gc/closure.c

     1	// Copyright 2009 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	/*
     6	 * function literals aka closures
     7	 */
     8	
     9	#include "go.h"
    10	
    11	void
    12	closurehdr(Node *ntype)
    13	{
    14		Node *n, *name, *a;
    15		NodeList *l;
    16	
    17		n = nod(OCLOSURE, N, N);
    18		n->ntype = ntype;
    19		n->funcdepth = funcdepth;
    20	
    21		funchdr(n);
    22	
    23		// steal ntype's argument names and
    24		// leave a fresh copy in their place.
    25		// references to these variables need to
    26		// refer to the variables in the external
    27		// function declared below; see walkclosure.
    28		n->list = ntype->list;
    29		n->rlist = ntype->rlist;
    30		ntype->list = nil;
    31		ntype->rlist = nil;
    32		for(l=n->list; l; l=l->next) {
    33			name = l->n->left;
    34			if(name)
    35				name = newname(name->sym);
    36			a = nod(ODCLFIELD, name, l->n->right);
    37			a->isddd = l->n->isddd;
    38			if(name)
    39				name->isddd = a->isddd;
    40			ntype->list = list(ntype->list, a);
    41		}
    42		for(l=n->rlist; l; l=l->next) {
    43			name = l->n->left;
    44			if(name)
    45				name = newname(name->sym);
    46			ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, name, l->n->right));
    47		}
    48	}
    49	
    50	Node*
    51	closurebody(NodeList *body)
    52	{
    53		Node *func, *v;
    54		NodeList *l;
    55	
    56		if(body == nil)
    57			body = list1(nod(OEMPTY, N, N));
    58	
    59		func = curfn;
    60		l = func->dcl;
    61		func->nbody = body;
    62		funcbody(func);
    63	
    64		// closure-specific variables are hanging off the
    65		// ordinary ones in the symbol table; see oldname.
    66		// unhook them.
    67		// make the list of pointers for the closure call.
    68		for(l=func->cvars; l; l=l->next) {
    69			v = l->n;
    70			v->closure->closure = v->outer;
    71			v->heapaddr = nod(OADDR, oldname(v->sym), N);
    72		}
    73	
    74		return func;
    75	}
    76	
    77	void
    78	typecheckclosure(Node *func, int top)
    79	{
    80		Node *oldfn;
    81		NodeList *l;
    82		Node *v;
    83	
    84		oldfn = curfn;
    85		typecheck(&func->ntype, Etype);
    86		func->type = func->ntype->type;
    87		if(curfn == nil) {
    88			xtop = list(xtop, func);
    89			return;
    90		}
    91	
    92		if(func->type != T) {
    93			curfn = func;
    94			typechecklist(func->nbody, Etop);
    95			curfn = oldfn;
    96		}
    97	
    98		// type check the & of closed variables outside the closure,
    99		// so that the outer frame also grabs them and knows they
   100		// escape.
   101		func->enter = nil;
   102		for(l=func->cvars; l; l=l->next) {
   103			v = l->n;
   104			if(v->type == T) {
   105				// if v->type is nil, it means v looked like it was
   106				// going to be used in the closure but wasn't.
   107				// this happens because when parsing a, b, c := f()
   108				// the a, b, c gets parsed as references to older
   109				// a, b, c before the parser figures out this is a
   110				// declaration.
   111				v->op = 0;
   112				continue;
   113			}
   114			// For a closure that is called in place, but not
   115			// inside a go statement, avoid moving variables to the heap.
   116			if ((top & (Ecall|Eproc)) == Ecall)
   117				v->heapaddr->etype = 1;
   118			typecheck(&v->heapaddr, Erv);
   119			func->enter = list(func->enter, v->heapaddr);
   120			v->heapaddr = N;
   121		}
   122	}
   123	
   124	static Node*
   125	makeclosure(Node *func, NodeList **init, int nowrap)
   126	{
   127		Node *xtype, *v, *addr, *xfunc;
   128		NodeList *l;
   129		static int closgen;
   130		char *p;
   131	
   132		/*
   133		 * wrap body in external function
   134		 * with extra closure parameters.
   135		 */
   136		xtype = nod(OTFUNC, N, N);
   137	
   138		// each closure variable has a corresponding
   139		// address parameter.
   140		for(l=func->cvars; l; l=l->next) {
   141			v = l->n;
   142			if(v->op == 0)
   143				continue;
   144			addr = nod(ONAME, N, N);
   145			p = smprint("&%s", v->sym->name);
   146			addr->sym = lookup(p);
   147			free(p);
   148			addr->ntype = nod(OIND, typenod(v->type), N);
   149			addr->class = PPARAM;
   150			addr->addable = 1;
   151			addr->ullman = 1;
   152	
   153			v->heapaddr = addr;
   154	
   155			xtype->list = list(xtype->list, nod(ODCLFIELD, addr, addr->ntype));
   156		}
   157	
   158		// then a dummy arg where the closure's caller pc sits
   159		if (!nowrap)
   160			xtype->list = list(xtype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
   161	
   162		// then the function arguments
   163		xtype->list = concat(xtype->list, func->list);
   164		xtype->rlist = concat(xtype->rlist, func->rlist);
   165	
   166		// create the function
   167		xfunc = nod(ODCLFUNC, N, N);
   168		snprint(namebuf, sizeof namebuf, "_func_%.3d", ++closgen);
   169		xfunc->nname = newname(lookup(namebuf));
   170		xfunc->nname->ntype = xtype;
   171		xfunc->nname->defn = xfunc;
   172		declare(xfunc->nname, PFUNC);
   173		xfunc->nname->funcdepth = func->funcdepth;
   174		xfunc->funcdepth = func->funcdepth;
   175		xfunc->nbody = func->nbody;
   176		xfunc->dcl = func->dcl;
   177		if(xfunc->nbody == nil)
   178			fatal("empty body - won't generate any code");
   179		typecheck(&xfunc, Etop);
   180		closures = list(closures, xfunc);
   181	
   182		return xfunc;
   183	}
   184	
   185	Node*
   186	walkclosure(Node *func, NodeList **init)
   187	{
   188		int narg;
   189		Node *xtype, *xfunc, *call, *clos;
   190		NodeList *l, *in;
   191	
   192		/*
   193		 * wrap body in external function
   194		 * with extra closure parameters.
   195		 */
   196	
   197		// create the function
   198		xfunc = makeclosure(func, init, 0);
   199		xtype = xfunc->nname->ntype;
   200	
   201		// prepare call of sys.closure that turns external func into func literal value.
   202		clos = syslook("closure", 1);
   203		clos->type = T;
   204		clos->ntype = nod(OTFUNC, N, N);
   205		in = list1(nod(ODCLFIELD, N, typenod(types[TINT])));	// siz
   206		in = list(in, nod(ODCLFIELD, N, xtype));
   207		narg = 0;
   208		for(l=func->cvars; l; l=l->next) {
   209			if(l->n->op == 0)
   210				continue;
   211			narg++;
   212			in = list(in, nod(ODCLFIELD, N, l->n->heapaddr->ntype));
   213		}
   214		clos->ntype->list = in;
   215		clos->ntype->rlist = list1(nod(ODCLFIELD, N, typenod(func->type)));
   216		typecheck(&clos, Erv);
   217	
   218		call = nod(OCALL, clos, N);
   219		if(narg*widthptr > 100)
   220			yyerror("closure needs too many variables; runtime will reject it");
   221		in = list1(nodintconst(narg*widthptr));
   222		in = list(in, xfunc->nname);
   223		in = concat(in, func->enter);
   224		call->list = in;
   225	
   226		typecheck(&call, Erv);
   227		walkexpr(&call, init);
   228		return call;
   229	}
   230	
   231	// Special case for closures that get called in place.
   232	// Optimize runtime.closure(X, __func__xxxx_, .... ) away
   233	// to __func__xxxx_(Y ....).
   234	// On entry, expect n->op == OCALL, n->left->op == OCLOSURE.
   235	void
   236	walkcallclosure(Node *n, NodeList **init)
   237	{
   238		if (n->op != OCALLFUNC || n->left->op != OCLOSURE) {
   239			dump("walkcallclosure", n);
   240			fatal("abuse of walkcallclosure");
   241		}
   242	
   243		// New arg list for n. First the closure-args
   244		// and then the original parameter list.
   245		n->list = concat(n->left->enter, n->list);
   246		n->left = makeclosure(n->left, init, 1)->nname;
   247		dowidth(n->left->type);
   248		n->type = getoutargx(n->left->type);
   249		// for a single valued function, pull the field type out of the struct
   250		if (n->type && n->type->type && !n->type->type->down)
   251			n->type = n->type->type->type;
   252	}

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.