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