1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(DMDSRC _statementsem.d)
9  */
10 
11 module ddmd.statementsem;
12 
13 import core.stdc.stdio;
14 
15 import ddmd.aggregate;
16 import ddmd.aliasthis;
17 import ddmd.arrayop;
18 import ddmd.arraytypes;
19 import ddmd.clone;
20 import ddmd.cond;
21 import ddmd.dcast;
22 import ddmd.dclass;
23 import ddmd.declaration;
24 import ddmd.denum;
25 import ddmd.dimport;
26 import ddmd.dinterpret;
27 import ddmd.dmodule;
28 import ddmd.dscope;
29 import ddmd.dsymbol;
30 import ddmd.dtemplate;
31 import ddmd.errors;
32 import ddmd.escape;
33 import ddmd.expression;
34 import ddmd.func;
35 import ddmd.globals;
36 import ddmd.gluelayer;
37 import ddmd.id;
38 import ddmd.identifier;
39 import ddmd.init;
40 import ddmd.intrange;
41 import ddmd.mtype;
42 import ddmd.nogc;
43 import ddmd.opover;
44 import ddmd.sideeffect;
45 import ddmd.statement;
46 import ddmd.target;
47 import ddmd.tokens;
48 import ddmd.visitor;
49 
50 private extern (C++) final class StatementSemanticVisitor : Visitor
51 {
52     alias visit = super.visit;
53 
54     Statement result;
55     Scope* sc;
56 
57     this(Scope* sc)
58     {
59         this.sc = sc;
60     }
61 
62     private void setError()
63     {
64         result = new ErrorStatement();
65     }
66 
67     override void visit(Statement s)
68     {
69         result = s;
70     }
71 
72     override void visit(ErrorStatement s)
73     {
74         result = s;
75     }
76 
77     override void visit(PeelStatement s)
78     {
79         /* "peel" off this wrapper, and don't run semantic()
80          * on the result.
81          */
82         result = s.s;
83     }
84 
85     override void visit(ExpStatement s)
86     {
87         if (s.exp)
88         {
89             //printf("ExpStatement::semantic() %s\n", exp.toChars());
90 
91             // Allow CommaExp in ExpStatement because return isn't used
92             CommaExp.allow(s.exp);
93 
94             s.exp = s.exp.semantic(sc);
95             s.exp = resolveProperties(sc, s.exp);
96             s.exp = s.exp.addDtorHook(sc);
97             if (checkNonAssignmentArrayOp(s.exp))
98                 s.exp = new ErrorExp();
99             if (auto f = isFuncAddress(s.exp))
100             {
101                 if (f.checkForwardRef(s.exp.loc))
102                     s.exp = new ErrorExp();
103             }
104             discardValue(s.exp);
105 
106             s.exp = s.exp.optimize(WANTvalue);
107             s.exp = checkGC(sc, s.exp);
108             if (s.exp.op == TOKerror)
109                 return setError();
110         }
111         result = s;
112     }
113 
114     override void visit(CompileStatement cs)
115     {
116         //printf("CompileStatement::semantic() %s\n", exp->toChars());
117         Statements* a = cs.flatten(sc);
118         if (!a)
119             return;
120         Statement s = new CompoundStatement(cs.loc, a);
121         result = s.semantic(sc);
122     }
123 
124     override void visit(CompoundStatement cs)
125     {
126         //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc);
127         version (none)
128         {
129             foreach (i, s; cs.statements)
130             {
131                 if (s)
132                     printf("[%d]: %s", i, s.toChars());
133             }
134         }
135 
136         for (size_t i = 0; i < cs.statements.dim;)
137         {
138             Statement s = (*cs.statements)[i];
139             if (s)
140             {
141                 Statements* flt = s.flatten(sc);
142                 if (flt)
143                 {
144                     cs.statements.remove(i);
145                     cs.statements.insert(i, flt);
146                     continue;
147                 }
148                 s = s.semantic(sc);
149                 (*cs.statements)[i] = s;
150                 if (s)
151                 {
152                     Statement sentry;
153                     Statement sexception;
154                     Statement sfinally;
155 
156                     (*cs.statements)[i] = s.scopeCode(sc, &sentry, &sexception, &sfinally);
157                     if (sentry)
158                     {
159                         sentry = sentry.semantic(sc);
160                         cs.statements.insert(i, sentry);
161                         i++;
162                     }
163                     if (sexception)
164                         sexception = sexception.semantic(sc);
165                     if (sexception)
166                     {
167                         if (i + 1 == cs.statements.dim && !sfinally)
168                         {
169                         }
170                         else
171                         {
172                             /* Rewrite:
173                              *      s; s1; s2;
174                              * As:
175                              *      s;
176                              *      try { s1; s2; }
177                              *      catch (Throwable __o)
178                              *      { sexception; throw __o; }
179                              */
180                             auto a = new Statements();
181                             foreach (j; i + 1 .. cs.statements.dim)
182                             {
183                                 a.push((*cs.statements)[j]);
184                             }
185                             Statement _body = new CompoundStatement(Loc(), a);
186                             _body = new ScopeStatement(Loc(), _body, Loc());
187 
188                             Identifier id = Identifier.generateId("__o");
189 
190                             Statement handler = new PeelStatement(sexception);
191                             if (sexception.blockExit(sc.func, false) & BEfallthru)
192                             {
193                                 auto ts = new ThrowStatement(Loc(), new IdentifierExp(Loc(), id));
194                                 ts.internalThrow = true;
195                                 handler = new CompoundStatement(Loc(), handler, ts);
196                             }
197 
198                             auto catches = new Catches();
199                             auto ctch = new Catch(Loc(), getThrowable(), id, handler);
200                             ctch.internalCatch = true;
201                             catches.push(ctch);
202 
203                             s = new TryCatchStatement(Loc(), _body, catches);
204                             if (sfinally)
205                                 s = new TryFinallyStatement(Loc(), s, sfinally);
206                             s = s.semantic(sc);
207 
208                             cs.statements.setDim(i + 1);
209                             cs.statements.push(s);
210                             break;
211                         }
212                     }
213                     else if (sfinally)
214                     {
215                         if (0 && i + 1 == cs.statements.dim)
216                         {
217                             cs.statements.push(sfinally);
218                         }
219                         else
220                         {
221                             /* Rewrite:
222                              *      s; s1; s2;
223                              * As:
224                              *      s; try { s1; s2; } finally { sfinally; }
225                              */
226                             auto a = new Statements();
227                             foreach (j; i + 1 .. cs.statements.dim)
228                             {
229                                 a.push((*cs.statements)[j]);
230                             }
231                             Statement _body = new CompoundStatement(Loc(), a);
232                             s = new TryFinallyStatement(Loc(), _body, sfinally);
233                             s = s.semantic(sc);
234                             cs.statements.setDim(i + 1);
235                             cs.statements.push(s);
236                             break;
237                         }
238                     }
239                 }
240                 else
241                 {
242                     /* Remove NULL statements from the list.
243                      */
244                     cs.statements.remove(i);
245                     continue;
246                 }
247             }
248             i++;
249         }
250         foreach (i; 0 .. cs.statements.dim)
251         {
252         Lagain:
253             Statement s = (*cs.statements)[i];
254             if (!s)
255                 continue;
256 
257             Statement se = s.isErrorStatement();
258             if (se)
259             {
260                 result = se;
261                 return;
262             }
263 
264             /* Bugzilla 11653: 'semantic' may return another CompoundStatement
265              * (eg. CaseRangeStatement), so flatten it here.
266              */
267             Statements* flt = s.flatten(sc);
268             if (flt)
269             {
270                 cs.statements.remove(i);
271                 cs.statements.insert(i, flt);
272                 if (cs.statements.dim <= i)
273                     break;
274                 goto Lagain;
275             }
276         }
277         if (cs.statements.dim == 1)
278         {
279             result = (*cs.statements)[0];
280             return;
281         }
282         result = cs;
283     }
284 
285     override void visit(UnrolledLoopStatement uls)
286     {
287         //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc);
288         Scope* scd = sc.push();
289         scd.sbreak = uls;
290         scd.scontinue = uls;
291 
292         Statement serror = null;
293         foreach (i, ref s; *uls.statements)
294         {
295             if (s)
296             {
297                 //printf("[%d]: %s\n", i, s->toChars());
298                 s = s.semantic(scd);
299                 if (s && !serror)
300                     serror = s.isErrorStatement();
301             }
302         }
303 
304         scd.pop();
305         result = serror ? serror : uls;
306     }
307 
308     override void visit(ScopeStatement ss)
309     {
310         ScopeDsymbol sym;
311         //printf("ScopeStatement::semantic(sc = %p)\n", sc);
312         if (ss.statement)
313         {
314             sym = new ScopeDsymbol();
315             sym.parent = sc.scopesym;
316             sym.endlinnum = ss.endloc.linnum;
317             sc = sc.push(sym);
318 
319             Statements* a = ss.statement.flatten(sc);
320             if (a)
321             {
322                 ss.statement = new CompoundStatement(ss.loc, a);
323             }
324 
325             ss.statement = ss.statement.semantic(sc);
326             if (ss.statement)
327             {
328                 if (ss.statement.isErrorStatement())
329                 {
330                     sc.pop();
331                     result = ss.statement;
332                     return;
333                 }
334 
335                 Statement sentry;
336                 Statement sexception;
337                 Statement sfinally;
338 
339                 ss.statement = ss.statement.scopeCode(sc, &sentry, &sexception, &sfinally);
340                 assert(!sentry);
341                 assert(!sexception);
342                 if (sfinally)
343                 {
344                     //printf("adding sfinally\n");
345                     sfinally = sfinally.semantic(sc);
346                     ss.statement = new CompoundStatement(ss.loc, ss.statement, sfinally);
347                 }
348             }
349 
350             sc.pop();
351         }
352         result = ss;
353     }
354 
355     override void visit(WhileStatement ws)
356     {
357         /* Rewrite as a for(;condition;) loop
358          */
359         Statement s = new ForStatement(ws.loc, null, ws.condition, null, ws._body, ws.endloc);
360         s = s.semantic(sc);
361         result = s;
362     }
363 
364     override void visit(DoStatement ds)
365     {
366         sc.noctor++;
367         if (ds._body)
368             ds._body = ds._body.semanticScope(sc, ds, ds);
369         sc.noctor--;
370 
371         if (ds.condition.op == TOKdotid)
372             (cast(DotIdExp)ds.condition).noderef = true;
373 
374         // check in syntax level
375         ds.condition = checkAssignmentAsCondition(ds.condition);
376 
377         ds.condition = ds.condition.semantic(sc);
378         ds.condition = resolveProperties(sc, ds.condition);
379         if (checkNonAssignmentArrayOp(ds.condition))
380             ds.condition = new ErrorExp();
381         ds.condition = ds.condition.optimize(WANTvalue);
382         ds.condition = checkGC(sc, ds.condition);
383 
384         ds.condition = ds.condition.toBoolean(sc);
385 
386         if (ds.condition.op == TOKerror)
387             return setError();
388         if (ds._body && ds._body.isErrorStatement())
389         {
390             result = ds._body;
391             return;
392         }
393 
394         result = ds;
395     }
396 
397     override void visit(ForStatement fs)
398     {
399         //printf("ForStatement::semantic %s\n", fs.toChars());
400 
401         if (fs._init)
402         {
403             /* Rewrite:
404              *  for (auto v1 = i1, v2 = i2; condition; increment) { ... }
405              * to:
406              *  { auto v1 = i1, v2 = i2; for (; condition; increment) { ... } }
407              * then lowered to:
408              *  auto v1 = i1;
409              *  try {
410              *    auto v2 = i2;
411              *    try {
412              *      for (; condition; increment) { ... }
413              *    } finally { v2.~this(); }
414              *  } finally { v1.~this(); }
415              */
416             auto ainit = new Statements();
417             ainit.push(fs._init);
418             fs._init = null;
419             ainit.push(fs);
420             Statement s = new CompoundStatement(fs.loc, ainit);
421             s = new ScopeStatement(fs.loc, s, fs.endloc);
422             s = s.semantic(sc);
423             if (!s.isErrorStatement())
424             {
425                 if (LabelStatement ls = checkLabeledLoop(sc, fs))
426                     ls.gotoTarget = fs;
427                 fs.relatedLabeled = s;
428             }
429             result = s;
430             return;
431         }
432         assert(fs._init is null);
433 
434         auto sym = new ScopeDsymbol();
435         sym.parent = sc.scopesym;
436         sym.endlinnum = fs.endloc.linnum;
437         sc = sc.push(sym);
438 
439         sc.noctor++;
440         if (fs.condition)
441         {
442             if (fs.condition.op == TOKdotid)
443                 (cast(DotIdExp)fs.condition).noderef = true;
444 
445             // check in syntax level
446             fs.condition = checkAssignmentAsCondition(fs.condition);
447 
448             fs.condition = fs.condition.semantic(sc);
449             fs.condition = resolveProperties(sc, fs.condition);
450             if (checkNonAssignmentArrayOp(fs.condition))
451                 fs.condition = new ErrorExp();
452             fs.condition = fs.condition.optimize(WANTvalue);
453             fs.condition = checkGC(sc, fs.condition);
454 
455             fs.condition = fs.condition.toBoolean(sc);
456         }
457         if (fs.increment)
458         {
459             CommaExp.allow(fs.increment);
460             fs.increment = fs.increment.semantic(sc);
461             fs.increment = resolveProperties(sc, fs.increment);
462             if (checkNonAssignmentArrayOp(fs.increment))
463                 fs.increment = new ErrorExp();
464             fs.increment = fs.increment.optimize(WANTvalue);
465             fs.increment = checkGC(sc, fs.increment);
466         }
467 
468         sc.sbreak = fs;
469         sc.scontinue = fs;
470         if (fs._body)
471             fs._body = fs._body.semanticNoScope(sc);
472         sc.noctor--;
473 
474         sc.pop();
475 
476         if (fs.condition && fs.condition.op == TOKerror ||
477             fs.increment && fs.increment.op == TOKerror ||
478             fs._body && fs._body.isErrorStatement())
479             return setError();
480         result = fs;
481     }
482 
483     override void visit(ForeachStatement fs)
484     {
485         //printf("ForeachStatement::semantic() %p\n", fs);
486         ScopeDsymbol sym;
487         Statement s = fs;
488         auto loc = fs.loc;
489         size_t dim = fs.parameters.dim;
490         TypeAArray taa = null;
491         Dsymbol sapply = null;
492 
493         Type tn = null;
494         Type tnv = null;
495 
496         fs.func = sc.func;
497         if (fs.func.fes)
498             fs.func = fs.func.fes.func;
499 
500         VarDeclaration vinit = null;
501         fs.aggr = fs.aggr.semantic(sc);
502         fs.aggr = resolveProperties(sc, fs.aggr);
503         fs.aggr = fs.aggr.optimize(WANTvalue);
504         if (fs.aggr.op == TOKerror)
505             return setError();
506         Expression oaggr = fs.aggr;
507         if (fs.aggr.type && fs.aggr.type.toBasetype().ty == Tstruct &&
508             fs.aggr.op != TOKtype && !fs.aggr.isLvalue())
509         {
510             // Bugzilla 14653: Extend the life of rvalue aggregate till the end of foreach.
511             vinit = copyToTemp(STCrvalue, "__aggr", fs.aggr);
512             vinit.endlinnum = fs.endloc.linnum;
513             vinit.semantic(sc);
514             fs.aggr = new VarExp(fs.aggr.loc, vinit);
515         }
516 
517         if (!inferAggregate(fs, sc, sapply))
518         {
519             const(char)* msg = "";
520             if (fs.aggr.type && isAggregate(fs.aggr.type))
521             {
522                 msg = ", define opApply(), range primitives, or use .tupleof";
523             }
524             fs.error("invalid foreach aggregate %s%s", oaggr.toChars(), msg);
525             return setError();
526         }
527 
528         Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
529 
530         /* Check for inference errors
531          */
532         if (!inferApplyArgTypes(fs, sc, sapply))
533         {
534             /**
535              Try and extract the parameter count of the opApply callback function, e.g.:
536              int opApply(int delegate(int, float)) => 2 args
537              */
538             bool foundMismatch = false;
539             size_t foreachParamCount = 0;
540             if (sapplyOld)
541             {
542                 if (FuncDeclaration fd = sapplyOld.isFuncDeclaration())
543                 {
544                     int fvarargs; // ignored (opApply shouldn't take variadics)
545                     Parameters* fparameters = fd.getParameters(&fvarargs);
546 
547                     if (Parameter.dim(fparameters) == 1)
548                     {
549                         // first param should be the callback function
550                         Parameter fparam = Parameter.getNth(fparameters, 0);
551                         if ((fparam.type.ty == Tpointer ||
552                              fparam.type.ty == Tdelegate) &&
553                             fparam.type.nextOf().ty == Tfunction)
554                         {
555                             TypeFunction tf = cast(TypeFunction)fparam.type.nextOf();
556                             foreachParamCount = Parameter.dim(tf.parameters);
557                             foundMismatch = true;
558                         }
559                     }
560                 }
561             }
562 
563             //printf("dim = %d, parameters->dim = %d\n", dim, parameters->dim);
564             if (foundMismatch && dim != foreachParamCount)
565             {
566                 const(char)* plural = foreachParamCount > 1 ? "s" : "";
567                 fs.error("cannot infer argument types, expected %d argument%s, not %d",
568                     foreachParamCount, plural, dim);
569             }
570             else
571                 fs.error("cannot uniquely infer foreach argument types");
572 
573             return setError();
574         }
575 
576         Type tab = fs.aggr.type.toBasetype();
577 
578         if (tab.ty == Ttuple) // don't generate new scope for tuple loops
579         {
580             if (dim < 1 || dim > 2)
581             {
582                 fs.error("only one (value) or two (key,value) arguments for tuple foreach");
583                 return setError();
584             }
585 
586             Type paramtype = (*fs.parameters)[dim - 1].type;
587             if (paramtype)
588             {
589                 paramtype = paramtype.semantic(loc, sc);
590                 if (paramtype.ty == Terror)
591                     return setError();
592             }
593 
594             TypeTuple tuple = cast(TypeTuple)tab;
595             auto statements = new Statements();
596             //printf("aggr: op = %d, %s\n", fs.aggr->op, fs.aggr->toChars());
597             size_t n;
598             TupleExp te = null;
599             if (fs.aggr.op == TOKtuple) // expression tuple
600             {
601                 te = cast(TupleExp)fs.aggr;
602                 n = te.exps.dim;
603             }
604             else if (fs.aggr.op == TOKtype) // type tuple
605             {
606                 n = Parameter.dim(tuple.arguments);
607             }
608             else
609                 assert(0);
610             foreach (j; 0 .. n)
611             {
612                 size_t k = (fs.op == TOKforeach) ? j : n - 1 - j;
613                 Expression e = null;
614                 Type t = null;
615                 if (te)
616                     e = (*te.exps)[k];
617                 else
618                     t = Parameter.getNth(tuple.arguments, k).type;
619                 Parameter p = (*fs.parameters)[0];
620                 auto st = new Statements();
621 
622                 if (dim == 2)
623                 {
624                     // Declare key
625                     if (p.storageClass & (STCout | STCref | STClazy))
626                     {
627                         fs.error("no storage class for key %s", p.ident.toChars());
628                         return setError();
629                     }
630                     p.type = p.type.semantic(loc, sc);
631                     TY keyty = p.type.ty;
632                     if (keyty != Tint32 && keyty != Tuns32)
633                     {
634                         if (global.params.isLP64)
635                         {
636                             if (keyty != Tint64 && keyty != Tuns64)
637                             {
638                                 fs.error("foreach: key type must be int or uint, long or ulong, not %s", p.type.toChars());
639                                 return setError();
640                             }
641                         }
642                         else
643                         {
644                             fs.error("foreach: key type must be int or uint, not %s", p.type.toChars());
645                             return setError();
646                         }
647                     }
648                     Initializer ie = new ExpInitializer(Loc(), new IntegerExp(k));
649                     auto var = new VarDeclaration(loc, p.type, p.ident, ie);
650                     var.storage_class |= STCmanifest;
651                     st.push(new ExpStatement(loc, var));
652                     p = (*fs.parameters)[1]; // value
653                 }
654                 // Declare value
655                 if (p.storageClass & (STCout | STClazy) ||
656                     p.storageClass & STCref && !te)
657                 {
658                     fs.error("no storage class for value %s", p.ident.toChars());
659                     return setError();
660                 }
661                 Dsymbol var;
662                 if (te)
663                 {
664                     Type tb = e.type.toBasetype();
665                     Dsymbol ds = null;
666                     if ((tb.ty == Tfunction || tb.ty == Tsarray) && e.op == TOKvar)
667                         ds = (cast(VarExp)e).var;
668                     else if (e.op == TOKtemplate)
669                         ds = (cast(TemplateExp)e).td;
670                     else if (e.op == TOKscope)
671                         ds = (cast(ScopeExp)e).sds;
672                     else if (e.op == TOKfunction)
673                     {
674                         auto fe = cast(FuncExp)e;
675                         ds = fe.td ? cast(Dsymbol)fe.td : fe.fd;
676                     }
677 
678                     if (ds)
679                     {
680                         var = new AliasDeclaration(loc, p.ident, ds);
681                         if (p.storageClass & STCref)
682                         {
683                             fs.error("symbol %s cannot be ref", s.toChars());
684                             return setError();
685                         }
686                         if (paramtype)
687                         {
688                             fs.error("cannot specify element type for symbol %s", ds.toChars());
689                             return setError();
690                         }
691                     }
692                     else if (e.op == TOKtype)
693                     {
694                         var = new AliasDeclaration(loc, p.ident, e.type);
695                         if (paramtype)
696                         {
697                             fs.error("cannot specify element type for type %s", e.type.toChars());
698                             return setError();
699                         }
700                     }
701                     else
702                     {
703                         p.type = e.type;
704                         if (paramtype)
705                             p.type = paramtype;
706                         Initializer ie = new ExpInitializer(Loc(), e);
707                         auto v = new VarDeclaration(loc, p.type, p.ident, ie);
708                         if (p.storageClass & STCref)
709                             v.storage_class |= STCref | STCforeach;
710                         if (e.isConst() ||
711                             e.op == TOKstring ||
712                             e.op == TOKstructliteral ||
713                             e.op == TOKarrayliteral)
714                         {
715                             if (v.storage_class & STCref)
716                             {
717                                 fs.error("constant value %s cannot be ref", ie.toChars());
718                                 return setError();
719                             }
720                             else
721                                 v.storage_class |= STCmanifest;
722                         }
723                         var = v;
724                     }
725                 }
726                 else
727                 {
728                     var = new AliasDeclaration(loc, p.ident, t);
729                     if (paramtype)
730                     {
731                         fs.error("cannot specify element type for symbol %s", s.toChars());
732                         return setError();
733                     }
734                 }
735                 st.push(new ExpStatement(loc, var));
736 
737                 st.push(fs._body.syntaxCopy());
738                 s = new CompoundStatement(loc, st);
739                 s = new ScopeStatement(loc, s, fs.endloc);
740                 statements.push(s);
741             }
742 
743             s = new UnrolledLoopStatement(loc, statements);
744             if (LabelStatement ls = checkLabeledLoop(sc, fs))
745                 ls.gotoTarget = s;
746             if (te && te.e0)
747                 s = new CompoundStatement(loc, new ExpStatement(te.e0.loc, te.e0), s);
748             if (vinit)
749                 s = new CompoundStatement(loc, new ExpStatement(loc, vinit), s);
750             s = s.semantic(sc);
751             result = s;
752             return;
753         }
754 
755         sym = new ScopeDsymbol();
756         sym.parent = sc.scopesym;
757         sym.endlinnum = fs.endloc.linnum;
758         auto sc2 = sc.push(sym);
759 
760         sc2.noctor++;
761 
762         switch (tab.ty)
763         {
764         case Tarray:
765         case Tsarray:
766             {
767                 if (fs.checkForArgTypes())
768                 {
769                     result = fs;
770                     return;
771                 }
772 
773                 if (dim < 1 || dim > 2)
774                 {
775                     fs.error("only one or two arguments for array foreach");
776                     goto Lerror2;
777                 }
778 
779                 /* Look for special case of parsing char types out of char type
780                  * array.
781                  */
782                 tn = tab.nextOf().toBasetype();
783                 if (tn.ty == Tchar || tn.ty == Twchar || tn.ty == Tdchar)
784                 {
785                     int i = (dim == 1) ? 0 : 1; // index of value
786                     Parameter p = (*fs.parameters)[i];
787                     p.type = p.type.semantic(loc, sc2);
788                     p.type = p.type.addStorageClass(p.storageClass);
789                     tnv = p.type.toBasetype();
790                     if (tnv.ty != tn.ty &&
791                         (tnv.ty == Tchar || tnv.ty == Twchar || tnv.ty == Tdchar))
792                     {
793                         if (p.storageClass & STCref)
794                         {
795                             fs.error("foreach: value of UTF conversion cannot be ref");
796                             goto Lerror2;
797                         }
798                         if (dim == 2)
799                         {
800                             p = (*fs.parameters)[0];
801                             if (p.storageClass & STCref)
802                             {
803                                 fs.error("foreach: key cannot be ref");
804                                 goto Lerror2;
805                             }
806                         }
807                         goto Lapply;
808                     }
809                 }
810 
811                 foreach (i; 0 .. dim)
812                 {
813                     // Declare parameterss
814                     Parameter p = (*fs.parameters)[i];
815                     p.type = p.type.semantic(loc, sc2);
816                     p.type = p.type.addStorageClass(p.storageClass);
817                     VarDeclaration var;
818 
819                     if (dim == 2 && i == 0)
820                     {
821                         var = new VarDeclaration(loc, p.type.mutableOf(), Identifier.generateId("__key"), null);
822                         var.storage_class |= STCtemp | STCforeach;
823                         if (var.storage_class & (STCref | STCout))
824                             var.storage_class |= STCnodtor;
825 
826                         fs.key = var;
827                         if (p.storageClass & STCref)
828                         {
829                             if (var.type.constConv(p.type) <= MATCHnomatch)
830                             {
831                                 fs.error("key type mismatch, %s to ref %s",
832                                     var.type.toChars(), p.type.toChars());
833                                 goto Lerror2;
834                             }
835                         }
836                         if (tab.ty == Tsarray)
837                         {
838                             TypeSArray ta = cast(TypeSArray)tab;
839                             IntRange dimrange = getIntRange(ta.dim);
840                             if (!IntRange.fromType(var.type).contains(dimrange))
841                             {
842                                 fs.error("index type '%s' cannot cover index range 0..%llu",
843                                     p.type.toChars(), ta.dim.toInteger());
844                                 goto Lerror2;
845                             }
846                             fs.key.range = new IntRange(SignExtendedNumber(0), dimrange.imax);
847                         }
848                     }
849                     else
850                     {
851                         var = new VarDeclaration(loc, p.type, p.ident, null);
852                         var.storage_class |= STCforeach;
853                         var.storage_class |= p.storageClass & (STCin | STCout | STCref | STC_TYPECTOR);
854                         if (var.storage_class & (STCref | STCout))
855                             var.storage_class |= STCnodtor;
856 
857                         fs.value = var;
858                         if (var.storage_class & STCref)
859                         {
860                             if (fs.aggr.checkModifiable(sc2, 1) == 2)
861                                 var.storage_class |= STCctorinit;
862 
863                             Type t = tab.nextOf();
864                             if (t.constConv(p.type) <= MATCHnomatch)
865                             {
866                                 fs.error("argument type mismatch, %s to ref %s",
867                                     t.toChars(), p.type.toChars());
868                                 goto Lerror2;
869                             }
870                         }
871                     }
872                 }
873 
874                 /* Convert to a ForStatement
875                  *   foreach (key, value; a) body =>
876                  *   for (T[] tmp = a[], size_t key; key < tmp.length; ++key)
877                  *   { T value = tmp[k]; body }
878                  *
879                  *   foreach_reverse (key, value; a) body =>
880                  *   for (T[] tmp = a[], size_t key = tmp.length; key--; )
881                  *   { T value = tmp[k]; body }
882                  */
883                 auto id = Identifier.generateId("__r");
884                 auto ie = new ExpInitializer(loc, new SliceExp(loc, fs.aggr, null, null));
885                 VarDeclaration tmp;
886                 if (fs.aggr.op == TOKarrayliteral &&
887                     !((*fs.parameters)[dim - 1].storageClass & STCref))
888                 {
889                     auto ale = cast(ArrayLiteralExp)fs.aggr;
890                     size_t edim = ale.elements ? ale.elements.dim : 0;
891                     auto telem = (*fs.parameters)[dim - 1].type;
892 
893                     // Bugzilla 12936: if telem has been specified explicitly,
894                     // converting array literal elements to telem might make it @nogc.
895                     fs.aggr = fs.aggr.implicitCastTo(sc, telem.sarrayOf(edim));
896                     if (fs.aggr.op == TOKerror)
897                         goto Lerror2;
898 
899                     // for (T[edim] tmp = a, ...)
900                     tmp = new VarDeclaration(loc, fs.aggr.type, id, ie);
901                 }
902                 else
903                     tmp = new VarDeclaration(loc, tab.nextOf().arrayOf(), id, ie);
904                 tmp.storage_class |= STCtemp;
905 
906                 Expression tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id.length);
907 
908                 if (!fs.key)
909                 {
910                     Identifier idkey = Identifier.generateId("__key");
911                     fs.key = new VarDeclaration(loc, Type.tsize_t, idkey, null);
912                     fs.key.storage_class |= STCtemp;
913                 }
914                 if (fs.op == TOKforeach_reverse)
915                     fs.key._init = new ExpInitializer(loc, tmp_length);
916                 else
917                     fs.key._init = new ExpInitializer(loc, new IntegerExp(loc, 0, fs.key.type));
918 
919                 auto cs = new Statements();
920                 if (vinit)
921                     cs.push(new ExpStatement(loc, vinit));
922                 cs.push(new ExpStatement(loc, tmp));
923                 cs.push(new ExpStatement(loc, fs.key));
924                 Statement forinit = new CompoundDeclarationStatement(loc, cs);
925 
926                 Expression cond;
927                 if (fs.op == TOKforeach_reverse)
928                 {
929                     // key--
930                     cond = new PostExp(TOKminusminus, loc, new VarExp(loc, fs.key));
931                 }
932                 else
933                 {
934                     // key < tmp.length
935                     cond = new CmpExp(TOKlt, loc, new VarExp(loc, fs.key), tmp_length);
936                 }
937 
938                 Expression increment = null;
939                 if (fs.op == TOKforeach)
940                 {
941                     // key += 1
942                     increment = new AddAssignExp(loc, new VarExp(loc, fs.key), new IntegerExp(loc, 1, fs.key.type));
943                 }
944 
945                 // T value = tmp[key];
946                 IndexExp indexExp = new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, fs.key));
947                 indexExp.indexIsInBounds = true; // disabling bounds checking in foreach statements.
948                 fs.value._init = new ExpInitializer(loc, indexExp);
949                 Statement ds = new ExpStatement(loc, fs.value);
950 
951                 if (dim == 2)
952                 {
953                     Parameter p = (*fs.parameters)[0];
954                     if ((p.storageClass & STCref) && p.type.equals(fs.key.type))
955                     {
956                         fs.key.range = null;
957                         auto v = new AliasDeclaration(loc, p.ident, fs.key);
958                         fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
959                     }
960                     else
961                     {
962                         auto ei = new ExpInitializer(loc, new IdentifierExp(loc, fs.key.ident));
963                         auto v = new VarDeclaration(loc, p.type, p.ident, ei);
964                         v.storage_class |= STCforeach | (p.storageClass & STCref);
965                         fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
966                         if (fs.key.range && !p.type.isMutable())
967                         {
968                             /* Limit the range of the key to the specified range
969                              */
970                             v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
971                         }
972                     }
973                 }
974                 fs._body = new CompoundStatement(loc, ds, fs._body);
975 
976                 s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
977                 if (auto ls = checkLabeledLoop(sc, fs))   // Bugzilla 15450: don't use sc2
978                     ls.gotoTarget = s;
979                 s = s.semantic(sc2);
980                 break;
981             }
982         case Taarray:
983             if (fs.op == TOKforeach_reverse)
984                 fs.warning("cannot use foreach_reverse with an associative array");
985             if (fs.checkForArgTypes())
986             {
987                 result = fs;
988                 return;
989             }
990 
991             taa = cast(TypeAArray)tab;
992             if (dim < 1 || dim > 2)
993             {
994                 fs.error("only one or two arguments for associative array foreach");
995                 goto Lerror2;
996             }
997             goto Lapply;
998 
999         case Tclass:
1000         case Tstruct:
1001             /* Prefer using opApply, if it exists
1002              */
1003             if (sapply)
1004                 goto Lapply;
1005             {
1006                 /* Look for range iteration, i.e. the properties
1007                  * .empty, .popFront, .popBack, .front and .back
1008                  *    foreach (e; aggr) { ... }
1009                  * translates to:
1010                  *    for (auto __r = aggr[]; !__r.empty; __r.popFront()) {
1011                  *        auto e = __r.front;
1012                  *        ...
1013                  *    }
1014                  */
1015                 auto ad = (tab.ty == Tclass) ?
1016                     cast(AggregateDeclaration)(cast(TypeClass)tab).sym :
1017                     cast(AggregateDeclaration)(cast(TypeStruct)tab).sym;
1018                 Identifier idfront;
1019                 Identifier idpopFront;
1020                 if (fs.op == TOKforeach)
1021                 {
1022                     idfront = Id.Ffront;
1023                     idpopFront = Id.FpopFront;
1024                 }
1025                 else
1026                 {
1027                     idfront = Id.Fback;
1028                     idpopFront = Id.FpopBack;
1029                 }
1030                 auto sfront = ad.search(Loc(), idfront);
1031                 if (!sfront)
1032                     goto Lapply;
1033 
1034                 /* Generate a temporary __r and initialize it with the aggregate.
1035                  */
1036                 VarDeclaration r;
1037                 Statement _init;
1038                 if (vinit && fs.aggr.op == TOKvar && (cast(VarExp)fs.aggr).var == vinit)
1039                 {
1040                     r = vinit;
1041                     _init = new ExpStatement(loc, vinit);
1042                 }
1043                 else
1044                 {
1045                     r = copyToTemp(0, "__r", fs.aggr);
1046                     _init = new ExpStatement(loc, r);
1047                     if (vinit)
1048                         _init = new CompoundStatement(loc, new ExpStatement(loc, vinit), _init);
1049                 }
1050 
1051                 // !__r.empty
1052                 Expression e = new VarExp(loc, r);
1053                 e = new DotIdExp(loc, e, Id.Fempty);
1054                 Expression condition = new NotExp(loc, e);
1055 
1056                 // __r.idpopFront()
1057                 e = new VarExp(loc, r);
1058                 Expression increment = new CallExp(loc, new DotIdExp(loc, e, idpopFront));
1059 
1060                 /* Declaration statement for e:
1061                  *    auto e = __r.idfront;
1062                  */
1063                 e = new VarExp(loc, r);
1064                 Expression einit = new DotIdExp(loc, e, idfront);
1065                 Statement makeargs, forbody;
1066                 if (dim == 1)
1067                 {
1068                     auto p = (*fs.parameters)[0];
1069                     auto ve = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, einit));
1070                     ve.storage_class |= STCforeach;
1071                     ve.storage_class |= p.storageClass & (STCin | STCout | STCref | STC_TYPECTOR);
1072 
1073                     makeargs = new ExpStatement(loc, ve);
1074                 }
1075                 else
1076                 {
1077                     auto vd = copyToTemp(STCref, "__front", einit);
1078                     makeargs = new ExpStatement(loc, vd);
1079 
1080                     Type tfront;
1081                     if (auto fd = sfront.isFuncDeclaration())
1082                     {
1083                         if (!fd.functionSemantic())
1084                             goto Lrangeerr;
1085                         tfront = fd.type;
1086                     }
1087                     else if (auto td = sfront.isTemplateDeclaration())
1088                     {
1089                         Expressions a;
1090                         if (auto f = resolveFuncCall(loc, sc, td, null, tab, &a, 1))
1091                             tfront = f.type;
1092                     }
1093                     else if (auto d = sfront.isDeclaration())
1094                     {
1095                         tfront = d.type;
1096                     }
1097                     if (!tfront || tfront.ty == Terror)
1098                         goto Lrangeerr;
1099                     if (tfront.toBasetype().ty == Tfunction)
1100                         tfront = tfront.toBasetype().nextOf();
1101                     if (tfront.ty == Tvoid)
1102                     {
1103                         fs.error("%s.front is void and has no value", oaggr.toChars());
1104                         goto Lerror2;
1105                     }
1106 
1107                     // Resolve inout qualifier of front type
1108                     tfront = tfront.substWildTo(tab.mod);
1109 
1110                     Expression ve = new VarExp(loc, vd);
1111                     ve.type = tfront;
1112 
1113                     auto exps = new Expressions();
1114                     exps.push(ve);
1115                     int pos = 0;
1116                     while (exps.dim < dim)
1117                     {
1118                         pos = expandAliasThisTuples(exps, pos);
1119                         if (pos == -1)
1120                             break;
1121                     }
1122                     if (exps.dim != dim)
1123                     {
1124                         const(char)* plural = exps.dim > 1 ? "s" : "";
1125                         fs.error("cannot infer argument types, expected %d argument%s, not %d",
1126                             exps.dim, plural, dim);
1127                         goto Lerror2;
1128                     }
1129 
1130                     foreach (i; 0 .. dim)
1131                     {
1132                         auto p = (*fs.parameters)[i];
1133                         auto exp = (*exps)[i];
1134                         version (none)
1135                         {
1136                             printf("[%d] p = %s %s, exp = %s %s\n", i,
1137                                 p.type ? p.type.toChars() : "?", p.ident.toChars(),
1138                                 exp.type.toChars(), exp.toChars());
1139                         }
1140                         if (!p.type)
1141                             p.type = exp.type;
1142                         p.type = p.type.addStorageClass(p.storageClass).semantic(loc, sc2);
1143                         if (!exp.implicitConvTo(p.type))
1144                             goto Lrangeerr;
1145 
1146                         auto var = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, exp));
1147                         var.storage_class |= STCctfe | STCref | STCforeach;
1148                         makeargs = new CompoundStatement(loc, makeargs, new ExpStatement(loc, var));
1149                     }
1150                 }
1151 
1152                 forbody = new CompoundStatement(loc, makeargs, fs._body);
1153 
1154                 s = new ForStatement(loc, _init, condition, increment, forbody, fs.endloc);
1155                 if (auto ls = checkLabeledLoop(sc, fs))
1156                     ls.gotoTarget = s;
1157 
1158                 version (none)
1159                 {
1160                     printf("init: %s\n", _init.toChars());
1161                     printf("condition: %s\n", condition.toChars());
1162                     printf("increment: %s\n", increment.toChars());
1163                     printf("body: %s\n", forbody.toChars());
1164                 }
1165                 s = s.semantic(sc2);
1166                 break;
1167 
1168             Lrangeerr:
1169                 fs.error("cannot infer argument types");
1170                 goto Lerror2;
1171             }
1172         case Tdelegate:
1173             if (fs.op == TOKforeach_reverse)
1174                 fs.deprecation("cannot use foreach_reverse with a delegate");
1175         Lapply:
1176             {
1177                 if (fs.checkForArgTypes())
1178                 {
1179                     fs._body = fs._body.semanticNoScope(sc2);
1180                     result = fs;
1181                     return;
1182                 }
1183 
1184                 TypeFunction tfld = null;
1185                 if (sapply)
1186                 {
1187                     FuncDeclaration fdapply = sapply.isFuncDeclaration();
1188                     if (fdapply)
1189                     {
1190                         assert(fdapply.type && fdapply.type.ty == Tfunction);
1191                         tfld = cast(TypeFunction)fdapply.type.semantic(loc, sc2);
1192                         goto Lget;
1193                     }
1194                     else if (tab.ty == Tdelegate)
1195                     {
1196                         tfld = cast(TypeFunction)tab.nextOf();
1197                     Lget:
1198                         //printf("tfld = %s\n", tfld->toChars());
1199                         if (tfld.parameters.dim == 1)
1200                         {
1201                             Parameter p = Parameter.getNth(tfld.parameters, 0);
1202                             if (p.type && p.type.ty == Tdelegate)
1203                             {
1204                                 auto t = p.type.semantic(loc, sc2);
1205                                 assert(t.ty == Tdelegate);
1206                                 tfld = cast(TypeFunction)t.nextOf();
1207                             }
1208                         }
1209                     }
1210                 }
1211 
1212                 /* Turn body into the function literal:
1213                  *  int delegate(ref T param) { body }
1214                  */
1215                 auto params = new Parameters();
1216                 foreach (i; 0 .. dim)
1217                 {
1218                     Parameter p = (*fs.parameters)[i];
1219                     StorageClass stc = STCref;
1220                     Identifier id;
1221 
1222                     p.type = p.type.semantic(loc, sc2);
1223                     p.type = p.type.addStorageClass(p.storageClass);
1224                     if (tfld)
1225                     {
1226                         Parameter prm = Parameter.getNth(tfld.parameters, i);
1227                         //printf("\tprm = %s%s\n", (prm->storageClass&STCref?"ref ":""), prm->ident->toChars());
1228                         stc = prm.storageClass & STCref;
1229                         id = p.ident; // argument copy is not need.
1230                         if ((p.storageClass & STCref) != stc)
1231                         {
1232                             if (!stc)
1233                             {
1234                                 fs.error("foreach: cannot make %s ref", p.ident.toChars());
1235                                 goto Lerror2;
1236                             }
1237                             goto LcopyArg;
1238                         }
1239                     }
1240                     else if (p.storageClass & STCref)
1241                     {
1242                         // default delegate parameters are marked as ref, then
1243                         // argument copy is not need.
1244                         id = p.ident;
1245                     }
1246                     else
1247                     {
1248                         // Make a copy of the ref argument so it isn't
1249                         // a reference.
1250                     LcopyArg:
1251                         id = Identifier.generateId("__applyArg", cast(int)i);
1252 
1253                         Initializer ie = new ExpInitializer(Loc(), new IdentifierExp(Loc(), id));
1254                         auto v = new VarDeclaration(Loc(), p.type, p.ident, ie);
1255                         v.storage_class |= STCtemp;
1256                         s = new ExpStatement(Loc(), v);
1257                         fs._body = new CompoundStatement(loc, s, fs._body);
1258                     }
1259                     params.push(new Parameter(stc, p.type, id, null));
1260                 }
1261                 // Bugzilla 13840: Throwable nested function inside nothrow function is acceptable.
1262                 StorageClass stc = mergeFuncAttrs(STCsafe | STCpure | STCnogc, fs.func);
1263                 tfld = new TypeFunction(params, Type.tint32, 0, LINKd, stc);
1264                 fs.cases = new Statements();
1265                 fs.gotos = new ScopeStatements();
1266                 auto fld = new FuncLiteralDeclaration(loc, Loc(), tfld, TOKdelegate, fs);
1267                 fld.fbody = fs._body;
1268                 Expression flde = new FuncExp(loc, fld);
1269                 flde = flde.semantic(sc2);
1270                 fld.tookAddressOf = 0;
1271 
1272                 // Resolve any forward referenced goto's
1273                 foreach (i; 0 .. fs.gotos.dim)
1274                 {
1275                     GotoStatement gs = cast(GotoStatement)(*fs.gotos)[i].statement;
1276                     if (!gs.label.statement)
1277                     {
1278                         // 'Promote' it to this scope, and replace with a return
1279                         fs.cases.push(gs);
1280                         s = new ReturnStatement(Loc(), new IntegerExp(fs.cases.dim + 1));
1281                         (*fs.gotos)[i].statement = s;
1282                     }
1283                 }
1284 
1285                 Expression e = null;
1286                 Expression ec;
1287                 if (vinit)
1288                 {
1289                     e = new DeclarationExp(loc, vinit);
1290                     e = e.semantic(sc2);
1291                     if (e.op == TOKerror)
1292                         goto Lerror2;
1293                 }
1294 
1295                 if (taa)
1296                 {
1297                     // Check types
1298                     Parameter p = (*fs.parameters)[0];
1299                     bool isRef = (p.storageClass & STCref) != 0;
1300                     Type ta = p.type;
1301                     if (dim == 2)
1302                     {
1303                         Type ti = (isRef ? taa.index.addMod(MODconst) : taa.index);
1304                         if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta))
1305                         {
1306                             fs.error("foreach: index must be type %s, not %s",
1307                                 ti.toChars(), ta.toChars());
1308                             goto Lerror2;
1309                         }
1310                         p = (*fs.parameters)[1];
1311                         isRef = (p.storageClass & STCref) != 0;
1312                         ta = p.type;
1313                     }
1314                     Type taav = taa.nextOf();
1315                     if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
1316                     {
1317                         fs.error("foreach: value must be type %s, not %s",
1318                             taav.toChars(), ta.toChars());
1319                         goto Lerror2;
1320                     }
1321 
1322                     /* Call:
1323                      *  extern(C) int _aaApply(void*, in size_t, int delegate(void*))
1324                      *      _aaApply(aggr, keysize, flde)
1325                      *
1326                      *  extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
1327                      *      _aaApply2(aggr, keysize, flde)
1328                      */
1329                     static __gshared const(char)** name = ["_aaApply", "_aaApply2"];
1330                     static __gshared FuncDeclaration* fdapply = [null, null];
1331                     static __gshared TypeDelegate* fldeTy = [null, null];
1332 
1333                     ubyte i = (dim == 2 ? 1 : 0);
1334                     if (!fdapply[i])
1335                     {
1336                         params = new Parameters();
1337                         params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null));
1338                         params.push(new Parameter(STCin, Type.tsize_t, null, null));
1339                         auto dgparams = new Parameters();
1340                         dgparams.push(new Parameter(0, Type.tvoidptr, null, null));
1341                         if (dim == 2)
1342                             dgparams.push(new Parameter(0, Type.tvoidptr, null, null));
1343                         fldeTy[i] = new TypeDelegate(new TypeFunction(dgparams, Type.tint32, 0, LINKd));
1344                         params.push(new Parameter(0, fldeTy[i], null, null));
1345                         fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, name[i]);
1346                     }
1347 
1348                     auto exps = new Expressions();
1349                     exps.push(fs.aggr);
1350                     auto keysize = taa.index.size();
1351                     if (keysize == SIZE_INVALID)
1352                         goto Lerror2;
1353                     assert(keysize < keysize.max - Target.ptrsize);
1354                     keysize = (keysize + (Target.ptrsize - 1)) & ~(Target.ptrsize - 1);
1355                     // paint delegate argument to the type runtime expects
1356                     if (!fldeTy[i].equals(flde.type))
1357                     {
1358                         flde = new CastExp(loc, flde, flde.type);
1359                         flde.type = fldeTy[i];
1360                     }
1361                     exps.push(new IntegerExp(Loc(), keysize, Type.tsize_t));
1362                     exps.push(flde);
1363                     ec = new VarExp(Loc(), fdapply[i], false);
1364                     ec = new CallExp(loc, ec, exps);
1365                     ec.type = Type.tint32; // don't run semantic() on ec
1366                 }
1367                 else if (tab.ty == Tarray || tab.ty == Tsarray)
1368                 {
1369                     /* Call:
1370                      *      _aApply(aggr, flde)
1371                      */
1372                     static __gshared const(char)** fntab =
1373                     [
1374                         "cc", "cw", "cd",
1375                         "wc", "cc", "wd",
1376                         "dc", "dw", "dd"
1377                     ];
1378 
1379                     const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1;
1380                     char[BUFFER_LEN] fdname;
1381                     int flag;
1382 
1383                     switch (tn.ty)
1384                     {
1385                     case Tchar:     flag = 0;   break;
1386                     case Twchar:    flag = 3;   break;
1387                     case Tdchar:    flag = 6;   break;
1388                     default:
1389                         assert(0);
1390                     }
1391                     switch (tnv.ty)
1392                     {
1393                     case Tchar:     flag += 0;  break;
1394                     case Twchar:    flag += 1;  break;
1395                     case Tdchar:    flag += 2;  break;
1396                     default:
1397                         assert(0);
1398                     }
1399                     const(char)* r = (fs.op == TOKforeach_reverse) ? "R" : "";
1400                     int j = sprintf(fdname.ptr, "_aApply%s%.*s%llu", r, 2, fntab[flag], cast(ulong)dim);
1401                     assert(j < BUFFER_LEN);
1402 
1403                     FuncDeclaration fdapply;
1404                     TypeDelegate dgty;
1405                     params = new Parameters();
1406                     params.push(new Parameter(STCin, tn.arrayOf(), null, null));
1407                     auto dgparams = new Parameters();
1408                     dgparams.push(new Parameter(0, Type.tvoidptr, null, null));
1409                     if (dim == 2)
1410                         dgparams.push(new Parameter(0, Type.tvoidptr, null, null));
1411                     dgty = new TypeDelegate(new TypeFunction(dgparams, Type.tint32, 0, LINKd));
1412                     params.push(new Parameter(0, dgty, null, null));
1413                     fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);
1414 
1415                     if (tab.ty == Tsarray)
1416                         fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf());
1417                     // paint delegate argument to the type runtime expects
1418                     if (!dgty.equals(flde.type))
1419                     {
1420                         flde = new CastExp(loc, flde, flde.type);
1421                         flde.type = dgty;
1422                     }
1423                     ec = new VarExp(Loc(), fdapply, false);
1424                     ec = new CallExp(loc, ec, fs.aggr, flde);
1425                     ec.type = Type.tint32; // don't run semantic() on ec
1426                 }
1427                 else if (tab.ty == Tdelegate)
1428                 {
1429                     /* Call:
1430                      *      aggr(flde)
1431                      */
1432                     if (fs.aggr.op == TOKdelegate && (cast(DelegateExp)fs.aggr).func.isNested())
1433                     {
1434                         // See Bugzilla 3560
1435                         fs.aggr = (cast(DelegateExp)fs.aggr).e1;
1436                     }
1437                     ec = new CallExp(loc, fs.aggr, flde);
1438                     ec = ec.semantic(sc2);
1439                     if (ec.op == TOKerror)
1440                         goto Lerror2;
1441                     if (ec.type != Type.tint32)
1442                     {
1443                         fs.error("opApply() function for %s must return an int", tab.toChars());
1444                         goto Lerror2;
1445                     }
1446                 }
1447                 else
1448                 {
1449                     assert(tab.ty == Tstruct || tab.ty == Tclass);
1450                     assert(sapply);
1451                     /* Call:
1452                      *  aggr.apply(flde)
1453                      */
1454                     ec = new DotIdExp(loc, fs.aggr, sapply.ident);
1455                     ec = new CallExp(loc, ec, flde);
1456                     ec = ec.semantic(sc2);
1457                     if (ec.op == TOKerror)
1458                         goto Lerror2;
1459                     if (ec.type != Type.tint32)
1460                     {
1461                         fs.error("opApply() function for %s must return an int", tab.toChars());
1462                         goto Lerror2;
1463                     }
1464                 }
1465                 e = Expression.combine(e, ec);
1466 
1467                 if (!fs.cases.dim)
1468                 {
1469                     // Easy case, a clean exit from the loop
1470                     e = new CastExp(loc, e, Type.tvoid); // Bugzilla 13899
1471                     s = new ExpStatement(loc, e);
1472                 }
1473                 else
1474                 {
1475                     // Construct a switch statement around the return value
1476                     // of the apply function.
1477                     auto a = new Statements();
1478 
1479                     // default: break; takes care of cases 0 and 1
1480                     s = new BreakStatement(Loc(), null);
1481                     s = new DefaultStatement(Loc(), s);
1482                     a.push(s);
1483 
1484                     // cases 2...
1485                     foreach (i, c; *fs.cases)
1486                     {
1487                         s = new CaseStatement(Loc(), new IntegerExp(i + 2), c);
1488                         a.push(s);
1489                     }
1490 
1491                     s = new CompoundStatement(loc, a);
1492                     s = new SwitchStatement(loc, e, s, false);
1493                 }
1494                 s = s.semantic(sc2);
1495                 break;
1496             }
1497         case Terror:
1498         Lerror2:
1499             s = new ErrorStatement();
1500             break;
1501 
1502         default:
1503             fs.error("foreach: %s is not an aggregate type", fs.aggr.type.toChars());
1504             goto Lerror2;
1505         }
1506         sc2.noctor--;
1507         sc2.pop();
1508         result = s;
1509     }
1510 
1511     override void visit(ForeachRangeStatement fs)
1512     {
1513         //printf("ForeachRangeStatement::semantic() %p\n", fs);
1514         auto loc = fs.loc;
1515         fs.lwr = fs.lwr.semantic(sc);
1516         fs.lwr = resolveProperties(sc, fs.lwr);
1517         fs.lwr = fs.lwr.optimize(WANTvalue);
1518         if (!fs.lwr.type)
1519         {
1520             fs.error("invalid range lower bound %s", fs.lwr.toChars());
1521         Lerror:
1522             return setError();
1523         }
1524 
1525         fs.upr = fs.upr.semantic(sc);
1526         fs.upr = resolveProperties(sc, fs.upr);
1527         fs.upr = fs.upr.optimize(WANTvalue);
1528         if (!fs.upr.type)
1529         {
1530             fs.error("invalid range upper bound %s", fs.upr.toChars());
1531             goto Lerror;
1532         }
1533 
1534         if (fs.prm.type)
1535         {
1536             fs.prm.type = fs.prm.type.semantic(loc, sc);
1537             fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
1538             fs.lwr = fs.lwr.implicitCastTo(sc, fs.prm.type);
1539 
1540             if (fs.upr.implicitConvTo(fs.prm.type) || (fs.prm.storageClass & STCref))
1541             {
1542                 fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
1543             }
1544             else
1545             {
1546                 // See if upr-1 fits in prm->type
1547                 Expression limit = new MinExp(loc, fs.upr, new IntegerExp(1));
1548                 limit = limit.semantic(sc);
1549                 limit = limit.optimize(WANTvalue);
1550                 if (!limit.implicitConvTo(fs.prm.type))
1551                 {
1552                     fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
1553                 }
1554             }
1555         }
1556         else
1557         {
1558             /* Must infer types from lwr and upr
1559              */
1560             Type tlwr = fs.lwr.type.toBasetype();
1561             if (tlwr.ty == Tstruct || tlwr.ty == Tclass)
1562             {
1563                 /* Just picking the first really isn't good enough.
1564                  */
1565                 fs.prm.type = fs.lwr.type;
1566             }
1567             else if (fs.lwr.type == fs.upr.type)
1568             {
1569                 /* Same logic as CondExp ?lwr:upr
1570                  */
1571                 fs.prm.type = fs.lwr.type;
1572             }
1573             else
1574             {
1575                 scope AddExp ea = new AddExp(loc, fs.lwr, fs.upr);
1576                 if (typeCombine(ea, sc))
1577                     return setError();
1578                 fs.prm.type = ea.type;
1579                 fs.lwr = ea.e1;
1580                 fs.upr = ea.e2;
1581             }
1582             fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
1583         }
1584         if (fs.prm.type.ty == Terror || fs.lwr.op == TOKerror || fs.upr.op == TOKerror)
1585         {
1586             return setError();
1587         }
1588 
1589         /* Convert to a for loop:
1590          *  foreach (key; lwr .. upr) =>
1591          *  for (auto key = lwr, auto tmp = upr; key < tmp; ++key)
1592          *
1593          *  foreach_reverse (key; lwr .. upr) =>
1594          *  for (auto tmp = lwr, auto key = upr; key-- > tmp;)
1595          */
1596         auto ie = new ExpInitializer(loc, (fs.op == TOKforeach) ? fs.lwr : fs.upr);
1597         fs.key = new VarDeclaration(loc, fs.upr.type.mutableOf(), Identifier.generateId("__key"), ie);
1598         fs.key.storage_class |= STCtemp;
1599         SignExtendedNumber lower = getIntRange(fs.lwr).imin;
1600         SignExtendedNumber upper = getIntRange(fs.upr).imax;
1601         if (lower <= upper)
1602         {
1603             fs.key.range = new IntRange(lower, upper);
1604         }
1605 
1606         Identifier id = Identifier.generateId("__limit");
1607         ie = new ExpInitializer(loc, (fs.op == TOKforeach) ? fs.upr : fs.lwr);
1608         auto tmp = new VarDeclaration(loc, fs.upr.type, id, ie);
1609         tmp.storage_class |= STCtemp;
1610 
1611         auto cs = new Statements();
1612         // Keep order of evaluation as lwr, then upr
1613         if (fs.op == TOKforeach)
1614         {
1615             cs.push(new ExpStatement(loc, fs.key));
1616             cs.push(new ExpStatement(loc, tmp));
1617         }
1618         else
1619         {
1620             cs.push(new ExpStatement(loc, tmp));
1621             cs.push(new ExpStatement(loc, fs.key));
1622         }
1623         Statement forinit = new CompoundDeclarationStatement(loc, cs);
1624 
1625         Expression cond;
1626         if (fs.op == TOKforeach_reverse)
1627         {
1628             cond = new PostExp(TOKminusminus, loc, new VarExp(loc, fs.key));
1629             if (fs.prm.type.isscalar())
1630             {
1631                 // key-- > tmp
1632                 cond = new CmpExp(TOKgt, loc, cond, new VarExp(loc, tmp));
1633             }
1634             else
1635             {
1636                 // key-- != tmp
1637                 cond = new EqualExp(TOKnotequal, loc, cond, new VarExp(loc, tmp));
1638             }
1639         }
1640         else
1641         {
1642             if (fs.prm.type.isscalar())
1643             {
1644                 // key < tmp
1645                 cond = new CmpExp(TOKlt, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
1646             }
1647             else
1648             {
1649                 // key != tmp
1650                 cond = new EqualExp(TOKnotequal, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
1651             }
1652         }
1653 
1654         Expression increment = null;
1655         if (fs.op == TOKforeach)
1656         {
1657             // key += 1
1658             //increment = new AddAssignExp(loc, new VarExp(loc, fs.key), new IntegerExp(1));
1659             increment = new PreExp(TOKpreplusplus, loc, new VarExp(loc, fs.key));
1660         }
1661         if ((fs.prm.storageClass & STCref) && fs.prm.type.equals(fs.key.type))
1662         {
1663             fs.key.range = null;
1664             auto v = new AliasDeclaration(loc, fs.prm.ident, fs.key);
1665             fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1666         }
1667         else
1668         {
1669             ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs.key), fs.prm.type));
1670             auto v = new VarDeclaration(loc, fs.prm.type, fs.prm.ident, ie);
1671             v.storage_class |= STCtemp | STCforeach | (fs.prm.storageClass & STCref);
1672             fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1673             if (fs.key.range && !fs.prm.type.isMutable())
1674             {
1675                 /* Limit the range of the key to the specified range
1676                  */
1677                 v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
1678             }
1679         }
1680         if (fs.prm.storageClass & STCref)
1681         {
1682             if (fs.key.type.constConv(fs.prm.type) <= MATCHnomatch)
1683             {
1684                 fs.error("prmument type mismatch, %s to ref %s", fs.key.type.toChars(), fs.prm.type.toChars());
1685                 goto Lerror;
1686             }
1687         }
1688 
1689         auto s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
1690         if (LabelStatement ls = checkLabeledLoop(sc, fs))
1691             ls.gotoTarget = s;
1692         result = s.semantic(sc);
1693     }
1694 
1695     override void visit(IfStatement ifs)
1696     {
1697         // Evaluate at runtime
1698         uint cs0 = sc.callSuper;
1699         uint cs1;
1700         uint* fi0 = sc.saveFieldInit();
1701         uint* fi1 = null;
1702 
1703         // check in syntax level
1704         ifs.condition = checkAssignmentAsCondition(ifs.condition);
1705 
1706         auto sym = new ScopeDsymbol();
1707         sym.parent = sc.scopesym;
1708         sym.endlinnum = ifs.endloc.linnum;
1709         Scope* scd = sc.push(sym);
1710         if (ifs.prm)
1711         {
1712             /* Declare prm, which we will set to be the
1713              * result of condition.
1714              */
1715             auto ei = new ExpInitializer(ifs.loc, ifs.condition);
1716             ifs.match = new VarDeclaration(ifs.loc, ifs.prm.type, ifs.prm.ident, ei);
1717             ifs.match.parent = scd.func;
1718             ifs.match.storage_class |= ifs.prm.storageClass;
1719             ifs.match.semantic(scd);
1720 
1721             auto de = new DeclarationExp(ifs.loc, ifs.match);
1722             auto ve = new VarExp(ifs.loc, ifs.match);
1723             ifs.condition = new CommaExp(ifs.loc, de, ve);
1724             ifs.condition = ifs.condition.semantic(scd);
1725 
1726             if (ifs.match.edtor)
1727             {
1728                 Statement sdtor = new DtorExpStatement(ifs.loc, ifs.match.edtor, ifs.match);
1729                 sdtor = new OnScopeStatement(ifs.loc, TOKon_scope_exit, sdtor);
1730                 ifs.ifbody = new CompoundStatement(ifs.loc, sdtor, ifs.ifbody);
1731                 ifs.match.storage_class |= STCnodtor;
1732             }
1733         }
1734         else
1735         {
1736             if (ifs.condition.op == TOKdotid)
1737                 (cast(DotIdExp)ifs.condition).noderef = true;
1738 
1739             ifs.condition = ifs.condition.semantic(scd);
1740             ifs.condition = resolveProperties(scd, ifs.condition);
1741             ifs.condition = ifs.condition.addDtorHook(scd);
1742         }
1743         if (checkNonAssignmentArrayOp(ifs.condition))
1744             ifs.condition = new ErrorExp();
1745         ifs.condition = checkGC(scd, ifs.condition);
1746 
1747         // Convert to boolean after declaring prm so this works:
1748         //  if (S prm = S()) {}
1749         // where S is a struct that defines opCast!bool.
1750         ifs.condition = ifs.condition.toBoolean(scd);
1751 
1752         // If we can short-circuit evaluate the if statement, don't do the
1753         // semantic analysis of the skipped code.
1754         // This feature allows a limited form of conditional compilation.
1755         ifs.condition = ifs.condition.optimize(WANTvalue);
1756 
1757         ifs.ifbody = ifs.ifbody.semanticNoScope(scd);
1758         scd.pop();
1759 
1760         cs1 = sc.callSuper;
1761         fi1 = sc.fieldinit;
1762         sc.callSuper = cs0;
1763         sc.fieldinit = fi0;
1764         if (ifs.elsebody)
1765             ifs.elsebody = ifs.elsebody.semanticScope(sc, null, null);
1766         sc.mergeCallSuper(ifs.loc, cs1);
1767         sc.mergeFieldInit(ifs.loc, fi1);
1768 
1769         if (ifs.condition.op == TOKerror ||
1770             (ifs.ifbody && ifs.ifbody.isErrorStatement()) ||
1771             (ifs.elsebody && ifs.elsebody.isErrorStatement()))
1772         {
1773             return setError();
1774         }
1775         result = ifs;
1776     }
1777 
1778     override void visit(ConditionalStatement cs)
1779     {
1780         //printf("ConditionalStatement::semantic()\n");
1781 
1782         // If we can short-circuit evaluate the if statement, don't do the
1783         // semantic analysis of the skipped code.
1784         // This feature allows a limited form of conditional compilation.
1785         if (cs.condition.include(sc, null))
1786         {
1787             DebugCondition dc = cs.condition.isDebugCondition();
1788             if (dc)
1789             {
1790                 sc = sc.push();
1791                 sc.flags |= SCOPEdebug;
1792                 cs.ifbody = cs.ifbody.semantic(sc);
1793                 sc.pop();
1794             }
1795             else
1796                 cs.ifbody = cs.ifbody.semantic(sc);
1797             result = cs.ifbody;
1798         }
1799         else
1800         {
1801             if (cs.elsebody)
1802                 cs.elsebody = cs.elsebody.semantic(sc);
1803             result = cs.elsebody;
1804         }
1805     }
1806 
1807     override void visit(PragmaStatement ps)
1808     {
1809         // Should be merged with PragmaDeclaration
1810 
1811         //printf("PragmaStatement::semantic() %s\n", ps.toChars());
1812         //printf("body = %p\n", ps._body);
1813         if (ps.ident == Id.msg)
1814         {
1815             if (ps.args)
1816             {
1817                 foreach (arg; *ps.args)
1818                 {
1819                     sc = sc.startCTFE();
1820                     auto e = arg.semantic(sc);
1821                     e = resolveProperties(sc, e);
1822                     sc = sc.endCTFE();
1823 
1824                     // pragma(msg) is allowed to contain types as well as expressions
1825                     e = ctfeInterpretForPragmaMsg(e);
1826                     if (e.op == TOKerror)
1827                     {
1828                         errorSupplemental(ps.loc, "while evaluating pragma(msg, %s)", arg.toChars());
1829                         goto Lerror;
1830                     }
1831                     StringExp se = e.toStringExp();
1832                     if (se)
1833                     {
1834                         se = se.toUTF8(sc);
1835                         fprintf(stderr, "%.*s", cast(int)se.len, se..string);
1836                     }
1837                     else
1838                         fprintf(stderr, "%s", e.toChars());
1839                 }
1840                 fprintf(stderr, "\n");
1841             }
1842         }
1843         else if (ps.ident == Id.lib)
1844         {
1845             version (all)
1846             {
1847                 /* Should this be allowed?
1848                  */
1849                 ps.error("pragma(lib) not allowed as statement");
1850                 goto Lerror;
1851             }
1852             else
1853             {
1854                 if (!ps.args || ps.args.dim != 1)
1855                 {
1856                     ps.error("string expected for library name");
1857                     goto Lerror;
1858                 }
1859                 else
1860                 {
1861                     auto se = semanticString(sc, (*ps.args)[0], "library name");
1862                     if (!se)
1863                         goto Lerror;
1864 
1865                     if (global.params.verbose)
1866                     {
1867                         fprintf(global.stdmsg, "library   %.*s\n", cast(int)se.len, se..string);
1868                     }
1869                 }
1870             }
1871         }
1872         else if (ps.ident == Id.startaddress)
1873         {
1874             if (!ps.args || ps.args.dim != 1)
1875                 ps.error("function name expected for start address");
1876             else
1877             {
1878                 Expression e = (*ps.args)[0];
1879                 sc = sc.startCTFE();
1880                 e = e.semantic(sc);
1881                 e = resolveProperties(sc, e);
1882                 sc = sc.endCTFE();
1883 
1884                 e = e.ctfeInterpret();
1885                 (*ps.args)[0] = e;
1886                 Dsymbol sa = getDsymbol(e);
1887                 if (!sa || !sa.isFuncDeclaration())
1888                 {
1889                     ps.error("function name expected for start address, not '%s'", e.toChars());
1890                     goto Lerror;
1891                 }
1892                 if (ps._body)
1893                 {
1894                     ps._body = ps._body.semantic(sc);
1895                     if (ps._body.isErrorStatement())
1896                     {
1897                         result = ps._body;
1898                         return;
1899                     }
1900                 }
1901                 result = ps;
1902                 return;
1903             }
1904         }
1905         else if (ps.ident == Id.Pinline)
1906         {
1907             PINLINE inlining = PINLINEdefault;
1908             if (!ps.args || ps.args.dim == 0)
1909                 inlining = PINLINEdefault;
1910             else if (!ps.args || ps.args.dim != 1)
1911             {
1912                 ps.error("boolean expression expected for pragma(inline)");
1913                 goto Lerror;
1914             }
1915             else
1916             {
1917                 Expression e = (*ps.args)[0];
1918                 if (e.op != TOKint64 || !e.type.equals(Type.tbool))
1919                 {
1920                     ps.error("pragma(inline, true or false) expected, not %s", e.toChars());
1921                     goto Lerror;
1922                 }
1923 
1924                 if (e.isBool(true))
1925                     inlining = PINLINEalways;
1926                 else if (e.isBool(false))
1927                     inlining = PINLINEnever;
1928 
1929                     FuncDeclaration fd = sc.func;
1930                 if (!fd)
1931                 {
1932                     ps.error("pragma(inline) is not inside a function");
1933                     goto Lerror;
1934                 }
1935                 fd.inlining = inlining;
1936             }
1937         }
1938         else
1939         {
1940             ps.error("unrecognized pragma(%s)", ps.ident.toChars());
1941             goto Lerror;
1942         }
1943 
1944         if (ps._body)
1945         {
1946             ps._body = ps._body.semantic(sc);
1947         }
1948         result = ps._body;
1949         return;
1950 
1951     Lerror:
1952         return setError();
1953     }
1954 
1955     override void visit(StaticAssertStatement s)
1956     {
1957         s.sa.semantic2(sc);
1958     }
1959 
1960     override void visit(SwitchStatement ss)
1961     {
1962         //printf("SwitchStatement::semantic(%p)\n", ss);
1963         ss.tf = sc.tf;
1964         if (ss.cases)
1965         {
1966             result = ss; // already run
1967             return;
1968         }
1969 
1970         bool conditionError = false;
1971         ss.condition = ss.condition.semantic(sc);
1972         ss.condition = resolveProperties(sc, ss.condition);
1973 
1974         Type att = null;
1975         TypeEnum te = null;
1976         while (ss.condition.op != TOKerror)
1977         {
1978             // preserve enum type for final switches
1979             if (ss.condition.type.ty == Tenum)
1980                 te = cast(TypeEnum)ss.condition.type;
1981             if (ss.condition.type.isString())
1982             {
1983                 // If it's not an array, cast it to one
1984                 if (ss.condition.type.ty != Tarray)
1985                 {
1986                     ss.condition = ss.condition.implicitCastTo(sc, ss.condition.type.nextOf().arrayOf());
1987                 }
1988                 ss.condition.type = ss.condition.type.constOf();
1989                 break;
1990             }
1991             ss.condition = integralPromotions(ss.condition, sc);
1992             if (ss.condition.op != TOKerror && ss.condition.type.isintegral())
1993                 break;
1994 
1995             auto ad = isAggregate(ss.condition.type);
1996             if (ad && ad.aliasthis && ss.condition.type != att)
1997             {
1998                 if (!att && ss.condition.type.checkAliasThisRec())
1999                     att = ss.condition.type;
2000                 if (auto e = resolveAliasThis(sc, ss.condition, true))
2001                 {
2002                     ss.condition = e;
2003                     continue;
2004                 }
2005             }
2006 
2007             if (ss.condition.op != TOKerror)
2008             {
2009                 ss.error("'%s' must be of integral or string type, it is a %s",
2010                     ss.condition.toChars(), ss.condition.type.toChars());
2011                 conditionError = true;
2012                 break;
2013             }
2014         }
2015         if (checkNonAssignmentArrayOp(ss.condition))
2016             ss.condition = new ErrorExp();
2017         ss.condition = ss.condition.optimize(WANTvalue);
2018         ss.condition = checkGC(sc, ss.condition);
2019         if (ss.condition.op == TOKerror)
2020             conditionError = true;
2021 
2022         bool needswitcherror = false;
2023 
2024         ss.lastVar = sc.lastVar;
2025 
2026         sc = sc.push();
2027         sc.sbreak = ss;
2028         sc.sw = ss;
2029 
2030         ss.cases = new CaseStatements();
2031         sc.noctor++; // BUG: should use Scope::mergeCallSuper() for each case instead
2032         ss._body = ss._body.semantic(sc);
2033         sc.noctor--;
2034 
2035         if (conditionError || ss._body.isErrorStatement())
2036             goto Lerror;
2037 
2038         // Resolve any goto case's with exp
2039         foreach (gcs; ss.gotoCases)
2040         {
2041             if (!gcs.exp)
2042             {
2043                 gcs.error("no case statement following goto case;");
2044                 goto Lerror;
2045             }
2046 
2047             for (Scope* scx = sc; scx; scx = scx.enclosing)
2048             {
2049                 if (!scx.sw)
2050                     continue;
2051                 foreach (cs; *scx.sw.cases)
2052                 {
2053                     if (cs.exp.equals(gcs.exp))
2054                     {
2055                         gcs.cs = cs;
2056                         goto Lfoundcase;
2057                     }
2058                 }
2059             }
2060             gcs.error("case %s not found", gcs.exp.toChars());
2061             goto Lerror;
2062 
2063         Lfoundcase:
2064         }
2065 
2066         if (ss.isFinal)
2067         {
2068             Type t = ss.condition.type;
2069             Dsymbol ds;
2070             EnumDeclaration ed = null;
2071             if (t && ((ds = t.toDsymbol(sc)) !is null))
2072                 ed = ds.isEnumDeclaration(); // typedef'ed enum
2073             if (!ed && te && ((ds = te.toDsymbol(sc)) !is null))
2074                 ed = ds.isEnumDeclaration();
2075             if (ed)
2076             {
2077                 foreach (es; *ed.members)
2078                 {
2079                     EnumMember em = es.isEnumMember();
2080                     if (em)
2081                     {
2082                         foreach (cs; *ss.cases)
2083                         {
2084                             if (cs.exp.equals(em.value) || (!cs.exp.type.isString() && !em.value.type.isString() && cs.exp.toInteger() == em.value.toInteger()))
2085                                 goto L1;
2086                         }
2087                         ss.error("enum member %s not represented in final switch", em.toChars());
2088                         goto Lerror;
2089                     }
2090                 L1:
2091                 }
2092             }
2093             else
2094                 needswitcherror = true;
2095         }
2096 
2097         if (!sc.sw.sdefault && (!ss.isFinal || needswitcherror || global.params.useAssert))
2098         {
2099             ss.hasNoDefault = 1;
2100 
2101             if (!ss.isFinal && !ss._body.isErrorStatement())
2102                 ss.error("switch statement without a default; use 'final switch' or add 'default: assert(0);' or add 'default: break;'");
2103 
2104                 // Generate runtime error if the default is hit
2105             auto a = new Statements();
2106             CompoundStatement cs;
2107             Statement s;
2108 
2109             if (global.params.useSwitchError)
2110                 s = new SwitchErrorStatement(ss.loc);
2111             else
2112                 s = new ExpStatement(ss.loc, new HaltExp(ss.loc));
2113 
2114             a.reserve(2);
2115             sc.sw.sdefault = new DefaultStatement(ss.loc, s);
2116             a.push(ss._body);
2117             if (ss._body.blockExit(sc.func, false) & BEfallthru)
2118                 a.push(new BreakStatement(Loc(), null));
2119             a.push(sc.sw.sdefault);
2120             cs = new CompoundStatement(ss.loc, a);
2121             ss._body = cs;
2122         }
2123 
2124         if (ss.checkLabel())
2125             goto Lerror;
2126 
2127         sc.pop();
2128         result = ss;
2129         return;
2130 
2131     Lerror:
2132         sc.pop();
2133         result = new ErrorStatement();
2134     }
2135 
2136     override void visit(CaseStatement cs)
2137     {
2138         SwitchStatement sw = sc.sw;
2139         bool errors = false;
2140 
2141         //printf("CaseStatement::semantic() %s\n", toChars());
2142         sc = sc.startCTFE();
2143         cs.exp = cs.exp.semantic(sc);
2144         cs.exp = resolveProperties(sc, cs.exp);
2145         sc = sc.endCTFE();
2146 
2147         if (sw)
2148         {
2149             cs.exp = cs.exp.implicitCastTo(sc, sw.condition.type);
2150             cs.exp = cs.exp.optimize(WANTvalue | WANTexpand);
2151 
2152             /* This is where variables are allowed as case expressions.
2153              */
2154             if (cs.exp.op == TOKvar)
2155             {
2156                 VarExp ve = cast(VarExp)cs.exp;
2157                 VarDeclaration v = ve.var.isVarDeclaration();
2158                 Type t = cs.exp.type.toBasetype();
2159                 if (v && (t.isintegral() || t.ty == Tclass))
2160                 {
2161                     /* Flag that we need to do special code generation
2162                      * for this, i.e. generate a sequence of if-then-else
2163                      */
2164                     sw.hasVars = 1;
2165                     if (sw.isFinal)
2166                     {
2167                         cs.error("case variables not allowed in final switch statements");
2168                         errors = true;
2169                     }
2170                     goto L1;
2171                 }
2172             }
2173             else
2174                 cs.exp = cs.exp.ctfeInterpret();
2175 
2176             if (StringExp se = cs.exp.toStringExp())
2177                 cs.exp = se;
2178             else if (cs.exp.op != TOKint64 && cs.exp.op != TOKerror)
2179             {
2180                 cs.error("case must be a string or an integral constant, not %s", cs.exp.toChars());
2181                 errors = true;
2182             }
2183 
2184         L1:
2185             foreach (cs2; *sw.cases)
2186             {
2187                 //printf("comparing '%s' with '%s'\n", exp->toChars(), cs->exp->toChars());
2188                 if (cs2.exp.equals(cs.exp))
2189                 {
2190                     cs.error("duplicate case %s in switch statement", cs.exp.toChars());
2191                     errors = true;
2192                     break;
2193                 }
2194             }
2195 
2196             sw.cases.push(cs);
2197 
2198             // Resolve any goto case's with no exp to this case statement
2199             for (size_t i = 0; i < sw.gotoCases.dim;)
2200             {
2201                 GotoCaseStatement gcs = sw.gotoCases[i];
2202                 if (!gcs.exp)
2203                 {
2204                     gcs.cs = cs;
2205                     sw.gotoCases.remove(i); // remove from array
2206                     continue;
2207                 }
2208                 i++;
2209             }
2210 
2211             if (sc.sw.tf != sc.tf)
2212             {
2213                 cs.error("switch and case are in different finally blocks");
2214                 errors = true;
2215             }
2216         }
2217         else
2218         {
2219             cs.error("case not in switch statement");
2220             errors = true;
2221         }
2222 
2223         cs.statement = cs.statement.semantic(sc);
2224         if (cs.statement.isErrorStatement())
2225         {
2226             result = cs.statement;
2227             return;
2228         }
2229         if (errors || cs.exp.op == TOKerror)
2230             return setError();
2231 
2232         cs.lastVar = sc.lastVar;
2233         result = cs;
2234     }
2235 
2236     override void visit(CaseRangeStatement crs)
2237     {
2238         SwitchStatement sw = sc.sw;
2239         if (sw is null)
2240         {
2241             crs.error("case range not in switch statement");
2242             return setError();
2243         }
2244 
2245         //printf("CaseRangeStatement::semantic() %s\n", toChars());
2246         bool errors = false;
2247         if (sw.isFinal)
2248         {
2249             crs.error("case ranges not allowed in final switch");
2250             errors = true;
2251         }
2252 
2253         sc = sc.startCTFE();
2254         crs.first = crs.first.semantic(sc);
2255         crs.first = resolveProperties(sc, crs.first);
2256         sc = sc.endCTFE();
2257         crs.first = crs.first.implicitCastTo(sc, sw.condition.type);
2258         crs.first = crs.first.ctfeInterpret();
2259 
2260         sc = sc.startCTFE();
2261         crs.last = crs.last.semantic(sc);
2262         crs.last = resolveProperties(sc, crs.last);
2263         sc = sc.endCTFE();
2264         crs.last = crs.last.implicitCastTo(sc, sw.condition.type);
2265         crs.last = crs.last.ctfeInterpret();
2266 
2267         if (crs.first.op == TOKerror || crs.last.op == TOKerror || errors)
2268         {
2269             if (crs.statement)
2270                 crs.statement.semantic(sc);
2271             return setError();
2272         }
2273 
2274         uinteger_t fval = crs.first.toInteger();
2275         uinteger_t lval = crs.last.toInteger();
2276         if ((crs.first.type.isunsigned() && fval > lval) || (!crs.first.type.isunsigned() && cast(sinteger_t)fval > cast(sinteger_t)lval))
2277         {
2278             crs.error("first case %s is greater than last case %s", crs.first.toChars(), crs.last.toChars());
2279             errors = true;
2280             lval = fval;
2281         }
2282 
2283         if (lval - fval > 256)
2284         {
2285             crs.error("had %llu cases which is more than 256 cases in case range", lval - fval);
2286             errors = true;
2287             lval = fval + 256;
2288         }
2289 
2290         if (errors)
2291             return setError();
2292 
2293         /* This works by replacing the CaseRange with an array of Case's.
2294          *
2295          * case a: .. case b: s;
2296          *    =>
2297          * case a:
2298          *   [...]
2299          * case b:
2300          *   s;
2301          */
2302 
2303         auto statements = new Statements();
2304         for (uinteger_t i = fval; i != lval + 1; i++)
2305         {
2306             Statement s = crs.statement;
2307             if (i != lval) // if not last case
2308                 s = new ExpStatement(crs.loc, cast(Expression)null);
2309             Expression e = new IntegerExp(crs.loc, i, crs.first.type);
2310             Statement cs = new CaseStatement(crs.loc, e, s);
2311             statements.push(cs);
2312         }
2313         Statement s = new CompoundStatement(crs.loc, statements);
2314         s = s.semantic(sc);
2315         result = s;
2316     }
2317 
2318     override void visit(DefaultStatement ds)
2319     {
2320         //printf("DefaultStatement::semantic()\n");
2321         bool errors = false;
2322         if (sc.sw)
2323         {
2324             if (sc.sw.sdefault)
2325             {
2326                 ds.error("switch statement already has a default");
2327                 errors = true;
2328             }
2329             sc.sw.sdefault = ds;
2330 
2331             if (sc.sw.tf != sc.tf)
2332             {
2333                 ds.error("switch and default are in different finally blocks");
2334                 errors = true;
2335             }
2336             if (sc.sw.isFinal)
2337             {
2338                 ds.error("default statement not allowed in final switch statement");
2339                 errors = true;
2340             }
2341         }
2342         else
2343         {
2344             ds.error("default not in switch statement");
2345             errors = true;
2346         }
2347 
2348         ds.statement = ds.statement.semantic(sc);
2349         if (errors || ds.statement.isErrorStatement())
2350             return setError();
2351 
2352         ds.lastVar = sc.lastVar;
2353         result = ds;
2354     }
2355 
2356     override void visit(GotoDefaultStatement gds)
2357     {
2358         gds.sw = sc.sw;
2359         if (!gds.sw)
2360         {
2361             gds.error("goto default not in switch statement");
2362             return setError();
2363         }
2364         if (gds.sw.isFinal)
2365         {
2366             gds.error("goto default not allowed in final switch statement");
2367             return setError();
2368         }
2369         result = gds;
2370     }
2371 
2372     override void visit(GotoCaseStatement gcs)
2373     {
2374         if (!sc.sw)
2375         {
2376             gcs.error("goto case not in switch statement");
2377             return setError();
2378         }
2379 
2380         if (gcs.exp)
2381         {
2382             gcs.exp = gcs.exp.semantic(sc);
2383             gcs.exp = gcs.exp.implicitCastTo(sc, sc.sw.condition.type);
2384             gcs.exp = gcs.exp.optimize(WANTvalue);
2385             if (gcs.exp.op == TOKerror)
2386                 return setError();
2387         }
2388 
2389         sc.sw.gotoCases.push(gcs);
2390         result = gcs;
2391     }
2392 
2393     override void visit(ReturnStatement rs)
2394     {
2395         //printf("ReturnStatement.semantic() %s\n", rs.toChars());
2396 
2397         FuncDeclaration fd = sc.parent.isFuncDeclaration();
2398         if (fd.fes)
2399             fd = fd.fes.func; // fd is now function enclosing foreach
2400 
2401             TypeFunction tf = cast(TypeFunction)fd.type;
2402         assert(tf.ty == Tfunction);
2403 
2404         if (rs.exp && rs.exp.op == TOKvar && (cast(VarExp)rs.exp).var == fd.vresult)
2405         {
2406             // return vresult;
2407             if (sc.fes)
2408             {
2409                 assert(rs.caseDim == 0);
2410                 sc.fes.cases.push(rs);
2411                 result = new ReturnStatement(Loc(), new IntegerExp(sc.fes.cases.dim + 1));
2412                 return;
2413             }
2414             if (fd.returnLabel)
2415             {
2416                 auto gs = new GotoStatement(rs.loc, Id.returnLabel);
2417                 gs.label = fd.returnLabel;
2418                 result = gs;
2419                 return;
2420             }
2421 
2422             if (!fd.returns)
2423                 fd.returns = new ReturnStatements();
2424             fd.returns.push(rs);
2425             result = rs;
2426             return;
2427         }
2428 
2429         Type tret = tf.next;
2430         Type tbret = tret ? tret.toBasetype() : null;
2431 
2432         bool inferRef = (tf.isref && (fd.storage_class & STCauto));
2433         Expression e0 = null;
2434 
2435         bool errors = false;
2436         if (sc.flags & SCOPEcontract)
2437         {
2438             rs.error("return statements cannot be in contracts");
2439             errors = true;
2440         }
2441         if (sc.os && sc.os.tok != TOKon_scope_failure)
2442         {
2443             rs.error("return statements cannot be in %s bodies", Token.toChars(sc.os.tok));
2444             errors = true;
2445         }
2446         if (sc.tf)
2447         {
2448             rs.error("return statements cannot be in finally bodies");
2449             errors = true;
2450         }
2451 
2452         if (fd.isCtorDeclaration())
2453         {
2454             if (rs.exp)
2455             {
2456                 rs.error("cannot return expression from constructor");
2457                 errors = true;
2458             }
2459 
2460             // Constructors implicitly do:
2461             //      return this;
2462             rs.exp = new ThisExp(Loc());
2463             rs.exp.type = tret;
2464         }
2465         else if (rs.exp)
2466         {
2467             fd.hasReturnExp |= 1;
2468 
2469             FuncLiteralDeclaration fld = fd.isFuncLiteralDeclaration();
2470             if (tret)
2471                 rs.exp = inferType(rs.exp, tret);
2472             else if (fld && fld.treq)
2473                 rs.exp = inferType(rs.exp, fld.treq.nextOf().nextOf());
2474 
2475             rs.exp = rs.exp.semantic(sc);
2476             rs.exp = resolveProperties(sc, rs.exp);
2477             if (rs.exp.checkType())
2478                 rs.exp = new ErrorExp();
2479             if (auto f = isFuncAddress(rs.exp))
2480             {
2481                 if (fd.inferRetType && f.checkForwardRef(rs.exp.loc))
2482                     rs.exp = new ErrorExp();
2483             }
2484             if (checkNonAssignmentArrayOp(rs.exp))
2485                 rs.exp = new ErrorExp();
2486 
2487             // Extract side-effect part
2488             rs.exp = Expression.extractLast(rs.exp, &e0);
2489             if (rs.exp.op == TOKcall)
2490                 rs.exp = valueNoDtor(rs.exp);
2491 
2492             if (e0)
2493                 e0 = e0.optimize(WANTvalue);
2494 
2495             /* Void-return function can have void typed expression
2496              * on return statement.
2497              */
2498             if (tbret && tbret.ty == Tvoid || rs.exp.type.ty == Tvoid)
2499             {
2500                 if (rs.exp.type.ty != Tvoid)
2501                 {
2502                     rs.error("cannot return non-void from void function");
2503                     errors = true;
2504                     rs.exp = new CastExp(rs.loc, rs.exp, Type.tvoid);
2505                     rs.exp = rs.exp.semantic(sc);
2506                 }
2507 
2508                 /* Replace:
2509                  *      return exp;
2510                  * with:
2511                  *      exp; return;
2512                  */
2513                 e0 = Expression.combine(e0, rs.exp);
2514                 rs.exp = null;
2515             }
2516             if (e0)
2517                 e0 = checkGC(sc, e0);
2518         }
2519 
2520         if (rs.exp)
2521         {
2522             if (fd.inferRetType) // infer return type
2523             {
2524                 if (!tret)
2525                 {
2526                     tf.next = rs.exp.type;
2527                 }
2528                 else if (tret.ty != Terror && !rs.exp.type.equals(tret))
2529                 {
2530                     int m1 = rs.exp.type.implicitConvTo(tret);
2531                     int m2 = tret.implicitConvTo(rs.exp.type);
2532                     //printf("exp->type = %s m2<-->m1 tret %s\n", exp->type->toChars(), tret->toChars());
2533                     //printf("m1 = %d, m2 = %d\n", m1, m2);
2534 
2535                     if (m1 && m2)
2536                     {
2537                     }
2538                     else if (!m1 && m2)
2539                         tf.next = rs.exp.type;
2540                     else if (m1 && !m2)
2541                     {
2542                     }
2543                     else if (rs.exp.op != TOKerror)
2544                     {
2545                         rs.error("mismatched function return type inference of %s and %s", rs.exp.type.toChars(), tret.toChars());
2546                         errors = true;
2547                         tf.next = Type.terror;
2548                     }
2549                 }
2550 
2551                 tret = tf.next;
2552                 tbret = tret.toBasetype();
2553             }
2554 
2555             if (inferRef) // deduce 'auto ref'
2556             {
2557                 /* Determine "refness" of function return:
2558                  * if it's an lvalue, return by ref, else return by value
2559                  */
2560                 if (rs.exp.isLvalue())
2561                 {
2562                     /* May return by ref
2563                      */
2564                     if (checkEscapeRef(sc, rs.exp, true))
2565                         tf.isref = false; // return by value
2566                 }
2567                 else
2568                     tf.isref = false; // return by value
2569 
2570                 /* The "refness" is determined by all of return statements.
2571                  * This means:
2572                  *    return 3; return x;  // ok, x can be a value
2573                  *    return x; return 3;  // ok, x can be a value
2574                  */
2575             }
2576 
2577             // handle NRVO
2578             if (fd.nrvo_can && rs.exp.op == TOKvar)
2579             {
2580                 VarExp ve = cast(VarExp)rs.exp;
2581                 VarDeclaration v = ve.var.isVarDeclaration();
2582                 if (tf.isref)
2583                 {
2584                     // Function returns a reference
2585                     if (!inferRef)
2586                         fd.nrvo_can = 0;
2587                 }
2588                 else if (!v || v.isOut() || v.isRef())
2589                     fd.nrvo_can = 0;
2590                 else if (fd.nrvo_var is null)
2591                 {
2592                     if (!v.isDataseg() && !v.isParameter() && v.toParent2() == fd)
2593                     {
2594                         //printf("Setting nrvo to %s\n", v->toChars());
2595                         fd.nrvo_var = v;
2596                     }
2597                     else
2598                         fd.nrvo_can = 0;
2599                 }
2600                 else if (fd.nrvo_var != v)
2601                     fd.nrvo_can = 0;
2602             }
2603             else //if (!exp->isLvalue())    // keep NRVO-ability
2604                 fd.nrvo_can = 0;
2605         }
2606         else
2607         {
2608             // handle NRVO
2609             fd.nrvo_can = 0;
2610 
2611             // infer return type
2612             if (fd.inferRetType)
2613             {
2614                 if (tf.next && tf.next.ty != Tvoid)
2615                 {
2616                     if (tf.next.ty != Terror)
2617                     {
2618                         rs.error("mismatched function return type inference of void and %s", tf.next.toChars());
2619                     }
2620                     errors = true;
2621                     tf.next = Type.terror;
2622                 }
2623                 else
2624                     tf.next = Type.tvoid;
2625 
2626                     tret = tf.next;
2627                 tbret = tret.toBasetype();
2628             }
2629 
2630             if (inferRef) // deduce 'auto ref'
2631                 tf.isref = false;
2632 
2633             if (tbret.ty != Tvoid) // if non-void return
2634             {
2635                 if (tbret.ty != Terror)
2636                     rs.error("return expression expected");
2637                 errors = true;
2638             }
2639             else if (fd.isMain())
2640             {
2641                 // main() returns 0, even if it returns void
2642                 rs.exp = new IntegerExp(0);
2643             }
2644         }
2645 
2646         // If any branches have called a ctor, but this branch hasn't, it's an error
2647         if (sc.callSuper & CSXany_ctor && !(sc.callSuper & (CSXthis_ctor | CSXsuper_ctor)))
2648         {
2649             rs.error("return without calling constructor");
2650             errors = true;
2651         }
2652         sc.callSuper |= CSXreturn;
2653         if (sc.fieldinit)
2654         {
2655             auto ad = fd.isMember2();
2656             assert(ad);
2657             size_t dim = sc.fieldinit_dim;
2658             foreach (i; 0 .. dim)
2659             {
2660                 VarDeclaration v = ad.fields[i];
2661                 bool mustInit = (v.storage_class & STCnodefaultctor || v.type.needsNested());
2662                 if (mustInit && !(sc.fieldinit[i] & CSXthis_ctor))
2663                 {
2664                     rs.error("an earlier return statement skips field %s initialization", v.toChars());
2665                     errors = true;
2666                 }
2667                 sc.fieldinit[i] |= CSXreturn;
2668             }
2669         }
2670 
2671         if (errors)
2672             return setError();
2673 
2674         if (sc.fes)
2675         {
2676             if (!rs.exp)
2677             {
2678                 // Send out "case receiver" statement to the foreach.
2679                 //  return exp;
2680                 Statement s = new ReturnStatement(Loc(), rs.exp);
2681                 sc.fes.cases.push(s);
2682 
2683                 // Immediately rewrite "this" return statement as:
2684                 //  return cases->dim+1;
2685                 rs.exp = new IntegerExp(sc.fes.cases.dim + 1);
2686                 if (e0)
2687                 {
2688                     result = new CompoundStatement(rs.loc, new ExpStatement(rs.loc, e0), rs);
2689                     return;
2690                 }
2691                 result = rs;
2692                 return;
2693             }
2694             else
2695             {
2696                 fd.buildResultVar(null, rs.exp.type);
2697                 bool r = fd.vresult.checkNestedReference(sc, Loc());
2698                 assert(!r); // vresult should be always accessible
2699 
2700                 // Send out "case receiver" statement to the foreach.
2701                 //  return vresult;
2702                 Statement s = new ReturnStatement(Loc(), new VarExp(Loc(), fd.vresult));
2703                 sc.fes.cases.push(s);
2704 
2705                 // Save receiver index for the later rewriting from:
2706                 //  return exp;
2707                 // to:
2708                 //  vresult = exp; retrun caseDim;
2709                 rs.caseDim = sc.fes.cases.dim + 1;
2710             }
2711         }
2712         if (rs.exp)
2713         {
2714             if (!fd.returns)
2715                 fd.returns = new ReturnStatements();
2716             fd.returns.push(rs);
2717         }
2718         if (e0)
2719         {
2720             result = new CompoundStatement(rs.loc, new ExpStatement(rs.loc, e0), rs);
2721             return;
2722         }
2723         result = rs;
2724     }
2725 
2726     override void visit(BreakStatement bs)
2727     {
2728         //printf("BreakStatement::semantic()\n");
2729 
2730         // If:
2731         //  break Identifier;
2732         if (bs.ident)
2733         {
2734             bs.ident = fixupLabelName(sc, bs.ident);
2735 
2736             FuncDeclaration thisfunc = sc.func;
2737 
2738             for (Scope* scx = sc; scx; scx = scx.enclosing)
2739             {
2740                 if (scx.func != thisfunc) // if in enclosing function
2741                 {
2742                     if (sc.fes) // if this is the body of a foreach
2743                     {
2744                         /* Post this statement to the fes, and replace
2745                          * it with a return value that caller will put into
2746                          * a switch. Caller will figure out where the break
2747                          * label actually is.
2748                          * Case numbers start with 2, not 0, as 0 is continue
2749                          * and 1 is break.
2750                          */
2751                         sc.fes.cases.push(bs);
2752                         result = new ReturnStatement(Loc(), new IntegerExp(sc.fes.cases.dim + 1));
2753                         return;
2754                     }
2755                     break; // can't break to it
2756                 }
2757 
2758                 LabelStatement ls = scx.slabel;
2759                 if (ls && ls.ident == bs.ident)
2760                 {
2761                     Statement s = ls.statement;
2762                     if (!s || !s.hasBreak())
2763                         bs.error("label '%s' has no break", bs.ident.toChars());
2764                     else if (ls.tf != sc.tf)
2765                         bs.error("cannot break out of finally block");
2766                     else
2767                     {
2768                         ls.breaks = true;
2769                         result = bs;
2770                         return;
2771                     }
2772                     return setError();
2773                 }
2774             }
2775             bs.error("enclosing label '%s' for break not found", bs.ident.toChars());
2776             return setError();
2777         }
2778         else if (!sc.sbreak)
2779         {
2780             if (sc.os && sc.os.tok != TOKon_scope_failure)
2781             {
2782                 bs.error("break is not inside %s bodies", Token.toChars(sc.os.tok));
2783             }
2784             else if (sc.fes)
2785             {
2786                 // Replace break; with return 1;
2787                 result = new ReturnStatement(Loc(), new IntegerExp(1));
2788                 return;
2789             }
2790             else
2791                 bs.error("break is not inside a loop or switch");
2792             return setError();
2793         }
2794         result = bs;
2795     }
2796 
2797     override void visit(ContinueStatement cs)
2798     {
2799         //printf("ContinueStatement::semantic() %p\n", cs);
2800         if (cs.ident)
2801         {
2802             cs.ident = fixupLabelName(sc, cs.ident);
2803 
2804             Scope* scx;
2805             FuncDeclaration thisfunc = sc.func;
2806 
2807             for (scx = sc; scx; scx = scx.enclosing)
2808             {
2809                 LabelStatement ls;
2810                 if (scx.func != thisfunc) // if in enclosing function
2811                 {
2812                     if (sc.fes) // if this is the body of a foreach
2813                     {
2814                         for (; scx; scx = scx.enclosing)
2815                         {
2816                             ls = scx.slabel;
2817                             if (ls && ls.ident == cs.ident && ls.statement == sc.fes)
2818                             {
2819                                 // Replace continue ident; with return 0;
2820                                 result = new ReturnStatement(Loc(), new IntegerExp(0));
2821                                 return;
2822                             }
2823                         }
2824 
2825                         /* Post this statement to the fes, and replace
2826                          * it with a return value that caller will put into
2827                          * a switch. Caller will figure out where the break
2828                          * label actually is.
2829                          * Case numbers start with 2, not 0, as 0 is continue
2830                          * and 1 is break.
2831                          */
2832                         sc.fes.cases.push(cs);
2833                         result = new ReturnStatement(Loc(), new IntegerExp(sc.fes.cases.dim + 1));
2834                         return;
2835                     }
2836                     break; // can't continue to it
2837                 }
2838 
2839                 ls = scx.slabel;
2840                 if (ls && ls.ident == cs.ident)
2841                 {
2842                     Statement s = ls.statement;
2843                     if (!s || !s.hasContinue())
2844                         cs.error("label '%s' has no continue", cs.ident.toChars());
2845                     else if (ls.tf != sc.tf)
2846                         cs.error("cannot continue out of finally block");
2847                     else
2848                     {
2849                         result = cs;
2850                         return;
2851                     }
2852                     return setError();
2853                 }
2854             }
2855             cs.error("enclosing label '%s' for continue not found", cs.ident.toChars());
2856             return setError();
2857         }
2858         else if (!sc.scontinue)
2859         {
2860             if (sc.os && sc.os.tok != TOKon_scope_failure)
2861             {
2862                 cs.error("continue is not inside %s bodies", Token.toChars(sc.os.tok));
2863             }
2864             else if (sc.fes)
2865             {
2866                 // Replace continue; with return 0;
2867                 result = new ReturnStatement(Loc(), new IntegerExp(0));
2868                 return;
2869             }
2870             else
2871                 cs.error("continue is not inside a loop");
2872             return setError();
2873         }
2874         result = cs;
2875     }
2876 
2877     override void visit(SynchronizedStatement ss)
2878     {
2879         if (ss.exp)
2880         {
2881             ss.exp = ss.exp.semantic(sc);
2882             ss.exp = resolveProperties(sc, ss.exp);
2883             ss.exp = ss.exp.optimize(WANTvalue);
2884             ss.exp = checkGC(sc, ss.exp);
2885             if (ss.exp.op == TOKerror)
2886                 goto Lbody;
2887 
2888             ClassDeclaration cd = ss.exp.type.isClassHandle();
2889             if (!cd)
2890             {
2891                 ss.error("can only synchronize on class objects, not '%s'", ss.exp.type.toChars());
2892                 return setError();
2893             }
2894             else if (cd.isInterfaceDeclaration())
2895             {
2896                 /* Cast the interface to an object, as the object has the monitor,
2897                  * not the interface.
2898                  */
2899                 if (!ClassDeclaration.object)
2900                 {
2901                     ss.error("missing or corrupt object.d");
2902                     fatal();
2903                 }
2904 
2905                 Type t = ClassDeclaration.object.type;
2906                 t = t.semantic(Loc(), sc).toBasetype();
2907                 assert(t.ty == Tclass);
2908 
2909                 ss.exp = new CastExp(ss.loc, ss.exp, t);
2910                 ss.exp = ss.exp.semantic(sc);
2911             }
2912             version (all)
2913             {
2914                 /* Rewrite as:
2915                  *  auto tmp = exp;
2916                  *  _d_monitorenter(tmp);
2917                  *  try { body } finally { _d_monitorexit(tmp); }
2918                  */
2919                 auto tmp = copyToTemp(0, "__sync", ss.exp);
2920 
2921                 auto cs = new Statements();
2922                 cs.push(new ExpStatement(ss.loc, tmp));
2923 
2924                 auto args = new Parameters();
2925                 args.push(new Parameter(0, ClassDeclaration.object.type, null, null));
2926 
2927                 FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter);
2928                 Expression e = new CallExp(ss.loc, new VarExp(ss.loc, fdenter, false), new VarExp(ss.loc, tmp));
2929                 e.type = Type.tvoid; // do not run semantic on e
2930 
2931                 cs.push(new ExpStatement(ss.loc, e));
2932                 FuncDeclaration fdexit = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorexit);
2933                 e = new CallExp(ss.loc, new VarExp(ss.loc, fdexit, false), new VarExp(ss.loc, tmp));
2934                 e.type = Type.tvoid; // do not run semantic on e
2935                 Statement s = new ExpStatement(ss.loc, e);
2936                 s = new TryFinallyStatement(ss.loc, ss._body, s);
2937                 cs.push(s);
2938 
2939                 s = new CompoundStatement(ss.loc, cs);
2940                 result = s.semantic(sc);
2941                 return;
2942             }
2943         }
2944         else
2945         {
2946             /* Generate our own critical section, then rewrite as:
2947              *  __gshared byte[CriticalSection.sizeof] critsec;
2948              *  _d_criticalenter(critsec.ptr);
2949              *  try { body } finally { _d_criticalexit(critsec.ptr); }
2950              */
2951             auto id = Identifier.generateId("__critsec");
2952             auto t = Type.tint8.sarrayOf(Target.ptrsize + Target.critsecsize());
2953             auto tmp = new VarDeclaration(ss.loc, t, id, null);
2954             tmp.storage_class |= STCtemp | STCgshared | STCstatic;
2955 
2956             auto cs = new Statements();
2957             cs.push(new ExpStatement(ss.loc, tmp));
2958 
2959             /* This is just a dummy variable for "goto skips declaration" error.
2960              * Backend optimizer could remove this unused variable.
2961              */
2962             auto v = new VarDeclaration(ss.loc, Type.tvoidptr, Identifier.generateId("__sync"), null);
2963             v.semantic(sc);
2964             cs.push(new ExpStatement(ss.loc, v));
2965 
2966             auto args = new Parameters();
2967             args.push(new Parameter(0, t.pointerTo(), null, null));
2968 
2969             FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.criticalenter, STCnothrow);
2970             Expression e = new DotIdExp(ss.loc, new VarExp(ss.loc, tmp), Id.ptr);
2971             e = e.semantic(sc);
2972             e = new CallExp(ss.loc, new VarExp(ss.loc, fdenter, false), e);
2973             e.type = Type.tvoid; // do not run semantic on e
2974             cs.push(new ExpStatement(ss.loc, e));
2975 
2976             FuncDeclaration fdexit = FuncDeclaration.genCfunc(args, Type.tvoid, Id.criticalexit, STCnothrow);
2977             e = new DotIdExp(ss.loc, new VarExp(ss.loc, tmp), Id.ptr);
2978             e = e.semantic(sc);
2979             e = new CallExp(ss.loc, new VarExp(ss.loc, fdexit, false), e);
2980             e.type = Type.tvoid; // do not run semantic on e
2981             Statement s = new ExpStatement(ss.loc, e);
2982             s = new TryFinallyStatement(ss.loc, ss._body, s);
2983             cs.push(s);
2984 
2985             s = new CompoundStatement(ss.loc, cs);
2986             result = s.semantic(sc);
2987             return;
2988         }
2989     Lbody:
2990         if (ss._body)
2991             ss._body = ss._body.semantic(sc);
2992         if (ss._body && ss._body.isErrorStatement())
2993         {
2994             result = ss._body;
2995             return;
2996         }
2997         result = ss;
2998     }
2999 
3000     override void visit(WithStatement ws)
3001     {
3002         ScopeDsymbol sym;
3003         Initializer _init;
3004 
3005         //printf("WithStatement::semantic()\n");
3006         ws.exp = ws.exp.semantic(sc);
3007         ws.exp = resolveProperties(sc, ws.exp);
3008         ws.exp = ws.exp.optimize(WANTvalue);
3009         ws.exp = checkGC(sc, ws.exp);
3010         if (ws.exp.op == TOKerror)
3011             return setError();
3012         if (ws.exp.op == TOKscope)
3013         {
3014             sym = new WithScopeSymbol(ws);
3015             sym.parent = sc.scopesym;
3016             sym.endlinnum = ws.endloc.linnum;
3017         }
3018         else if (ws.exp.op == TOKtype)
3019         {
3020             Dsymbol s = (cast(TypeExp)ws.exp).type.toDsymbol(sc);
3021             if (!s || !s.isScopeDsymbol())
3022             {
3023                 ws.error("with type %s has no members", ws.exp.toChars());
3024                 return setError();
3025             }
3026             sym = new WithScopeSymbol(ws);
3027             sym.parent = sc.scopesym;
3028             sym.endlinnum = ws.endloc.linnum;
3029         }
3030         else
3031         {
3032             Type t = ws.exp.type.toBasetype();
3033 
3034             Expression olde = ws.exp;
3035             if (t.ty == Tpointer)
3036             {
3037                 ws.exp = new PtrExp(ws.loc, ws.exp);
3038                 ws.exp = ws.exp.semantic(sc);
3039                 t = ws.exp.type.toBasetype();
3040             }
3041 
3042             assert(t);
3043             t = t.toBasetype();
3044             if (t.isClassHandle())
3045             {
3046                 _init = new ExpInitializer(ws.loc, ws.exp);
3047                 ws.wthis = new VarDeclaration(ws.loc, ws.exp.type, Id.withSym, _init);
3048                 ws.wthis.semantic(sc);
3049 
3050                 sym = new WithScopeSymbol(ws);
3051                 sym.parent = sc.scopesym;
3052                 sym.endlinnum = ws.endloc.linnum;
3053             }
3054             else if (t.ty == Tstruct)
3055             {
3056                 if (!ws.exp.isLvalue())
3057                 {
3058                     /* Re-write to
3059                      * {
3060                      *   auto __withtmp = exp
3061                      *   with(__withtmp)
3062                      *   {
3063                      *     ...
3064                      *   }
3065                      * }
3066                      */
3067                     auto tmp = copyToTemp(0, "__withtmp", ws.exp);
3068                     auto es = new ExpStatement(ws.loc, tmp);
3069                     ws.exp = new VarExp(ws.loc, tmp);
3070                     Statement ss = new ScopeStatement(ws.loc, new CompoundStatement(ws.loc, es, ws), ws.endloc);
3071                     result = ss.semantic(sc);
3072                     return;
3073                 }
3074                 Expression e = ws.exp.addressOf();
3075                 _init = new ExpInitializer(ws.loc, e);
3076                 ws.wthis = new VarDeclaration(ws.loc, e.type, Id.withSym, _init);
3077                 ws.wthis.semantic(sc);
3078                 sym = new WithScopeSymbol(ws);
3079                 // Need to set the scope to make use of resolveAliasThis
3080                 sym.setScope(sc);
3081                 sym.parent = sc.scopesym;
3082                 sym.endlinnum = ws.endloc.linnum;
3083             }
3084             else
3085             {
3086                 ws.error("with expressions must be aggregate types or pointers to them, not '%s'", olde.type.toChars());
3087                 return setError();
3088             }
3089         }
3090 
3091         if (ws._body)
3092         {
3093             sym._scope = sc;
3094             sc = sc.push(sym);
3095             sc.insert(sym);
3096             ws._body = ws._body.semantic(sc);
3097             sc.pop();
3098             if (ws._body && ws._body.isErrorStatement())
3099             {
3100                 result = ws._body;
3101                 return;
3102             }
3103         }
3104 
3105         result = ws;
3106     }
3107 
3108     override void visit(TryCatchStatement tcs)
3109     {
3110         uint flags;
3111         enum FLAGcpp = 1;
3112         enum FLAGd = 2;
3113 
3114         tcs._body = tcs._body.semanticScope(sc, null, null);
3115         assert(tcs._body);
3116 
3117         /* Even if body is empty, still do semantic analysis on catches
3118          */
3119         bool catchErrors = false;
3120         foreach (i, c; *tcs.catches)
3121         {
3122             c.semantic(sc);
3123             if (c.errors)
3124             {
3125                 catchErrors = true;
3126                 continue;
3127             }
3128             auto cd = c.type.toBasetype().isClassHandle();
3129             flags |= cd.isCPPclass() ? FLAGcpp : FLAGd;
3130 
3131             // Determine if current catch 'hides' any previous catches
3132             foreach (j; 0 .. i)
3133             {
3134                 Catch cj = (*tcs.catches)[j];
3135                 const si = c.loc.toChars();
3136                 const sj = cj.loc.toChars();
3137                 if (c.type.toBasetype().implicitConvTo(cj.type.toBasetype()))
3138                 {
3139                     tcs.error("catch at %s hides catch at %s", sj, si);
3140                     catchErrors = true;
3141                 }
3142             }
3143         }
3144 
3145         if (sc.func)
3146         {
3147             if (flags == (FLAGcpp | FLAGd))
3148             {
3149                 tcs.error("cannot mix catching D and C++ exceptions in the same try-catch");
3150                 catchErrors = true;
3151             }
3152         }
3153 
3154         if (catchErrors)
3155             return setError();
3156 
3157         if (tcs._body.isErrorStatement())
3158         {
3159             result = tcs._body;
3160             return;
3161         }
3162 
3163         /* If the try body never throws, we can eliminate any catches
3164          * of recoverable exceptions.
3165          */
3166         if (!(tcs._body.blockExit(sc.func, false) & BEthrow) && ClassDeclaration.exception)
3167         {
3168             foreach_reverse (i; 0 .. tcs.catches.dim)
3169             {
3170                 Catch c = (*tcs.catches)[i];
3171 
3172                 /* If catch exception type is derived from Exception
3173                  */
3174                 if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) && (!c.handler || !c.handler.comeFrom()))
3175                 {
3176                     // Remove c from the array of catches
3177                     tcs.catches.remove(i);
3178                 }
3179             }
3180         }
3181 
3182         if (tcs.catches.dim == 0)
3183         {
3184             result = tcs._body.hasCode() ? tcs._body : null;
3185             return;
3186         }
3187 
3188         result = tcs;
3189     }
3190 
3191     override void visit(TryFinallyStatement tfs)
3192     {
3193         //printf("TryFinallyStatement::semantic()\n");
3194         tfs._body = tfs._body.semantic(sc);
3195 
3196         sc = sc.push();
3197         sc.tf = tfs;
3198         sc.sbreak = null;
3199         sc.scontinue = null; // no break or continue out of finally block
3200         tfs.finalbody = tfs.finalbody.semanticNoScope(sc);
3201         sc.pop();
3202 
3203         if (!tfs._body)
3204         {
3205             result = tfs.finalbody;
3206             return;
3207         }
3208         if (!tfs.finalbody)
3209         {
3210             result = tfs._body;
3211             return;
3212         }
3213 
3214         if (tfs._body.blockExit(sc.func, false) == BEfallthru)
3215         {
3216             result = new CompoundStatement(tfs.loc, tfs._body, tfs.finalbody);
3217             return;
3218         }
3219         result = tfs;
3220     }
3221 
3222     override void visit(OnScopeStatement oss)
3223     {
3224         static if (!IN_GCC)
3225         {
3226             if (oss.tok != TOKon_scope_exit)
3227             {
3228                 // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement,
3229                 // so the generated catch block cannot be placed in finally block.
3230                 // See also Catch::semantic.
3231                 if (sc.os && sc.os.tok != TOKon_scope_failure)
3232                 {
3233                     // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
3234                     oss.error("cannot put %s statement inside %s", Token.toChars(oss.tok), Token.toChars(sc.os.tok));
3235                     return setError();
3236                 }
3237                 if (sc.tf)
3238                 {
3239                     oss.error("cannot put %s statement inside finally block", Token.toChars(oss.tok));
3240                     return setError();
3241                 }
3242             }
3243         }
3244 
3245         sc = sc.push();
3246         sc.tf = null;
3247         sc.os = oss;
3248         if (oss.tok != TOKon_scope_failure)
3249         {
3250             // Jump out from scope(failure) block is allowed.
3251             sc.sbreak = null;
3252             sc.scontinue = null;
3253         }
3254         oss.statement = oss.statement.semanticNoScope(sc);
3255         sc.pop();
3256 
3257         if (!oss.statement || oss.statement.isErrorStatement())
3258         {
3259             result = oss.statement;
3260             return;
3261         }
3262         result = oss;
3263     }
3264 
3265     override void visit(ThrowStatement ts)
3266     {
3267         //printf("ThrowStatement::semantic()\n");
3268         FuncDeclaration fd = sc.parent.isFuncDeclaration();
3269         fd.hasReturnExp |= 2;
3270 
3271         ts.exp = ts.exp.semantic(sc);
3272         ts.exp = resolveProperties(sc, ts.exp);
3273         ts.exp = checkGC(sc, ts.exp);
3274         if (ts.exp.op == TOKerror)
3275             return setError();
3276 
3277         ClassDeclaration cd = ts.exp.type.toBasetype().isClassHandle();
3278         if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null)))
3279         {
3280             ts.error("can only throw class objects derived from Throwable, not type %s", ts.exp.type.toChars());
3281             return setError();
3282         }
3283 
3284         result = ts;
3285     }
3286 
3287     override void visit(DebugStatement ds)
3288     {
3289         if (ds.statement)
3290         {
3291             sc = sc.push();
3292             sc.flags |= SCOPEdebug;
3293             ds.statement = ds.statement.semantic(sc);
3294             sc.pop();
3295         }
3296         result = ds.statement;
3297     }
3298 
3299     override void visit(GotoStatement gs)
3300     {
3301         //printf("GotoStatement::semantic()\n");
3302         FuncDeclaration fd = sc.func;
3303 
3304         gs.ident = fixupLabelName(sc, gs.ident);
3305         gs.label = fd.searchLabel(gs.ident);
3306         gs.tf = sc.tf;
3307         gs.os = sc.os;
3308         gs.lastVar = sc.lastVar;
3309 
3310         if (!gs.label.statement && sc.fes)
3311         {
3312             /* Either the goto label is forward referenced or it
3313              * is in the function that the enclosing foreach is in.
3314              * Can't know yet, so wrap the goto in a scope statement
3315              * so we can patch it later, and add it to a 'look at this later'
3316              * list.
3317              */
3318             auto ss = new ScopeStatement(gs.loc, gs, gs.loc);
3319             sc.fes.gotos.push(ss); // 'look at this later' list
3320             result = ss;
3321             return;
3322         }
3323 
3324         // Add to fwdref list to check later
3325         if (!gs.label.statement)
3326         {
3327             if (!fd.gotos)
3328                 fd.gotos = new GotoStatements();
3329             fd.gotos.push(gs);
3330         }
3331         else if (gs.checkLabel())
3332             return setError();
3333 
3334         result = gs;
3335     }
3336 
3337     override void visit(LabelStatement ls)
3338     {
3339         //printf("LabelStatement::semantic()\n");
3340         FuncDeclaration fd = sc.parent.isFuncDeclaration();
3341 
3342         ls.ident = fixupLabelName(sc, ls.ident);
3343         ls.tf = sc.tf;
3344         ls.os = sc.os;
3345         ls.lastVar = sc.lastVar;
3346 
3347         LabelDsymbol ls2 = fd.searchLabel(ls.ident);
3348         if (ls2.statement)
3349         {
3350             ls.error("label '%s' already defined", ls2.toChars());
3351             return setError();
3352         }
3353         else
3354             ls2.statement = ls;
3355 
3356         sc = sc.push();
3357         sc.scopesym = sc.enclosing.scopesym;
3358         sc.callSuper |= CSXlabel;
3359         if (sc.fieldinit)
3360         {
3361             size_t dim = sc.fieldinit_dim;
3362             foreach (i; 0 .. dim)
3363                 sc.fieldinit[i] |= CSXlabel;
3364         }
3365         sc.slabel = ls;
3366         if (ls.statement)
3367             ls.statement = ls.statement.semantic(sc);
3368         sc.pop();
3369 
3370         result = ls;
3371     }
3372 
3373     override void visit(AsmStatement s)
3374     {
3375         result = asmSemantic(s, sc);
3376     }
3377 
3378     override void visit(CompoundAsmStatement cas)
3379     {
3380         foreach (ref s; *cas.statements)
3381         {
3382             s = s ? s.semantic(sc) : null;
3383         }
3384 
3385         assert(sc.func);
3386         // use setImpure/setGC when the deprecation cycle is over
3387         PURE purity;
3388         if (!(cas.stc & STCpure) && (purity = sc.func.isPureBypassingInference()) != PUREimpure && purity != PUREfwdref)
3389             cas.deprecation("asm statement is assumed to be impure - mark it with 'pure' if it is not");
3390         if (!(cas.stc & STCnogc) && sc.func.isNogcBypassingInference())
3391             cas.deprecation("asm statement is assumed to use the GC - mark it with '@nogc' if it does not");
3392         if (!(cas.stc & (STCtrusted | STCsafe)) && sc.func.setUnsafe())
3393             cas.error("asm statement is assumed to be @system - mark it with '@trusted' if it is not");
3394 
3395         result = cas;
3396     }
3397 
3398     override void visit(ImportStatement imps)
3399     {
3400         foreach (i; 0 .. imps.imports.dim)
3401         {
3402             Import s = (*imps.imports)[i].isImport();
3403             assert(!s.aliasdecls.dim);
3404             foreach (j, name; s.names)
3405             {
3406                 Identifier _alias = s.aliases[j];
3407                 if (!_alias)
3408                     _alias = name;
3409 
3410                 auto tname = new TypeIdentifier(s.loc, name);
3411                 auto ad = new AliasDeclaration(s.loc, _alias, tname);
3412                 ad._import = s;
3413                 s.aliasdecls.push(ad);
3414             }
3415 
3416             s.semantic(sc);
3417             Module.addDeferredSemantic2(s);     // Bugzilla 14666
3418             sc.insert(s);
3419 
3420             foreach (aliasdecl; s.aliasdecls)
3421             {
3422                 sc.insert(aliasdecl);
3423             }
3424         }
3425         result = imps;
3426     }
3427 }
3428 
3429 Statement semantic(Statement s, Scope* sc)
3430 {
3431     scope v = new StatementSemanticVisitor(sc);
3432     s.accept(v);
3433     return v.result;
3434 }
3435 
3436 void semantic(Catch c, Scope* sc)
3437 {
3438     //printf("Catch::semantic(%s)\n", ident->toChars());
3439 
3440     static if (!IN_GCC)
3441     {
3442         if (sc.os && sc.os.tok != TOKon_scope_failure)
3443         {
3444             // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
3445             error(c.loc, "cannot put catch statement inside %s", Token.toChars(sc.os.tok));
3446             c.errors = true;
3447         }
3448         if (sc.tf)
3449         {
3450             /* This is because the _d_local_unwind() gets the stack munged
3451              * up on this. The workaround is to place any try-catches into
3452              * a separate function, and call that.
3453              * To fix, have the compiler automatically convert the finally
3454              * body into a nested function.
3455              */
3456             error(c.loc, "cannot put catch statement inside finally block");
3457             c.errors = true;
3458         }
3459     }
3460 
3461     auto sym = new ScopeDsymbol();
3462     sym.parent = sc.scopesym;
3463     sc = sc.push(sym);
3464 
3465     if (!c.type)
3466     {
3467         deprecation(c.loc, "catch statement without an exception " ~
3468             "specification is deprecated; use catch(Throwable) for old behavior");
3469 
3470         // reference .object.Throwable
3471         c.type = getThrowable();
3472     }
3473     c.type = c.type.semantic(c.loc, sc);
3474     if (c.type == Type.terror)
3475         c.errors = true;
3476     else
3477     {
3478         auto cd = c.type.toBasetype().isClassHandle();
3479         if (!cd)
3480         {
3481             error(c.loc, "can only catch class objects, not '%s'", c.type.toChars());
3482             c.errors = true;
3483         }
3484         else if (cd.isCPPclass())
3485         {
3486             if (!Target.cppExceptions)
3487             {
3488                 error(c.loc, "catching C++ class objects not supported for this target");
3489                 c.errors = true;
3490             }
3491             if (sc.func && !sc.intypeof && !c.internalCatch && sc.func.setUnsafe())
3492             {
3493                 error(c.loc, "cannot catch C++ class objects in @safe code");
3494                 c.errors = true;
3495             }
3496         }
3497         else if (cd != ClassDeclaration.throwable && !ClassDeclaration.throwable.isBaseOf(cd, null))
3498         {
3499             error(c.loc, "can only catch class objects derived from Throwable, not '%s'", c.type.toChars());
3500             c.errors = true;
3501         }
3502         else if (sc.func && !sc.intypeof && !c.internalCatch &&
3503                  cd != ClassDeclaration.exception && !ClassDeclaration.exception.isBaseOf(cd, null) &&
3504                  sc.func.setUnsafe())
3505         {
3506             error(c.loc, "can only catch class objects derived from Exception in @safe code, not '%s'", c.type.toChars());
3507             c.errors = true;
3508         }
3509 
3510         if (c.ident)
3511         {
3512             c.var = new VarDeclaration(c.loc, c.type, c.ident, null);
3513             c.var.semantic(sc);
3514             sc.insert(c.var);
3515         }
3516         c.handler = c.handler.semantic(sc);
3517         if (c.handler && c.handler.isErrorStatement())
3518             c.errors = true;
3519     }
3520 
3521     sc.pop();
3522 }
3523 
3524 Statement semanticNoScope(Statement s, Scope* sc)
3525 {
3526     //printf("Statement::semanticNoScope() %s\n", toChars());
3527     if (!s.isCompoundStatement() && !s.isScopeStatement())
3528     {
3529         s = new CompoundStatement(s.loc, s); // so scopeCode() gets called
3530     }
3531     s = s.semantic(sc);
3532     return s;
3533 }
3534 
3535 // Same as semanticNoScope(), but do create a new scope
3536 Statement semanticScope(Statement s, Scope* sc, Statement sbreak, Statement scontinue)
3537 {
3538     Scope* scd = sc.push();
3539     if (sbreak)
3540         scd.sbreak = sbreak;
3541     if (scontinue)
3542         scd.scontinue = scontinue;
3543     s = s.semanticNoScope(scd);
3544     scd.pop();
3545     return s;
3546 }