1 /**
2  * Performs inlining, which is an optimization pass enabled with the `-inline` flag.
3  *
4  * The AST is traversed, and every function call is considered for inlining using `inlinecost.d`.
5  * The function call is then inlined if this cost is below a threshold.
6  *
7  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
8  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
9  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
10  * Source:    $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/inline.d, _inline.d)
11  * Documentation:  https://dlang.org/phobos/dmd_inline.html
12  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/inline.d
13  */
14 
15 module dmd.inline;
16 
17 import core.stdc.stdio;
18 import core.stdc.string;
19 
20 import dmd.aggregate;
21 import dmd.apply;
22 import dmd.arraytypes;
23 import dmd.astenums;
24 import dmd.attrib;
25 import dmd.declaration;
26 import dmd.dmodule;
27 import dmd.dscope;
28 import dmd.dstruct;
29 import dmd.dsymbol;
30 import dmd.dtemplate;
31 import dmd.expression;
32 import dmd.errors;
33 import dmd.func;
34 import dmd.globals;
35 import dmd.id;
36 import dmd.identifier;
37 import dmd.init;
38 import dmd.initsem;
39 import dmd.location;
40 import dmd.mtype;
41 import dmd.opover;
42 import dmd.printast;
43 import dmd.statement;
44 import dmd.tokens;
45 import dmd.visitor;
46 import dmd.inlinecost;
47 
48 /***********************************************************
49  * Scan function implementations in Module m looking for functions that can be inlined,
50  * and inline them in situ.
51  *
52  * Params:
53  *    m = module to scan
54  */
55 public void inlineScanModule(Module m)
56 {
57     if (m.semanticRun != PASS.semantic3done)
58         return;
59     m.semanticRun = PASS.inline;
60 
61     // Note that modules get their own scope, from scratch.
62     // This is so regardless of where in the syntax a module
63     // gets imported, it is unaffected by context.
64 
65     //printf("Module = %p\n", m.sc.scopesym);
66 
67     foreach (i; 0 .. m.members.length)
68     {
69         Dsymbol s = (*m.members)[i];
70         //if (global.params.verbose)
71         //    message("inline scan symbol %s", s.toChars());
72         inlineScanDsymbol(s);
73     }
74     m.semanticRun = PASS.inlinedone;
75 }
76 
77 private void inlineScanDsymbol(Dsymbol s)
78 {
79     scope InlineScanVisitorDsymbol v = new InlineScanVisitorDsymbol();
80     s.accept(v);
81 }
82 
83 /***********************************************************
84  * Perform the "inline copying" of a default argument for a function parameter.
85  *
86  * Todo:
87  *  The hack for bugzilla 4820 case is still questionable. Perhaps would have to
88  *  handle a delegate expression with 'null' context properly in front-end.
89  */
90 public Expression inlineCopy(Expression e, Scope* sc)
91 {
92     /* See https://issues.dlang.org/show_bug.cgi?id=2935
93      * for explanation of why just a copy() is broken
94      */
95     //return e.copy();
96     if (auto de = e.isDelegateExp())
97     {
98         if (de.func.isNested())
99         {
100             /* https://issues.dlang.org/show_bug.cgi?id=4820
101              * Defer checking until later if we actually need the 'this' pointer
102              */
103             return de.copy();
104         }
105     }
106     int cost = inlineCostExpression(e);
107     if (cost >= COST_MAX)
108     {
109         e.error("cannot inline default argument `%s`", e.toChars());
110         return ErrorExp.get();
111     }
112     scope ids = new InlineDoState(sc.parent, null);
113     return doInlineAs!Expression(e, ids);
114 }
115 
116 
117 
118 
119 
120 
121 private:
122 
123 
124 
125 enum LOG = false;
126 enum CANINLINE_LOG = false;
127 enum EXPANDINLINE_LOG = false;
128 
129 
130 /***********************************************************
131  * Represent a context to inline statements and expressions.
132  *
133  * Todo:
134  *  It would be better to make foundReturn an instance field of DoInlineAs visitor class,
135  *  like as DoInlineAs!Result.result field, because it's one another result of inlining.
136  *  The best would be to return a pair of result Expression and a bool value as foundReturn
137  *  from doInlineAs function.
138  */
139 private final class InlineDoState
140 {
141     // inline context
142     VarDeclaration vthis;
143     Dsymbols from;      // old Dsymbols
144     Dsymbols to;        // parallel array of new Dsymbols
145     Dsymbol parent;     // new parent
146     FuncDeclaration fd; // function being inlined (old parent)
147     // inline result
148     bool foundReturn;
149 
150     this(Dsymbol parent, FuncDeclaration fd) scope
151     {
152         this.parent = parent;
153         this.fd = fd;
154     }
155 }
156 
157 /***********************************************************
158  * Perform the inlining from (Statement or Expression) to (Statement or Expression).
159  *
160  * Inlining is done by:
161  *  - Converting to an Expression
162  *  - Copying the trees of the function to be inlined
163  *  - Renaming the variables
164  */
165 private extern (C++) final class DoInlineAs(Result) : Visitor
166 if (is(Result == Statement) || is(Result == Expression))
167 {
168     alias visit = Visitor.visit;
169 public:
170     InlineDoState ids;
171     Result result;
172 
173     enum asStatements = is(Result == Statement);
174 
175     extern (D) this(InlineDoState ids) scope
176     {
177         this.ids = ids;
178     }
179 
180     // Statement -> (Statement | Expression)
181 
182     override void visit(Statement s)
183     {
184         printf("Statement.doInlineAs!%s()\n%s\n", Result.stringof.ptr, s.toChars());
185         fflush(stdout);
186         assert(0); // default is we can't inline it
187     }
188 
189     override void visit(ExpStatement s)
190     {
191         static if (LOG)
192         {
193             if (s.exp)
194                 printf("ExpStatement.doInlineAs!%s() '%s'\n", Result.stringof.ptr, s.exp.toChars());
195         }
196 
197         auto exp = doInlineAs!Expression(s.exp, ids);
198         static if (asStatements)
199             result = new ExpStatement(s.loc, exp);
200         else
201             result = exp;
202     }
203 
204     override void visit(CompoundStatement s)
205     {
206         //printf("CompoundStatement.doInlineAs!%s() %d\n", Result.stringof.ptr, s.statements.length);
207         static if (asStatements)
208         {
209             auto as = new Statements();
210             as.reserve(s.statements.length);
211         }
212 
213         foreach (i, sx; *s.statements)
214         {
215             if (!sx)
216                 continue;
217             static if (asStatements)
218             {
219                 as.push(doInlineAs!Statement(sx, ids));
220             }
221             else
222             {
223                 /* Specifically allow:
224                  *  if (condition)
225                  *      return exp1;
226                  *  return exp2;
227                  */
228                 IfStatement ifs;
229                 Statement s3;
230                 if ((ifs = sx.isIfStatement()) !is null &&
231                     ifs.ifbody &&
232                     ifs.ifbody.endsWithReturnStatement() &&
233                     !ifs.elsebody &&
234                     i + 1 < s.statements.length &&
235                     (s3 = (*s.statements)[i + 1]) !is null &&
236                     s3.endsWithReturnStatement()
237                    )
238                 {
239                     /* Rewrite as ?:
240                      */
241                     auto econd = doInlineAs!Expression(ifs.condition, ids);
242                     assert(econd);
243                     auto e1 = doInlineAs!Expression(ifs.ifbody, ids);
244                     assert(ids.foundReturn);
245                     auto e2 = doInlineAs!Expression(s3, ids);
246                     assert(e2);
247                     Expression e = new CondExp(econd.loc, econd, e1, e2);
248                     e.type = e1.type;
249                     if (e.type.ty == Ttuple)
250                     {
251                         e1.type = Type.tvoid;
252                         e2.type = Type.tvoid;
253                         e.type = Type.tvoid;
254                     }
255                     result = Expression.combine(result, e);
256                 }
257                 else
258                 {
259                     ids.foundReturn = false;
260                     auto e = doInlineAs!Expression(sx, ids);
261                     result = Expression.combine(result, e);
262                 }
263             }
264 
265             if (ids.foundReturn)
266                 break;
267         }
268 
269         static if (asStatements)
270             result = new CompoundStatement(s.loc, as);
271     }
272 
273     override void visit(UnrolledLoopStatement s)
274     {
275         //printf("UnrolledLoopStatement.doInlineAs!%s() %d\n", Result.stringof.ptr, s.statements.length);
276         static if (asStatements)
277         {
278             auto as = new Statements();
279             as.reserve(s.statements.length);
280         }
281 
282         foreach (sx; *s.statements)
283         {
284             if (!sx)
285                 continue;
286             auto r = doInlineAs!Result(sx, ids);
287             static if (asStatements)
288                 as.push(r);
289             else
290                 result = Expression.combine(result, r);
291 
292             if (ids.foundReturn)
293                 break;
294         }
295 
296         static if (asStatements)
297             result = new UnrolledLoopStatement(s.loc, as);
298     }
299 
300     override void visit(ScopeStatement s)
301     {
302         //printf("ScopeStatement.doInlineAs!%s() %d\n", Result.stringof.ptr, s.statement.length);
303         auto r = doInlineAs!Result(s.statement, ids);
304         static if (asStatements)
305             result = new ScopeStatement(s.loc, r, s.endloc);
306         else
307             result = r;
308     }
309 
310     override void visit(IfStatement s)
311     {
312         assert(!s.prm);
313         auto econd = doInlineAs!Expression(s.condition, ids);
314         assert(econd);
315 
316         auto ifbody = doInlineAs!Result(s.ifbody, ids);
317         bool bodyReturn = ids.foundReturn;
318 
319         ids.foundReturn = false;
320         auto elsebody = doInlineAs!Result(s.elsebody, ids);
321 
322         static if (asStatements)
323         {
324             result = new IfStatement(s.loc, s.prm, econd, ifbody, elsebody, s.endloc);
325         }
326         else
327         {
328             alias e1 = ifbody;
329             alias e2 = elsebody;
330             if (e1 && e2)
331             {
332                 result = new CondExp(econd.loc, econd, e1, e2);
333                 result.type = e1.type;
334                 if (result.type.ty == Ttuple)
335                 {
336                     e1.type = Type.tvoid;
337                     e2.type = Type.tvoid;
338                     result.type = Type.tvoid;
339                 }
340             }
341             else if (e1)
342             {
343                 result = new LogicalExp(econd.loc, EXP.andAnd, econd, e1);
344                 result.type = Type.tvoid;
345             }
346             else if (e2)
347             {
348                 result = new LogicalExp(econd.loc, EXP.orOr, econd, e2);
349                 result.type = Type.tvoid;
350             }
351             else
352             {
353                 result = econd;
354             }
355         }
356         ids.foundReturn = ids.foundReturn && bodyReturn;
357     }
358 
359     override void visit(ReturnStatement s)
360     {
361         //printf("ReturnStatement.doInlineAs!%s() '%s'\n", Result.stringof.ptr, s.exp ? s.exp.toChars() : "");
362         ids.foundReturn = true;
363 
364         auto exp = doInlineAs!Expression(s.exp, ids);
365         if (!exp) // https://issues.dlang.org/show_bug.cgi?id=14560
366                   // 'return' must not leave in the expand result
367             return;
368         static if (asStatements)
369         {
370             /* Any return statement should be the last statement in the function being
371              * inlined, otherwise things shouldn't have gotten this far. Since the
372              * return value is being ignored (otherwise it wouldn't be inlined as a statement)
373              * we only need to evaluate `exp` for side effects.
374              * Already disallowed this if `exp` produces an object that needs destruction -
375              * an enhancement would be to do the destruction here.
376              */
377             result = new ExpStatement(s.loc, exp);
378         }
379         else
380             result = exp;
381     }
382 
383     override void visit(ImportStatement s)
384     {
385         //printf("ImportStatement.doInlineAs!%s()\n", Result.stringof.ptr);
386     }
387 
388     override void visit(ForStatement s)
389     {
390         //printf("ForStatement.doInlineAs!%s()\n", Result.stringof.ptr);
391         static if (asStatements)
392         {
393             auto sinit = doInlineAs!Statement(s._init, ids);
394             auto scond = doInlineAs!Expression(s.condition, ids);
395             auto sincr = doInlineAs!Expression(s.increment, ids);
396             auto sbody = doInlineAs!Statement(s._body, ids);
397             result = new ForStatement(s.loc, sinit, scond, sincr, sbody, s.endloc);
398         }
399         else
400             result = null;  // cannot be inlined as an Expression
401     }
402 
403     override void visit(ThrowStatement s)
404     {
405         //printf("ThrowStatement.doInlineAs!%s() '%s'\n", Result.stringof.ptr, s.exp.toChars());
406         static if (asStatements)
407             result = new ThrowStatement(s.loc, doInlineAs!Expression(s.exp, ids));
408         else
409             result = null;  // cannot be inlined as an Expression
410     }
411 
412     // Expression -> (Statement | Expression)
413 
414     static if (asStatements)
415     {
416         override void visit(Expression e)
417         {
418             result = new ExpStatement(e.loc, doInlineAs!Expression(e, ids));
419         }
420     }
421     else
422     {
423         /******************************
424          * Perform doInlineAs() on an array of Expressions.
425          */
426         Expressions* arrayExpressionDoInline(Expressions* a)
427         {
428             if (!a)
429                 return null;
430 
431             auto newa = new Expressions(a.length);
432 
433             foreach (i; 0 .. a.length)
434             {
435                 (*newa)[i] = doInlineAs!Expression((*a)[i], ids);
436             }
437             return newa;
438         }
439 
440         override void visit(Expression e)
441         {
442             //printf("Expression.doInlineAs!%s(%s): %s\n", Result.stringof.ptr, EXPtoString(e.op).ptr, e.toChars());
443             result = e.copy();
444         }
445 
446         override void visit(SymOffExp e)
447         {
448             //printf("SymOffExp.doInlineAs!%s(%s)\n", Result.stringof.ptr, e.toChars());
449             foreach (i; 0 .. ids.from.length)
450             {
451                 if (e.var != ids.from[i])
452                     continue;
453                 auto se = e.copy().isSymOffExp();
454                 se.var = ids.to[i].isDeclaration();
455                 result = se;
456                 return;
457             }
458             result = e;
459         }
460 
461         override void visit(VarExp e)
462         {
463             //printf("VarExp.doInlineAs!%s(%s)\n", Result.stringof.ptr, e.toChars());
464             foreach (i; 0 .. ids.from.length)
465             {
466                 if (e.var != ids.from[i])
467                     continue;
468                 auto ve = e.copy().isVarExp();
469                 ve.var = ids.to[i].isDeclaration();
470                 result = ve;
471                 return;
472             }
473             if (ids.fd && e.var == ids.fd.vthis)
474             {
475                 result = new VarExp(e.loc, ids.vthis);
476                 if (ids.fd.hasDualContext())
477                     result = new AddrExp(e.loc, result);
478                 result.type = e.type;
479                 return;
480             }
481 
482             /* Inlining context pointer access for nested referenced variables.
483              * For example:
484              *      auto fun() {
485              *        int i = 40;
486              *        auto foo() {
487              *          int g = 2;
488              *          struct Result {
489              *            auto bar() { return i + g; }
490              *          }
491              *          return Result();
492              *        }
493              *        return foo();
494              *      }
495              *      auto t = fun();
496              * 'i' and 'g' are nested referenced variables in Result.bar(), so:
497              *      auto x = t.bar();
498              * should be inlined to:
499              *      auto x = *(t.vthis.vthis + i.voffset) + *(t.vthis + g.voffset)
500              */
501             auto v = e.var.isVarDeclaration();
502             if (v && v.nestedrefs.length && ids.vthis)
503             {
504                 Dsymbol s = ids.fd;
505                 auto fdv = v.toParent().isFuncDeclaration();
506                 assert(fdv);
507                 result = new VarExp(e.loc, ids.vthis);
508                 result.type = ids.vthis.type;
509                 if (ids.fd.hasDualContext())
510                 {
511                     // &__this
512                     result = new AddrExp(e.loc, result);
513                     result.type = ids.vthis.type.pointerTo();
514                 }
515                 while (s != fdv)
516                 {
517                     auto f = s.isFuncDeclaration();
518                     AggregateDeclaration ad;
519                     if (f && f.hasDualContext())
520                     {
521                         if (f.hasNestedFrameRefs())
522                         {
523                             result = new DotVarExp(e.loc, result, f.vthis);
524                             result.type = f.vthis.type;
525                         }
526                         // (*__this)[i]
527                         uint i = f.followInstantiationContext(fdv);
528                         if (i == 1 && f == ids.fd)
529                         {
530                             auto ve = e.copy().isVarExp();
531                             ve.originalScope = ids.fd;
532                             result = ve;
533                             return;
534                         }
535                         result = new PtrExp(e.loc, result);
536                         result.type = Type.tvoidptr.sarrayOf(2);
537                         auto ie = new IndexExp(e.loc, result, new IntegerExp(i));
538                         ie.indexIsInBounds = true; // no runtime bounds checking
539                         result = ie;
540                         result.type = Type.tvoidptr;
541                         s = f.toParentP(fdv);
542                         ad = s.isAggregateDeclaration();
543                         if (ad)
544                             goto Lad;
545                         continue;
546                     }
547                     else if ((ad = s.isThis()) !is null)
548                     {
549                 Lad:
550                         while (ad)
551                         {
552                             assert(ad.vthis);
553                             bool i = ad.followInstantiationContext(fdv);
554                             auto vthis = i ? ad.vthis2 : ad.vthis;
555                             result = new DotVarExp(e.loc, result, vthis);
556                             result.type = vthis.type;
557                             s = ad.toParentP(fdv);
558                             ad = s.isAggregateDeclaration();
559                         }
560                     }
561                     else if (f && f.isNested())
562                     {
563                         assert(f.vthis);
564                         if (f.hasNestedFrameRefs())
565                         {
566                             result = new DotVarExp(e.loc, result, f.vthis);
567                             result.type = f.vthis.type;
568                         }
569                         s = f.toParent2();
570                     }
571                     else
572                         assert(0);
573                     assert(s);
574                 }
575                 result = new DotVarExp(e.loc, result, v);
576                 result.type = v.type;
577                 //printf("\t==> result = %s, type = %s\n", result.toChars(), result.type.toChars());
578                 return;
579             }
580             else if (v && v.nestedrefs.length)
581             {
582                 auto ve = e.copy().isVarExp();
583                 ve.originalScope = ids.fd;
584                 result = ve;
585                 return;
586             }
587 
588             result = e;
589         }
590 
591         override void visit(ThisExp e)
592         {
593             //if (!ids.vthis)
594             //    e.error("no `this` when inlining `%s`", ids.parent.toChars());
595             if (!ids.vthis)
596             {
597                 result = e;
598                 return;
599             }
600             result = new VarExp(e.loc, ids.vthis);
601             if (ids.fd.hasDualContext())
602             {
603                 // __this[0]
604                 result.type = ids.vthis.type;
605                 auto ie = new IndexExp(e.loc, result, IntegerExp.literal!0);
606                 ie.indexIsInBounds = true; // no runtime bounds checking
607                 result = ie;
608                 if (e.type.ty == Tstruct)
609                 {
610                     result.type = e.type.pointerTo();
611                     result = new PtrExp(e.loc, result);
612                 }
613             }
614             result.type = e.type;
615         }
616 
617         override void visit(SuperExp e)
618         {
619             assert(ids.vthis);
620             result = new VarExp(e.loc, ids.vthis);
621             if (ids.fd.hasDualContext())
622             {
623                 // __this[0]
624                 result.type = ids.vthis.type;
625                 auto ie = new IndexExp(e.loc, result, IntegerExp.literal!0);
626                 ie.indexIsInBounds = true; // no runtime bounds checking
627                 result = ie;
628             }
629             result.type = e.type;
630         }
631 
632         override void visit(DeclarationExp e)
633         {
634             //printf("DeclarationExp.doInlineAs!%s(%s)\n", Result.stringof.ptr, e.toChars());
635             if (auto vd = e.declaration.isVarDeclaration())
636             {
637                 version (none)
638                 {
639                     // Need to figure this out before inlining can work for tuples
640                     if (auto tup = vd.toAlias().isTupleDeclaration())
641                     {
642                         tup.foreachVar((s) { s; });
643                         result = st.objects.length;
644                         return;
645                     }
646                 }
647                 if (vd.isStatic())
648                     return;
649 
650                 if (ids.fd && vd == ids.fd.nrvo_var)
651                 {
652                     foreach (i; 0 .. ids.from.length)
653                     {
654                         if (vd != ids.from[i])
655                             continue;
656                         if (vd._init && !vd._init.isVoidInitializer())
657                         {
658                             result = vd._init.initializerToExpression();
659                             assert(result);
660                             result = doInlineAs!Expression(result, ids);
661                         }
662                         else
663                             result = IntegerExp.literal!0;
664                         return;
665                     }
666                 }
667 
668                 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init);
669                 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration));
670                 vto.parent = ids.parent;
671                 vto.csym = null;
672                 vto.isym = null;
673 
674                 ids.from.push(vd);
675                 ids.to.push(vto);
676 
677                 if (vd._init)
678                 {
679                     if (vd._init.isVoidInitializer())
680                     {
681                         vto._init = new VoidInitializer(vd._init.loc);
682                     }
683                     else
684                     {
685                         auto ei = vd._init.initializerToExpression();
686                         assert(ei);
687                         vto._init = new ExpInitializer(ei.loc, doInlineAs!Expression(ei, ids));
688                     }
689                 }
690                 if (vd.edtor)
691                 {
692                     vto.edtor = doInlineAs!Expression(vd.edtor, ids);
693                 }
694                 auto de = e.copy().isDeclarationExp();
695                 de.declaration = vto;
696                 result = de;
697                 return;
698             }
699 
700             // Prevent the copy of the aggregates allowed in inlineable funcs
701             if (isInlinableNestedAggregate(e))
702                 return;
703 
704             /* This needs work, like DeclarationExp.toElem(), if we are
705              * to handle TemplateMixin's. For now, we just don't inline them.
706              */
707             visit(cast(Expression)e);
708         }
709 
710         override void visit(TypeidExp e)
711         {
712             //printf("TypeidExp.doInlineAs!%s(): %s\n", Result.stringof.ptr, e.toChars());
713             auto te = e.copy().isTypeidExp();
714             if (auto ex = isExpression(te.obj))
715             {
716                 te.obj = doInlineAs!Expression(ex, ids);
717             }
718             else
719                 assert(isType(te.obj));
720             result = te;
721         }
722 
723         override void visit(NewExp e)
724         {
725             //printf("NewExp.doInlineAs!%s(): %s\n", Result.stringof.ptr, e.toChars());
726             auto ne = e.copy().isNewExp();
727             ne.thisexp = doInlineAs!Expression(e.thisexp, ids);
728             ne.argprefix = doInlineAs!Expression(e.argprefix, ids);
729             ne.arguments = arrayExpressionDoInline(e.arguments);
730             result = ne;
731 
732             semanticTypeInfo(null, e.type);
733         }
734 
735         override void visit(UnaExp e)
736         {
737             auto ue = cast(UnaExp)e.copy();
738             ue.e1 = doInlineAs!Expression(e.e1, ids);
739             result = ue;
740         }
741 
742         override void visit(AssertExp e)
743         {
744             auto ae = e.copy().isAssertExp();
745             ae.e1 = doInlineAs!Expression(e.e1, ids);
746             ae.msg = doInlineAs!Expression(e.msg, ids);
747             result = ae;
748         }
749 
750         override void visit(CatExp e)
751         {
752             auto ce = e.copy().isCatExp();
753 
754             if (auto lowering = ce.lowering)
755                 ce.lowering = doInlineAs!Expression(lowering, ids);
756             else
757             {
758                 ce.e1 = doInlineAs!Expression(e.e1, ids);
759                 ce.e2 = doInlineAs!Expression(e.e2, ids);
760             }
761 
762             result = ce;
763         }
764 
765         override void visit(BinExp e)
766         {
767             auto be = cast(BinExp)e.copy();
768             be.e1 = doInlineAs!Expression(e.e1, ids);
769             be.e2 = doInlineAs!Expression(e.e2, ids);
770             result = be;
771         }
772 
773         override void visit(CallExp e)
774         {
775             auto ce = e.copy().isCallExp();
776             ce.e1 = doInlineAs!Expression(e.e1, ids);
777             ce.arguments = arrayExpressionDoInline(e.arguments);
778             result = ce;
779         }
780 
781         override void visit(AssignExp e)
782         {
783             visit(cast(BinExp)e);
784         }
785 
786         override void visit(LoweredAssignExp e)
787         {
788             result = doInlineAs!Expression(e.lowering, ids);
789         }
790 
791         override void visit(EqualExp e)
792         {
793             visit(cast(BinExp)e);
794 
795             Type t1 = e.e1.type.toBasetype();
796             if (t1.ty == Tarray || t1.ty == Tsarray)
797             {
798                 Type t = t1.nextOf().toBasetype();
799                 while (t.toBasetype().nextOf())
800                     t = t.nextOf().toBasetype();
801                 if (t.ty == Tstruct)
802                     semanticTypeInfo(null, t);
803             }
804             else if (t1.ty == Taarray)
805             {
806                 semanticTypeInfo(null, t1);
807             }
808         }
809 
810         override void visit(IndexExp e)
811         {
812             auto are = e.copy().isIndexExp();
813             are.e1 = doInlineAs!Expression(e.e1, ids);
814             if (e.lengthVar)
815             {
816                 //printf("lengthVar\n");
817                 auto vd = e.lengthVar;
818                 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init);
819                 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration));
820                 vto.parent = ids.parent;
821                 vto.csym = null;
822                 vto.isym = null;
823 
824                 ids.from.push(vd);
825                 ids.to.push(vto);
826 
827                 if (vd._init && !vd._init.isVoidInitializer())
828                 {
829                     auto ie = vd._init.isExpInitializer();
830                     assert(ie);
831                     vto._init = new ExpInitializer(ie.loc, doInlineAs!Expression(ie.exp, ids));
832                 }
833                 are.lengthVar = vto;
834             }
835             are.e2 = doInlineAs!Expression(e.e2, ids);
836             result = are;
837         }
838 
839         override void visit(SliceExp e)
840         {
841             auto are = e.copy().isSliceExp();
842             are.e1 = doInlineAs!Expression(e.e1, ids);
843             if (e.lengthVar)
844             {
845                 //printf("lengthVar\n");
846                 auto vd = e.lengthVar;
847                 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init);
848                 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration));
849                 vto.parent = ids.parent;
850                 vto.csym = null;
851                 vto.isym = null;
852 
853                 ids.from.push(vd);
854                 ids.to.push(vto);
855 
856                 if (vd._init && !vd._init.isVoidInitializer())
857                 {
858                     auto ie = vd._init.isExpInitializer();
859                     assert(ie);
860                     vto._init = new ExpInitializer(ie.loc, doInlineAs!Expression(ie.exp, ids));
861                 }
862 
863                 are.lengthVar = vto;
864             }
865             are.lwr = doInlineAs!Expression(e.lwr, ids);
866             are.upr = doInlineAs!Expression(e.upr, ids);
867             result = are;
868         }
869 
870         override void visit(TupleExp e)
871         {
872             auto ce = e.copy().isTupleExp();
873             ce.e0 = doInlineAs!Expression(e.e0, ids);
874             ce.exps = arrayExpressionDoInline(e.exps);
875             result = ce;
876         }
877 
878         override void visit(ArrayLiteralExp e)
879         {
880             auto ce = e.copy().isArrayLiteralExp();
881             ce.basis = doInlineAs!Expression(e.basis, ids);
882             ce.elements = arrayExpressionDoInline(e.elements);
883             result = ce;
884 
885             semanticTypeInfo(null, e.type);
886         }
887 
888         override void visit(AssocArrayLiteralExp e)
889         {
890             auto ce = e.copy().isAssocArrayLiteralExp();
891             ce.keys = arrayExpressionDoInline(e.keys);
892             ce.values = arrayExpressionDoInline(e.values);
893             result = ce;
894 
895             semanticTypeInfo(null, e.type);
896         }
897 
898         override void visit(StructLiteralExp e)
899         {
900             if (e.inlinecopy)
901             {
902                 result = e.inlinecopy;
903                 return;
904             }
905             auto ce = e.copy().isStructLiteralExp();
906             e.inlinecopy = ce;
907             ce.elements = arrayExpressionDoInline(e.elements);
908             e.inlinecopy = null;
909             result = ce;
910         }
911 
912         override void visit(ArrayExp e)
913         {
914             assert(0); // this should have been lowered to something else
915         }
916 
917         override void visit(CondExp e)
918         {
919             auto ce = e.copy().isCondExp();
920             ce.econd = doInlineAs!Expression(e.econd, ids);
921             ce.e1 = doInlineAs!Expression(e.e1, ids);
922             ce.e2 = doInlineAs!Expression(e.e2, ids);
923             result = ce;
924         }
925     }
926 }
927 
928 /// ditto
929 private Result doInlineAs(Result)(Statement s, InlineDoState ids)
930 {
931     if (!s)
932         return null;
933 
934     scope DoInlineAs!Result v = new DoInlineAs!Result(ids);
935     s.accept(v);
936     return v.result;
937 }
938 
939 /// ditto
940 private Result doInlineAs(Result)(Expression e, InlineDoState ids)
941 {
942     if (!e)
943         return null;
944 
945     scope DoInlineAs!Result v = new DoInlineAs!Result(ids);
946     e.accept(v);
947     return v.result;
948 }
949 
950 /***********************************************************
951  * Walk the trees, looking for functions to inline.
952  * Inline any that can be.
953  */
954 private extern (C++) final class InlineScanVisitor : Visitor
955 {
956     alias visit = Visitor.visit;
957 public:
958     FuncDeclaration parent;     // function being scanned
959     // As the visit method cannot return a value, these variables
960     // are used to pass the result from 'visit' back to 'inlineScan'
961     Statement sresult;
962     Expression eresult;
963     bool again;
964 
965     extern (D) this() scope
966     {
967     }
968 
969     override void visit(Statement s)
970     {
971     }
972 
973     override void visit(ExpStatement s)
974     {
975         static if (LOG)
976         {
977             printf("ExpStatement.inlineScan(%s)\n", s.toChars());
978         }
979         if (!s.exp)
980             return;
981 
982         Statement inlineScanExpAsStatement(ref Expression exp)
983         {
984             /* If there's a EXP.call at the top, then it may fail to inline
985              * as an Expression. Try to inline as a Statement instead.
986              */
987             if (auto ce = exp.isCallExp())
988             {
989                 visitCallExp(ce, null, true);
990                 if (eresult)
991                     exp = eresult;
992                 auto s = sresult;
993                 sresult = null;
994                 eresult = null;
995                 return s;
996             }
997 
998             /* If there's a CondExp or CommaExp at the top, then its
999              * sub-expressions may be inlined as statements.
1000              */
1001             if (auto e = exp.isCondExp())
1002             {
1003                 inlineScan(e.econd);
1004                 auto s1 = inlineScanExpAsStatement(e.e1);
1005                 auto s2 = inlineScanExpAsStatement(e.e2);
1006                 if (!s1 && !s2)
1007                     return null;
1008                 auto ifbody   = !s1 ? new ExpStatement(e.e1.loc, e.e1) : s1;
1009                 auto elsebody = !s2 ? new ExpStatement(e.e2.loc, e.e2) : s2;
1010                 return new IfStatement(exp.loc, null, e.econd, ifbody, elsebody, exp.loc);
1011             }
1012             if (auto e = exp.isCommaExp())
1013             {
1014                 /* If expression declares temporaries which have to be destructed
1015                  * at the end of the scope then it is better handled as an expression.
1016                  */
1017                 if (expNeedsDtor(e.e1))
1018                 {
1019                     inlineScan(exp);
1020                     return null;
1021                 }
1022 
1023                 auto s1 = inlineScanExpAsStatement(e.e1);
1024                 auto s2 = inlineScanExpAsStatement(e.e2);
1025                 if (!s1 && !s2)
1026                     return null;
1027                 auto a = new Statements();
1028                 a.push(!s1 ? new ExpStatement(e.e1.loc, e.e1) : s1);
1029                 a.push(!s2 ? new ExpStatement(e.e2.loc, e.e2) : s2);
1030                 return new CompoundStatement(exp.loc, a);
1031             }
1032 
1033             // inline as an expression
1034             inlineScan(exp);
1035             return null;
1036         }
1037 
1038         sresult = inlineScanExpAsStatement(s.exp);
1039     }
1040 
1041     override void visit(CompoundStatement s)
1042     {
1043         foreach (i; 0 .. s.statements.length)
1044         {
1045             inlineScan((*s.statements)[i]);
1046         }
1047     }
1048 
1049     override void visit(UnrolledLoopStatement s)
1050     {
1051         foreach (i; 0 .. s.statements.length)
1052         {
1053             inlineScan((*s.statements)[i]);
1054         }
1055     }
1056 
1057     override void visit(ScopeStatement s)
1058     {
1059         inlineScan(s.statement);
1060     }
1061 
1062     override void visit(WhileStatement s)
1063     {
1064         inlineScan(s.condition);
1065         inlineScan(s._body);
1066     }
1067 
1068     override void visit(DoStatement s)
1069     {
1070         inlineScan(s._body);
1071         inlineScan(s.condition);
1072     }
1073 
1074     override void visit(ForStatement s)
1075     {
1076         inlineScan(s._init);
1077         inlineScan(s.condition);
1078         inlineScan(s.increment);
1079         inlineScan(s._body);
1080     }
1081 
1082     override void visit(ForeachStatement s)
1083     {
1084         inlineScan(s.aggr);
1085         inlineScan(s._body);
1086     }
1087 
1088     override void visit(ForeachRangeStatement s)
1089     {
1090         inlineScan(s.lwr);
1091         inlineScan(s.upr);
1092         inlineScan(s._body);
1093     }
1094 
1095     override void visit(IfStatement s)
1096     {
1097         inlineScan(s.condition);
1098         inlineScan(s.ifbody);
1099         inlineScan(s.elsebody);
1100     }
1101 
1102     override void visit(SwitchStatement s)
1103     {
1104         //printf("SwitchStatement.inlineScan()\n");
1105         inlineScan(s.condition);
1106         inlineScan(s._body);
1107         Statement sdefault = s.sdefault;
1108         inlineScan(sdefault);
1109         s.sdefault = cast(DefaultStatement)sdefault;
1110         if (s.cases)
1111         {
1112             foreach (i; 0 .. s.cases.length)
1113             {
1114                 Statement scase = (*s.cases)[i];
1115                 inlineScan(scase);
1116                 (*s.cases)[i] = cast(CaseStatement)scase;
1117             }
1118         }
1119     }
1120 
1121     override void visit(CaseStatement s)
1122     {
1123         //printf("CaseStatement.inlineScan()\n");
1124         inlineScan(s.exp);
1125         inlineScan(s.statement);
1126     }
1127 
1128     override void visit(DefaultStatement s)
1129     {
1130         inlineScan(s.statement);
1131     }
1132 
1133     override void visit(ReturnStatement s)
1134     {
1135         //printf("ReturnStatement.inlineScan()\n");
1136         inlineScan(s.exp);
1137     }
1138 
1139     override void visit(SynchronizedStatement s)
1140     {
1141         inlineScan(s.exp);
1142         inlineScan(s._body);
1143     }
1144 
1145     override void visit(WithStatement s)
1146     {
1147         inlineScan(s.exp);
1148         inlineScan(s._body);
1149     }
1150 
1151     override void visit(TryCatchStatement s)
1152     {
1153         inlineScan(s._body);
1154         if (s.catches)
1155         {
1156             foreach (c; *s.catches)
1157             {
1158                 inlineScan(c.handler);
1159             }
1160         }
1161     }
1162 
1163     override void visit(TryFinallyStatement s)
1164     {
1165         inlineScan(s._body);
1166         inlineScan(s.finalbody);
1167     }
1168 
1169     override void visit(ThrowStatement s)
1170     {
1171         inlineScan(s.exp);
1172     }
1173 
1174     override void visit(LabelStatement s)
1175     {
1176         inlineScan(s.statement);
1177     }
1178 
1179     /********************************
1180      * Scan Statement s for inlining opportunities,
1181      * and if found replace s with an inlined one.
1182      * Params:
1183      *  s = Statement to be scanned and updated
1184      */
1185     void inlineScan(ref Statement s)
1186     {
1187         if (!s)
1188             return;
1189         assert(sresult is null);
1190         s.accept(this);
1191         if (sresult)
1192         {
1193             s = sresult;
1194             sresult = null;
1195         }
1196     }
1197 
1198     /* -------------------------- */
1199     void arrayInlineScan(Expressions* arguments)
1200     {
1201         if (arguments)
1202         {
1203             foreach (i; 0 .. arguments.length)
1204             {
1205                 inlineScan((*arguments)[i]);
1206             }
1207         }
1208     }
1209 
1210     override void visit(Expression e)
1211     {
1212     }
1213 
1214     void scanVar(Dsymbol s)
1215     {
1216         //printf("scanVar(%s %s)\n", s.kind(), s.toPrettyChars());
1217         VarDeclaration vd = s.isVarDeclaration();
1218         if (vd)
1219         {
1220             TupleDeclaration td = vd.toAlias().isTupleDeclaration();
1221             if (td)
1222             {
1223                 td.foreachVar((s)
1224                 {
1225                     scanVar(s); // TODO
1226                 });
1227             }
1228             else if (vd._init)
1229             {
1230                 if (ExpInitializer ie = vd._init.isExpInitializer())
1231                 {
1232                     inlineScan(ie.exp);
1233                 }
1234             }
1235         }
1236         else
1237         {
1238             inlineScanDsymbol(s);
1239         }
1240     }
1241 
1242     override void visit(DeclarationExp e)
1243     {
1244         //printf("DeclarationExp.inlineScan() %s\n", e.toChars());
1245         scanVar(e.declaration);
1246     }
1247 
1248     override void visit(UnaExp e)
1249     {
1250         inlineScan(e.e1);
1251     }
1252 
1253     override void visit(AssertExp e)
1254     {
1255         inlineScan(e.e1);
1256         inlineScan(e.msg);
1257     }
1258 
1259     override void visit(CatExp e)
1260     {
1261         if (auto lowering = e.lowering)
1262         {
1263             inlineScan(lowering);
1264             return;
1265         }
1266 
1267         inlineScan(e.e1);
1268         inlineScan(e.e2);
1269     }
1270 
1271     override void visit(BinExp e)
1272     {
1273         inlineScan(e.e1);
1274         inlineScan(e.e2);
1275     }
1276 
1277     override void visit(AssignExp e)
1278     {
1279         // Look for NRVO, as inlining NRVO function returns require special handling
1280         if (e.op == EXP.construct && e.e2.op == EXP.call)
1281         {
1282             auto ce = e.e2.isCallExp();
1283             if (ce.f && ce.f.isNRVO() && ce.f.nrvo_var) // NRVO
1284             {
1285                 if (auto ve = e.e1.isVarExp())
1286                 {
1287                     /* Inlining:
1288                      *   S s = foo();   // initializing by rvalue
1289                      *   S s = S(1);    // constructor call
1290                      */
1291                     Declaration d = ve.var;
1292                     if (d.storage_class & (STC.out_ | STC.ref_)) // refinit
1293                         goto L1;
1294                 }
1295                 else
1296                 {
1297                     /* Inlining:
1298                      *   this.field = foo();   // inside constructor
1299                      */
1300                     inlineScan(e.e1);
1301                 }
1302 
1303                 visitCallExp(ce, e.e1, false);
1304                 if (eresult)
1305                 {
1306                     //printf("call with nrvo: %s ==> %s\n", e.toChars(), eresult.toChars());
1307                     return;
1308                 }
1309             }
1310         }
1311     L1:
1312         visit(cast(BinExp)e);
1313     }
1314 
1315     override void visit(LoweredAssignExp e)
1316     {
1317         inlineScan(e.lowering);
1318     }
1319 
1320     override void visit(CallExp e)
1321     {
1322         //printf("CallExp.inlineScan() %s\n", e.toChars());
1323         visitCallExp(e, null, false);
1324     }
1325 
1326     /**************************************
1327      * Check function call to see if can be inlined,
1328      * and then inline it if it can.
1329      * Params:
1330      *  e = the function call
1331      *  eret = if !null, then this is the lvalue of the nrvo function result
1332      *  asStatements = if inline as statements rather than as an Expression
1333      * Returns:
1334      *  this.eresult if asStatements == false
1335      *  this.sresult if asStatements == true
1336      */
1337     void visitCallExp(CallExp e, Expression eret, bool asStatements)
1338     {
1339         inlineScan(e.e1);
1340         arrayInlineScan(e.arguments);
1341 
1342         //printf("visitCallExp() %s\n", e.toChars());
1343         FuncDeclaration fd;
1344 
1345         void inlineFd()
1346         {
1347             if (!fd || fd == parent)
1348                 return;
1349 
1350             /* If the arguments generate temporaries that need destruction, the destruction
1351              * must be done after the function body is executed.
1352              * The easiest way to accomplish that is to do the inlining as an Expression.
1353              * https://issues.dlang.org/show_bug.cgi?id=16652
1354              */
1355             bool asStates = asStatements;
1356             if (asStates)
1357             {
1358                 if (fd.inlineStatusExp == ILS.yes)
1359                     asStates = false;           // inline as expressions
1360                                                 // so no need to recompute argumentsNeedDtors()
1361                 else if (argumentsNeedDtors(e.arguments))
1362                     asStates = false;
1363             }
1364 
1365             if (canInline(fd, false, false, asStates))
1366             {
1367                 expandInline(e.loc, fd, parent, eret, null, e.arguments, asStates, e.vthis2, eresult, sresult, again);
1368                 if (asStatements && eresult)
1369                 {
1370                     sresult = new ExpStatement(eresult.loc, eresult);
1371                     eresult = null;
1372                 }
1373             }
1374         }
1375 
1376         /* Pattern match various ASTs looking for indirect function calls, delegate calls,
1377          * function literal calls, delegate literal calls, and dot member calls.
1378          * If so, and that is only assigned its _init.
1379          * If so, do 'copy propagation' of the _init value and try to inline it.
1380          */
1381         if (auto ve = e.e1.isVarExp())
1382         {
1383             fd = ve.var.isFuncDeclaration();
1384             if (fd)
1385                 // delegate call
1386                 inlineFd();
1387             else
1388             {
1389                 // delegate literal call
1390                 auto v = ve.var.isVarDeclaration();
1391                 if (v && v._init && v.type.ty == Tdelegate && onlyOneAssign(v, parent))
1392                 {
1393                     //printf("init: %s\n", v._init.toChars());
1394                     auto ei = v._init.isExpInitializer();
1395                     if (ei && ei.exp.op == EXP.blit)
1396                     {
1397                         Expression e2 = (cast(AssignExp)ei.exp).e2;
1398                         if (auto fe = e2.isFuncExp())
1399                         {
1400                             auto fld = fe.fd;
1401                             assert(fld.tok == TOK.delegate_);
1402                             fd = fld;
1403                             inlineFd();
1404                         }
1405                         else if (auto de = e2.isDelegateExp())
1406                         {
1407                             if (auto ve2 = de.e1.isVarExp())
1408                             {
1409                                 fd = ve2.var.isFuncDeclaration();
1410                                 inlineFd();
1411                             }
1412                         }
1413                     }
1414                 }
1415             }
1416         }
1417         else if (auto dve = e.e1.isDotVarExp())
1418         {
1419             fd = dve.var.isFuncDeclaration();
1420             if (fd && fd != parent && canInline(fd, true, false, asStatements))
1421             {
1422                 if (dve.e1.op == EXP.call && dve.e1.type.toBasetype().ty == Tstruct)
1423                 {
1424                     /* To create ethis, we'll need to take the address
1425                      * of dve.e1, but this won't work if dve.e1 is
1426                      * a function call.
1427                      */
1428                 }
1429                 else
1430                 {
1431                     expandInline(e.loc, fd, parent, eret, dve.e1, e.arguments, asStatements, e.vthis2, eresult, sresult, again);
1432                 }
1433             }
1434         }
1435         else if (e.e1.op == EXP.star &&
1436                  (cast(PtrExp)e.e1).e1.op == EXP.variable)
1437         {
1438             auto ve = e.e1.isPtrExp().e1.isVarExp();
1439             VarDeclaration v = ve.var.isVarDeclaration();
1440             if (v && v._init && onlyOneAssign(v, parent))
1441             {
1442                 //printf("init: %s\n", v._init.toChars());
1443                 auto ei = v._init.isExpInitializer();
1444                 if (ei && ei.exp.op == EXP.blit)
1445                 {
1446                     Expression e2 = (cast(AssignExp)ei.exp).e2;
1447                     // function pointer call
1448                     if (auto se = e2.isSymOffExp())
1449                     {
1450                         fd = se.var.isFuncDeclaration();
1451                         inlineFd();
1452                     }
1453                     // function literal call
1454                     else if (auto fe = e2.isFuncExp())
1455                     {
1456                         auto fld = fe.fd;
1457                         assert(fld.tok == TOK.function_);
1458                         fd = fld;
1459                         inlineFd();
1460                     }
1461                 }
1462             }
1463         }
1464         else if (auto fe = e.e1.isFuncExp())
1465         {
1466             if (fe.fd)
1467             {
1468                 fd = fe.fd;
1469                 inlineFd();
1470             }
1471             else
1472                 return;
1473         }
1474         else
1475         {
1476             return;
1477         }
1478 
1479         if (global.params.verbose && (eresult || sresult))
1480             message("inlined   %s =>\n          %s", fd.toPrettyChars(), parent.toPrettyChars());
1481 
1482         if (eresult && e.type.ty != Tvoid)
1483         {
1484             Expression ex = eresult;
1485             while (ex.op == EXP.comma)
1486             {
1487                 ex.type = e.type;
1488                 ex = ex.isCommaExp().e2;
1489             }
1490             ex.type = e.type;
1491         }
1492     }
1493 
1494     override void visit(SliceExp e)
1495     {
1496         inlineScan(e.e1);
1497         inlineScan(e.lwr);
1498         inlineScan(e.upr);
1499     }
1500 
1501     override void visit(TupleExp e)
1502     {
1503         //printf("TupleExp.inlineScan()\n");
1504         inlineScan(e.e0);
1505         arrayInlineScan(e.exps);
1506     }
1507 
1508     override void visit(ArrayLiteralExp e)
1509     {
1510         //printf("ArrayLiteralExp.inlineScan()\n");
1511         inlineScan(e.basis);
1512         arrayInlineScan(e.elements);
1513     }
1514 
1515     override void visit(AssocArrayLiteralExp e)
1516     {
1517         //printf("AssocArrayLiteralExp.inlineScan()\n");
1518         arrayInlineScan(e.keys);
1519         arrayInlineScan(e.values);
1520     }
1521 
1522     override void visit(StructLiteralExp e)
1523     {
1524         //printf("StructLiteralExp.inlineScan()\n");
1525         if (e.stageflags & stageInlineScan)
1526             return;
1527         const old = e.stageflags;
1528         e.stageflags |= stageInlineScan;
1529         arrayInlineScan(e.elements);
1530         e.stageflags = old;
1531     }
1532 
1533     override void visit(ArrayExp e)
1534     {
1535         //printf("ArrayExp.inlineScan()\n");
1536         inlineScan(e.e1);
1537         arrayInlineScan(e.arguments);
1538     }
1539 
1540     override void visit(CondExp e)
1541     {
1542         inlineScan(e.econd);
1543         inlineScan(e.e1);
1544         inlineScan(e.e2);
1545     }
1546 
1547     /********************************
1548      * Scan Expression e for inlining opportunities,
1549      * and if found replace e with an inlined one.
1550      * Params:
1551      *  e = Expression to be scanned and updated
1552      */
1553     void inlineScan(ref Expression e)
1554     {
1555         if (!e)
1556             return;
1557         assert(eresult is null);
1558         e.accept(this);
1559         if (eresult)
1560         {
1561             e = eresult;
1562             eresult = null;
1563         }
1564     }
1565 }
1566 
1567 /***********************************************************
1568  * Walk the trees, looking for functions to inline.
1569  * Inline any that can be.
1570  */
1571 private extern (C++) final class InlineScanVisitorDsymbol : Visitor
1572 {
1573     alias visit = Visitor.visit;
1574 public:
1575 
1576     extern (D) this() scope
1577     {
1578     }
1579 
1580     /*************************************
1581      * Look for function inlining possibilities.
1582      */
1583     override void visit(Dsymbol d)
1584     {
1585         // Most Dsymbols aren't functions
1586     }
1587 
1588     override void visit(FuncDeclaration fd)
1589     {
1590         static if (LOG)
1591         {
1592             printf("FuncDeclaration.inlineScan('%s')\n", fd.toPrettyChars());
1593         }
1594         if (!(global.params.useInline || fd.hasAlwaysInlines))
1595             return;
1596         if (fd.isUnitTestDeclaration() && !global.params.useUnitTests || fd.inlineScanned)
1597             return;
1598         if (fd.fbody && !fd.isNaked())
1599         {
1600             while (1)
1601             {
1602                 fd.inlineNest++;
1603                 fd.inlineScanned = true;
1604 
1605                 scope InlineScanVisitor v = new InlineScanVisitor();
1606                 v.parent = fd;
1607                 v.inlineScan(fd.fbody);
1608                 bool again = v.again;
1609 
1610                 fd.inlineNest--;
1611                 if (!again)
1612                     break;
1613             }
1614         }
1615     }
1616 
1617     override void visit(AttribDeclaration d)
1618     {
1619         Dsymbols* decls = d.include(null);
1620         if (decls)
1621         {
1622             foreach (i; 0 .. decls.length)
1623             {
1624                 Dsymbol s = (*decls)[i];
1625                 //printf("AttribDeclaration.inlineScan %s\n", s.toChars());
1626                 s.accept(this);
1627             }
1628         }
1629     }
1630 
1631     override void visit(AggregateDeclaration ad)
1632     {
1633         //printf("AggregateDeclaration.inlineScan(%s)\n", toChars());
1634         if (ad.members)
1635         {
1636             foreach (i; 0 .. ad.members.length)
1637             {
1638                 Dsymbol s = (*ad.members)[i];
1639                 //printf("inline scan aggregate symbol '%s'\n", s.toChars());
1640                 s.accept(this);
1641             }
1642         }
1643     }
1644 
1645     override void visit(TemplateInstance ti)
1646     {
1647         static if (LOG)
1648         {
1649             printf("TemplateInstance.inlineScan('%s')\n", ti.toChars());
1650         }
1651         if (!ti.errors && ti.members)
1652         {
1653             foreach (i; 0 .. ti.members.length)
1654             {
1655                 Dsymbol s = (*ti.members)[i];
1656                 s.accept(this);
1657             }
1658         }
1659     }
1660 }
1661 
1662 /***********************************************************
1663  * Test that `fd` can be inlined.
1664  *
1665  * Params:
1666  *  hasthis = `true` if the function call has explicit 'this' expression.
1667  *  hdrscan = `true` if the inline scan is for 'D header' content.
1668  *  statementsToo = `true` if the function call is placed on ExpStatement.
1669  *      It means more code-block dependent statements in fd body - ForStatement,
1670  *      ThrowStatement, etc. can be inlined.
1671  *
1672  * Returns:
1673  *  true if the function body can be expanded.
1674  *
1675  * Todo:
1676  *  - Would be able to eliminate `hasthis` parameter, because semantic analysis
1677  *    no longer accepts calls of contextful function without valid 'this'.
1678  *  - Would be able to eliminate `hdrscan` parameter, because it's always false.
1679  */
1680 private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsToo)
1681 {
1682     int cost;
1683 
1684     static if (CANINLINE_LOG)
1685     {
1686         printf("FuncDeclaration.canInline(hasthis = %d, statementsToo = %d, '%s')\n",
1687             hasthis, statementsToo, fd.toPrettyChars());
1688     }
1689 
1690     if (fd.needThis() && !hasthis)
1691         return false;
1692 
1693     if (fd.inlineNest)
1694     {
1695         static if (CANINLINE_LOG)
1696         {
1697             printf("\t1: no, inlineNest = %d, semanticRun = %d\n", fd.inlineNest, fd.semanticRun);
1698         }
1699         return false;
1700     }
1701 
1702     if (fd.semanticRun < PASS.semantic3 && !hdrscan)
1703     {
1704         if (!fd.fbody)
1705             return false;
1706         if (!fd.functionSemantic3())
1707             return false;
1708         Module.runDeferredSemantic3();
1709         if (global.errors)
1710             return false;
1711         assert(fd.semanticRun >= PASS.semantic3done);
1712     }
1713 
1714     if (fd.skipCodegen)
1715         return false;
1716 
1717     final switch (statementsToo ? fd.inlineStatusStmt : fd.inlineStatusExp)
1718     {
1719     case ILS.yes:
1720         static if (CANINLINE_LOG)
1721         {
1722             printf("\t1: yes %s\n", fd.toChars());
1723         }
1724         return true;
1725     case ILS.no:
1726         static if (CANINLINE_LOG)
1727         {
1728             printf("\t1: no %s\n", fd.toChars());
1729         }
1730         return false;
1731     case ILS.uninitialized:
1732         break;
1733     }
1734 
1735     final switch (fd.inlining)
1736     {
1737     case PINLINE.default_:
1738         if (!global.params.useInline)
1739             return false;
1740         break;
1741     case PINLINE.always:
1742         break;
1743     case PINLINE.never:
1744         return false;
1745     }
1746 
1747     if (fd.type)
1748     {
1749         TypeFunction tf = fd.type.isTypeFunction();
1750 
1751         // no variadic parameter lists
1752         if (tf.parameterList.varargs == VarArg.variadic ||
1753             tf.parameterList.varargs == VarArg.KRvariadic)
1754             goto Lno;
1755 
1756         /* No lazy parameters when inlining by statement, as the inliner tries to
1757          * operate on the created delegate itself rather than the return value.
1758          * Discussion: https://github.com/dlang/dmd/pull/6815
1759          */
1760         if (statementsToo && fd.parameters)
1761         {
1762             foreach (param; *fd.parameters)
1763             {
1764                 if (param.storage_class & STC.lazy_)
1765                     goto Lno;
1766             }
1767         }
1768 
1769         static bool hasDtor(Type t)
1770         {
1771             auto tv = t.baseElemOf();
1772             return tv.ty == Tstruct || tv.ty == Tclass; // for now assume these might have a destructor
1773         }
1774 
1775         /* Don't inline a function that returns non-void, but has
1776          * no or multiple return expression.
1777          * When inlining as a statement:
1778          * 1. don't inline array operations, because the order the arguments
1779          *    get evaluated gets reversed. This is the same issue that e2ir.callfunc()
1780          *    has with them
1781          * 2. don't inline when the return value has a destructor, as it doesn't
1782          *    get handled properly
1783          */
1784         if (tf.next && tf.next.ty != Tvoid &&
1785             (!(fd.hasReturnExp & 1) ||
1786              statementsToo && hasDtor(tf.next)) &&
1787             !hdrscan)
1788         {
1789             static if (CANINLINE_LOG)
1790             {
1791                 printf("\t3: no %s\n", fd.toChars());
1792             }
1793             goto Lno;
1794         }
1795 
1796         /* https://issues.dlang.org/show_bug.cgi?id=14560
1797          * If fd returns void, all explicit `return;`s
1798          * must not appear in the expanded result.
1799          * See also ReturnStatement.doInlineAs!Statement().
1800          */
1801     }
1802 
1803     // cannot inline constructor calls because we need to convert:
1804     //      return;
1805     // to:
1806     //      return this;
1807     // ensure() has magic properties the inliner loses
1808     // require() has magic properties too
1809     // see bug 7699
1810     // no nested references to this frame
1811     if (!fd.fbody ||
1812         fd.ident == Id.ensure ||
1813         (fd.ident == Id.require &&
1814          fd.toParent().isFuncDeclaration() &&
1815          fd.toParent().isFuncDeclaration().needThis()) ||
1816         !hdrscan && (fd.isSynchronized() ||
1817                      fd.isImportedSymbol() ||
1818                      fd.hasNestedFrameRefs() ||
1819                      (fd.isVirtual() && !fd.isFinalFunc())))
1820     {
1821         static if (CANINLINE_LOG)
1822         {
1823             printf("\t4: no %s\n", fd.toChars());
1824         }
1825         goto Lno;
1826     }
1827 
1828     // cannot inline functions as statement if they have multiple
1829     //  return statements
1830     if ((fd.hasReturnExp & 16) && statementsToo)
1831     {
1832         static if (CANINLINE_LOG)
1833         {
1834             printf("\t5: no %s\n", fd.toChars());
1835         }
1836         goto Lno;
1837     }
1838 
1839     {
1840         cost = inlineCostFunction(fd, hasthis, hdrscan);
1841     }
1842     static if (CANINLINE_LOG)
1843     {
1844         printf("\tcost = %d for %s\n", cost, fd.toChars());
1845     }
1846 
1847     if (tooCostly(cost))
1848         goto Lno;
1849     if (!statementsToo && cost > COST_MAX)
1850         goto Lno;
1851 
1852     if (!hdrscan)
1853     {
1854         // Don't modify inlineStatus for header content scan
1855         if (statementsToo)
1856             fd.inlineStatusStmt = ILS.yes;
1857         else
1858             fd.inlineStatusExp = ILS.yes;
1859 
1860         inlineScanDsymbol(fd); // Don't scan recursively for header content scan
1861 
1862         if (fd.inlineStatusExp == ILS.uninitialized)
1863         {
1864             // Need to redo cost computation, as some statements or expressions have been inlined
1865             cost = inlineCostFunction(fd, hasthis, hdrscan);
1866             static if (CANINLINE_LOG)
1867             {
1868                 printf("recomputed cost = %d for %s\n", cost, fd.toChars());
1869             }
1870 
1871             if (tooCostly(cost))
1872                 goto Lno;
1873             if (!statementsToo && cost > COST_MAX)
1874                 goto Lno;
1875 
1876             if (statementsToo)
1877                 fd.inlineStatusStmt = ILS.yes;
1878             else
1879                 fd.inlineStatusExp = ILS.yes;
1880         }
1881     }
1882     static if (CANINLINE_LOG)
1883     {
1884         printf("\t2: yes %s\n", fd.toChars());
1885     }
1886     return true;
1887 
1888 Lno:
1889     if (fd.inlining == PINLINE.always && global.params.warnings == DiagnosticReporting.inform)
1890         warning(fd.loc, "cannot inline function `%s`", fd.toPrettyChars());
1891 
1892     if (!hdrscan) // Don't modify inlineStatus for header content scan
1893     {
1894         if (statementsToo)
1895             fd.inlineStatusStmt = ILS.no;
1896         else
1897             fd.inlineStatusExp = ILS.no;
1898     }
1899     static if (CANINLINE_LOG)
1900     {
1901         printf("\t2: no %s\n", fd.toChars());
1902     }
1903     return false;
1904 }
1905 
1906 /***********************************************************
1907  * Expand a function call inline,
1908  *      ethis.fd(arguments)
1909  *
1910  * Params:
1911  *      callLoc = location of CallExp
1912  *      fd = function to expand
1913  *      parent = function that the call to fd is being expanded into
1914  *      eret = if !null then the lvalue of where the nrvo return value goes
1915  *      ethis = 'this' reference
1916  *      arguments = arguments passed to fd
1917  *      asStatements = expand to Statements rather than Expressions
1918  *      eresult = if expanding to an expression, this is where the expression is written to
1919  *      sresult = if expanding to a statement, this is where the statement is written to
1920  *      again = if true, then fd can be inline scanned again because there may be
1921  *           more opportunities for inlining
1922  */
1923 private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration parent, Expression eret,
1924         Expression ethis, Expressions* arguments, bool asStatements, VarDeclaration vthis2,
1925         out Expression eresult, out Statement sresult, out bool again)
1926 {
1927     auto tf = fd.type.isTypeFunction();
1928     static if (LOG || CANINLINE_LOG || EXPANDINLINE_LOG)
1929         printf("FuncDeclaration.expandInline('%s', %d)\n", fd.toChars(), asStatements);
1930     static if (EXPANDINLINE_LOG)
1931     {
1932         if (eret) printf("\teret = %s\n", eret.toChars());
1933         if (ethis) printf("\tethis = %s\n", ethis.toChars());
1934     }
1935     scope ids = new InlineDoState(parent, fd);
1936 
1937     if (fd.isNested())
1938     {
1939         if (!parent.inlinedNestedCallees)
1940             parent.inlinedNestedCallees = new FuncDeclarations();
1941         parent.inlinedNestedCallees.push(fd);
1942     }
1943 
1944     VarDeclaration vret;    // will be set the function call result
1945     if (eret)
1946     {
1947         if (auto ve = eret.isVarExp())
1948         {
1949             vret = ve.var.isVarDeclaration();
1950             assert(!(vret.storage_class & (STC.out_ | STC.ref_)));
1951             eret = null;
1952         }
1953         else
1954         {
1955             /* Inlining:
1956              *   this.field = foo();   // inside constructor
1957              */
1958             auto ei = new ExpInitializer(callLoc, null);
1959             auto tmp = Identifier.generateId("__retvar");
1960             vret = new VarDeclaration(fd.loc, eret.type, tmp, ei);
1961             vret.storage_class |= STC.temp | STC.ref_;
1962             vret._linkage = LINK.d;
1963             vret.parent = parent;
1964 
1965             ei.exp = new ConstructExp(fd.loc, vret, eret);
1966             ei.exp.type = vret.type;
1967 
1968             auto de = new DeclarationExp(fd.loc, vret);
1969             de.type = Type.tvoid;
1970             eret = de;
1971         }
1972 
1973         if (!asStatements && fd.nrvo_var)
1974         {
1975             ids.from.push(fd.nrvo_var);
1976             ids.to.push(vret);
1977         }
1978     }
1979     else
1980     {
1981         if (!asStatements && fd.nrvo_var)
1982         {
1983             auto tmp = Identifier.generateId("__retvar");
1984             vret = new VarDeclaration(fd.loc, fd.nrvo_var.type, tmp, new VoidInitializer(fd.loc));
1985             assert(!tf.isref);
1986             vret.storage_class = STC.temp | STC.rvalue;
1987             vret._linkage = tf.linkage;
1988             vret.parent = parent;
1989 
1990             auto de = new DeclarationExp(fd.loc, vret);
1991             de.type = Type.tvoid;
1992             eret = de;
1993 
1994             ids.from.push(fd.nrvo_var);
1995             ids.to.push(vret);
1996         }
1997     }
1998 
1999     // Set up vthis
2000     VarDeclaration vthis;
2001     if (ethis)
2002     {
2003         Expression e0;
2004         ethis = Expression.extractLast(ethis, e0);
2005         assert(vthis2 || !fd.hasDualContext());
2006         if (vthis2)
2007         {
2008             // void*[2] __this = [ethis, this]
2009             if (ethis.type.ty == Tstruct)
2010             {
2011                 // &ethis
2012                 Type t = ethis.type.pointerTo();
2013                 ethis = new AddrExp(ethis.loc, ethis);
2014                 ethis.type = t;
2015             }
2016             auto elements = new Expressions(2);
2017             (*elements)[0] = ethis;
2018             (*elements)[1] = new NullExp(Loc.initial, Type.tvoidptr);
2019             Expression ae = new ArrayLiteralExp(vthis2.loc, vthis2.type, elements);
2020             Expression ce = new ConstructExp(vthis2.loc, vthis2, ae);
2021             ce.type = vthis2.type;
2022             vthis2._init = new ExpInitializer(vthis2.loc, ce);
2023             vthis = vthis2;
2024         }
2025         else if (auto ve = ethis.isVarExp())
2026         {
2027             vthis = ve.var.isVarDeclaration();
2028         }
2029         else
2030         {
2031             //assert(ethis.type.ty != Tpointer);
2032             if (ethis.type.ty == Tpointer)
2033             {
2034                 Type t = ethis.type.nextOf();
2035                 ethis = new PtrExp(ethis.loc, ethis);
2036                 ethis.type = t;
2037             }
2038 
2039             auto ei = new ExpInitializer(fd.loc, ethis);
2040             vthis = new VarDeclaration(fd.loc, ethis.type, Id.This, ei);
2041             if (ethis.type.ty != Tclass)
2042                 vthis.storage_class = STC.ref_;
2043             else
2044                 vthis.storage_class = STC.in_;
2045             vthis._linkage = LINK.d;
2046             vthis.parent = parent;
2047 
2048             ei.exp = new ConstructExp(fd.loc, vthis, ethis);
2049             ei.exp.type = vthis.type;
2050 
2051             auto de = new DeclarationExp(fd.loc, vthis);
2052             de.type = Type.tvoid;
2053             e0 = Expression.combine(e0, de);
2054         }
2055         ethis = e0;
2056 
2057         ids.vthis = vthis;
2058     }
2059 
2060     // Set up parameters
2061     Expression eparams;
2062     if (arguments && arguments.length)
2063     {
2064         assert(fd.parameters.length == arguments.length);
2065         foreach (i; 0 .. arguments.length)
2066         {
2067             auto vfrom = (*fd.parameters)[i];
2068             auto arg = (*arguments)[i];
2069 
2070             auto ei = new ExpInitializer(vfrom.loc, arg);
2071             auto vto = new VarDeclaration(vfrom.loc, vfrom.type, vfrom.ident, ei);
2072             vto.storage_class |= vfrom.storage_class & (STC.temp | STC.IOR | STC.lazy_ | STC.nodtor);
2073             vto._linkage = vfrom._linkage;
2074             vto.parent = parent;
2075             //printf("vto = '%s', vto.storage_class = x%x\n", vto.toChars(), vto.storage_class);
2076             //printf("vto.parent = '%s'\n", parent.toChars());
2077 
2078             if (VarExp ve = arg.isVarExp())
2079             {
2080                 VarDeclaration va = ve.var.isVarDeclaration();
2081                 if (va && va.isArgDtorVar)
2082                 {
2083                     assert(vto.storage_class & STC.nodtor);
2084                     // The destructor is called on va so take it by ref
2085                     vto.storage_class |= STC.ref_;
2086                 }
2087             }
2088 
2089             // Even if vto is STC.lazy_, `vto = arg` is handled correctly in glue layer.
2090             ei.exp = new BlitExp(vto.loc, vto, arg);
2091             ei.exp.type = vto.type;
2092 
2093             ids.from.push(vfrom);
2094             ids.to.push(vto);
2095 
2096             auto de = new DeclarationExp(vto.loc, vto);
2097             de.type = Type.tvoid;
2098             eparams = Expression.combine(eparams, de);
2099 
2100             /* If function pointer or delegate parameters are present,
2101              * inline scan again because if they are initialized to a symbol,
2102              * any calls to the fp or dg can be inlined.
2103              */
2104             if (vfrom.type.ty == Tdelegate ||
2105                 vfrom.type.isPtrToFunction())
2106             {
2107                 if (auto ve = arg.isVarExp())
2108                 {
2109                     if (ve.var.isFuncDeclaration())
2110                         again = true;
2111                 }
2112                 else if (auto se = arg.isSymOffExp())
2113                 {
2114                     if (se.var.isFuncDeclaration())
2115                         again = true;
2116                 }
2117                 else if (arg.op == EXP.function_ || arg.op == EXP.delegate_)
2118                     again = true;
2119             }
2120         }
2121     }
2122 
2123     if (asStatements)
2124     {
2125         /* Construct:
2126          *  { eret; ethis; eparams; fd.fbody; }
2127          * or:
2128          *  { eret; ethis; try { eparams; fd.fbody; } finally { vthis.edtor; } }
2129          */
2130 
2131         auto as = new Statements();
2132         if (eret)
2133             as.push(new ExpStatement(callLoc, eret));
2134         if (ethis)
2135             as.push(new ExpStatement(callLoc, ethis));
2136 
2137         auto as2 = as;
2138         if (vthis && !vthis.isDataseg())
2139         {
2140             if (vthis.needsScopeDtor())
2141             {
2142                 // same with ExpStatement.scopeCode()
2143                 as2 = new Statements();
2144                 vthis.storage_class |= STC.nodtor;
2145             }
2146         }
2147 
2148         if (eparams)
2149             as2.push(new ExpStatement(callLoc, eparams));
2150 
2151         fd.inlineNest++;
2152         Statement s = doInlineAs!Statement(fd.fbody, ids);
2153         fd.inlineNest--;
2154         as2.push(s);
2155 
2156         if (as2 != as)
2157         {
2158             as.push(new TryFinallyStatement(callLoc,
2159                         new CompoundStatement(callLoc, as2),
2160                         new DtorExpStatement(callLoc, vthis.edtor, vthis)));
2161         }
2162 
2163         sresult = new ScopeStatement(callLoc, new CompoundStatement(callLoc, as), callLoc);
2164 
2165         static if (EXPANDINLINE_LOG)
2166             printf("\n[%s] %s expandInline sresult =\n%s\n",
2167                 callLoc.toChars(), fd.toPrettyChars(), sresult.toChars());
2168     }
2169     else
2170     {
2171         /* Construct:
2172          *  (eret, ethis, eparams, fd.fbody)
2173          */
2174 
2175         fd.inlineNest++;
2176         auto e = doInlineAs!Expression(fd.fbody, ids);
2177         fd.inlineNest--;
2178 
2179         // https://issues.dlang.org/show_bug.cgi?id=11322
2180         if (tf.isref)
2181             e = e.toLvalue(null, null);
2182 
2183         /* If the inlined function returns a copy of a struct,
2184          * and then the return value is used subsequently as an
2185          * lvalue, as in a struct return that is then used as a 'this'.
2186          * Taking the address of the return value will be taking the address
2187          * of the original, not the copy. Fix this by assigning the return value to
2188          * a temporary, then returning the temporary. If the temporary is used as an
2189          * lvalue, it will work.
2190          * This only happens with struct returns.
2191          * See https://issues.dlang.org/show_bug.cgi?id=2127 for an example.
2192          *
2193          * On constructor call making __inlineretval is merely redundant, because
2194          * the returned reference is exactly same as vthis, and the 'this' variable
2195          * already exists at the caller side.
2196          */
2197         if (tf.next.ty == Tstruct && !fd.nrvo_var && !fd.isCtorDeclaration() &&
2198             !isConstruction(e))
2199         {
2200             /* Generate a new variable to hold the result and initialize it with the
2201              * inlined body of the function:
2202              *   tret __inlineretval = e;
2203              */
2204             auto ei = new ExpInitializer(callLoc, e);
2205             auto tmp = Identifier.generateId("__inlineretval");
2206             auto vd = new VarDeclaration(callLoc, tf.next, tmp, ei);
2207             vd.storage_class = STC.temp | (tf.isref ? STC.ref_ : STC.rvalue);
2208             vd._linkage = tf.linkage;
2209             vd.parent = parent;
2210 
2211             ei.exp = new ConstructExp(callLoc, vd, e);
2212             ei.exp.type = vd.type;
2213 
2214             auto de = new DeclarationExp(callLoc, vd);
2215             de.type = Type.tvoid;
2216 
2217             // Chain the two together:
2218             //   ( typeof(return) __inlineretval = ( inlined body )) , __inlineretval
2219             e = Expression.combine(de, new VarExp(callLoc, vd));
2220         }
2221 
2222         // https://issues.dlang.org/show_bug.cgi?id=15210
2223         if (tf.next.ty == Tvoid && e && e.type.ty != Tvoid)
2224         {
2225             e = new CastExp(callLoc, e, Type.tvoid);
2226             e.type = Type.tvoid;
2227         }
2228 
2229         eresult = Expression.combine(eresult, eret, ethis, eparams);
2230         eresult = Expression.combine(eresult, e);
2231 
2232         static if (EXPANDINLINE_LOG)
2233             printf("\n[%s] %s expandInline eresult = %s\n",
2234                 callLoc.toChars(), fd.toPrettyChars(), eresult.toChars());
2235     }
2236 
2237     // Need to reevaluate whether parent can now be inlined
2238     // in expressions, as we might have inlined statements
2239     parent.inlineStatusExp = ILS.uninitialized;
2240 }
2241 
2242 /****************************************************
2243  * Determine if the value of `e` is the result of construction.
2244  *
2245  * Params:
2246  *      e = expression to check
2247  * Returns:
2248  *      true for value generated by a constructor or struct literal
2249  */
2250 private bool isConstruction(Expression e)
2251 {
2252     e = lastComma(e);
2253 
2254     if (e.op == EXP.structLiteral)
2255     {
2256         return true;
2257     }
2258     /* Detect:
2259      *    structliteral.ctor(args)
2260      */
2261     else if (e.op == EXP.call)
2262     {
2263         auto ce = cast(CallExp)e;
2264         if (ce.e1.op == EXP.dotVariable)
2265         {
2266             auto dve = cast(DotVarExp)ce.e1;
2267             auto fd = dve.var.isFuncDeclaration();
2268             if (fd && fd.isCtorDeclaration())
2269             {
2270                 if (dve.e1.op == EXP.structLiteral)
2271                 {
2272                     return true;
2273                 }
2274             }
2275         }
2276     }
2277     return false;
2278 }
2279 
2280 
2281 /***********************************************************
2282  * Determine if v is 'head const', meaning
2283  * that once it is initialized it is not changed
2284  * again.
2285  *
2286  * This is done using a primitive flow analysis.
2287  *
2288  * v is head const if v is const or immutable.
2289  * Otherwise, v is assumed to be head const unless one of the
2290  * following is true:
2291  *      1. v is a `ref` or `out` variable
2292  *      2. v is a parameter and fd is a variadic function
2293  *      3. v is assigned to again
2294  *      4. the address of v is taken
2295  *      5. v is referred to by a function nested within fd
2296  *      6. v is ever assigned to a `ref` or `out` variable
2297  *      7. v is ever passed to another function as `ref` or `out`
2298  *
2299  * Params:
2300  *      v       variable to check
2301  *      fd      function that v is local to
2302  * Returns:
2303  *      true if v's initializer is the only value assigned to v
2304  */
2305 private bool onlyOneAssign(VarDeclaration v, FuncDeclaration fd)
2306 {
2307     if (!v.type.isMutable())
2308         return true;            // currently the only case handled atm
2309     return false;
2310 }
2311 
2312 /************************************************************
2313  * See if arguments to a function are creating temporaries that
2314  * will need destruction after the function is executed.
2315  * Params:
2316  *      arguments = arguments to function
2317  * Returns:
2318  *      true if temporaries need destruction
2319  */
2320 
2321 private bool argumentsNeedDtors(Expressions* arguments)
2322 {
2323     if (arguments)
2324     {
2325         foreach (arg; *arguments)
2326         {
2327             if (expNeedsDtor(arg))
2328                 return true;
2329         }
2330     }
2331     return false;
2332 }
2333 
2334 /************************************************************
2335  * See if expression is creating temporaries that
2336  * will need destruction at the end of the scope.
2337  * Params:
2338  *      exp = expression
2339  * Returns:
2340  *      true if temporaries need destruction
2341  */
2342 
2343 private bool expNeedsDtor(Expression exp)
2344 {
2345     extern (C++) final class NeedsDtor : StoppableVisitor
2346     {
2347         alias visit = typeof(super).visit;
2348         Expression exp;
2349 
2350     public:
2351         extern (D) this(Expression exp) scope
2352         {
2353             this.exp = exp;
2354         }
2355 
2356         override void visit(Expression)
2357         {
2358         }
2359 
2360         override void visit(DeclarationExp de)
2361         {
2362             Dsymbol_needsDtor(de.declaration);
2363         }
2364 
2365         void Dsymbol_needsDtor(Dsymbol s)
2366         {
2367             /* This mirrors logic of Dsymbol_toElem() in e2ir.d
2368              * perhaps they can be combined.
2369              */
2370 
2371             void symbolDg(Dsymbol s)
2372             {
2373                 Dsymbol_needsDtor(s);
2374             }
2375 
2376             if (auto vd = s.isVarDeclaration())
2377             {
2378                 s = s.toAlias();
2379                 if (s != vd)
2380                     return Dsymbol_needsDtor(s);
2381                 else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.gshared | STC.manifest))
2382                     return;
2383                 if (vd.needsScopeDtor())
2384                 {
2385                     stop = true;
2386                 }
2387             }
2388             else if (auto tm = s.isTemplateMixin())
2389             {
2390                 tm.members.foreachDsymbol(&symbolDg);
2391             }
2392             else if (auto ad = s.isAttribDeclaration())
2393             {
2394                 ad.include(null).foreachDsymbol(&symbolDg);
2395             }
2396             else if (auto td = s.isTupleDeclaration())
2397             {
2398                 td.foreachVar(&symbolDg);
2399             }
2400 
2401 
2402         }
2403     }
2404 
2405     scope NeedsDtor ct = new NeedsDtor(exp);
2406     return walkPostorder(exp, ct);
2407 }