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 }