]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/d/dmd/delegatize.d
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
[thirdparty/gcc.git] / gcc / d / dmd / delegatize.d
1 /**
2 * Implements conversion from expressions to delegates for lazy parameters.
3 *
4 * Specification: $(LINK2 https://dlang.org/spec/function.html#lazy-params, Lazy Parameters)
5 *
6 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/delegatize.d, _delegatize.d)
10 * Documentation: https://dlang.org/phobos/dmd_delegatize.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/delegatize.d
12 */
13
14 module dmd.delegatize;
15
16 import core.stdc.stdio;
17 import dmd.apply;
18 import dmd.astenums;
19 import dmd.declaration;
20 import dmd.dscope;
21 import dmd.dsymbol;
22 import dmd.expression;
23 import dmd.expressionsem;
24 import dmd.func;
25 import dmd.globals;
26 import dmd.init;
27 import dmd.initsem;
28 import dmd.mtype;
29 import dmd.statement;
30 import dmd.tokens;
31 import dmd.visitor;
32
33
34 /*********************************
35 * Convert expression into a delegate.
36 *
37 * Used to convert the argument to a lazy parameter.
38 *
39 * Params:
40 * e = argument to convert to a delegate
41 * t = the type to be returned by the delegate
42 * sc = context
43 * Returns:
44 * A delegate literal
45 */
46 Expression toDelegate(Expression e, Type t, Scope* sc)
47 {
48 //printf("Expression::toDelegate(t = %s) %s\n", t.toChars(), e.toChars());
49 Loc loc = e.loc;
50 auto tf = new TypeFunction(ParameterList(), t, LINK.d);
51 if (t.hasWild())
52 tf.mod = MODFlags.wild;
53 auto fld = new FuncLiteralDeclaration(loc, loc, tf, TOK.delegate_, null);
54 lambdaSetParent(e, fld);
55
56 sc = sc.push();
57 sc.parent = fld; // set current function to be the delegate
58 bool r = lambdaCheckForNestedRef(e, sc);
59 sc = sc.pop();
60 if (r)
61 return ErrorExp.get();
62
63 Statement s;
64 if (t.ty == Tvoid)
65 s = new ExpStatement(loc, e);
66 else
67 s = new ReturnStatement(loc, e);
68 fld.fbody = s;
69 e = new FuncExp(loc, fld);
70 e = e.expressionSemantic(sc);
71 return e;
72 }
73
74 /******************************************
75 * Patch the parent of declarations to be the new function literal.
76 *
77 * Since the expression is going to be moved into a function literal,
78 * the parent for declarations in the expression needs to be
79 * reset to that function literal.
80 * Params:
81 * e = expression to check
82 * fd = function literal symbol (the new parent)
83 */
84 private void lambdaSetParent(Expression e, FuncDeclaration fd)
85 {
86 extern (C++) final class LambdaSetParent : StoppableVisitor
87 {
88 alias visit = typeof(super).visit;
89 FuncDeclaration fd;
90
91 private void setParent(Dsymbol s)
92 {
93 VarDeclaration vd = s.isVarDeclaration();
94 FuncDeclaration pfd = s.parent ? s.parent.isFuncDeclaration() : null;
95 s.parent = fd;
96 if (!vd || !pfd)
97 return;
98 // move to fd's closure when applicable
99 foreach (i; 0 .. pfd.closureVars.dim)
100 {
101 if (vd == pfd.closureVars[i])
102 {
103 pfd.closureVars.remove(i);
104 fd.closureVars.push(vd);
105 break;
106 }
107 }
108 }
109
110 public:
111 extern (D) this(FuncDeclaration fd)
112 {
113 this.fd = fd;
114 }
115
116 override void visit(Expression)
117 {
118 }
119
120 override void visit(DeclarationExp e)
121 {
122 setParent(e.declaration);
123 e.declaration.accept(this);
124 }
125
126 override void visit(IndexExp e)
127 {
128 if (e.lengthVar)
129 {
130 //printf("lengthVar\n");
131 setParent(e.lengthVar);
132 e.lengthVar.accept(this);
133 }
134 }
135
136 override void visit(SliceExp e)
137 {
138 if (e.lengthVar)
139 {
140 //printf("lengthVar\n");
141 setParent(e.lengthVar);
142 e.lengthVar.accept(this);
143 }
144 }
145
146 override void visit(Dsymbol)
147 {
148 }
149
150 override void visit(VarDeclaration v)
151 {
152 if (v._init)
153 v._init.accept(this);
154 }
155
156 override void visit(Initializer)
157 {
158 }
159
160 override void visit(ExpInitializer ei)
161 {
162 walkPostorder(ei.exp ,this);
163 }
164
165 override void visit(StructInitializer si)
166 {
167 foreach (i, const id; si.field)
168 if (Initializer iz = si.value[i])
169 iz.accept(this);
170 }
171
172 override void visit(ArrayInitializer ai)
173 {
174 foreach (i, ex; ai.index)
175 {
176 if (ex)
177 walkPostorder(ex, this);
178 if (Initializer iz = ai.value[i])
179 iz.accept(this);
180 }
181 }
182 }
183
184 scope LambdaSetParent lsp = new LambdaSetParent(fd);
185 walkPostorder(e, lsp);
186 }
187
188 /*******************************************
189 * Look for references to variables in a scope enclosing the new function literal.
190 *
191 * Essentially just calls `checkNestedReference() for each variable reference in `e`.
192 * Params:
193 * sc = context
194 * e = expression to check
195 * Returns:
196 * true if error occurs.
197 */
198 bool lambdaCheckForNestedRef(Expression e, Scope* sc)
199 {
200 extern (C++) final class LambdaCheckForNestedRef : StoppableVisitor
201 {
202 alias visit = typeof(super).visit;
203 public:
204 Scope* sc;
205 bool result;
206
207 extern (D) this(Scope* sc)
208 {
209 this.sc = sc;
210 }
211
212 override void visit(Expression)
213 {
214 }
215
216 override void visit(SymOffExp e)
217 {
218 VarDeclaration v = e.var.isVarDeclaration();
219 if (v)
220 result = v.checkNestedReference(sc, Loc.initial);
221 }
222
223 override void visit(VarExp e)
224 {
225 VarDeclaration v = e.var.isVarDeclaration();
226 if (v)
227 result = v.checkNestedReference(sc, Loc.initial);
228 }
229
230 override void visit(ThisExp e)
231 {
232 if (e.var)
233 result = e.var.checkNestedReference(sc, Loc.initial);
234 }
235
236 override void visit(DeclarationExp e)
237 {
238 VarDeclaration v = e.declaration.isVarDeclaration();
239 if (v)
240 {
241 result = v.checkNestedReference(sc, Loc.initial);
242 if (result)
243 return;
244 /* Some expressions cause the frontend to create a temporary.
245 * For example, structs with cpctors replace the original
246 * expression e with:
247 * __cpcttmp = __cpcttmp.cpctor(e);
248 *
249 * In this instance, we need to ensure that the original
250 * expression e does not have any nested references by
251 * checking the declaration initializer too.
252 */
253 if (v._init && v._init.isExpInitializer())
254 {
255 Expression ie = v._init.initializerToExpression();
256 result = lambdaCheckForNestedRef(ie, sc);
257 }
258 }
259 }
260 }
261
262 scope LambdaCheckForNestedRef v = new LambdaCheckForNestedRef(sc);
263 walkPostorder(e, v);
264 return v.result;
265 }
266
267 /*****************************************
268 * See if context `s` is nested within context `p`, meaning
269 * it `p` is reachable at runtime by walking the static links.
270 * If any of the intervening contexts are function literals,
271 * make sure they are delegates.
272 * Params:
273 * s = inner context
274 * p = outer context
275 * Returns:
276 * true means it is accessible by walking the context pointers at runtime
277 * References:
278 * for static links see https://en.wikipedia.org/wiki/Call_stack#Functions_of_the_call_stack
279 */
280 bool ensureStaticLinkTo(Dsymbol s, Dsymbol p)
281 {
282 while (s)
283 {
284 if (s == p) // hit!
285 return true;
286
287 if (auto fd = s.isFuncDeclaration())
288 {
289 if (!fd.isThis() && !fd.isNested())
290 break;
291
292 // https://issues.dlang.org/show_bug.cgi?id=15332
293 // change to delegate if fd is actually nested.
294 if (auto fld = fd.isFuncLiteralDeclaration())
295 fld.tok = TOK.delegate_;
296 }
297 if (auto ad = s.isAggregateDeclaration())
298 {
299 if (ad.storage_class & STC.static_)
300 break;
301 }
302 s = s.toParentP(p);
303 }
304 return false;
305 }