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