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