blob: 2f102993c6fa9b2aa7b604e7ca313ac07b32cd68 [file] [log] [blame]
Russ Coxb6487162009-08-07 12:50:26 -07001// 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
11void
12closurehdr(Node *ntype)
13{
14 Node *n, *name;
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 ntype->list = list(ntype->list, nod(ODCLFIELD, name, l->n->right));
37 }
38 for(l=n->rlist; l; l=l->next) {
39 name = l->n->left;
40 if(name)
41 name = newname(name->sym);
42 ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, name, l->n->right));
43 }
44}
45
46Node*
47closurebody(NodeList *body)
48{
49 Node *func, *v;
50 NodeList *l;
51
52 if(body == nil)
53 body = list1(nod(OEMPTY, N, N));
54
55 func = curfn;
56 l = func->dcl;
57 func->nbody = body;
58 funcbody(func);
59
60 // closure-specific variables are hanging off the
61 // ordinary ones in the symbol table; see oldname.
62 // unhook them.
63 // make the list of pointers for the closure call.
64 for(l=func->cvars; l; l=l->next) {
65 v = l->n;
66 v->closure->closure = v->outer;
67 v->heapaddr = nod(OADDR, oldname(v->sym), N);
68 }
69
70 return func;
71}
72
73void
74typecheckclosure(Node *func)
75{
76 Node *oldfn;
77 NodeList *l;
78 Node *v;
79
80 oldfn = curfn;
81 typecheck(&func->ntype, Etype);
82 func->type = func->ntype->type;
83 if(func->type != T) {
84 curfn = func;
85 typechecklist(func->nbody, Etop);
86 curfn = oldfn;
87 }
88
89 // type check the & of closed variables outside the closure,
90 // so that the outer frame also grabs them and knows they
91 // escape.
92 func->enter = nil;
93 for(l=func->cvars; l; l=l->next) {
94 v = l->n;
95 if(v->type == T) {
96 // if v->type is nil, it means v looked like it was
97 // going to be used in the closure but wasn't.
98 // this happens because when parsing a, b, c := f()
99 // the a, b, c gets parsed as references to older
100 // a, b, c before the parser figures out this is a
101 // declaration.
102 v->op = 0;
103 continue;
104 }
105 typecheck(&v->heapaddr, Erv);
106 func->enter = list(func->enter, v->heapaddr);
107 v->heapaddr = N;
108 }
109}
110
111Node*
112walkclosure(Node *func, NodeList **init)
113{
114 int narg;
115 Node *xtype, *v, *addr, *xfunc, *call, *clos;
116 NodeList *l, *in;
117 static int closgen;
118
119 /*
120 * wrap body in external function
121 * with extra closure parameters.
122 */
123 xtype = nod(OTFUNC, N, N);
124
125 // each closure variable has a corresponding
126 // address parameter.
127 narg = 0;
128 for(l=func->cvars; l; l=l->next) {
129 v = l->n;
130 if(v->op == 0)
131 continue;
132 addr = nod(ONAME, N, N);
133 snprint(namebuf, sizeof namebuf, "&%s", v->sym->name);
134 addr->sym = lookup(namebuf);
135 addr->ntype = nod(OIND, typenod(v->type), N);
136 addr->class = PPARAM;
137 addr->addable = 1;
138 addr->ullman = 1;
139 narg++;
140
141 v->heapaddr = addr;
142
143 xtype->list = list(xtype->list, nod(ODCLFIELD, addr, addr->ntype));
144 }
145
146 // then a dummy arg where the closure's caller pc sits
147 xtype->list = list(xtype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
148
149 // then the function arguments
150 xtype->list = concat(xtype->list, func->list);
151 xtype->rlist = concat(xtype->rlist, func->rlist);
152
153 // create the function
154 xfunc = nod(ODCLFUNC, N, N);
Russ Cox107d4042009-09-03 15:23:21 -0700155 snprint(namebuf, sizeof namebuf, "_f%.3ld", ++closgen);
Russ Coxb6487162009-08-07 12:50:26 -0700156 xfunc->nname = newname(lookup(namebuf));
157 xfunc->nname->ntype = xtype;
158 declare(xfunc->nname, PFUNC);
159 xfunc->nname->funcdepth = func->funcdepth;
160 xfunc->funcdepth = func->funcdepth;
161 xfunc->nbody = func->nbody;
162 xfunc->dcl = func->dcl;
163 if(xfunc->nbody == nil)
164 fatal("empty body - won't generate any code");
165 typecheck(&xfunc, Etop);
166 closures = list(closures, xfunc);
167
168 // prepare call of sys.closure that turns external func into func literal value.
169 clos = syslook("closure", 1);
170 clos->type = T;
171 clos->ntype = nod(OTFUNC, N, N);
172 in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // siz
173 in = list(in, nod(ODCLFIELD, N, xtype));
174 for(l=func->cvars; l; l=l->next) {
175 if(l->n->op == 0)
176 continue;
177 in = list(in, nod(ODCLFIELD, N, l->n->heapaddr->ntype));
178 }
179 clos->ntype->list = in;
180 clos->ntype->rlist = list1(nod(ODCLFIELD, N, typenod(func->type)));
181 typecheck(&clos, Erv);
182
183 call = nod(OCALL, clos, N);
184 if(narg*widthptr > 100)
185 yyerror("closure needs too many variables; runtime will reject it");
186 in = list1(nodintconst(narg*widthptr));
187 in = list(in, xfunc->nname);
188 in = concat(in, func->enter);
189 call->list = in;
190
191 typecheck(&call, Erv);
192 walkexpr(&call, init);
193 return call;
194}