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.arraytypes;
22 import dmd.astenums;
23 import dmd.attrib;
24 import dmd.declaration;
25 import dmd.dmodule;
26 import dmd.dscope;
27 import dmd.dstruct;
28 import dmd.dsymbol;
29 import dmd.dtemplate;
30 import dmd.expression;
31 import dmd.errors;
32 import dmd.func;
33 import dmd.globals;
34 import dmd.id;
35 import dmd.identifier;
36 import dmd.init;
37 import dmd.initsem;
38 import dmd.location;
39 import dmd.mtype;
40 import dmd.opover;
41 import dmd.printast;
42 import dmd.postordervisitor;
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.v.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 https://issues.dlang.org/show_bug.cgi?id=4820 case is still questionable.
88  *  Perhaps would have to 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         error(e.loc, "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             auto lowering = ne.lowering;
728             if (lowering)
729                 if (auto ce = lowering.isCallExp())
730                     if (ce.f.ident == Id._d_newarrayT)
731                     {
732                         ne.lowering = doInlineAs!Expression(lowering, ids);
733                         goto LhasLowering;
734                     }
735 
736             ne.thisexp = doInlineAs!Expression(e.thisexp, ids);
737             ne.argprefix = doInlineAs!Expression(e.argprefix, ids);
738             ne.arguments = arrayExpressionDoInline(e.arguments);
739 
740         LhasLowering:
741             result = ne;
742 
743             semanticTypeInfo(null, e.type);
744         }
745 
746         override void visit(UnaExp e)
747         {
748             auto ue = cast(UnaExp)e.copy();
749             ue.e1 = doInlineAs!Expression(e.e1, ids);
750             result = ue;
751         }
752 
753         override void visit(AssertExp e)
754         {
755             auto ae = e.copy().isAssertExp();
756             ae.e1 = doInlineAs!Expression(e.e1, ids);
757             ae.msg = doInlineAs!Expression(e.msg, ids);
758             result = ae;
759         }
760 
761         override void visit(CatExp e)
762         {
763             auto ce = e.copy().isCatExp();
764 
765             if (auto lowering = ce.lowering)
766                 ce.lowering = doInlineAs!Expression(lowering, ids);
767             else
768             {
769                 ce.e1 = doInlineAs!Expression(e.e1, ids);
770                 ce.e2 = doInlineAs!Expression(e.e2, ids);
771             }
772 
773             result = ce;
774         }
775 
776         override void visit(BinExp e)
777         {
778             auto be = cast(BinExp)e.copy();
779             be.e1 = doInlineAs!Expression(e.e1, ids);
780             be.e2 = doInlineAs!Expression(e.e2, ids);
781             result = be;
782         }
783 
784         override void visit(CallExp e)
785         {
786             auto ce = e.copy().isCallExp();
787             ce.e1 = doInlineAs!Expression(e.e1, ids);
788             ce.arguments = arrayExpressionDoInline(e.arguments);
789             result = ce;
790         }
791 
792         override void visit(AssignExp e)
793         {
794             visit(cast(BinExp)e);
795         }
796 
797         override void visit(LoweredAssignExp e)
798         {
799             result = doInlineAs!Expression(e.lowering, ids);
800         }
801 
802         override void visit(EqualExp e)
803         {
804             visit(cast(BinExp)e);
805 
806             Type t1 = e.e1.type.toBasetype();
807             if (t1.ty == Tarray || t1.ty == Tsarray)
808             {
809                 Type t = t1.nextOf().toBasetype();
810                 while (t.toBasetype().nextOf())
811                     t = t.nextOf().toBasetype();
812                 if (t.ty == Tstruct)
813                     semanticTypeInfo(null, t);
814             }
815             else if (t1.ty == Taarray)
816             {
817                 semanticTypeInfo(null, t1);
818             }
819         }
820 
821         override void visit(IndexExp e)
822         {
823             auto are = e.copy().isIndexExp();
824             are.e1 = doInlineAs!Expression(e.e1, ids);
825             if (e.lengthVar)
826             {
827                 //printf("lengthVar\n");
828                 auto vd = e.lengthVar;
829                 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init);
830                 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration));
831                 vto.parent = ids.parent;
832                 vto.csym = null;
833                 vto.isym = null;
834 
835                 ids.from.push(vd);
836                 ids.to.push(vto);
837 
838                 if (vd._init && !vd._init.isVoidInitializer())
839                 {
840                     auto ie = vd._init.isExpInitializer();
841                     assert(ie);
842                     vto._init = new ExpInitializer(ie.loc, doInlineAs!Expression(ie.exp, ids));
843                 }
844                 are.lengthVar = vto;
845             }
846             are.e2 = doInlineAs!Expression(e.e2, ids);
847             result = are;
848         }
849 
850         override void visit(SliceExp e)
851         {
852             auto are = e.copy().isSliceExp();
853             are.e1 = doInlineAs!Expression(e.e1, ids);
854             if (e.lengthVar)
855             {
856                 //printf("lengthVar\n");
857                 auto vd = e.lengthVar;
858                 auto vto = new VarDeclaration(vd.loc, vd.type, vd.ident, vd._init);
859                 memcpy(cast(void*)vto, cast(void*)vd, __traits(classInstanceSize, VarDeclaration));
860                 vto.parent = ids.parent;
861                 vto.csym = null;
862                 vto.isym = null;
863 
864                 ids.from.push(vd);
865                 ids.to.push(vto);
866 
867                 if (vd._init && !vd._init.isVoidInitializer())
868                 {
869                     auto ie = vd._init.isExpInitializer();
870                     assert(ie);
871                     vto._init = new ExpInitializer(ie.loc, doInlineAs!Expression(ie.exp, ids));
872                 }
873 
874                 are.lengthVar = vto;
875             }
876             are.lwr = doInlineAs!Expression(e.lwr, ids);
877             are.upr = doInlineAs!Expression(e.upr, ids);
878             result = are;
879         }
880 
881         override void visit(TupleExp e)
882         {
883             auto ce = e.copy().isTupleExp();
884             ce.e0 = doInlineAs!Expression(e.e0, ids);
885             ce.exps = arrayExpressionDoInline(e.exps);
886             result = ce;
887         }
888 
889         override void visit(ArrayLiteralExp e)
890         {
891             auto ce = e.copy().isArrayLiteralExp();
892             ce.basis = doInlineAs!Expression(e.basis, ids);
893             ce.elements = arrayExpressionDoInline(e.elements);
894             result = ce;
895 
896             semanticTypeInfo(null, e.type);
897         }
898 
899         override void visit(AssocArrayLiteralExp e)
900         {
901             auto ce = e.copy().isAssocArrayLiteralExp();
902             ce.keys = arrayExpressionDoInline(e.keys);
903             ce.values = arrayExpressionDoInline(e.values);
904             result = ce;
905 
906             semanticTypeInfo(null, e.type);
907         }
908 
909         override void visit(StructLiteralExp e)
910         {
911             if (e.inlinecopy)
912             {
913                 result = e.inlinecopy;
914                 return;
915             }
916             auto ce = e.copy().isStructLiteralExp();
917             e.inlinecopy = ce;
918             ce.elements = arrayExpressionDoInline(e.elements);
919             e.inlinecopy = null;
920             result = ce;
921         }
922 
923         override void visit(ArrayExp e)
924         {
925             assert(0); // this should have been lowered to something else
926         }
927 
928         override void visit(CondExp e)
929         {
930             auto ce = e.copy().isCondExp();
931             ce.econd = doInlineAs!Expression(e.econd, ids);
932             ce.e1 = doInlineAs!Expression(e.e1, ids);
933             ce.e2 = doInlineAs!Expression(e.e2, ids);
934             result = ce;
935         }
936     }
937 }
938 
939 /// ditto
940 private Result doInlineAs(Result)(Statement s, InlineDoState ids)
941 {
942     if (!s)
943         return null;
944 
945     scope DoInlineAs!Result v = new DoInlineAs!Result(ids);
946     s.accept(v);
947     return v.result;
948 }
949 
950 /// ditto
951 private Result doInlineAs(Result)(Expression e, InlineDoState ids)
952 {
953     if (!e)
954         return null;
955 
956     scope DoInlineAs!Result v = new DoInlineAs!Result(ids);
957     e.accept(v);
958     return v.result;
959 }
960 
961 /***********************************************************
962  * Walk the trees, looking for functions to inline.
963  * Inline any that can be.
964  */
965 private extern (C++) final class InlineScanVisitor : Visitor
966 {
967     alias visit = Visitor.visit;
968 public:
969     FuncDeclaration parent;     // function being scanned
970     // As the visit method cannot return a value, these variables
971     // are used to pass the result from 'visit' back to 'inlineScan'
972     Statement sresult;
973     Expression eresult;
974     bool again;
975 
976     extern (D) this() scope @safe
977     {
978     }
979 
980     override void visit(Statement s)
981     {
982     }
983 
984     override void visit(ExpStatement s)
985     {
986         static if (LOG)
987         {
988             printf("ExpStatement.inlineScan(%s)\n", s.toChars());
989         }
990         if (!s.exp)
991             return;
992 
993         Statement inlineScanExpAsStatement(ref Expression exp)
994         {
995             /* If there's a EXP.call at the top, then it may fail to inline
996              * as an Expression. Try to inline as a Statement instead.
997              */
998             if (auto ce = exp.isCallExp())
999             {
1000                 visitCallExp(ce, null, true);
1001                 if (eresult)
1002                     exp = eresult;
1003                 auto s = sresult;
1004                 sresult = null;
1005                 eresult = null;
1006                 return s;
1007             }
1008 
1009             /* If there's a CondExp or CommaExp at the top, then its
1010              * sub-expressions may be inlined as statements.
1011              */
1012             if (auto e = exp.isCondExp())
1013             {
1014                 inlineScan(e.econd);
1015                 auto s1 = inlineScanExpAsStatement(e.e1);
1016                 auto s2 = inlineScanExpAsStatement(e.e2);
1017                 if (!s1 && !s2)
1018                     return null;
1019                 auto ifbody   = !s1 ? new ExpStatement(e.e1.loc, e.e1) : s1;
1020                 auto elsebody = !s2 ? new ExpStatement(e.e2.loc, e.e2) : s2;
1021                 return new IfStatement(exp.loc, null, e.econd, ifbody, elsebody, exp.loc);
1022             }
1023             if (auto e = exp.isCommaExp())
1024             {
1025                 /* If expression declares temporaries which have to be destructed
1026                  * at the end of the scope then it is better handled as an expression.
1027                  */
1028                 if (expNeedsDtor(e.e1))
1029                 {
1030                     inlineScan(exp);
1031                     return null;
1032                 }
1033 
1034                 auto s1 = inlineScanExpAsStatement(e.e1);
1035                 auto s2 = inlineScanExpAsStatement(e.e2);
1036                 if (!s1 && !s2)
1037                     return null;
1038                 auto a = new Statements();
1039                 a.push(!s1 ? new ExpStatement(e.e1.loc, e.e1) : s1);
1040                 a.push(!s2 ? new ExpStatement(e.e2.loc, e.e2) : s2);
1041                 return new CompoundStatement(exp.loc, a);
1042             }
1043 
1044             // inline as an expression
1045             inlineScan(exp);
1046             return null;
1047         }
1048 
1049         sresult = inlineScanExpAsStatement(s.exp);
1050     }
1051 
1052     override void visit(CompoundStatement s)
1053     {
1054         foreach (i; 0 .. s.statements.length)
1055         {
1056             inlineScan((*s.statements)[i]);
1057         }
1058     }
1059 
1060     override void visit(UnrolledLoopStatement s)
1061     {
1062         foreach (i; 0 .. s.statements.length)
1063         {
1064             inlineScan((*s.statements)[i]);
1065         }
1066     }
1067 
1068     override void visit(ScopeStatement s)
1069     {
1070         inlineScan(s.statement);
1071     }
1072 
1073     override void visit(WhileStatement s)
1074     {
1075         inlineScan(s.condition);
1076         inlineScan(s._body);
1077     }
1078 
1079     override void visit(DoStatement s)
1080     {
1081         inlineScan(s._body);
1082         inlineScan(s.condition);
1083     }
1084 
1085     override void visit(ForStatement s)
1086     {
1087         inlineScan(s._init);
1088         inlineScan(s.condition);
1089         inlineScan(s.increment);
1090         inlineScan(s._body);
1091     }
1092 
1093     override void visit(ForeachStatement s)
1094     {
1095         inlineScan(s.aggr);
1096         inlineScan(s._body);
1097     }
1098 
1099     override void visit(ForeachRangeStatement s)
1100     {
1101         inlineScan(s.lwr);
1102         inlineScan(s.upr);
1103         inlineScan(s._body);
1104     }
1105 
1106     override void visit(IfStatement s)
1107     {
1108         inlineScan(s.condition);
1109         inlineScan(s.ifbody);
1110         inlineScan(s.elsebody);
1111     }
1112 
1113     override void visit(SwitchStatement s)
1114     {
1115         //printf("SwitchStatement.inlineScan()\n");
1116         inlineScan(s.condition);
1117         inlineScan(s._body);
1118         Statement sdefault = s.sdefault;
1119         inlineScan(sdefault);
1120         s.sdefault = cast(DefaultStatement)sdefault;
1121         if (s.cases)
1122         {
1123             foreach (i; 0 .. s.cases.length)
1124             {
1125                 Statement scase = (*s.cases)[i];
1126                 inlineScan(scase);
1127                 (*s.cases)[i] = cast(CaseStatement)scase;
1128             }
1129         }
1130     }
1131 
1132     override void visit(CaseStatement s)
1133     {
1134         //printf("CaseStatement.inlineScan()\n");
1135         inlineScan(s.exp);
1136         inlineScan(s.statement);
1137     }
1138 
1139     override void visit(DefaultStatement s)
1140     {
1141         inlineScan(s.statement);
1142     }
1143 
1144     override void visit(ReturnStatement s)
1145     {
1146         //printf("ReturnStatement.inlineScan()\n");
1147         inlineScan(s.exp);
1148     }
1149 
1150     override void visit(SynchronizedStatement s)
1151     {
1152         inlineScan(s.exp);
1153         inlineScan(s._body);
1154     }
1155 
1156     override void visit(WithStatement s)
1157     {
1158         inlineScan(s.exp);
1159         inlineScan(s._body);
1160     }
1161 
1162     override void visit(TryCatchStatement s)
1163     {
1164         inlineScan(s._body);
1165         if (s.catches)
1166         {
1167             foreach (c; *s.catches)
1168             {
1169                 inlineScan(c.handler);
1170             }
1171         }
1172     }
1173 
1174     override void visit(TryFinallyStatement s)
1175     {
1176         inlineScan(s._body);
1177         inlineScan(s.finalbody);
1178     }
1179 
1180     override void visit(ThrowStatement s)
1181     {
1182         inlineScan(s.exp);
1183     }
1184 
1185     override void visit(LabelStatement s)
1186     {
1187         inlineScan(s.statement);
1188     }
1189 
1190     /********************************
1191      * Scan Statement s for inlining opportunities,
1192      * and if found replace s with an inlined one.
1193      * Params:
1194      *  s = Statement to be scanned and updated
1195      */
1196     void inlineScan(ref Statement s)
1197     {
1198         if (!s)
1199             return;
1200         assert(sresult is null);
1201         s.accept(this);
1202         if (sresult)
1203         {
1204             s = sresult;
1205             sresult = null;
1206         }
1207     }
1208 
1209     /* -------------------------- */
1210     void arrayInlineScan(Expressions* arguments)
1211     {
1212         if (arguments)
1213         {
1214             foreach (i; 0 .. arguments.length)
1215             {
1216                 inlineScan((*arguments)[i]);
1217             }
1218         }
1219     }
1220 
1221     override void visit(Expression e)
1222     {
1223     }
1224 
1225     void scanVar(Dsymbol s)
1226     {
1227         //printf("scanVar(%s %s)\n", s.kind(), s.toPrettyChars());
1228         VarDeclaration vd = s.isVarDeclaration();
1229         if (vd)
1230         {
1231             TupleDeclaration td = vd.toAlias().isTupleDeclaration();
1232             if (td)
1233             {
1234                 td.foreachVar((s)
1235                 {
1236                     scanVar(s); // TODO
1237                 });
1238             }
1239             else if (vd._init)
1240             {
1241                 if (ExpInitializer ie = vd._init.isExpInitializer())
1242                 {
1243                     inlineScan(ie.exp);
1244                 }
1245             }
1246         }
1247         else
1248         {
1249             inlineScanDsymbol(s);
1250         }
1251     }
1252 
1253     override void visit(DeclarationExp e)
1254     {
1255         //printf("DeclarationExp.inlineScan() %s\n", e.toChars());
1256         scanVar(e.declaration);
1257     }
1258 
1259     override void visit(UnaExp e)
1260     {
1261         inlineScan(e.e1);
1262     }
1263 
1264     override void visit(AssertExp e)
1265     {
1266         inlineScan(e.e1);
1267         inlineScan(e.msg);
1268     }
1269 
1270     override void visit(CatExp e)
1271     {
1272         if (auto lowering = e.lowering)
1273         {
1274             inlineScan(lowering);
1275             return;
1276         }
1277 
1278         inlineScan(e.e1);
1279         inlineScan(e.e2);
1280     }
1281 
1282     override void visit(BinExp e)
1283     {
1284         inlineScan(e.e1);
1285         inlineScan(e.e2);
1286     }
1287 
1288     override void visit(AssignExp e)
1289     {
1290         // Look for NRVO, as inlining NRVO function returns require special handling
1291         if (e.op == EXP.construct && e.e2.op == EXP.call)
1292         {
1293             auto ce = e.e2.isCallExp();
1294             if (ce.f && ce.f.isNRVO() && ce.f.nrvo_var) // NRVO
1295             {
1296                 if (auto ve = e.e1.isVarExp())
1297                 {
1298                     /* Inlining:
1299                      *   S s = foo();   // initializing by rvalue
1300                      *   S s = S(1);    // constructor call
1301                      */
1302                     Declaration d = ve.var;
1303                     if (d.storage_class & (STC.out_ | STC.ref_)) // refinit
1304                         goto L1;
1305                 }
1306                 else
1307                 {
1308                     /* Inlining:
1309                      *   this.field = foo();   // inside constructor
1310                      */
1311                     inlineScan(e.e1);
1312                 }
1313 
1314                 visitCallExp(ce, e.e1, false);
1315                 if (eresult)
1316                 {
1317                     //printf("call with nrvo: %s ==> %s\n", e.toChars(), eresult.toChars());
1318                     return;
1319                 }
1320             }
1321         }
1322     L1:
1323         visit(cast(BinExp)e);
1324     }
1325 
1326     override void visit(LoweredAssignExp e)
1327     {
1328         inlineScan(e.lowering);
1329     }
1330 
1331     override void visit(CallExp e)
1332     {
1333         //printf("CallExp.inlineScan() %s\n", e.toChars());
1334         visitCallExp(e, null, false);
1335     }
1336 
1337     /**************************************
1338      * Check function call to see if can be inlined,
1339      * and then inline it if it can.
1340      * Params:
1341      *  e = the function call
1342      *  eret = if !null, then this is the lvalue of the nrvo function result
1343      *  asStatements = if inline as statements rather than as an Expression
1344      * Returns:
1345      *  this.eresult if asStatements == false
1346      *  this.sresult if asStatements == true
1347      */
1348     void visitCallExp(CallExp e, Expression eret, bool asStatements)
1349     {
1350         inlineScan(e.e1);
1351         arrayInlineScan(e.arguments);
1352 
1353         //printf("visitCallExp() %s\n", e.toChars());
1354         FuncDeclaration fd;
1355 
1356         void inlineFd()
1357         {
1358             if (!fd || fd == parent)
1359                 return;
1360 
1361             /* If the arguments generate temporaries that need destruction, the destruction
1362              * must be done after the function body is executed.
1363              * The easiest way to accomplish that is to do the inlining as an Expression.
1364              * https://issues.dlang.org/show_bug.cgi?id=16652
1365              */
1366             bool asStates = asStatements;
1367             if (asStates)
1368             {
1369                 if (fd.inlineStatusExp == ILS.yes)
1370                     asStates = false;           // inline as expressions
1371                                                 // so no need to recompute argumentsNeedDtors()
1372                 else if (argumentsNeedDtors(e.arguments))
1373                     asStates = false;
1374             }
1375 
1376             if (canInline(fd, false, false, asStates))
1377             {
1378                 expandInline(e.loc, fd, parent, eret, null, e.arguments, asStates, e.vthis2, eresult, sresult, again);
1379                 if (asStatements && eresult)
1380                 {
1381                     sresult = new ExpStatement(eresult.loc, eresult);
1382                     eresult = null;
1383                 }
1384             }
1385         }
1386 
1387         /* Pattern match various ASTs looking for indirect function calls, delegate calls,
1388          * function literal calls, delegate literal calls, and dot member calls.
1389          * If so, and that is only assigned its _init.
1390          * If so, do 'copy propagation' of the _init value and try to inline it.
1391          */
1392         if (auto ve = e.e1.isVarExp())
1393         {
1394             fd = ve.var.isFuncDeclaration();
1395             if (fd)
1396                 // delegate call
1397                 inlineFd();
1398             else
1399             {
1400                 // delegate literal call
1401                 auto v = ve.var.isVarDeclaration();
1402                 if (v && v._init && v.type.ty == Tdelegate && onlyOneAssign(v, parent))
1403                 {
1404                     //printf("init: %s\n", v._init.toChars());
1405                     auto ei = v._init.isExpInitializer();
1406                     if (ei && ei.exp.op == EXP.blit)
1407                     {
1408                         Expression e2 = (cast(AssignExp)ei.exp).e2;
1409                         if (auto fe = e2.isFuncExp())
1410                         {
1411                             auto fld = fe.fd;
1412                             assert(fld.tok == TOK.delegate_);
1413                             fd = fld;
1414                             inlineFd();
1415                         }
1416                         else if (auto de = e2.isDelegateExp())
1417                         {
1418                             if (auto ve2 = de.e1.isVarExp())
1419                             {
1420                                 fd = ve2.var.isFuncDeclaration();
1421                                 inlineFd();
1422                             }
1423                         }
1424                     }
1425                 }
1426             }
1427         }
1428         else if (auto dve = e.e1.isDotVarExp())
1429         {
1430             fd = dve.var.isFuncDeclaration();
1431             if (fd && fd != parent && canInline(fd, true, false, asStatements))
1432             {
1433                 if (dve.e1.op == EXP.call && dve.e1.type.toBasetype().ty == Tstruct)
1434                 {
1435                     /* To create ethis, we'll need to take the address
1436                      * of dve.e1, but this won't work if dve.e1 is
1437                      * a function call.
1438                      */
1439                 }
1440                 else
1441                 {
1442                     expandInline(e.loc, fd, parent, eret, dve.e1, e.arguments, asStatements, e.vthis2, eresult, sresult, again);
1443                 }
1444             }
1445         }
1446         else if (e.e1.op == EXP.star &&
1447                  (cast(PtrExp)e.e1).e1.op == EXP.variable)
1448         {
1449             auto ve = e.e1.isPtrExp().e1.isVarExp();
1450             VarDeclaration v = ve.var.isVarDeclaration();
1451             if (v && v._init && onlyOneAssign(v, parent))
1452             {
1453                 //printf("init: %s\n", v._init.toChars());
1454                 auto ei = v._init.isExpInitializer();
1455                 if (ei && ei.exp.op == EXP.blit)
1456                 {
1457                     Expression e2 = (cast(AssignExp)ei.exp).e2;
1458                     // function pointer call
1459                     if (auto se = e2.isSymOffExp())
1460                     {
1461                         fd = se.var.isFuncDeclaration();
1462                         inlineFd();
1463                     }
1464                     // function literal call
1465                     else if (auto fe = e2.isFuncExp())
1466                     {
1467                         auto fld = fe.fd;
1468                         assert(fld.tok == TOK.function_);
1469                         fd = fld;
1470                         inlineFd();
1471                     }
1472                 }
1473             }
1474         }
1475         else if (auto fe = e.e1.isFuncExp())
1476         {
1477             if (fe.fd)
1478             {
1479                 fd = fe.fd;
1480                 inlineFd();
1481             }
1482             else
1483                 return;
1484         }
1485         else
1486         {
1487             return;
1488         }
1489 
1490         if (global.params.v.verbose && (eresult || sresult))
1491             message("inlined   %s =>\n          %s", fd.toPrettyChars(), parent.toPrettyChars());
1492 
1493         if (eresult && e.type.ty != Tvoid)
1494         {
1495             Expression ex = eresult;
1496             while (ex.op == EXP.comma)
1497             {
1498                 ex.type = e.type;
1499                 ex = ex.isCommaExp().e2;
1500             }
1501             ex.type = e.type;
1502         }
1503     }
1504 
1505     override void visit(SliceExp e)
1506     {
1507         inlineScan(e.e1);
1508         inlineScan(e.lwr);
1509         inlineScan(e.upr);
1510     }
1511 
1512     override void visit(TupleExp e)
1513     {
1514         //printf("TupleExp.inlineScan()\n");
1515         inlineScan(e.e0);
1516         arrayInlineScan(e.exps);
1517     }
1518 
1519     override void visit(ArrayLiteralExp e)
1520     {
1521         //printf("ArrayLiteralExp.inlineScan()\n");
1522         inlineScan(e.basis);
1523         arrayInlineScan(e.elements);
1524     }
1525 
1526     override void visit(AssocArrayLiteralExp e)
1527     {
1528         //printf("AssocArrayLiteralExp.inlineScan()\n");
1529         arrayInlineScan(e.keys);
1530         arrayInlineScan(e.values);
1531     }
1532 
1533     override void visit(StructLiteralExp e)
1534     {
1535         //printf("StructLiteralExp.inlineScan()\n");
1536         if (e.stageflags & stageInlineScan)
1537             return;
1538         const old = e.stageflags;
1539         e.stageflags |= stageInlineScan;
1540         arrayInlineScan(e.elements);
1541         e.stageflags = old;
1542     }
1543 
1544     override void visit(ArrayExp e)
1545     {
1546         //printf("ArrayExp.inlineScan()\n");
1547         inlineScan(e.e1);
1548         arrayInlineScan(e.arguments);
1549     }
1550 
1551     override void visit(CondExp e)
1552     {
1553         inlineScan(e.econd);
1554         inlineScan(e.e1);
1555         inlineScan(e.e2);
1556     }
1557 
1558     /********************************
1559      * Scan Expression e for inlining opportunities,
1560      * and if found replace e with an inlined one.
1561      * Params:
1562      *  e = Expression to be scanned and updated
1563      */
1564     void inlineScan(ref Expression e)
1565     {
1566         if (!e)
1567             return;
1568         assert(eresult is null);
1569         e.accept(this);
1570         if (eresult)
1571         {
1572             e = eresult;
1573             eresult = null;
1574         }
1575     }
1576 }
1577 
1578 /***********************************************************
1579  * Walk the trees, looking for functions to inline.
1580  * Inline any that can be.
1581  */
1582 private extern (C++) final class InlineScanVisitorDsymbol : Visitor
1583 {
1584     alias visit = Visitor.visit;
1585 public:
1586 
1587     extern (D) this() scope @safe
1588     {
1589     }
1590 
1591     /*************************************
1592      * Look for function inlining possibilities.
1593      */
1594     override void visit(Dsymbol d)
1595     {
1596         // Most Dsymbols aren't functions
1597     }
1598 
1599     override void visit(FuncDeclaration fd)
1600     {
1601         static if (LOG)
1602         {
1603             printf("FuncDeclaration.inlineScan('%s')\n", fd.toPrettyChars());
1604         }
1605         if (!(global.params.useInline || fd.hasAlwaysInlines))
1606             return;
1607         if (fd.isUnitTestDeclaration() && !global.params.useUnitTests || fd.inlineScanned)
1608             return;
1609         if (fd.fbody && !fd.isNaked())
1610         {
1611             while (1)
1612             {
1613                 fd.inlineNest++;
1614                 fd.inlineScanned = true;
1615 
1616                 scope InlineScanVisitor v = new InlineScanVisitor();
1617                 v.parent = fd;
1618                 v.inlineScan(fd.fbody);
1619                 bool again = v.again;
1620 
1621                 fd.inlineNest--;
1622                 if (!again)
1623                     break;
1624             }
1625         }
1626     }
1627 
1628     override void visit(AttribDeclaration d)
1629     {
1630         Dsymbols* decls = d.include(null);
1631         if (decls)
1632         {
1633             foreach (i; 0 .. decls.length)
1634             {
1635                 Dsymbol s = (*decls)[i];
1636                 //printf("AttribDeclaration.inlineScan %s\n", s.toChars());
1637                 s.accept(this);
1638             }
1639         }
1640     }
1641 
1642     override void visit(AggregateDeclaration ad)
1643     {
1644         //printf("AggregateDeclaration.inlineScan(%s)\n", toChars());
1645         if (ad.members)
1646         {
1647             foreach (i; 0 .. ad.members.length)
1648             {
1649                 Dsymbol s = (*ad.members)[i];
1650                 //printf("inline scan aggregate symbol '%s'\n", s.toChars());
1651                 s.accept(this);
1652             }
1653         }
1654     }
1655 
1656     override void visit(TemplateInstance ti)
1657     {
1658         static if (LOG)
1659         {
1660             printf("TemplateInstance.inlineScan('%s')\n", ti.toChars());
1661         }
1662         if (!ti.errors && ti.members)
1663         {
1664             foreach (i; 0 .. ti.members.length)
1665             {
1666                 Dsymbol s = (*ti.members)[i];
1667                 s.accept(this);
1668             }
1669         }
1670     }
1671 }
1672 
1673 /***********************************************************
1674  * Test that `fd` can be inlined.
1675  *
1676  * Params:
1677  *  hasthis = `true` if the function call has explicit 'this' expression.
1678  *  hdrscan = `true` if the inline scan is for 'D header' content.
1679  *  statementsToo = `true` if the function call is placed on ExpStatement.
1680  *      It means more code-block dependent statements in fd body - ForStatement,
1681  *      ThrowStatement, etc. can be inlined.
1682  *
1683  * Returns:
1684  *  true if the function body can be expanded.
1685  *
1686  * Todo:
1687  *  - Would be able to eliminate `hasthis` parameter, because semantic analysis
1688  *    no longer accepts calls of contextful function without valid 'this'.
1689  *  - Would be able to eliminate `hdrscan` parameter, because it's always false.
1690  */
1691 private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsToo)
1692 {
1693     int cost;
1694 
1695     static if (CANINLINE_LOG)
1696     {
1697         printf("FuncDeclaration.canInline(hasthis = %d, statementsToo = %d, '%s')\n",
1698             hasthis, statementsToo, fd.toPrettyChars());
1699     }
1700 
1701     if (fd.needThis() && !hasthis)
1702         return false;
1703 
1704     if (fd.inlineNest)
1705     {
1706         static if (CANINLINE_LOG)
1707         {
1708             printf("\t1: no, inlineNest = %d, semanticRun = %d\n", fd.inlineNest, fd.semanticRun);
1709         }
1710         return false;
1711     }
1712 
1713     if (fd.semanticRun < PASS.semantic3 && !hdrscan)
1714     {
1715         if (!fd.fbody)
1716             return false;
1717         if (!fd.functionSemantic3())
1718             return false;
1719         Module.runDeferredSemantic3();
1720         if (global.errors)
1721             return false;
1722         assert(fd.semanticRun >= PASS.semantic3done);
1723     }
1724 
1725     if (fd.skipCodegen)
1726         return false;
1727 
1728     final switch (statementsToo ? fd.inlineStatusStmt : fd.inlineStatusExp)
1729     {
1730     case ILS.yes:
1731         static if (CANINLINE_LOG)
1732         {
1733             printf("\t1: yes %s\n", fd.toChars());
1734         }
1735         return true;
1736     case ILS.no:
1737         static if (CANINLINE_LOG)
1738         {
1739             printf("\t1: no %s\n", fd.toChars());
1740         }
1741         return false;
1742     case ILS.uninitialized:
1743         break;
1744     }
1745 
1746     final switch (fd.inlining)
1747     {
1748     case PINLINE.default_:
1749         if (!global.params.useInline)
1750             return false;
1751         break;
1752     case PINLINE.always:
1753         break;
1754     case PINLINE.never:
1755         return false;
1756     }
1757 
1758     if (fd.type)
1759     {
1760         TypeFunction tf = fd.type.isTypeFunction();
1761 
1762         // no variadic parameter lists
1763         if (tf.parameterList.varargs == VarArg.variadic ||
1764             tf.parameterList.varargs == VarArg.KRvariadic)
1765             goto Lno;
1766 
1767         /* No lazy parameters when inlining by statement, as the inliner tries to
1768          * operate on the created delegate itself rather than the return value.
1769          * Discussion: https://github.com/dlang/dmd/pull/6815
1770          */
1771         if (statementsToo && fd.parameters)
1772         {
1773             foreach (param; *fd.parameters)
1774             {
1775                 if (param.storage_class & STC.lazy_)
1776                     goto Lno;
1777             }
1778         }
1779 
1780         static bool hasDtor(Type t)
1781         {
1782             auto tv = t.baseElemOf();
1783             return tv.ty == Tstruct || tv.ty == Tclass; // for now assume these might have a destructor
1784         }
1785 
1786         /* Don't inline a function that returns non-void, but has
1787          * no or multiple return expression.
1788          * When inlining as a statement:
1789          * 1. don't inline array operations, because the order the arguments
1790          *    get evaluated gets reversed. This is the same issue that e2ir.callfunc()
1791          *    has with them
1792          * 2. don't inline when the return value has a destructor, as it doesn't
1793          *    get handled properly
1794          */
1795         if (tf.next && tf.next.ty != Tvoid &&
1796             (!(fd.hasReturnExp & 1) ||
1797              statementsToo && hasDtor(tf.next)) &&
1798             !hdrscan)
1799         {
1800             static if (CANINLINE_LOG)
1801             {
1802                 printf("\t3: no %s\n", fd.toChars());
1803             }
1804             goto Lno;
1805         }
1806 
1807         /* https://issues.dlang.org/show_bug.cgi?id=14560
1808          * If fd returns void, all explicit `return;`s
1809          * must not appear in the expanded result.
1810          * See also ReturnStatement.doInlineAs!Statement().
1811          */
1812     }
1813 
1814     // cannot inline constructor calls because we need to convert:
1815     //      return;
1816     // to:
1817     //      return this;
1818     // ensure() has magic properties the inliner loses
1819     // require() has magic properties too
1820     // see bug 7699
1821     // no nested references to this frame
1822     if (!fd.fbody ||
1823         fd.ident == Id.ensure ||
1824         (fd.ident == Id.require &&
1825          fd.toParent().isFuncDeclaration() &&
1826          fd.toParent().isFuncDeclaration().needThis()) ||
1827         !hdrscan && (fd.isSynchronized() ||
1828                      fd.isImportedSymbol() ||
1829                      fd.hasNestedFrameRefs() ||
1830                      (fd.isVirtual() && !fd.isFinalFunc())))
1831     {
1832         static if (CANINLINE_LOG)
1833         {
1834             printf("\t4: no %s\n", fd.toChars());
1835         }
1836         goto Lno;
1837     }
1838 
1839     // cannot inline functions as statement if they have multiple
1840     //  return statements
1841     if ((fd.hasReturnExp & 16) && statementsToo)
1842     {
1843         static if (CANINLINE_LOG)
1844         {
1845             printf("\t5: no %s\n", fd.toChars());
1846         }
1847         goto Lno;
1848     }
1849 
1850     {
1851         cost = inlineCostFunction(fd, hasthis, hdrscan);
1852     }
1853     static if (CANINLINE_LOG)
1854     {
1855         printf("\tcost = %d for %s\n", cost, fd.toChars());
1856     }
1857 
1858     if (tooCostly(cost))
1859         goto Lno;
1860     if (!statementsToo && cost > COST_MAX)
1861         goto Lno;
1862 
1863     if (!hdrscan)
1864     {
1865         // Don't modify inlineStatus for header content scan
1866         if (statementsToo)
1867             fd.inlineStatusStmt = ILS.yes;
1868         else
1869             fd.inlineStatusExp = ILS.yes;
1870 
1871         inlineScanDsymbol(fd); // Don't scan recursively for header content scan
1872 
1873         if (fd.inlineStatusExp == ILS.uninitialized)
1874         {
1875             // Need to redo cost computation, as some statements or expressions have been inlined
1876             cost = inlineCostFunction(fd, hasthis, hdrscan);
1877             static if (CANINLINE_LOG)
1878             {
1879                 printf("recomputed cost = %d for %s\n", cost, fd.toChars());
1880             }
1881 
1882             if (tooCostly(cost))
1883                 goto Lno;
1884             if (!statementsToo && cost > COST_MAX)
1885                 goto Lno;
1886 
1887             if (statementsToo)
1888                 fd.inlineStatusStmt = ILS.yes;
1889             else
1890                 fd.inlineStatusExp = ILS.yes;
1891         }
1892     }
1893     static if (CANINLINE_LOG)
1894     {
1895         printf("\t2: yes %s\n", fd.toChars());
1896     }
1897     return true;
1898 
1899 Lno:
1900     if (fd.inlining == PINLINE.always && global.params.warnings == DiagnosticReporting.inform)
1901         warning(fd.loc, "cannot inline function `%s`", fd.toPrettyChars());
1902 
1903     if (!hdrscan) // Don't modify inlineStatus for header content scan
1904     {
1905         if (statementsToo)
1906             fd.inlineStatusStmt = ILS.no;
1907         else
1908             fd.inlineStatusExp = ILS.no;
1909     }
1910     static if (CANINLINE_LOG)
1911     {
1912         printf("\t2: no %s\n", fd.toChars());
1913     }
1914     return false;
1915 }
1916 
1917 /***********************************************************
1918  * Expand a function call inline,
1919  *      ethis.fd(arguments)
1920  *
1921  * Params:
1922  *      callLoc = location of CallExp
1923  *      fd = function to expand
1924  *      parent = function that the call to fd is being expanded into
1925  *      eret = if !null then the lvalue of where the nrvo return value goes
1926  *      ethis = 'this' reference
1927  *      arguments = arguments passed to fd
1928  *      asStatements = expand to Statements rather than Expressions
1929  *      eresult = if expanding to an expression, this is where the expression is written to
1930  *      sresult = if expanding to a statement, this is where the statement is written to
1931  *      again = if true, then fd can be inline scanned again because there may be
1932  *           more opportunities for inlining
1933  */
1934 private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration parent, Expression eret,
1935         Expression ethis, Expressions* arguments, bool asStatements, VarDeclaration vthis2,
1936         out Expression eresult, out Statement sresult, out bool again)
1937 {
1938     auto tf = fd.type.isTypeFunction();
1939     static if (LOG || CANINLINE_LOG || EXPANDINLINE_LOG)
1940         printf("FuncDeclaration.expandInline('%s', %d)\n", fd.toChars(), asStatements);
1941     static if (EXPANDINLINE_LOG)
1942     {
1943         if (eret) printf("\teret = %s\n", eret.toChars());
1944         if (ethis) printf("\tethis = %s\n", ethis.toChars());
1945     }
1946     scope ids = new InlineDoState(parent, fd);
1947 
1948     if (fd.isNested())
1949     {
1950         if (!parent.inlinedNestedCallees)
1951             parent.inlinedNestedCallees = new FuncDeclarations();
1952         parent.inlinedNestedCallees.push(fd);
1953     }
1954 
1955     VarDeclaration vret;    // will be set the function call result
1956     if (eret)
1957     {
1958         if (auto ve = eret.isVarExp())
1959         {
1960             vret = ve.var.isVarDeclaration();
1961             assert(!(vret.storage_class & (STC.out_ | STC.ref_)));
1962             eret = null;
1963         }
1964         else
1965         {
1966             /* Inlining:
1967              *   this.field = foo();   // inside constructor
1968              */
1969             auto ei = new ExpInitializer(callLoc, null);
1970             auto tmp = Identifier.generateId("__retvar");
1971             vret = new VarDeclaration(fd.loc, eret.type, tmp, ei);
1972             vret.storage_class |= STC.temp | STC.ref_;
1973             vret._linkage = LINK.d;
1974             vret.parent = parent;
1975 
1976             ei.exp = new ConstructExp(fd.loc, vret, eret);
1977             ei.exp.type = vret.type;
1978 
1979             auto de = new DeclarationExp(fd.loc, vret);
1980             de.type = Type.tvoid;
1981             eret = de;
1982         }
1983 
1984         if (!asStatements && fd.nrvo_var)
1985         {
1986             ids.from.push(fd.nrvo_var);
1987             ids.to.push(vret);
1988         }
1989     }
1990     else
1991     {
1992         if (!asStatements && fd.nrvo_var)
1993         {
1994             auto tmp = Identifier.generateId("__retvar");
1995             vret = new VarDeclaration(fd.loc, fd.nrvo_var.type, tmp, new VoidInitializer(fd.loc));
1996             assert(!tf.isref);
1997             vret.storage_class = STC.temp | STC.rvalue;
1998             vret._linkage = tf.linkage;
1999             vret.parent = parent;
2000 
2001             auto de = new DeclarationExp(fd.loc, vret);
2002             de.type = Type.tvoid;
2003             eret = de;
2004 
2005             ids.from.push(fd.nrvo_var);
2006             ids.to.push(vret);
2007         }
2008     }
2009 
2010     // Set up vthis
2011     VarDeclaration vthis;
2012     if (ethis)
2013     {
2014         Expression e0;
2015         ethis = Expression.extractLast(ethis, e0);
2016         assert(vthis2 || !fd.hasDualContext());
2017         if (vthis2)
2018         {
2019             // void*[2] __this = [ethis, this]
2020             if (ethis.type.ty == Tstruct)
2021             {
2022                 // &ethis
2023                 Type t = ethis.type.pointerTo();
2024                 ethis = new AddrExp(ethis.loc, ethis);
2025                 ethis.type = t;
2026             }
2027             auto elements = new Expressions(2);
2028             (*elements)[0] = ethis;
2029             (*elements)[1] = new NullExp(Loc.initial, Type.tvoidptr);
2030             Expression ae = new ArrayLiteralExp(vthis2.loc, vthis2.type, elements);
2031             Expression ce = new ConstructExp(vthis2.loc, vthis2, ae);
2032             ce.type = vthis2.type;
2033             vthis2._init = new ExpInitializer(vthis2.loc, ce);
2034             vthis = vthis2;
2035         }
2036         else if (auto ve = ethis.isVarExp())
2037         {
2038             vthis = ve.var.isVarDeclaration();
2039         }
2040         else
2041         {
2042             //assert(ethis.type.ty != Tpointer);
2043             if (ethis.type.ty == Tpointer)
2044             {
2045                 Type t = ethis.type.nextOf();
2046                 ethis = new PtrExp(ethis.loc, ethis);
2047                 ethis.type = t;
2048             }
2049 
2050             auto ei = new ExpInitializer(fd.loc, ethis);
2051             vthis = new VarDeclaration(fd.loc, ethis.type, Id.This, ei);
2052             if (ethis.type.ty != Tclass)
2053                 vthis.storage_class = STC.ref_;
2054             else
2055                 vthis.storage_class = STC.in_;
2056             vthis._linkage = LINK.d;
2057             vthis.parent = parent;
2058 
2059             ei.exp = new ConstructExp(fd.loc, vthis, ethis);
2060             ei.exp.type = vthis.type;
2061 
2062             auto de = new DeclarationExp(fd.loc, vthis);
2063             de.type = Type.tvoid;
2064             e0 = Expression.combine(e0, de);
2065         }
2066         ethis = e0;
2067 
2068         ids.vthis = vthis;
2069     }
2070 
2071     // Set up parameters
2072     Expression eparams;
2073     if (arguments && arguments.length)
2074     {
2075         assert(fd.parameters.length == arguments.length);
2076         foreach (i; 0 .. arguments.length)
2077         {
2078             auto vfrom = (*fd.parameters)[i];
2079             auto arg = (*arguments)[i];
2080 
2081             auto ei = new ExpInitializer(vfrom.loc, arg);
2082             auto vto = new VarDeclaration(vfrom.loc, vfrom.type, vfrom.ident, ei);
2083             vto.storage_class |= vfrom.storage_class & (STC.temp | STC.IOR | STC.lazy_ | STC.nodtor);
2084             vto._linkage = vfrom._linkage;
2085             vto.parent = parent;
2086             //printf("vto = '%s', vto.storage_class = x%x\n", vto.toChars(), vto.storage_class);
2087             //printf("vto.parent = '%s'\n", parent.toChars());
2088 
2089             if (VarExp ve = arg.isVarExp())
2090             {
2091                 VarDeclaration va = ve.var.isVarDeclaration();
2092                 if (va && va.isArgDtorVar)
2093                 {
2094                     assert(vto.storage_class & STC.nodtor);
2095                     // The destructor is called on va so take it by ref
2096                     vto.storage_class |= STC.ref_;
2097                 }
2098             }
2099 
2100             // Even if vto is STC.lazy_, `vto = arg` is handled correctly in glue layer.
2101             ei.exp = new BlitExp(vto.loc, vto, arg);
2102             ei.exp.type = vto.type;
2103 
2104             ids.from.push(vfrom);
2105             ids.to.push(vto);
2106 
2107             auto de = new DeclarationExp(vto.loc, vto);
2108             de.type = Type.tvoid;
2109             eparams = Expression.combine(eparams, de);
2110 
2111             /* If function pointer or delegate parameters are present,
2112              * inline scan again because if they are initialized to a symbol,
2113              * any calls to the fp or dg can be inlined.
2114              */
2115             if (vfrom.type.ty == Tdelegate ||
2116                 vfrom.type.isPtrToFunction())
2117             {
2118                 if (auto ve = arg.isVarExp())
2119                 {
2120                     if (ve.var.isFuncDeclaration())
2121                         again = true;
2122                 }
2123                 else if (auto se = arg.isSymOffExp())
2124                 {
2125                     if (se.var.isFuncDeclaration())
2126                         again = true;
2127                 }
2128                 else if (arg.op == EXP.function_ || arg.op == EXP.delegate_)
2129                     again = true;
2130             }
2131         }
2132     }
2133 
2134     if (asStatements)
2135     {
2136         /* Construct:
2137          *  { eret; ethis; eparams; fd.fbody; }
2138          * or:
2139          *  { eret; ethis; try { eparams; fd.fbody; } finally { vthis.edtor; } }
2140          */
2141 
2142         auto as = new Statements();
2143         if (eret)
2144             as.push(new ExpStatement(callLoc, eret));
2145         if (ethis)
2146             as.push(new ExpStatement(callLoc, ethis));
2147 
2148         auto as2 = as;
2149         if (vthis && !vthis.isDataseg())
2150         {
2151             if (vthis.needsScopeDtor())
2152             {
2153                 // same with ExpStatement.scopeCode()
2154                 as2 = new Statements();
2155                 vthis.storage_class |= STC.nodtor;
2156             }
2157         }
2158 
2159         if (eparams)
2160             as2.push(new ExpStatement(callLoc, eparams));
2161 
2162         fd.inlineNest++;
2163         Statement s = doInlineAs!Statement(fd.fbody, ids);
2164         fd.inlineNest--;
2165         as2.push(s);
2166 
2167         if (as2 != as)
2168         {
2169             as.push(new TryFinallyStatement(callLoc,
2170                         new CompoundStatement(callLoc, as2),
2171                         new DtorExpStatement(callLoc, vthis.edtor, vthis)));
2172         }
2173 
2174         sresult = new ScopeStatement(callLoc, new CompoundStatement(callLoc, as), callLoc);
2175 
2176         static if (EXPANDINLINE_LOG)
2177             printf("\n[%s] %s expandInline sresult =\n%s\n",
2178                 callLoc.toChars(), fd.toPrettyChars(), sresult.toChars());
2179     }
2180     else
2181     {
2182         /* Construct:
2183          *  (eret, ethis, eparams, fd.fbody)
2184          */
2185 
2186         fd.inlineNest++;
2187         auto e = doInlineAs!Expression(fd.fbody, ids);
2188         fd.inlineNest--;
2189 
2190         // https://issues.dlang.org/show_bug.cgi?id=11322
2191         if (tf.isref)
2192             e = e.toLvalue(null, null);
2193 
2194         /* If the inlined function returns a copy of a struct,
2195          * and then the return value is used subsequently as an
2196          * lvalue, as in a struct return that is then used as a 'this'.
2197          * Taking the address of the return value will be taking the address
2198          * of the original, not the copy. Fix this by assigning the return value to
2199          * a temporary, then returning the temporary. If the temporary is used as an
2200          * lvalue, it will work.
2201          * This only happens with struct returns.
2202          * See https://issues.dlang.org/show_bug.cgi?id=2127 for an example.
2203          *
2204          * On constructor call making __inlineretval is merely redundant, because
2205          * the returned reference is exactly same as vthis, and the 'this' variable
2206          * already exists at the caller side.
2207          */
2208         if (tf.next.ty == Tstruct && !fd.nrvo_var && !fd.isCtorDeclaration() &&
2209             !isConstruction(e))
2210         {
2211             /* Generate a new variable to hold the result and initialize it with the
2212              * inlined body of the function:
2213              *   tret __inlineretval = e;
2214              */
2215             auto ei = new ExpInitializer(callLoc, e);
2216             auto tmp = Identifier.generateId("__inlineretval");
2217             auto vd = new VarDeclaration(callLoc, tf.next, tmp, ei);
2218             vd.storage_class = STC.temp | (tf.isref ? STC.ref_ : STC.rvalue);
2219             vd._linkage = tf.linkage;
2220             vd.parent = parent;
2221 
2222             ei.exp = new ConstructExp(callLoc, vd, e);
2223             ei.exp.type = vd.type;
2224 
2225             auto de = new DeclarationExp(callLoc, vd);
2226             de.type = Type.tvoid;
2227 
2228             // Chain the two together:
2229             //   ( typeof(return) __inlineretval = ( inlined body )) , __inlineretval
2230             e = Expression.combine(de, new VarExp(callLoc, vd));
2231         }
2232 
2233         // https://issues.dlang.org/show_bug.cgi?id=15210
2234         if (tf.next.ty == Tvoid && e && e.type.ty != Tvoid)
2235         {
2236             e = new CastExp(callLoc, e, Type.tvoid);
2237             e.type = Type.tvoid;
2238         }
2239 
2240         eresult = Expression.combine(eresult, eret, ethis, eparams);
2241         eresult = Expression.combine(eresult, e);
2242 
2243         static if (EXPANDINLINE_LOG)
2244             printf("\n[%s] %s expandInline eresult = %s\n",
2245                 callLoc.toChars(), fd.toPrettyChars(), eresult.toChars());
2246     }
2247 
2248     // Need to reevaluate whether parent can now be inlined
2249     // in expressions, as we might have inlined statements
2250     parent.inlineStatusExp = ILS.uninitialized;
2251 }
2252 
2253 /****************************************************
2254  * Determine if the value of `e` is the result of construction.
2255  *
2256  * Params:
2257  *      e = expression to check
2258  * Returns:
2259  *      true for value generated by a constructor or struct literal
2260  */
2261 private bool isConstruction(Expression e)
2262 {
2263     e = lastComma(e);
2264 
2265     if (e.op == EXP.structLiteral)
2266     {
2267         return true;
2268     }
2269     /* Detect:
2270      *    structliteral.ctor(args)
2271      */
2272     else if (e.op == EXP.call)
2273     {
2274         auto ce = cast(CallExp)e;
2275         if (ce.e1.op == EXP.dotVariable)
2276         {
2277             auto dve = cast(DotVarExp)ce.e1;
2278             auto fd = dve.var.isFuncDeclaration();
2279             if (fd && fd.isCtorDeclaration())
2280             {
2281                 if (dve.e1.op == EXP.structLiteral)
2282                 {
2283                     return true;
2284                 }
2285             }
2286         }
2287     }
2288     return false;
2289 }
2290 
2291 
2292 /***********************************************************
2293  * Determine if v is 'head const', meaning
2294  * that once it is initialized it is not changed
2295  * again.
2296  *
2297  * This is done using a primitive flow analysis.
2298  *
2299  * v is head const if v is const or immutable.
2300  * Otherwise, v is assumed to be head const unless one of the
2301  * following is true:
2302  *      1. v is a `ref` or `out` variable
2303  *      2. v is a parameter and fd is a variadic function
2304  *      3. v is assigned to again
2305  *      4. the address of v is taken
2306  *      5. v is referred to by a function nested within fd
2307  *      6. v is ever assigned to a `ref` or `out` variable
2308  *      7. v is ever passed to another function as `ref` or `out`
2309  *
2310  * Params:
2311  *      v       variable to check
2312  *      fd      function that v is local to
2313  * Returns:
2314  *      true if v's initializer is the only value assigned to v
2315  */
2316 private bool onlyOneAssign(VarDeclaration v, FuncDeclaration fd) @safe
2317 {
2318     if (!v.type.isMutable())
2319         return true;            // currently the only case handled atm
2320     return false;
2321 }
2322 
2323 /************************************************************
2324  * See if arguments to a function are creating temporaries that
2325  * will need destruction after the function is executed.
2326  * Params:
2327  *      arguments = arguments to function
2328  * Returns:
2329  *      true if temporaries need destruction
2330  */
2331 
2332 private bool argumentsNeedDtors(Expressions* arguments)
2333 {
2334     if (arguments)
2335     {
2336         foreach (arg; *arguments)
2337         {
2338             if (expNeedsDtor(arg))
2339                 return true;
2340         }
2341     }
2342     return false;
2343 }
2344 
2345 /************************************************************
2346  * See if expression is creating temporaries that
2347  * will need destruction at the end of the scope.
2348  * Params:
2349  *      exp = expression
2350  * Returns:
2351  *      true if temporaries need destruction
2352  */
2353 
2354 private bool expNeedsDtor(Expression exp)
2355 {
2356     extern (C++) final class NeedsDtor : StoppableVisitor
2357     {
2358         alias visit = typeof(super).visit;
2359         Expression exp;
2360 
2361     public:
2362         extern (D) this(Expression exp) scope @safe
2363         {
2364             this.exp = exp;
2365         }
2366 
2367         override void visit(Expression)
2368         {
2369         }
2370 
2371         override void visit(DeclarationExp de)
2372         {
2373             Dsymbol_needsDtor(de.declaration);
2374         }
2375 
2376         void Dsymbol_needsDtor(Dsymbol s)
2377         {
2378             /* This mirrors logic of Dsymbol_toElem() in e2ir.d
2379              * perhaps they can be combined.
2380              */
2381 
2382             void symbolDg(Dsymbol s)
2383             {
2384                 Dsymbol_needsDtor(s);
2385             }
2386 
2387             if (auto vd = s.isVarDeclaration())
2388             {
2389                 s = s.toAlias();
2390                 if (s != vd)
2391                     return Dsymbol_needsDtor(s);
2392                 else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.gshared | STC.manifest))
2393                     return;
2394                 if (vd.needsScopeDtor())
2395                 {
2396                     stop = true;
2397                 }
2398             }
2399             else if (auto tm = s.isTemplateMixin())
2400             {
2401                 tm.members.foreachDsymbol(&symbolDg);
2402             }
2403             else if (auto ad = s.isAttribDeclaration())
2404             {
2405                 ad.include(null).foreachDsymbol(&symbolDg);
2406             }
2407             else if (auto td = s.isTupleDeclaration())
2408             {
2409                 td.foreachVar(&symbolDg);
2410             }
2411 
2412 
2413         }
2414     }
2415 
2416     scope NeedsDtor ct = new NeedsDtor(exp);
2417     return walkPostorder(exp, ct);
2418 }