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