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