1 /**
2  * Does semantic analysis for statements.
3  *
4  * Specification: $(LINK2 https://dlang.org/spec/statement.html, Statements)
5  *
6  * Copyright:   Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/statementsem.d, _statementsem.d)
10  * Documentation:  https://dlang.org/phobos/dmd_statementsem.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statementsem.d
12  */
13 
14 module dmd.statementsem;
15 
16 import core.stdc.stdio;
17 
18 import dmd.aggregate;
19 import dmd.aliasthis;
20 import dmd.arrayop;
21 import dmd.arraytypes;
22 import dmd.astcodegen;
23 import dmd.astenums;
24 import dmd.ast_node;
25 import dmd.attrib;
26 import dmd.blockexit;
27 import dmd.clone;
28 import dmd.cond;
29 import dmd.ctorflow;
30 import dmd.dcast;
31 import dmd.dclass;
32 import dmd.declaration;
33 import dmd.denum;
34 import dmd.dimport;
35 import dmd.dinterpret;
36 import dmd.dmodule;
37 import dmd.dscope;
38 import dmd.dsymbol;
39 import dmd.dsymbolsem;
40 import dmd.dtemplate;
41 import dmd.errors;
42 import dmd.escape;
43 import dmd.expression;
44 import dmd.expressionsem;
45 import dmd.func;
46 import dmd.globals;
47 import dmd.gluelayer;
48 import dmd.id;
49 import dmd.identifier;
50 import dmd.init;
51 import dmd.intrange;
52 import dmd.mtype;
53 import dmd.nogc;
54 import dmd.opover;
55 import dmd.parse;
56 import dmd.printast;
57 import dmd.root.outbuffer;
58 import dmd.root.string;
59 import dmd.semantic2;
60 import dmd.sideeffect;
61 import dmd.statement;
62 import dmd.staticassert;
63 import dmd.target;
64 import dmd.tokens;
65 import dmd.typesem;
66 import dmd.visitor;
67 import dmd.compiler;
68 
version(DMDLIB)69 version (DMDLIB)
70 {
71     version = CallbackAPI;
72 }
73 
74 /*****************************************
75  * CTFE requires FuncDeclaration::labtab for the interpretation.
76  * So fixing the label name inside in/out contracts is necessary
77  * for the uniqueness in labtab.
78  * Params:
79  *      sc = context
80  *      ident = statement label name to be adjusted
81  * Returns:
82  *      adjusted label name
83  */
fixupLabelName(Scope * sc,Identifier ident)84 private Identifier fixupLabelName(Scope* sc, Identifier ident)
85 {
86     uint flags = (sc.flags & SCOPE.contract);
87     const id = ident.toString();
88     if (flags && flags != SCOPE.invariant_ &&
89         !(id.length >= 2 && id[0] == '_' && id[1] == '_'))  // does not start with "__"
90     {
91         OutBuffer buf;
92         buf.writestring(flags == SCOPE.require ? "__in_" : "__out_");
93         buf.writestring(ident.toString());
94 
95         ident = Identifier.idPool(buf[]);
96     }
97     return ident;
98 }
99 
100 /*******************************************
101  * Check to see if statement is the innermost labeled statement.
102  * Params:
103  *      sc = context
104  *      statement = Statement to check
105  * Returns:
106  *      if `true`, then the `LabelStatement`, otherwise `null`
107  */
checkLabeledLoop(Scope * sc,Statement statement)108 private LabelStatement checkLabeledLoop(Scope* sc, Statement statement)
109 {
110     if (sc.slabel && sc.slabel.statement == statement)
111     {
112         return sc.slabel;
113     }
114     return null;
115 }
116 
117 /***********************************************************
118  * Check an assignment is used as a condition.
119  * Intended to be use before the `semantic` call on `e`.
120  * Params:
121  *  e = condition expression which is not yet run semantic analysis.
122  * Returns:
123  *  `e` or ErrorExp.
124  */
checkAssignmentAsCondition(Expression e)125 private Expression checkAssignmentAsCondition(Expression e)
126 {
127     auto ec = lastComma(e);
128     if (ec.op == TOK.assign)
129     {
130         ec.error("assignment cannot be used as a condition, perhaps `==` was meant?");
131         return ErrorExp.get();
132     }
133     return e;
134 }
135 
136 // Performs semantic analysis in Statement AST nodes
statementSemantic(Statement s,Scope * sc)137 extern(C++) Statement statementSemantic(Statement s, Scope* sc)
138 {
139     version (CallbackAPI)
140         Compiler.onStatementSemanticStart(s, sc);
141 
142     scope v = new StatementSemanticVisitor(sc);
143     s.accept(v);
144 
145     version (CallbackAPI)
146         Compiler.onStatementSemanticDone(s, sc);
147 
148     return v.result;
149 }
150 
151 private extern (C++) final class StatementSemanticVisitor : Visitor
152 {
153     alias visit = Visitor.visit;
154 
155     Statement result;
156     Scope* sc;
157 
this(Scope * sc)158     this(Scope* sc)
159     {
160         this.sc = sc;
161     }
162 
setError()163     private void setError()
164     {
165         result = new ErrorStatement();
166     }
167 
visit(Statement s)168     override void visit(Statement s)
169     {
170         result = s;
171     }
172 
visit(ErrorStatement s)173     override void visit(ErrorStatement s)
174     {
175         result = s;
176     }
177 
visit(PeelStatement s)178     override void visit(PeelStatement s)
179     {
180         /* "peel" off this wrapper, and don't run semantic()
181          * on the result.
182          */
183         result = s.s;
184     }
185 
visit(ExpStatement s)186     override void visit(ExpStatement s)
187     {
188         /* https://dlang.org/spec/statement.html#expression-statement
189          */
190 
191         if (!s.exp)
192         {
193             result = s;
194             return;
195         }
196         //printf("ExpStatement::semantic() %s\n", exp.toChars());
197 
198         // Allow CommaExp in ExpStatement because return isn't used
199         CommaExp.allow(s.exp);
200 
201         s.exp = s.exp.expressionSemantic(sc);
202         s.exp = resolveProperties(sc, s.exp);
203         s.exp = s.exp.addDtorHook(sc);
204         if (checkNonAssignmentArrayOp(s.exp))
205             s.exp = ErrorExp.get();
206         if (auto f = isFuncAddress(s.exp))
207         {
208             if (f.checkForwardRef(s.exp.loc))
209                 s.exp = ErrorExp.get();
210         }
211         if (discardValue(s.exp))
212             s.exp = ErrorExp.get();
213 
214         s.exp = s.exp.optimize(WANTvalue);
215         s.exp = checkGC(sc, s.exp);
216         if (s.exp.op == TOK.error)
217             return setError();
218         result = s;
219     }
220 
visit(CompileStatement cs)221     override void visit(CompileStatement cs)
222     {
223         /* https://dlang.org/spec/statement.html#mixin-statement
224          */
225 
226         //printf("CompileStatement::semantic() %s\n", exp.toChars());
227         Statements* a = cs.flatten(sc);
228         if (!a)
229             return;
230         Statement s = new CompoundStatement(cs.loc, a);
231         result = s.statementSemantic(sc);
232     }
233 
visit(CompoundStatement cs)234     override void visit(CompoundStatement cs)
235     {
236         //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc);
237         version (none)
238         {
239             foreach (i, s; cs.statements)
240             {
241                 if (s)
242                     printf("[%d]: %s", i, s.toChars());
243             }
244         }
245 
246         for (size_t i = 0; i < cs.statements.dim;)
247         {
248             Statement s = (*cs.statements)[i];
249             if (!s)
250             {
251                 ++i;
252                 continue;
253             }
254 
255             Statements* flt = s.flatten(sc);
256             if (flt)
257             {
258                 cs.statements.remove(i);
259                 cs.statements.insert(i, flt);
260                 continue;
261             }
262             s = s.statementSemantic(sc);
263             (*cs.statements)[i] = s;
264             if (!s)
265             {
266                 /* Remove NULL statements from the list.
267                  */
268                 cs.statements.remove(i);
269                 continue;
270             }
271             if (s.isErrorStatement())
272             {
273                 result = s;     // propagate error up the AST
274                 ++i;
275                 continue;       // look for errors in rest of statements
276             }
277             Statement sentry;
278             Statement sexception;
279             Statement sfinally;
280 
281             (*cs.statements)[i] = s.scopeCode(sc, sentry, sexception, sfinally);
282             if (sentry)
283             {
284                 sentry = sentry.statementSemantic(sc);
285                 cs.statements.insert(i, sentry);
286                 i++;
287             }
288             if (sexception)
289                 sexception = sexception.statementSemantic(sc);
290             if (sexception)
291             {
292                 /* Returns: true if statements[] are empty statements
293                  */
294                 static bool isEmpty(const Statement[] statements)
295                 {
296                     foreach (s; statements)
297                     {
298                         if (const cs = s.isCompoundStatement())
299                         {
300                             if (!isEmpty((*cs.statements)[]))
301                                 return false;
302                         }
303                         else
304                             return false;
305                     }
306                     return true;
307                 }
308 
309                 if (!sfinally && isEmpty((*cs.statements)[i + 1 .. cs.statements.dim]))
310                 {
311                 }
312                 else
313                 {
314                     /* Rewrite:
315                      *      s; s1; s2;
316                      * As:
317                      *      s;
318                      *      try { s1; s2; }
319                      *      catch (Throwable __o)
320                      *      { sexception; throw __o; }
321                      */
322                     auto a = new Statements();
323                     a.pushSlice((*cs.statements)[i + 1 .. cs.statements.length]);
324                     cs.statements.setDim(i + 1);
325 
326                     Statement _body = new CompoundStatement(Loc.initial, a);
327                     _body = new ScopeStatement(Loc.initial, _body, Loc.initial);
328 
329                     Identifier id = Identifier.generateId("__o");
330 
331                     Statement handler = new PeelStatement(sexception);
332                     if (sexception.blockExit(sc.func, false) & BE.fallthru)
333                     {
334                         auto ts = new ThrowStatement(Loc.initial, new IdentifierExp(Loc.initial, id));
335                         ts.internalThrow = true;
336                         handler = new CompoundStatement(Loc.initial, handler, ts);
337                     }
338 
339                     auto catches = new Catches();
340                     auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
341                     ctch.internalCatch = true;
342                     catches.push(ctch);
343 
344                     Statement st = new TryCatchStatement(Loc.initial, _body, catches);
345                     if (sfinally)
346                         st = new TryFinallyStatement(Loc.initial, st, sfinally);
347                     st = st.statementSemantic(sc);
348 
349                     cs.statements.push(st);
350                     break;
351                 }
352             }
353             else if (sfinally)
354             {
355                 if (0 && i + 1 == cs.statements.dim)
356                 {
357                     cs.statements.push(sfinally);
358                 }
359                 else
360                 {
361                     /* Rewrite:
362                      *      s; s1; s2;
363                      * As:
364                      *      s; try { s1; s2; } finally { sfinally; }
365                      */
366                     auto a = new Statements();
367                     a.pushSlice((*cs.statements)[i + 1 .. cs.statements.length]);
368                     cs.statements.setDim(i + 1);
369 
370                     auto _body = new CompoundStatement(Loc.initial, a);
371                     Statement stf = new TryFinallyStatement(Loc.initial, _body, sfinally);
372                     stf = stf.statementSemantic(sc);
373                     cs.statements.push(stf);
374                     break;
375                 }
376             }
377             i++;
378         }
379 
380         /* Flatten them in place
381          */
382         void flatten(Statements* statements)
383         {
384             for (size_t i = 0; i < statements.length;)
385             {
386                 Statement s = (*statements)[i];
387                 if (s)
388                 {
389                     if (auto flt = s.flatten(sc))
390                     {
391                         statements.remove(i);
392                         statements.insert(i, flt);
393                         continue;
394                     }
395                 }
396                 ++i;
397             }
398         }
399 
400         /* https://issues.dlang.org/show_bug.cgi?id=11653
401          * 'semantic' may return another CompoundStatement
402          * (eg. CaseRangeStatement), so flatten it here.
403          */
404         flatten(cs.statements);
405 
406         foreach (s; *cs.statements)
407         {
408             if (!s)
409                 continue;
410 
411             if (auto se = s.isErrorStatement())
412             {
413                 result = se;
414                 return;
415             }
416         }
417 
418         if (cs.statements.length == 1)
419         {
420             result = (*cs.statements)[0];
421             return;
422         }
423         result = cs;
424     }
425 
visit(UnrolledLoopStatement uls)426     override void visit(UnrolledLoopStatement uls)
427     {
428         //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc);
429         Scope* scd = sc.push();
430         scd.sbreak = uls;
431         scd.scontinue = uls;
432 
433         Statement serror = null;
434         foreach (i, ref s; *uls.statements)
435         {
436             if (s)
437             {
438                 //printf("[%d]: %s\n", i, s.toChars());
439                 s = s.statementSemantic(scd);
440                 if (s && !serror)
441                     serror = s.isErrorStatement();
442             }
443         }
444 
445         scd.pop();
446         result = serror ? serror : uls;
447     }
448 
visit(ScopeStatement ss)449     override void visit(ScopeStatement ss)
450     {
451         //printf("ScopeStatement::semantic(sc = %p)\n", sc);
452         if (!ss.statement)
453         {
454             result = ss;
455             return;
456         }
457 
458         ScopeDsymbol sym = new ScopeDsymbol();
459         sym.parent = sc.scopesym;
460         sym.endlinnum = ss.endloc.linnum;
461         sc = sc.push(sym);
462 
463         Statements* a = ss.statement.flatten(sc);
464         if (a)
465         {
466             ss.statement = new CompoundStatement(ss.loc, a);
467         }
468 
469         ss.statement = ss.statement.statementSemantic(sc);
470         if (ss.statement)
471         {
472             if (ss.statement.isErrorStatement())
473             {
474                 sc.pop();
475                 result = ss.statement;
476                 return;
477             }
478 
479             Statement sentry;
480             Statement sexception;
481             Statement sfinally;
482             ss.statement = ss.statement.scopeCode(sc, sentry, sexception, sfinally);
483             assert(!sentry);
484             assert(!sexception);
485             if (sfinally)
486             {
487                 //printf("adding sfinally\n");
488                 sfinally = sfinally.statementSemantic(sc);
489                 ss.statement = new CompoundStatement(ss.loc, ss.statement, sfinally);
490             }
491         }
492         sc.pop();
493         result = ss;
494     }
495 
visit(ForwardingStatement ss)496     override void visit(ForwardingStatement ss)
497     {
498         assert(ss.sym);
499         for (Scope* csc = sc; !ss.sym.forward; csc = csc.enclosing)
500         {
501             assert(csc);
502             ss.sym.forward = csc.scopesym;
503         }
504         sc = sc.push(ss.sym);
505         sc.sbreak = ss;
506         sc.scontinue = ss;
507         ss.statement = ss.statement.statementSemantic(sc);
508         sc = sc.pop();
509         result = ss.statement;
510     }
511 
visit(WhileStatement ws)512     override void visit(WhileStatement ws)
513     {
514         /* Rewrite as a for(;condition;) loop
515          * https://dlang.org/spec/statement.html#while-statement
516          */
517         Expression cond = ws.condition;
518         Statement _body = ws._body;
519         if (ws.param)
520         {
521             /**
522              * If the while loop is of form `while(auto a = exp) { loop_body }`,
523              * rewrite to:
524              *
525              * while(true)
526              *     if (auto a = exp)
527              *     { loop_body }
528              *     else
529              *     { break; }
530              */
531             _body = new IfStatement(ws.loc, ws.param, ws.condition, ws._body, new BreakStatement(ws.loc, null), ws.endloc);
532             cond = IntegerExp.createBool(true);
533         }
534         Statement s = new ForStatement(ws.loc, null, cond, null, _body, ws.endloc);
535         s = s.statementSemantic(sc);
536         result = s;
537     }
538 
visit(DoStatement ds)539     override void visit(DoStatement ds)
540     {
541         /* https://dlang.org/spec/statement.html#do-statement
542          */
543         const inLoopSave = sc.inLoop;
544         sc.inLoop = true;
545         if (ds._body)
546             ds._body = ds._body.semanticScope(sc, ds, ds, null);
547         sc.inLoop = inLoopSave;
548 
549         if (ds.condition.op == TOK.dotIdentifier)
550             (cast(DotIdExp)ds.condition).noderef = true;
551 
552         // check in syntax level
553         ds.condition = checkAssignmentAsCondition(ds.condition);
554 
555         ds.condition = ds.condition.expressionSemantic(sc);
556         ds.condition = resolveProperties(sc, ds.condition);
557         if (checkNonAssignmentArrayOp(ds.condition))
558             ds.condition = ErrorExp.get();
559         ds.condition = ds.condition.optimize(WANTvalue);
560         ds.condition = checkGC(sc, ds.condition);
561 
562         ds.condition = ds.condition.toBoolean(sc);
563 
564         if (ds.condition.op == TOK.error)
565             return setError();
566         if (ds._body && ds._body.isErrorStatement())
567         {
568             result = ds._body;
569             return;
570         }
571 
572         result = ds;
573     }
574 
visit(ForStatement fs)575     override void visit(ForStatement fs)
576     {
577         /* https://dlang.org/spec/statement.html#for-statement
578          */
579         //printf("ForStatement::semantic %s\n", fs.toChars());
580 
581         if (fs._init)
582         {
583             /* Rewrite:
584              *  for (auto v1 = i1, v2 = i2; condition; increment) { ... }
585              * to:
586              *  { auto v1 = i1, v2 = i2; for (; condition; increment) { ... } }
587              * then lowered to:
588              *  auto v1 = i1;
589              *  try {
590              *    auto v2 = i2;
591              *    try {
592              *      for (; condition; increment) { ... }
593              *    } finally { v2.~this(); }
594              *  } finally { v1.~this(); }
595              */
596             auto ainit = new Statements();
597             ainit.push(fs._init);
598             fs._init = null;
599             ainit.push(fs);
600             Statement s = new CompoundStatement(fs.loc, ainit);
601             s = new ScopeStatement(fs.loc, s, fs.endloc);
602             s = s.statementSemantic(sc);
603             if (!s.isErrorStatement())
604             {
605                 if (LabelStatement ls = checkLabeledLoop(sc, fs))
606                     ls.gotoTarget = fs;
607                 fs.relatedLabeled = s;
608             }
609             result = s;
610             return;
611         }
612         assert(fs._init is null);
613 
614         auto sym = new ScopeDsymbol();
615         sym.parent = sc.scopesym;
616         sym.endlinnum = fs.endloc.linnum;
617         sc = sc.push(sym);
618         sc.inLoop = true;
619 
620         if (fs.condition)
621         {
622             if (fs.condition.op == TOK.dotIdentifier)
623                 (cast(DotIdExp)fs.condition).noderef = true;
624 
625             // check in syntax level
626             fs.condition = checkAssignmentAsCondition(fs.condition);
627 
628             fs.condition = fs.condition.expressionSemantic(sc);
629             fs.condition = resolveProperties(sc, fs.condition);
630             if (checkNonAssignmentArrayOp(fs.condition))
631                 fs.condition = ErrorExp.get();
632             fs.condition = fs.condition.optimize(WANTvalue);
633             fs.condition = checkGC(sc, fs.condition);
634 
635             fs.condition = fs.condition.toBoolean(sc);
636         }
637         if (fs.increment)
638         {
639             CommaExp.allow(fs.increment);
640             fs.increment = fs.increment.expressionSemantic(sc);
641             fs.increment = resolveProperties(sc, fs.increment);
642             if (checkNonAssignmentArrayOp(fs.increment))
643                 fs.increment = ErrorExp.get();
644             fs.increment = fs.increment.optimize(WANTvalue);
645             fs.increment = checkGC(sc, fs.increment);
646         }
647 
648         sc.sbreak = fs;
649         sc.scontinue = fs;
650         if (fs._body)
651             fs._body = fs._body.semanticNoScope(sc);
652 
653         sc.pop();
654 
655         if (fs.condition && fs.condition.op == TOK.error ||
656             fs.increment && fs.increment.op == TOK.error ||
657             fs._body && fs._body.isErrorStatement())
658             return setError();
659         result = fs;
660     }
661 
662     /*******************
663      * Determines the return type of makeTupleForeach.
664      */
MakeTupleForeachRet(bool isDecl)665     private static template MakeTupleForeachRet(bool isDecl)
666     {
667         static if(isDecl)
668         {
669             alias MakeTupleForeachRet = Dsymbols*;
670         }
671         else
672         {
673             alias MakeTupleForeachRet = void;
674         }
675     }
676 
677     /*******************
678      * Type check and unroll `foreach` over an expression tuple as well
679      * as `static foreach` statements and `static foreach`
680      * declarations. For `static foreach` statements and `static
681      * foreach` declarations, the visitor interface is used (and the
682      * result is written into the `result` field.) For `static
683      * foreach` declarations, the resulting Dsymbols* are returned
684      * directly.
685      *
686      * The unrolled body is wrapped into a
687      *  - UnrolledLoopStatement, for `foreach` over an expression tuple.
688      *  - ForwardingStatement, for `static foreach` statements.
689      *  - ForwardingAttribDeclaration, for `static foreach` declarations.
690      *
691      * `static foreach` variables are declared as `STC.local`, such
692      * that they are inserted into the local symbol tables of the
693      * forwarding constructs instead of forwarded. For `static
694      * foreach` with multiple foreach loop variables whose aggregate
695      * has been lowered into a sequence of tuples, this function
696      * expands the tuples into multiple `STC.local` `static foreach`
697      * variables.
698      */
699     MakeTupleForeachRet!isDecl makeTupleForeach(bool isStatic, bool isDecl)(ForeachStatement fs, TupleForeachArgs!(isStatic, isDecl) args)
700     {
returnEarly()701         auto returnEarly()
702         {
703             static if (isDecl)
704             {
705                 return null;
706             }
707             else
708             {
709                 result = new ErrorStatement();
710                 return;
711             }
712         }
713         static if(isDecl)
714         {
715             static assert(isStatic);
716             auto dbody = args[0];
717         }
718         static if(isStatic)
719         {
720             auto needExpansion = args[$-1];
721             assert(sc);
722         }
723 
724         auto loc = fs.loc;
725         size_t dim = fs.parameters.dim;
726         static if(isStatic) bool skipCheck = needExpansion;
727         else enum skipCheck = false;
728         if (!skipCheck && (dim < 1 || dim > 2))
729         {
730             fs.error("only one (value) or two (key,value) arguments for tuple `foreach`");
731             setError();
732             return returnEarly();
733         }
734 
735         Type paramtype = (*fs.parameters)[dim - 1].type;
736         if (paramtype)
737         {
738             paramtype = paramtype.typeSemantic(loc, sc);
739             if (paramtype.ty == Terror)
740             {
741                 setError();
742                 return returnEarly();
743             }
744         }
745 
746         Type tab = fs.aggr.type.toBasetype();
747         TypeTuple tuple = cast(TypeTuple)tab;
748         static if(!isDecl)
749         {
750             auto statements = new Statements();
751         }
752         else
753         {
754             auto declarations = new Dsymbols();
755         }
756         //printf("aggr: op = %d, %s\n", fs.aggr.op, fs.aggr.toChars());
757         size_t n;
758         TupleExp te = null;
759         if (fs.aggr.op == TOK.tuple) // expression tuple
760         {
761             te = cast(TupleExp)fs.aggr;
762             n = te.exps.dim;
763         }
764         else if (fs.aggr.op == TOK.type) // type tuple
765         {
766             n = Parameter.dim(tuple.arguments);
767         }
768         else
769             assert(0);
770         foreach (j; 0 .. n)
771         {
772             size_t k = (fs.op == TOK.foreach_) ? j : n - 1 - j;
773             Expression e = null;
774             Type t = null;
775             if (te)
776                 e = (*te.exps)[k];
777             else
778                 t = Parameter.getNth(tuple.arguments, k).type;
779             Parameter p = (*fs.parameters)[0];
780             static if(!isDecl)
781             {
782                 auto st = new Statements();
783             }
784             else
785             {
786                 auto st = new Dsymbols();
787             }
788 
789             static if(isStatic) bool skip = needExpansion;
790             else enum skip = false;
791             if (!skip && dim == 2)
792             {
793                 // Declare key
794                 if (p.storageClass & (STC.out_ | STC.ref_ | STC.lazy_))
795                 {
796                     fs.error("no storage class for key `%s`", p.ident.toChars());
797                     setError();
798                     return returnEarly();
799                 }
800                 static if(isStatic)
801                 {
802                     if(!p.type)
803                     {
804                         p.type = Type.tsize_t;
805                     }
806                 }
807                 p.type = p.type.typeSemantic(loc, sc);
808 
809                 if (!p.type.isintegral())
810                 {
811                     fs.error("foreach: key cannot be of non-integral type `%s`",
812                              p.type.toChars());
813                     setError();
814                     return returnEarly();
815                 }
816 
817                 const length = te ? te.exps.length : tuple.arguments.length;
818                 IntRange dimrange = IntRange(SignExtendedNumber(length))._cast(Type.tsize_t);
819                 // https://issues.dlang.org/show_bug.cgi?id=12504
820                 dimrange.imax = SignExtendedNumber(dimrange.imax.value-1);
821                 if (!IntRange.fromType(p.type).contains(dimrange))
822                 {
823                     fs.error("index type `%s` cannot cover index range 0..%llu",
824                              p.type.toChars(), cast(ulong)length);
825                     setError();
826                     return returnEarly();
827                 }
828                 Initializer ie = new ExpInitializer(Loc.initial, new IntegerExp(k));
829                 auto var = new VarDeclaration(loc, p.type, p.ident, ie);
830                 var.storage_class |= STC.manifest;
831                 static if(isStatic) var.storage_class |= STC.local;
832                 static if(!isDecl)
833                 {
834                     st.push(new ExpStatement(loc, var));
835                 }
836                 else
837                 {
838                     st.push(var);
839                 }
840                 p = (*fs.parameters)[1]; // value
841             }
842             /***********************
843              * Declares a unrolled `foreach` loop variable or a `static foreach` variable.
844              *
845              * Params:
846              *     storageClass = The storage class of the variable.
847              *     type = The declared type of the variable.
848              *     ident = The name of the variable.
849              *     e = The initializer of the variable (i.e. the current element of the looped over aggregate).
850              *     t = The type of the initializer.
851              * Returns:
852              *     `true` iff the declaration was successful.
853              */
declareVariable(StorageClass storageClass,Type type,Identifier ident,Expression e,Type t)854             bool declareVariable(StorageClass storageClass, Type type, Identifier ident, Expression e, Type t)
855             {
856                 if (storageClass & (STC.out_ | STC.lazy_) ||
857                     storageClass & STC.ref_ && !te)
858                 {
859                     fs.error("no storage class for value `%s`", ident.toChars());
860                     setError();
861                     return false;
862                 }
863                 Declaration var;
864                 if (e)
865                 {
866                     Type tb = e.type.toBasetype();
867                     Dsymbol ds = null;
868                     if (!(storageClass & STC.manifest))
869                     {
870                         if ((isStatic || tb.ty == Tfunction || storageClass&STC.alias_) && e.op == TOK.variable)
871                             ds = (cast(VarExp)e).var;
872                         else if (e.op == TOK.template_)
873                             ds = (cast(TemplateExp)e).td;
874                         else if (e.op == TOK.scope_)
875                             ds = (cast(ScopeExp)e).sds;
876                         else if (e.op == TOK.function_)
877                         {
878                             auto fe = cast(FuncExp)e;
879                             ds = fe.td ? cast(Dsymbol)fe.td : fe.fd;
880                         }
881                         else if (e.op == TOK.overloadSet)
882                             ds = (cast(OverExp)e).vars;
883                     }
884                     else if (storageClass & STC.alias_)
885                     {
886                         fs.error("`foreach` loop variable cannot be both `enum` and `alias`");
887                         setError();
888                         return false;
889                     }
890 
891                     if (ds)
892                     {
893                         var = new AliasDeclaration(loc, ident, ds);
894                         if (storageClass & STC.ref_)
895                         {
896                             fs.error("symbol `%s` cannot be `ref`", ds.toChars());
897                             setError();
898                             return false;
899                         }
900                         if (paramtype)
901                         {
902                             fs.error("cannot specify element type for symbol `%s`", ds.toChars());
903                             setError();
904                             return false;
905                         }
906                     }
907                     else if (e.op == TOK.type)
908                     {
909                         var = new AliasDeclaration(loc, ident, e.type);
910                         if (paramtype)
911                         {
912                             fs.error("cannot specify element type for type `%s`", e.type.toChars());
913                             setError();
914                             return false;
915                         }
916                     }
917                     else
918                     {
919                         e = resolveProperties(sc, e);
920                         Initializer ie = new ExpInitializer(Loc.initial, e);
921                         auto v = new VarDeclaration(loc, type, ident, ie, storageClass);
922                         if (storageClass & STC.ref_)
923                             v.storage_class |= STC.ref_ | STC.foreach_;
924                         if (isStatic || storageClass&STC.manifest || e.isConst() ||
925                             e.op == TOK.string_ ||
926                             e.op == TOK.structLiteral ||
927                             e.op == TOK.arrayLiteral)
928                         {
929                             if (v.storage_class & STC.ref_)
930                             {
931                                 static if (!isStatic)
932                                 {
933                                     fs.error("constant value `%s` cannot be `ref`", ie.toChars());
934                                 }
935                                 else
936                                 {
937                                     if (!needExpansion)
938                                     {
939                                         fs.error("constant value `%s` cannot be `ref`", ie.toChars());
940                                     }
941                                     else
942                                     {
943                                         fs.error("constant value `%s` cannot be `ref`", ident.toChars());
944                                     }
945                                 }
946                                 setError();
947                                 return false;
948                             }
949                             else
950                                 v.storage_class |= STC.manifest;
951                         }
952                         var = v;
953                     }
954                 }
955                 else
956                 {
957                     var = new AliasDeclaration(loc, ident, t);
958                     if (paramtype)
959                     {
960                         fs.error("cannot specify element type for symbol `%s`", fs.toChars());
961                         setError();
962                         return false;
963                     }
964                 }
965                 static if (isStatic)
966                 {
967                     var.storage_class |= STC.local;
968                 }
969                 static if (!isDecl)
970                 {
971                     st.push(new ExpStatement(loc, var));
972                 }
973                 else
974                 {
975                     st.push(var);
976                 }
977                 return true;
978             }
979             static if (!isStatic)
980             {
981                 // Declare value
982                 if (!declareVariable(p.storageClass, p.type, p.ident, e, t))
983                 {
984                     return returnEarly();
985                 }
986             }
987             else
988             {
989                 if (!needExpansion)
990                 {
991                     // Declare value
992                     if (!declareVariable(p.storageClass, p.type, p.ident, e, t))
993                     {
994                         return returnEarly();
995                     }
996                 }
997                 else
998                 { // expand tuples into multiple `static foreach` variables.
999                     assert(e && !t);
1000                     auto ident = Identifier.generateId("__value");
1001                     declareVariable(0, e.type, ident, e, null);
1002                     import dmd.cond: StaticForeach;
1003                     auto field = Identifier.idPool(StaticForeach.tupleFieldName.ptr,StaticForeach.tupleFieldName.length);
1004                     Expression access = new DotIdExp(loc, e, field);
1005                     access = expressionSemantic(access, sc);
1006                     if (!tuple) return returnEarly();
1007                     //printf("%s\n",tuple.toChars());
1008                     foreach (l; 0 .. dim)
1009                     {
1010                         auto cp = (*fs.parameters)[l];
1011                         Expression init_ = new IndexExp(loc, access, new IntegerExp(loc, l, Type.tsize_t));
1012                         init_ = init_.expressionSemantic(sc);
1013                         assert(init_.type);
1014                         declareVariable(p.storageClass, init_.type, cp.ident, init_, null);
1015                     }
1016                 }
1017             }
1018 
1019             static if (!isDecl)
1020             {
1021                 if (fs._body) // https://issues.dlang.org/show_bug.cgi?id=17646
1022                     st.push(fs._body.syntaxCopy());
1023                 Statement res = new CompoundStatement(loc, st);
1024             }
1025             else
1026             {
1027                 st.append(Dsymbol.arraySyntaxCopy(dbody));
1028             }
1029             static if (!isStatic)
1030             {
1031                 res = new ScopeStatement(loc, res, fs.endloc);
1032             }
1033             else static if (!isDecl)
1034             {
1035                 auto fwd = new ForwardingStatement(loc, res);
1036                 res = fwd;
1037             }
1038             else
1039             {
1040                 import dmd.attrib: ForwardingAttribDeclaration;
1041                 auto res = new ForwardingAttribDeclaration(st);
1042             }
1043             static if (!isDecl)
1044             {
1045                 statements.push(res);
1046             }
1047             else
1048             {
1049                 declarations.push(res);
1050             }
1051         }
1052 
1053         static if (!isStatic)
1054         {
1055             Statement res = new UnrolledLoopStatement(loc, statements);
1056             if (LabelStatement ls = checkLabeledLoop(sc, fs))
1057                 ls.gotoTarget = res;
1058             if (te && te.e0)
1059                 res = new CompoundStatement(loc, new ExpStatement(te.e0.loc, te.e0), res);
1060         }
1061         else static if (!isDecl)
1062         {
1063             Statement res = new CompoundStatement(loc, statements);
1064         }
1065         else
1066         {
1067             auto res = declarations;
1068         }
1069         static if (!isDecl)
1070         {
1071             result = res;
1072         }
1073         else
1074         {
1075             return res;
1076         }
1077     }
1078 
visit(ForeachStatement fs)1079     override void visit(ForeachStatement fs)
1080     {
1081         /* https://dlang.org/spec/statement.html#foreach-statement
1082          */
1083 
1084         //printf("ForeachStatement::semantic() %p\n", fs);
1085 
1086         /******
1087          * Issue error if any of the ForeachTypes were not supplied and could not be inferred.
1088          * Returns:
1089          *      true if error issued
1090          */
1091         static bool checkForArgTypes(ForeachStatement fs)
1092         {
1093             bool result = false;
1094             foreach (p; *fs.parameters)
1095             {
1096                 if (!p.type)
1097                 {
1098                     fs.error("cannot infer type for `foreach` variable `%s`, perhaps set it explicitly", p.ident.toChars());
1099                     p.type = Type.terror;
1100                     result = true;
1101                 }
1102             }
1103             return result;
1104         }
1105 
1106         const loc = fs.loc;
1107         const dim = fs.parameters.dim;
1108 
1109         fs.func = sc.func;
1110         if (fs.func.fes)
1111             fs.func = fs.func.fes.func;
1112 
1113         VarDeclaration vinit = null;
1114         fs.aggr = fs.aggr.expressionSemantic(sc);
1115         fs.aggr = resolveProperties(sc, fs.aggr);
1116         fs.aggr = fs.aggr.optimize(WANTvalue);
1117         if (fs.aggr.op == TOK.error)
1118             return setError();
1119         Expression oaggr = fs.aggr;     // remember original for error messages
1120         if (fs.aggr.type && fs.aggr.type.toBasetype().ty == Tstruct &&
1121             (cast(TypeStruct)(fs.aggr.type.toBasetype())).sym.dtor &&
1122             fs.aggr.op != TOK.type && !fs.aggr.isLvalue())
1123         {
1124             // https://issues.dlang.org/show_bug.cgi?id=14653
1125             // Extend the life of rvalue aggregate till the end of foreach.
1126             vinit = copyToTemp(STC.rvalue, "__aggr", fs.aggr);
1127             vinit.endlinnum = fs.endloc.linnum;
1128             vinit.dsymbolSemantic(sc);
1129             fs.aggr = new VarExp(fs.aggr.loc, vinit);
1130         }
1131 
1132         /* If aggregate is a vector type, add the .array to make it a static array
1133          */
1134         if (fs.aggr.type)
1135             if (auto tv = fs.aggr.type.toBasetype().isTypeVector())
1136             {
1137                 auto vae = new VectorArrayExp(fs.aggr.loc, fs.aggr);
1138                 vae.type = tv.basetype;
1139                 fs.aggr = vae;
1140             }
1141 
1142         Dsymbol sapply = null;                  // the inferred opApply() or front() function
1143         if (!inferForeachAggregate(sc, fs.op == TOK.foreach_, fs.aggr, sapply))
1144         {
1145             const(char)* msg = "";
1146             if (fs.aggr.type && isAggregate(fs.aggr.type))
1147             {
1148                 msg = ", define `opApply()`, range primitives, or use `.tupleof`";
1149             }
1150             fs.error("invalid `foreach` aggregate `%s`%s", oaggr.toChars(), msg);
1151             return setError();
1152         }
1153 
1154         Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
1155 
1156         /* Check for inference errors
1157          */
1158         if (!inferApplyArgTypes(fs, sc, sapply))
1159         {
1160             /**
1161              Try and extract the parameter count of the opApply callback function, e.g.:
1162              int opApply(int delegate(int, float)) => 2 args
1163              */
1164             bool foundMismatch = false;
1165             size_t foreachParamCount = 0;
1166             if (sapplyOld)
1167             {
1168                 if (FuncDeclaration fd = sapplyOld.isFuncDeclaration())
1169                 {
1170                     auto fparameters = fd.getParameterList();
1171 
1172                     if (fparameters.length == 1)
1173                     {
1174                         // first param should be the callback function
1175                         Parameter fparam = fparameters[0];
1176                         if ((fparam.type.ty == Tpointer ||
1177                              fparam.type.ty == Tdelegate) &&
1178                             fparam.type.nextOf().ty == Tfunction)
1179                         {
1180                             TypeFunction tf = cast(TypeFunction)fparam.type.nextOf();
1181                             foreachParamCount = tf.parameterList.length;
1182                             foundMismatch = true;
1183                         }
1184                     }
1185                 }
1186             }
1187 
1188             //printf("dim = %d, parameters.dim = %d\n", dim, parameters.dim);
1189             if (foundMismatch && dim != foreachParamCount)
1190             {
1191                 const(char)* plural = foreachParamCount > 1 ? "s" : "";
1192                 fs.error("cannot infer argument types, expected %llu argument%s, not %llu",
1193                     cast(ulong) foreachParamCount, plural, cast(ulong) dim);
1194             }
1195             else
1196                 fs.error("cannot uniquely infer `foreach` argument types");
1197 
1198             return setError();
1199         }
1200 
1201         Type tab = fs.aggr.type.toBasetype();
1202 
1203         if (tab.ty == Ttuple) // don't generate new scope for tuple loops
1204         {
1205             makeTupleForeach!(false,false)(fs);
1206             if (vinit)
1207                 result = new CompoundStatement(loc, new ExpStatement(loc, vinit), result);
1208             result = result.statementSemantic(sc);
1209             return;
1210         }
1211 
1212         auto sym = new ScopeDsymbol();
1213         sym.parent = sc.scopesym;
1214         sym.endlinnum = fs.endloc.linnum;
1215         auto sc2 = sc.push(sym);
1216         sc2.inLoop = true;
1217 
1218         foreach (Parameter p; *fs.parameters)
1219         {
1220             if (p.storageClass & STC.manifest)
1221             {
1222                 fs.error("cannot declare `enum` loop variables for non-unrolled foreach");
1223             }
1224             if (p.storageClass & STC.alias_)
1225             {
1226                 fs.error("cannot declare `alias` loop variables for non-unrolled foreach");
1227             }
1228         }
1229 
1230         void retError()
1231         {
1232             sc2.pop();
1233             result = new ErrorStatement();
1234         }
1235 
1236         void rangeError()
1237         {
1238             fs.error("cannot infer argument types");
1239             return retError();
1240         }
1241 
1242         void retStmt(Statement s)
1243         {
1244             if (!s)
1245                 return retError();
1246             s = s.statementSemantic(sc2);
1247             sc2.pop();
1248             result = s;
1249         }
1250 
1251         TypeAArray taa = null;
1252         Type tn = null;
1253         Type tnv = null;
1254         Statement apply()
1255         {
1256             if (checkForArgTypes(fs))
1257                 return null;
1258 
1259             TypeFunction tfld = null;
1260             if (sapply)
1261             {
1262                 FuncDeclaration fdapply = sapply.isFuncDeclaration();
1263                 if (fdapply)
1264                 {
1265                     assert(fdapply.type && fdapply.type.ty == Tfunction);
1266                     tfld = cast(TypeFunction)fdapply.type.typeSemantic(loc, sc2);
1267                     goto Lget;
1268                 }
1269                 else if (tab.ty == Tdelegate)
1270                 {
1271                     tfld = cast(TypeFunction)tab.nextOf();
1272                 Lget:
1273                     //printf("tfld = %s\n", tfld.toChars());
1274                     if (tfld.parameterList.parameters.dim == 1)
1275                     {
1276                         Parameter p = tfld.parameterList[0];
1277                         if (p.type && p.type.ty == Tdelegate)
1278                         {
1279                             auto t = p.type.typeSemantic(loc, sc2);
1280                             assert(t.ty == Tdelegate);
1281                             tfld = cast(TypeFunction)t.nextOf();
1282                         }
1283                         //printf("tfld = %s\n", tfld.toChars());
1284                     }
1285                 }
1286             }
1287 
1288             FuncExp flde = foreachBodyToFunction(sc2, fs, tfld);
1289             if (!flde)
1290                 return null;
1291 
1292             // Resolve any forward referenced goto's
1293             foreach (ScopeStatement ss; *fs.gotos)
1294             {
1295                 GotoStatement gs = ss.statement.isGotoStatement();
1296                 if (!gs.label.statement)
1297                 {
1298                     // 'Promote' it to this scope, and replace with a return
1299                     fs.cases.push(gs);
1300                     ss.statement = new ReturnStatement(Loc.initial, new IntegerExp(fs.cases.dim + 1));
1301                 }
1302             }
1303 
1304             Expression e = null;
1305             Expression ec;
1306             if (vinit)
1307             {
1308                 e = new DeclarationExp(loc, vinit);
1309                 e = e.expressionSemantic(sc2);
1310                 if (e.op == TOK.error)
1311                     return null;
1312             }
1313 
1314             if (taa)
1315                 ec = applyAssocArray(fs, flde, taa);
1316             else if (tab.ty == Tarray || tab.ty == Tsarray)
1317                 ec = applyArray(fs, flde, sc2, tn, tnv, tab.ty);
1318             else if (tab.ty == Tdelegate)
1319                 ec = applyDelegate(fs, flde, sc2, tab);
1320             else
1321                 ec = applyOpApply(fs, tab, sapply, sc2, flde);
1322             if (!ec)
1323                 return null;
1324             e = Expression.combine(e, ec);
1325             return loopReturn(e, fs.cases, loc);
1326         }
1327         switch (tab.ty)
1328         {
1329         case Tarray:
1330         case Tsarray:
1331             {
1332                 if (checkForArgTypes(fs))
1333                     return retError();
1334 
1335                 if (dim < 1 || dim > 2)
1336                 {
1337                     fs.error("only one or two arguments for array `foreach`");
1338                     return retError();
1339                 }
1340 
1341                 // Finish semantic on all foreach parameter types.
1342                 foreach (i; 0 .. dim)
1343                 {
1344                     Parameter p = (*fs.parameters)[i];
1345                     p.type = p.type.typeSemantic(loc, sc2);
1346                     p.type = p.type.addStorageClass(p.storageClass);
1347                 }
1348 
1349                 tn = tab.nextOf().toBasetype();
1350 
1351                 if (dim == 2)
1352                 {
1353                     Type tindex = (*fs.parameters)[0].type;
1354                     if (!tindex.isintegral())
1355                     {
1356                         fs.error("foreach: key cannot be of non-integral type `%s`", tindex.toChars());
1357                         return retError();
1358                     }
1359                     /* What cases to deprecate implicit conversions for:
1360                      *  1. foreach aggregate is a dynamic array
1361                      *  2. foreach body is lowered to _aApply (see special case below).
1362                      */
1363                     Type tv = (*fs.parameters)[1].type.toBasetype();
1364                     if ((tab.ty == Tarray ||
1365                          (tn.ty != tv.ty && tn.ty.isSomeChar && tv.ty.isSomeChar)) &&
1366                         !Type.tsize_t.implicitConvTo(tindex))
1367                     {
1368                         fs.deprecation("foreach: loop index implicitly converted from `size_t` to `%s`",
1369                                        tindex.toChars());
1370                     }
1371                 }
1372 
1373                 /* Look for special case of parsing char types out of char type
1374                  * array.
1375                  */
1376                 if (tn.ty.isSomeChar)
1377                 {
1378                     int i = (dim == 1) ? 0 : 1; // index of value
1379                     Parameter p = (*fs.parameters)[i];
1380                     tnv = p.type.toBasetype();
1381                     if (tnv.ty != tn.ty && tnv.ty.isSomeChar)
1382                     {
1383                         if (p.storageClass & STC.ref_)
1384                         {
1385                             fs.error("`foreach`: value of UTF conversion cannot be `ref`");
1386                             return retError();
1387                         }
1388                         if (dim == 2)
1389                         {
1390                             p = (*fs.parameters)[0];
1391                             if (p.storageClass & STC.ref_)
1392                             {
1393                                 fs.error("`foreach`: key cannot be `ref`");
1394                                 return retError();
1395                             }
1396                         }
1397                         return retStmt(apply());
1398                     }
1399                 }
1400 
1401                 // Declare the key
1402                 if (dim == 2)
1403                 {
1404                     Parameter p = (*fs.parameters)[0];
1405                     auto var = new VarDeclaration(loc, p.type.mutableOf(), Identifier.generateId("__key"), null);
1406                     var.storage_class |= STC.temp | STC.foreach_;
1407                     if (var.storage_class & (STC.ref_ | STC.out_))
1408                         var.storage_class |= STC.nodtor;
1409 
1410                     fs.key = var;
1411                     if (p.storageClass & STC.ref_)
1412                     {
1413                         if (var.type.constConv(p.type) == MATCH.nomatch)
1414                         {
1415                             fs.error("key type mismatch, `%s` to `ref %s`",
1416                                      var.type.toChars(), p.type.toChars());
1417                             return retError();
1418                         }
1419                     }
1420                     if (tab.ty == Tsarray)
1421                     {
1422                         TypeSArray ta = cast(TypeSArray)tab;
1423                         IntRange dimrange = getIntRange(ta.dim);
1424                         // https://issues.dlang.org/show_bug.cgi?id=12504
1425                         dimrange.imax = SignExtendedNumber(dimrange.imax.value-1);
1426                         if (!IntRange.fromType(var.type).contains(dimrange))
1427                         {
1428                             fs.error("index type `%s` cannot cover index range 0..%llu",
1429                                      p.type.toChars(), ta.dim.toInteger());
1430                             return retError();
1431                         }
1432                         fs.key.range = new IntRange(SignExtendedNumber(0), dimrange.imax);
1433                     }
1434                 }
1435                 // Now declare the value
1436                 {
1437                     Parameter p = (*fs.parameters)[dim - 1];
1438                     auto var = new VarDeclaration(loc, p.type, p.ident, null);
1439                     var.storage_class |= STC.foreach_;
1440                     var.storage_class |= p.storageClass & (STC.scope_ | STC.IOR | STC.TYPECTOR);
1441                     if (var.isReference())
1442                         var.storage_class |= STC.nodtor;
1443 
1444                     fs.value = var;
1445                     if (var.storage_class & STC.ref_)
1446                     {
1447                         if (fs.aggr.checkModifiable(sc2, ModifyFlags.noError) == Modifiable.initialization)
1448                             var.storage_class |= STC.ctorinit;
1449 
1450                         Type t = tab.nextOf();
1451                         if (t.constConv(p.type) == MATCH.nomatch)
1452                         {
1453                             fs.error("argument type mismatch, `%s` to `ref %s`",
1454                                      t.toChars(), p.type.toChars());
1455                             return retError();
1456                         }
1457                     }
1458                 }
1459 
1460                 /* Convert to a ForStatement
1461                  *   foreach (key, value; a) body =>
1462                  *   for (T[] tmp = a[], size_t key; key < tmp.length; ++key)
1463                  *   { T value = tmp[k]; body }
1464                  *
1465                  *   foreach_reverse (key, value; a) body =>
1466                  *   for (T[] tmp = a[], size_t key = tmp.length; key--; )
1467                  *   { T value = tmp[k]; body }
1468                  */
1469                 auto id = Identifier.generateId("__r");
1470                 auto ie = new ExpInitializer(loc, new SliceExp(loc, fs.aggr, null, null));
1471                 const valueIsRef = cast(bool) ((*fs.parameters)[dim - 1].storageClass & STC.ref_);
1472                 VarDeclaration tmp;
1473                 if (fs.aggr.op == TOK.arrayLiteral && !valueIsRef)
1474                 {
1475                     auto ale = cast(ArrayLiteralExp)fs.aggr;
1476                     size_t edim = ale.elements ? ale.elements.dim : 0;
1477                     auto telem = (*fs.parameters)[dim - 1].type;
1478 
1479                     // https://issues.dlang.org/show_bug.cgi?id=12936
1480                     // if telem has been specified explicitly,
1481                     // converting array literal elements to telem might make it @nogc.
1482                     fs.aggr = fs.aggr.implicitCastTo(sc, telem.sarrayOf(edim));
1483                     if (fs.aggr.op == TOK.error)
1484                         return retError();
1485 
1486                     // for (T[edim] tmp = a, ...)
1487                     tmp = new VarDeclaration(loc, fs.aggr.type, id, ie);
1488                 }
1489                 else
1490                 {
1491                     tmp = new VarDeclaration(loc, tab.nextOf().arrayOf(), id, ie);
1492                     if (!valueIsRef)
1493                         tmp.storage_class |= STC.scope_;
1494                 }
1495                 tmp.storage_class |= STC.temp;
1496 
1497                 Expression tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id.length);
1498 
1499                 if (!fs.key)
1500                 {
1501                     Identifier idkey = Identifier.generateId("__key");
1502                     fs.key = new VarDeclaration(loc, Type.tsize_t, idkey, null);
1503                     fs.key.storage_class |= STC.temp;
1504                 }
1505                 else if (fs.key.type.ty != Type.tsize_t.ty)
1506                 {
1507                     tmp_length = new CastExp(loc, tmp_length, fs.key.type);
1508                 }
1509                 if (fs.op == TOK.foreach_reverse_)
1510                     fs.key._init = new ExpInitializer(loc, tmp_length);
1511                 else
1512                     fs.key._init = new ExpInitializer(loc, new IntegerExp(loc, 0, fs.key.type));
1513 
1514                 auto cs = new Statements();
1515                 if (vinit)
1516                     cs.push(new ExpStatement(loc, vinit));
1517                 cs.push(new ExpStatement(loc, tmp));
1518                 cs.push(new ExpStatement(loc, fs.key));
1519                 Statement forinit = new CompoundDeclarationStatement(loc, cs);
1520 
1521                 Expression cond;
1522                 if (fs.op == TOK.foreach_reverse_)
1523                 {
1524                     // key--
1525                     cond = new PostExp(TOK.minusMinus, loc, new VarExp(loc, fs.key));
1526                 }
1527                 else
1528                 {
1529                     // key < tmp.length
1530                     cond = new CmpExp(TOK.lessThan, loc, new VarExp(loc, fs.key), tmp_length);
1531                 }
1532 
1533                 Expression increment = null;
1534                 if (fs.op == TOK.foreach_)
1535                 {
1536                     // key += 1
1537                     increment = new AddAssignExp(loc, new VarExp(loc, fs.key), new IntegerExp(loc, 1, fs.key.type));
1538                 }
1539 
1540                 // T value = tmp[key];
1541                 IndexExp indexExp = new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, fs.key));
1542                 indexExp.indexIsInBounds = true; // disabling bounds checking in foreach statements.
1543                 fs.value._init = new ExpInitializer(loc, indexExp);
1544                 Statement ds = new ExpStatement(loc, fs.value);
1545 
1546                 if (dim == 2)
1547                 {
1548                     Parameter p = (*fs.parameters)[0];
1549                     if ((p.storageClass & STC.ref_) && p.type.equals(fs.key.type))
1550                     {
1551                         fs.key.range = null;
1552                         auto v = new AliasDeclaration(loc, p.ident, fs.key);
1553                         fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1554                     }
1555                     else
1556                     {
1557                         auto ei = new ExpInitializer(loc, new IdentifierExp(loc, fs.key.ident));
1558                         auto v = new VarDeclaration(loc, p.type, p.ident, ei);
1559                         v.storage_class |= STC.foreach_ | (p.storageClass & STC.ref_);
1560                         fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1561                         if (fs.key.range && !p.type.isMutable())
1562                         {
1563                             /* Limit the range of the key to the specified range
1564                              */
1565                             v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
1566                         }
1567                     }
1568                 }
1569                 fs._body = new CompoundStatement(loc, ds, fs._body);
1570 
1571                 Statement s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
1572                 if (auto ls = checkLabeledLoop(sc, fs))   // https://issues.dlang.org/show_bug.cgi?id=15450
1573                                                           // don't use sc2
1574                     ls.gotoTarget = s;
1575                 return retStmt(s);
1576             }
1577         case Taarray:
1578             if (fs.op == TOK.foreach_reverse_)
1579                 fs.warning("cannot use `foreach_reverse` with an associative array");
1580             if (checkForArgTypes(fs))
1581                 return retError();
1582 
1583             taa = cast(TypeAArray)tab;
1584             if (dim < 1 || dim > 2)
1585             {
1586                 fs.error("only one or two arguments for associative array `foreach`");
1587                 return retError();
1588             }
1589             return retStmt(apply());
1590 
1591         case Tclass:
1592         case Tstruct:
1593             /* Prefer using opApply, if it exists
1594              */
1595             if (sapply)
1596                 return retStmt(apply());
1597             {
1598                 /* Look for range iteration, i.e. the properties
1599                  * .empty, .popFront, .popBack, .front and .back
1600                  *    foreach (e; aggr) { ... }
1601                  * translates to:
1602                  *    for (auto __r = aggr[]; !__r.empty; __r.popFront()) {
1603                  *        auto e = __r.front;
1604                  *        ...
1605                  *    }
1606                  */
1607                 auto ad = (tab.ty == Tclass) ?
1608                     cast(AggregateDeclaration)(cast(TypeClass)tab).sym :
1609                     cast(AggregateDeclaration)(cast(TypeStruct)tab).sym;
1610                 Identifier idfront;
1611                 Identifier idpopFront;
1612                 if (fs.op == TOK.foreach_)
1613                 {
1614                     idfront = Id.Ffront;
1615                     idpopFront = Id.FpopFront;
1616                 }
1617                 else
1618                 {
1619                     idfront = Id.Fback;
1620                     idpopFront = Id.FpopBack;
1621                 }
1622                 auto sfront = ad.search(Loc.initial, idfront);
1623                 if (!sfront)
1624                     return retStmt(apply());
1625 
1626                 /* Generate a temporary __r and initialize it with the aggregate.
1627                  */
1628                 VarDeclaration r;
1629                 Statement _init;
1630                 if (vinit && fs.aggr.op == TOK.variable && (cast(VarExp)fs.aggr).var == vinit)
1631                 {
1632                     r = vinit;
1633                     _init = new ExpStatement(loc, vinit);
1634                 }
1635                 else
1636                 {
1637                     r = copyToTemp(0, "__r", fs.aggr);
1638                     r.dsymbolSemantic(sc);
1639                     _init = new ExpStatement(loc, r);
1640                     if (vinit)
1641                         _init = new CompoundStatement(loc, new ExpStatement(loc, vinit), _init);
1642                 }
1643 
1644                 // !__r.empty
1645                 Expression e = new VarExp(loc, r);
1646                 e = new DotIdExp(loc, e, Id.Fempty);
1647                 Expression condition = new NotExp(loc, e);
1648 
1649                 // __r.idpopFront()
1650                 e = new VarExp(loc, r);
1651                 Expression increment = new CallExp(loc, new DotIdExp(loc, e, idpopFront));
1652 
1653                 /* Declaration statement for e:
1654                  *    auto e = __r.idfront;
1655                  */
1656                 e = new VarExp(loc, r);
1657                 Expression einit = new DotIdExp(loc, e, idfront);
1658                 Statement makeargs, forbody;
1659                 bool ignoreRef = false; // If a range returns a non-ref front we ignore ref on foreach
1660 
1661                 Type tfront;
1662                 if (auto fd = sfront.isFuncDeclaration())
1663                 {
1664                     if (!fd.functionSemantic())
1665                         return rangeError();
1666                     tfront = fd.type;
1667                 }
1668                 else if (auto td = sfront.isTemplateDeclaration())
1669                 {
1670                     Expressions a;
1671                     if (auto f = resolveFuncCall(loc, sc, td, null, tab, &a, FuncResolveFlag.quiet))
1672                         tfront = f.type;
1673                 }
1674                 else if (auto d = sfront.toAlias().isDeclaration())
1675                 {
1676                     tfront = d.type;
1677                 }
1678                 if (!tfront || tfront.ty == Terror)
1679                     return rangeError();
1680                 if (tfront.toBasetype().ty == Tfunction)
1681                 {
1682                     auto ftt = cast(TypeFunction)tfront.toBasetype();
1683                     tfront = tfront.toBasetype().nextOf();
1684                     if (!ftt.isref)
1685                     {
1686                         // .front() does not return a ref. We ignore ref on foreach arg.
1687                         // see https://issues.dlang.org/show_bug.cgi?id=11934
1688                         if (tfront.needsDestruction()) ignoreRef = true;
1689                     }
1690                 }
1691                 if (tfront.ty == Tvoid)
1692                 {
1693                     fs.error("`%s.front` is `void` and has no value", oaggr.toChars());
1694                     return retError();
1695                 }
1696 
1697                 if (dim == 1)
1698                 {
1699                     auto p = (*fs.parameters)[0];
1700                     auto ve = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, einit));
1701                     ve.storage_class |= STC.foreach_;
1702                     ve.storage_class |= p.storageClass & (STC.scope_ | STC.IOR | STC.TYPECTOR);
1703 
1704                     if (ignoreRef)
1705                         ve.storage_class &= ~STC.ref_;
1706 
1707                     makeargs = new ExpStatement(loc, ve);
1708                 }
1709                 else
1710                 {
1711                     auto vd = copyToTemp(STC.ref_, "__front", einit);
1712                     vd.dsymbolSemantic(sc);
1713                     makeargs = new ExpStatement(loc, vd);
1714 
1715                     // Resolve inout qualifier of front type
1716                     tfront = tfront.substWildTo(tab.mod);
1717 
1718                     Expression ve = new VarExp(loc, vd);
1719                     ve.type = tfront;
1720 
1721                     auto exps = new Expressions();
1722                     exps.push(ve);
1723                     int pos = 0;
1724                     while (exps.dim < dim)
1725                     {
1726                         pos = expandAliasThisTuples(exps, pos);
1727                         if (pos == -1)
1728                             break;
1729                     }
1730                     if (exps.dim != dim)
1731                     {
1732                         const(char)* plural = exps.dim > 1 ? "s" : "";
1733                         fs.error("cannot infer argument types, expected %llu argument%s, not %llu",
1734                             cast(ulong) exps.dim, plural, cast(ulong) dim);
1735                         return retError();
1736                     }
1737 
1738                     foreach (i; 0 .. dim)
1739                     {
1740                         auto p = (*fs.parameters)[i];
1741                         auto exp = (*exps)[i];
1742                         version (none)
1743                         {
1744                             printf("[%d] p = %s %s, exp = %s %s\n", i,
1745                                 p.type ? p.type.toChars() : "?", p.ident.toChars(),
1746                                 exp.type.toChars(), exp.toChars());
1747                         }
1748                         if (!p.type)
1749                             p.type = exp.type;
1750 
1751                         auto sc = p.storageClass;
1752                         if (ignoreRef) sc &= ~STC.ref_;
1753                         p.type = p.type.addStorageClass(sc).typeSemantic(loc, sc2);
1754                         if (!exp.implicitConvTo(p.type))
1755                             return rangeError();
1756 
1757                         auto var = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, exp));
1758                         var.storage_class |= STC.ctfe | STC.ref_ | STC.foreach_;
1759                         makeargs = new CompoundStatement(loc, makeargs, new ExpStatement(loc, var));
1760                     }
1761                 }
1762 
1763                 forbody = new CompoundStatement(loc, makeargs, fs._body);
1764 
1765                 Statement s = new ForStatement(loc, _init, condition, increment, forbody, fs.endloc);
1766                 if (auto ls = checkLabeledLoop(sc, fs))
1767                     ls.gotoTarget = s;
1768 
1769                 version (none)
1770                 {
1771                     printf("init: %s\n", _init.toChars());
1772                     printf("condition: %s\n", condition.toChars());
1773                     printf("increment: %s\n", increment.toChars());
1774                     printf("body: %s\n", forbody.toChars());
1775                 }
1776                 return retStmt(s);
1777             }
1778         case Tdelegate:
1779             if (fs.op == TOK.foreach_reverse_)
1780                 fs.deprecation("cannot use `foreach_reverse` with a delegate");
1781             return retStmt(apply());
1782         case Terror:
1783             return retError();
1784         default:
1785             fs.error("`foreach`: `%s` is not an aggregate type", fs.aggr.type.toChars());
1786             return retError();
1787         }
1788     }
1789 
private(D)1790     private static extern(D) Expression applyOpApply(ForeachStatement fs, Type tab, Dsymbol sapply,
1791                                                      Scope* sc2, Expression flde)
1792     {
1793         version (none)
1794         {
1795             if (global.params.useDIP1000 == FeatureState.enabled)
1796             {
1797                 message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`");
1798             }
1799             (cast(FuncExp)flde).fd.tookAddressOf = 1;
1800         }
1801         else
1802         {
1803             if (global.params.useDIP1000 == FeatureState.enabled)
1804                 ++(cast(FuncExp)flde).fd.tookAddressOf;  // allocate a closure unless the opApply() uses 'scope'
1805         }
1806         assert(tab.ty == Tstruct || tab.ty == Tclass);
1807         assert(sapply);
1808         /* Call:
1809          *  aggr.apply(flde)
1810          */
1811         Expression ec;
1812         ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident);
1813         ec = new CallExp(fs.loc, ec, flde);
1814         ec = ec.expressionSemantic(sc2);
1815         if (ec.op == TOK.error)
1816             return null;
1817         if (ec.type != Type.tint32)
1818         {
1819             fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars());
1820             return null;
1821         }
1822         return ec;
1823     }
1824 
private(D)1825     private static extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde,
1826                                                       Scope* sc2, Type tab)
1827     {
1828         Expression ec;
1829         /* Call:
1830          *      aggr(flde)
1831          */
1832         if (fs.aggr.op == TOK.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() &&
1833             !(cast(DelegateExp)fs.aggr).func.needThis())
1834         {
1835             // https://issues.dlang.org/show_bug.cgi?id=3560
1836             fs.aggr = (cast(DelegateExp)fs.aggr).e1;
1837         }
1838         ec = new CallExp(fs.loc, fs.aggr, flde);
1839         ec = ec.expressionSemantic(sc2);
1840         if (ec.op == TOK.error)
1841             return null;
1842         if (ec.type != Type.tint32)
1843         {
1844             fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars());
1845             return null;
1846         }
1847         return ec;
1848     }
1849 
private(D)1850     private static extern(D) Expression applyArray(ForeachStatement fs, Expression flde,
1851                                                    Scope* sc2, Type tn, Type tnv, TY tabty)
1852     {
1853         Expression ec;
1854         const dim = fs.parameters.dim;
1855         const loc = fs.loc;
1856         /* Call:
1857          *      _aApply(aggr, flde)
1858          */
1859         __gshared const(char)** fntab =
1860         [
1861          "cc", "cw", "cd",
1862          "wc", "cc", "wd",
1863          "dc", "dw", "dd"
1864          ];
1865 
1866         const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1;
1867         char[BUFFER_LEN] fdname;
1868         int flag;
1869 
1870         switch (tn.ty)
1871         {
1872             case Tchar:     flag = 0;   break;
1873             case Twchar:    flag = 3;   break;
1874             case Tdchar:    flag = 6;   break;
1875             default:
1876                 assert(0);
1877         }
1878         switch (tnv.ty)
1879         {
1880             case Tchar:     flag += 0;  break;
1881             case Twchar:    flag += 1;  break;
1882             case Tdchar:    flag += 2;  break;
1883             default:
1884                 assert(0);
1885         }
1886         const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : "";
1887         int j = sprintf(fdname.ptr, "_aApply%s%.*s%llu", r, 2, fntab[flag], cast(ulong)dim);
1888         assert(j < BUFFER_LEN);
1889 
1890         FuncDeclaration fdapply;
1891         TypeDelegate dgty;
1892         auto params = new Parameters();
1893         params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null));
1894         auto dgparams = new Parameters();
1895         dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
1896         if (dim == 2)
1897             dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
1898         dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
1899         params.push(new Parameter(0, dgty, null, null, null));
1900         fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);
1901 
1902         if (tabty == Tsarray)
1903             fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf());
1904         // paint delegate argument to the type runtime expects
1905         Expression fexp = flde;
1906         if (!dgty.equals(flde.type))
1907         {
1908             fexp = new CastExp(loc, flde, flde.type);
1909             fexp.type = dgty;
1910         }
1911         ec = new VarExp(Loc.initial, fdapply, false);
1912         ec = new CallExp(loc, ec, fs.aggr, fexp);
1913         ec.type = Type.tint32; // don't run semantic() on ec
1914         return ec;
1915     }
1916 
private(D)1917     private static extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, TypeAArray taa)
1918     {
1919         Expression ec;
1920         const dim = fs.parameters.dim;
1921         // Check types
1922         Parameter p = (*fs.parameters)[0];
1923         bool isRef = (p.storageClass & STC.ref_) != 0;
1924         Type ta = p.type;
1925         if (dim == 2)
1926         {
1927             Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index);
1928             if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta))
1929             {
1930                 fs.error("`foreach`: index must be type `%s`, not `%s`",
1931                          ti.toChars(), ta.toChars());
1932                 return null;
1933             }
1934             p = (*fs.parameters)[1];
1935             isRef = (p.storageClass & STC.ref_) != 0;
1936             ta = p.type;
1937         }
1938         Type taav = taa.nextOf();
1939         if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
1940         {
1941             fs.error("`foreach`: value must be type `%s`, not `%s`",
1942                      taav.toChars(), ta.toChars());
1943             return null;
1944         }
1945 
1946         /* Call:
1947          *  extern(C) int _aaApply(void*, in size_t, int delegate(void*))
1948          *      _aaApply(aggr, keysize, flde)
1949          *
1950          *  extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
1951          *      _aaApply2(aggr, keysize, flde)
1952          */
1953         __gshared FuncDeclaration* fdapply = [null, null];
1954         __gshared TypeDelegate* fldeTy = [null, null];
1955         ubyte i = (dim == 2 ? 1 : 0);
1956         if (!fdapply[i])
1957         {
1958             auto params = new Parameters();
1959             params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null));
1960             params.push(new Parameter(STC.const_, Type.tsize_t, null, null, null));
1961             auto dgparams = new Parameters();
1962             dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
1963             if (dim == 2)
1964                 dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
1965             fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
1966             params.push(new Parameter(0, fldeTy[i], null, null, null));
1967             fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply);
1968         }
1969 
1970         auto exps = new Expressions();
1971         exps.push(fs.aggr);
1972         auto keysize = taa.index.size();
1973         if (keysize == SIZE_INVALID)
1974             return null;
1975         assert(keysize < keysize.max - target.ptrsize);
1976         keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1);
1977         // paint delegate argument to the type runtime expects
1978         Expression fexp = flde;
1979         if (!fldeTy[i].equals(flde.type))
1980         {
1981             fexp = new CastExp(fs.loc, flde, flde.type);
1982             fexp.type = fldeTy[i];
1983         }
1984         exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t));
1985         exps.push(fexp);
1986         ec = new VarExp(Loc.initial, fdapply[i], false);
1987         ec = new CallExp(fs.loc, ec, exps);
1988         ec.type = Type.tint32; // don't run semantic() on ec
1989         return ec;
1990     }
1991 
private(D)1992     private static extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc)
1993     {
1994         if (!cases.dim)
1995         {
1996             // Easy case, a clean exit from the loop
1997             e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899
1998             return new ExpStatement(loc, e);
1999         }
2000         // Construct a switch statement around the return value
2001         // of the apply function.
2002         Statement s;
2003         auto a = new Statements();
2004 
2005         // default: break; takes care of cases 0 and 1
2006         s = new BreakStatement(Loc.initial, null);
2007         s = new DefaultStatement(Loc.initial, s);
2008         a.push(s);
2009 
2010         // cases 2...
2011         foreach (i, c; *cases)
2012         {
2013             s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c);
2014             a.push(s);
2015         }
2016 
2017         s = new CompoundStatement(loc, a);
2018         return new SwitchStatement(loc, e, s, false);
2019     }
2020     /*************************************
2021      * Turn foreach body into the function literal:
2022      *  int delegate(ref T param) { body }
2023      * Params:
2024      *  sc = context
2025      *  fs = ForeachStatement
2026      *  tfld = type of function literal to be created, can be null
2027      * Returns:
2028      *  Function literal created, as an expression
2029      *  null if error.
2030      */
foreachBodyToFunction(Scope * sc,ForeachStatement fs,TypeFunction tfld)2031     static FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld)
2032     {
2033         auto params = new Parameters();
2034         foreach (i; 0 .. fs.parameters.dim)
2035         {
2036             Parameter p = (*fs.parameters)[i];
2037             StorageClass stc = STC.ref_;
2038             Identifier id;
2039 
2040             p.type = p.type.typeSemantic(fs.loc, sc);
2041             p.type = p.type.addStorageClass(p.storageClass);
2042             if (tfld)
2043             {
2044                 Parameter prm = tfld.parameterList[i];
2045                 //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars());
2046                 stc = prm.storageClass & STC.ref_;
2047                 id = p.ident; // argument copy is not need.
2048                 if ((p.storageClass & STC.ref_) != stc)
2049                 {
2050                     if (!stc)
2051                     {
2052                         fs.error("`foreach`: cannot make `%s` `ref`", p.ident.toChars());
2053                         return null;
2054                     }
2055                     goto LcopyArg;
2056                 }
2057             }
2058             else if (p.storageClass & STC.ref_)
2059             {
2060                 // default delegate parameters are marked as ref, then
2061                 // argument copy is not need.
2062                 id = p.ident;
2063             }
2064             else
2065             {
2066                 // Make a copy of the ref argument so it isn't
2067                 // a reference.
2068             LcopyArg:
2069                 id = Identifier.generateId("__applyArg", cast(int)i);
2070 
2071                 Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id));
2072                 auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie);
2073                 v.storage_class |= STC.temp;
2074                 Statement s = new ExpStatement(fs.loc, v);
2075                 fs._body = new CompoundStatement(fs.loc, s, fs._body);
2076             }
2077             params.push(new Parameter(stc, p.type, id, null, null));
2078         }
2079         // https://issues.dlang.org/show_bug.cgi?id=13840
2080         // Throwable nested function inside nothrow function is acceptable.
2081         StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func);
2082         auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc);
2083         fs.cases = new Statements();
2084         fs.gotos = new ScopeStatements();
2085         auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs);
2086         fld.fbody = fs._body;
2087         Expression flde = new FuncExp(fs.loc, fld);
2088         flde = flde.expressionSemantic(sc);
2089         fld.tookAddressOf = 0;
2090         if (flde.op == TOK.error)
2091             return null;
2092         return cast(FuncExp)flde;
2093     }
2094 
visit(ForeachRangeStatement fs)2095     override void visit(ForeachRangeStatement fs)
2096     {
2097         /* https://dlang.org/spec/statement.html#foreach-range-statement
2098          */
2099 
2100         //printf("ForeachRangeStatement::semantic() %p\n", fs);
2101         auto loc = fs.loc;
2102         fs.lwr = fs.lwr.expressionSemantic(sc);
2103         fs.lwr = resolveProperties(sc, fs.lwr);
2104         fs.lwr = fs.lwr.optimize(WANTvalue);
2105         if (!fs.lwr.type)
2106         {
2107             fs.error("invalid range lower bound `%s`", fs.lwr.toChars());
2108             return setError();
2109         }
2110 
2111         fs.upr = fs.upr.expressionSemantic(sc);
2112         fs.upr = resolveProperties(sc, fs.upr);
2113         fs.upr = fs.upr.optimize(WANTvalue);
2114         if (!fs.upr.type)
2115         {
2116             fs.error("invalid range upper bound `%s`", fs.upr.toChars());
2117             return setError();
2118         }
2119 
2120         if (fs.prm.type)
2121         {
2122             fs.prm.type = fs.prm.type.typeSemantic(loc, sc);
2123             fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
2124             fs.lwr = fs.lwr.implicitCastTo(sc, fs.prm.type);
2125 
2126             if (fs.upr.implicitConvTo(fs.prm.type) || (fs.prm.storageClass & STC.ref_))
2127             {
2128                 fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
2129             }
2130             else
2131             {
2132                 // See if upr-1 fits in prm.type
2133                 Expression limit = new MinExp(loc, fs.upr, IntegerExp.literal!1);
2134                 limit = limit.expressionSemantic(sc);
2135                 limit = limit.optimize(WANTvalue);
2136                 if (!limit.implicitConvTo(fs.prm.type))
2137                 {
2138                     fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
2139                 }
2140             }
2141         }
2142         else
2143         {
2144             /* Must infer types from lwr and upr
2145              */
2146             Type tlwr = fs.lwr.type.toBasetype();
2147             if (tlwr.ty == Tstruct || tlwr.ty == Tclass)
2148             {
2149                 /* Just picking the first really isn't good enough.
2150                  */
2151                 fs.prm.type = fs.lwr.type;
2152             }
2153             else if (fs.lwr.type == fs.upr.type)
2154             {
2155                 /* Same logic as CondExp ?lwr:upr
2156                  */
2157                 fs.prm.type = fs.lwr.type;
2158             }
2159             else
2160             {
2161                 scope AddExp ea = new AddExp(loc, fs.lwr, fs.upr);
2162                 if (typeCombine(ea, sc))
2163                     return setError();
2164                 fs.prm.type = ea.type;
2165                 fs.lwr = ea.e1;
2166                 fs.upr = ea.e2;
2167             }
2168             fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
2169         }
2170         if (fs.prm.type.ty == Terror || fs.lwr.op == TOK.error || fs.upr.op == TOK.error)
2171         {
2172             return setError();
2173         }
2174 
2175         /* Convert to a for loop:
2176          *  foreach (key; lwr .. upr) =>
2177          *  for (auto key = lwr, auto tmp = upr; key < tmp; ++key)
2178          *
2179          *  foreach_reverse (key; lwr .. upr) =>
2180          *  for (auto tmp = lwr, auto key = upr; key-- > tmp;)
2181          */
2182         auto ie = new ExpInitializer(loc, (fs.op == TOK.foreach_) ? fs.lwr : fs.upr);
2183         fs.key = new VarDeclaration(loc, fs.upr.type.mutableOf(), Identifier.generateId("__key"), ie);
2184         fs.key.storage_class |= STC.temp;
2185         SignExtendedNumber lower = getIntRange(fs.lwr).imin;
2186         SignExtendedNumber upper = getIntRange(fs.upr).imax;
2187         if (lower <= upper)
2188         {
2189             fs.key.range = new IntRange(lower, upper);
2190         }
2191 
2192         Identifier id = Identifier.generateId("__limit");
2193         ie = new ExpInitializer(loc, (fs.op == TOK.foreach_) ? fs.upr : fs.lwr);
2194         auto tmp = new VarDeclaration(loc, fs.upr.type, id, ie);
2195         tmp.storage_class |= STC.temp;
2196 
2197         auto cs = new Statements();
2198         // Keep order of evaluation as lwr, then upr
2199         if (fs.op == TOK.foreach_)
2200         {
2201             cs.push(new ExpStatement(loc, fs.key));
2202             cs.push(new ExpStatement(loc, tmp));
2203         }
2204         else
2205         {
2206             cs.push(new ExpStatement(loc, tmp));
2207             cs.push(new ExpStatement(loc, fs.key));
2208         }
2209         Statement forinit = new CompoundDeclarationStatement(loc, cs);
2210 
2211         Expression cond;
2212         if (fs.op == TOK.foreach_reverse_)
2213         {
2214             cond = new PostExp(TOK.minusMinus, loc, new VarExp(loc, fs.key));
2215             if (fs.prm.type.isscalar())
2216             {
2217                 // key-- > tmp
2218                 cond = new CmpExp(TOK.greaterThan, loc, cond, new VarExp(loc, tmp));
2219             }
2220             else
2221             {
2222                 // key-- != tmp
2223                 cond = new EqualExp(TOK.notEqual, loc, cond, new VarExp(loc, tmp));
2224             }
2225         }
2226         else
2227         {
2228             if (fs.prm.type.isscalar())
2229             {
2230                 // key < tmp
2231                 cond = new CmpExp(TOK.lessThan, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
2232             }
2233             else
2234             {
2235                 // key != tmp
2236                 cond = new EqualExp(TOK.notEqual, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
2237             }
2238         }
2239 
2240         Expression increment = null;
2241         if (fs.op == TOK.foreach_)
2242         {
2243             // key += 1
2244             //increment = new AddAssignExp(loc, new VarExp(loc, fs.key), IntegerExp.literal!1);
2245             increment = new PreExp(TOK.prePlusPlus, loc, new VarExp(loc, fs.key));
2246         }
2247         if ((fs.prm.storageClass & STC.ref_) && fs.prm.type.equals(fs.key.type))
2248         {
2249             fs.key.range = null;
2250             auto v = new AliasDeclaration(loc, fs.prm.ident, fs.key);
2251             fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
2252         }
2253         else
2254         {
2255             ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs.key), fs.prm.type));
2256             auto v = new VarDeclaration(loc, fs.prm.type, fs.prm.ident, ie);
2257             v.storage_class |= STC.temp | STC.foreach_ | (fs.prm.storageClass & STC.ref_);
2258             fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
2259             if (fs.key.range && !fs.prm.type.isMutable())
2260             {
2261                 /* Limit the range of the key to the specified range
2262                  */
2263                 v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
2264             }
2265         }
2266         if (fs.prm.storageClass & STC.ref_)
2267         {
2268             if (fs.key.type.constConv(fs.prm.type) == MATCH.nomatch)
2269             {
2270                 fs.error("argument type mismatch, `%s` to `ref %s`", fs.key.type.toChars(), fs.prm.type.toChars());
2271                 return setError();
2272             }
2273         }
2274 
2275         auto s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
2276         if (LabelStatement ls = checkLabeledLoop(sc, fs))
2277             ls.gotoTarget = s;
2278         result = s.statementSemantic(sc);
2279     }
2280 
visit(IfStatement ifs)2281     override void visit(IfStatement ifs)
2282     {
2283         /* https://dlang.org/spec/statement.html#IfStatement
2284          */
2285 
2286         // check in syntax level
2287         ifs.condition = checkAssignmentAsCondition(ifs.condition);
2288 
2289         auto sym = new ScopeDsymbol();
2290         sym.parent = sc.scopesym;
2291         sym.endlinnum = ifs.endloc.linnum;
2292         Scope* scd = sc.push(sym);
2293         if (ifs.prm)
2294         {
2295             /* Declare prm, which we will set to be the
2296              * result of condition.
2297              */
2298             auto ei = new ExpInitializer(ifs.loc, ifs.condition);
2299             ifs.match = new VarDeclaration(ifs.loc, ifs.prm.type, ifs.prm.ident, ei);
2300             ifs.match.parent = scd.func;
2301             ifs.match.storage_class |= ifs.prm.storageClass;
2302             ifs.match.dsymbolSemantic(scd);
2303 
2304             auto de = new DeclarationExp(ifs.loc, ifs.match);
2305             auto ve = new VarExp(ifs.loc, ifs.match);
2306             ifs.condition = new CommaExp(ifs.loc, de, ve);
2307             ifs.condition = ifs.condition.expressionSemantic(scd);
2308 
2309             if (ifs.match.edtor)
2310             {
2311                 Statement sdtor = new DtorExpStatement(ifs.loc, ifs.match.edtor, ifs.match);
2312                 sdtor = new ScopeGuardStatement(ifs.loc, TOK.onScopeExit, sdtor);
2313                 ifs.ifbody = new CompoundStatement(ifs.loc, sdtor, ifs.ifbody);
2314                 ifs.match.storage_class |= STC.nodtor;
2315 
2316                 // the destructor is always called
2317                 // whether the 'ifbody' is executed or not
2318                 Statement sdtor2 = new DtorExpStatement(ifs.loc, ifs.match.edtor, ifs.match);
2319                 if (ifs.elsebody)
2320                     ifs.elsebody = new CompoundStatement(ifs.loc, sdtor2, ifs.elsebody);
2321                 else
2322                     ifs.elsebody = sdtor2;
2323             }
2324         }
2325         else
2326         {
2327             if (ifs.condition.op == TOK.dotIdentifier)
2328                 (cast(DotIdExp)ifs.condition).noderef = true;
2329 
2330             ifs.condition = ifs.condition.expressionSemantic(scd);
2331             ifs.condition = resolveProperties(scd, ifs.condition);
2332             ifs.condition = ifs.condition.addDtorHook(scd);
2333         }
2334         if (checkNonAssignmentArrayOp(ifs.condition))
2335             ifs.condition = ErrorExp.get();
2336         ifs.condition = checkGC(scd, ifs.condition);
2337 
2338         // Convert to boolean after declaring prm so this works:
2339         //  if (S prm = S()) {}
2340         // where S is a struct that defines opCast!bool.
2341         ifs.condition = ifs.condition.toBoolean(scd);
2342 
2343         // If we can short-circuit evaluate the if statement, don't do the
2344         // semantic analysis of the skipped code.
2345         // This feature allows a limited form of conditional compilation.
2346         ifs.condition = ifs.condition.optimize(WANTvalue);
2347 
2348         // Save 'root' of two branches (then and else) at the point where it forks
2349         CtorFlow ctorflow_root = scd.ctorflow.clone();
2350 
2351         ifs.ifbody = ifs.ifbody.semanticNoScope(scd);
2352         scd.pop();
2353 
2354         CtorFlow ctorflow_then = sc.ctorflow;   // move flow results
2355         sc.ctorflow = ctorflow_root;            // reset flow analysis back to root
2356         if (ifs.elsebody)
2357             ifs.elsebody = ifs.elsebody.semanticScope(sc, null, null, null);
2358 
2359         // Merge 'then' results into 'else' results
2360         sc.merge(ifs.loc, ctorflow_then);
2361 
2362         ctorflow_then.freeFieldinit();          // free extra copy of the data
2363 
2364         if (ifs.condition.op == TOK.error ||
2365             (ifs.ifbody && ifs.ifbody.isErrorStatement()) ||
2366             (ifs.elsebody && ifs.elsebody.isErrorStatement()))
2367         {
2368             return setError();
2369         }
2370         result = ifs;
2371     }
2372 
visit(ConditionalStatement cs)2373     override void visit(ConditionalStatement cs)
2374     {
2375         //printf("ConditionalStatement::semantic()\n");
2376 
2377         // If we can short-circuit evaluate the if statement, don't do the
2378         // semantic analysis of the skipped code.
2379         // This feature allows a limited form of conditional compilation.
2380         if (cs.condition.include(sc))
2381         {
2382             DebugCondition dc = cs.condition.isDebugCondition();
2383             if (dc)
2384             {
2385                 sc = sc.push();
2386                 sc.flags |= SCOPE.debug_;
2387                 cs.ifbody = cs.ifbody.statementSemantic(sc);
2388                 sc.pop();
2389             }
2390             else
2391                 cs.ifbody = cs.ifbody.statementSemantic(sc);
2392             result = cs.ifbody;
2393         }
2394         else
2395         {
2396             if (cs.elsebody)
2397                 cs.elsebody = cs.elsebody.statementSemantic(sc);
2398             result = cs.elsebody;
2399         }
2400     }
2401 
visit(PragmaStatement ps)2402     override void visit(PragmaStatement ps)
2403     {
2404         /* https://dlang.org/spec/statement.html#pragma-statement
2405          */
2406         // Should be merged with PragmaDeclaration
2407 
2408         //printf("PragmaStatement::semantic() %s\n", ps.toChars());
2409         //printf("body = %p\n", ps._body);
2410         if (ps.ident == Id.msg)
2411         {
2412             if (ps.args)
2413             {
2414                 foreach (arg; *ps.args)
2415                 {
2416                     sc = sc.startCTFE();
2417                     auto e = arg.expressionSemantic(sc);
2418                     e = resolveProperties(sc, e);
2419                     sc = sc.endCTFE();
2420 
2421                     // pragma(msg) is allowed to contain types as well as expressions
2422                     e = ctfeInterpretForPragmaMsg(e);
2423                     if (e.op == TOK.error)
2424                     {
2425                         errorSupplemental(ps.loc, "while evaluating `pragma(msg, %s)`", arg.toChars());
2426                         return setError();
2427                     }
2428                     if (auto se = e.toStringExp())
2429                     {
2430                         const slice = se.toUTF8(sc).peekString();
2431                         fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr);
2432                     }
2433                     else
2434                         fprintf(stderr, "%s", e.toChars());
2435                 }
2436                 fprintf(stderr, "\n");
2437             }
2438         }
2439         else if (ps.ident == Id.lib)
2440         {
2441             version (all)
2442             {
2443                 /* Should this be allowed?
2444                  */
2445                 ps.error("`pragma(lib)` not allowed as statement");
2446                 return setError();
2447             }
2448             else
2449             {
2450                 if (!ps.args || ps.args.dim != 1)
2451                 {
2452                     ps.error("`string` expected for library name");
2453                     return setError();
2454                 }
2455                 else
2456                 {
2457                     auto se = semanticString(sc, (*ps.args)[0], "library name");
2458                     if (!se)
2459                         return setError();
2460 
2461                     if (global.params.verbose)
2462                     {
2463                         message("library   %.*s", cast(int)se.len, se.string);
2464                     }
2465                 }
2466             }
2467         }
2468         else if (ps.ident == Id.linkerDirective)
2469         {
2470             /* Should this be allowed?
2471              */
2472             ps.error("`pragma(linkerDirective)` not allowed as statement");
2473             return setError();
2474         }
2475         else if (ps.ident == Id.startaddress)
2476         {
2477             if (!ps.args || ps.args.dim != 1)
2478                 ps.error("function name expected for start address");
2479             else
2480             {
2481                 Expression e = (*ps.args)[0];
2482                 sc = sc.startCTFE();
2483                 e = e.expressionSemantic(sc);
2484                 e = resolveProperties(sc, e);
2485                 sc = sc.endCTFE();
2486 
2487                 e = e.ctfeInterpret();
2488                 (*ps.args)[0] = e;
2489                 Dsymbol sa = getDsymbol(e);
2490                 if (!sa || !sa.isFuncDeclaration())
2491                 {
2492                     ps.error("function name expected for start address, not `%s`", e.toChars());
2493                     return setError();
2494                 }
2495                 if (ps._body)
2496                 {
2497                     ps._body = ps._body.statementSemantic(sc);
2498                     if (ps._body.isErrorStatement())
2499                     {
2500                         result = ps._body;
2501                         return;
2502                     }
2503                 }
2504                 result = ps;
2505                 return;
2506             }
2507         }
2508         else if (ps.ident == Id.Pinline)
2509         {
2510             PINLINE inlining = PINLINE.default_;
2511             if (!ps.args || ps.args.dim == 0)
2512                 inlining = PINLINE.default_;
2513             else if (!ps.args || ps.args.dim != 1)
2514             {
2515                 ps.error("boolean expression expected for `pragma(inline)`");
2516                 return setError();
2517             }
2518             else
2519             {
2520                 Expression e = (*ps.args)[0];
2521                 sc = sc.startCTFE();
2522                 e = e.expressionSemantic(sc);
2523                 e = resolveProperties(sc, e);
2524                 sc = sc.endCTFE();
2525                 e = e.ctfeInterpret();
2526                 e = e.toBoolean(sc);
2527                 if (e.isErrorExp())
2528                 {
2529                     ps.error("pragma(`inline`, `true` or `false`) expected, not `%s`", (*ps.args)[0].toChars());
2530                     return setError();
2531                 }
2532 
2533                 if (e.isBool(true))
2534                     inlining = PINLINE.always;
2535                 else if (e.isBool(false))
2536                     inlining = PINLINE.never;
2537 
2538                     FuncDeclaration fd = sc.func;
2539                 if (!fd)
2540                 {
2541                     ps.error("`pragma(inline)` is not inside a function");
2542                     return setError();
2543                 }
2544                 fd.inlining = inlining;
2545             }
2546         }
2547         else if (!global.params.ignoreUnsupportedPragmas)
2548         {
2549             ps.error("unrecognized `pragma(%s)`", ps.ident.toChars());
2550             return setError();
2551         }
2552 
2553         if (ps._body)
2554         {
2555             if (ps.ident == Id.msg || ps.ident == Id.startaddress)
2556             {
2557                 ps.error("`pragma(%s)` is missing a terminating `;`", ps.ident.toChars());
2558                 return setError();
2559             }
2560             ps._body = ps._body.statementSemantic(sc);
2561         }
2562         result = ps._body;
2563     }
2564 
visit(StaticAssertStatement s)2565     override void visit(StaticAssertStatement s)
2566     {
2567         s.sa.semantic2(sc);
2568         if (s.sa.errors)
2569             return setError();
2570     }
2571 
visit(SwitchStatement ss)2572     override void visit(SwitchStatement ss)
2573     {
2574         /* https://dlang.org/spec/statement.html#switch-statement
2575          */
2576 
2577         //printf("SwitchStatement::semantic(%p)\n", ss);
2578         ss.tryBody = sc.tryBody;
2579         ss.tf = sc.tf;
2580         if (ss.cases)
2581         {
2582             result = ss; // already run
2583             return;
2584         }
2585 
2586         bool conditionError = false;
2587         ss.condition = ss.condition.expressionSemantic(sc);
2588         ss.condition = resolveProperties(sc, ss.condition);
2589 
2590         Type att = null;
2591         TypeEnum te = null;
2592         while (ss.condition.op != TOK.error)
2593         {
2594             // preserve enum type for final switches
2595             if (ss.condition.type.ty == Tenum)
2596                 te = cast(TypeEnum)ss.condition.type;
2597             if (ss.condition.type.isString())
2598             {
2599                 // If it's not an array, cast it to one
2600                 if (ss.condition.type.ty != Tarray)
2601                 {
2602                     ss.condition = ss.condition.implicitCastTo(sc, ss.condition.type.nextOf().arrayOf());
2603                 }
2604                 ss.condition.type = ss.condition.type.constOf();
2605                 break;
2606             }
2607             ss.condition = integralPromotions(ss.condition, sc);
2608             if (ss.condition.op != TOK.error && ss.condition.type.isintegral())
2609                 break;
2610 
2611             auto ad = isAggregate(ss.condition.type);
2612             if (ad && ad.aliasthis && !isRecursiveAliasThis(att, ss.condition.type))
2613             {
2614                 if (auto e = resolveAliasThis(sc, ss.condition, true))
2615                 {
2616                     ss.condition = e;
2617                     continue;
2618                 }
2619             }
2620 
2621             if (ss.condition.op != TOK.error)
2622             {
2623                 ss.error("`%s` must be of integral or string type, it is a `%s`",
2624                     ss.condition.toChars(), ss.condition.type.toChars());
2625                 conditionError = true;
2626                 break;
2627             }
2628         }
2629         if (checkNonAssignmentArrayOp(ss.condition))
2630             ss.condition = ErrorExp.get();
2631         ss.condition = ss.condition.optimize(WANTvalue);
2632         ss.condition = checkGC(sc, ss.condition);
2633         if (ss.condition.op == TOK.error)
2634             conditionError = true;
2635 
2636         bool needswitcherror = false;
2637 
2638         ss.lastVar = sc.lastVar;
2639 
2640         sc = sc.push();
2641         sc.sbreak = ss;
2642         sc.sw = ss;
2643 
2644         ss.cases = new CaseStatements();
2645         const inLoopSave = sc.inLoop;
2646         sc.inLoop = true;        // BUG: should use Scope::mergeCallSuper() for each case instead
2647         ss._body = ss._body.statementSemantic(sc);
2648         sc.inLoop = inLoopSave;
2649 
2650         if (conditionError || (ss._body && ss._body.isErrorStatement()))
2651         {
2652             sc.pop();
2653             return setError();
2654         }
2655 
2656         // Resolve any goto case's with exp
2657       Lgotocase:
2658         foreach (gcs; ss.gotoCases)
2659         {
2660             if (!gcs.exp)
2661             {
2662                 gcs.error("no `case` statement following `goto case;`");
2663                 sc.pop();
2664                 return setError();
2665             }
2666 
2667             for (Scope* scx = sc; scx; scx = scx.enclosing)
2668             {
2669                 if (!scx.sw)
2670                     continue;
2671                 foreach (cs; *scx.sw.cases)
2672                 {
2673                     if (cs.exp.equals(gcs.exp))
2674                     {
2675                         gcs.cs = cs;
2676                         continue Lgotocase;
2677                     }
2678                 }
2679             }
2680             gcs.error("`case %s` not found", gcs.exp.toChars());
2681             sc.pop();
2682             return setError();
2683         }
2684 
2685         if (ss.isFinal)
2686         {
2687             Type t = ss.condition.type;
2688             Dsymbol ds;
2689             EnumDeclaration ed = null;
2690             if (t && ((ds = t.toDsymbol(sc)) !is null))
2691                 ed = ds.isEnumDeclaration(); // typedef'ed enum
2692             if (!ed && te && ((ds = te.toDsymbol(sc)) !is null))
2693                 ed = ds.isEnumDeclaration();
2694             if (ed && ss.cases.length < ed.members.length)
2695             {
2696                 int missingMembers = 0;
2697                 const maxShown = !global.params.verbose ? 6 : int.max;
2698             Lmembers:
2699                 foreach (es; *ed.members)
2700                 {
2701                     EnumMember em = es.isEnumMember();
2702                     if (em)
2703                     {
2704                         foreach (cs; *ss.cases)
2705                         {
2706                             if (cs.exp.equals(em.value) || (!cs.exp.type.isString() &&
2707                                 !em.value.type.isString() && cs.exp.toInteger() == em.value.toInteger()))
2708                                 continue Lmembers;
2709                         }
2710                         if (missingMembers == 0)
2711                             ss.error("missing cases for `enum` members in `final switch`:");
2712 
2713                         if (missingMembers < maxShown)
2714                             errorSupplemental(ss.loc, "`%s`", em.toChars());
2715                         missingMembers++;
2716                     }
2717                 }
2718                 if (missingMembers > 0)
2719                 {
2720                     if (missingMembers > maxShown)
2721                         errorSupplemental(ss.loc, "... (%d more, -v to show) ...", missingMembers - maxShown);
2722                     sc.pop();
2723                     return setError();
2724                 }
2725             }
2726             else
2727                 needswitcherror = true;
2728         }
2729 
2730         if (!sc.sw.sdefault && (!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on))
2731         {
2732             ss.hasNoDefault = 1;
2733 
2734             if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()))
2735                 ss.error("`switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`");
2736 
2737             // Generate runtime error if the default is hit
2738             auto a = new Statements();
2739             CompoundStatement cs;
2740             Statement s;
2741 
2742             if (global.params.useSwitchError == CHECKENABLE.on &&
2743                 global.params.checkAction != CHECKACTION.halt)
2744             {
2745                 if (global.params.checkAction == CHECKACTION.C)
2746                 {
2747                     /* Rewrite as an assert(0) and let e2ir generate
2748                      * the call to the C assert failure function
2749                      */
2750                     s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0));
2751                 }
2752                 else
2753                 {
2754                     if (!verifyHookExist(ss.loc, *sc, Id.__switch_error, "generating assert messages"))
2755                         return setError();
2756 
2757                     Expression sl = new IdentifierExp(ss.loc, Id.empty);
2758                     sl = new DotIdExp(ss.loc, sl, Id.object);
2759                     sl = new DotIdExp(ss.loc, sl, Id.__switch_error);
2760 
2761                     Expressions* args = new Expressions(2);
2762                     (*args)[0] = new StringExp(ss.loc, ss.loc.filename.toDString());
2763                     (*args)[1] = new IntegerExp(ss.loc.linnum);
2764 
2765                     sl = new CallExp(ss.loc, sl, args);
2766                     sl = sl.expressionSemantic(sc);
2767 
2768                     s = new SwitchErrorStatement(ss.loc, sl);
2769                 }
2770             }
2771             else
2772                 s = new ExpStatement(ss.loc, new HaltExp(ss.loc));
2773 
2774             a.reserve(2);
2775             sc.sw.sdefault = new DefaultStatement(ss.loc, s);
2776             a.push(ss._body);
2777             if (ss._body.blockExit(sc.func, false) & BE.fallthru)
2778                 a.push(new BreakStatement(Loc.initial, null));
2779             a.push(sc.sw.sdefault);
2780             cs = new CompoundStatement(ss.loc, a);
2781             ss._body = cs;
2782         }
2783 
2784         if (ss.checkLabel())
2785         {
2786             sc.pop();
2787             return setError();
2788         }
2789 
2790 
2791         if (!ss.condition.type.isString())
2792         {
2793             sc.pop();
2794             result = ss;
2795             return;
2796         }
2797 
2798         // Transform a switch with string labels into a switch with integer labels.
2799 
2800         // The integer value of each case corresponds to the index of each label
2801         // string in the sorted array of label strings.
2802 
2803         // The value of the integer condition is obtained by calling the druntime template
2804         // switch(object.__switch(cond, options...)) {0: {...}, 1: {...}, ...}
2805 
2806         // We sort a copy of the array of labels because we want to do a binary search in object.__switch,
2807         // without modifying the order of the case blocks here in the compiler.
2808 
2809         if (!verifyHookExist(ss.loc, *sc, Id.__switch, "switch cases on strings"))
2810             return setError();
2811 
2812         size_t numcases = 0;
2813         if (ss.cases)
2814             numcases = ss.cases.dim;
2815 
2816         for (size_t i = 0; i < numcases; i++)
2817         {
2818             CaseStatement cs = (*ss.cases)[i];
2819             cs.index = cast(int)i;
2820         }
2821 
2822         // Make a copy of all the cases so that qsort doesn't scramble the actual
2823         // data we pass to codegen (the order of the cases in the switch).
2824         CaseStatements *csCopy = (*ss.cases).copy();
2825 
2826         if (numcases)
2827         {
2828             static int sort_compare(in CaseStatement* x, in CaseStatement* y) @trusted
2829             {
2830                 auto se1 = x.exp.isStringExp();
2831                 auto se2 = y.exp.isStringExp();
2832                 return (se1 && se2) ? se1.compare(se2) : 0;
2833             }
2834             // Sort cases for efficient lookup
2835             csCopy.sort!sort_compare;
2836         }
2837 
2838         // The actual lowering
2839         auto arguments = new Expressions();
2840         arguments.push(ss.condition);
2841 
2842         auto compileTimeArgs = new Objects();
2843 
2844         // The type & label no.
2845         compileTimeArgs.push(new TypeExp(ss.loc, ss.condition.type.nextOf()));
2846 
2847         // The switch labels
2848         foreach (caseString; *csCopy)
2849         {
2850             compileTimeArgs.push(caseString.exp);
2851         }
2852 
2853         Expression sl = new IdentifierExp(ss.loc, Id.empty);
2854         sl = new DotIdExp(ss.loc, sl, Id.object);
2855         sl = new DotTemplateInstanceExp(ss.loc, sl, Id.__switch, compileTimeArgs);
2856 
2857         sl = new CallExp(ss.loc, sl, arguments);
2858         sl = sl.expressionSemantic(sc);
2859         ss.condition = sl;
2860 
2861         auto i = 0;
2862         foreach (c; *csCopy)
2863         {
2864             (*ss.cases)[c.index].exp = new IntegerExp(i++);
2865         }
2866 
2867         //printf("%s\n", ss._body.toChars());
2868         ss.statementSemantic(sc);
2869 
2870         sc.pop();
2871         result = ss;
2872     }
2873 
visit(CaseStatement cs)2874     override void visit(CaseStatement cs)
2875     {
2876         SwitchStatement sw = sc.sw;
2877         bool errors = false;
2878 
2879         //printf("CaseStatement::semantic() %s\n", toChars());
2880         sc = sc.startCTFE();
2881         cs.exp = cs.exp.expressionSemantic(sc);
2882         cs.exp = resolveProperties(sc, cs.exp);
2883         sc = sc.endCTFE();
2884 
2885         if (sw)
2886         {
2887             Expression initialExp = cs.exp;
2888 
2889             cs.exp = cs.exp.implicitCastTo(sc, sw.condition.type);
2890             cs.exp = cs.exp.optimize(WANTvalue | WANTexpand);
2891 
2892             Expression e = cs.exp;
2893             // Remove all the casts the user and/or implicitCastTo may introduce
2894             // otherwise we'd sometimes fail the check below.
2895             while (e.op == TOK.cast_)
2896                 e = (cast(CastExp)e).e1;
2897 
2898             /* This is where variables are allowed as case expressions.
2899              */
2900             if (e.op == TOK.variable)
2901             {
2902                 VarExp ve = cast(VarExp)e;
2903                 VarDeclaration v = ve.var.isVarDeclaration();
2904                 Type t = cs.exp.type.toBasetype();
2905                 if (v && (t.isintegral() || t.ty == Tclass))
2906                 {
2907                     /* Flag that we need to do special code generation
2908                      * for this, i.e. generate a sequence of if-then-else
2909                      */
2910                     sw.hasVars = 1;
2911 
2912                     /* TODO check if v can be uninitialized at that point.
2913                      */
2914                     if (!v.isConst() && !v.isImmutable())
2915                     {
2916                         cs.error("`case` variables have to be `const` or `immutable`");
2917                     }
2918 
2919                     if (sw.isFinal)
2920                     {
2921                         cs.error("`case` variables not allowed in `final switch` statements");
2922                         errors = true;
2923                     }
2924 
2925                     /* Find the outermost scope `scx` that set `sw`.
2926                      * Then search scope `scx` for a declaration of `v`.
2927                      */
2928                     for (Scope* scx = sc; scx; scx = scx.enclosing)
2929                     {
2930                         if (scx.enclosing && scx.enclosing.sw == sw)
2931                             continue;
2932                         assert(scx.sw == sw);
2933 
2934                         if (!scx.search(cs.exp.loc, v.ident, null))
2935                         {
2936                             cs.error("`case` variable `%s` declared at %s cannot be declared in `switch` body",
2937                                 v.toChars(), v.loc.toChars());
2938                             errors = true;
2939                         }
2940                         break;
2941                     }
2942                     goto L1;
2943                 }
2944             }
2945             else
2946                 cs.exp = cs.exp.ctfeInterpret();
2947 
2948             if (StringExp se = cs.exp.toStringExp())
2949                 cs.exp = se;
2950             else if (cs.exp.op != TOK.int64 && cs.exp.op != TOK.error)
2951             {
2952                 cs.error("`case` must be a `string` or an integral constant, not `%s`", cs.exp.toChars());
2953                 errors = true;
2954             }
2955 
2956         L1:
2957             foreach (cs2; *sw.cases)
2958             {
2959                 //printf("comparing '%s' with '%s'\n", exp.toChars(), cs.exp.toChars());
2960                 if (cs2.exp.equals(cs.exp))
2961                 {
2962                     // https://issues.dlang.org/show_bug.cgi?id=15909
2963                     cs.error("duplicate `case %s` in `switch` statement", initialExp.toChars());
2964                     errors = true;
2965                     break;
2966                 }
2967             }
2968 
2969             sw.cases.push(cs);
2970 
2971             // Resolve any goto case's with no exp to this case statement
2972             for (size_t i = 0; i < sw.gotoCases.dim;)
2973             {
2974                 GotoCaseStatement gcs = sw.gotoCases[i];
2975                 if (!gcs.exp)
2976                 {
2977                     gcs.cs = cs;
2978                     sw.gotoCases.remove(i); // remove from array
2979                     continue;
2980                 }
2981                 i++;
2982             }
2983 
2984             if (sc.sw.tf != sc.tf)
2985             {
2986                 cs.error("`switch` and `case` are in different `finally` blocks");
2987                 errors = true;
2988             }
2989             if (sc.sw.tryBody != sc.tryBody)
2990             {
2991                 cs.error("case cannot be in different `try` block level from `switch`");
2992                 errors = true;
2993             }
2994         }
2995         else
2996         {
2997             cs.error("`case` not in `switch` statement");
2998             errors = true;
2999         }
3000 
3001         sc.ctorflow.orCSX(CSX.label);
3002         cs.statement = cs.statement.statementSemantic(sc);
3003         if (cs.statement.isErrorStatement())
3004         {
3005             result = cs.statement;
3006             return;
3007         }
3008         if (errors || cs.exp.op == TOK.error)
3009             return setError();
3010 
3011         cs.lastVar = sc.lastVar;
3012         result = cs;
3013     }
3014 
visit(CaseRangeStatement crs)3015     override void visit(CaseRangeStatement crs)
3016     {
3017         SwitchStatement sw = sc.sw;
3018         if (sw is null)
3019         {
3020             crs.error("case range not in `switch` statement");
3021             return setError();
3022         }
3023 
3024         //printf("CaseRangeStatement::semantic() %s\n", toChars());
3025         bool errors = false;
3026         if (sw.isFinal)
3027         {
3028             crs.error("case ranges not allowed in `final switch`");
3029             errors = true;
3030         }
3031 
3032         sc = sc.startCTFE();
3033         crs.first = crs.first.expressionSemantic(sc);
3034         crs.first = resolveProperties(sc, crs.first);
3035         sc = sc.endCTFE();
3036         crs.first = crs.first.implicitCastTo(sc, sw.condition.type);
3037         crs.first = crs.first.ctfeInterpret();
3038 
3039         sc = sc.startCTFE();
3040         crs.last = crs.last.expressionSemantic(sc);
3041         crs.last = resolveProperties(sc, crs.last);
3042         sc = sc.endCTFE();
3043         crs.last = crs.last.implicitCastTo(sc, sw.condition.type);
3044         crs.last = crs.last.ctfeInterpret();
3045 
3046         if (crs.first.op == TOK.error || crs.last.op == TOK.error || errors)
3047         {
3048             if (crs.statement)
3049                 crs.statement.statementSemantic(sc);
3050             return setError();
3051         }
3052 
3053         uinteger_t fval = crs.first.toInteger();
3054         uinteger_t lval = crs.last.toInteger();
3055         if ((crs.first.type.isunsigned() && fval > lval) || (!crs.first.type.isunsigned() && cast(sinteger_t)fval > cast(sinteger_t)lval))
3056         {
3057             crs.error("first `case %s` is greater than last `case %s`", crs.first.toChars(), crs.last.toChars());
3058             errors = true;
3059             lval = fval;
3060         }
3061 
3062         if (lval - fval > 256)
3063         {
3064             crs.error("had %llu cases which is more than 256 cases in case range", lval - fval);
3065             errors = true;
3066             lval = fval + 256;
3067         }
3068 
3069         if (errors)
3070             return setError();
3071 
3072         /* This works by replacing the CaseRange with an array of Case's.
3073          *
3074          * case a: .. case b: s;
3075          *    =>
3076          * case a:
3077          *   [...]
3078          * case b:
3079          *   s;
3080          */
3081 
3082         auto statements = new Statements();
3083         for (uinteger_t i = fval; i != lval + 1; i++)
3084         {
3085             Statement s = crs.statement;
3086             if (i != lval) // if not last case
3087                 s = new ExpStatement(crs.loc, cast(Expression)null);
3088             Expression e = new IntegerExp(crs.loc, i, crs.first.type);
3089             Statement cs = new CaseStatement(crs.loc, e, s);
3090             statements.push(cs);
3091         }
3092         Statement s = new CompoundStatement(crs.loc, statements);
3093         sc.ctorflow.orCSX(CSX.label);
3094         s = s.statementSemantic(sc);
3095         result = s;
3096     }
3097 
visit(DefaultStatement ds)3098     override void visit(DefaultStatement ds)
3099     {
3100         //printf("DefaultStatement::semantic()\n");
3101         bool errors = false;
3102         if (sc.sw)
3103         {
3104             if (sc.sw.sdefault)
3105             {
3106                 ds.error("`switch` statement already has a default");
3107                 errors = true;
3108             }
3109             sc.sw.sdefault = ds;
3110 
3111             if (sc.sw.tf != sc.tf)
3112             {
3113                 ds.error("`switch` and `default` are in different `finally` blocks");
3114                 errors = true;
3115             }
3116             if (sc.sw.tryBody != sc.tryBody)
3117             {
3118                 ds.error("default cannot be in different `try` block level from `switch`");
3119                 errors = true;
3120             }
3121             if (sc.sw.isFinal)
3122             {
3123                 ds.error("`default` statement not allowed in `final switch` statement");
3124                 errors = true;
3125             }
3126         }
3127         else
3128         {
3129             ds.error("`default` not in `switch` statement");
3130             errors = true;
3131         }
3132 
3133         sc.ctorflow.orCSX(CSX.label);
3134         ds.statement = ds.statement.statementSemantic(sc);
3135         if (errors || ds.statement.isErrorStatement())
3136             return setError();
3137 
3138         ds.lastVar = sc.lastVar;
3139         result = ds;
3140     }
3141 
visit(GotoDefaultStatement gds)3142     override void visit(GotoDefaultStatement gds)
3143     {
3144         /* https://dlang.org/spec/statement.html#goto-statement
3145          */
3146 
3147         gds.sw = sc.sw;
3148         if (!gds.sw)
3149         {
3150             gds.error("`goto default` not in `switch` statement");
3151             return setError();
3152         }
3153         if (gds.sw.isFinal)
3154         {
3155             gds.error("`goto default` not allowed in `final switch` statement");
3156             return setError();
3157         }
3158         result = gds;
3159     }
3160 
visit(GotoCaseStatement gcs)3161     override void visit(GotoCaseStatement gcs)
3162     {
3163         /* https://dlang.org/spec/statement.html#goto-statement
3164          */
3165 
3166         if (!sc.sw)
3167         {
3168             gcs.error("`goto case` not in `switch` statement");
3169             return setError();
3170         }
3171 
3172         if (gcs.exp)
3173         {
3174             gcs.exp = gcs.exp.expressionSemantic(sc);
3175             gcs.exp = gcs.exp.implicitCastTo(sc, sc.sw.condition.type);
3176             gcs.exp = gcs.exp.optimize(WANTvalue);
3177             if (gcs.exp.op == TOK.error)
3178                 return setError();
3179         }
3180 
3181         sc.sw.gotoCases.push(gcs);
3182         result = gcs;
3183     }
3184 
visit(ReturnStatement rs)3185     override void visit(ReturnStatement rs)
3186     {
3187         /* https://dlang.org/spec/statement.html#return-statement
3188          */
3189 
3190         //printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars());
3191 
3192         FuncDeclaration fd = sc.parent.isFuncDeclaration();
3193         if (fd.fes)
3194             fd = fd.fes.func; // fd is now function enclosing foreach
3195 
3196             TypeFunction tf = cast(TypeFunction)fd.type;
3197         assert(tf.ty == Tfunction);
3198 
3199         if (rs.exp && rs.exp.op == TOK.variable && (cast(VarExp)rs.exp).var == fd.vresult)
3200         {
3201             // return vresult;
3202             if (sc.fes)
3203             {
3204                 assert(rs.caseDim == 0);
3205                 sc.fes.cases.push(rs);
3206                 result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.dim + 1));
3207                 return;
3208             }
3209             if (fd.returnLabel)
3210             {
3211                 auto gs = new GotoStatement(rs.loc, Id.returnLabel);
3212                 gs.label = fd.returnLabel;
3213                 result = gs;
3214                 return;
3215             }
3216 
3217             if (!fd.returns)
3218                 fd.returns = new ReturnStatements();
3219             fd.returns.push(rs);
3220             result = rs;
3221             return;
3222         }
3223 
3224         Type tret = tf.next;
3225         Type tbret = tret ? tret.toBasetype() : null;
3226 
3227         bool inferRef = (tf.isref && (fd.storage_class & STC.auto_));
3228         Expression e0 = null;
3229 
3230         bool errors = false;
3231         if (sc.flags & SCOPE.contract)
3232         {
3233             rs.error("`return` statements cannot be in contracts");
3234             errors = true;
3235         }
3236         if (sc.os && sc.os.tok != TOK.onScopeFailure)
3237         {
3238             rs.error("`return` statements cannot be in `%s` bodies", Token.toChars(sc.os.tok));
3239             errors = true;
3240         }
3241         if (sc.tf)
3242         {
3243             rs.error("`return` statements cannot be in `finally` bodies");
3244             errors = true;
3245         }
3246 
3247         if (fd.isCtorDeclaration())
3248         {
3249             if (rs.exp)
3250             {
3251                 rs.error("cannot return expression from constructor");
3252                 errors = true;
3253             }
3254 
3255             // Constructors implicitly do:
3256             //      return this;
3257             rs.exp = new ThisExp(Loc.initial);
3258             rs.exp.type = tret;
3259         }
3260         else if (rs.exp)
3261         {
3262             fd.hasReturnExp |= (fd.hasReturnExp & 1 ? 16 : 1);
3263 
3264             FuncLiteralDeclaration fld = fd.isFuncLiteralDeclaration();
3265             if (tret)
3266                 rs.exp = inferType(rs.exp, tret);
3267             else if (fld && fld.treq)
3268                 rs.exp = inferType(rs.exp, fld.treq.nextOf().nextOf());
3269 
3270             rs.exp = rs.exp.expressionSemantic(sc);
3271             // If we're returning by ref, allow the expression to be `shared`
3272             const returnSharedRef = (tf.isref && (fd.inferRetType || tret.isShared()));
3273             rs.exp.checkSharedAccess(sc, returnSharedRef);
3274 
3275             // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
3276             if (rs.exp.op == TOK.type)
3277                 rs.exp = resolveAliasThis(sc, rs.exp);
3278 
3279             rs.exp = resolveProperties(sc, rs.exp);
3280             if (rs.exp.checkType())
3281                 rs.exp = ErrorExp.get();
3282             if (auto f = isFuncAddress(rs.exp))
3283             {
3284                 if (fd.inferRetType && f.checkForwardRef(rs.exp.loc))
3285                     rs.exp = ErrorExp.get();
3286             }
3287             if (checkNonAssignmentArrayOp(rs.exp))
3288                 rs.exp = ErrorExp.get();
3289 
3290             // Extract side-effect part
3291             rs.exp = Expression.extractLast(rs.exp, e0);
3292             if (rs.exp.op == TOK.call)
3293                 rs.exp = valueNoDtor(rs.exp);
3294 
3295             if (e0)
3296                 e0 = e0.optimize(WANTvalue);
3297 
3298             /* Void-return function can have void typed expression
3299              * on return statement.
3300              */
3301             if (tbret && tbret.ty == Tvoid || rs.exp.type.ty == Tvoid)
3302             {
3303                 if (rs.exp.type.ty != Tvoid)
3304                 {
3305                     rs.error("cannot return non-void from `void` function");
3306                     errors = true;
3307                     rs.exp = new CastExp(rs.loc, rs.exp, Type.tvoid);
3308                     rs.exp = rs.exp.expressionSemantic(sc);
3309                 }
3310 
3311                 /* Replace:
3312                  *      return exp;
3313                  * with:
3314                  *      exp; return;
3315                  */
3316                 e0 = Expression.combine(e0, rs.exp);
3317                 rs.exp = null;
3318             }
3319             if (e0)
3320                 e0 = checkGC(sc, e0);
3321         }
3322 
3323         if (rs.exp)
3324         {
3325             if (fd.inferRetType) // infer return type
3326             {
3327                 if (!tret)
3328                 {
3329                     tf.next = rs.exp.type;
3330                 }
3331                 else if (tret.ty != Terror && !rs.exp.type.equals(tret))
3332                 {
3333                     int m1 = rs.exp.type.implicitConvTo(tret);
3334                     int m2 = tret.implicitConvTo(rs.exp.type);
3335                     //printf("exp.type = %s m2<-->m1 tret %s\n", exp.type.toChars(), tret.toChars());
3336                     //printf("m1 = %d, m2 = %d\n", m1, m2);
3337 
3338                     if (m1 && m2)
3339                     {
3340                     }
3341                     else if (!m1 && m2)
3342                         tf.next = rs.exp.type;
3343                     else if (m1 && !m2)
3344                     {
3345                     }
3346                     else if (rs.exp.op != TOK.error)
3347                     {
3348                         rs.error("Expected return type of `%s`, not `%s`:",
3349                                  tret.toChars(),
3350                                  rs.exp.type.toChars());
3351                         errorSupplemental((fd.returns) ? (*fd.returns)[0].loc : fd.loc,
3352                                           "Return type of `%s` inferred here.",
3353                                           tret.toChars());
3354 
3355                         errors = true;
3356                         tf.next = Type.terror;
3357                     }
3358                 }
3359 
3360                 tret = tf.next;
3361                 tbret = tret.toBasetype();
3362             }
3363 
3364             if (inferRef) // deduce 'auto ref'
3365             {
3366                 /* Determine "refness" of function return:
3367                  * if it's an lvalue, return by ref, else return by value
3368                  * https://dlang.org/spec/function.html#auto-ref-functions
3369                  */
3370 
3371                 void turnOffRef(scope void delegate() supplemental)
3372                 {
3373                     tf.isref = false;    // return by value
3374                     tf.isreturn = false; // ignore 'return' attribute, whether explicit or inferred
3375                     fd.storage_class &= ~STC.return_;
3376 
3377                     // If we previously assumed the function could be ref when
3378                     // checking for `shared`, make sure we were right
3379                     if (global.params.noSharedAccess && rs.exp.type.isShared())
3380                     {
3381                         fd.error("function returns `shared` but cannot be inferred `ref`");
3382                         supplemental();
3383                     }
3384                 }
3385 
3386                 if (rs.exp.isLvalue())
3387                 {
3388                     /* May return by ref
3389                      */
3390                     if (checkReturnEscapeRef(sc, rs.exp, true))
3391                         turnOffRef(() { checkReturnEscapeRef(sc, rs.exp, false); });
3392                     else if (!rs.exp.type.constConv(tf.next))
3393                         turnOffRef(
3394                             () => rs.loc.errorSupplemental("cannot implicitly convert `%s` of type `%s` to `%s`",
3395                                       rs.exp.toChars(), rs.exp.type.toChars(), tf.next.toChars())
3396                         );
3397                 }
3398                 else
3399                     turnOffRef(
3400                         () => rs.loc.errorSupplemental("return value `%s` is not an lvalue", rs.exp.toChars())
3401                     );
3402 
3403                 /* The "refness" is determined by all of return statements.
3404                  * This means:
3405                  *    return 3; return x;  // ok, x can be a value
3406                  *    return x; return 3;  // ok, x can be a value
3407                  */
3408             }
3409         }
3410         else
3411         {
3412             // infer return type
3413             if (fd.inferRetType)
3414             {
3415                 if (tf.next && tf.next.ty != Tvoid)
3416                 {
3417                     if (tf.next.ty != Terror)
3418                     {
3419                         rs.error("mismatched function return type inference of `void` and `%s`", tf.next.toChars());
3420                     }
3421                     errors = true;
3422                     tf.next = Type.terror;
3423                 }
3424                 else
3425                     tf.next = Type.tvoid;
3426 
3427                     tret = tf.next;
3428                 tbret = tret.toBasetype();
3429             }
3430 
3431             if (inferRef) // deduce 'auto ref'
3432                 tf.isref = false;
3433 
3434             if (tbret.ty != Tvoid) // if non-void return
3435             {
3436                 if (tbret.ty != Terror)
3437                     rs.error("`return` expression expected");
3438                 errors = true;
3439             }
3440             else if (fd.isMain())
3441             {
3442                 // main() returns 0, even if it returns void
3443                 rs.exp = IntegerExp.literal!0;
3444             }
3445         }
3446 
3447         // If any branches have called a ctor, but this branch hasn't, it's an error
3448         if (sc.ctorflow.callSuper & CSX.any_ctor && !(sc.ctorflow.callSuper & (CSX.this_ctor | CSX.super_ctor)))
3449         {
3450             rs.error("`return` without calling constructor");
3451             errors = true;
3452         }
3453 
3454         if (sc.ctorflow.fieldinit.length)       // if aggregate fields are being constructed
3455         {
3456             auto ad = fd.isMemberLocal();
3457             assert(ad);
3458             foreach (i, v; ad.fields)
3459             {
3460                 bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested());
3461                 if (mustInit && !(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor))
3462                 {
3463                     rs.error("an earlier `return` statement skips field `%s` initialization", v.toChars());
3464                     errors = true;
3465                 }
3466             }
3467         }
3468         sc.ctorflow.orCSX(CSX.return_);
3469 
3470         if (errors)
3471             return setError();
3472 
3473         if (sc.fes)
3474         {
3475             if (!rs.exp)
3476             {
3477                 // Send out "case receiver" statement to the foreach.
3478                 //  return exp;
3479                 Statement s = new ReturnStatement(Loc.initial, rs.exp);
3480                 sc.fes.cases.push(s);
3481 
3482                 // Immediately rewrite "this" return statement as:
3483                 //  return cases.dim+1;
3484                 rs.exp = new IntegerExp(sc.fes.cases.dim + 1);
3485                 if (e0)
3486                 {
3487                     result = new CompoundStatement(rs.loc, new ExpStatement(rs.loc, e0), rs);
3488                     return;
3489                 }
3490                 result = rs;
3491                 return;
3492             }
3493             else
3494             {
3495                 fd.buildResultVar(null, rs.exp.type);
3496                 bool r = fd.vresult.checkNestedReference(sc, Loc.initial);
3497                 assert(!r); // vresult should be always accessible
3498 
3499                 // Send out "case receiver" statement to the foreach.
3500                 //  return vresult;
3501                 Statement s = new ReturnStatement(Loc.initial, new VarExp(Loc.initial, fd.vresult));
3502                 sc.fes.cases.push(s);
3503 
3504                 // Save receiver index for the later rewriting from:
3505                 //  return exp;
3506                 // to:
3507                 //  vresult = exp; retrun caseDim;
3508                 rs.caseDim = sc.fes.cases.dim + 1;
3509             }
3510         }
3511         if (rs.exp)
3512         {
3513             if (!fd.returns)
3514                 fd.returns = new ReturnStatements();
3515             fd.returns.push(rs);
3516         }
3517         if (e0)
3518         {
3519             if (e0.op == TOK.declaration || e0.op == TOK.comma)
3520             {
3521                 rs.exp = Expression.combine(e0, rs.exp);
3522             }
3523             else
3524             {
3525                 result = new CompoundStatement(rs.loc, new ExpStatement(rs.loc, e0), rs);
3526                 return;
3527             }
3528         }
3529         result = rs;
3530     }
3531 
visit(BreakStatement bs)3532     override void visit(BreakStatement bs)
3533     {
3534         /* https://dlang.org/spec/statement.html#break-statement
3535          */
3536 
3537         //printf("BreakStatement::semantic()\n");
3538 
3539         // If:
3540         //  break Identifier;
3541         if (bs.ident)
3542         {
3543             bs.ident = fixupLabelName(sc, bs.ident);
3544 
3545             FuncDeclaration thisfunc = sc.func;
3546 
3547             for (Scope* scx = sc; scx; scx = scx.enclosing)
3548             {
3549                 if (scx.func != thisfunc) // if in enclosing function
3550                 {
3551                     if (sc.fes) // if this is the body of a foreach
3552                     {
3553                         /* Post this statement to the fes, and replace
3554                          * it with a return value that caller will put into
3555                          * a switch. Caller will figure out where the break
3556                          * label actually is.
3557                          * Case numbers start with 2, not 0, as 0 is continue
3558                          * and 1 is break.
3559                          */
3560                         sc.fes.cases.push(bs);
3561                         result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.dim + 1));
3562                         return;
3563                     }
3564                     break; // can't break to it
3565                 }
3566 
3567                 LabelStatement ls = scx.slabel;
3568                 if (ls && ls.ident == bs.ident)
3569                 {
3570                     Statement s = ls.statement;
3571                     if (!s || !s.hasBreak())
3572                         bs.error("label `%s` has no `break`", bs.ident.toChars());
3573                     else if (ls.tf != sc.tf)
3574                         bs.error("cannot break out of `finally` block");
3575                     else
3576                     {
3577                         ls.breaks = true;
3578                         result = bs;
3579                         return;
3580                     }
3581                     return setError();
3582                 }
3583             }
3584             bs.error("enclosing label `%s` for `break` not found", bs.ident.toChars());
3585             return setError();
3586         }
3587         else if (!sc.sbreak)
3588         {
3589             if (sc.os && sc.os.tok != TOK.onScopeFailure)
3590             {
3591                 bs.error("`break` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok));
3592             }
3593             else if (sc.fes)
3594             {
3595                 // Replace break; with return 1;
3596                 result = new ReturnStatement(Loc.initial, IntegerExp.literal!1);
3597                 return;
3598             }
3599             else
3600                 bs.error("`break` is not inside a loop or `switch`");
3601             return setError();
3602         }
3603         else if (sc.sbreak.isForwardingStatement())
3604         {
3605             bs.error("must use labeled `break` within `static foreach`");
3606         }
3607         result = bs;
3608     }
3609 
visit(ContinueStatement cs)3610     override void visit(ContinueStatement cs)
3611     {
3612         /* https://dlang.org/spec/statement.html#continue-statement
3613          */
3614 
3615         //printf("ContinueStatement::semantic() %p\n", cs);
3616         if (cs.ident)
3617         {
3618             cs.ident = fixupLabelName(sc, cs.ident);
3619 
3620             Scope* scx;
3621             FuncDeclaration thisfunc = sc.func;
3622 
3623             for (scx = sc; scx; scx = scx.enclosing)
3624             {
3625                 LabelStatement ls;
3626                 if (scx.func != thisfunc) // if in enclosing function
3627                 {
3628                     if (sc.fes) // if this is the body of a foreach
3629                     {
3630                         for (; scx; scx = scx.enclosing)
3631                         {
3632                             ls = scx.slabel;
3633                             if (ls && ls.ident == cs.ident && ls.statement == sc.fes)
3634                             {
3635                                 // Replace continue ident; with return 0;
3636                                 result = new ReturnStatement(Loc.initial, IntegerExp.literal!0);
3637                                 return;
3638                             }
3639                         }
3640 
3641                         /* Post this statement to the fes, and replace
3642                          * it with a return value that caller will put into
3643                          * a switch. Caller will figure out where the break
3644                          * label actually is.
3645                          * Case numbers start with 2, not 0, as 0 is continue
3646                          * and 1 is break.
3647                          */
3648                         sc.fes.cases.push(cs);
3649                         result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.dim + 1));
3650                         return;
3651                     }
3652                     break; // can't continue to it
3653                 }
3654 
3655                 ls = scx.slabel;
3656                 if (ls && ls.ident == cs.ident)
3657                 {
3658                     Statement s = ls.statement;
3659                     if (!s || !s.hasContinue())
3660                         cs.error("label `%s` has no `continue`", cs.ident.toChars());
3661                     else if (ls.tf != sc.tf)
3662                         cs.error("cannot continue out of `finally` block");
3663                     else
3664                     {
3665                         result = cs;
3666                         return;
3667                     }
3668                     return setError();
3669                 }
3670             }
3671             cs.error("enclosing label `%s` for `continue` not found", cs.ident.toChars());
3672             return setError();
3673         }
3674         else if (!sc.scontinue)
3675         {
3676             if (sc.os && sc.os.tok != TOK.onScopeFailure)
3677             {
3678                 cs.error("`continue` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok));
3679             }
3680             else if (sc.fes)
3681             {
3682                 // Replace continue; with return 0;
3683                 result = new ReturnStatement(Loc.initial, IntegerExp.literal!0);
3684                 return;
3685             }
3686             else
3687                 cs.error("`continue` is not inside a loop");
3688             return setError();
3689         }
3690         else if (sc.scontinue.isForwardingStatement())
3691         {
3692             cs.error("must use labeled `continue` within `static foreach`");
3693         }
3694         result = cs;
3695     }
3696 
visit(SynchronizedStatement ss)3697     override void visit(SynchronizedStatement ss)
3698     {
3699         /* https://dlang.org/spec/statement.html#synchronized-statement
3700          */
3701 
3702         if (ss.exp)
3703         {
3704             ss.exp = ss.exp.expressionSemantic(sc);
3705             ss.exp = resolveProperties(sc, ss.exp);
3706             ss.exp = ss.exp.optimize(WANTvalue);
3707             ss.exp = checkGC(sc, ss.exp);
3708             if (ss.exp.op == TOK.error)
3709             {
3710                 if (ss._body)
3711                     ss._body = ss._body.statementSemantic(sc);
3712                 return setError();
3713             }
3714 
3715             ClassDeclaration cd = ss.exp.type.isClassHandle();
3716             if (!cd)
3717             {
3718                 ss.error("can only `synchronize` on class objects, not `%s`", ss.exp.type.toChars());
3719                 return setError();
3720             }
3721             else if (cd.isInterfaceDeclaration())
3722             {
3723                 /* Cast the interface to an object, as the object has the monitor,
3724                  * not the interface.
3725                  */
3726                 if (!ClassDeclaration.object)
3727                 {
3728                     ss.error("missing or corrupt object.d");
3729                     fatal();
3730                 }
3731 
3732                 Type t = ClassDeclaration.object.type;
3733                 t = t.typeSemantic(Loc.initial, sc).toBasetype();
3734                 assert(t.ty == Tclass);
3735 
3736                 ss.exp = new CastExp(ss.loc, ss.exp, t);
3737                 ss.exp = ss.exp.expressionSemantic(sc);
3738             }
3739             version (all)
3740             {
3741                 /* Rewrite as:
3742                  *  auto tmp = exp;
3743                  *  _d_monitorenter(tmp);
3744                  *  try { body } finally { _d_monitorexit(tmp); }
3745                  */
3746                 auto tmp = copyToTemp(0, "__sync", ss.exp);
3747                 tmp.dsymbolSemantic(sc);
3748 
3749                 auto cs = new Statements();
3750                 cs.push(new ExpStatement(ss.loc, tmp));
3751 
3752                 auto args = new Parameters();
3753                 args.push(new Parameter(0, ClassDeclaration.object.type, null, null, null));
3754 
3755                 FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter);
3756                 Expression e = new CallExp(ss.loc, fdenter, new VarExp(ss.loc, tmp));
3757                 e.type = Type.tvoid; // do not run semantic on e
3758 
3759                 cs.push(new ExpStatement(ss.loc, e));
3760                 FuncDeclaration fdexit = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorexit);
3761                 e = new CallExp(ss.loc, fdexit, new VarExp(ss.loc, tmp));
3762                 e.type = Type.tvoid; // do not run semantic on e
3763                 Statement s = new ExpStatement(ss.loc, e);
3764                 s = new TryFinallyStatement(ss.loc, ss._body, s);
3765                 cs.push(s);
3766 
3767                 s = new CompoundStatement(ss.loc, cs);
3768                 result = s.statementSemantic(sc);
3769             }
3770         }
3771         else
3772         {
3773             /* Generate our own critical section, then rewrite as:
3774              *  static shared void* __critsec;
3775              *  _d_criticalenter2(&__critsec);
3776              *  try { body } finally { _d_criticalexit(__critsec); }
3777              */
3778             auto id = Identifier.generateId("__critsec");
3779             auto t = Type.tvoidptr;
3780             auto tmp = new VarDeclaration(ss.loc, t, id, null);
3781             tmp.storage_class |= STC.temp | STC.shared_ | STC.static_;
3782             Expression tmpExp = new VarExp(ss.loc, tmp);
3783 
3784             auto cs = new Statements();
3785             cs.push(new ExpStatement(ss.loc, tmp));
3786 
3787             /* This is just a dummy variable for "goto skips declaration" error.
3788              * Backend optimizer could remove this unused variable.
3789              */
3790             auto v = new VarDeclaration(ss.loc, Type.tvoidptr, Identifier.generateId("__sync"), null);
3791             v.dsymbolSemantic(sc);
3792             cs.push(new ExpStatement(ss.loc, v));
3793 
3794             auto enterArgs = new Parameters();
3795             enterArgs.push(new Parameter(0, t.pointerTo(), null, null, null));
3796 
3797             FuncDeclaration fdenter = FuncDeclaration.genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_);
3798             Expression e = new AddrExp(ss.loc, tmpExp);
3799             e = e.expressionSemantic(sc);
3800             e = new CallExp(ss.loc, fdenter, e);
3801             e.type = Type.tvoid; // do not run semantic on e
3802             cs.push(new ExpStatement(ss.loc, e));
3803 
3804             auto exitArgs = new Parameters();
3805             exitArgs.push(new Parameter(0, t, null, null, null));
3806 
3807             FuncDeclaration fdexit = FuncDeclaration.genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_);
3808             e = new CallExp(ss.loc, fdexit, tmpExp);
3809             e.type = Type.tvoid; // do not run semantic on e
3810             Statement s = new ExpStatement(ss.loc, e);
3811             s = new TryFinallyStatement(ss.loc, ss._body, s);
3812             cs.push(s);
3813 
3814             s = new CompoundStatement(ss.loc, cs);
3815             result = s.statementSemantic(sc);
3816         }
3817     }
3818 
visit(WithStatement ws)3819     override void visit(WithStatement ws)
3820     {
3821         /* https://dlang.org/spec/statement.html#with-statement
3822          */
3823 
3824         ScopeDsymbol sym;
3825         Initializer _init;
3826 
3827         //printf("WithStatement::semantic()\n");
3828         ws.exp = ws.exp.expressionSemantic(sc);
3829         ws.exp = resolveProperties(sc, ws.exp);
3830         ws.exp = ws.exp.optimize(WANTvalue);
3831         ws.exp = checkGC(sc, ws.exp);
3832         if (ws.exp.op == TOK.error)
3833             return setError();
3834         if (ws.exp.op == TOK.scope_)
3835         {
3836             sym = new WithScopeSymbol(ws);
3837             sym.parent = sc.scopesym;
3838             sym.endlinnum = ws.endloc.linnum;
3839         }
3840         else if (ws.exp.op == TOK.type)
3841         {
3842             Dsymbol s = (cast(TypeExp)ws.exp).type.toDsymbol(sc);
3843             if (!s || !s.isScopeDsymbol())
3844             {
3845                 ws.error("`with` type `%s` has no members", ws.exp.toChars());
3846                 return setError();
3847             }
3848             sym = new WithScopeSymbol(ws);
3849             sym.parent = sc.scopesym;
3850             sym.endlinnum = ws.endloc.linnum;
3851         }
3852         else
3853         {
3854             Type t = ws.exp.type.toBasetype();
3855 
3856             Expression olde = ws.exp;
3857             if (t.ty == Tpointer)
3858             {
3859                 ws.exp = new PtrExp(ws.loc, ws.exp);
3860                 ws.exp = ws.exp.expressionSemantic(sc);
3861                 t = ws.exp.type.toBasetype();
3862             }
3863 
3864             assert(t);
3865             t = t.toBasetype();
3866             if (t.isClassHandle())
3867             {
3868                 _init = new ExpInitializer(ws.loc, ws.exp);
3869                 ws.wthis = new VarDeclaration(ws.loc, ws.exp.type, Id.withSym, _init);
3870                 ws.wthis.storage_class |= STC.temp;
3871                 ws.wthis.dsymbolSemantic(sc);
3872 
3873                 sym = new WithScopeSymbol(ws);
3874                 sym.parent = sc.scopesym;
3875                 sym.endlinnum = ws.endloc.linnum;
3876             }
3877             else if (t.ty == Tstruct)
3878             {
3879                 if (!ws.exp.isLvalue())
3880                 {
3881                     /* Re-write to
3882                      * {
3883                      *   auto __withtmp = exp
3884                      *   with(__withtmp)
3885                      *   {
3886                      *     ...
3887                      *   }
3888                      * }
3889                      */
3890                     auto tmp = copyToTemp(0, "__withtmp", ws.exp);
3891                     tmp.dsymbolSemantic(sc);
3892                     auto es = new ExpStatement(ws.loc, tmp);
3893                     ws.exp = new VarExp(ws.loc, tmp);
3894                     Statement ss = new ScopeStatement(ws.loc, new CompoundStatement(ws.loc, es, ws), ws.endloc);
3895                     result = ss.statementSemantic(sc);
3896                     return;
3897                 }
3898                 Expression e = ws.exp.addressOf();
3899                 _init = new ExpInitializer(ws.loc, e);
3900                 ws.wthis = new VarDeclaration(ws.loc, e.type, Id.withSym, _init);
3901                 ws.wthis.storage_class |= STC.temp;
3902                 ws.wthis.dsymbolSemantic(sc);
3903                 sym = new WithScopeSymbol(ws);
3904                 // Need to set the scope to make use of resolveAliasThis
3905                 sym.setScope(sc);
3906                 sym.parent = sc.scopesym;
3907                 sym.endlinnum = ws.endloc.linnum;
3908             }
3909             else
3910             {
3911                 ws.error("`with` expressions must be aggregate types or pointers to them, not `%s`", olde.type.toChars());
3912                 return setError();
3913             }
3914         }
3915 
3916         if (ws._body)
3917         {
3918             sym._scope = sc;
3919             sc = sc.push(sym);
3920             sc.insert(sym);
3921             ws._body = ws._body.statementSemantic(sc);
3922             sc.pop();
3923             if (ws._body && ws._body.isErrorStatement())
3924             {
3925                 result = ws._body;
3926                 return;
3927             }
3928         }
3929 
3930         result = ws;
3931     }
3932 
3933     // https://dlang.org/spec/statement.html#TryStatement
visit(TryCatchStatement tcs)3934     override void visit(TryCatchStatement tcs)
3935     {
3936         //printf("TryCatchStatement.semantic()\n");
3937 
3938         if (!global.params.useExceptions)
3939         {
3940             tcs.error("Cannot use try-catch statements with -betterC");
3941             return setError();
3942         }
3943 
3944         if (!ClassDeclaration.throwable)
3945         {
3946             tcs.error("Cannot use try-catch statements because `object.Throwable` was not declared");
3947             return setError();
3948         }
3949 
3950         uint flags;
3951         enum FLAGcpp = 1;
3952         enum FLAGd = 2;
3953 
3954         tcs.tryBody = sc.tryBody;   // chain on the in-flight tryBody
3955         tcs._body = tcs._body.semanticScope(sc, null, null, tcs);
3956         assert(tcs._body);
3957 
3958         /* Even if body is empty, still do semantic analysis on catches
3959          */
3960         bool catchErrors = false;
3961         foreach (i, c; *tcs.catches)
3962         {
3963             c.catchSemantic(sc);
3964             if (c.errors)
3965             {
3966                 catchErrors = true;
3967                 continue;
3968             }
3969             auto cd = c.type.toBasetype().isClassHandle();
3970             flags |= cd.isCPPclass() ? FLAGcpp : FLAGd;
3971 
3972             // Determine if current catch 'hides' any previous catches
3973             foreach (j; 0 .. i)
3974             {
3975                 Catch cj = (*tcs.catches)[j];
3976                 const si = c.loc.toChars();
3977                 const sj = cj.loc.toChars();
3978                 if (c.type.toBasetype().implicitConvTo(cj.type.toBasetype()))
3979                 {
3980                     tcs.error("`catch` at %s hides `catch` at %s", sj, si);
3981                     catchErrors = true;
3982                 }
3983             }
3984         }
3985 
3986         if (sc.func)
3987         {
3988             sc.func.flags |= FUNCFLAG.hasCatches;
3989             if (flags == (FLAGcpp | FLAGd))
3990             {
3991                 tcs.error("cannot mix catching D and C++ exceptions in the same try-catch");
3992                 catchErrors = true;
3993             }
3994         }
3995 
3996         if (catchErrors)
3997             return setError();
3998 
3999         if (tcs._body.isErrorStatement())
4000         {
4001             result = tcs._body;
4002             return;
4003         }
4004 
4005         /* If the try body never throws, we can eliminate any catches
4006          * of recoverable exceptions.
4007          */
4008         if (!(tcs._body.blockExit(sc.func, false) & BE.throw_) && ClassDeclaration.exception)
4009         {
4010             foreach_reverse (i; 0 .. tcs.catches.dim)
4011             {
4012                 Catch c = (*tcs.catches)[i];
4013 
4014                 /* If catch exception type is derived from Exception
4015                  */
4016                 if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) &&
4017                     (!c.handler || !c.handler.comeFrom()))
4018                 {
4019                     // Remove c from the array of catches
4020                     tcs.catches.remove(i);
4021                 }
4022             }
4023         }
4024 
4025         if (tcs.catches.dim == 0)
4026         {
4027             result = tcs._body.hasCode() ? tcs._body : null;
4028             return;
4029         }
4030 
4031         result = tcs;
4032     }
4033 
visit(TryFinallyStatement tfs)4034     override void visit(TryFinallyStatement tfs)
4035     {
4036         //printf("TryFinallyStatement::semantic()\n");
4037         tfs.tryBody = sc.tryBody;   // chain on in-flight tryBody
4038         tfs._body = tfs._body.semanticScope(sc, null, null, tfs);
4039 
4040         sc = sc.push();
4041         sc.tf = tfs;
4042         sc.sbreak = null;
4043         sc.scontinue = null; // no break or continue out of finally block
4044         tfs.finalbody = tfs.finalbody.semanticNoScope(sc);
4045         sc.pop();
4046 
4047         if (!tfs._body)
4048         {
4049             result = tfs.finalbody;
4050             return;
4051         }
4052         if (!tfs.finalbody)
4053         {
4054             result = tfs._body;
4055             return;
4056         }
4057 
4058         auto blockexit = tfs._body.blockExit(sc.func, false);
4059 
4060         // if not worrying about exceptions
4061         if (!(global.params.useExceptions && ClassDeclaration.throwable))
4062             blockexit &= ~BE.throw_;            // don't worry about paths that otherwise may throw
4063 
4064         // Don't care about paths that halt, either
4065         if ((blockexit & ~BE.halt) == BE.fallthru)
4066         {
4067             result = new CompoundStatement(tfs.loc, tfs._body, tfs.finalbody);
4068             return;
4069         }
4070         tfs.bodyFallsThru = (blockexit & BE.fallthru) != 0;
4071         result = tfs;
4072     }
4073 
visit(ScopeGuardStatement oss)4074     override void visit(ScopeGuardStatement oss)
4075     {
4076         /* https://dlang.org/spec/statement.html#scope-guard-statement
4077          */
4078 
4079         if (oss.tok != TOK.onScopeExit)
4080         {
4081             // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement,
4082             // so the generated catch block cannot be placed in finally block.
4083             // See also Catch::semantic.
4084             if (sc.os && sc.os.tok != TOK.onScopeFailure)
4085             {
4086                 // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
4087                 oss.error("cannot put `%s` statement inside `%s`", Token.toChars(oss.tok), Token.toChars(sc.os.tok));
4088                 return setError();
4089             }
4090             if (sc.tf)
4091             {
4092                 oss.error("cannot put `%s` statement inside `finally` block", Token.toChars(oss.tok));
4093                 return setError();
4094             }
4095         }
4096 
4097         sc = sc.push();
4098         sc.tf = null;
4099         sc.os = oss;
4100         if (oss.tok != TOK.onScopeFailure)
4101         {
4102             // Jump out from scope(failure) block is allowed.
4103             sc.sbreak = null;
4104             sc.scontinue = null;
4105         }
4106         oss.statement = oss.statement.semanticNoScope(sc);
4107         sc.pop();
4108 
4109         if (!oss.statement || oss.statement.isErrorStatement())
4110         {
4111             result = oss.statement;
4112             return;
4113         }
4114         result = oss;
4115     }
4116 
visit(ThrowStatement ts)4117     override void visit(ThrowStatement ts)
4118     {
4119         /* https://dlang.org/spec/statement.html#throw-statement
4120          */
4121 
4122         //printf("ThrowStatement::semantic()\n");
4123 
4124         if (!global.params.useExceptions)
4125         {
4126             ts.error("Cannot use `throw` statements with -betterC");
4127             return setError();
4128         }
4129 
4130         if (!ClassDeclaration.throwable)
4131         {
4132             ts.error("Cannot use `throw` statements because `object.Throwable` was not declared");
4133             return setError();
4134         }
4135 
4136         FuncDeclaration fd = sc.parent.isFuncDeclaration();
4137         fd.hasReturnExp |= 2;
4138 
4139         if (ts.exp.op == TOK.new_)
4140         {
4141             NewExp ne = cast(NewExp)ts.exp;
4142             ne.thrownew = true;
4143         }
4144 
4145         ts.exp = ts.exp.expressionSemantic(sc);
4146         ts.exp = resolveProperties(sc, ts.exp);
4147         ts.exp = checkGC(sc, ts.exp);
4148         if (ts.exp.op == TOK.error)
4149             return setError();
4150 
4151         checkThrowEscape(sc, ts.exp, false);
4152 
4153         ClassDeclaration cd = ts.exp.type.toBasetype().isClassHandle();
4154         if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null)))
4155         {
4156             ts.error("can only throw class objects derived from `Throwable`, not type `%s`", ts.exp.type.toChars());
4157             return setError();
4158         }
4159 
4160         result = ts;
4161     }
4162 
visit(DebugStatement ds)4163     override void visit(DebugStatement ds)
4164     {
4165         if (ds.statement)
4166         {
4167             sc = sc.push();
4168             sc.flags |= SCOPE.debug_;
4169             ds.statement = ds.statement.statementSemantic(sc);
4170             sc.pop();
4171         }
4172         result = ds.statement;
4173     }
4174 
visit(GotoStatement gs)4175     override void visit(GotoStatement gs)
4176     {
4177         /* https://dlang.org/spec/statement.html#goto-statement
4178          */
4179 
4180         //printf("GotoStatement::semantic()\n");
4181         FuncDeclaration fd = sc.func;
4182 
4183         gs.ident = fixupLabelName(sc, gs.ident);
4184         gs.label = fd.searchLabel(gs.ident, gs.loc);
4185         gs.tryBody = sc.tryBody;
4186         gs.tf = sc.tf;
4187         gs.os = sc.os;
4188         gs.lastVar = sc.lastVar;
4189 
4190         if (!gs.label.statement && sc.fes)
4191         {
4192             /* Either the goto label is forward referenced or it
4193              * is in the function that the enclosing foreach is in.
4194              * Can't know yet, so wrap the goto in a scope statement
4195              * so we can patch it later, and add it to a 'look at this later'
4196              * list.
4197              */
4198             gs.label.deleted = true;
4199             auto ss = new ScopeStatement(gs.loc, gs, gs.loc);
4200             sc.fes.gotos.push(ss); // 'look at this later' list
4201             result = ss;
4202             return;
4203         }
4204 
4205         // Add to fwdref list to check later
4206         if (!gs.label.statement)
4207         {
4208             if (!fd.gotos)
4209                 fd.gotos = new GotoStatements();
4210             fd.gotos.push(gs);
4211         }
4212         else if (gs.checkLabel())
4213             return setError();
4214 
4215         result = gs;
4216     }
4217 
visit(LabelStatement ls)4218     override void visit(LabelStatement ls)
4219     {
4220         //printf("LabelStatement::semantic()\n");
4221         FuncDeclaration fd = sc.parent.isFuncDeclaration();
4222 
4223         ls.ident = fixupLabelName(sc, ls.ident);
4224         ls.tryBody = sc.tryBody;
4225         ls.tf = sc.tf;
4226         ls.os = sc.os;
4227         ls.lastVar = sc.lastVar;
4228 
4229         LabelDsymbol ls2 = fd.searchLabel(ls.ident, ls.loc);
4230         if (ls2.statement)
4231         {
4232             ls.error("label `%s` already defined", ls2.toChars());
4233             return setError();
4234         }
4235         else
4236             ls2.statement = ls;
4237 
4238         sc = sc.push();
4239         sc.scopesym = sc.enclosing.scopesym;
4240 
4241         sc.ctorflow.orCSX(CSX.label);
4242 
4243         sc.slabel = ls;
4244         if (ls.statement)
4245             ls.statement = ls.statement.statementSemantic(sc);
4246         sc.pop();
4247 
4248         result = ls;
4249     }
4250 
visit(AsmStatement s)4251     override void visit(AsmStatement s)
4252     {
4253         /* https://dlang.org/spec/statement.html#asm
4254          */
4255 
4256         //printf("AsmStatement()::semantic()\n");
4257         result = asmSemantic(s, sc);
4258     }
4259 
visit(CompoundAsmStatement cas)4260     override void visit(CompoundAsmStatement cas)
4261     {
4262         //printf("CompoundAsmStatement()::semantic()\n");
4263         // Apply postfix attributes of the asm block to each statement.
4264         sc = sc.push();
4265         sc.stc |= cas.stc;
4266 
4267         /* Go through the statements twice, first to declare any labels,
4268          * second for anything else.
4269          */
4270 
4271         foreach (ref s; *cas.statements)
4272         {
4273             if (s)
4274             {
4275                 if (auto ls = s.isLabelStatement())
4276                 {
4277                     sc.func.searchLabel(ls.ident, ls.loc);
4278                 }
4279             }
4280         }
4281 
4282         foreach (ref s; *cas.statements)
4283         {
4284             s = s ? s.statementSemantic(sc) : null;
4285         }
4286 
4287         assert(sc.func);
4288         // use setImpure/setGC when the deprecation cycle is over
4289         PURE purity;
4290         if (!(cas.stc & STC.pure_) && (purity = sc.func.isPureBypassingInference()) != PURE.impure && purity != PURE.fwdref)
4291             cas.deprecation("`asm` statement is assumed to be impure - mark it with `pure` if it is not");
4292         if (!(cas.stc & STC.nogc) && sc.func.isNogcBypassingInference())
4293             cas.deprecation("`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not");
4294         if (!(cas.stc & (STC.trusted | STC.safe)) && sc.func.setUnsafe())
4295             cas.error("`asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not");
4296 
4297         sc.pop();
4298         result = cas;
4299     }
4300 
visit(ImportStatement imps)4301     override void visit(ImportStatement imps)
4302     {
4303         /* https://dlang.org/spec/module.html#ImportDeclaration
4304          */
4305 
4306         foreach (i; 0 .. imps.imports.dim)
4307         {
4308             Import s = (*imps.imports)[i].isImport();
4309             assert(!s.aliasdecls.dim);
4310             foreach (j, name; s.names)
4311             {
4312                 Identifier _alias = s.aliases[j];
4313                 if (!_alias)
4314                     _alias = name;
4315 
4316                 auto tname = new TypeIdentifier(s.loc, name);
4317                 auto ad = new AliasDeclaration(s.loc, _alias, tname);
4318                 ad._import = s;
4319                 s.aliasdecls.push(ad);
4320             }
4321 
4322             s.dsymbolSemantic(sc);
4323 
4324             // https://issues.dlang.org/show_bug.cgi?id=19942
4325             // If the module that's being imported doesn't exist, don't add it to the symbol table
4326             // for the current scope.
4327             if (s.mod !is null)
4328             {
4329                 Module.addDeferredSemantic2(s);     // https://issues.dlang.org/show_bug.cgi?id=14666
4330                 sc.insert(s);
4331 
4332                 foreach (aliasdecl; s.aliasdecls)
4333                 {
4334                     sc.insert(aliasdecl);
4335                 }
4336             }
4337         }
4338         result = imps;
4339     }
4340 }
4341 
catchSemantic(Catch c,Scope * sc)4342 void catchSemantic(Catch c, Scope* sc)
4343 {
4344     //printf("Catch::semantic(%s)\n", ident.toChars());
4345 
4346     if (sc.os && sc.os.tok != TOK.onScopeFailure)
4347     {
4348         // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
4349         error(c.loc, "cannot put `catch` statement inside `%s`", Token.toChars(sc.os.tok));
4350         c.errors = true;
4351     }
4352     if (sc.tf)
4353     {
4354         /* This is because the _d_local_unwind() gets the stack munged
4355          * up on this. The workaround is to place any try-catches into
4356          * a separate function, and call that.
4357          * To fix, have the compiler automatically convert the finally
4358          * body into a nested function.
4359          */
4360         error(c.loc, "cannot put `catch` statement inside `finally` block");
4361         c.errors = true;
4362     }
4363 
4364     auto sym = new ScopeDsymbol();
4365     sym.parent = sc.scopesym;
4366     sc = sc.push(sym);
4367 
4368     if (!c.type)
4369     {
4370         error(c.loc, "`catch` statement without an exception specification is deprecated");
4371         errorSupplemental(c.loc, "use `catch(Throwable)` for old behavior");
4372         c.errors = true;
4373 
4374         // reference .object.Throwable
4375         c.type = getThrowable();
4376     }
4377     c.type = c.type.typeSemantic(c.loc, sc);
4378     if (c.type == Type.terror)
4379     {
4380         c.errors = true;
4381         sc.pop();
4382         return;
4383     }
4384 
4385     StorageClass stc;
4386     auto cd = c.type.toBasetype().isClassHandle();
4387     if (!cd)
4388     {
4389         error(c.loc, "can only catch class objects, not `%s`", c.type.toChars());
4390         c.errors = true;
4391     }
4392     else if (cd.isCPPclass())
4393     {
4394         if (!target.cpp.exceptions)
4395         {
4396             error(c.loc, "catching C++ class objects not supported for this target");
4397             c.errors = true;
4398         }
4399         if (sc.func && !sc.intypeof && !c.internalCatch && sc.func.setUnsafe())
4400         {
4401             error(c.loc, "cannot catch C++ class objects in `@safe` code");
4402             c.errors = true;
4403         }
4404     }
4405     else if (cd != ClassDeclaration.throwable && !ClassDeclaration.throwable.isBaseOf(cd, null))
4406     {
4407         error(c.loc, "can only catch class objects derived from `Throwable`, not `%s`", c.type.toChars());
4408         c.errors = true;
4409     }
4410     else if (sc.func && !sc.intypeof && !c.internalCatch && ClassDeclaration.exception &&
4411              cd != ClassDeclaration.exception && !ClassDeclaration.exception.isBaseOf(cd, null) &&
4412              sc.func.setUnsafe())
4413     {
4414         error(c.loc, "can only catch class objects derived from `Exception` in `@safe` code, not `%s`", c.type.toChars());
4415         c.errors = true;
4416     }
4417     else if (global.params.ehnogc)
4418     {
4419         stc |= STC.scope_;
4420     }
4421 
4422     // DIP1008 requires destruction of the Throwable, even if the user didn't specify an identifier
4423     auto ident = c.ident;
4424     if (!ident && global.params.ehnogc)
4425         ident = Identifier.generateAnonymousId("var");
4426 
4427     if (ident)
4428     {
4429         c.var = new VarDeclaration(c.loc, c.type, ident, null, stc);
4430         c.var.iscatchvar = true;
4431         c.var.dsymbolSemantic(sc);
4432         sc.insert(c.var);
4433 
4434         if (global.params.ehnogc && stc & STC.scope_)
4435         {
4436             /* Add a destructor for c.var
4437              * try { handler } finally { if (!__ctfe) _d_delThrowable(var); }
4438              */
4439             assert(!c.var.edtor);           // ensure we didn't create one in callScopeDtor()
4440 
4441             Loc loc = c.loc;
4442             Expression e = new VarExp(loc, c.var);
4443             e = new CallExp(loc, new IdentifierExp(loc, Id._d_delThrowable), e);
4444 
4445             Expression ec = new IdentifierExp(loc, Id.ctfe);
4446             ec = new NotExp(loc, ec);
4447             Statement s = new IfStatement(loc, null, ec, new ExpStatement(loc, e), null, loc);
4448             c.handler = new TryFinallyStatement(loc, c.handler, s);
4449         }
4450 
4451     }
4452     c.handler = c.handler.statementSemantic(sc);
4453     if (c.handler && c.handler.isErrorStatement())
4454         c.errors = true;
4455 
4456     sc.pop();
4457 }
4458 
semanticNoScope(Statement s,Scope * sc)4459 Statement semanticNoScope(Statement s, Scope* sc)
4460 {
4461     //printf("Statement::semanticNoScope() %s\n", toChars());
4462     if (!s.isCompoundStatement() && !s.isScopeStatement())
4463     {
4464         s = new CompoundStatement(s.loc, s); // so scopeCode() gets called
4465     }
4466     s = s.statementSemantic(sc);
4467     return s;
4468 }
4469 
4470 // Same as semanticNoScope(), but do create a new scope
semanticScope(Statement s,Scope * sc,Statement sbreak,Statement scontinue,Statement tryBody)4471 private Statement semanticScope(Statement s, Scope* sc, Statement sbreak, Statement scontinue, Statement tryBody)
4472 {
4473     auto sym = new ScopeDsymbol();
4474     sym.parent = sc.scopesym;
4475     Scope* scd = sc.push(sym);
4476     if (sbreak)
4477         scd.sbreak = sbreak;
4478     if (scontinue)
4479         scd.scontinue = scontinue;
4480     if (tryBody)
4481         scd.tryBody = tryBody;
4482     s = s.semanticNoScope(scd);
4483     scd.pop();
4484     return s;
4485 }
4486 
4487 
4488 /****************************************
4489  * If `statement` has code that needs to run in a finally clause
4490  * at the end of the current scope, return that code in the form of
4491  * a Statement.
4492  * Params:
4493  *     statement = the statement
4494  *     sc = context
4495  *     sentry     = set to code executed upon entry to the scope
4496  *     sexception = set to code executed upon exit from the scope via exception
4497  *     sfinally   = set to code executed in finally block
4498  * Returns:
4499  *    code to be run in the finally clause
4500  */
scopeCode(Statement statement,Scope * sc,out Statement sentry,out Statement sexception,out Statement sfinally)4501 Statement scopeCode(Statement statement, Scope* sc, out Statement sentry, out Statement sexception, out Statement sfinally)
4502 {
4503     if (auto es = statement.isExpStatement())
4504     {
4505         if (es.exp && es.exp.op == TOK.declaration)
4506         {
4507             auto de = cast(DeclarationExp)es.exp;
4508             auto v = de.declaration.isVarDeclaration();
4509             if (v && !v.isDataseg())
4510             {
4511                 if (v.needsScopeDtor())
4512                 {
4513                     sfinally = new DtorExpStatement(es.loc, v.edtor, v);
4514                     v.storage_class |= STC.nodtor; // don't add in dtor again
4515                 }
4516             }
4517         }
4518         return es;
4519 
4520     }
4521     else if (auto sgs = statement.isScopeGuardStatement())
4522     {
4523         Statement s = new PeelStatement(sgs.statement);
4524 
4525         switch (sgs.tok)
4526         {
4527         case TOK.onScopeExit:
4528             sfinally = s;
4529             break;
4530 
4531         case TOK.onScopeFailure:
4532             sexception = s;
4533             break;
4534 
4535         case TOK.onScopeSuccess:
4536             {
4537                 /* Create:
4538                  *  sentry:   bool x = false;
4539                  *  sexception:    x = true;
4540                  *  sfinally: if (!x) statement;
4541                  */
4542                 auto v = copyToTemp(0, "__os", IntegerExp.createBool(false));
4543                 v.dsymbolSemantic(sc);
4544                 sentry = new ExpStatement(statement.loc, v);
4545 
4546                 Expression e = IntegerExp.createBool(true);
4547                 e = new AssignExp(Loc.initial, new VarExp(Loc.initial, v), e);
4548                 sexception = new ExpStatement(Loc.initial, e);
4549 
4550                 e = new VarExp(Loc.initial, v);
4551                 e = new NotExp(Loc.initial, e);
4552                 sfinally = new IfStatement(Loc.initial, null, e, s, null, Loc.initial);
4553 
4554                 break;
4555             }
4556         default:
4557             assert(0);
4558         }
4559         return null;
4560     }
4561     else if (auto ls = statement.isLabelStatement())
4562     {
4563         if (ls.statement)
4564             ls.statement = ls.statement.scopeCode(sc, sentry, sexception, sfinally);
4565         return ls;
4566     }
4567 
4568     return statement;
4569 }
4570 
4571 
4572 /*******************
4573  * Determines additional argument types for makeTupleForeach.
4574  */
TupleForeachArgs(bool isStatic,bool isDecl)4575 static template TupleForeachArgs(bool isStatic, bool isDecl)
4576 {
4577     alias Seq(T...)=T;
4578     static if(isStatic) alias T = Seq!(bool);
4579     else alias T = Seq!();
4580     static if(!isDecl) alias TupleForeachArgs = T;
4581     else alias TupleForeachArgs = Seq!(Dsymbols*,T);
4582 }
4583 
4584 /*******************
4585  * Determines the return type of makeTupleForeach.
4586  */
TupleForeachRet(bool isStatic,bool isDecl)4587 static template TupleForeachRet(bool isStatic, bool isDecl)
4588 {
4589     alias Seq(T...)=T;
4590     static if(!isDecl) alias TupleForeachRet = Statement;
4591     else alias TupleForeachRet = Dsymbols*;
4592 }
4593 
4594 
4595 /*******************
4596  * See StatementSemanticVisitor.makeTupleForeach.  This is a simple
4597  * wrapper that returns the generated statements/declarations.
4598  */
4599 TupleForeachRet!(isStatic, isDecl) makeTupleForeach(bool isStatic, bool isDecl)(Scope* sc, ForeachStatement fs, TupleForeachArgs!(isStatic, isDecl) args)
4600 {
4601     scope v = new StatementSemanticVisitor(sc);
4602     static if(!isDecl)
4603     {
4604         v.makeTupleForeach!(isStatic, isDecl)(fs, args);
4605         return v.result;
4606     }
4607     else
4608     {
4609         return v.makeTupleForeach!(isStatic, isDecl)(fs, args);
4610     }
4611 }
4612 
4613 /*********************************
4614  * Flatten out the scope by presenting `statement`
4615  * as an array of statements.
4616  * Params:
4617  *     statement = the statement to flatten
4618  *     sc = context
4619  * Returns:
4620  *     The array of `Statements`, or `null` if no flattening necessary
4621  */
flatten(Statement statement,Scope * sc)4622 private Statements* flatten(Statement statement, Scope* sc)
4623 {
4624     static auto errorStatements()
4625     {
4626         auto a = new Statements();
4627         a.push(new ErrorStatement());
4628         return a;
4629     }
4630 
4631 
4632     /*compound and expression statements have classes that inherit from them with the same
4633      *flattening behavior, so the isXXX methods won't work
4634      */
4635     switch(statement.stmt)
4636     {
4637         case STMT.Compound:
4638         case STMT.CompoundDeclaration:
4639             return (cast(CompoundStatement)statement).statements;
4640 
4641         case STMT.Exp:
4642         case STMT.DtorExp:
4643             auto es = cast(ExpStatement)statement;
4644             /* https://issues.dlang.org/show_bug.cgi?id=14243
4645              * expand template mixin in statement scope
4646              * to handle variable destructors.
4647              */
4648             if (!es.exp || es.exp.op != TOK.declaration)
4649                 return null;
4650 
4651             Dsymbol d = (cast(DeclarationExp)es.exp).declaration;
4652             auto tm = d.isTemplateMixin();
4653             if (!tm)
4654                 return null;
4655 
4656             Expression e = es.exp.expressionSemantic(sc);
4657             if (e.op == TOK.error || tm.errors)
4658                 return errorStatements();
4659             assert(tm.members);
4660 
4661             Statement s = toStatement(tm);
4662             version (none)
4663             {
4664                 OutBuffer buf;
4665                 buf.doindent = 1;
4666                 HdrGenState hgs;
4667                 hgs.hdrgen = true;
4668                 toCBuffer(s, &buf, &hgs);
4669                 printf("tm ==> s = %s\n", buf.peekChars());
4670             }
4671             auto a = new Statements();
4672             a.push(s);
4673             return a;
4674 
4675         case STMT.Forwarding:
4676             /***********************
4677              * ForwardingStatements are distributed over the flattened
4678              * sequence of statements. This prevents flattening to be
4679              * "blocked" by a ForwardingStatement and is necessary, for
4680              * example, to support generating scope guards with `static
4681              * foreach`:
4682              *
4683              *     static foreach(i; 0 .. 10) scope(exit) writeln(i);
4684              *     writeln("this is printed first");
4685              *     // then, it prints 10, 9, 8, 7, ...
4686              */
4687             auto fs = statement.isForwardingStatement();
4688             if (!fs.statement)
4689             {
4690                 return null;
4691             }
4692             sc = sc.push(fs.sym);
4693             auto a = fs.statement.flatten(sc);
4694             sc = sc.pop();
4695             if (!a)
4696             {
4697                 return a;
4698             }
4699             auto b = new Statements(a.dim);
4700             foreach (i, s; *a)
4701             {
4702                 (*b)[i] = s ? new ForwardingStatement(s.loc, fs.sym, s) : null;
4703             }
4704             return b;
4705 
4706         case STMT.Conditional:
4707             auto cs = statement.isConditionalStatement();
4708             Statement s;
4709 
4710             //printf("ConditionalStatement::flatten()\n");
4711             if (cs.condition.include(sc))
4712             {
4713                 DebugCondition dc = cs.condition.isDebugCondition();
4714                 if (dc)
4715                 {
4716                     s = new DebugStatement(cs.loc, cs.ifbody);
4717                     debugThrowWalker(cs.ifbody);
4718                 }
4719                 else
4720                     s = cs.ifbody;
4721             }
4722             else
4723                 s = cs.elsebody;
4724 
4725             auto a = new Statements();
4726             a.push(s);
4727             return a;
4728 
4729         case STMT.StaticForeach:
4730             auto sfs = statement.isStaticForeachStatement();
4731             sfs.sfe.prepare(sc);
4732             if (sfs.sfe.ready())
4733             {
4734                 auto s = makeTupleForeach!(true, false)(sc, sfs.sfe.aggrfe, sfs.sfe.needExpansion);
4735                 auto result = s.flatten(sc);
4736                 if (result)
4737                 {
4738                     return result;
4739                 }
4740                 result = new Statements();
4741                 result.push(s);
4742                 return result;
4743             }
4744             else
4745                 return errorStatements();
4746 
4747         case STMT.Debug:
4748             auto ds = statement.isDebugStatement();
4749             Statements* a = ds.statement ? ds.statement.flatten(sc) : null;
4750             if (!a)
4751                 return null;
4752 
4753             foreach (ref s; *a)
4754             {
4755                 s = new DebugStatement(ds.loc, s);
4756             }
4757             return a;
4758 
4759         case STMT.Label:
4760             auto ls = statement.isLabelStatement();
4761             if (!ls.statement)
4762                 return null;
4763 
4764             Statements* a = null;
4765             a = ls.statement.flatten(sc);
4766             if (!a)
4767                 return null;
4768 
4769             if (!a.dim)
4770             {
4771                 a.push(new ExpStatement(ls.loc, cast(Expression)null));
4772             }
4773 
4774             // reuse 'this' LabelStatement
4775             ls.statement = (*a)[0];
4776             (*a)[0] = ls;
4777             return a;
4778 
4779         case STMT.Compile:
4780             auto cs = statement.isCompileStatement();
4781 
4782 
4783             OutBuffer buf;
4784             if (expressionsToString(buf, sc, cs.exps))
4785                 return errorStatements();
4786 
4787             const errors = global.errors;
4788             const len = buf.length;
4789             buf.writeByte(0);
4790             const str = buf.extractSlice()[0 .. len];
4791             scope p = new Parser!ASTCodegen(cs.loc, sc._module, str, false);
4792             p.nextToken();
4793 
4794             auto a = new Statements();
4795             while (p.token.value != TOK.endOfFile)
4796             {
4797                 Statement s = p.parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope);
4798                 if (!s || global.errors != errors)
4799                     return errorStatements();
4800                 a.push(s);
4801             }
4802             return a;
4803         default:
4804             return null;
4805     }
4806 }
4807 
4808 /***********************************************************
4809  * Convert TemplateMixin members (== Dsymbols) to Statements.
4810  */
toStatement(Dsymbol s)4811 private Statement toStatement(Dsymbol s)
4812 {
4813     extern (C++) final class ToStmt : Visitor
4814     {
4815         alias visit = Visitor.visit;
4816     public:
4817         Statement result;
4818 
4819         Statement visitMembers(Loc loc, Dsymbols* a)
4820         {
4821             if (!a)
4822                 return null;
4823 
4824             auto statements = new Statements();
4825             foreach (s; *a)
4826             {
4827                 statements.push(toStatement(s));
4828             }
4829             return new CompoundStatement(loc, statements);
4830         }
4831 
4832         override void visit(Dsymbol s)
4833         {
4834             .error(Loc.initial, "Internal Compiler Error: cannot mixin %s `%s`\n", s.kind(), s.toChars());
4835             result = new ErrorStatement();
4836         }
4837 
4838         override void visit(TemplateMixin tm)
4839         {
4840             auto a = new Statements();
4841             foreach (m; *tm.members)
4842             {
4843                 Statement s = toStatement(m);
4844                 if (s)
4845                     a.push(s);
4846             }
4847             result = new CompoundStatement(tm.loc, a);
4848         }
4849 
4850         /* An actual declaration symbol will be converted to DeclarationExp
4851          * with ExpStatement.
4852          */
4853         Statement declStmt(Dsymbol s)
4854         {
4855             auto de = new DeclarationExp(s.loc, s);
4856             de.type = Type.tvoid; // avoid repeated semantic
4857             return new ExpStatement(s.loc, de);
4858         }
4859 
4860         override void visit(VarDeclaration d)
4861         {
4862             result = declStmt(d);
4863         }
4864 
4865         override void visit(AggregateDeclaration d)
4866         {
4867             result = declStmt(d);
4868         }
4869 
4870         override void visit(FuncDeclaration d)
4871         {
4872             result = declStmt(d);
4873         }
4874 
4875         override void visit(EnumDeclaration d)
4876         {
4877             result = declStmt(d);
4878         }
4879 
4880         override void visit(AliasDeclaration d)
4881         {
4882             result = declStmt(d);
4883         }
4884 
4885         override void visit(TemplateDeclaration d)
4886         {
4887             result = declStmt(d);
4888         }
4889 
4890         /* All attributes have been already picked by the semantic analysis of
4891          * 'bottom' declarations (function, struct, class, etc).
4892          * So we don't have to copy them.
4893          */
4894         override void visit(StorageClassDeclaration d)
4895         {
4896             result = visitMembers(d.loc, d.decl);
4897         }
4898 
4899         override void visit(DeprecatedDeclaration d)
4900         {
4901             result = visitMembers(d.loc, d.decl);
4902         }
4903 
4904         override void visit(LinkDeclaration d)
4905         {
4906             result = visitMembers(d.loc, d.decl);
4907         }
4908 
4909         override void visit(VisibilityDeclaration d)
4910         {
4911             result = visitMembers(d.loc, d.decl);
4912         }
4913 
4914         override void visit(AlignDeclaration d)
4915         {
4916             result = visitMembers(d.loc, d.decl);
4917         }
4918 
4919         override void visit(UserAttributeDeclaration d)
4920         {
4921             result = visitMembers(d.loc, d.decl);
4922         }
4923 
4924         override void visit(ForwardingAttribDeclaration d)
4925         {
4926             result = visitMembers(d.loc, d.decl);
4927         }
4928 
4929         override void visit(StaticAssert s)
4930         {
4931         }
4932 
4933         override void visit(Import s)
4934         {
4935         }
4936 
4937         override void visit(PragmaDeclaration d)
4938         {
4939         }
4940 
4941         override void visit(ConditionalDeclaration d)
4942         {
4943             result = visitMembers(d.loc, d.include(null));
4944         }
4945 
4946         override void visit(StaticForeachDeclaration d)
4947         {
4948             assert(d.sfe && !!d.sfe.aggrfe ^ !!d.sfe.rangefe);
4949             result = visitMembers(d.loc, d.include(null));
4950         }
4951 
4952         override void visit(CompileDeclaration d)
4953         {
4954             result = visitMembers(d.loc, d.include(null));
4955         }
4956     }
4957 
4958     if (!s)
4959         return null;
4960 
4961     scope ToStmt v = new ToStmt();
4962     s.accept(v);
4963     return v.result;
4964 }
4965 
4966 /**
4967 Marks all occurring ThrowStatements as internalThrows.
4968 This is intended to be called from a DebugStatement as it allows
4969 to mark all its nodes as nothrow.
4970 
4971 Params:
4972     s = AST Node to traverse
4973 */
debugThrowWalker(Statement s)4974 private void debugThrowWalker(Statement s)
4975 {
4976 
4977     extern(C++) final class DebugWalker : SemanticTimeTransitiveVisitor
4978     {
4979         alias visit = SemanticTimeTransitiveVisitor.visit;
4980     public:
4981 
4982         override void visit(ThrowStatement s)
4983         {
4984             s.internalThrow = true;
4985         }
4986 
4987         override void visit(CallExp s)
4988         {
4989             s.inDebugStatement = true;
4990         }
4991     }
4992 
4993     scope walker = new DebugWalker();
4994     s.accept(walker);
4995 }
4996