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