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