1
/**
2
 * Does semantic analysis for statements.
3
 *
4
 * Specification: $(LINK2 https://dlang.org/spec/statement.html, Statements)
5
 *
6
 * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
7
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8
 * License:     $(LINK2 http://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.blockexit;
23
import dmd.clone;
24
import dmd.cond;
25
import dmd.ctorflow;
26
import dmd.dcast;
27
import dmd.dclass;
28
import dmd.declaration;
29
import dmd.denum;
30
import dmd.dimport;
31
import dmd.dinterpret;
32
import dmd.dmodule;
33
import dmd.dscope;
34
import dmd.dsymbol;
35
import dmd.dsymbolsem;
36
import dmd.dtemplate;
37
import dmd.errors;
38
import dmd.escape;
39
import dmd.expression;
40
import dmd.expressionsem;
41
import dmd.func;
42
import dmd.globals;
43
import dmd.gluelayer;
44
import dmd.id;
45
import dmd.identifier;
46
import dmd.init;
47
import dmd.intrange;
48
import dmd.mtype;
49
import dmd.nogc;
50
import dmd.opover;
51
import dmd.root.outbuffer;
52
import dmd.root.rmem;
53
import dmd.root.string;
54
import dmd.semantic2;
55
import dmd.sideeffect;
56
import dmd.statement;
57
import dmd.target;
58
import dmd.tokens;
59
import dmd.typesem;
60
import dmd.visitor;
61
import dmd.compiler;
62

63
/*****************************************
64
 * CTFE requires FuncDeclaration::labtab for the interpretation.
65
 * So fixing the label name inside in/out contracts is necessary
66
 * for the uniqueness in labtab.
67
 * Params:
68
 *      sc = context
69
 *      ident = statement label name to be adjusted
70
 * Returns:
71
 *      adjusted label name
72
 */
73
private Identifier fixupLabelName(Scope* sc, Identifier ident)
74
{
75 1
    uint flags = (sc.flags & SCOPE.contract);
76 1
    const id = ident.toString();
77 1
    if (flags && flags != SCOPE.invariant_ &&
78 1
        !(id.length >= 2 && id[0] == '_' && id[1] == '_'))  // does not start with "__"
79
    {
80 1
        OutBuffer buf;
81 1
        buf.writestring(flags == SCOPE.require ? "__in_" : "__out_");
82 1
        buf.writestring(ident.toString());
83

84 1
        ident = Identifier.idPool(buf[]);
85
    }
86 1
    return ident;
87
}
88

89
/*******************************************
90
 * Check to see if statement is the innermost labeled statement.
91
 * Params:
92
 *      sc = context
93
 *      statement = Statement to check
94
 * Returns:
95
 *      if `true`, then the `LabelStatement`, otherwise `null`
96
 */
97
private LabelStatement checkLabeledLoop(Scope* sc, Statement statement)
98
{
99 1
    if (sc.slabel && sc.slabel.statement == statement)
100
    {
101 1
        return sc.slabel;
102
    }
103 1
    return null;
104
}
105

106
/***********************************************************
107
 * Check an assignment is used as a condition.
108
 * Intended to be use before the `semantic` call on `e`.
109
 * Params:
110
 *  e = condition expression which is not yet run semantic analysis.
111
 * Returns:
112
 *  `e` or ErrorExp.
113
 */
114
private Expression checkAssignmentAsCondition(Expression e)
115
{
116 1
    auto ec = lastComma(e);
117 1
    if (ec.op == TOK.assign)
118
    {
119 1
        ec.error("assignment cannot be used as a condition, perhaps `==` was meant?");
120 1
        return ErrorExp.get();
121
    }
122 1
    return e;
123
}
124

125
// Performs semantic analysis in Statement AST nodes
126
extern(C++) Statement statementSemantic(Statement s, Scope* sc)
127
{
128
    version (CallbackAPI)
129
        Compiler.onStatementSemanticStart(s, sc);
130

131 1
    scope v = new StatementSemanticVisitor(sc);
132 1
    s.accept(v);
133

134
    version (CallbackAPI)
135
        Compiler.onStatementSemanticDone(s, sc);
136

137 1
    return v.result;
138
}
139

140
private extern (C++) final class StatementSemanticVisitor : Visitor
141
{
142
    alias visit = Visitor.visit;
143

144
    Statement result;
145
    Scope* sc;
146

147 1
    this(Scope* sc)
148
    {
149 1
        this.sc = sc;
150
    }
151

152
    private void setError()
153
    {
154 1
        result = new ErrorStatement();
155
    }
156

157
    override void visit(Statement s)
158
    {
159 0
        result = s;
160
    }
161

162
    override void visit(ErrorStatement s)
163
    {
164 1
        result = s;
165
    }
166

167
    override void visit(PeelStatement s)
168
    {
169
        /* "peel" off this wrapper, and don't run semantic()
170
         * on the result.
171
         */
172 1
        result = s.s;
173
    }
174

175
    override void visit(ExpStatement s)
176
    {
177
        /* https://dlang.org/spec/statement.html#expression-statement
178
         */
179

180 1
        if (s.exp)
181
        {
182
            //printf("ExpStatement::semantic() %s\n", exp.toChars());
183

184
            // Allow CommaExp in ExpStatement because return isn't used
185 1
            CommaExp.allow(s.exp);
186

187 1
            s.exp = s.exp.expressionSemantic(sc);
188 1
            s.exp = resolveProperties(sc, s.exp);
189 1
            s.exp = s.exp.addDtorHook(sc);
190 1
            if (checkNonAssignmentArrayOp(s.exp))
191 1
                s.exp = ErrorExp.get();
192 1
            if (auto f = isFuncAddress(s.exp))
193
            {
194 1
                if (f.checkForwardRef(s.exp.loc))
195 1
                    s.exp = ErrorExp.get();
196
            }
197 1
            if (discardValue(s.exp))
198 1
                s.exp = ErrorExp.get();
199

200 1
            s.exp = s.exp.optimize(WANTvalue);
201 1
            s.exp = checkGC(sc, s.exp);
202 1
            if (s.exp.op == TOK.error)
203 1
                return setError();
204
        }
205 1
        result = s;
206
    }
207

208
    override void visit(CompileStatement cs)
209
    {
210
        /* https://dlang.org/spec/statement.html#mixin-statement
211
         */
212

213
        //printf("CompileStatement::semantic() %s\n", exp.toChars());
214 1
        Statements* a = cs.flatten(sc);
215 1
        if (!a)
216 0
            return;
217 1
        Statement s = new CompoundStatement(cs.loc, a);
218 1
        result = s.statementSemantic(sc);
219
    }
220

221
    override void visit(CompoundStatement cs)
222
    {
223
        //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc);
224
        version (none)
225
        {
226
            foreach (i, s; cs.statements)
227
            {
228
                if (s)
229
                    printf("[%d]: %s", i, s.toChars());
230
            }
231
        }
232

233 1
        for (size_t i = 0; i < cs.statements.dim;)
234
        {
235 1
            Statement s = (*cs.statements)[i];
236 1
            if (s)
237
            {
238 1
                Statements* flt = s.flatten(sc);
239 1
                if (flt)
240
                {
241 1
                    cs.statements.remove(i);
242 1
                    cs.statements.insert(i, flt);
243 1
                    continue;
244
                }
245 1
                s = s.statementSemantic(sc);
246 1
                (*cs.statements)[i] = s;
247 1
                if (s)
248
                {
249 1
                    Statement sentry;
250 1
                    Statement sexception;
251 1
                    Statement sfinally;
252

253 1
                    (*cs.statements)[i] = s.scopeCode(sc, &sentry, &sexception, &sfinally);
254 1
                    if (sentry)
255
                    {
256 1
                        sentry = sentry.statementSemantic(sc);
257 1
                        cs.statements.insert(i, sentry);
258 1
                        i++;
259
                    }
260 1
                    if (sexception)
261 1
                        sexception = sexception.statementSemantic(sc);
262 1
                    if (sexception)
263
                    {
264
                        /* Returns: true if statements[] are empty statements
265
                         */
266
                        static bool isEmpty(const Statement[] statements)
267
                        {
268 1
                            foreach (s; statements)
269
                            {
270 1
                                if (const cs = s.isCompoundStatement())
271
                                {
272 0
                                    if (!isEmpty((*cs.statements)[]))
273 0
                                        return false;
274
                                }
275
                                else
276 1
                                    return false;
277
                            }
278 1
                            return true;
279
                        }
280

281 1
                        if (!sfinally && isEmpty((*cs.statements)[i + 1 .. cs.statements.dim]))
282
                        {
283
                        }
284
                        else
285
                        {
286
                            /* Rewrite:
287
                             *      s; s1; s2;
288
                             * As:
289
                             *      s;
290
                             *      try { s1; s2; }
291
                             *      catch (Throwable __o)
292
                             *      { sexception; throw __o; }
293
                             */
294 1
                            auto a = new Statements();
295 1
                            a.pushSlice((*cs.statements)[i + 1 .. cs.statements.length]);
296 1
                            cs.statements.setDim(i + 1);
297

298 1
                            Statement _body = new CompoundStatement(Loc.initial, a);
299 1
                            _body = new ScopeStatement(Loc.initial, _body, Loc.initial);
300

301 1
                            Identifier id = Identifier.generateId("__o");
302

303 1
                            Statement handler = new PeelStatement(sexception);
304 1
                            if (sexception.blockExit(sc.func, false) & BE.fallthru)
305
                            {
306 1
                                auto ts = new ThrowStatement(Loc.initial, new IdentifierExp(Loc.initial, id));
307 1
                                ts.internalThrow = true;
308 1
                                handler = new CompoundStatement(Loc.initial, handler, ts);
309
                            }
310

311 1
                            auto catches = new Catches();
312 1
                            auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
313 1
                            ctch.internalCatch = true;
314 1
                            catches.push(ctch);
315

316 1
                            Statement st = new TryCatchStatement(Loc.initial, _body, catches);
317 1
                            if (sfinally)
318 1
                                st = new TryFinallyStatement(Loc.initial, st, sfinally);
319 1
                            st = st.statementSemantic(sc);
320

321 1
                            cs.statements.push(st);
322 1
                            break;
323
                        }
324
                    }
325 1
                    else if (sfinally)
326
                    {
327 1
                        if (0 && i + 1 == cs.statements.dim)
328
                        {
329 0
                            cs.statements.push(sfinally);
330
                        }
331
                        else
332
                        {
333
                            /* Rewrite:
334
                             *      s; s1; s2;
335
                             * As:
336
                             *      s; try { s1; s2; } finally { sfinally; }
337
                             */
338 1
                            auto a = new Statements();
339 1
                            a.pushSlice((*cs.statements)[i + 1 .. cs.statements.length]);
340 1
                            cs.statements.setDim(i + 1);
341

342 1
                            auto _body = new CompoundStatement(Loc.initial, a);
343 1
                            Statement stf = new TryFinallyStatement(Loc.initial, _body, sfinally);
344 1
                            stf = stf.statementSemantic(sc);
345 1
                            cs.statements.push(stf);
346 1
                            break;
347
                        }
348
                    }
349
                }
350
                else
351
                {
352
                    /* Remove NULL statements from the list.
353
                     */
354 1
                    cs.statements.remove(i);
355 1
                    continue;
356
                }
357
            }
358 1
            i++;
359
        }
360

361
        /* Flatten them in place
362
         */
363
        void flatten(Statements* statements)
364
        {
365 1
            for (size_t i = 0; i < statements.length;)
366
            {
367 1
                Statement s = (*statements)[i];
368 1
                if (s)
369
                {
370 1
                    if (auto flt = s.flatten(sc))
371
                    {
372 1
                        statements.remove(i);
373 1
                        statements.insert(i, flt);
374 1
                        continue;
375
                    }
376
                }
377 1
                ++i;
378
            }
379
        }
380

381
        /* https://issues.dlang.org/show_bug.cgi?id=11653
382
         * 'semantic' may return another CompoundStatement
383
         * (eg. CaseRangeStatement), so flatten it here.
384
         */
385 1
        flatten(cs.statements);
386

387 1
        foreach (s; *cs.statements)
388
        {
389 1
            if (!s)
390 1
                continue;
391

392 1
            if (auto se = s.isErrorStatement())
393
            {
394 1
                result = se;
395 1
                return;
396
            }
397
        }
398

399 1
        if (cs.statements.length == 1)
400
        {
401 1
            result = (*cs.statements)[0];
402 1
            return;
403
        }
404 1
        result = cs;
405
    }
406

407
    override void visit(UnrolledLoopStatement uls)
408
    {
409
        //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc);
410 1
        Scope* scd = sc.push();
411 1
        scd.sbreak = uls;
412 1
        scd.scontinue = uls;
413

414 1
        Statement serror = null;
415 1
        foreach (i, ref s; *uls.statements)
416
        {
417 1
            if (s)
418
            {
419
                //printf("[%d]: %s\n", i, s.toChars());
420 1
                s = s.statementSemantic(scd);
421 1
                if (s && !serror)
422 1
                    serror = s.isErrorStatement();
423
            }
424
        }
425

426 1
        scd.pop();
427 1
        result = serror ? serror : uls;
428
    }
429

430
    override void visit(ScopeStatement ss)
431
    {
432
        //printf("ScopeStatement::semantic(sc = %p)\n", sc);
433 1
        if (ss.statement)
434
        {
435 1
            ScopeDsymbol sym = new ScopeDsymbol();
436 1
            sym.parent = sc.scopesym;
437 1
            sym.endlinnum = ss.endloc.linnum;
438 1
            sc = sc.push(sym);
439

440 1
            Statements* a = ss.statement.flatten(sc);
441 1
            if (a)
442
            {
443 1
                ss.statement = new CompoundStatement(ss.loc, a);
444
            }
445

446 1
            ss.statement = ss.statement.statementSemantic(sc);
447 1
            if (ss.statement)
448
            {
449 1
                if (ss.statement.isErrorStatement())
450
                {
451 1
                    sc.pop();
452 1
                    result = ss.statement;
453 1
                    return;
454
                }
455

456 1
                Statement sentry;
457 1
                Statement sexception;
458 1
                Statement sfinally;
459 1
                ss.statement = ss.statement.scopeCode(sc, &sentry, &sexception, &sfinally);
460 1
                assert(!sentry);
461 1
                assert(!sexception);
462 1
                if (sfinally)
463
                {
464
                    //printf("adding sfinally\n");
465 1
                    sfinally = sfinally.statementSemantic(sc);
466 1
                    ss.statement = new CompoundStatement(ss.loc, ss.statement, sfinally);
467
                }
468
            }
469

470 1
            sc.pop();
471
        }
472 1
        result = ss;
473
    }
474

475
    override void visit(ForwardingStatement ss)
476
    {
477 1
        assert(ss.sym);
478 1
        for (Scope* csc = sc; !ss.sym.forward; csc = csc.enclosing)
479
        {
480 1
            assert(csc);
481 1
            ss.sym.forward = csc.scopesym;
482
        }
483 1
        sc = sc.push(ss.sym);
484 1
        sc.sbreak = ss;
485 1
        sc.scontinue = ss;
486 1
        ss.statement = ss.statement.statementSemantic(sc);
487 1
        sc = sc.pop();
488 1
        result = ss.statement;
489
    }
490

491
    override void visit(WhileStatement ws)
492
    {
493
        /* Rewrite as a for(;condition;) loop
494
         * https://dlang.org/spec/statement.html#while-statement
495
         */
496 1
        Statement s = new ForStatement(ws.loc, null, ws.condition, null, ws._body, ws.endloc);
497 1
        s = s.statementSemantic(sc);
498 1
        result = s;
499
    }
500

501
    override void visit(DoStatement ds)
502
    {
503
        /* https://dlang.org/spec/statement.html#do-statement
504
         */
505 1
        const inLoopSave = sc.inLoop;
506 1
        sc.inLoop = true;
507 1
        if (ds._body)
508 1
            ds._body = ds._body.semanticScope(sc, ds, ds);
509 1
        sc.inLoop = inLoopSave;
510

511 1
        if (ds.condition.op == TOK.dotIdentifier)
512 1
            (cast(DotIdExp)ds.condition).noderef = true;
513

514
        // check in syntax level
515 1
        ds.condition = checkAssignmentAsCondition(ds.condition);
516

517 1
        ds.condition = ds.condition.expressionSemantic(sc);
518 1
        ds.condition = resolveProperties(sc, ds.condition);
519 1
        if (checkNonAssignmentArrayOp(ds.condition))
520 1
            ds.condition = ErrorExp.get();
521 1
        ds.condition = ds.condition.optimize(WANTvalue);
522 1
        ds.condition = checkGC(sc, ds.condition);
523

524 1
        ds.condition = ds.condition.toBoolean(sc);
525

526 1
        if (ds.condition.op == TOK.error)
527 1
            return setError();
528 1
        if (ds._body && ds._body.isErrorStatement())
529
        {
530 0
            result = ds._body;
531 0
            return;
532
        }
533

534 1
        result = ds;
535
    }
536

537
    override void visit(ForStatement fs)
538
    {
539
        /* https://dlang.org/spec/statement.html#for-statement
540
         */
541
        //printf("ForStatement::semantic %s\n", fs.toChars());
542

543 1
        if (fs._init)
544
        {
545
            /* Rewrite:
546
             *  for (auto v1 = i1, v2 = i2; condition; increment) { ... }
547
             * to:
548
             *  { auto v1 = i1, v2 = i2; for (; condition; increment) { ... } }
549
             * then lowered to:
550
             *  auto v1 = i1;
551
             *  try {
552
             *    auto v2 = i2;
553
             *    try {
554
             *      for (; condition; increment) { ... }
555
             *    } finally { v2.~this(); }
556
             *  } finally { v1.~this(); }
557
             */
558 1
            auto ainit = new Statements();
559 1
            ainit.push(fs._init);
560 1
            fs._init = null;
561 1
            ainit.push(fs);
562 1
            Statement s = new CompoundStatement(fs.loc, ainit);
563 1
            s = new ScopeStatement(fs.loc, s, fs.endloc);
564 1
            s = s.statementSemantic(sc);
565 1
            if (!s.isErrorStatement())
566
            {
567 1
                if (LabelStatement ls = checkLabeledLoop(sc, fs))
568 1
                    ls.gotoTarget = fs;
569 1
                fs.relatedLabeled = s;
570
            }
571 1
            result = s;
572 1
            return;
573
        }
574 1
        assert(fs._init is null);
575

576 1
        auto sym = new ScopeDsymbol();
577 1
        sym.parent = sc.scopesym;
578 1
        sym.endlinnum = fs.endloc.linnum;
579 1
        sc = sc.push(sym);
580 1
        sc.inLoop = true;
581

582 1
        if (fs.condition)
583
        {
584 1
            if (fs.condition.op == TOK.dotIdentifier)
585 1
                (cast(DotIdExp)fs.condition).noderef = true;
586

587
            // check in syntax level
588 1
            fs.condition = checkAssignmentAsCondition(fs.condition);
589

590 1
            fs.condition = fs.condition.expressionSemantic(sc);
591 1
            fs.condition = resolveProperties(sc, fs.condition);
592 1
            if (checkNonAssignmentArrayOp(fs.condition))
593 1
                fs.condition = ErrorExp.get();
594 1
            fs.condition = fs.condition.optimize(WANTvalue);
595 1
            fs.condition = checkGC(sc, fs.condition);
596

597 1
            fs.condition = fs.condition.toBoolean(sc);
598
        }
599 1
        if (fs.increment)
600
        {
601 1
            CommaExp.allow(fs.increment);
602 1
            fs.increment = fs.increment.expressionSemantic(sc);
603 1
            fs.increment = resolveProperties(sc, fs.increment);
604 1
            if (checkNonAssignmentArrayOp(fs.increment))
605 1
                fs.increment = ErrorExp.get();
606 1
            fs.increment = fs.increment.optimize(WANTvalue);
607 1
            fs.increment = checkGC(sc, fs.increment);
608
        }
609

610 1
        sc.sbreak = fs;
611 1
        sc.scontinue = fs;
612 1
        if (fs._body)
613 1
            fs._body = fs._body.semanticNoScope(sc);
614

615 1
        sc.pop();
616

617 1
        if (fs.condition && fs.condition.op == TOK.error ||
618 1
            fs.increment && fs.increment.op == TOK.error ||
619 1
            fs._body && fs._body.isErrorStatement())
620 1
            return setError();
621 1
        result = fs;
622
    }
623

624
    /*******************
625
     * Determines the return type of makeTupleForeach.
626
     */
627
    private static template MakeTupleForeachRet(bool isDecl)
628
    {
629
        static if(isDecl)
630
        {
631
            alias MakeTupleForeachRet = Dsymbols*;
632
        }
633
        else
634
        {
635
            alias MakeTupleForeachRet = void;
636
        }
637
    }
638

639
    /*******************
640
     * Type check and unroll `foreach` over an expression tuple as well
641
     * as `static foreach` statements and `static foreach`
642
     * declarations. For `static foreach` statements and `static
643
     * foreach` declarations, the visitor interface is used (and the
644
     * result is written into the `result` field.) For `static
645
     * foreach` declarations, the resulting Dsymbols* are returned
646
     * directly.
647
     *
648
     * The unrolled body is wrapped into a
649
     *  - UnrolledLoopStatement, for `foreach` over an expression tuple.
650
     *  - ForwardingStatement, for `static foreach` statements.
651
     *  - ForwardingAttribDeclaration, for `static foreach` declarations.
652
     *
653
     * `static foreach` variables are declared as `STC.local`, such
654
     * that they are inserted into the local symbol tables of the
655
     * forwarding constructs instead of forwarded. For `static
656
     * foreach` with multiple foreach loop variables whose aggregate
657
     * has been lowered into a sequence of tuples, this function
658
     * expands the tuples into multiple `STC.local` `static foreach`
659
     * variables.
660
     */
661
    MakeTupleForeachRet!isDecl makeTupleForeach(bool isStatic, bool isDecl)(ForeachStatement fs, TupleForeachArgs!(isStatic, isDecl) args)
662
    {
663
        auto returnEarly()
664
        {
665
            static if (isDecl)
666
            {
667 0
                return null;
668
            }
669
            else
670
            {
671 1
                result = new ErrorStatement();
672 1
                return;
673
            }
674
        }
675
        static if(isDecl)
676
        {
677
            static assert(isStatic);
678 1
            auto dbody = args[0];
679
        }
680
        static if(isStatic)
681
        {
682 1
            auto needExpansion = args[$-1];
683 1
            assert(sc);
684
        }
685

686 1
        auto loc = fs.loc;
687 1
        size_t dim = fs.parameters.dim;
688 1
        static if(isStatic) bool skipCheck = needExpansion;
689
        else enum skipCheck = false;
690 1
        if (!skipCheck && (dim < 1 || dim > 2))
691
        {
692 0
            fs.error("only one (value) or two (key,value) arguments for tuple `foreach`");
693 0
            setError();
694 0
            return returnEarly();
695
        }
696

697 1
        Type paramtype = (*fs.parameters)[dim - 1].type;
698 1
        if (paramtype)
699
        {
700 1
            paramtype = paramtype.typeSemantic(loc, sc);
701 1
            if (paramtype.ty == Terror)
702
            {
703 0
                setError();
704 0
                return returnEarly();
705
            }
706
        }
707

708 1
        Type tab = fs.aggr.type.toBasetype();
709 1
        TypeTuple tuple = cast(TypeTuple)tab;
710
        static if(!isDecl)
711
        {
712 1
            auto statements = new Statements();
713
        }
714
        else
715
        {
716 1
            auto declarations = new Dsymbols();
717
        }
718
        //printf("aggr: op = %d, %s\n", fs.aggr.op, fs.aggr.toChars());
719 1
        size_t n;
720 1
        TupleExp te = null;
721 1
        if (fs.aggr.op == TOK.tuple) // expression tuple
722
        {
723 1
            te = cast(TupleExp)fs.aggr;
724 1
            n = te.exps.dim;
725
        }
726 1
        else if (fs.aggr.op == TOK.type) // type tuple
727
        {
728 1
            n = Parameter.dim(tuple.arguments);
729
        }
730
        else
731 0
            assert(0);
732 1
        foreach (j; 0 .. n)
733
        {
734 1
            size_t k = (fs.op == TOK.foreach_) ? j : n - 1 - j;
735 1
            Expression e = null;
736 1
            Type t = null;
737 1
            if (te)
738 1
                e = (*te.exps)[k];
739
            else
740 1
                t = Parameter.getNth(tuple.arguments, k).type;
741 1
            Parameter p = (*fs.parameters)[0];
742
            static if(!isDecl)
743
            {
744 1
                auto st = new Statements();
745
            }
746
            else
747
            {
748 1
                auto st = new Dsymbols();
749
            }
750

751 1
            static if(isStatic) bool skip = needExpansion;
752
            else enum skip = false;
753 1
            if (!skip && dim == 2)
754
            {
755
                // Declare key
756 1
                if (p.storageClass & (STC.out_ | STC.ref_ | STC.lazy_))
757
                {
758 0
                    fs.error("no storage class for key `%s`", p.ident.toChars());
759 0
                    setError();
760 0
                    return returnEarly();
761
                }
762
                static if(isStatic)
763
                {
764 1
                    if(!p.type)
765
                    {
766 1
                        p.type = Type.tsize_t;
767
                    }
768
                }
769 1
                p.type = p.type.typeSemantic(loc, sc);
770 1
                TY keyty = p.type.ty;
771 1
                if (keyty != Tint32 && keyty != Tuns32)
772
                {
773 1
                    if (global.params.isLP64)
774
                    {
775 1
                        if (keyty != Tint64 && keyty != Tuns64)
776
                        {
777 0
                            fs.error("`foreach`: key type must be `int` or `uint`, `long` or `ulong`, not `%s`", p.type.toChars());
778 0
                            setError();
779 0
                            return returnEarly();
780
                        }
781
                    }
782
                    else
783
                    {
784 0
                        fs.error("`foreach`: key type must be `int` or `uint`, not `%s`", p.type.toChars());
785 0
                        setError();
786 0
                        return returnEarly();
787
                    }
788
                }
789 1
                Initializer ie = new ExpInitializer(Loc.initial, new IntegerExp(k));
790 1
                auto var = new VarDeclaration(loc, p.type, p.ident, ie);
791 1
                var.storage_class |= STC.manifest;
792 1
                static if(isStatic) var.storage_class |= STC.local;
793
                static if(!isDecl)
794
                {
795 1
                    st.push(new ExpStatement(loc, var));
796
                }
797
                else
798
                {
799 1
                    st.push(var);
800
                }
801 1
                p = (*fs.parameters)[1]; // value
802
            }
803
            /***********************
804
             * Declares a unrolled `foreach` loop variable or a `static foreach` variable.
805
             *
806
             * Params:
807
             *     storageClass = The storage class of the variable.
808
             *     type = The declared type of the variable.
809
             *     ident = The name of the variable.
810
             *     e = The initializer of the variable (i.e. the current element of the looped over aggregate).
811
             *     t = The type of the initializer.
812
             * Returns:
813
             *     `true` iff the declaration was successful.
814
             */
815
            bool declareVariable(StorageClass storageClass, Type type, Identifier ident, Expression e, Type t)
816
            {
817 1
                if (storageClass & (STC.out_ | STC.lazy_) ||
818 1
                    storageClass & STC.ref_ && !te)
819
                {
820 0
                    fs.error("no storage class for value `%s`", ident.toChars());
821 0
                    setError();
822 0
                    return false;
823
                }
824 1
                Declaration var;
825 1
                if (e)
826
                {
827 1
                    Type tb = e.type.toBasetype();
828 1
                    Dsymbol ds = null;
829 1
                    if (!(storageClass & STC.manifest))
830
                    {
831 1
                        if ((isStatic || tb.ty == Tfunction || tb.ty == Tsarray || storageClass&STC.alias_) && e.op == TOK.variable)
832 1
                            ds = (cast(VarExp)e).var;
833 1
                        else if (e.op == TOK.template_)
834 1
                            ds = (cast(TemplateExp)e).td;
835 1
                        else if (e.op == TOK.scope_)
836 1
                            ds = (cast(ScopeExp)e).sds;
837 1
                        else if (e.op == TOK.function_)
838
                        {
839 1
                            auto fe = cast(FuncExp)e;
840 1
                            ds = fe.td ? cast(Dsymbol)fe.td : fe.fd;
841
                        }
842 1
                        else if (e.op == TOK.overloadSet)
843 1
                            ds = (cast(OverExp)e).vars;
844
                    }
845 1
                    else if (storageClass & STC.alias_)
846
                    {
847 1
                        fs.error("`foreach` loop variable cannot be both `enum` and `alias`");
848 1
                        setError();
849 1
                        return false;
850
                    }
851

852 1
                    if (ds)
853
                    {
854 1
                        var = new AliasDeclaration(loc, ident, ds);
855 1
                        if (storageClass & STC.ref_)
856
                        {
857 1
                            fs.error("symbol `%s` cannot be `ref`", ds.toChars());
858 1
                            setError();
859 1
                            return false;
860
                        }
861 1
                        if (paramtype)
862
                        {
863 0
                            fs.error("cannot specify element type for symbol `%s`", ds.toChars());
864 0
                            setError();
865 0
                            return false;
866
                        }
867
                    }
868 1
                    else if (e.op == TOK.type)
869
                    {
870 1
                        var = new AliasDeclaration(loc, ident, e.type);
871 1
                        if (paramtype)
872
                        {
873 0
                            fs.error("cannot specify element type for type `%s`", e.type.toChars());
874 0
                            setError();
875 0
                            return false;
876
                        }
877
                    }
878
                    else
879
                    {
880 1
                        e = resolveProperties(sc, e);
881 1
                        type = e.type;
882 1
                        if (paramtype)
883 1
                            type = paramtype;
884 1
                        Initializer ie = new ExpInitializer(Loc.initial, e);
885 1
                        auto v = new VarDeclaration(loc, type, ident, ie, storageClass);
886 1
                        if (storageClass & STC.ref_)
887 1
                            v.storage_class |= STC.ref_ | STC.foreach_;
888 1
                        if (isStatic || storageClass&STC.manifest || e.isConst() ||
889 1
                            e.op == TOK.string_ ||
890 1
                            e.op == TOK.structLiteral ||
891 1
                            e.op == TOK.arrayLiteral)
892
                        {
893 1
                            if (v.storage_class & STC.ref_)
894
                            {
895
                                static if (!isStatic)
896
                                {
897 0
                                    fs.error("constant value `%s` cannot be `ref`", ie.toChars());
898
                                }
899
                                else
900
                                {
901 1
                                    if (!needExpansion)
902
                                    {
903 1
                                        fs.error("constant value `%s` cannot be `ref`", ie.toChars());
904
                                    }
905
                                    else
906
                                    {
907 0
                                        fs.error("constant value `%s` cannot be `ref`", ident.toChars());
908
                                    }
909
                                }
910 1
                                setError();
911 1
                                return false;
912
                            }
913
                            else
914 1
                                v.storage_class |= STC.manifest;
915
                        }
916 1
                        var = v;
917
                    }
918
                }
919
                else
920
                {
921 1
                    var = new AliasDeclaration(loc, ident, t);
922 1
                    if (paramtype)
923
                    {
924 0
                        fs.error("cannot specify element type for symbol `%s`", fs.toChars());
925 0
                        setError();
926 0
                        return false;
927
                    }
928
                }
929
                static if (isStatic)
930
                {
931 1
                    var.storage_class |= STC.local;
932
                }
933
                static if (!isDecl)
934
                {
935 1
                    st.push(new ExpStatement(loc, var));
936
                }
937
                else
938
                {
939 1
                    st.push(var);
940
                }
941 1
                return true;
942
            }
943
            static if (!isStatic)
944
            {
945
                // Declare value
946 1
                if (!declareVariable(p.storageClass, p.type, p.ident, e, t))
947
                {
948 0
                    return returnEarly();
949
                }
950
            }
951
            else
952
            {
953 1
                if (!needExpansion)
954
                {
955
                    // Declare value
956 1
                    if (!declareVariable(p.storageClass, p.type, p.ident, e, t))
957
                    {
958 1
                        return returnEarly();
959
                    }
960
                }
961
                else
962
                { // expand tuples into multiple `static foreach` variables.
963 1
                    assert(e && !t);
964 1
                    auto ident = Identifier.generateId("__value");
965 1
                    declareVariable(0, e.type, ident, e, null);
966
                    import dmd.cond: StaticForeach;
967 1
                    auto field = Identifier.idPool(StaticForeach.tupleFieldName.ptr,StaticForeach.tupleFieldName.length);
968 1
                    Expression access = new DotIdExp(loc, e, field);
969 1
                    access = expressionSemantic(access, sc);
970 1
                    if (!tuple) return returnEarly();
971
                    //printf("%s\n",tuple.toChars());
972 1
                    foreach (l; 0 .. dim)
973
                    {
974 1
                        auto cp = (*fs.parameters)[l];
975 1
                        Expression init_ = new IndexExp(loc, access, new IntegerExp(loc, l, Type.tsize_t));
976 1
                        init_ = init_.expressionSemantic(sc);
977 1
                        assert(init_.type);
978 1
                        declareVariable(p.storageClass, init_.type, cp.ident, init_, null);
979
                    }
980
                }
981
            }
982

983
            static if (!isDecl)
984
            {
985 1
                if (fs._body) // https://issues.dlang.org/show_bug.cgi?id=17646
986 1
                    st.push(fs._body.syntaxCopy());
987 1
                Statement res = new CompoundStatement(loc, st);
988
            }
989
            else
990
            {
991 1
                st.append(Dsymbol.arraySyntaxCopy(dbody));
992
            }
993
            static if (!isStatic)
994
            {
995 1
                res = new ScopeStatement(loc, res, fs.endloc);
996
            }
997
            else static if (!isDecl)
998
            {
999 1
                auto fwd = new ForwardingStatement(loc, res);
1000 1
                res = fwd;
1001
            }
1002
            else
1003
            {
1004
                import dmd.attrib: ForwardingAttribDeclaration;
1005 1
                auto res = new ForwardingAttribDeclaration(st);
1006
            }
1007
            static if (!isDecl)
1008
            {
1009 1
                statements.push(res);
1010
            }
1011
            else
1012
            {
1013 1
                declarations.push(res);
1014
            }
1015
        }
1016

1017
        static if (!isStatic)
1018
        {
1019 1
            Statement res = new UnrolledLoopStatement(loc, statements);
1020 1
            if (LabelStatement ls = checkLabeledLoop(sc, fs))
1021 1
                ls.gotoTarget = res;
1022 1
            if (te && te.e0)
1023 1
                res = new CompoundStatement(loc, new ExpStatement(te.e0.loc, te.e0), res);
1024
        }
1025
        else static if (!isDecl)
1026
        {
1027 1
            Statement res = new CompoundStatement(loc, statements);
1028
        }
1029
        else
1030
        {
1031 1
            auto res = declarations;
1032
        }
1033
        static if (!isDecl)
1034
        {
1035 1
            result = res;
1036
        }
1037
        else
1038
        {
1039 1
            return res;
1040
        }
1041
    }
1042

1043
    override void visit(ForeachStatement fs)
1044
    {
1045
        /* https://dlang.org/spec/statement.html#foreach-statement
1046
         */
1047

1048
        //printf("ForeachStatement::semantic() %p\n", fs);
1049

1050
        /******
1051
         * Issue error if any of the ForeachTypes were not supplied and could not be inferred.
1052
         * Returns:
1053
         *      true if error issued
1054
         */
1055
        static bool checkForArgTypes(ForeachStatement fs)
1056
        {
1057 1
            bool result = false;
1058 1
            foreach (p; *fs.parameters)
1059
            {
1060 1
                if (!p.type)
1061
                {
1062 0
                    fs.error("cannot infer type for `foreach` variable `%s`, perhaps set it explicitly", p.ident.toChars());
1063 0
                    p.type = Type.terror;
1064 0
                    result = true;
1065
                }
1066
            }
1067 1
            return result;
1068
        }
1069

1070 1
        const loc = fs.loc;
1071 1
        const dim = fs.parameters.dim;
1072 1
        TypeAArray taa = null;
1073

1074 1
        Type tn = null;
1075 1
        Type tnv = null;
1076

1077 1
        fs.func = sc.func;
1078 1
        if (fs.func.fes)
1079 1
            fs.func = fs.func.fes.func;
1080

1081 1
        VarDeclaration vinit = null;
1082 1
        fs.aggr = fs.aggr.expressionSemantic(sc);
1083 1
        fs.aggr = resolveProperties(sc, fs.aggr);
1084 1
        fs.aggr = fs.aggr.optimize(WANTvalue);
1085 1
        if (fs.aggr.op == TOK.error)
1086 1
            return setError();
1087 1
        Expression oaggr = fs.aggr;
1088 1
        if (fs.aggr.type && fs.aggr.type.toBasetype().ty == Tstruct &&
1089 1
            (cast(TypeStruct)(fs.aggr.type.toBasetype())).sym.dtor &&
1090 1
            fs.aggr.op != TOK.type && !fs.aggr.isLvalue())
1091
        {
1092
            // https://issues.dlang.org/show_bug.cgi?id=14653
1093
            // Extend the life of rvalue aggregate till the end of foreach.
1094 1
            vinit = copyToTemp(STC.rvalue, "__aggr", fs.aggr);
1095 1
            vinit.endlinnum = fs.endloc.linnum;
1096 1
            vinit.dsymbolSemantic(sc);
1097 1
            fs.aggr = new VarExp(fs.aggr.loc, vinit);
1098
        }
1099

1100 1
        Dsymbol sapply = null;                  // the inferred opApply() or front() function
1101 1
        if (!inferForeachAggregate(sc, fs.op == TOK.foreach_, fs.aggr, sapply))
1102
        {
1103 1
            const(char)* msg = "";
1104 1
            if (fs.aggr.type && isAggregate(fs.aggr.type))
1105
            {
1106 1
                msg = ", define `opApply()`, range primitives, or use `.tupleof`";
1107
            }
1108 1
            fs.error("invalid `foreach` aggregate `%s`%s", oaggr.toChars(), msg);
1109 1
            return setError();
1110
        }
1111

1112 1
        Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
1113

1114
        /* Check for inference errors
1115
         */
1116 1
        if (!inferApplyArgTypes(fs, sc, sapply))
1117
        {
1118
            /**
1119
             Try and extract the parameter count of the opApply callback function, e.g.:
1120
             int opApply(int delegate(int, float)) => 2 args
1121
             */
1122 1
            bool foundMismatch = false;
1123 1
            size_t foreachParamCount = 0;
1124 1
            if (sapplyOld)
1125
            {
1126 1
                if (FuncDeclaration fd = sapplyOld.isFuncDeclaration())
1127
                {
1128 1
                    auto fparameters = fd.getParameterList();
1129

1130 1
                    if (fparameters.length == 1)
1131
                    {
1132
                        // first param should be the callback function
1133 1
                        Parameter fparam = fparameters[0];
1134 1
                        if ((fparam.type.ty == Tpointer ||
1135 1
                             fparam.type.ty == Tdelegate) &&
1136 1
                            fparam.type.nextOf().ty == Tfunction)
1137
                        {
1138 1
                            TypeFunction tf = cast(TypeFunction)fparam.type.nextOf();
1139 1
                            foreachParamCount = tf.parameterList.length;
1140 1
                            foundMismatch = true;
1141
                        }
1142
                    }
1143
                }
1144
            }
1145

1146
            //printf("dim = %d, parameters.dim = %d\n", dim, parameters.dim);
1147 1
            if (foundMismatch && dim != foreachParamCount)
1148
            {
1149 1
                const(char)* plural = foreachParamCount > 1 ? "s" : "";
1150 1
                fs.error("cannot infer argument types, expected %llu argument%s, not %llu",
1151
                    cast(ulong) foreachParamCount, plural, cast(ulong) dim);
1152
            }
1153
            else
1154 1
                fs.error("cannot uniquely infer `foreach` argument types");
1155

1156 1
            return setError();
1157
        }
1158

1159 1
        Type tab = fs.aggr.type.toBasetype();
1160

1161 1
        if (tab.ty == Ttuple) // don't generate new scope for tuple loops
1162
        {
1163 1
            makeTupleForeach!(false,false)(fs);
1164 1
            if (vinit)
1165 0
                result = new CompoundStatement(loc, new ExpStatement(loc, vinit), result);
1166 1
            result = result.statementSemantic(sc);
1167 1
            return;
1168
        }
1169

1170 1
        auto sym = new ScopeDsymbol();
1171 1
        sym.parent = sc.scopesym;
1172 1
        sym.endlinnum = fs.endloc.linnum;
1173 1
        auto sc2 = sc.push(sym);
1174 1
        sc2.inLoop = true;
1175

1176 1
        foreach (Parameter p; *fs.parameters)
1177
        {
1178 1
            if (p.storageClass & STC.manifest)
1179
            {
1180 1
                fs.error("cannot declare `enum` loop variables for non-unrolled foreach");
1181
            }
1182 1
            if (p.storageClass & STC.alias_)
1183
            {
1184 1
                fs.error("cannot declare `alias` loop variables for non-unrolled foreach");
1185
            }
1186
        }
1187

1188 1
        Statement s;
1189 1
        switch (tab.ty)
1190
        {
1191 1
        case Tarray:
1192 1
        case Tsarray:
1193
            {
1194 1
                if (checkForArgTypes(fs))
1195 0
                    goto case Terror;
1196

1197 1
                if (dim < 1 || dim > 2)
1198
                {
1199 0
                    fs.error("only one or two arguments for array `foreach`");
1200 0
                    goto case Terror;
1201
                }
1202

1203
                // Finish semantic on all foreach parameter types.
1204 1
                foreach (i; 0 .. dim)
1205
                {
1206 1
                    Parameter p = (*fs.parameters)[i];
1207 1
                    p.type = p.type.typeSemantic(loc, sc2);
1208 1
                    p.type = p.type.addStorageClass(p.storageClass);
1209
                }
1210

1211 1
                tn = tab.nextOf().toBasetype();
1212

1213 1
                if (dim == 2)
1214
                {
1215 1
                    Type tindex = (*fs.parameters)[0].type;
1216 1
                    if (!tindex.isintegral())
1217
                    {
1218 1
                        fs.error("foreach: key cannot be of non-integral type `%s`", tindex.toChars());
1219 1
                        goto case Terror;
1220
                    }
1221
                    /* What cases to deprecate implicit conversions for:
1222
                     *  1. foreach aggregate is a dynamic array
1223
                     *  2. foreach body is lowered to _aApply (see special case below).
1224
                     */
1225 1
                    Type tv = (*fs.parameters)[1].type.toBasetype();
1226 1
                    if ((tab.ty == Tarray ||
1227 1
                         (tn.ty != tv.ty && tn.ty.isSomeChar && tv.ty.isSomeChar)) &&
1228 1
                        !Type.tsize_t.implicitConvTo(tindex))
1229
                    {
1230 1
                        fs.deprecation("foreach: loop index implicitly converted from `size_t` to `%s`",
1231
                                       tindex.toChars());
1232
                    }
1233
                }
1234

1235
                /* Look for special case of parsing char types out of char type
1236
                 * array.
1237
                 */
1238 1
                if (tn.ty.isSomeChar)
1239
                {
1240 1
                    int i = (dim == 1) ? 0 : 1; // index of value
1241 1
                    Parameter p = (*fs.parameters)[i];
1242 1
                    tnv = p.type.toBasetype();
1243 1
                    if (tnv.ty != tn.ty && tnv.ty.isSomeChar)
1244
                    {
1245 1
                        if (p.storageClass & STC.ref_)
1246
                        {
1247 0
                            fs.error("`foreach`: value of UTF conversion cannot be `ref`");
1248 0
                            goto case Terror;
1249
                        }
1250 1
                        if (dim == 2)
1251
                        {
1252 1
                            p = (*fs.parameters)[0];
1253 1
                            if (p.storageClass & STC.ref_)
1254
                            {
1255 0
                                fs.error("`foreach`: key cannot be `ref`");
1256 0
                                goto case Terror;
1257
                            }
1258
                        }
1259 1
                        goto Lapply;
1260
                    }
1261
                }
1262

1263
                // Declare the key
1264 1
                if (dim == 2)
1265
                {
1266 1
                    Parameter p = (*fs.parameters)[0];
1267 1
                    auto var = new VarDeclaration(loc, p.type.mutableOf(), Identifier.generateId("__key"), null);
1268 1
                    var.storage_class |= STC.temp | STC.foreach_;
1269 1
                    if (var.storage_class & (STC.ref_ | STC.out_))
1270 0
                        var.storage_class |= STC.nodtor;
1271

1272 1
                    fs.key = var;
1273 1
                    if (p.storageClass & STC.ref_)
1274
                    {
1275 1
                        if (var.type.constConv(p.type) <= MATCH.nomatch)
1276
                        {
1277 1
                            fs.error("key type mismatch, `%s` to `ref %s`",
1278
                                     var.type.toChars(), p.type.toChars());
1279 1
                            goto case Terror;
1280
                        }
1281
                    }
1282 1
                    if (tab.ty == Tsarray)
1283
                    {
1284 1
                        TypeSArray ta = cast(TypeSArray)tab;
1285 1
                        IntRange dimrange = getIntRange(ta.dim);
1286
                        // https://issues.dlang.org/show_bug.cgi?id=12504
1287 1
                        dimrange.imax = SignExtendedNumber(dimrange.imax.value-1);
1288 1
                        if (!IntRange.fromType(var.type).contains(dimrange))
1289
                        {
1290 1
                            fs.error("index type `%s` cannot cover index range 0..%llu",
1291
                                     p.type.toChars(), ta.dim.toInteger());
1292 1
                            goto case Terror;
1293
                        }
1294 1
                        fs.key.range = new IntRange(SignExtendedNumber(0), dimrange.imax);
1295
                    }
1296
                }
1297
                // Now declare the value
1298
                {
1299 1
                    Parameter p = (*fs.parameters)[dim - 1];
1300 1
                    auto var = new VarDeclaration(loc, p.type, p.ident, null);
1301 1
                    var.storage_class |= STC.foreach_;
1302 1
                    var.storage_class |= p.storageClass & (STC.IOR | STC.TYPECTOR);
1303 1
                    if (var.storage_class & (STC.ref_ | STC.out_))
1304 1
                        var.storage_class |= STC.nodtor;
1305

1306 1
                    fs.value = var;
1307 1
                    if (var.storage_class & STC.ref_)
1308
                    {
1309 1
                        if (fs.aggr.checkModifiable(sc2, 1) == Modifiable.initialization)
1310 1
                            var.storage_class |= STC.ctorinit;
1311

1312 1
                        Type t = tab.nextOf();
1313 1
                        if (t.constConv(p.type) <= MATCH.nomatch)
1314
                        {
1315 1
                            fs.error("argument type mismatch, `%s` to `ref %s`",
1316
                                     t.toChars(), p.type.toChars());
1317 1
                            goto case Terror;
1318
                        }
1319
                    }
1320
                }
1321

1322
                /* Convert to a ForStatement
1323
                 *   foreach (key, value; a) body =>
1324
                 *   for (T[] tmp = a[], size_t key; key < tmp.length; ++key)
1325
                 *   { T value = tmp[k]; body }
1326
                 *
1327
                 *   foreach_reverse (key, value; a) body =>
1328
                 *   for (T[] tmp = a[], size_t key = tmp.length; key--; )
1329
                 *   { T value = tmp[k]; body }
1330
                 */
1331 1
                auto id = Identifier.generateId("__r");
1332 1
                auto ie = new ExpInitializer(loc, new SliceExp(loc, fs.aggr, null, null));
1333 1
                VarDeclaration tmp;
1334 1
                if (fs.aggr.op == TOK.arrayLiteral &&
1335 1
                    !((*fs.parameters)[dim - 1].storageClass & STC.ref_))
1336
                {
1337 1
                    auto ale = cast(ArrayLiteralExp)fs.aggr;
1338 1
                    size_t edim = ale.elements ? ale.elements.dim : 0;
1339 1
                    auto telem = (*fs.parameters)[dim - 1].type;
1340

1341
                    // https://issues.dlang.org/show_bug.cgi?id=12936
1342
                    // if telem has been specified explicitly,
1343
                    // converting array literal elements to telem might make it @nogc.
1344 1
                    fs.aggr = fs.aggr.implicitCastTo(sc, telem.sarrayOf(edim));
1345 1
                    if (fs.aggr.op == TOK.error)
1346 0
                        goto case Terror;
1347

1348
                    // for (T[edim] tmp = a, ...)
1349 1
                    tmp = new VarDeclaration(loc, fs.aggr.type, id, ie);
1350
                }
1351
                else
1352 1
                    tmp = new VarDeclaration(loc, tab.nextOf().arrayOf(), id, ie);
1353 1
                tmp.storage_class |= STC.temp;
1354

1355 1
                Expression tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id.length);
1356

1357 1
                if (!fs.key)
1358
                {
1359 1
                    Identifier idkey = Identifier.generateId("__key");
1360 1
                    fs.key = new VarDeclaration(loc, Type.tsize_t, idkey, null);
1361 1
                    fs.key.storage_class |= STC.temp;
1362
                }
1363 1
                else if (fs.key.type.ty != Type.tsize_t.ty)
1364
                {
1365 1
                    tmp_length = new CastExp(loc, tmp_length, fs.key.type);
1366
                }
1367 1
                if (fs.op == TOK.foreach_reverse_)
1368 1
                    fs.key._init = new ExpInitializer(loc, tmp_length);
1369
                else
1370 1
                    fs.key._init = new ExpInitializer(loc, new IntegerExp(loc, 0, fs.key.type));
1371

1372 1
                auto cs = new Statements();
1373 1
                if (vinit)
1374 1
                    cs.push(new ExpStatement(loc, vinit));
1375 1
                cs.push(new ExpStatement(loc, tmp));
1376 1
                cs.push(new ExpStatement(loc, fs.key));
1377 1
                Statement forinit = new CompoundDeclarationStatement(loc, cs);
1378

1379 1
                Expression cond;
1380 1
                if (fs.op == TOK.foreach_reverse_)
1381
                {
1382
                    // key--
1383 1
                    cond = new PostExp(TOK.minusMinus, loc, new VarExp(loc, fs.key));
1384
                }
1385
                else
1386
                {
1387
                    // key < tmp.length
1388 1
                    cond = new CmpExp(TOK.lessThan, loc, new VarExp(loc, fs.key), tmp_length);
1389
                }
1390

1391 1
                Expression increment = null;
1392 1
                if (fs.op == TOK.foreach_)
1393
                {
1394
                    // key += 1
1395 1
                    increment = new AddAssignExp(loc, new VarExp(loc, fs.key), new IntegerExp(loc, 1, fs.key.type));
1396
                }
1397

1398
                // T value = tmp[key];
1399 1
                IndexExp indexExp = new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, fs.key));
1400 1
                indexExp.indexIsInBounds = true; // disabling bounds checking in foreach statements.
1401 1
                fs.value._init = new ExpInitializer(loc, indexExp);
1402 1
                Statement ds = new ExpStatement(loc, fs.value);
1403

1404 1
                if (dim == 2)
1405
                {
1406 1
                    Parameter p = (*fs.parameters)[0];
1407 1
                    if ((p.storageClass & STC.ref_) && p.type.equals(fs.key.type))
1408
                    {
1409 1
                        fs.key.range = null;
1410 1
                        auto v = new AliasDeclaration(loc, p.ident, fs.key);
1411 1
                        fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1412
                    }
1413
                    else
1414
                    {
1415 1
                        auto ei = new ExpInitializer(loc, new IdentifierExp(loc, fs.key.ident));
1416 1
                        auto v = new VarDeclaration(loc, p.type, p.ident, ei);
1417 1
                        v.storage_class |= STC.foreach_ | (p.storageClass & STC.ref_);
1418 1
                        fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1419 1
                        if (fs.key.range && !p.type.isMutable())
1420
                        {
1421
                            /* Limit the range of the key to the specified range
1422
                             */
1423 1
                            v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
1424
                        }
1425
                    }
1426
                }
1427 1
                fs._body = new CompoundStatement(loc, ds, fs._body);
1428

1429 1
                s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
1430 1
                if (auto ls = checkLabeledLoop(sc, fs))   // https://issues.dlang.org/show_bug.cgi?id=15450
1431
                                                          // don't use sc2
1432 1
                    ls.gotoTarget = s;
1433 1
                s = s.statementSemantic(sc2);
1434 1
                break;
1435
            }
1436 1
        case Taarray:
1437 1
            if (fs.op == TOK.foreach_reverse_)
1438 1
                fs.warning("cannot use `foreach_reverse` with an associative array");
1439 1
            if (checkForArgTypes(fs))
1440 0
                goto case Terror;
1441

1442 1
            taa = cast(TypeAArray)tab;
1443 1
            if (dim < 1 || dim > 2)
1444
            {
1445 0
                fs.error("only one or two arguments for associative array `foreach`");
1446 0
                goto case Terror;
1447
            }
1448 1
            goto Lapply;
1449

1450 1
        case Tclass:
1451 1
        case Tstruct:
1452
            /* Prefer using opApply, if it exists
1453
             */
1454 1
            if (sapply)
1455 1
                goto Lapply;
1456
            {
1457
                /* Look for range iteration, i.e. the properties
1458
                 * .empty, .popFront, .popBack, .front and .back
1459
                 *    foreach (e; aggr) { ... }
1460
                 * translates to:
1461
                 *    for (auto __r = aggr[]; !__r.empty; __r.popFront()) {
1462
                 *        auto e = __r.front;
1463
                 *        ...
1464
                 *    }
1465
                 */
1466 1
                auto ad = (tab.ty == Tclass) ?
1467 1
                    cast(AggregateDeclaration)(cast(TypeClass)tab).sym :
1468 1
                    cast(AggregateDeclaration)(cast(TypeStruct)tab).sym;
1469 1
                Identifier idfront;
1470 1
                Identifier idpopFront;
1471 1
                if (fs.op == TOK.foreach_)
1472
                {
1473 1
                    idfront = Id.Ffront;
1474 1
                    idpopFront = Id.FpopFront;
1475
                }
1476
                else
1477
                {
1478 1
                    idfront = Id.Fback;
1479 1
                    idpopFront = Id.FpopBack;
1480
                }
1481 1
                auto sfront = ad.search(Loc.initial, idfront);
1482 1
                if (!sfront)
1483 0
                    goto Lapply;
1484

1485
                /* Generate a temporary __r and initialize it with the aggregate.
1486
                 */
1487 1
                VarDeclaration r;
1488 1
                Statement _init;
1489 1
                if (vinit && fs.aggr.op == TOK.variable && (cast(VarExp)fs.aggr).var == vinit)
1490
                {
1491 1
                    r = vinit;
1492 1
                    _init = new ExpStatement(loc, vinit);
1493
                }
1494
                else
1495
                {
1496 1
                    r = copyToTemp(0, "__r", fs.aggr);
1497 1
                    r.dsymbolSemantic(sc);
1498 1
                    _init = new ExpStatement(loc, r);
1499 1
                    if (vinit)
1500 1
                        _init = new CompoundStatement(loc, new ExpStatement(loc, vinit), _init);
1501
                }
1502

1503
                // !__r.empty
1504 1
                Expression e = new VarExp(loc, r);
1505 1
                e = new DotIdExp(loc, e, Id.Fempty);
1506 1
                Expression condition = new NotExp(loc, e);
1507

1508
                // __r.idpopFront()
1509 1
                e = new VarExp(loc, r);
1510 1
                Expression increment = new CallExp(loc, new DotIdExp(loc, e, idpopFront));
1511

1512
                /* Declaration statement for e:
1513
                 *    auto e = __r.idfront;
1514
                 */
1515 1
                e = new VarExp(loc, r);
1516 1
                Expression einit = new DotIdExp(loc, e, idfront);
1517 1
                Statement makeargs, forbody;
1518 1
                bool ignoreRef = false; // If a range returns a non-ref front we ignore ref on foreach
1519

1520 1
                Type tfront;
1521 1
                if (auto fd = sfront.isFuncDeclaration())
1522
                {
1523 1
                    if (!fd.functionSemantic())
1524 0
                        goto Lrangeerr;
1525 1
                    tfront = fd.type;
1526
                }
1527 1
                else if (auto td = sfront.isTemplateDeclaration())
1528
                {
1529 1
                    Expressions a;
1530 1
                    if (auto f = resolveFuncCall(loc, sc, td, null, tab, &a, FuncResolveFlag.quiet))
1531 1
                        tfront = f.type;
1532
                }
1533 1
                else if (auto d = sfront.toAlias().isDeclaration())
1534
                {
1535 1
                    tfront = d.type;
1536
                }
1537 1
                if (!tfront || tfront.ty == Terror)
1538 0
                    goto Lrangeerr;
1539 1
                if (tfront.toBasetype().ty == Tfunction)
1540
                {
1541 1
                    auto ftt = cast(TypeFunction)tfront.toBasetype();
1542 1
                    tfront = tfront.toBasetype().nextOf();
1543 1
                    if (!ftt.isref)
1544
                    {
1545
                        // .front() does not return a ref. We ignore ref on foreach arg.
1546
                        // see https://issues.dlang.org/show_bug.cgi?id=11934
1547 1
                        if (tfront.needsDestruction()) ignoreRef = true;
1548
                    }
1549
                }
1550 1
                if (tfront.ty == Tvoid)
1551
                {
1552 1
                    fs.error("`%s.front` is `void` and has no value", oaggr.toChars());
1553 1
                    goto case Terror;
1554
                }
1555

1556 1
                if (dim == 1)
1557
                {
1558 1
                    auto p = (*fs.parameters)[0];
1559 1
                    auto ve = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, einit));
1560 1
                    ve.storage_class |= STC.foreach_;
1561 1
                    ve.storage_class |= p.storageClass & (STC.IOR | STC.TYPECTOR);
1562

1563 1
                    if (ignoreRef)
1564 1
                        ve.storage_class &= ~STC.ref_;
1565

1566 1
                    makeargs = new ExpStatement(loc, ve);
1567
                }
1568
                else
1569
                {
1570 1
                    auto vd = copyToTemp(STC.ref_, "__front", einit);
1571 1
                    vd.dsymbolSemantic(sc);
1572 1
                    makeargs = new ExpStatement(loc, vd);
1573

1574
                    // Resolve inout qualifier of front type
1575 1
                    tfront = tfront.substWildTo(tab.mod);
1576

1577 1
                    Expression ve = new VarExp(loc, vd);
1578 1
                    ve.type = tfront;
1579

1580 1
                    auto exps = new Expressions();
1581 1
                    exps.push(ve);
1582 1
                    int pos = 0;
1583 1
                    while (exps.dim < dim)
1584
                    {
1585 1
                        pos = expandAliasThisTuples(exps, pos);
1586 1
                        if (pos == -1)
1587 1
                            break;
1588
                    }
1589 1
                    if (exps.dim != dim)
1590
                    {
1591 1
                        const(char)* plural = exps.dim > 1 ? "s" : "";
1592 1
                        fs.error("cannot infer argument types, expected %llu argument%s, not %llu",
1593
                            cast(ulong) exps.dim, plural, cast(ulong) dim);
1594 1
                        goto case Terror;
1595
                    }
1596

1597 1
                    foreach (i; 0 .. dim)
1598
                    {
1599 1
                        auto p = (*fs.parameters)[i];
1600 1
                        auto exp = (*exps)[i];
1601
                        version (none)
1602
                        {
1603
                            printf("[%d] p = %s %s, exp = %s %s\n", i,
1604
                                p.type ? p.type.toChars() : "?", p.ident.toChars(),
1605
                                exp.type.toChars(), exp.toChars());
1606
                        }
1607 1
                        if (!p.type)
1608 1
                            p.type = exp.type;
1609

1610 1
                        auto sc = p.storageClass;
1611 1
                        if (ignoreRef) sc &= ~STC.ref_;
1612 1
                        p.type = p.type.addStorageClass(sc).typeSemantic(loc, sc2);
1613 1
                        if (!exp.implicitConvTo(p.type))
1614 0
                            goto Lrangeerr;
1615

1616 1
                        auto var = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, exp));
1617 1
                        var.storage_class |= STC.ctfe | STC.ref_ | STC.foreach_;
1618 1
                        makeargs = new CompoundStatement(loc, makeargs, new ExpStatement(loc, var));
1619
                    }
1620
                }
1621

1622 1
                forbody = new CompoundStatement(loc, makeargs, fs._body);
1623

1624 1
                s = new ForStatement(loc, _init, condition, increment, forbody, fs.endloc);
1625 1
                if (auto ls = checkLabeledLoop(sc, fs))
1626 1
                    ls.gotoTarget = s;
1627

1628
                version (none)
1629
                {
1630
                    printf("init: %s\n", _init.toChars());
1631
                    printf("condition: %s\n", condition.toChars());
1632
                    printf("increment: %s\n", increment.toChars());
1633
                    printf("body: %s\n", forbody.toChars());
1634
                }
1635 1
                s = s.statementSemantic(sc2);
1636 1
                break;
1637

1638
            Lrangeerr:
1639 0
                fs.error("cannot infer argument types");
1640 0
                goto case Terror;
1641
            }
1642 1
        case Tdelegate:
1643 1
            if (fs.op == TOK.foreach_reverse_)
1644 1
                fs.deprecation("cannot use `foreach_reverse` with a delegate");
1645
        Lapply:
1646
            {
1647 1
                if (checkForArgTypes(fs))
1648 0
                    goto case Terror;
1649

1650 1
                TypeFunction tfld = null;
1651 1
                if (sapply)
1652
                {
1653 1
                    FuncDeclaration fdapply = sapply.isFuncDeclaration();
1654 1
                    if (fdapply)
1655
                    {
1656 1
                        assert(fdapply.type && fdapply.type.ty == Tfunction);
1657 1
                        tfld = cast(TypeFunction)fdapply.type.typeSemantic(loc, sc2);
1658 1
                        goto Lget;
1659
                    }
1660 1
                    else if (tab.ty == Tdelegate)
1661
                    {
1662 0
                        tfld = cast(TypeFunction)tab.nextOf();
1663
                    Lget:
1664
                        //printf("tfld = %s\n", tfld.toChars());
1665 1
                        if (tfld.parameterList.parameters.dim == 1)
1666
                        {
1667 1
                            Parameter p = tfld.parameterList[0];
1668 1
                            if (p.type && p.type.ty == Tdelegate)
1669
                            {
1670 1
                                auto t = p.type.typeSemantic(loc, sc2);
1671 1
                                assert(t.ty == Tdelegate);
1672 1
                                tfld = cast(TypeFunction)t.nextOf();
1673
                            }
1674
                            //printf("tfld = %s\n", tfld.toChars());
1675
                        }
1676
                    }
1677
                }
1678

1679 1
                FuncExp flde = foreachBodyToFunction(sc2, fs, tfld);
1680 1
                if (!flde)
1681 1
                    goto case Terror;
1682

1683
                // Resolve any forward referenced goto's
1684 1
                foreach (ScopeStatement ss; *fs.gotos)
1685
                {
1686 1
                    GotoStatement gs = ss.statement.isGotoStatement();
1687 1
                    if (!gs.label.statement)
1688
                    {
1689
                        // 'Promote' it to this scope, and replace with a return
1690 1
                        fs.cases.push(gs);
1691 1
                        ss.statement = new ReturnStatement(Loc.initial, new IntegerExp(fs.cases.dim + 1));
1692
                    }
1693
                }
1694

1695 1
                Expression e = null;
1696 1
                Expression ec;
1697 1
                if (vinit)
1698
                {
1699 0
                    e = new DeclarationExp(loc, vinit);
1700 0
                    e = e.expressionSemantic(sc2);
1701 0
                    if (e.op == TOK.error)
1702 0
                        goto case Terror;
1703
                }
1704

1705 1
                if (taa)
1706
                {
1707
                    // Check types
1708 1
                    Parameter p = (*fs.parameters)[0];
1709 1
                    bool isRef = (p.storageClass & STC.ref_) != 0;
1710 1
                    Type ta = p.type;
1711 1
                    if (dim == 2)
1712
                    {
1713 1
                        Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index);
1714 1
                        if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta))
1715
                        {
1716 1
                            fs.error("`foreach`: index must be type `%s`, not `%s`",
1717
                                ti.toChars(), ta.toChars());
1718 1
                            goto case Terror;
1719
                        }
1720 1
                        p = (*fs.parameters)[1];
1721 1
                        isRef = (p.storageClass & STC.ref_) != 0;
1722 1
                        ta = p.type;
1723
                    }
1724 1
                    Type taav = taa.nextOf();
1725 1
                    if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
1726
                    {
1727 0
                        fs.error("`foreach`: value must be type `%s`, not `%s`",
1728
                            taav.toChars(), ta.toChars());
1729 0
                        goto case Terror;
1730
                    }
1731

1732
                    /* Call:
1733
                     *  extern(C) int _aaApply(void*, in size_t, int delegate(void*))
1734
                     *      _aaApply(aggr, keysize, flde)
1735
                     *
1736
                     *  extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
1737
                     *      _aaApply2(aggr, keysize, flde)
1738
                     */
1739 1
                    __gshared FuncDeclaration* fdapply = [null, null];
1740 1
                    __gshared TypeDelegate* fldeTy = [null, null];
1741

1742 1
                    ubyte i = (dim == 2 ? 1 : 0);
1743 1
                    if (!fdapply[i])
1744
                    {
1745 1
                        auto params = new Parameters();
1746 1
                        params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null));
1747 1
                        params.push(new Parameter(STC.in_, Type.tsize_t, null, null, null));
1748 1
                        auto dgparams = new Parameters();
1749 1
                        dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
1750 1
                        if (dim == 2)
1751 1
                            dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
1752 1
                        fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
1753 1
                        params.push(new Parameter(0, fldeTy[i], null, null, null));
1754 1
                        fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply);
1755
                    }
1756

1757 1
                    auto exps = new Expressions();
1758 1
                    exps.push(fs.aggr);
1759 1
                    auto keysize = taa.index.size();
1760 1
                    if (keysize == SIZE_INVALID)
1761 0
                        goto case Terror;
1762 1
                    assert(keysize < keysize.max - target.ptrsize);
1763 1
                    keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1);
1764
                    // paint delegate argument to the type runtime expects
1765 1
                    Expression fexp = flde;
1766 1
                    if (!fldeTy[i].equals(flde.type))
1767
                    {
1768 1
                        fexp = new CastExp(loc, flde, flde.type);
1769 1
                        fexp.type = fldeTy[i];
1770
                    }
1771 1
                    exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t));
1772 1
                    exps.push(fexp);
1773 1
                    ec = new VarExp(Loc.initial, fdapply[i], false);
1774 1
                    ec = new CallExp(loc, ec, exps);
1775 1
                    ec.type = Type.tint32; // don't run semantic() on ec
1776
                }
1777 1
                else if (tab.ty == Tarray || tab.ty == Tsarray)
1778
                {
1779
                    /* Call:
1780
                     *      _aApply(aggr, flde)
1781
                     */
1782 1
                    __gshared const(char)** fntab =
1783
                    [
1784
                        "cc", "cw", "cd",
1785
                        "wc", "cc", "wd",
1786
                        "dc", "dw", "dd"
1787
                    ];
1788

1789 1
                    const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1;
1790 1
                    char[BUFFER_LEN] fdname;
1791 1
                    int flag;
1792

1793 1
                    switch (tn.ty)
1794
                    {
1795 1
                    case Tchar:     flag = 0;   break;
1796 1
                    case Twchar:    flag = 3;   break;
1797 1
                    case Tdchar:    flag = 6;   break;
1798 0
                    default:
1799 0
                        assert(0);
1800
                    }
1801 1
                    switch (tnv.ty)
1802
                    {
1803 1
                    case Tchar:     flag += 0;  break;
1804 1
                    case Twchar:    flag += 1;  break;
1805 1
                    case Tdchar:    flag += 2;  break;
1806 0
                    default:
1807 0
                        assert(0);
1808
                    }
1809 1
                    const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : "";
1810 1
                    int j = sprintf(fdname.ptr, "_aApply%s%.*s%llu", r, 2, fntab[flag], cast(ulong)dim);
1811 1
                    assert(j < BUFFER_LEN);
1812

1813 1
                    FuncDeclaration fdapply;
1814 1
                    TypeDelegate dgty;
1815 1
                    auto params = new Parameters();
1816 1
                    params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null));
1817 1
                    auto dgparams = new Parameters();
1818 1
                    dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
1819 1
                    if (dim == 2)
1820 1
                        dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
1821 1
                    dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
1822 1
                    params.push(new Parameter(0, dgty, null, null, null));
1823 1
                    fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);
1824

1825 1
                    if (tab.ty == Tsarray)
1826 1
                        fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf());
1827
                    // paint delegate argument to the type runtime expects
1828 1
                    Expression fexp = flde;
1829 1
                    if (!dgty.equals(flde.type))
1830
                    {
1831 1
                        fexp = new CastExp(loc, flde, flde.type);
1832 1
                        fexp.type = dgty;
1833
                    }
1834 1
                    ec = new VarExp(Loc.initial, fdapply, false);
1835 1
                    ec = new CallExp(loc, ec, fs.aggr, fexp);
1836 1
                    ec.type = Type.tint32; // don't run semantic() on ec
1837
                }
1838 1
                else if (tab.ty == Tdelegate)
1839
                {
1840
                    /* Call:
1841
                     *      aggr(flde)
1842
                     */
1843 1
                    if (fs.aggr.op == TOK.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() && !(cast(DelegateExp)fs.aggr).func.needThis())
1844
                    {
1845
                        // https://issues.dlang.org/show_bug.cgi?id=3560
1846 1
                        fs.aggr = (cast(DelegateExp)fs.aggr).e1;
1847
                    }
1848 1
                    ec = new CallExp(loc, fs.aggr, flde);
1849 1
                    ec = ec.expressionSemantic(sc2);
1850 1
                    if (ec.op == TOK.error)
1851 0
                        goto case Terror;
1852 1
                    if (ec.type != Type.tint32)
1853
                    {
1854 0
                        fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars());
1855 0
                        goto case Terror;
1856
                    }
1857
                }
1858
                else
1859
                {
1860
version (none)
1861
{
1862
                    if (global.params.vsafe)
1863
                    {
1864
                        message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`");
1865
                    }
1866
                    flde.fd.tookAddressOf = 1;
1867
}
1868
else
1869
{
1870 1
                    if (global.params.vsafe)
1871 1
                        ++flde.fd.tookAddressOf;  // allocate a closure unless the opApply() uses 'scope'
1872
}
1873 1
                    assert(tab.ty == Tstruct || tab.ty == Tclass);
1874 1
                    assert(sapply);
1875
                    /* Call:
1876
                     *  aggr.apply(flde)
1877
                     */
1878 1
                    ec = new DotIdExp(loc, fs.aggr, sapply.ident);
1879 1
                    ec = new CallExp(loc, ec, flde);
1880 1
                    ec = ec.expressionSemantic(sc2);
1881 1
                    if (ec.op == TOK.error)
1882 1
                        goto case Terror;
1883 1
                    if (ec.type != Type.tint32)
1884
                    {
1885 0
                        fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars());
1886 0
                        goto case Terror;
1887
                    }
1888
                }
1889 1
                e = Expression.combine(e, ec);
1890

1891 1
                if (!fs.cases.dim)
1892
                {
1893
                    // Easy case, a clean exit from the loop
1894 1
                    e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899
1895 1
                    s = new ExpStatement(loc, e);
1896
                }
1897
                else
1898
                {
1899
                    // Construct a switch statement around the return value
1900
                    // of the apply function.
1901 1
                    auto a = new Statements();
1902

1903
                    // default: break; takes care of cases 0 and 1
1904 1
                    s = new BreakStatement(Loc.initial, null);
1905 1
                    s = new DefaultStatement(Loc.initial, s);
1906 1
                    a.push(s);
1907

1908
                    // cases 2...
1909 1
                    foreach (i, c; *fs.cases)
1910
                    {
1911 1
                        s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c);
1912 1
                        a.push(s);
1913
                    }
1914

1915 1
                    s = new CompoundStatement(loc, a);
1916 1
                    s = new SwitchStatement(loc, e, s, false);
1917
                }
1918 1
                s = s.statementSemantic(sc2);
1919 1
                break;
1920
            }
1921 0
            assert(0);
1922

1923 1
        case Terror:
1924 1
            s = new ErrorStatement();
1925 1
            break;
1926

1927 0
        default:
1928 0
            fs.error("`foreach`: `%s` is not an aggregate type", fs.aggr.type.toChars());
1929 0
            goto case Terror;
1930
        }
1931 1
        sc2.pop();
1932 1
        result = s;
1933
    }
1934

1935
    /*************************************
1936
     * Turn foreach body into the function literal:
1937
     *  int delegate(ref T param) { body }
1938
     * Params:
1939
     *  sc = context
1940
     *  fs = ForeachStatement
1941
     *  tfld = type of function literal to be created, can be null
1942
     * Returns:
1943
     *  Function literal created, as an expression
1944
     *  null if error.
1945
     */
1946
    static FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld)
1947
    {
1948 1
        auto params = new Parameters();
1949 1
        foreach (i; 0 .. fs.parameters.dim)
1950
        {
1951 1
            Parameter p = (*fs.parameters)[i];
1952 1
            StorageClass stc = STC.ref_;
1953 1
            Identifier id;
1954

1955 1
            p.type = p.type.typeSemantic(fs.loc, sc);
1956 1
            p.type = p.type.addStorageClass(p.storageClass);
1957 1
            if (tfld)
1958
            {
1959 1
                Parameter prm = tfld.parameterList[i];
1960
                //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars());
1961 1
                stc = prm.storageClass & STC.ref_;
1962 1
                id = p.ident; // argument copy is not need.
1963 1
                if ((p.storageClass & STC.ref_) != stc)
1964
                {
1965 1
                    if (!stc)
1966
                    {
1967 1
                        fs.error("`foreach`: cannot make `%s` `ref`", p.ident.toChars());
1968 1
                        return null;
1969
                    }
1970 1
                    goto LcopyArg;
1971
                }
1972
            }
1973 1
            else if (p.storageClass & STC.ref_)
1974
            {
1975
                // default delegate parameters are marked as ref, then
1976
                // argument copy is not need.
1977 1
                id = p.ident;
1978
            }
1979
            else
1980
            {
1981
                // Make a copy of the ref argument so it isn't
1982
                // a reference.
1983
            LcopyArg:
1984 1
                id = Identifier.generateId("__applyArg", cast(int)i);
1985

1986 1
                Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id));
1987 1
                auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie);
1988 1
                v.storage_class |= STC.temp;
1989 1
                Statement s = new ExpStatement(fs.loc, v);
1990 1
                fs._body = new CompoundStatement(fs.loc, s, fs._body);
1991
            }
1992 1
            params.push(new Parameter(stc, p.type, id, null, null));
1993
        }
1994
        // https://issues.dlang.org/show_bug.cgi?id=13840
1995
        // Throwable nested function inside nothrow function is acceptable.
1996 1
        StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func);
1997 1
        auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc);
1998 1
        fs.cases = new Statements();
1999 1
        fs.gotos = new ScopeStatements();
2000 1
        auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs);
2001 1
        fld.fbody = fs._body;
2002 1
        Expression flde = new FuncExp(fs.loc, fld);
2003 1
        flde = flde.expressionSemantic(sc);
2004 1
        fld.tookAddressOf = 0;
2005 1
        if (flde.op == TOK.error)
2006 1
            return null;
2007 1
        return cast(FuncExp)flde;
2008
    }
2009

2010
    override void visit(ForeachRangeStatement fs)
2011
    {
2012
        /* https://dlang.org/spec/statement.html#foreach-range-statement
2013
         */
2014

2015
        //printf("ForeachRangeStatement::semantic() %p\n", fs);
2016 1
        auto loc = fs.loc;
2017 1
        fs.lwr = fs.lwr.expressionSemantic(sc);
2018 1
        fs.lwr = resolveProperties(sc, fs.lwr);
2019 1
        fs.lwr = fs.lwr.optimize(WANTvalue);
2020 1
        if (!fs.lwr.type)
2021
        {
2022 0
            fs.error("invalid range lower bound `%s`", fs.lwr.toChars());
2023 0
            return setError();
2024
        }
2025

2026 1
        fs.upr = fs.upr.expressionSemantic(sc);
2027 1
        fs.upr = resolveProperties(sc, fs.upr);
2028 1
        fs.upr = fs.upr.optimize(WANTvalue);
2029 1
        if (!fs.upr.type)
2030
        {
2031 0
            fs.error("invalid range upper bound `%s`", fs.upr.toChars());
2032 0
            return setError();
2033
        }
2034

2035 1
        if (fs.prm.type)
2036
        {
2037 1
            fs.prm.type = fs.prm.type.typeSemantic(loc, sc);
2038 1
            fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
2039 1
            fs.lwr = fs.lwr.implicitCastTo(sc, fs.prm.type);
2040

2041 1
            if (fs.upr.implicitConvTo(fs.prm.type) || (fs.prm.storageClass & STC.ref_))
2042
            {
2043 1
                fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
2044
            }
2045
            else
2046
            {
2047
                // See if upr-1 fits in prm.type
2048 1
                Expression limit = new MinExp(loc, fs.upr, IntegerExp.literal!1);
2049 1
                limit = limit.expressionSemantic(sc);
2050 1
                limit = limit.optimize(WANTvalue);
2051 1
                if (!limit.implicitConvTo(fs.prm.type))
2052
                {
2053 1
                    fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
2054
                }
2055
            }
2056
        }
2057
        else
2058
        {
2059
            /* Must infer types from lwr and upr
2060
             */
2061 1
            Type tlwr = fs.lwr.type.toBasetype();
2062 1
            if (tlwr.ty == Tstruct || tlwr.ty == Tclass)
2063
            {
2064
                /* Just picking the first really isn't good enough.
2065
                 */
2066 1
                fs.prm.type = fs.lwr.type;
2067
            }
2068 1
            else if (fs.lwr.type == fs.upr.type)
2069
            {
2070
                /* Same logic as CondExp ?lwr:upr
2071
                 */
2072 1
                fs.prm.type = fs.lwr.type;
2073
            }
2074
            else
2075
            {
2076 1
                scope AddExp ea = new AddExp(loc, fs.lwr, fs.upr);
2077 1
                if (typeCombine(ea, sc))
2078 1
                    return setError();
2079 1
                fs.prm.type = ea.type;
2080 1
                fs.lwr = ea.e1;
2081 1
                fs.upr = ea.e2;
2082
            }
2083 1
            fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
2084
        }
2085 1
        if (fs.prm.type.ty == Terror || fs.lwr.op == TOK.error || fs.upr.op == TOK.error)
2086
        {
2087 1
            return setError();
2088
        }
2089

2090
        /* Convert to a for loop:
2091
         *  foreach (key; lwr .. upr) =>
2092
         *  for (auto key = lwr, auto tmp = upr; key < tmp; ++key)
2093
         *
2094
         *  foreach_reverse (key; lwr .. upr) =>
2095
         *  for (auto tmp = lwr, auto key = upr; key-- > tmp;)
2096
         */
2097 1
        auto ie = new ExpInitializer(loc, (fs.op == TOK.foreach_) ? fs.lwr : fs.upr);
2098 1
        fs.key = new VarDeclaration(loc, fs.upr.type.mutableOf(), Identifier.generateId("__key"), ie);
2099 1
        fs.key.storage_class |= STC.temp;
2100 1
        SignExtendedNumber lower = getIntRange(fs.lwr).imin;
2101 1
        SignExtendedNumber upper = getIntRange(fs.upr).imax;
2102 1
        if (lower <= upper)
2103
        {
2104 1
            fs.key.range = new IntRange(lower, upper);
2105
        }
2106

2107 1
        Identifier id = Identifier.generateId("__limit");
2108 1
        ie = new ExpInitializer(loc, (fs.op == TOK.foreach_) ? fs.upr : fs.lwr);
2109 1
        auto tmp = new VarDeclaration(loc, fs.upr.type, id, ie);
2110 1
        tmp.storage_class |= STC.temp;
2111

2112 1
        auto cs = new Statements();
2113
        // Keep order of evaluation as lwr, then upr
2114 1
        if (fs.op == TOK.foreach_)
2115
        {
2116 1
            cs.push(new ExpStatement(loc, fs.key));
2117 1
            cs.push(new ExpStatement(loc, tmp));
2118
        }
2119
        else
2120
        {
2121 1
            cs.push(new ExpStatement(loc, tmp));
2122 1
            cs.push(new ExpStatement(loc, fs.key));
2123
        }
2124 1
        Statement forinit = new CompoundDeclarationStatement(loc, cs);
2125

2126 1
        Expression cond;
2127 1
        if (fs.op == TOK.foreach_reverse_)
2128
        {
2129 1
            cond = new PostExp(TOK.minusMinus, loc, new VarExp(loc, fs.key));
2130 1
            if (fs.prm.type.isscalar())
2131
            {
2132
                // key-- > tmp
2133 1
                cond = new CmpExp(TOK.greaterThan, loc, cond, new VarExp(loc, tmp));
2134
            }
2135
            else
2136
            {
2137
                // key-- != tmp
2138 1
                cond = new EqualExp(TOK.notEqual, loc, cond, new VarExp(loc, tmp));
2139
            }
2140
        }
2141
        else
2142
        {
2143 1
            if (fs.prm.type.isscalar())
2144
            {
2145
                // key < tmp
2146 1
                cond = new CmpExp(TOK.lessThan, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
2147
            }
2148
            else
2149
            {
2150
                // key != tmp
2151 1
                cond = new EqualExp(TOK.notEqual, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
2152
            }
2153
        }
2154

2155 1
        Expression increment = null;
2156 1
        if (fs.op == TOK.foreach_)
2157
        {
2158
            // key += 1
2159
            //increment = new AddAssignExp(loc, new VarExp(loc, fs.key), IntegerExp.literal!1);
2160 1
            increment = new PreExp(TOK.prePlusPlus, loc, new VarExp(loc, fs.key));
2161
        }
2162 1
        if ((fs.prm.storageClass & STC.ref_) && fs.prm.type.equals(fs.key.type))
2163
        {
2164 1
            fs.key.range = null;
2165 1
            auto v = new AliasDeclaration(loc, fs.prm.ident, fs.key);
2166 1
            fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
2167
        }
2168
        else
2169
        {
2170 1
            ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs.key), fs.prm.type));
2171 1
            auto v = new VarDeclaration(loc, fs.prm.type, fs.prm.ident, ie);
2172 1
            v.storage_class |= STC.temp | STC.foreach_ | (fs.prm.storageClass & STC.ref_);
2173 1
            fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
2174 1
            if (fs.key.range && !fs.prm.type.isMutable())
2175
            {
2176
                /* Limit the range of the key to the specified range
2177
                 */
2178 1
                v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
2179
            }
2180
        }
2181 1
        if (fs.prm.storageClass & STC.ref_)
2182
        {
2183 1
            if (fs.key.type.constConv(fs.prm.type) <= MATCH.nomatch)
2184
            {
2185 1
                fs.error("argument type mismatch, `%s` to `ref %s`", fs.key.type.toChars(), fs.prm.type.toChars());
2186 1
                return setError();
2187
            }
2188
        }
2189

2190 1
        auto s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
2191 1
        if (LabelStatement ls = checkLabeledLoop(sc, fs))
2192 1
            ls.gotoTarget = s;
2193 1
        result = s.statementSemantic(sc);
2194
    }
2195

2196
    override void visit(IfStatement ifs)
2197
    {
2198
        /* https://dlang.org/spec/statement.html#IfStatement
2199
         */
2200

2201
        // check in syntax level
2202 1
        ifs.condition = checkAssignmentAsCondition(ifs.condition);
2203

2204 1
        auto sym = new ScopeDsymbol();
2205 1
        sym.parent = sc.scopesym;
2206 1
        sym.endlinnum = ifs.endloc.linnum;
2207 1
        Scope* scd = sc.push(sym);
2208 1
        if (ifs.prm)
2209
        {
2210
            /* Declare prm, which we will set to be the
2211
             * result of condition.
2212
             */
2213 1
            auto ei = new ExpInitializer(ifs.loc, ifs.condition);
2214 1
            ifs.match = new VarDeclaration(ifs.loc, ifs.prm.type, ifs.prm.ident, ei);
2215 1
            ifs.match.parent = scd.func;
2216 1
            ifs.match.storage_class |= ifs.prm.storageClass;
2217 1
            ifs.match.dsymbolSemantic(scd);
2218

2219 1
            auto de = new DeclarationExp(ifs.loc, ifs.match);
2220 1
            auto ve = new VarExp(ifs.loc, ifs.match);
2221 1
            ifs.condition = new CommaExp(ifs.loc, de, ve);
2222 1
            ifs.condition = ifs.condition.expressionSemantic(scd);
2223

2224 1
            if (ifs.match.edtor)
2225
            {
2226 1
                Statement sdtor = new DtorExpStatement(ifs.loc, ifs.match.edtor, ifs.match);
2227 1
                sdtor = new ScopeGuardStatement(ifs.loc, TOK.onScopeExit, sdtor);
2228 1
                ifs.ifbody = new CompoundStatement(ifs.loc, sdtor, ifs.ifbody);
2229 1
                ifs.match.storage_class |= STC.nodtor;
2230

2231
                // the destructor is always called
2232
                // whether the 'ifbody' is executed or not
2233 1
                Statement sdtor2 = new DtorExpStatement(ifs.loc, ifs.match.edtor, ifs.match);
2234 1
                if (ifs.elsebody)
2235 1
                    ifs.elsebody = new CompoundStatement(ifs.loc, sdtor2, ifs.elsebody);
2236
                else
2237 1
                    ifs.elsebody = sdtor2;
2238
            }
2239
        }
2240
        else
2241
        {
2242 1
            if (ifs.condition.op == TOK.dotIdentifier)
2243 1
                (cast(DotIdExp)ifs.condition).noderef = true;
2244

2245 1
            ifs.condition = ifs.condition.expressionSemantic(scd);
2246 1
            ifs.condition = resolveProperties(scd, ifs.condition);
2247 1
            ifs.condition = ifs.condition.addDtorHook(scd);
2248
        }
2249 1
        if (checkNonAssignmentArrayOp(ifs.condition))
2250 1
            ifs.condition = ErrorExp.get();
2251 1
        ifs.condition = checkGC(scd, ifs.condition);
2252

2253
        // Convert to boolean after declaring prm so this works:
2254
        //  if (S prm = S()) {}
2255
        // where S is a struct that defines opCast!bool.
2256 1
        ifs.condition = ifs.condition.toBoolean(scd);
2257

2258
        // If we can short-circuit evaluate the if statement, don't do the
2259
        // semantic analysis of the skipped code.
2260
        // This feature allows a limited form of conditional compilation.
2261 1
        ifs.condition = ifs.condition.optimize(WANTvalue);
2262

2263
        // Save 'root' of two branches (then and else) at the point where it forks
2264 1
        CtorFlow ctorflow_root = scd.ctorflow.clone();
2265

2266 1
        ifs.ifbody = ifs.ifbody.semanticNoScope(scd);
2267 1
        scd.pop();
2268

2269 1
        CtorFlow ctorflow_then = sc.ctorflow;   // move flow results
2270 1
        sc.ctorflow = ctorflow_root;            // reset flow analysis back to root
2271 1
        if (ifs.elsebody)
2272 1
            ifs.elsebody = ifs.elsebody.semanticScope(sc, null, null);
2273

2274
        // Merge 'then' results into 'else' results
2275 1
        sc.merge(ifs.loc, ctorflow_then);
2276

2277 1
        ctorflow_then.freeFieldinit();          // free extra copy of the data
2278

2279 1
        if (ifs.condition.op == TOK.error ||
2280 1
            (ifs.ifbody && ifs.ifbody.isErrorStatement()) ||
2281 1
            (ifs.elsebody && ifs.elsebody.isErrorStatement()))
2282
        {
2283 1
            return setError();
2284
        }
2285 1
        result = ifs;
2286
    }
2287

2288
    override void visit(ConditionalStatement cs)
2289
    {
2290
        //printf("ConditionalStatement::semantic()\n");
2291

2292
        // If we can short-circuit evaluate the if statement, don't do the
2293
        // semantic analysis of the skipped code.
2294
        // This feature allows a limited form of conditional compilation.
2295 0
        if (cs.condition.include(sc))
2296
        {
2297 0
            DebugCondition dc = cs.condition.isDebugCondition();
2298 0
            if (dc)
2299
            {
2300 0
                sc = sc.push();
2301 0
                sc.flags |= SCOPE.debug_;
2302 0
                cs.ifbody = cs.ifbody.statementSemantic(sc);
2303 0
                sc.pop();
2304
            }
2305
            else
2306 0
                cs.ifbody = cs.ifbody.statementSemantic(sc);
2307 0
            result = cs.ifbody;
2308
        }
2309
        else
2310
        {
2311 0
            if (cs.elsebody)
2312 0
                cs.elsebody = cs.elsebody.statementSemantic(sc);
2313 0
            result = cs.elsebody;
2314
        }
2315
    }
2316

2317
    override void visit(PragmaStatement ps)
2318
    {
2319
        /* https://dlang.org/spec/statement.html#pragma-statement
2320
         */
2321
        // Should be merged with PragmaDeclaration
2322

2323
        //printf("PragmaStatement::semantic() %s\n", ps.toChars());
2324
        //printf("body = %p\n", ps._body);
2325 1
        if (ps.ident == Id.msg)
2326
        {
2327 1
            if (ps.args)
2328
            {
2329 1
                foreach (arg; *ps.args)
2330
                {
2331 1
                    sc = sc.startCTFE();
2332 1
                    auto e = arg.expressionSemantic(sc);
2333 1
                    e = resolveProperties(sc, e);
2334 1
                    sc = sc.endCTFE();
2335

2336
                    // pragma(msg) is allowed to contain types as well as expressions
2337 1
                    e = ctfeInterpretForPragmaMsg(e);
2338 1
                    if (e.op == TOK.error)
2339
                    {
2340 1
                        errorSupplemental(ps.loc, "while evaluating `pragma(msg, %s)`", arg.toChars());
2341 1
                        return setError();
2342
                    }
2343 1
                    if (auto se = e.toStringExp())
2344
                    {
2345 1
                        const slice = se.toUTF8(sc).peekString();
2346 1
                        fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr);
2347
                    }
2348
                    else
2349 1
                        fprintf(stderr, "%s", e.toChars());
2350
                }
2351 1
                fprintf(stderr, "\n");
2352
            }
2353
        }
2354 1
        else if (ps.ident == Id.lib)
2355
        {
2356
            version (all)
2357
            {
2358
                /* Should this be allowed?
2359
                 */
2360 0
                ps.error("`pragma(lib)` not allowed as statement");
2361 0
                return setError();
2362
            }
2363
            else
2364
            {
2365
                if (!ps.args || ps.args.dim != 1)
2366
                {
2367
                    ps.error("`string` expected for library name");
2368
                    return setError();
2369
                }
2370
                else
2371
                {
2372
                    auto se = semanticString(sc, (*ps.args)[0], "library name");
2373
                    if (!se)
2374
                        return setError();
2375

2376
                    if (global.params.verbose)
2377
                    {
2378
                        message("library   %.*s", cast(int)se.len, se.string);
2379
                    }
2380
                }
2381
            }
2382
        }
2383 1
        else if (ps.ident == Id.linkerDirective)
2384
        {
2385
            /* Should this be allowed?
2386
             */
2387 0
            ps.error("`pragma(linkerDirective)` not allowed as statement");
2388 0
            return setError();
2389
        }
2390 1
        else if (ps.ident == Id.startaddress)
2391
        {
2392 0
            if (!ps.args || ps.args.dim != 1)
2393 0
                ps.error("function name expected for start address");
2394
            else
2395
            {
2396 0
                Expression e = (*ps.args)[0];
2397 0
                sc = sc.startCTFE();
2398 0
                e = e.expressionSemantic(sc);
2399 0
                e = resolveProperties(sc, e);
2400 0
                sc = sc.endCTFE();
2401

2402 0
                e = e.ctfeInterpret();
2403 0
                (*ps.args)[0] = e;
2404 0
                Dsymbol sa = getDsymbol(e);
2405 0
                if (!sa || !sa.isFuncDeclaration())
2406
                {
2407 0
                    ps.error("function name expected for start address, not `%s`", e.toChars());
2408 0
                    return setError();
2409
                }
2410 0
                if (ps._body)
2411
                {
2412 0
                    ps._body = ps._body.statementSemantic(sc);
2413 0
                    if (ps._body.isErrorStatement())
2414
                    {
2415 0
                        result = ps._body;
2416 0
                        return;
2417
                    }
2418
                }
2419 0
                result = ps;
2420 0
                return;
2421
            }
2422
        }
2423 1
        else if (ps.ident == Id.Pinline)
2424
        {
2425 1
            PINLINE inlining = PINLINE.default_;
2426 1
            if (!ps.args || ps.args.dim == 0)
2427 1
                inlining = PINLINE.default_;
2428 1
            else if (!ps.args || ps.args.dim != 1)
2429
            {
2430 1
                ps.error("boolean expression expected for `pragma(inline)`");
2431 1
                return setError();
2432
            }
2433
            else
2434
            {
2435 1
                Expression e = (*ps.args)[0];
2436 1
                if (e.op != TOK.int64 || !e.type.equals(Type.tbool))
2437
                {
2438 1
                    ps.error("pragma(inline, true or false) expected, not `%s`", e.toChars());
2439 1
                    return setError();
2440
                }
2441

2442 1
                if (e.isBool(true))
2443 1
                    inlining = PINLINE.always;
2444 1
                else if (e.isBool(false))
2445 1
                    inlining = PINLINE.never;
2446

2447 1
                    FuncDeclaration fd = sc.func;
2448 1
                if (!fd)
2449
                {
2450 0
                    ps.error("`pragma(inline)` is not inside a function");
2451 0
                    return setError();
2452
                }
2453 1
                fd.inlining = inlining;
2454
            }
2455
        }
2456 1
        else if (!global.params.ignoreUnsupportedPragmas)
2457
        {
2458 1
            ps.error("unrecognized `pragma(%s)`", ps.ident.toChars());
2459 1
            return setError();
2460
        }
2461

2462 1
        if (ps._body)
2463
        {
2464 1
            if (ps.ident == Id.msg || ps.ident == Id.startaddress)
2465
            {
2466 1
                ps.error("`pragma(%s)` is missing a terminating `;`", ps.ident.toChars());
2467 1
                return setError();
2468
            }
2469 1
            ps._body = ps._body.statementSemantic(sc);
2470
        }
2471 1
        result = ps._body;
2472
    }
2473

2474
    override void visit(StaticAssertStatement s)
2475
    {
2476 1
        s.sa.semantic2(sc);
2477
    }
2478

2479
    override void visit(SwitchStatement ss)
2480
    {
2481
        /* https://dlang.org/spec/statement.html#switch-statement
2482
         */
2483

2484
        //printf("SwitchStatement::semantic(%p)\n", ss);
2485 1
        ss.tryBody = sc.tryBody;
2486 1
        ss.tf = sc.tf;
2487 1
        if (ss.cases)
2488
        {
2489 1
            result = ss; // already run
2490 1
            return;
2491
        }
2492

2493 1
        bool conditionError = false;
2494 1
        ss.condition = ss.condition.expressionSemantic(sc);
2495 1
        ss.condition = resolveProperties(sc, ss.condition);
2496

2497 1
        Type att = null;
2498 1
        TypeEnum te = null;
2499 1
        while (ss.condition.op != TOK.error)
2500
        {
2501
            // preserve enum type for final switches
2502 1
            if (ss.condition.type.ty == Tenum)
2503 1
                te = cast(TypeEnum)ss.condition.type;
2504 1
            if (ss.condition.type.isString())
2505
            {
2506
                // If it's not an array, cast it to one
2507 1
                if (ss.condition.type.ty != Tarray)
2508
                {
2509 1
                    ss.condition = ss.condition.implicitCastTo(sc, ss.condition.type.nextOf().arrayOf());
2510
                }
2511 1
                ss.condition.type = ss.condition.type.constOf();
2512 1
                break;
2513
            }
2514 1
            ss.condition = integralPromotions(ss.condition, sc);
2515 1
            if (ss.condition.op != TOK.error && ss.condition.type.isintegral())
2516 1
                break;
2517

2518 1
            auto ad = isAggregate(ss.condition.type);
2519 1
            if (ad && ad.aliasthis && ss.condition.type != att)
2520
            {
2521 1
                if (!att && ss.condition.type.checkAliasThisRec())
2522 1
                    att = ss.condition.type;
2523 1
                if (auto e = resolveAliasThis(sc, ss.condition, true))
2524
                {
2525 1
                    ss.condition = e;
2526 1
                    continue;
2527
                }
2528
            }
2529

2530 1
            if (ss.condition.op != TOK.error)
2531
            {
2532 1
                ss.error("`%s` must be of integral or string type, it is a `%s`",
2533
                    ss.condition.toChars(), ss.condition.type.toChars());
2534 1
                conditionError = true;
2535 1
                break;
2536
            }
2537
        }
2538 1
        if (checkNonAssignmentArrayOp(ss.condition))
2539 1
            ss.condition = ErrorExp.get();
2540 1
        ss.condition = ss.condition.optimize(WANTvalue);
2541 1
        ss.condition = checkGC(sc, ss.condition);
2542 1
        if (ss.condition.op == TOK.error)
2543 1
            conditionError = true;
2544

2545 1
        bool needswitcherror = false;
2546

2547 1
        ss.lastVar = sc.lastVar;
2548

2549 1
        sc = sc.push();
2550 1
        sc.sbreak = ss;
2551 1
        sc.sw = ss;
2552

2553 1
        ss.cases = new CaseStatements();
2554 1
        const inLoopSave = sc.inLoop;
2555 1
        sc.inLoop = true;        // BUG: should use Scope::mergeCallSuper() for each case instead
2556 1
        ss._body = ss._body.statementSemantic(sc);
2557 1
        sc.inLoop = inLoopSave;
2558

2559 1
        if (conditionError || (ss._body && ss._body.isErrorStatement()))
2560
        {
2561 1
            sc.pop();
2562 1
            return setError();
2563
        }
2564

2565
        // Resolve any goto case's with exp
2566
      Lgotocase:
2567 1
        foreach (gcs; ss.gotoCases)
2568
        {
2569 1
            if (!gcs.exp)
2570
            {
2571 1
                gcs.error("no `case` statement following `goto case;`");
2572 1
                sc.pop();
2573 1
                return setError();
2574
            }
2575

2576 1
            for (Scope* scx = sc; scx; scx = scx.enclosing)
2577
            {
2578 1
                if (!scx.sw)
2579 1
                    continue;
2580 1
                foreach (cs; *scx.sw.cases)
2581
                {
2582 1
                    if (cs.exp.equals(gcs.exp))
2583
                    {
2584 1
                        gcs.cs = cs;
2585 1
                        continue Lgotocase;
2586
                    }
2587
                }
2588
            }
2589 1
            gcs.error("`case %s` not found", gcs.exp.toChars());
2590 1
            sc.pop();
2591 1
            return setError();
2592
        }
2593

2594 1
        if (ss.isFinal)
2595
        {
2596 1
            Type t = ss.condition.type;
2597 1
            Dsymbol ds;
2598 1
            EnumDeclaration ed = null;
2599 1
            if (t && ((ds = t.toDsymbol(sc)) !is null))
2600 1
                ed = ds.isEnumDeclaration(); // typedef'ed enum
2601 1
            if (!ed && te && ((ds = te.toDsymbol(sc)) !is null))
2602 1
                ed = ds.isEnumDeclaration();
2603 1
            if (ed)
2604
            {
2605
              Lmembers:
2606 1
                foreach (es; *ed.members)
2607
                {
2608 1
                    EnumMember em = es.isEnumMember();
2609 1
                    if (em)
2610
                    {
2611 1
                        foreach (cs; *ss.cases)
2612
                        {
2613 1
                            if (cs.exp.equals(em.value) || (!cs.exp.type.isString() && !em.value.type.isString() && cs.exp.toInteger() == em.value.toInteger()))
2614 1
                                continue Lmembers;
2615
                        }
2616 1
                        ss.error("`enum` member `%s` not represented in `final switch`", em.toChars());
2617 1
                        sc.pop();
2618 1
                        return setError();
2619
                    }
2620
                }
2621
            }
2622
            else
2623 1
                needswitcherror = true;
2624
        }
2625

2626 1
        if (!sc.sw.sdefault && (!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on))
2627
        {
2628 1
            ss.hasNoDefault = 1;
2629

2630 1
            if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()))
2631 1
                ss.error("`switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`");
2632

2633
            // Generate runtime error if the default is hit
2634 1
            auto a = new Statements();
2635 1
            CompoundStatement cs;
2636 1
            Statement s;
2637

2638 1
            if (global.params.useSwitchError == CHECKENABLE.on &&
2639 1
                global.params.checkAction != CHECKACTION.halt)
2640
            {
2641 1
                if (global.params.checkAction == CHECKACTION.C)
2642
                {
2643
                    /* Rewrite as an assert(0) and let e2ir generate
2644
                     * the call to the C assert failure function
2645
                     */
2646 0
                    s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0));
2647
                }
2648
                else
2649
                {
2650 1
                    if (!verifyHookExist(ss.loc, *sc, Id.__switch_error, "generating assert messages"))
2651 1
                        return setError();
2652

2653 1
                    Expression sl = new IdentifierExp(ss.loc, Id.empty);
2654 1
                    sl = new DotIdExp(ss.loc, sl, Id.object);
2655 1
                    sl = new DotIdExp(ss.loc, sl, Id.__switch_error);
2656

2657 1
                    Expressions* args = new Expressions(2);
2658 1
                    (*args)[0] = new StringExp(ss.loc, ss.loc.filename.toDString());
2659 1
                    (*args)[1] = new IntegerExp(ss.loc.linnum);
2660

2661 1
                    sl = new CallExp(ss.loc, sl, args);
2662 1
                    sl.expressionSemantic(sc);
2663

2664 1
                    s = new SwitchErrorStatement(ss.loc, sl);
2665
                }
2666
            }
2667
            else
2668 1
                s = new ExpStatement(ss.loc, new HaltExp(ss.loc));
2669

2670 1
            a.reserve(2);
2671 1
            sc.sw.sdefault = new DefaultStatement(ss.loc, s);
2672 1
            a.push(ss._body);
2673 1
            if (ss._body.blockExit(sc.func, false) & BE.fallthru)
2674 1
                a.push(new BreakStatement(Loc.initial, null));
2675 1
            a.push(sc.sw.sdefault);
2676 1
            cs = new CompoundStatement(ss.loc, a);
2677 1
            ss._body = cs;
2678
        }
2679