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