1
/**
2
 * Most of the logic to implement scoped pointers and scoped references is here.
3
 *
4
 * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
5
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/escape.d, _escape.d)
8
 * Documentation:  https://dlang.org/phobos/dmd_escape.html
9
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/escape.d
10
 */
11

12
module dmd.escape;
13

14
import core.stdc.stdio : printf;
15
import core.stdc.stdlib;
16
import core.stdc.string;
17

18
import dmd.root.rmem;
19

20
import dmd.aggregate;
21
import dmd.declaration;
22
import dmd.dscope;
23
import dmd.dsymbol;
24
import dmd.errors;
25
import dmd.expression;
26
import dmd.func;
27
import dmd.globals;
28
import dmd.id;
29
import dmd.identifier;
30
import dmd.init;
31
import dmd.mtype;
32
import dmd.printast;
33
import dmd.root.rootobject;
34
import dmd.tokens;
35
import dmd.visitor;
36
import dmd.arraytypes;
37

38
/******************************************************
39
 * Checks memory objects passed to a function.
40
 * Checks that if a memory object is passed by ref or by pointer,
41
 * all of the refs or pointers are const, or there is only one mutable
42
 * ref or pointer to it.
43
 * References:
44
 *      DIP 1021
45
 * Params:
46
 *      sc = used to determine current function and module
47
 *      fd = function being called
48
 *      tf = fd's type
49
 *      ethis = if not null, the `this` pointer
50
 *      arguments = actual arguments to function
51
 *      gag = do not print error messages
52
 * Returns:
53
 *      `true` if error
54
 */
55
bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf,
56
    Expression ethis, Expressions* arguments, bool gag)
57
{
58
    enum log = false;
59 1
    if (log) printf("[%s] checkMutableArguments, fd: `%s`\n", fd.loc.toChars(), fd.toChars());
60 1
    if (log && ethis) printf("ethis: `%s`\n", ethis.toChars());
61 1
    bool errors = false;
62

63
    /* Outer variable references are treated as if they are extra arguments
64
     * passed by ref to the function (which they essentially are via the static link).
65
     */
66 1
    VarDeclaration[] outerVars = fd ? fd.outerVars[] : null;
67

68 1
    const len = arguments.length + (ethis !is null) + outerVars.length;
69 1
    if (len <= 1)
70 1
        return errors;
71

72
    struct EscapeBy
73
    {
74
        EscapeByResults er;
75
        Parameter param;        // null if no Parameter for this argument
76
        bool isMutable;         // true if reference to mutable
77
    }
78

79
    /* Store escapeBy as static data escapeByStorage so we can keep reusing the same
80
     * arrays rather than reallocating them.
81
     */
82 1
    __gshared EscapeBy[] escapeByStorage;
83 1
    auto escapeBy = escapeByStorage;
84 1
    if (escapeBy.length < len)
85
    {
86 1
        auto newPtr = cast(EscapeBy*)mem.xrealloc(escapeBy.ptr, len * EscapeBy.sizeof);
87
        // Clear the new section
88 1
        memset(newPtr + escapeBy.length, 0, (len - escapeBy.length) * EscapeBy.sizeof);
89 1
        escapeBy = newPtr[0 .. len];
90 1
        escapeByStorage = escapeBy;
91
    }
92
    else
93 1
        escapeBy = escapeBy[0 .. len];
94

95 1
    const paramLength = tf.parameterList.length;
96

97
    // Fill in escapeBy[] with arguments[], ethis, and outerVars[]
98 1
    foreach (const i, ref eb; escapeBy)
99
    {
100 1
        bool refs;
101 1
        Expression arg;
102 1
        if (i < arguments.length)
103
        {
104 1
            arg = (*arguments)[i];
105 1
            if (i < paramLength)
106
            {
107 1
                eb.param = tf.parameterList[i];
108 1
                refs = eb.param.isReference();
109 1
                eb.isMutable = eb.param.isReferenceToMutable(arg.type);
110
            }
111
            else
112
            {
113 1
                eb.param = null;
114 1
                refs = false;
115 1
                eb.isMutable = arg.type.isReferenceToMutable();
116
            }
117
        }
118 1
        else if (ethis)
119
        {
120
            /* ethis is passed by value if a class reference,
121
             * by ref if a struct value
122
             */
123 1
            eb.param = null;
124 1
            arg = ethis;
125 1
            auto ad = fd.isThis();
126 1
            assert(ad);
127 1
            assert(ethis);
128 1
            if (ad.isClassDeclaration())
129
            {
130 1
                refs = false;
131 1
                eb.isMutable = arg.type.isReferenceToMutable();
132
            }
133
            else
134
            {
135 1
                assert(ad.isStructDeclaration());
136 1
                refs = true;
137 1
                eb.isMutable = arg.type.isMutable();
138
            }
139
        }
140
        else
141
        {
142
            // outer variables are passed by ref
143 1
            eb.param = null;
144 1
            refs = true;
145 1
            auto var = outerVars[i - (len - outerVars.length)];
146 1
            eb.isMutable = var.type.isMutable();
147 1
            eb.er.byref.push(var);
148 1
            continue;
149
        }
150

151 1
        if (refs)
152 1
            escapeByRef(arg, &eb.er);
153
        else
154 1
            escapeByValue(arg, &eb.er);
155
    }
156

157
    void checkOnePair(size_t i, ref EscapeBy eb, ref EscapeBy eb2,
158
                      VarDeclaration v, VarDeclaration v2, bool of)
159
    {
160 1
        if (log) printf("v2: `%s`\n", v2.toChars());
161 1
        if (v2 != v)
162 1
            return;
163
        //printf("v %d v2 %d\n", eb.isMutable, eb2.isMutable);
164 1
        if (!(eb.isMutable || eb2.isMutable))
165 1
            return;
166

167 1
        if (!(global.params.vsafe && sc.func.setUnsafe()))
168 1
            return;
169

170 1
        if (!gag)
171
        {
172
            // int i; funcThatEscapes(ref int i);
173
            // funcThatEscapes(i); // error escaping reference _to_ `i`
174
            // int* j; funcThatEscapes2(int* j);
175
            // funcThatEscapes2(j); // error escaping reference _of_ `i`
176 1
            const(char)* referenceVerb = of ? "of" : "to";
177 1
            const(char)* msg = eb.isMutable && eb2.isMutable
178 1
                                ? "more than one mutable reference %s `%s` in arguments to `%s()`"
179 1
                                : "mutable and const references %s `%s` in arguments to `%s()`";
180 1
            error((*arguments)[i].loc, msg,
181
                  referenceVerb,
182
                  v.toChars(),
183 1
                  fd ? fd.toPrettyChars() : "indirectly");
184
        }
185 1
        errors = true;
186
    }
187

188
    void escape(size_t i, ref EscapeBy eb, bool byval)
189
    {
190 1
        foreach (VarDeclaration v; byval ? eb.er.byvalue : eb.er.byref)
191
        {
192 1
            if (log)
193
            {
194 0
                const(char)* by = byval ? "byval" : "byref";
195 0
                printf("%s %s\n", by, v.toChars());
196
            }
197 1
            if (byval && !v.type.hasPointers())
198 0
                continue;
199 1
            foreach (ref eb2; escapeBy[i + 1 .. $])
200
            {
201 1
                foreach (VarDeclaration v2; byval ? eb2.er.byvalue : eb2.er.byref)
202
                {
203 1
                    checkOnePair(i, eb, eb2, v, v2, byval);
204
                }
205
            }
206
        }
207
    }
208 1
    foreach (const i, ref eb; escapeBy[0 .. $ - 1])
209
    {
210 1
        escape(i, eb, true);
211 1
        escape(i, eb, false);
212
    }
213

214
    /* Reset the arrays in escapeBy[] so we can reuse them next time through
215
     */
216 1
    foreach (ref eb; escapeBy)
217
    {
218 1
        eb.er.reset();
219
    }
220

221 1
    return errors;
222
}
223

224
/******************************************
225
 * Array literal is going to be allocated on the GC heap.
226
 * Check its elements to see if any would escape by going on the heap.
227
 * Params:
228
 *      sc = used to determine current function and module
229
 *      ae = array literal expression
230
 *      gag = do not print error messages
231
 * Returns:
232
 *      `true` if any elements escaped
233
 */
234
bool checkArrayLiteralEscape(Scope *sc, ArrayLiteralExp ae, bool gag)
235
{
236 1
    bool errors;
237 1
    if (ae.basis)
238 0
        errors = checkNewEscape(sc, ae.basis, gag);
239 1
    foreach (ex; *ae.elements)
240
    {
241 1
        if (ex)
242 1
            errors |= checkNewEscape(sc, ex, gag);
243
    }
244 1
    return errors;
245
}
246

247
/******************************************
248
 * Associative array literal is going to be allocated on the GC heap.
249
 * Check its elements to see if any would escape by going on the heap.
250
 * Params:
251
 *      sc = used to determine current function and module
252
 *      ae = associative array literal expression
253
 *      gag = do not print error messages
254
 * Returns:
255
 *      `true` if any elements escaped
256
 */
257
bool checkAssocArrayLiteralEscape(Scope *sc, AssocArrayLiteralExp ae, bool gag)
258
{
259 1
    bool errors;
260 1
    foreach (ex; *ae.keys)
261
    {
262 1
        if (ex)
263 1
            errors |= checkNewEscape(sc, ex, gag);
264
    }
265 1
    foreach (ex; *ae.values)
266
    {
267 1
        if (ex)
268 1
            errors |= checkNewEscape(sc, ex, gag);
269
    }
270 1
    return errors;
271
}
272

273
/****************************************
274
 * Function parameter `par` is being initialized to `arg`,
275
 * and `par` may escape.
276
 * Detect if scoped values can escape this way.
277
 * Print error messages when these are detected.
278
 * Params:
279
 *      sc = used to determine current function and module
280
 *      fdc = function being called, `null` if called indirectly
281
 *      par = function parameter (`this` if null)
282
 *      arg = initializer for param
283
 *      assertmsg = true if the parameter is the msg argument to assert(bool, msg).
284
 *      gag = do not print error messages
285
 * Returns:
286
 *      `true` if pointers to the stack can escape via assignment
287
 */
288
bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, Expression arg, bool assertmsg, bool gag)
289
{
290
    enum log = false;
291 1
    if (log) printf("checkParamArgumentEscape(arg: %s par: %s)\n",
292 0
        arg ? arg.toChars() : "null",
293 0
        par ? par.toChars() : "this");
294
    //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
295

296 1
    if (!arg.type.hasPointers())
297 1
        return false;
298

299 1
    EscapeByResults er;
300

301 1
    escapeByValue(arg, &er);
302

303 1
    if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
304 1
        return false;
305

306 1
    bool result = false;
307

308
    /* 'v' is assigned unsafely to 'par'
309
     */
310
    void unsafeAssign(VarDeclaration v, const char* desc)
311
    {
312 1
        if (global.params.vsafe && sc.func.setUnsafe())
313
        {
314 1
            if (!gag)
315
            {
316 1
                if (assertmsg)
317
                {
318 1
                    error(arg.loc, "%s `%s` assigned to non-scope parameter calling `assert()`",
319
                        desc, v.toChars());
320
                }
321
                else
322
                {
323 1
                    error(arg.loc, "%s `%s` assigned to non-scope parameter `%s` calling %s",
324
                        desc, v.toChars(),
325 1
                        par ? par.toChars() : "this",
326 1
                        fdc ? fdc.toPrettyChars() : "indirectly");
327
                }
328
            }
329 1
            result = true;
330
        }
331
    }
332

333 1
    foreach (VarDeclaration v; er.byvalue)
334
    {
335 1
        if (log) printf("byvalue %s\n", v.toChars());
336 1
        if (v.isDataseg())
337 1
            continue;
338

339 1
        Dsymbol p = v.toParent2();
340

341 1
        notMaybeScope(v);
342

343 1
        if (v.isScope())
344
        {
345 1
            unsafeAssign(v, "scope variable");
346
        }
347 1
        else if (v.storage_class & STC.variadic && p == sc.func)
348
        {
349 0
            Type tb = v.type.toBasetype();
350 0
            if (tb.ty == Tarray || tb.ty == Tsarray)
351
            {
352 0
                unsafeAssign(v, "variadic variable");
353
            }
354
        }
355
        else
356
        {
357
            /* v is not 'scope', and is assigned to a parameter that may escape.
358
             * Therefore, v can never be 'scope'.
359
             */
360 1
            if (log) printf("no infer for %s in %s loc %s, fdc %s, %d\n",
361
                v.toChars(), sc.func.ident.toChars(), sc.func.loc.toChars(), fdc.ident.toChars(),  __LINE__);
362 1
            v.doNotInferScope = true;
363
        }
364
    }
365

366 1
    foreach (VarDeclaration v; er.byref)
367
    {
368 1
        if (log) printf("byref %s\n", v.toChars());
369 1
        if (v.isDataseg())
370 1
            continue;
371

372 1
        Dsymbol p = v.toParent2();
373

374 1
        notMaybeScope(v);
375

376 1
        if ((v.storage_class & (STC.ref_ | STC.out_)) == 0 && p == sc.func)
377
        {
378 1
            if (par && (par.storageClass & (STC.scope_ | STC.return_)) == STC.scope_)
379 0
                continue;
380

381 1
            unsafeAssign(v, "reference to local variable");
382 1
            continue;
383
        }
384
    }
385

386 1
    foreach (FuncDeclaration fd; er.byfunc)
387
    {
388
        //printf("fd = %s, %d\n", fd.toChars(), fd.tookAddressOf);
389 1
        VarDeclarations vars;
390 1
        findAllOuterAccessedVariables(fd, &vars);
391

392 1
        foreach (v; vars)
393
        {
394
            //printf("v = %s\n", v.toChars());
395 1
            assert(!v.isDataseg());     // these are not put in the closureVars[]
396

397 1
            Dsymbol p = v.toParent2();
398

399 1
            notMaybeScope(v);
400

401 1
            if ((v.storage_class & (STC.ref_ | STC.out_ | STC.scope_)) && p == sc.func)
402
            {
403 1
                unsafeAssign(v, "reference to local");
404 1
                continue;
405
            }
406
        }
407
    }
408

409 1
    foreach (Expression ee; er.byexp)
410
    {
411 1
        if (sc.func && sc.func.setUnsafe())
412
        {
413 1
            if (!gag)
414 1
                error(ee.loc, "reference to stack allocated value returned by `%s` assigned to non-scope parameter `%s`",
415
                    ee.toChars(),
416 1
                    par ? par.toChars() : "this");
417 1
            result = true;
418
        }
419
    }
420

421 1
    return result;
422
}
423

424
/*****************************************************
425
 * Function argument initializes a `return` parameter,
426
 * and that parameter gets assigned to `firstArg`.
427
 * Essentially, treat as `firstArg = arg;`
428
 * Params:
429
 *      sc = used to determine current function and module
430
 *      firstArg = `ref` argument through which `arg` may be assigned
431
 *      arg = initializer for parameter
432
 *      gag = do not print error messages
433
 * Returns:
434
 *      `true` if assignment to `firstArg` would cause an error
435
 */
436
bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, bool gag)
437
{
438
    enum log = false;
439 1
    if (log) printf("checkParamArgumentReturn(firstArg: %s arg: %s)\n",
440
        firstArg.toChars(), arg.toChars());
441
    //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
442

443 1
    if (!arg.type.hasPointers())
444 0
        return false;
445

446 1
    scope e = new AssignExp(arg.loc, firstArg, arg);
447 1
    return checkAssignEscape(sc, e, gag);
448
}
449

450
/*****************************************************
451
 * Check struct constructor of the form `s.this(args)`, by
452
 * checking each `return` parameter to see if it gets
453
 * assigned to `s`.
454
 * Params:
455
 *      sc = used to determine current function and module
456
 *      ce = constructor call of the form `s.this(args)`
457
 *      gag = do not print error messages
458
 * Returns:
459
 *      `true` if construction would cause an escaping reference error
460
 */
461
bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag)
462
{
463
    enum log = false;
464 1
    if (log) printf("checkConstructorEscape(%s, %s)\n", ce.toChars(), ce.type.toChars());
465 1
    Type tthis = ce.type.toBasetype();
466 1
    assert(tthis.ty == Tstruct);
467 1
    if (!tthis.hasPointers())
468 1
        return false;
469

470 1
    if (!ce.arguments && ce.arguments.dim)
471 0
        return false;
472

473 1
    assert(ce.e1.op == TOK.dotVariable);
474 1
    DotVarExp dve = cast(DotVarExp)ce.e1;
475 1
    CtorDeclaration ctor = dve.var.isCtorDeclaration();
476 1
    assert(ctor);
477 1
    assert(ctor.type.ty == Tfunction);
478 1
    TypeFunction tf = cast(TypeFunction)ctor.type;
479

480 1
    const nparams = tf.parameterList.length;
481 1
    const n = ce.arguments.dim;
482

483
    // j=1 if _arguments[] is first argument
484 1
    const j = tf.isDstyleVariadic();
485

486
    /* Attempt to assign each `return` arg to the `this` reference
487
     */
488 1
    foreach (const i; 0 .. n)
489
    {
490 1
        Expression arg = (*ce.arguments)[i];
491 1
        if (!arg.type.hasPointers())
492 1
            return false;
493

494
        //printf("\targ[%d]: %s\n", i, arg.toChars());
495

496 1
        if (i - j < nparams && i >= j)
497
        {
498 1
            Parameter p = tf.parameterList[i - j];
499

500 1
            if (p.storageClass & STC.return_)
501
            {
502
                /* Fake `dve.e1 = arg;` and look for scope violations
503
                 */
504 1
                scope e = new AssignExp(arg.loc, dve.e1, arg);
505 1
                if (checkAssignEscape(sc, e, gag))
506 0
                    return true;
507
            }
508
        }
509
    }
510

511 1
    return false;
512
}
513

514
/****************************************
515
 * Given an `AssignExp`, determine if the lvalue will cause
516
 * the contents of the rvalue to escape.
517
 * Print error messages when these are detected.
518
 * Infer `scope` attribute for the lvalue where possible, in order
519
 * to eliminate the error.
520
 * Params:
521
 *      sc = used to determine current function and module
522
 *      e = `AssignExp` or `CatAssignExp` to check for any pointers to the stack
523
 *      gag = do not print error messages
524
 * Returns:
525
 *      `true` if pointers to the stack can escape via assignment
526
 */
527
bool checkAssignEscape(Scope* sc, Expression e, bool gag)
528
{
529
    enum log = false;
530 1
    if (log) printf("checkAssignEscape(e: %s)\n", e.toChars());
531 1
    if (e.op != TOK.assign && e.op != TOK.blit && e.op != TOK.construct &&
532 1
        e.op != TOK.concatenateAssign && e.op != TOK.concatenateElemAssign && e.op != TOK.concatenateDcharAssign)
533 1
        return false;
534 1
    auto ae = cast(BinExp)e;
535 1
    Expression e1 = ae.e1;
536 1
    Expression e2 = ae.e2;
537
    //printf("type = %s, %d\n", e1.type.toChars(), e1.type.hasPointers());
538

539 1
    if (!e1.type.hasPointers())
540 1
        return false;
541

542 1
    if (e1.op == TOK.slice)
543 1
        return false;
544

545
    /* The struct literal case can arise from the S(e2) constructor call:
546
     *    return S(e2);
547
     * and appears in this function as:
548
     *    structLiteral = e2;
549
     * Such an assignment does not necessarily remove scope-ness.
550
     */
551 1
    if (e1.op == TOK.structLiteral)
552 1
        return false;
553

554 1
    EscapeByResults er;
555

556 1
    escapeByValue(e2, &er);
557

558 1
    if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
559 1
        return false;
560

561 1
    VarDeclaration va = expToVariable(e1);
562

563 1
    if (va && e.op == TOK.concatenateElemAssign)
564
    {
565
        /* https://issues.dlang.org/show_bug.cgi?id=17842
566
         * Draw an equivalence between:
567
         *   *q = p;
568
         * and:
569
         *   va ~= e;
570
         * since we are not assigning to va, but are assigning indirectly through va.
571
         */
572 0
        va = null;
573
    }
574

575 1
    if (va && e1.op == TOK.dotVariable && va.type.toBasetype().ty == Tclass)
576
    {
577
        /* https://issues.dlang.org/show_bug.cgi?id=17949
578
         * Draw an equivalence between:
579
         *   *q = p;
580
         * and:
581
         *   va.field = e2;
582
         * since we are not assigning to va, but are assigning indirectly through class reference va.
583
         */
584 1
        va = null;
585
    }
586

587 1
    if (log && va) printf("va: %s\n", va.toChars());
588

589
    // Try to infer 'scope' for va if in a function not marked @system
590 1
    bool inferScope = false;
591 1
    if (va && sc.func && sc.func.type && sc.func.type.ty == Tfunction)
592 1
        inferScope = (cast(TypeFunction)sc.func.type).trust != TRUST.system;
593
    //printf("inferScope = %d, %d\n", inferScope, (va.storage_class & STCmaybescope) != 0);
594

595
    // Determine if va is a parameter that is an indirect reference
596 1
    const bool vaIsRef = va && va.storage_class & STC.parameter &&
597 1
        (va.storage_class & (STC.ref_ | STC.out_) || va.type.toBasetype().ty == Tclass);
598 1
    if (log && vaIsRef) printf("va is ref `%s`\n", va.toChars());
599

600
    /* Determine if va is the first parameter, through which other 'return' parameters
601
     * can be assigned.
602
     */
603
    bool isFirstRef()
604
    {
605 1
        if (!vaIsRef)
606 1
            return false;
607 1
        Dsymbol p = va.toParent2();
608 1
        FuncDeclaration fd = sc.func;
609 1
        if (p == fd && fd.type && fd.type.ty == Tfunction)
610
        {
611 1
            TypeFunction tf = cast(TypeFunction)fd.type;
612 1
            if (!tf.nextOf() || (tf.nextOf().ty != Tvoid && !fd.isCtorDeclaration()))
613 1
                return false;
614 1
            if (va == fd.vthis)
615 1
                return true;
616 1
            if (fd.parameters && fd.parameters.dim && (*fd.parameters)[0] == va)
617 1
                return true;
618
        }
619 1
        return false;
620
    }
621 1
    const bool vaIsFirstRef = isFirstRef();
622 1
    if (log && vaIsFirstRef) printf("va is first ref `%s`\n", va.toChars());
623

624 1
    bool result = false;
625 1
    foreach (VarDeclaration v; er.byvalue)
626
    {
627 1
        if (log) printf("byvalue: %s\n", v.toChars());
628 1
        if (v.isDataseg())
629 1
            continue;
630

631 1
        if (v == va)
632 1
            continue;
633

634 1
        Dsymbol p = v.toParent2();
635

636 1
        if (va && !vaIsRef && !va.isScope() && !v.isScope() &&
637 1
            (va.storage_class & v.storage_class & (STC.maybescope | STC.variadic)) == STC.maybescope &&
638 1
            p == sc.func)
639
        {
640
            /* Add v to va's list of dependencies
641
             */
642 1
            va.addMaybe(v);
643 1
            continue;
644
        }
645

646 1
        if (vaIsFirstRef &&
647 1
            (v.isScope() || (v.storage_class & STC.maybescope)) &&
648 1
            !(v.storage_class & STC.return_) &&
649 1
            v.isParameter() &&
650 1
            sc.func.flags & FUNCFLAG.returnInprocess &&
651 1
            p == sc.func)
652
        {
653 1
            if (log) printf("inferring 'return' for parameter %s in function %s\n", v.toChars(), sc.func.toChars());
654 1
            inferReturn(sc.func, v);        // infer addition of 'return'
655
        }
656

657 1
        if (!(va && va.isScope()) || vaIsRef)
658 1
            notMaybeScope(v);
659

660 1
        if (v.isScope())
661
        {
662 1
            if (vaIsFirstRef && v.isParameter() && v.storage_class & STC.return_)
663
            {
664 1
                if (va.isScope())
665 1
                    continue;
666

667 1
                if (inferScope && !va.doNotInferScope)
668
                {
669 1
                    if (log) printf("inferring scope for lvalue %s\n", va.toChars());
670 1
                    va.storage_class |= STC.scope_ | STC.scopeinferred;
671 1
                    continue;
672
                }
673
            }
674

675 1
            if (va && va.isScope() && va.storage_class & STC.return_ && !(v.storage_class & STC.return_) &&
676 1
                sc.func.setUnsafe())
677
            {
678 1
                if (!gag)
679 1
                    error(ae.loc, "scope variable `%s` assigned to return scope `%s`", v.toChars(), va.toChars());
680 1
                result = true;
681 1
                continue;
682
            }
683

684
            // If va's lifetime encloses v's, then error
685 1
            if (va &&
686 1
                (va.enclosesLifetimeOf(v) && !(v.storage_class & (STC.parameter | STC.temp)) ||
687
                 // va is class reference
688 1
                 ae.e1.op == TOK.dotVariable && va.type.toBasetype().ty == Tclass && (va.enclosesLifetimeOf(v) || !va.isScope()) ||
689 1
                 vaIsRef ||
690 1
                 va.storage_class & (STC.ref_ | STC.out_) && !(v.storage_class & (STC.parameter | STC.temp))) &&
691 1
                sc.func.setUnsafe())
692
            {
693 1
                if (!gag)
694 1
                    error(ae.loc, "scope variable `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars());
695 1
                result = true;
696 1
                continue;
697
            }
698

699 1
            if (va && !va.isDataseg() && !va.doNotInferScope)
700
            {
701 1
                if (!va.isScope() && inferScope)
702
                {   //printf("inferring scope for %s\n", va.toChars());
703 1
                    va.storage_class |= STC.scope_ | STC.scopeinferred;
704 1
                    if (v.storage_class & STC.return_ &&
705 1
                        !(va.storage_class & STC.return_))
706
                    {
707 1
                        va.storage_class |= STC.return_ | STC.returninferred;
708
                    }
709
                }
710 1
                continue;
711
            }
712 1
            if (sc.func.setUnsafe())
713
            {
714 1
                if (!gag)
715 1
                    error(ae.loc, "scope variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars());
716 1
                result = true;
717
            }
718
        }
719 1
        else if (v.storage_class & STC.variadic && p == sc.func)
720
        {
721 1
            Type tb = v.type.toBasetype();
722 1
            if (tb.ty == Tarray || tb.ty == Tsarray)
723
            {
724 1
                if (va && !va.isDataseg() && !va.doNotInferScope)
725
                {
726 1
                    if (!va.isScope() && inferScope)
727
                    {   //printf("inferring scope for %s\n", va.toChars());
728 1
                        va.storage_class |= STC.scope_ | STC.scopeinferred;
729
                    }
730 1
                    continue;
731
                }
732 0
                if (sc.func.setUnsafe())
733
                {
734 0
                    if (!gag)
735 0
                        error(ae.loc, "variadic variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars());
736 0
                    result = true;
737
                }
738
            }
739
        }
740
        else
741
        {
742
            /* v is not 'scope', and we didn't check the scope of where we assigned it to.
743
             * It may escape via that assignment, therefore, v can never be 'scope'.
744
             */
745
            //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
746 1
            v.doNotInferScope = true;
747
        }
748
    }
749

750
ByRef:
751 1
    foreach (VarDeclaration v; er.byref)
752
    {
753 1
        if (log) printf("byref: %s\n", v.toChars());
754 1
        if (v.isDataseg())
755 1
            continue;
756

757 1
        if (global.params.vsafe)
758
        {
759 1
            if (va && va.isScope() && (v.storage_class & (STC.ref_ | STC.out_)) == 0)
760
            {
761 1
                if (!(va.storage_class & STC.return_))
762
                {
763 1
                    va.doNotInferReturn = true;
764
                }
765 1
                else if (sc.func.setUnsafe())
766
                {
767 1
                    if (!gag)
768 1
                        error(ae.loc, "address of local variable `%s` assigned to return scope `%s`", v.toChars(), va.toChars());
769 1
                    result = true;
770 1
                    continue;
771
                }
772
            }
773
        }
774

775 1
        Dsymbol p = v.toParent2();
776

777
        // If va's lifetime encloses v's, then error
778 1
        if (va &&
779 1
            (va.enclosesLifetimeOf(v) && !(v.storage_class & STC.parameter) ||
780 1
             va.storage_class & STC.ref_ ||
781 1
             va.isDataseg()) &&
782 1
            sc.func.setUnsafe())
783
        {
784 1
            if (!gag)
785 1
                error(ae.loc, "address of variable `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars());
786 1
            result = true;
787 1
            continue;
788
        }
789

790 1
        if (va && v.storage_class & (STC.ref_ | STC.out_))
791
        {
792 1
            Dsymbol pva = va.toParent2();
793 1
            for (Dsymbol pv = p; pv; )
794
            {
795 1
                pv = pv.toParent2();
796 1
                if (pva == pv)  // if v is nested inside pva
797
                {
798 1
                    if (sc.func.setUnsafe())
799
                    {
800 1
                        if (!gag)
801 1
                            error(ae.loc, "reference `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars());
802 1
                        result = true;
803 1
                        continue ByRef;
804
                    }
805 1
                    break;
806
                }
807
            }
808
        }
809

810 1
        if (!(va && va.isScope()))
811 1
            notMaybeScope(v);
812

813 1
        if ((v.storage_class & (STC.ref_ | STC.out_)) || p != sc.func)
814 1
            continue;
815

816 1
        if (va && !va.isDataseg() && !va.doNotInferScope)
817
        {
818 1
            if (!va.isScope() && inferScope)
819
            {   //printf("inferring scope for %s\n", va.toChars());
820 1
                va.storage_class |= STC.scope_ | STC.scopeinferred;
821
            }
822 1
            continue;
823
        }
824 1
        if (e1.op == TOK.structLiteral)
825 0
            continue;
826 1
        if (sc.func.setUnsafe())
827
        {
828 1
            if (!gag)
829 1
                error(ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars());
830 1
            result = true;
831
        }
832
    }
833

834 1
    foreach (FuncDeclaration fd; er.byfunc)
835
    {
836 1
        if (log) printf("byfunc: %s, %d\n", fd.toChars(), fd.tookAddressOf);
837 1
        VarDeclarations vars;
838 1
        findAllOuterAccessedVariables(fd, &vars);
839

840
        /* https://issues.dlang.org/show_bug.cgi?id=16037
841
         * If assigning the address of a delegate to a scope variable,
842
         * then uncount that address of. This is so it won't cause a
843
         * closure to be allocated.
844
         */
845 1
        if (va && va.isScope() && fd.tookAddressOf)
846 1
            --fd.tookAddressOf;
847

848 1
        foreach (v; vars)
849
        {
850
            //printf("v = %s\n", v.toChars());
851 1
            assert(!v.isDataseg());     // these are not put in the closureVars[]
852

853 1
            Dsymbol p = v.toParent2();
854

855 1
            if (!(va && va.isScope()))
856 1
                notMaybeScope(v);
857

858 1
            if (!(v.storage_class & (STC.ref_ | STC.out_ | STC.scope_)) || p != sc.func)
859 1
                continue;
860

861 1
            if (va && !va.isDataseg() && !va.doNotInferScope)
862
            {
863
                /* Don't infer STC.scope_ for va, because then a closure
864
                 * won't be generated for sc.func.
865
                 */
866
                //if (!va.isScope() && inferScope)
867
                    //va.storage_class |= STC.scope_ | STC.scopeinferred;
868 1
                continue;
869
            }
870 1
            if (sc.func.setUnsafe())
871
            {
872 1
                if (!gag)
873 1
                    error(ae.loc, "reference to local `%s` assigned to non-scope `%s` in @safe code", v.toChars(), e1.toChars());
874 1
                result = true;
875
            }
876
        }
877
    }
878

879 1
    foreach (Expression ee; er.byexp)
880
    {
881 1
        if (log) printf("byexp: %s\n", ee.toChars());
882

883
        /* Do not allow slicing of a static array returned by a function
884
         */
885 1
        if (va && ee.op == TOK.call && ee.type.toBasetype().ty == Tsarray && va.type.toBasetype().ty == Tarray &&
886 1
            !(va.storage_class & STC.temp))
887
        {
888 1
            if (!gag)
889 1
                deprecation(ee.loc, "slice of static array temporary returned by `%s` assigned to longer lived variable `%s`",
890
                    ee.toChars(), va.toChars());
891
            //result = true;
892 1
            continue;
893
        }
894

895 1
        if (va && ee.op == TOK.call && ee.type.toBasetype().ty == Tstruct &&
896 1
            !(va.storage_class & STC.temp) && va.ident != Id.withSym &&
897 1
            sc.func.setUnsafe())
898
        {
899 1
            if (!gag)
900 1
                error(ee.loc, "address of struct temporary returned by `%s` assigned to longer lived variable `%s`",
901
                    ee.toChars(), va.toChars());
902 1
            result = true;
903 1
            continue;
904
        }
905

906 1
        if (va && ee.op == TOK.structLiteral &&
907 1
            !(va.storage_class & STC.temp) && va.ident != Id.withSym &&
908 1
            sc.func.setUnsafe())
909
        {
910 0
            if (!gag)
911 0
                error(ee.loc, "address of struct literal `%s` assigned to longer lived variable `%s`",
912
                    ee.toChars(), va.toChars());
913 0
            result = true;
914 0
            continue;
915
        }
916

917 1
        if (va && !va.isDataseg() && !va.doNotInferScope)
918
        {
919 1
            if (!va.isScope() && inferScope)
920
            {   //printf("inferring scope for %s\n", va.toChars());
921 1
                va.storage_class |= STC.scope_ | STC.scopeinferred;
922
            }
923 1
            continue;
924
        }
925

926 0
        if (sc.func.setUnsafe())
927
        {
928 0
            if (!gag)
929 0
                error(ee.loc, "reference to stack allocated value returned by `%s` assigned to non-scope `%s`",
930
                    ee.toChars(), e1.toChars());
931 0
            result = true;
932
        }
933
    }
934

935 1
    return result;
936
}
937

938
/************************************
939
 * Detect cases where pointers to the stack can escape the
940
 * lifetime of the stack frame when throwing `e`.
941
 * Print error messages when these are detected.
942
 * Params:
943
 *      sc = used to determine current function and module
944
 *      e = expression to check for any pointers to the stack
945
 *      gag = do not print error messages
946
 * Returns:
947
 *      `true` if pointers to the stack can escape
948
 */
949
bool checkThrowEscape(Scope* sc, Expression e, bool gag)
950
{
951
    //printf("[%s] checkThrowEscape, e = %s\n", e.loc.toChars(), e.toChars());
952 1
    EscapeByResults er;
953

954 1
    escapeByValue(e, &er);
955

956 1
    if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
957 1
        return false;
958

959 1
    bool result = false;
960 1
    foreach (VarDeclaration v; er.byvalue)
961
    {
962
        //printf("byvalue %s\n", v.toChars());
963 1
        if (v.isDataseg())
964 0
            continue;
965

966 1
        if (v.isScope() && !v.iscatchvar)       // special case: allow catch var to be rethrown
967
                                                // despite being `scope`
968
        {
969 1
            if (sc._module && sc._module.isRoot())
970
            {
971
                // Only look for errors if in module listed on command line
972 1
                if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029
973
                {
974 1
                    if (!gag)
975 1
                        error(e.loc, "scope variable `%s` may not be thrown", v.toChars());
976 1
                    result = true;
977
                }
978 1
                continue;
979
            }
980
        }
981
        else
982
        {
983
            //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
984 1
            v.doNotInferScope = true;
985
        }
986
    }
987 1
    return result;
988
}
989

990
/************************************
991
 * Detect cases where pointers to the stack can escape the
992
 * lifetime of the stack frame by being placed into a GC allocated object.
993
 * Print error messages when these are detected.
994
 * Params:
995
 *      sc = used to determine current function and module
996
 *      e = expression to check for any pointers to the stack
997
 *      gag = do not print error messages
998
 * Returns:
999
 *      `true` if pointers to the stack can escape
1000
 */
1001
bool checkNewEscape(Scope* sc, Expression e, bool gag)
1002
{
1003
    //printf("[%s] checkNewEscape, e = %s\n", e.loc.toChars(), e.toChars());
1004
    enum log = false;
1005 1
    if (log) printf("[%s] checkNewEscape, e: `%s`\n", e.loc.toChars(), e.toChars());
1006 1
    EscapeByResults er;
1007

1008 1
    escapeByValue(e, &er);
1009

1010 1
    if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
1011 1
        return false;
1012

1013 1
    bool result = false;
1014 1
    foreach (VarDeclaration v; er.byvalue)
1015
    {
1016 1
        if (log) printf("byvalue `%s`\n", v.toChars());
1017 1
        if (v.isDataseg())
1018 0
            continue;
1019

1020 1
        Dsymbol p = v.toParent2();
1021

1022 1
        if (v.isScope())
1023
        {
1024 1
            if (sc._module && sc._module.isRoot() &&
1025
                /* This case comes up when the ReturnStatement of a __foreachbody is
1026
                 * checked for escapes by the caller of __foreachbody. Skip it.
1027
                 *
1028
                 * struct S { static int opApply(int delegate(S*) dg); }
1029
                 * S* foo() {
1030
                 *    foreach (S* s; S) // create __foreachbody for body of foreach
1031
                 *        return s;     // s is inferred as 'scope' but incorrectly tested in foo()
1032
                 *    return null; }
1033
                 */
1034 1
                !(p.parent == sc.func))
1035
            {
1036
                // Only look for errors if in module listed on command line
1037 1
                if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029
1038
                {
1039 1
                    if (!gag)
1040 1
                        error(e.loc, "scope variable `%s` may not be copied into allocated memory", v.toChars());
1041 1
                    result = true;
1042
                }
1043 1
                continue;
1044
            }
1045
        }
1046 1
        else if (v.storage_class & STC.variadic && p == sc.func)
1047
        {
1048 1
            Type tb = v.type.toBasetype();
1049 1
            if (tb.ty == Tarray || tb.ty == Tsarray)
1050
            {
1051 1
                if (!gag)
1052 1
                    error(e.loc, "copying `%s` into allocated memory escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars());
1053 1
                result = false;
1054
            }
1055
        }
1056
        else
1057
        {
1058
            //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
1059 1
            v.doNotInferScope = true;
1060
        }
1061
    }
1062

1063 1
    foreach (VarDeclaration v; er.byref)
1064
    {
1065 1
        if (log) printf("byref `%s`\n", v.toChars());
1066

1067
        // 'emitError' tells us whether to emit an error or a deprecation,
1068
        // depending on the flag passed to the CLI for DIP25
1069
        void escapingRef(VarDeclaration v, bool emitError = true)
1070
        {
1071 1
            if (!gag)
1072
            {
1073 1
                const(char)* kind = (v.storage_class & STC.parameter) ? "parameter" : "local";
1074 1
                const(char)* msg = "copying `%s` into allocated memory escapes a reference to %s variable `%s`";
1075 1
                if (emitError)
1076 1
                    error(e.loc, msg, e.toChars(), kind, v.toChars());
1077
                else
1078 0
                    deprecation(e.loc, msg, e.toChars(), kind, v.toChars());
1079
            }
1080 1
            result |= emitError;
1081
        }
1082

1083 1
        if (v.isDataseg())
1084 1
            continue;
1085

1086 1
        Dsymbol p = v.toParent2();
1087

1088 1
        if ((v.storage_class & (STC.ref_ | STC.out_)) == 0)
1089
        {
1090 1
            if (p == sc.func)
1091
            {
1092 1
                escapingRef(v);
1093 1
                continue;
1094
            }
1095
        }
1096

1097
        /* Check for returning a ref variable by 'ref', but should be 'return ref'
1098
         * Infer the addition of 'return', or set result to be the offending expression.
1099
         */
1100 1
        if (!(v.storage_class & (STC.ref_ | STC.out_)))
1101 0
            continue;
1102

1103 1
        if (!sc._module || !sc._module.isRoot())
1104 0
            continue;
1105

1106
        // If -preview=dip25 is used, the user wants an error
1107
        // Otherwise, issue a deprecation
1108 1
        const emitError = global.params.useDIP25;
1109
        // https://dlang.org/spec/function.html#return-ref-parameters
1110
        // Only look for errors if in module listed on command line
1111 1
        if (p == sc.func)
1112
        {
1113
            //printf("escaping reference to local ref variable %s\n", v.toChars());
1114
            //printf("storage class = x%llx\n", v.storage_class);
1115 1
            escapingRef(v, emitError);
1116 1
            continue;
1117
        }
1118
        // Don't need to be concerned if v's parent does not return a ref
1119 1
        FuncDeclaration fd = p.isFuncDeclaration();
1120 1
        if (!fd || !fd.type)
1121 0
            continue;
1122 1
        if (auto tf = fd.type.isTypeFunction())
1123
        {
1124 1
            if (!tf.isref)
1125 0
                continue;
1126

1127 1
            const(char)* msg = "storing reference to outer local variable `%s` into allocated memory causes it to escape";
1128 1
            if (!gag && emitError)
1129 1
                error(e.loc, msg, v.toChars());
1130 0
            else if (!gag)
1131 0
                deprecation(e.loc, msg, v.toChars());
1132 1
            result |= emitError;
1133
        }
1134
    }
1135

1136 1
    foreach (Expression ee; er.byexp)
1137
    {
1138 1
        if (log) printf("byexp %s\n", ee.toChars());
1139 1
        if (!gag)
1140 1
            error(ee.loc, "storing reference to stack allocated value returned by `%s` into allocated memory causes it to escape",
1141
                  ee.toChars());
1142 1
        result = true;
1143
    }
1144

1145 1
    return result;
1146
}
1147

1148

1149
/************************************
1150
 * Detect cases where pointers to the stack can escape the
1151
 * lifetime of the stack frame by returning `e` by value.
1152
 * Print error messages when these are detected.
1153
 * Params:
1154
 *      sc = used to determine current function and module
1155
 *      e = expression to check for any pointers to the stack
1156
 *      gag = do not print error messages
1157
 * Returns:
1158
 *      `true` if pointers to the stack can escape
1159
 */
1160
bool checkReturnEscape(Scope* sc, Expression e, bool gag)
1161
{
1162
    //printf("[%s] checkReturnEscape, e: %s\n", e.loc.toChars(), e.toChars());
1163 1
    return checkReturnEscapeImpl(sc, e, false, gag);
1164
}
1165

1166
/************************************
1167
 * Detect cases where returning `e` by `ref` can result in a reference to the stack
1168
 * being returned.
1169
 * Print error messages when these are detected.
1170
 * Params:
1171
 *      sc = used to determine current function and module
1172
 *      e = expression to check
1173
 *      gag = do not print error messages
1174
 * Returns:
1175
 *      `true` if references to the stack can escape
1176
 */
1177
bool checkReturnEscapeRef(Scope* sc, Expression e, bool gag)
1178
{
1179
    version (none)
1180
    {
1181
        printf("[%s] checkReturnEscapeRef, e = %s\n", e.loc.toChars(), e.toChars());
1182
        printf("current function %s\n", sc.func.toChars());
1183
        printf("parent2 function %s\n", sc.func.toParent2().toChars());
1184
    }
1185

1186 1
    return checkReturnEscapeImpl(sc, e, true, gag);
1187
}
1188

1189
/***************************************
1190
 * Implementation of checking for escapes in return expressions.
1191
 * Params:
1192
 *      sc = used to determine current function and module
1193
 *      e = expression to check
1194
 *      refs = `true`: escape by value, `false`: escape by `ref`
1195
 *      gag = do not print error messages
1196
 * Returns:
1197
 *      `true` if references to the stack can escape
1198
 */
1199
private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag)
1200
{
1201
    enum log = false;
1202 1
    if (log) printf("[%s] checkReturnEscapeImpl, refs: %d e: `%s`\n", e.loc.toChars(), refs, e.toChars());
1203 1
    EscapeByResults er;
1204

1205 1
    if (refs)
1206 1
        escapeByRef(e, &er);
1207
    else
1208 1
        escapeByValue(e, &er);
1209

1210 1
    if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
1211 1
        return false;
1212

1213 1
    bool result = false;
1214 1
    foreach (VarDeclaration v; er.byvalue)
1215
    {
1216 1
        if (log) printf("byvalue `%s`\n", v.toChars());
1217 1
        if (v.isDataseg())
1218 1
            continue;
1219

1220 1
        Dsymbol p = v.toParent2();
1221

1222 1
        if ((v.isScope() || (v.storage_class & STC.maybescope)) &&
1223 1
            !(v.storage_class & STC.return_) &&
1224 1
            v.isParameter() &&
1225 1
            !v.doNotInferReturn &&
1226 1
            sc.func.flags & FUNCFLAG.returnInprocess &&
1227 1
            p == sc.func)
1228
        {
1229 1
            inferReturn(sc.func, v);        // infer addition of 'return'
1230 1
            continue;
1231
        }
1232

1233 1
        if (v.isScope())
1234
        {
1235 1
            if (v.storage_class & STC.return_)
1236 1
                continue;
1237

1238 1
            if (sc._module && sc._module.isRoot() &&
1239
                /* This case comes up when the ReturnStatement of a __foreachbody is
1240
                 * checked for escapes by the caller of __foreachbody. Skip it.
1241
                 *
1242
                 * struct S { static int opApply(int delegate(S*) dg); }
1243
                 * S* foo() {
1244
                 *    foreach (S* s; S) // create __foreachbody for body of foreach
1245
                 *        return s;     // s is inferred as 'scope' but incorrectly tested in foo()
1246
                 *    return null; }
1247
                 */
1248 1
                !(!refs && p.parent == sc.func && p.isFuncDeclaration() && p.isFuncDeclaration().fes) &&
1249
                /*
1250
                 *  auto p(scope string s) {
1251
                 *      string scfunc() { return s; }
1252
                 *  }
1253
                 */
1254 1
                !(!refs && p.isFuncDeclaration() && sc.func.isFuncDeclaration().getLevel(p.isFuncDeclaration(), sc.intypeof) > 0)
1255
               )
1256
            {
1257
                // Only look for errors if in module listed on command line
1258 1
                if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029
1259
                {
1260 1
                    if (!gag)
1261 1
                        error(e.loc, "scope variable `%s` may not be returned", v.toChars());
1262 1
                    result = true;
1263
                }
1264 1
                continue;
1265
            }
1266
        }
1267 1
        else if (v.storage_class & STC.variadic && p == sc.func)
1268
        {
1269 1
            Type tb = v.type.toBasetype();
1270 1
            if (tb.ty == Tarray || tb.ty == Tsarray)
1271
            {
1272 1
                if (!gag)
1273 1
                    error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars());
1274 1
                result = false;
1275
            }
1276
        }
1277
        else
1278
        {
1279
            //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
1280 1
            v.doNotInferScope = true;
1281
        }
1282
    }
1283

1284 1
    foreach (VarDeclaration v; er.byref)
1285
    {
1286 1
        if (log) printf("byref `%s`\n", v.toChars());
1287

1288
        // 'emitError' tells us whether to emit an error or a deprecation,
1289
        // depending on the flag passed to the CLI for DIP25
1290
        void escapingRef(VarDeclaration v, bool emitError = true)
1291
        {
1292 1
            if (!gag)
1293
            {
1294 1
                const(char)* msg, supplemental;
1295 1
                if (v.storage_class & STC.parameter &&
1296 1
                    (v.type.hasPointers() || v.storage_class & STC.ref_))
1297
                {
1298 1
                    msg = "returning `%s` escapes a reference to parameter `%s`";
1299 1
                    supplemental = "perhaps annotate the parameter with `return`";
1300
                }
1301
                else
1302
                {
1303 1
                    msg = "returning `%s` escapes a reference to local variable `%s`";
1304 1
                    if (v.ident is Id.This)
1305 0
                        supplemental = "perhaps annotate the function with `return`";
1306
                }
1307

1308 1
                if (emitError)
1309
                {
1310 1
                    e.error(msg, e.toChars(), v.toChars());
1311 1
                    if (supplemental)
1312 1
                        e.errorSupplemental(supplemental);
1313
                }
1314
                else
1315
                {
1316 1
                    e.deprecation(msg, e.toChars(), v.toChars());
1317 1
                    if (supplemental)
1318 1
                        deprecationSupplemental(e.loc, supplemental);
1319
                }
1320
            }
1321 1
            result = true;
1322
        }
1323

1324 1
        if (v.isDataseg())
1325 1
            continue;
1326

1327 1
        Dsymbol p = v.toParent2();
1328

1329
        // https://issues.dlang.org/show_bug.cgi?id=19965
1330 1
        if (!refs && sc.func.vthis == v)
1331 1
            notMaybeScope(v);
1332

1333 1
        if ((v.storage_class & (STC.ref_ | STC.out_)) == 0)
1334
        {
1335 1
            if (p == sc.func)
1336
            {
1337 1
                escapingRef(v);
1338 1
                continue;
1339
            }
1340 1
            FuncDeclaration fd = p.isFuncDeclaration();
1341 1
            if (fd && sc.func.flags & FUNCFLAG.returnInprocess)
1342
            {
1343
                /* Code like:
1344
                 *   int x;
1345
                 *   auto dg = () { return &x; }
1346
                 * Making it:
1347
                 *   auto dg = () return { return &x; }
1348
                 * Because dg.ptr points to x, this is returning dt.ptr+offset
1349
                 */
1350 1
                if (global.params.vsafe)
1351
                {
1352 1
                    sc.func.storage_class |= STC.return_ | STC.returninferred;
1353
                }
1354
            }
1355

1356
        }
1357

1358
        /* Check for returning a ref variable by 'ref', but should be 'return ref'
1359
         * Infer the addition of 'return', or set result to be the offending expression.
1360
         */
1361 1
        if ( (v.storage_class & (STC.ref_ | STC.out_)) &&
1362 1
            !(v.storage_class & (STC.return_ | STC.foreach_)))
1363
        {
1364 1
            if (sc.func.flags & FUNCFLAG.returnInprocess && p == sc.func)
1365
            {
1366 1
                inferReturn(sc.func, v);        // infer addition of 'return'
1367
            }
1368 1
            else if (sc._module && sc._module.isRoot())
1369
            {
1370
                // If -preview=dip25 is used, the user wants an error
1371
                // Otherwise, issue a deprecation
1372 1
                const emitError = global.params.useDIP25;
1373
                // https://dlang.org/spec/function.html#return-ref-parameters
1374
                // Only look for errors if in module listed on command line
1375 1
                if (p == sc.func)
1376
                {
1377
                    //printf("escaping reference to local ref variable %s\n", v.toChars());
1378
                    //printf("storage class = x%llx\n", v.storage_class);
1379 1
                    escapingRef(v, emitError);
1380 1
                    continue;
1381
                }
1382
                // Don't need to be concerned if v's parent does not return a ref
1383 1
                FuncDeclaration fd = p.isFuncDeclaration();
1384 1
                if (fd && fd.type && fd.type.ty == Tfunction)
1385
                {
1386 1
                    TypeFunction tf = cast(TypeFunction)fd.type;
1387 1
                    if (tf.isref)
1388
                    {
1389 1
                        const(char)* msg = "escaping reference to outer local variable `%s`";
1390 1
                        if (!gag && emitError)
1391 1
                            error(e.loc, msg, v.toChars());
1392 0
                        else if (!gag)
1393 0
                            deprecation(e.loc, msg, v.toChars());
1394 1
                        result = true;
1395 1
                        continue;
1396
                    }
1397
                }
1398

1399
            }
1400
        }
1401
    }
1402

1403 1
    foreach (Expression ee; er.byexp)
1404
    {
1405 1
        if (log) printf("byexp %s\n", ee.toChars());
1406 1
        if (!gag)
1407 1
            error(ee.loc, "escaping reference to stack allocated value returned by `%s`", ee.toChars());
1408 1
        result = true;
1409
    }
1410

1411 1
    return result;
1412
}
1413

1414

1415
/*************************************
1416
 * Variable v needs to have 'return' inferred for it.
1417
 * Params:
1418
 *      fd = function that v is a parameter to
1419
 *      v = parameter that needs to be STC.return_
1420
 */
1421

1422
private void inferReturn(FuncDeclaration fd, VarDeclaration v)
1423
{
1424
    // v is a local in the current function
1425

1426
    //printf("for function '%s' inferring 'return' for variable '%s'\n", fd.toChars(), v.toChars());
1427 1
    v.storage_class |= STC.return_ | STC.returninferred;
1428

1429 1
    TypeFunction tf = cast(TypeFunction)fd.type;
1430 1
    if (v == fd.vthis)
1431
    {
1432
        /* v is the 'this' reference, so mark the function
1433
         */
1434 1
        fd.storage_class |= STC.return_ | STC.returninferred;
1435 1
        if (tf.ty == Tfunction)
1436
        {
1437
            //printf("'this' too %p %s\n", tf, sc.func.toChars());
1438 1
            tf.isreturn = true;
1439 1
            tf.isreturninferred = true;
1440
        }
1441
    }
1442
    else
1443
    {
1444
        // Perform 'return' inference on parameter
1445 1
        if (tf.ty == Tfunction)
1446
        {
1447 1
            foreach (i, p; tf.parameterList)
1448
            {
1449 1
                if (p.ident == v.ident)
1450
                {
1451 1
                    p.storageClass |= STC.return_ | STC.returninferred;
1452
                    break;              // there can be only one
1453
                }
1454
            }
1455
        }
1456
    }
1457
}
1458

1459

1460
/****************************************
1461
 * e is an expression to be returned by value, and that value contains pointers.
1462
 * Walk e to determine which variables are possibly being
1463
 * returned by value, such as:
1464
 *      int* function(int* p) { return p; }
1465
 * If e is a form of &p, determine which variables have content
1466
 * which is being returned as ref, such as:
1467
 *      int* function(int i) { return &i; }
1468
 * Multiple variables can be inserted, because of expressions like this:
1469
 *      int function(bool b, int i, int* p) { return b ? &i : p; }
1470
 *
1471
 * No side effects.
1472
 *
1473
 * Params:
1474
 *      e = expression to be returned by value
1475
 *      er = where to place collected data
1476
 *      live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`.
1477
 */
1478
void escapeByValue(Expression e, EscapeByResults* er, bool live = false)
1479
{
1480
    //printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars());
1481
    extern (C++) final class EscapeVisitor : Visitor
1482
    {
1483
        alias visit = Visitor.visit;
1484
    public:
1485
        EscapeByResults* er;
1486
        bool live;
1487

1488 1
        extern (D) this(EscapeByResults* er, bool live)
1489
        {
1490 1
            this.er = er;
1491 1
            this.live = live;
1492
        }
1493

1494
        override void visit(Expression e)
1495
        {
1496
        }
1497

1498
        override void visit(AddrExp e)
1499
        {
1500
            /* Taking the address of struct literal is normally not
1501
             * allowed, but CTFE can generate one out of a new expression,
1502
             * but it'll be placed in static data so no need to check it.
1503
             */
1504 1
            if (e.e1.op != TOK.structLiteral)
1505 1
                escapeByRef(e.e1, er, live);
1506
        }
1507

1508
        override void visit(SymOffExp e)
1509
        {
1510 1
            VarDeclaration v = e.var.isVarDeclaration();
1511 1
            if (v)
1512 1
                er.byref.push(v);
1513
        }
1514

1515
        override void visit(VarExp e)
1516
        {
1517 1
            if (auto v = e.var.isVarDeclaration())
1518
            {
1519 1
                if (v.type.hasPointers() || // not tracking non-pointers
1520 1
                    v.storage_class & STC.lazy_) // lazy variables are actually pointers
1521 1
                    er.byvalue.push(v);
1522
            }
1523
        }
1524

1525
        override void visit(ThisExp e)
1526
        {
1527 1
            if (e.var)
1528 1
                er.byvalue.push(e.var);
1529
        }
1530

1531
        override void visit(PtrExp e)
1532
        {
1533 1
            if (live && e.type.hasPointers())
1534 0
                e.e1.accept(this);
1535
        }
1536

1537
        override void visit(DotVarExp e)
1538
        {
1539 1
            auto t = e.e1.type.toBasetype();
1540 1
            if (e.type.hasPointers() && (live || t.ty == Tstruct))
1541
            {
1542 1
                e.e1.accept(this);
1543
            }
1544
        }
1545

1546
        override void visit(DelegateExp e)
1547
        {
1548 1
            Type t = e.e1.type.toBasetype();
1549 1
            if (t.ty == Tclass || t.ty == Tpointer)
1550 1
                escapeByValue(e.e1, er, live);
1551
            else
1552 1
                escapeByRef(e.e1, er, live);
1553 1
            er.byfunc.push(e.func);
1554
        }
1555

1556
        override void visit(FuncExp e)
1557
        {
1558 1
            if (e.fd.tok == TOK.delegate_)
1559 1
                er.byfunc.push(e.fd);
1560
        }
1561

1562
        override void visit(TupleExp e)
1563
        {
1564 0
            assert(0); // should have been lowered by now
1565
        }
1566

1567
        override void visit(ArrayLiteralExp e)
1568
        {
1569 1
            Type tb = e.type.toBasetype();
1570 1
            if (tb.ty == Tsarray || tb.ty == Tarray)
1571
            {
1572 1
                if (e.basis)
1573 1
                    e.basis.accept(this);
1574 1
                foreach (el; *e.elements)
1575
                {
1576 1
                    if (el)
1577 1
                        el.accept(this);
1578
                }
1579
            }
1580
        }
1581

1582
        override void visit(StructLiteralExp e)
1583
        {
1584 1
            if (e.elements)
1585
            {
1586 1
                foreach (ex; *e.elements)
1587
                {
1588 1
                    if (ex)
1589 1
                        ex.accept(this);
1590
                }
1591
            }
1592
        }
1593

1594
        override void visit(NewExp e)
1595
        {
1596 1
            Type tb = e.newtype.toBasetype();
1597 1
            if (tb.ty == Tstruct && !e.member && e.arguments)
1598
            {
1599 1
                foreach (ex; *e.arguments)
1600
                {
1601 1
                    if (ex)
1602 1
                        ex.accept(this);
1603
                }
1604
            }
1605
        }
1606

1607
        override void visit(CastExp e)
1608
        {
1609 1
            if (!e.type.hasPointers())
1610 1
                return;
1611 1
            Type tb = e.type.toBasetype();
1612 1
            if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray)
1613
            {
1614 1
                escapeByRef(e.e1, er, live);
1615
            }
1616
            else
1617 1
                e.e1.accept(this);
1618
        }
1619

1620
        override void visit(SliceExp e)
1621
        {
1622 1
            if (e.e1.op == TOK.variable)
1623
            {
1624 1
                VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration();
1625 1
                Type tb = e.type.toBasetype();
1626 1
                if (v)
1627
                {
1628 1
                    if (tb.ty == Tsarray)
1629 1
                        return;
1630 1
                    if (v.storage_class & STC.variadic)
1631
                    {
1632 1
                        er.byvalue.push(v);
1633 1
                        return;
1634
                    }
1635
                }
1636
            }
1637 1
            Type t1b = e.e1.type.toBasetype();
1638 1
            if (t1b.ty == Tsarray)
1639
            {
1640 1
                Type tb = e.type.toBasetype();
1641 1
                if (tb.ty != Tsarray)
1642 1
                    escapeByRef(e.e1, er, live);
1643
            }
1644
            else
1645 1
                e.e1.accept(this);
1646
        }
1647

1648
        override void visit(IndexExp e)
1649
        {
1650 1
            if (e.e1.type.toBasetype().ty == Tsarray ||
1651 1
                live && e.type.hasPointers())
1652
            {
1653 1
                e.e1.accept(this);
1654
            }
1655
        }
1656

1657
        override void visit(BinExp e)
1658
        {
1659 1
            Type tb = e.type.toBasetype();
1660 1
            if (tb.ty == Tpointer)
1661
            {
1662 1
                e.e1.accept(this);
1663 1
                e.e2.accept(this);
1664
            }
1665
        }
1666

1667
        override void visit(BinAssignExp e)
1668
        {
1669 1
            e.e1.accept(this);
1670
        }
1671

1672
        override void visit(AssignExp e)
1673
        {
1674 1
            e.e1.accept(this);
1675
        }
1676

1677
        override void visit(CommaExp e)
1678
        {
1679 1
            e.e2.accept(this);
1680
        }
1681

1682
        override void visit(CondExp e)
1683
        {
1684 1
            e.e1.accept(this);
1685 1
            e.e2.accept(this);
1686
        }
1687

1688
        override void visit(CallExp e)
1689
        {
1690
            //printf("CallExp(): %s\n", e.toChars());
1691
            /* Check each argument that is
1692
             * passed as 'return scope'.
1693
             */
1694 1
            Type t1 = e.e1.type.toBasetype();
1695 1
            TypeFunction tf;
1696 1
            TypeDelegate dg;
1697 1
            if (t1.ty == Tdelegate)
1698
            {
1699 1
                dg = cast(TypeDelegate)t1;
1700 1
                tf = cast(TypeFunction)(cast(TypeDelegate)t1).next;
1701
            }
1702 1
            else if (t1.ty == Tfunction)
1703 1
                tf = cast(TypeFunction)t1;
1704
            else
1705 0
                return;
1706

1707 1
            if (!e.type.hasPointers())
1708 1
                return;
1709

1710 1
            if (e.arguments && e.arguments.dim)
1711
            {
1712
                /* j=1 if _arguments[] is first argument,
1713
                 * skip it because it is not passed by ref
1714
                 */
1715 1
                int j = tf.isDstyleVariadic();
1716 1
                for (size_t i = j; i < e.arguments.dim; ++i)
1717
                {
1718 1
                    Expression arg = (*e.arguments)[i];
1719 1
                    size_t nparams = tf.parameterList.length;
1720 1
                    if (i - j < nparams && i >= j)
1721
                    {
1722 1
                        Parameter p = tf.parameterList[i - j];
1723 1
                        const stc = tf.parameterStorageClass(null, p);
1724 1
                        if ((stc & (STC.scope_)) && (stc & STC.return_))
1725 1
                            arg.accept(this);
1726 1
                        else if ((stc & (STC.ref_)) && (stc & STC.return_))
1727
                        {
1728 1
                            if (tf.isref)
1729
                            {
1730
                                /* Treat:
1731
                                 *   ref P foo(return ref P p)
1732
                                 * as:
1733
                                 *   p;
1734
                                 */
1735 1
                                arg.accept(this);
1736
                            }
1737
                            else
1738 1
                                escapeByRef(arg, er, live);
1739
                        }
1740
                    }
1741
                }
1742
            }
1743
            // If 'this' is returned, check it too
1744 1
            if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction)
1745
            {
1746 1
                DotVarExp dve = cast(DotVarExp)e.e1;
1747 1
                FuncDeclaration fd = dve.var.isFuncDeclaration();
1748 1
                AggregateDeclaration ad;
1749 1
                if (global.params.vsafe && tf.isreturn && fd && (ad = fd.isThis()) !is null)
1750
                {
1751 1
                    if (ad.isClassDeclaration() || tf.isScopeQual)       // this is 'return scope'
1752 1
                        dve.e1.accept(this);
1753 1
                    else if (ad.isStructDeclaration()) // this is 'return ref'
1754
                    {
1755 1
                        if (tf.isref)
1756
                        {
1757
                            /* Treat calling:
1758
                             *   struct S { ref S foo() return; }
1759
                             * as:
1760
                             *   this;
1761
                             */
1762 1
                            dve.e1.accept(this);
1763
                        }
1764
                        else
1765 1
                            escapeByRef(dve.e1, er, live);
1766
                    }
1767
                }
1768 1
                else if (dve.var.storage_class & STC.return_ || tf.isreturn)
1769
                {
1770 1
                    if (dve.var.storage_class & STC.scope_)
1771 0
                        dve.e1.accept(this);
1772 1
                    else if (dve.var.storage_class & STC.ref_)
1773 0
                        escapeByRef(dve.e1, er, live);
1774
                }
1775
                // If it's also a nested function that is 'return scope'
1776 1
                if (fd && fd.isNested())
1777
                {
1778 1
                    if (tf.isreturn && tf.isScopeQual)
1779 0
                        er.byexp.push(e);
1780
                }
1781
            }
1782

1783
            /* If returning the result of a delegate call, the .ptr
1784
             * field of the delegate must be checked.
1785
             */
1786 1
            if (dg)
1787
            {
1788 1
                if (tf.isreturn)
1789 1
                    e.e1.accept(this);
1790
            }
1791

1792
            /* If it's a nested function that is 'return scope'
1793
             */
1794 1
            if (e.e1.op == TOK.variable)
1795
            {
1796 1
                VarExp ve = cast(VarExp)e.e1;
1797 1
                FuncDeclaration fd = ve.var.isFuncDeclaration();
1798 1
                if (fd && fd.isNested())
1799
                {
1800 1
                    if (tf.isreturn && tf.isScopeQual)
1801 1
                        er.byexp.push(e);
1802
                }
1803
            }
1804
        }
1805
    }
1806

1807 1
    scope EscapeVisitor v = new EscapeVisitor(er, live);
1808 1
    e.accept(v);
1809
}
1810

1811

1812
/****************************************
1813
 * e is an expression to be returned by 'ref'.
1814
 * Walk e to determine which variables are possibly being
1815
 * returned by ref, such as:
1816
 *      ref int function(int i) { return i; }
1817
 * If e is a form of *p, determine which variables have content
1818
 * which is being returned as ref, such as:
1819
 *      ref int function(int* p) { return *p; }
1820
 * Multiple variables can be inserted, because of expressions like this:
1821
 *      ref int function(bool b, int i, int* p) { return b ? i : *p; }
1822
 *
1823
 * No side effects.
1824
 *
1825
 * Params:
1826
 *      e = expression to be returned by 'ref'
1827
 *      er = where to place collected data
1828
 *      live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`.
1829
 */
1830
void escapeByRef(Expression e, EscapeByResults* er, bool live = false)
1831
{
1832
    //printf("[%s] escapeByRef, e: %s\n", e.loc.toChars(), e.toChars());
1833
    extern (C++) final class EscapeRefVisitor : Visitor
1834
    {
1835
        alias visit = Visitor.visit;
1836
    public:
1837
        EscapeByResults* er;
1838
        bool live;
1839

1840 1
        extern (D) this(EscapeByResults* er, bool live)
1841
        {
1842 1
            this.er = er;
1843 1
            this.live = live;
1844
        }
1845

1846
        override void visit(Expression e)
1847
        {
1848
        }
1849

1850
        override void visit(VarExp e)
1851
        {
1852 1
            auto v = e.var.isVarDeclaration();
1853 1
            if (v)
1854
            {
1855 1
                if (v.storage_class & STC.ref_ && v.storage_class & (STC.foreach_ | STC.temp) && v._init)
1856
                {
1857
                    /* If compiler generated ref temporary
1858
                     *   (ref v = ex; ex)
1859
                     * look at the initializer instead
1860
                     */
1861 1
                    if (ExpInitializer ez = v._init.isExpInitializer())
1862
                    {
1863 1
                        assert(ez.exp && ez.exp.op == TOK.construct);
1864 1
                        Expression ex = (cast(ConstructExp)ez.exp).e2;
1865 1
                        ex.accept(this);
1866
                    }
1867
                }
1868
                else
1869 1
                    er.byref.push(v);
1870
            }
1871
        }
1872

1873
        override void visit(ThisExp e)
1874
        {
1875 1
            if (e.var && e.var.toParent2().isFuncDeclaration().isThis2)
1876 1
                escapeByValue(e, er, live);
1877 1
            else if (e.var)
1878 1
                er.byref.push(e.var);
1879
        }
1880

1881
        override void visit(PtrExp e)
1882
        {
1883 1
            escapeByValue(e.e1, er, live);
1884
        }
1885

1886
        override void visit(IndexExp e)
1887
        {
1888 1
            Type tb = e.e1.type.toBasetype();
1889 1
            if (e.e1.op == TOK.variable)
1890
            {
1891 1
                VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration();
1892 1
                if (tb.ty == Tarray || tb.ty == Tsarray)
1893
                {
1894 1
                    if (v && v.storage_class & STC.variadic)
1895
                    {
1896 1
                        er.byref.push(v);
1897 1
                        return;
1898
                    }
1899
                }
1900
            }
1901 1
            if (tb.ty == Tsarray)
1902
            {
1903 1
                e.e1.accept(this);
1904
            }
1905 1
            else if (tb.ty == Tarray)
1906
            {
1907 1
                escapeByValue(e.e1, er, live);
1908
            }
1909
        }
1910

1911
        override void visit(StructLiteralExp e)
1912
        {
1913 1
            if (e.elements)
1914
            {
1915 1
                foreach (ex; *e.elements)
1916
                {
1917 1
                    if (ex)
1918 1
                        ex.accept(this);
1919
                }
1920
            }
1921 1
            er.byexp.push(e);
1922
        }
1923

1924
        override void visit(DotVarExp e)
1925
        {
1926 1
            Type t1b = e.e1.type.toBasetype();
1927 1
            if (t1b.ty == Tclass)
1928 1
                escapeByValue(e.e1, er, live);
1929
            else
1930 1
                e.e1.accept(this);
1931
        }
1932

1933
        override void visit(BinAssignExp e)
1934
        {
1935 1
            e.e1.accept(this);
1936
        }
1937

1938
        override void visit(AssignExp e)
1939
        {
1940 1
            e.e1.accept(this);
1941
        }
1942

1943
        override void visit(CommaExp e)
1944
        {
1945 0
            e.e2.accept(this);
1946
        }
1947

1948
        override void visit(CondExp e)
1949
        {
1950 1
            e.e1.accept(this);
1951 1
            e.e2.accept(this);
1952
        }
1953

1954
        override void visit(CallExp e)
1955
        {
1956
            //printf("escapeByRef.CallExp(): %s\n", e.toChars());
1957
            /* If the function returns by ref, check each argument that is
1958
             * passed as 'return ref'.
1959
             */
1960 1
            Type t1 = e.e1.type.toBasetype();
1961 1
            TypeFunction tf;
1962 1
            if (t1.ty == Tdelegate)
1963 1
                tf = cast(TypeFunction)(cast(TypeDelegate)t1).next;
1964 1
            else if (t1.ty == Tfunction)
1965 1
                tf = cast(TypeFunction)t1;
1966
            else
1967 0
                return;
1968 1
            if (tf.isref)
1969
            {
1970 1
                if (e.arguments && e.arguments.dim)
1971
                {
1972
                    /* j=1 if _arguments[] is first argument,
1973
                     * skip it because it is not passed by ref
1974
                     */
1975 1
                    int j = tf.isDstyleVariadic();
1976 1
                    for (size_t i = j; i < e.arguments.dim; ++i)
1977
                    {
1978 1
                        Expression arg = (*e.arguments)[i];
1979 1
                        size_t nparams = tf.parameterList.length;
1980 1
                        if (i - j < nparams && i >= j)
1981
                        {
1982 1
                            Parameter p = tf.parameterList[i - j];
1983 1
                            const stc = tf.parameterStorageClass(null, p);
1984 1
                            if ((stc & (STC.out_ | STC.ref_)) && (stc & STC.return_))
1985 1
                                arg.accept(this);
1986 1
                            else if ((stc & STC.scope_) && (stc & STC.return_))
1987
                            {
1988 1
                                if (arg.op == TOK.delegate_)
1989
                                {
1990 1
                                    DelegateExp de = cast(DelegateExp)arg;
1991 1
                                    if (de.func.isNested())
1992 1
                                        er.byexp.push(de);
1993
                                }
1994
                                else
1995 1
                                    escapeByValue(arg, er, live);
1996
                            }
1997
                        }
1998
                    }
1999
                }
2000
                // If 'this' is returned by ref, check it too
2001 1
                if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction)
2002
                {
2003 1
                    DotVarExp dve = cast(DotVarExp)e.e1;
2004

2005
                    // https://issues.dlang.org/show_bug.cgi?id=20149#c10
2006 1
                    if (dve.var.isCtorDeclaration())
2007
                    {
2008 1
                        er.byexp.push(e);
2009 1
                        return;
2010
                    }
2011

2012 1
                    if (dve.var.storage_class & STC.return_ || tf.isreturn)
2013
                    {
2014 1
                        if (dve.var.storage_class & STC.ref_ || tf.isref)
2015 1
                            dve.e1.accept(this);
2016 0
                        else if (dve.var.storage_class & STC.scope_ || tf.isScopeQual)
2017 0
                            escapeByValue(dve.e1, er, live);
2018
                    }
2019
                    // If it's also a nested function that is 'return ref'
2020 1
                    FuncDeclaration fd = dve.var.isFuncDeclaration();
2021 1
                    if (fd && fd.isNested())
2022
                    {
2023 0
                        if (tf.isreturn)
2024 0
                            er.byexp.push(e);
2025
                    }
2026
                }
2027
                // If it's a delegate, check it too
2028 1
                if (e.e1.op == TOK.variable && t1.ty == Tdelegate)
2029
                {
2030 1
                    escapeByValue(e.e1, er, live);
2031
                }
2032

2033
                /* If it's a nested function that is 'return ref'
2034
                 */
2035 1
                if (e.e1.op == TOK.variable)
2036
                {
2037 1
                    VarExp ve = cast(VarExp)e.e1;
2038 1
                    FuncDeclaration fd = ve.var.isFuncDeclaration();
2039 1
                    if (fd && fd.isNested())
2040
                    {
2041 1
                        if (tf.isreturn)
2042 1
                            er.byexp.push(e);
2043
                    }
2044
                }
2045
            }
2046
            else
2047 1
                er.byexp.push(e);
2048
        }
2049
    }
2050

2051 1
    scope EscapeRefVisitor v = new EscapeRefVisitor(er, live);
2052 1
    e.accept(v);
2053
}
2054

2055

2056
/************************************
2057
 * Aggregate the data collected by the escapeBy??() functions.
2058
 */
2059
struct EscapeByResults
2060
{
2061
    VarDeclarations byref;      // array into which variables being returned by ref are inserted
2062
    VarDeclarations byvalue;    // array into which variables with values containing pointers are inserted
2063
    FuncDeclarations byfunc;    // nested functions that are turned into delegates
2064
    Expressions byexp;          // array into which temporaries being returned by ref are inserted
2065

2066
    /** Reset arrays so the storage can be used again
2067
     */
2068
    void reset()
2069
    {
2070 1
        byref.setDim(0);
2071 1
        byvalue.setDim(0);
2072 1
        byfunc.setDim(0);
2073 1
        byexp.setDim(0);
2074
    }
2075
}
2076

2077
/*************************
2078
 * Find all variables accessed by this delegate that are
2079
 * in functions enclosing it.
2080
 * Params:
2081
 *      fd = function
2082
 *      vars = array to append found variables to
2083
 */
2084
public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* vars)
2085
{
2086
    //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars());
2087 1
    for (auto p = fd.parent; p; p = p.parent)
2088
    {
2089 1
        auto fdp = p.isFuncDeclaration();
2090 1
        if (!fdp)
2091 1
            continue;
2092

2093 1
        foreach (v; fdp.closureVars)
2094
        {
2095 1
            foreach (const fdv; v.nestedrefs)
2096
            {
2097 1
                if (fdv == fd)
2098
                {
2099
                    //printf("accessed: %s, type %s\n", v.toChars(), v.type.toChars());
2100 1
                    vars.push(v);
2101
                }
2102
            }
2103
        }
2104
    }
2105
}
2106

2107
/***********************************
2108
 * Turn off `STC.maybescope` for variable `v`.
2109
 *
2110
 * This exists in order to find where `STC.maybescope` is getting turned off.
2111
 * Params:
2112
 *      v = variable
2113
 */
2114
version (none)
2115
{
2116
    public void notMaybeScope(string file = __FILE__, int line = __LINE__)(VarDeclaration v)
2117
    {
2118
        printf("%.*s(%d): notMaybeScope('%s')\n", cast(int)file.length, file.ptr, line, v.toChars());
2119
        v.storage_class &= ~STC.maybescope;
2120
    }
2121
}
2122
else
2123
{
2124
    public void notMaybeScope(VarDeclaration v)
2125
    {
2126 1
        v.storage_class &= ~STC.maybescope;
2127
    }
2128
}
2129

2130

2131
/**********************************************
2132
 * Have some variables that are maybescopes that were
2133
 * assigned values from other maybescope variables.
2134
 * Now that semantic analysis of the function is
2135
 * complete, we can finalize this by turning off
2136
 * maybescope for array elements that cannot be scope.
2137
 *
2138
 * $(TABLE2 Scope Table,
2139
 * $(THEAD `va`, `v`,    =>,  `va` ,  `v`  )
2140
 * $(TROW maybe, maybe,  =>,  scope,  scope)
2141
 * $(TROW scope, scope,  =>,  scope,  scope)
2142
 * $(TROW scope, maybe,  =>,  scope,  scope)
2143
 * $(TROW maybe, scope,  =>,  scope,  scope)
2144
 * $(TROW -    , -    ,  =>,  -    ,  -    )
2145
 * $(TROW -    , maybe,  =>,  -    ,  -    )
2146
 * $(TROW -    , scope,  =>,  error,  error)
2147
 * $(TROW maybe, -    ,  =>,  scope,  -    )
2148
 * $(TROW scope, -    ,  =>,  scope,  -    )
2149
 * )
2150
 * Params:
2151
 *      array = array of variables that were assigned to from maybescope variables
2152
 */
2153
public void eliminateMaybeScopes(VarDeclaration[] array)
2154
{
2155
    enum log = false;
2156 1
    if (log) printf("eliminateMaybeScopes()\n");
2157 1
    bool changes;
2158
    do
2159
    {
2160 1
        changes = false;
2161 1
        foreach (va; array)
2162
        {
2163 1
            if (log) printf("  va = %s\n", va.toChars());
2164 1
            if (!(va.storage_class & (STC.maybescope | STC.scope_)))
2165
            {
2166 1
                if (va.maybes)
2167
                {
2168 1
                    foreach (v; *va.maybes)
2169
                    {
2170 1
                        if (log) printf("    v = %s\n", v.toChars());
2171 1
                        if (v.storage_class & STC.maybescope)
2172
                        {
2173
                            // v cannot be scope since it is assigned to a non-scope va
2174 1
                            notMaybeScope(v);
2175 1
                            if (!(v.storage_class & (STC.ref_ | STC.out_)))
2176 1
                                v.storage_class &= ~(STC.return_ | STC.returninferred);
2177 1
                            changes = true;
2178
                        }
2179
                    }
2180
                }
2181
            }
2182
        }
2183 1
    } while (changes);
2184
}
2185

2186
/************************************************
2187
 * Is type a reference to a mutable value?
2188
 *
2189
 * This is used to determine if an argument that does not have a corresponding
2190
 * Parameter, i.e. a variadic argument, is a pointer to mutable data.
2191
 * Params:
2192
 *      t = type of the argument
2193
 * Returns:
2194
 *      true if it's a pointer (or reference) to mutable data
2195
 */
2196
bool isReferenceToMutable(Type t)
2197
{
2198 1
    t = t.baseElemOf();
2199

2200 1
    if (!t.isMutable() ||
2201 1
        !t.hasPointers())
2202 1
        return false;
2203

2204 1
    switch (t.ty)
2205
    {
2206 1
        case Tpointer:
2207 1
            if (t.nextOf().isTypeFunction())
2208 1
                break;
2209 1
            goto case;
2210

2211 1
        case Tarray:
2212 1
        case Taarray:
2213 1
        case Tdelegate:
2214 1
            if (t.nextOf().isMutable())
2215 1
                return true;
2216 1
            break;
2217

2218 1
        case Tclass:
2219 1
            return true;        // even if the class fields are not mutable
2220

2221 1
        case Tstruct:
2222
            // Have to look at each field
2223 1
            foreach (VarDeclaration v; t.isTypeStruct().sym.fields)
2224
            {
2225 1
                if (v.storage_class & STC.ref_)
2226
                {
2227 0
                    if (v.type.isMutable())
2228 0
                        return true;
2229
                }
2230 1
                else if (v.type.isReferenceToMutable())
2231 1
                    return true;
2232
            }
2233 1
            break;
2234

2235 0
        default:
2236 0
            assert(0);
2237
    }
2238 1
    return false;
2239
}
2240

2241
/****************************************
2242
 * Is parameter a reference to a mutable value?
2243
 *
2244
 * This is used if an argument has a corresponding Parameter.
2245
 * The argument type is necessary if the Parameter is inout.
2246
 * Params:
2247
 *      p = Parameter to check
2248
 *      t = type of corresponding argument
2249
 * Returns:
2250
 *      true if it's a pointer (or reference) to mutable data
2251
 */
2252
bool isReferenceToMutable(Parameter p, Type t)
2253
{
2254 1
    if (p.isReference())
2255
    {
2256 1
        if (p.type.isConst() || p.type.isImmutable())
2257 1
            return false;
2258 1
        if (p.type.isWild())
2259
        {
2260 1
            return t.isMutable();
2261
        }
2262 1
        return p.type.isMutable();
2263
    }
2264 1
    return isReferenceToMutable(p.type);
2265
}

Read our documentation on viewing source code .

Loading