2 * Implements conversion from expressions to delegates for lazy parameters.
4 * Specification: $(LINK2 https://dlang.org/spec/function.html#lazy-params, Lazy Parameters)
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
14 module dmd.delegatize;
16 import core.stdc.stdio;
19 import dmd.declaration;
22 import dmd.expression;
23 import dmd.expressionsem;
34 /*********************************
35 * Convert expression into a delegate.
37 * Used to convert the argument to a lazy parameter.
40 * e = argument to convert to a delegate
41 * t = the type to be returned by the delegate
46 Expression toDelegate(Expression e, Type t, Scope* sc)
48 //printf("Expression::toDelegate(t = %s) %s\n", t.toChars(), e.toChars());
50 auto tf = new TypeFunction(ParameterList(), t, LINK.d);
52 tf.mod = MODFlags.wild;
53 auto fld = new FuncLiteralDeclaration(loc, loc, tf, TOK.delegate_, null);
54 lambdaSetParent(e, fld);
57 sc.parent = fld; // set current function to be the delegate
58 bool r = lambdaCheckForNestedRef(e, sc);
61 return ErrorExp.get();
65 s = new ExpStatement(loc, e);
67 s = new ReturnStatement(loc, e);
69 e = new FuncExp(loc, fld);
70 e = e.expressionSemantic(sc);
74 /******************************************
75 * Patch the parent of declarations to be the new function literal.
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.
81 * e = expression to check
82 * fd = function literal symbol (the new parent)
84 private void lambdaSetParent(Expression e, FuncDeclaration fd)
86 extern (C++) final class LambdaSetParent : StoppableVisitor
88 alias visit = typeof(super).visit;
91 private void setParent(Dsymbol s)
93 VarDeclaration vd = s.isVarDeclaration();
94 FuncDeclaration pfd = s.parent ? s.parent.isFuncDeclaration() : null;
98 // move to fd's closure when applicable
99 foreach (i; 0 .. pfd.closureVars.dim)
101 if (vd == pfd.closureVars[i])
103 pfd.closureVars.remove(i);
104 fd.closureVars.push(vd);
111 extern (D) this(FuncDeclaration fd)
116 override void visit(Expression)
120 override void visit(DeclarationExp e)
122 setParent(e.declaration);
123 e.declaration.accept(this);
126 override void visit(IndexExp e)
130 //printf("lengthVar\n");
131 setParent(e.lengthVar);
132 e.lengthVar.accept(this);
136 override void visit(SliceExp e)
140 //printf("lengthVar\n");
141 setParent(e.lengthVar);
142 e.lengthVar.accept(this);
146 override void visit(Dsymbol)
150 override void visit(VarDeclaration v)
153 v._init.accept(this);
156 override void visit(Initializer)
160 override void visit(ExpInitializer ei)
162 walkPostorder(ei.exp ,this);
165 override void visit(StructInitializer si)
167 foreach (i, const id; si.field)
168 if (Initializer iz = si.value[i])
172 override void visit(ArrayInitializer ai)
174 foreach (i, ex; ai.index)
177 walkPostorder(ex, this);
178 if (Initializer iz = ai.value[i])
184 scope LambdaSetParent lsp = new LambdaSetParent(fd);
185 walkPostorder(e, lsp);
188 /*******************************************
189 * Look for references to variables in a scope enclosing the new function literal.
191 * Essentially just calls `checkNestedReference() for each variable reference in `e`.
194 * e = expression to check
196 * true if error occurs.
198 bool lambdaCheckForNestedRef(Expression e, Scope* sc)
200 extern (C++) final class LambdaCheckForNestedRef : StoppableVisitor
202 alias visit = typeof(super).visit;
207 extern (D) this(Scope* sc)
212 override void visit(Expression)
216 override void visit(SymOffExp e)
218 VarDeclaration v = e.var.isVarDeclaration();
220 result = v.checkNestedReference(sc, Loc.initial);
223 override void visit(VarExp e)
225 VarDeclaration v = e.var.isVarDeclaration();
227 result = v.checkNestedReference(sc, Loc.initial);
230 override void visit(ThisExp e)
233 result = e.var.checkNestedReference(sc, Loc.initial);
236 override void visit(DeclarationExp e)
238 VarDeclaration v = e.declaration.isVarDeclaration();
241 result = v.checkNestedReference(sc, Loc.initial);
244 /* Some expressions cause the frontend to create a temporary.
245 * For example, structs with cpctors replace the original
247 * __cpcttmp = __cpcttmp.cpctor(e);
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.
253 if (v._init && v._init.isExpInitializer())
255 Expression ie = v._init.initializerToExpression();
256 result = lambdaCheckForNestedRef(ie, sc);
262 scope LambdaCheckForNestedRef v = new LambdaCheckForNestedRef(sc);
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.
276 * true means it is accessible by walking the context pointers at runtime
278 * for static links see https://en.wikipedia.org/wiki/Call_stack#Functions_of_the_call_stack
280 bool ensureStaticLinkTo(Dsymbol s, Dsymbol p)
287 if (auto fd = s.isFuncDeclaration())
289 if (!fd.isThis() && !fd.isNested())
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_;
297 if (auto ad = s.isAggregateDeclaration())
299 if (ad.storage_class & STC.static_)