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