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-2020 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.declaration;
19
import dmd.dscope;
20
import dmd.dsymbol;
21
import dmd.expression;
22
import dmd.expressionsem;
23
import dmd.func;
24
import dmd.globals;
25
import dmd.init;
26
import dmd.initsem;
27
import dmd.mtype;
28
import dmd.statement;
29
import dmd.tokens;
30
import dmd.visitor;
31

32

33
/*********************************
34
 * Convert expression into a delegate.
35
 *
36
 * Used to convert the argument to a lazy parameter.
37
 *
38
 * Params:
39
 *  e = argument to convert to a delegate
40
 *  t = the type to be returned by the delegate
41
 *  sc = context
42
 * Returns:
43
 *  A delegate literal
44
 */
45
Expression toDelegate(Expression e, Type t, Scope* sc)
46
{
47
    //printf("Expression::toDelegate(t = %s) %s\n", t.toChars(), e.toChars());
48 1
    Loc loc = e.loc;
49 1
    auto tf = new TypeFunction(ParameterList(), t, LINK.d);
50 1
    if (t.hasWild())
51 1
        tf.mod = MODFlags.wild;
52 1
    auto fld = new FuncLiteralDeclaration(loc, loc, tf, TOK.delegate_, null);
53 1
    lambdaSetParent(e, fld);
54

55 1
    sc = sc.push();
56 1
    sc.parent = fld; // set current function to be the delegate
57 1
    bool r = lambdaCheckForNestedRef(e, sc);
58 1
    sc = sc.pop();
59 1
    if (r)
60 0
        return ErrorExp.get();
61

62 1
    Statement s;
63 1
    if (t.ty == Tvoid)
64 1
        s = new ExpStatement(loc, e);
65
    else
66 1
        s = new ReturnStatement(loc, e);
67 1
    fld.fbody = s;
68 1
    e = new FuncExp(loc, fld);
69 1
    e = e.expressionSemantic(sc);
70 1
    return e;
71
}
72

73
/******************************************
74
 * Patch the parent of declarations to be the new function literal.
75
 *
76
 * Since the expression is going to be moved into a function literal,
77
 * the parent for declarations in the expression needs to be
78
 * reset to that function literal.
79
 * Params:
80
 *   e = expression to check
81
 *   fd = function literal symbol (the new parent)
82
 */
83
private void lambdaSetParent(Expression e, FuncDeclaration fd)
84
{
85
    extern (C++) final class LambdaSetParent : StoppableVisitor
86
    {
87
        alias visit = typeof(super).visit;
88
        FuncDeclaration fd;
89

90
        private void setParent(Dsymbol s)
91
        {
92 1
            VarDeclaration vd = s.isVarDeclaration();
93 1
            FuncDeclaration pfd = s.parent ? s.parent.isFuncDeclaration() : null;
94 1
            s.parent = fd;
95 1
            if (!vd || !pfd)
96 0
                return;
97
            // move to fd's closure when applicable
98 1
            foreach (i; 0 .. pfd.closureVars.dim)
99
            {
100 1
                if (vd == pfd.closureVars[i])
101
                {
102 1
                    pfd.closureVars.remove(i);
103 1
                    fd.closureVars.push(vd);
104 1
                    break;
105
                }
106
            }
107
        }
108

109
    public:
110 1
        extern (D) this(FuncDeclaration fd)
111
        {
112 1
            this.fd = fd;
113
        }
114

115
        override void visit(Expression)
116
        {
117
        }
118

119
        override void visit(DeclarationExp e)
120
        {
121 1
            setParent(e.declaration);
122 1
            e.declaration.accept(this);
123
        }
124

125
        override void visit(IndexExp e)
126
        {
127 1
            if (e.lengthVar)
128
            {
129
                //printf("lengthVar\n");
130 0
                setParent(e.lengthVar);
131 0
                e.lengthVar.accept(this);
132
            }
133
        }
134

135
        override void visit(SliceExp e)
136
        {
137 1
            if (e.lengthVar)
138
            {
139
                //printf("lengthVar\n");
140 1
                setParent(e.lengthVar);
141 1
                e.lengthVar.accept(this);
142
            }
143
        }
144

145
        override void visit(Dsymbol)
146
        {
147
        }
148

149
        override void visit(VarDeclaration v)
150
        {
151 1
            if (v._init)
152 1
                v._init.accept(this);
153
        }
154

155
        override void visit(Initializer)
156
        {
157
        }
158

159
        override void visit(ExpInitializer ei)
160
        {
161 1
            walkPostorder(ei.exp ,this);
162
        }
163

164
        override void visit(StructInitializer si)
165
        {
166 0
            foreach (i, const id; si.field)
167 0
                if (Initializer iz = si.value[i])
168 0
                    iz.accept(this);
169
        }
170

171
        override void visit(ArrayInitializer ai)
172
        {
173 0
            foreach (i, ex; ai.index)
174
            {
175 0
                if (ex)
176 0
                    walkPostorder(ex, this);
177 0
                if (Initializer iz = ai.value[i])
178 0
                    iz.accept(this);
179
            }
180
        }
181
    }
182

183 1
    scope LambdaSetParent lsp = new LambdaSetParent(fd);
184 1
    walkPostorder(e, lsp);
185
}
186

187
/*******************************************
188
 * Look for references to variables in a scope enclosing the new function literal.
189
 *
190
 * Essentially just calls `checkNestedReference() for each variable reference in `e`.
191
 * Params:
192
 *      sc = context
193
 *      e = expression to check
194
 * Returns:
195
 *      true if error occurs.
196
 */
197
bool lambdaCheckForNestedRef(Expression e, Scope* sc)
198
{
199
    extern (C++) final class LambdaCheckForNestedRef : StoppableVisitor
200
    {
201
        alias visit = typeof(super).visit;
202
    public:
203
        Scope* sc;
204
        bool result;
205

206 1
        extern (D) this(Scope* sc)
207
        {
208 1
            this.sc = sc;
209
        }
210

211
        override void visit(Expression)
212
        {
213
        }
214

215
        override void visit(SymOffExp e)
216
        {
217 1
            VarDeclaration v = e.var.isVarDeclaration();
218 1
            if (v)
219 1
                result = v.checkNestedReference(sc, Loc.initial);
220
        }
221

222
        override void visit(VarExp e)
223
        {
224 1
            VarDeclaration v = e.var.isVarDeclaration();
225 1
            if (v)
226 1
                result = v.checkNestedReference(sc, Loc.initial);
227
        }
228

229
        override void visit(ThisExp e)
230
        {
231 1
            if (e.var)
232 1
                result = e.var.checkNestedReference(sc, Loc.initial);
233
        }
234

235
        override void visit(DeclarationExp e)
236
        {
237 1
            VarDeclaration v = e.declaration.isVarDeclaration();
238 1
            if (v)
239
            {
240 1
                result = v.checkNestedReference(sc, Loc.initial);
241 1
                if (result)
242 0
                    return;
243
                /* Some expressions cause the frontend to create a temporary.
244
                 * For example, structs with cpctors replace the original
245
                 * expression e with:
246
                 *  __cpcttmp = __cpcttmp.cpctor(e);
247
                 *
248
                 * In this instance, we need to ensure that the original
249
                 * expression e does not have any nested references by
250
                 * checking the declaration initializer too.
251
                 */
252 1
                if (v._init && v._init.isExpInitializer())
253
                {
254 1
                    Expression ie = v._init.initializerToExpression();
255 1
                    result = lambdaCheckForNestedRef(ie, sc);
256
                }
257
            }
258
        }
259
    }
260

261 1
    scope LambdaCheckForNestedRef v = new LambdaCheckForNestedRef(sc);
262 1
    walkPostorder(e, v);
263 1
    return v.result;
264
}
265

266
/*****************************************
267
 * See if context `s` is nested within context `p`, meaning
268
 * it `p` is reachable at runtime by walking the static links.
269
 * If any of the intervening contexts are function literals,
270
 * make sure they are delegates.
271
 * Params:
272
 *      s = inner context
273
 *      p = outer context
274
 * Returns:
275
 *      true means it is accessible by walking the context pointers at runtime
276
 * References:
277
 *      for static links see https://en.wikipedia.org/wiki/Call_stack#Functions_of_the_call_stack
278
 */
279
bool ensureStaticLinkTo(Dsymbol s, Dsymbol p)
280
{
281 1
    while (s)
282
    {
283 1
        if (s == p) // hit!
284 1
            return true;
285

286 1
        if (auto fd = s.isFuncDeclaration())
287
        {
288 1
            if (!fd.isThis() && !fd.isNested())
289 1
                break;
290

291
            // https://issues.dlang.org/show_bug.cgi?id=15332
292
            // change to delegate if fd is actually nested.
293 1
            if (auto fld = fd.isFuncLiteralDeclaration())
294 1
                fld.tok = TOK.delegate_;
295
        }
296 1
        if (auto ad = s.isAggregateDeclaration())
297
        {
298 1
            if (ad.storage_class & STC.static_)
299 0
                break;
300
        }
301 1
        s = s.toParentP(p);
302
    }
303 1
    return false;
304
}

Read our documentation on viewing source code .

Loading