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 new ErrorExp();
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 = new IntegerExp(vd._init.loc, 0, Type.tint32);
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 (fd.isUnitTestDeclaration() && !global.params.useUnitTests ||
1547             fd.flags & FUNCFLAG.inlineScanned)
1548             return;
1549         if (fd.fbody && !fd.naked)
1550         {
1551             auto againsave = again;
1552             auto parentsave = parent;
1553             parent = fd;
1554             do
1555             {
1556                 again = false;
1557                 fd.inlineNest++;
1558                 fd.flags |= FUNCFLAG.inlineScanned;
1559                 inlineScan(fd.fbody);
1560                 fd.inlineNest--;
1561             }
1562             while (again);
1563             again = againsave;
1564             parent = parentsave;
1565         }
1566     }
1567 
1568     override void visit(AttribDeclaration d)
1569     {
1570         Dsymbols* decls = d.include(null);
1571         if (decls)
1572         {
1573             foreach (i; 0 .. decls.dim)
1574             {
1575                 Dsymbol s = (*decls)[i];
1576                 //printf("AttribDeclaration.inlineScan %s\n", s.toChars());
1577                 s.accept(this);
1578             }
1579         }
1580     }
1581 
1582     override void visit(AggregateDeclaration ad)
1583     {
1584         //printf("AggregateDeclaration.inlineScan(%s)\n", toChars());
1585         if (ad.members)
1586         {
1587             foreach (i; 0 .. ad.members.dim)
1588             {
1589                 Dsymbol s = (*ad.members)[i];
1590                 //printf("inline scan aggregate symbol '%s'\n", s.toChars());
1591                 s.accept(this);
1592             }
1593         }
1594     }
1595 
1596     override void visit(TemplateInstance ti)
1597     {
1598         static if (LOG)
1599         {
1600             printf("TemplateInstance.inlineScan('%s')\n", ti.toChars());
1601         }
1602         if (!ti.errors && ti.members)
1603         {
1604             foreach (i; 0 .. ti.members.dim)
1605             {
1606                 Dsymbol s = (*ti.members)[i];
1607                 s.accept(this);
1608             }
1609         }
1610     }
1611 }
1612 
1613 /***********************************************************
1614  * Test that `fd` can be inlined.
1615  *
1616  * Params:
1617  *  hasthis = `true` if the function call has explicit 'this' expression.
1618  *  hdrscan = `true` if the inline scan is for 'D header' content.
1619  *  statementsToo = `true` if the function call is placed on ExpStatement.
1620  *      It means more code-block dependent statements in fd body - ForStatement,
1621  *      ThrowStatement, etc. can be inlined.
1622  *
1623  * Returns:
1624  *  true if the function body can be expanded.
1625  *
1626  * Todo:
1627  *  - Would be able to eliminate `hasthis` parameter, because semantic analysis
1628  *    no longer accepts calls of contextful function without valid 'this'.
1629  *  - Would be able to eliminate `hdrscan` parameter, because it's always false.
1630  */
1631 private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsToo)
1632 {
1633     int cost;
1634 
1635     static if (CANINLINE_LOG)
1636     {
1637         printf("FuncDeclaration.canInline(hasthis = %d, statementsToo = %d, '%s')\n",
1638             hasthis, statementsToo, fd.toPrettyChars());
1639     }
1640 
1641     if (fd.needThis() && !hasthis)
1642         return false;
1643 
1644     if (fd.inlineNest)
1645     {
1646         static if (CANINLINE_LOG)
1647         {
1648             printf("\t1: no, inlineNest = %d, semanticRun = %d\n", fd.inlineNest, fd.semanticRun);
1649         }
1650         return false;
1651     }
1652 
1653     if (fd.semanticRun < PASS.semantic3 && !hdrscan)
1654     {
1655         if (!fd.fbody)
1656             return false;
1657         if (!fd.functionSemantic3())
1658             return false;
1659         Module.runDeferredSemantic3();
1660         if (global.errors)
1661             return false;
1662         assert(fd.semanticRun >= PASS.semantic3done);
1663     }
1664 
1665     final switch (statementsToo ? fd.inlineStatusStmt : fd.inlineStatusExp)
1666     {
1667     case ILS.yes:
1668         static if (CANINLINE_LOG)
1669         {
1670             printf("\t1: yes %s\n", fd.toChars());
1671         }
1672         return true;
1673     case ILS.no:
1674         static if (CANINLINE_LOG)
1675         {
1676             printf("\t1: no %s\n", fd.toChars());
1677         }
1678         return false;
1679     case ILS.uninitialized:
1680         break;
1681     }
1682 
1683     final switch (fd.inlining)
1684     {
1685     case PINLINE.default_:
1686         break;
1687     case PINLINE.always:
1688         break;
1689     case PINLINE.never:
1690         return false;
1691     }
1692 
1693     if (fd.type)
1694     {
1695         TypeFunction tf = fd.type.isTypeFunction();
1696 
1697         // no variadic parameter lists
1698         if (tf.parameterList.varargs == VarArg.variadic)
1699             goto Lno;
1700 
1701         /* No lazy parameters when inlining by statement, as the inliner tries to
1702          * operate on the created delegate itself rather than the return value.
1703          * Discussion: https://github.com/dlang/dmd/pull/6815
1704          */
1705         if (statementsToo && fd.parameters)
1706         {
1707             foreach (param; *fd.parameters)
1708             {
1709                 if (param.storage_class & STC.lazy_)
1710                     goto Lno;
1711             }
1712         }
1713 
1714         static bool hasDtor(Type t)
1715         {
1716             auto tv = t.baseElemOf();
1717             return tv.ty == Tstruct || tv.ty == Tclass; // for now assume these might have a destructor
1718         }
1719 
1720         /* Don't inline a function that returns non-void, but has
1721          * no or multiple return expression.
1722          * When inlining as a statement:
1723          * 1. don't inline array operations, because the order the arguments
1724          *    get evaluated gets reversed. This is the same issue that e2ir.callfunc()
1725          *    has with them
1726          * 2. don't inline when the return value has a destructor, as it doesn't
1727          *    get handled properly
1728          */
1729         if (tf.next && tf.next.ty != Tvoid &&
1730             (!(fd.hasReturnExp & 1) ||
1731              statementsToo && (fd.isArrayOp || hasDtor(tf.next))) &&
1732             !hdrscan)
1733         {
1734             static if (CANINLINE_LOG)
1735             {
1736                 printf("\t3: no %s\n", fd.toChars());
1737             }
1738             goto Lno;
1739         }
1740 
1741         /* https://issues.dlang.org/show_bug.cgi?id=14560
1742          * If fd returns void, all explicit `return;`s
1743          * must not appear in the expanded result.
1744          * See also ReturnStatement.doInlineAs!Statement().
1745          */
1746     }
1747 
1748     // cannot inline constructor calls because we need to convert:
1749     //      return;
1750     // to:
1751     //      return this;
1752     // ensure() has magic properties the inliner loses
1753     // require() has magic properties too
1754     // see bug 7699
1755     // no nested references to this frame
1756     if (!fd.fbody ||
1757         fd.ident == Id.ensure ||
1758         (fd.ident == Id.require &&
1759          fd.toParent().isFuncDeclaration() &&
1760          fd.toParent().isFuncDeclaration().needThis()) ||
1761         !hdrscan && (fd.isSynchronized() ||
1762                      fd.isImportedSymbol() ||
1763                      fd.hasNestedFrameRefs() ||
1764                      (fd.isVirtual() && !fd.isFinalFunc())))
1765     {
1766         static if (CANINLINE_LOG)
1767         {
1768             printf("\t4: no %s\n", fd.toChars());
1769         }
1770         goto Lno;
1771     }
1772 
1773     // cannot inline functions as statement if they have multiple
1774     //  return statements
1775     if ((fd.hasReturnExp & 16) && statementsToo)
1776     {
1777         static if (CANINLINE_LOG)
1778         {
1779             printf("\t5: no %s\n", fd.toChars());
1780         }
1781         goto Lno;
1782     }
1783 
1784     {
1785         cost = inlineCostFunction(fd, hasthis, hdrscan);
1786     }
1787     static if (CANINLINE_LOG)
1788     {
1789         printf("\tcost = %d for %s\n", cost, fd.toChars());
1790     }
1791 
1792     if (tooCostly(cost))
1793         goto Lno;
1794     if (!statementsToo && cost > COST_MAX)
1795         goto Lno;
1796 
1797     if (!hdrscan)
1798     {
1799         // Don't modify inlineStatus for header content scan
1800         if (statementsToo)
1801             fd.inlineStatusStmt = ILS.yes;
1802         else
1803             fd.inlineStatusExp = ILS.yes;
1804 
1805         scope InlineScanVisitor v = new InlineScanVisitor();
1806         fd.accept(v); // Don't scan recursively for header content scan
1807 
1808         if (fd.inlineStatusExp == ILS.uninitialized)
1809         {
1810             // Need to redo cost computation, as some statements or expressions have been inlined
1811             cost = inlineCostFunction(fd, hasthis, hdrscan);
1812             static if (CANINLINE_LOG)
1813             {
1814                 printf("recomputed cost = %d for %s\n", cost, fd.toChars());
1815             }
1816 
1817             if (tooCostly(cost))
1818                 goto Lno;
1819             if (!statementsToo && cost > COST_MAX)
1820                 goto Lno;
1821 
1822             if (statementsToo)
1823                 fd.inlineStatusStmt = ILS.yes;
1824             else
1825                 fd.inlineStatusExp = ILS.yes;
1826         }
1827     }
1828     static if (CANINLINE_LOG)
1829     {
1830         printf("\t2: yes %s\n", fd.toChars());
1831     }
1832     return true;
1833 
1834 Lno:
1835     if (fd.inlining == PINLINE.always)
1836         fd.error("cannot inline function");
1837 
1838     if (!hdrscan) // Don't modify inlineStatus for header content scan
1839     {
1840         if (statementsToo)
1841             fd.inlineStatusStmt = ILS.no;
1842         else
1843             fd.inlineStatusExp = ILS.no;
1844     }
1845     static if (CANINLINE_LOG)
1846     {
1847         printf("\t2: no %s\n", fd.toChars());
1848     }
1849     return false;
1850 }
1851 
1852 /***********************************************************
1853  * Expand a function call inline,
1854  *      ethis.fd(arguments)
1855  *
1856  * Params:
1857  *      callLoc = location of CallExp
1858  *      fd = function to expand
1859  *      parent = function that the call to fd is being expanded into
1860  *      eret = if !null then the lvalue of where the nrvo return value goes
1861  *      ethis = 'this' reference
1862  *      arguments = arguments passed to fd
1863  *      asStatements = expand to Statements rather than Expressions
1864  *      eresult = if expanding to an expression, this is where the expression is written to
1865  *      sresult = if expanding to a statement, this is where the statement is written to
1866  *      again = if true, then fd can be inline scanned again because there may be
1867  *           more opportunities for inlining
1868  */
1869 private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration parent, Expression eret,
1870         Expression ethis, Expressions* arguments, bool asStatements, VarDeclaration vthis2,
1871         out Expression eresult, out Statement sresult, out bool again)
1872 {
1873     auto tf = fd.type.isTypeFunction();
1874     static if (LOG || CANINLINE_LOG || EXPANDINLINE_LOG)
1875         printf("FuncDeclaration.expandInline('%s', %d)\n", fd.toChars(), asStatements);
1876     static if (EXPANDINLINE_LOG)
1877     {
1878         if (eret) printf("\teret = %s\n", eret.toChars());
1879         if (ethis) printf("\tethis = %s\n", ethis.toChars());
1880     }
1881     scope ids = new InlineDoState(parent, fd);
1882 
1883     if (fd.isNested())
1884     {
1885         if (!parent.inlinedNestedCallees)
1886             parent.inlinedNestedCallees = new FuncDeclarations();
1887         parent.inlinedNestedCallees.push(fd);
1888     }
1889 
1890     VarDeclaration vret;    // will be set the function call result
1891     if (eret)
1892     {
1893         if (auto ve = eret.isVarExp())
1894         {
1895             vret = ve.var.isVarDeclaration();
1896             assert(!(vret.storage_class & (STC.out_ | STC.ref_)));
1897             eret = null;
1898         }
1899         else
1900         {
1901             /* Inlining:
1902              *   this.field = foo();   // inside constructor
1903              */
1904             auto ei = new ExpInitializer(callLoc, null);
1905             auto tmp = Identifier.generateId("__retvar");
1906             vret = new VarDeclaration(fd.loc, eret.type, tmp, ei);
1907             vret.storage_class |= STC.temp | STC.ref_;
1908             vret.linkage = LINK.d;
1909             vret.parent = parent;
1910 
1911             ei.exp = new ConstructExp(fd.loc, vret, eret);
1912             ei.exp.type = vret.type;
1913 
1914             auto de = new DeclarationExp(fd.loc, vret);
1915             de.type = Type.tvoid;
1916             eret = de;
1917         }
1918 
1919         if (!asStatements && fd.nrvo_var)
1920         {
1921             ids.from.push(fd.nrvo_var);
1922             ids.to.push(vret);
1923         }
1924     }
1925     else
1926     {
1927         if (!asStatements && fd.nrvo_var)
1928         {
1929             auto tmp = Identifier.generateId("__retvar");
1930             vret = new VarDeclaration(fd.loc, fd.nrvo_var.type, tmp, new VoidInitializer(fd.loc));
1931             assert(!tf.isref);
1932             vret.storage_class = STC.temp | STC.rvalue;
1933             vret.linkage = tf.linkage;
1934             vret.parent = parent;
1935 
1936             auto de = new DeclarationExp(fd.loc, vret);
1937             de.type = Type.tvoid;
1938             eret = de;
1939 
1940             ids.from.push(fd.nrvo_var);
1941             ids.to.push(vret);
1942         }
1943     }
1944 
1945     // Set up vthis
1946     VarDeclaration vthis;
1947     if (ethis)
1948     {
1949         Expression e0;
1950         ethis = Expression.extractLast(ethis, e0);
1951         assert(vthis2 || !fd.isThis2);
1952         if (vthis2)
1953         {
1954             // void*[2] __this = [ethis, this]
1955             if (ethis.type.ty == Tstruct)
1956             {
1957                 // &ethis
1958                 Type t = ethis.type.pointerTo();
1959                 ethis = new AddrExp(ethis.loc, ethis);
1960                 ethis.type = t;
1961             }
1962             auto elements = new Expressions(2);
1963             (*elements)[0] = ethis;
1964             (*elements)[1] = new NullExp(Loc.initial, Type.tvoidptr);
1965             Expression ae = new ArrayLiteralExp(vthis2.loc, vthis2.type, elements);
1966             Expression ce = new ConstructExp(vthis2.loc, vthis2, ae);
1967             ce.type = vthis2.type;
1968             vthis2._init = new ExpInitializer(vthis2.loc, ce);
1969             vthis = vthis2;
1970         }
1971         else if (auto ve = ethis.isVarExp())
1972         {
1973             vthis = ve.var.isVarDeclaration();
1974         }
1975         else
1976         {
1977             //assert(ethis.type.ty != Tpointer);
1978             if (ethis.type.ty == Tpointer)
1979             {
1980                 Type t = ethis.type.nextOf();
1981                 ethis = new PtrExp(ethis.loc, ethis);
1982                 ethis.type = t;
1983             }
1984 
1985             auto ei = new ExpInitializer(fd.loc, ethis);
1986             vthis = new VarDeclaration(fd.loc, ethis.type, Id.This, ei);
1987             if (ethis.type.ty != Tclass)
1988                 vthis.storage_class = STC.ref_;
1989             else
1990                 vthis.storage_class = STC.in_;
1991             vthis.linkage = LINK.d;
1992             vthis.parent = parent;
1993 
1994             ei.exp = new ConstructExp(fd.loc, vthis, ethis);
1995             ei.exp.type = vthis.type;
1996 
1997             auto de = new DeclarationExp(fd.loc, vthis);
1998             de.type = Type.tvoid;
1999             e0 = Expression.combine(e0, de);
2000         }
2001         ethis = e0;
2002 
2003         ids.vthis = vthis;
2004     }
2005 
2006     // Set up parameters
2007     Expression eparams;
2008     if (arguments && arguments.dim)
2009     {
2010         assert(fd.parameters.dim == arguments.dim);
2011         foreach (i; 0 .. arguments.dim)
2012         {
2013             auto vfrom = (*fd.parameters)[i];
2014             auto arg = (*arguments)[i];
2015 
2016             auto ei = new ExpInitializer(vfrom.loc, arg);
2017             auto vto = new VarDeclaration(vfrom.loc, vfrom.type, vfrom.ident, ei);
2018             vto.storage_class |= vfrom.storage_class & (STC.temp | STC.in_ | STC.out_ | STC.lazy_ | STC.ref_);
2019             vto.linkage = vfrom.linkage;
2020             vto.parent = parent;
2021             //printf("vto = '%s', vto.storage_class = x%x\n", vto.toChars(), vto.storage_class);
2022             //printf("vto.parent = '%s'\n", parent.toChars());
2023 
2024             // Even if vto is STC.lazy_, `vto = arg` is handled correctly in glue layer.
2025             ei.exp = new BlitExp(vto.loc, vto, arg);
2026             ei.exp.type = vto.type;
2027 
2028             ids.from.push(vfrom);
2029             ids.to.push(vto);
2030 
2031             auto de = new DeclarationExp(vto.loc, vto);
2032             de.type = Type.tvoid;
2033             eparams = Expression.combine(eparams, de);
2034 
2035             /* If function pointer or delegate parameters are present,
2036              * inline scan again because if they are initialized to a symbol,
2037              * any calls to the fp or dg can be inlined.
2038              */
2039             if (vfrom.type.ty == Tdelegate ||
2040                 vfrom.type.ty == Tpointer && vfrom.type.nextOf().ty == Tfunction)
2041             {
2042                 if (auto ve = arg.isVarExp())
2043                 {
2044                     if (ve.var.isFuncDeclaration())
2045                         again = true;
2046                 }
2047                 else if (auto se = arg.isSymOffExp())
2048                 {
2049                     if (se.var.isFuncDeclaration())
2050                         again = true;
2051                 }
2052                 else if (arg.op == TOK.function_ || arg.op == TOK.delegate_)
2053                     again = true;
2054             }
2055         }
2056     }
2057 
2058     if (asStatements)
2059     {
2060         /* Construct:
2061          *  { eret; ethis; eparams; fd.fbody; }
2062          * or:
2063          *  { eret; ethis; try { eparams; fd.fbody; } finally { vthis.edtor; } }
2064          */
2065 
2066         auto as = new Statements();
2067         if (eret)
2068             as.push(new ExpStatement(callLoc, eret));
2069         if (ethis)
2070             as.push(new ExpStatement(callLoc, ethis));
2071 
2072         auto as2 = as;
2073         if (vthis && !vthis.isDataseg())
2074         {
2075             if (vthis.needsScopeDtor())
2076             {
2077                 // same with ExpStatement.scopeCode()
2078                 as2 = new Statements();
2079                 vthis.storage_class |= STC.nodtor;
2080             }
2081         }
2082 
2083         if (eparams)
2084             as2.push(new ExpStatement(callLoc, eparams));
2085 
2086         fd.inlineNest++;
2087         Statement s = doInlineAs!Statement(fd.fbody, ids);
2088         fd.inlineNest--;
2089         as2.push(s);
2090 
2091         if (as2 != as)
2092         {
2093             as.push(new TryFinallyStatement(callLoc,
2094                         new CompoundStatement(callLoc, as2),
2095                         new DtorExpStatement(callLoc, vthis.edtor, vthis)));
2096         }
2097 
2098         sresult = new ScopeStatement(callLoc, new CompoundStatement(callLoc, as), callLoc);
2099 
2100         static if (EXPANDINLINE_LOG)
2101             printf("\n[%s] %s expandInline sresult =\n%s\n",
2102                 callLoc.toChars(), fd.toPrettyChars(), sresult.toChars());
2103     }
2104     else
2105     {
2106         /* Construct:
2107          *  (eret, ethis, eparams, fd.fbody)
2108          */
2109 
2110         fd.inlineNest++;
2111         auto e = doInlineAs!Expression(fd.fbody, ids);
2112         fd.inlineNest--;
2113 
2114         // https://issues.dlang.org/show_bug.cgi?id=11322
2115         if (tf.isref)
2116             e = e.toLvalue(null, null);
2117 
2118         /* If the inlined function returns a copy of a struct,
2119          * and then the return value is used subsequently as an
2120          * lvalue, as in a struct return that is then used as a 'this'.
2121          * Taking the address of the return value will be taking the address
2122          * of the original, not the copy. Fix this by assigning the return value to
2123          * a temporary, then returning the temporary. If the temporary is used as an
2124          * lvalue, it will work.
2125          * This only happens with struct returns.
2126          * See https://issues.dlang.org/show_bug.cgi?id=2127 for an example.
2127          *
2128          * On constructor call making __inlineretval is merely redundant, because
2129          * the returned reference is exactly same as vthis, and the 'this' variable
2130          * already exists at the caller side.
2131          */
2132         if (tf.next.ty == Tstruct && !fd.nrvo_var && !fd.isCtorDeclaration() &&
2133             !isConstruction(e))
2134         {
2135             /* Generate a new variable to hold the result and initialize it with the
2136              * inlined body of the function:
2137              *   tret __inlineretval = e;
2138              */
2139             auto ei = new ExpInitializer(callLoc, e);
2140             auto tmp = Identifier.generateId("__inlineretval");
2141             auto vd = new VarDeclaration(callLoc, tf.next, tmp, ei);
2142             vd.storage_class = STC.temp | (tf.isref ? STC.ref_ : STC.rvalue);
2143             vd.linkage = tf.linkage;
2144             vd.parent = parent;
2145 
2146             ei.exp = new ConstructExp(callLoc, vd, e);
2147             ei.exp.type = vd.type;
2148 
2149             auto de = new DeclarationExp(callLoc, vd);
2150             de.type = Type.tvoid;
2151 
2152             // Chain the two together:
2153             //   ( typeof(return) __inlineretval = ( inlined body )) , __inlineretval
2154             e = Expression.combine(de, new VarExp(callLoc, vd));
2155         }
2156 
2157         // https://issues.dlang.org/show_bug.cgi?id=15210
2158         if (tf.next.ty == Tvoid && e && e.type.ty != Tvoid)
2159         {
2160             e = new CastExp(callLoc, e, Type.tvoid);
2161             e.type = Type.tvoid;
2162         }
2163 
2164         eresult = Expression.combine(eresult, eret, ethis, eparams);
2165         eresult = Expression.combine(eresult, e);
2166 
2167         static if (EXPANDINLINE_LOG)
2168             printf("\n[%s] %s expandInline eresult = %s\n",
2169                 callLoc.toChars(), fd.toPrettyChars(), eresult.toChars());
2170     }
2171 
2172     // Need to reevaluate whether parent can now be inlined
2173     // in expressions, as we might have inlined statements
2174     parent.inlineStatusExp = ILS.uninitialized;
2175 }
2176 
2177 /****************************************************
2178  * Determine if the value of `e` is the result of construction.
2179  *
2180  * Params:
2181  *      e = expression to check
2182  * Returns:
2183  *      true for value generated by a constructor or struct literal
2184  */
2185 private bool isConstruction(Expression e)
2186 {
2187     e = lastComma(e);
2188 
2189     if (e.op == TOK.structLiteral)
2190     {
2191         return true;
2192     }
2193     /* Detect:
2194      *    structliteral.ctor(args)
2195      */
2196     else if (e.op == TOK.call)
2197     {
2198         auto ce = cast(CallExp)e;
2199         if (ce.e1.op == TOK.dotVariable)
2200         {
2201             auto dve = cast(DotVarExp)ce.e1;
2202             auto fd = dve.var.isFuncDeclaration();
2203             if (fd && fd.isCtorDeclaration())
2204             {
2205                 if (dve.e1.op == TOK.structLiteral)
2206                 {
2207                     return true;
2208                 }
2209             }
2210         }
2211     }
2212     return false;
2213 }
2214 
2215 
2216 /***********************************************************
2217  * Determine if v is 'head const', meaning
2218  * that once it is initialized it is not changed
2219  * again.
2220  *
2221  * This is done using a primitive flow analysis.
2222  *
2223  * v is head const if v is const or immutable.
2224  * Otherwise, v is assumed to be head const unless one of the
2225  * following is true:
2226  *      1. v is a `ref` or `out` variable
2227  *      2. v is a parameter and fd is a variadic function
2228  *      3. v is assigned to again
2229  *      4. the address of v is taken
2230  *      5. v is referred to by a function nested within fd
2231  *      6. v is ever assigned to a `ref` or `out` variable
2232  *      7. v is ever passed to another function as `ref` or `out`
2233  *
2234  * Params:
2235  *      v       variable to check
2236  *      fd      function that v is local to
2237  * Returns:
2238  *      true if v's initializer is the only value assigned to v
2239  */
2240 private bool onlyOneAssign(VarDeclaration v, FuncDeclaration fd)
2241 {
2242     if (!v.type.isMutable())
2243         return true;            // currently the only case handled atm
2244     return false;
2245 }
2246 
2247 /************************************************************
2248  * See if arguments to a function are creating temporaries that
2249  * will need destruction after the function is executed.
2250  * Params:
2251  *      arguments = arguments to function
2252  * Returns:
2253  *      true if temporaries need destruction
2254  */
2255 
2256 private bool argumentsNeedDtors(Expressions* arguments)
2257 {
2258     if (arguments)
2259     {
2260         foreach (arg; *arguments)
2261         {
2262             if (argNeedsDtor(arg))
2263                 return true;
2264         }
2265     }
2266     return false;
2267 }
2268 
2269 /************************************************************
2270  * See if argument to a function is creating temporaries that
2271  * will need destruction after the function is executed.
2272  * Params:
2273  *      arg = argument to function
2274  * Returns:
2275  *      true if temporaries need destruction
2276  */
2277 
2278 private bool argNeedsDtor(Expression arg)
2279 {
2280     extern (C++) final class NeedsDtor : StoppableVisitor
2281     {
2282         alias visit = typeof(super).visit;
2283         Expression arg;
2284 
2285     public:
2286         extern (D) this(Expression arg)
2287         {
2288             this.arg = arg;
2289         }
2290 
2291         override void visit(Expression)
2292         {
2293         }
2294 
2295         override void visit(DeclarationExp de)
2296         {
2297             if (de != arg)
2298                 Dsymbol_needsDtor(de.declaration);
2299         }
2300 
2301         void Dsymbol_needsDtor(Dsymbol s)
2302         {
2303             /* This mirrors logic of Dsymbol_toElem() in e2ir.d
2304              * perhaps they can be combined.
2305              */
2306 
2307             void symbolDg(Dsymbol s)
2308             {
2309                 Dsymbol_needsDtor(s);
2310             }
2311 
2312             if (auto vd = s.isVarDeclaration())
2313             {
2314                 s = s.toAlias();
2315                 if (s != vd)
2316                     return Dsymbol_needsDtor(s);
2317                 else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.tls | STC.gshared | STC.manifest))
2318                     return;
2319                 if (vd.needsScopeDtor())
2320                 {
2321                     stop = true;
2322                 }
2323             }
2324             else if (auto tm = s.isTemplateMixin())
2325             {
2326                 tm.members.foreachDsymbol(&symbolDg);
2327             }
2328             else if (auto ad = s.isAttribDeclaration())
2329             {
2330                 ad.include(null).foreachDsymbol(&symbolDg);
2331             }
2332             else if (auto td = s.isTupleDeclaration())
2333             {
2334                 foreach (o; *td.objects)
2335                 {
2336                     import dmd.root.rootobject;
2337 
2338                     if (o.dyncast() == DYNCAST.expression)
2339                     {
2340                         Expression eo = cast(Expression)o;
2341                         if (eo.op == TOK.dSymbol)
2342                         {
2343                             DsymbolExp se = cast(DsymbolExp)eo;
2344                             Dsymbol_needsDtor(se.s);
2345                         }
2346                     }
2347                 }
2348             }
2349 
2350 
2351         }
2352     }
2353 
2354     scope NeedsDtor ct = new NeedsDtor(arg);
2355     return walkPostorder(arg, ct);
2356 }