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