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