C99 testsuite readiness: Compile more tests with -std=gnu89
[official-gcc.git] / gcc / d / dmd / statementsem.d
blob962ef625f5be3b694d76db61befa45cbb4c55a81
1 /**
2 * Does semantic analysis for statements.
4 * Specification: $(LINK2 https://dlang.org/spec/statement.html, Statements)
6 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/statementsem.d, _statementsem.d)
10 * Documentation: https://dlang.org/phobos/dmd_statementsem.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statementsem.d
14 module dmd.statementsem;
16 import core.stdc.stdio;
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.attrib;
25 import dmd.blockexit;
26 import dmd.clone;
27 import dmd.cond;
28 import dmd.ctorflow;
29 import dmd.dcast;
30 import dmd.dclass;
31 import dmd.declaration;
32 import dmd.denum;
33 import dmd.dimport;
34 import dmd.dinterpret;
35 import dmd.dmodule;
36 import dmd.dscope;
37 import dmd.dsymbol;
38 import dmd.dsymbolsem;
39 import dmd.dtemplate;
40 import dmd.errors;
41 import dmd.errorsink;
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.hdrgen;
49 import dmd.id;
50 import dmd.identifier;
51 import dmd.importc;
52 import dmd.init;
53 import dmd.intrange;
54 import dmd.location;
55 import dmd.mtype;
56 import dmd.mustuse;
57 import dmd.nogc;
58 import dmd.opover;
59 import dmd.parse;
60 import dmd.common.outbuffer;
61 import dmd.root.string;
62 import dmd.semantic2;
63 import dmd.sideeffect;
64 import dmd.statement;
65 import dmd.staticassert;
66 import dmd.target;
67 import dmd.tokens;
68 import dmd.typesem;
69 import dmd.visitor;
71 version (DMDLIB)
73 version = CallbackAPI;
76 /*****************************************
77 * CTFE requires FuncDeclaration::labtab for the interpretation.
78 * So fixing the label name inside in/out contracts is necessary
79 * for the uniqueness in labtab.
80 * Params:
81 * sc = context
82 * ident = statement label name to be adjusted
83 * Returns:
84 * adjusted label name
86 private Identifier fixupLabelName(Scope* sc, Identifier ident)
88 uint flags = (sc.flags & SCOPE.contract);
89 const id = ident.toString();
90 if (flags && flags != SCOPE.invariant_ &&
91 !(id.length >= 2 && id[0] == '_' && id[1] == '_')) // does not start with "__"
93 OutBuffer buf;
94 buf.writestring(flags == SCOPE.require ? "__in_" : "__out_");
95 buf.writestring(ident.toString());
97 ident = Identifier.idPool(buf[]);
99 return ident;
102 /*******************************************
103 * Check to see if statement is the innermost labeled statement.
104 * Params:
105 * sc = context
106 * statement = Statement to check
107 * Returns:
108 * if `true`, then the `LabelStatement`, otherwise `null`
110 private LabelStatement checkLabeledLoop(Scope* sc, Statement statement) @safe
112 if (sc.slabel && sc.slabel.statement == statement)
114 return sc.slabel;
116 return null;
119 /***********************************************************
120 * Check an assignment is used as a condition.
121 * Intended to be use before the `semantic` call on `e`.
122 * Params:
123 * e = condition expression which is not yet run semantic analysis.
124 * Returns:
125 * `e` or ErrorExp.
127 private Expression checkAssignmentAsCondition(Expression e, Scope* sc)
129 if (sc.flags & SCOPE.Cfile)
130 return e;
131 auto ec = lastComma(e);
132 if (ec.op == EXP.assign)
134 error(ec.loc, "assignment cannot be used as a condition, perhaps `==` was meant?");
135 return ErrorExp.get();
137 return e;
140 // Performs semantic analysis in Statement AST nodes
141 extern(C++) Statement statementSemantic(Statement s, Scope* sc)
143 import dmd.compiler;
145 version (CallbackAPI)
146 Compiler.onStatementSemanticStart(s, sc);
148 Statement result = statementSemanticVisit(s, sc);
150 version (CallbackAPI)
151 Compiler.onStatementSemanticDone(s, sc);
153 return result;
156 package (dmd)
157 Statement statementSemanticVisit(Statement s, Scope* sc)
159 Statement result;
161 void setError()
163 result = new ErrorStatement();
166 void visitDefaultCase(Statement s)
168 result = s;
171 void visitError(ErrorStatement s)
173 result = s;
176 void visitPeel(PeelStatement s)
178 /* "peel" off this wrapper, and don't run semantic()
179 * on the result.
181 result = s.s;
184 void visitExp(ExpStatement s)
186 /* https://dlang.org/spec/statement.html#expression-statement
189 if (!s.exp)
191 result = s;
192 return;
194 //printf("ExpStatement::semantic() %s\n", exp.toChars());
196 // Allow CommaExp in ExpStatement because return isn't used
197 CommaExp.allow(s.exp);
199 s.exp = s.exp.expressionSemantic(sc);
200 s.exp = resolveProperties(sc, s.exp);
201 s.exp = s.exp.addDtorHook(sc);
202 if (checkNonAssignmentArrayOp(s.exp))
203 s.exp = ErrorExp.get();
204 if (auto f = isFuncAddress(s.exp))
206 if (f.checkForwardRef(s.exp.loc))
207 s.exp = ErrorExp.get();
209 if (checkMustUse(s.exp, sc))
210 s.exp = ErrorExp.get();
211 if (!(sc.flags & SCOPE.Cfile) && discardValue(s.exp))
212 s.exp = ErrorExp.get();
214 s.exp = s.exp.optimize(WANTvalue);
215 s.exp = checkGC(sc, s.exp);
216 if (s.exp.op == EXP.error)
217 return setError();
218 result = s;
221 void visitDtorExp(DtorExpStatement s)
223 visitExp(s);
226 void visitMixin(MixinStatement cs)
228 /* https://dlang.org/spec/statement.html#mixin-statement
231 //printf("MixinStatement::semantic() %s\n", exp.toChars());
232 Statements* a = cs.flatten(sc);
233 if (!a)
234 return;
235 Statement s = new CompoundStatement(cs.loc, a);
236 result = s.statementSemantic(sc);
239 void visitCompound(CompoundStatement cs)
241 //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc);
242 version (none)
244 foreach (i, s; cs.statements)
246 if (s)
247 printf("[%d]: %s", i, s.toChars());
251 for (size_t i = 0; i < cs.statements.length;)
253 Statement s = (*cs.statements)[i];
254 if (!s)
256 ++i;
257 continue;
260 if (auto flt = s.flatten(sc))
262 cs.statements.remove(i);
263 cs.statements.insert(i, flt);
264 continue;
266 s = s.statementSemantic(sc);
267 (*cs.statements)[i] = s;
268 if (!s)
270 /* Remove NULL statements from the list.
272 cs.statements.remove(i);
273 continue;
275 if (s.isErrorStatement())
277 result = s; // propagate error up the AST
278 ++i;
279 continue; // look for errors in rest of statements
282 // expand tuple variables in order to attach destruction/exception logic
283 if (auto es = s.isExpStatement())
285 if (es.exp && es.exp.isDeclarationExp())
287 auto de = es.exp.isDeclarationExp();
288 auto vd = de.declaration.isVarDeclaration();
289 if (vd && vd.aliasTuple && vd.aliasTuple.objects.length)
291 auto j = i;
292 cs.statements.insert(i, vd.aliasTuple.objects.length - 1, null);
293 vd.aliasTuple.foreachVar((v) { (*cs.statements)[j++] = toStatement(v); });
294 s = (*cs.statements)[i];
299 Statement sentry;
300 Statement sexception;
301 Statement sfinally;
303 (*cs.statements)[i] = s.scopeCode(sc, sentry, sexception, sfinally);
304 if (sentry)
306 sentry = sentry.statementSemantic(sc);
307 cs.statements.insert(i, sentry);
308 i++;
310 if (sexception)
311 sexception = sexception.statementSemantic(sc);
312 if (sexception)
314 /* Returns: true if statements[] are empty statements
316 static bool isEmpty(const Statement[] statements)
318 foreach (s; statements)
320 if (const cs = s.isCompoundStatement())
322 if (!isEmpty((*cs.statements)[]))
323 return false;
325 else
326 return false;
328 return true;
331 if (!sfinally && isEmpty((*cs.statements)[i + 1 .. cs.statements.length]))
334 else
336 /* Rewrite:
337 * s; s1; s2;
338 * As:
339 * s;
340 * try { s1; s2; }
341 * catch (Throwable __o)
342 * { sexception; throw __o; }
344 auto a = new Statements();
345 a.pushSlice((*cs.statements)[i + 1 .. cs.statements.length]);
346 cs.statements.setDim(i + 1);
348 Statement _body = new CompoundStatement(Loc.initial, a);
349 _body = new ScopeStatement(Loc.initial, _body, Loc.initial);
351 Identifier id = Identifier.generateId("__o");
353 Statement handler = new PeelStatement(sexception);
354 if (sexception.blockExit(sc.func, null) & BE.fallthru)
356 auto ts = new ThrowStatement(Loc.initial, new IdentifierExp(Loc.initial, id));
357 ts.internalThrow = true;
358 handler = new CompoundStatement(Loc.initial, handler, ts);
361 auto catches = new Catches();
362 auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
363 ctch.internalCatch = true;
364 catches.push(ctch);
366 Statement st = new TryCatchStatement(Loc.initial, _body, catches);
367 if (sfinally)
368 st = new TryFinallyStatement(Loc.initial, st, sfinally);
369 st = st.statementSemantic(sc);
371 cs.statements.push(st);
372 break;
375 else if (sfinally)
377 if (0 && i + 1 == cs.statements.length)
379 cs.statements.push(sfinally);
381 else
383 /* Rewrite:
384 * s; s1; s2;
385 * As:
386 * s; try { s1; s2; } finally { sfinally; }
388 auto a = new Statements();
389 a.pushSlice((*cs.statements)[i + 1 .. cs.statements.length]);
390 cs.statements.setDim(i + 1);
392 auto _body = new CompoundStatement(Loc.initial, a);
393 Statement stf = new TryFinallyStatement(Loc.initial, _body, sfinally);
394 stf = stf.statementSemantic(sc);
395 cs.statements.push(stf);
396 break;
399 i++;
402 /* Flatten them in place
404 void flattenStatements(ref Statements statements)
406 for (size_t i = 0; i < statements.length;)
408 if (auto s = statements[i])
410 if (auto flt = s.flatten(sc))
412 statements.remove(i);
413 statements.insert(i, flt);
414 continue;
417 ++i;
421 /* https://issues.dlang.org/show_bug.cgi?id=11653
422 * 'semantic' may return another CompoundStatement
423 * (eg. CaseRangeStatement), so flatten it here.
425 flattenStatements(*cs.statements);
427 foreach (s; *cs.statements)
429 if (!s)
430 continue;
432 if (auto se = s.isErrorStatement())
434 result = se;
435 return;
439 if (cs.statements.length == 1)
441 result = (*cs.statements)[0];
442 return;
444 result = cs;
447 void visitUnrolledLoop(UnrolledLoopStatement uls)
449 //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc);
450 Scope* scd = sc.push();
451 scd.sbreak = uls;
452 scd.scontinue = uls;
454 Statement serror = null;
455 foreach (i, ref s; *uls.statements)
457 if (s)
459 //printf("[%d]: %s\n", i, s.toChars());
460 s = s.statementSemantic(scd);
461 if (s && !serror)
462 serror = s.isErrorStatement();
466 scd.pop();
467 result = serror ? serror : uls;
470 void visitScope(ScopeStatement ss)
472 //printf("ScopeStatement::semantic(sc = %p)\n", sc);
473 if (!ss.statement)
475 result = ss;
476 return;
479 ScopeDsymbol sym = new ScopeDsymbol();
480 sym.parent = sc.scopesym;
481 sym.endlinnum = ss.endloc.linnum;
482 sc = sc.push(sym);
484 Statements* a = ss.statement.flatten(sc);
485 if (a)
487 ss.statement = new CompoundStatement(ss.loc, a);
490 ss.statement = ss.statement.statementSemantic(sc);
491 if (ss.statement)
493 if (ss.statement.isErrorStatement())
495 sc.pop();
496 result = ss.statement;
497 return;
500 Statement sentry;
501 Statement sexception;
502 Statement sfinally;
503 ss.statement = ss.statement.scopeCode(sc, sentry, sexception, sfinally);
504 assert(!sentry);
505 assert(!sexception);
506 if (sfinally)
508 //printf("adding sfinally\n");
509 sfinally = sfinally.statementSemantic(sc);
510 ss.statement = new CompoundStatement(ss.loc, ss.statement, sfinally);
513 sc.pop();
514 result = ss;
517 void visitForwarding(ForwardingStatement ss)
519 assert(ss.sym);
520 for (Scope* csc = sc; !ss.sym.parent; csc = csc.enclosing)
522 assert(csc);
523 ss.sym.parent = csc.scopesym;
525 sc = sc.push(ss.sym);
526 sc.sbreak = ss;
527 sc.scontinue = ss;
528 ss.statement = ss.statement.statementSemantic(sc);
529 sc = sc.pop();
530 result = ss.statement;
533 void visitWhile(WhileStatement ws)
535 /* Rewrite as a for(;condition;) loop
536 * https://dlang.org/spec/statement.html#while-statement
538 Expression cond = ws.condition;
539 Statement _body = ws._body;
540 if (ws.param)
543 * If the while loop is of form `while(auto a = exp) { loop_body }`,
544 * rewrite to:
546 * while(true)
547 * if (auto a = exp)
548 * { loop_body }
549 * else
550 * { break; }
552 _body = new IfStatement(ws.loc, ws.param, ws.condition, ws._body, new BreakStatement(ws.loc, null), ws.endloc);
553 cond = IntegerExp.createBool(true);
555 Statement s = new ForStatement(ws.loc, null, cond, null, _body, ws.endloc);
556 s = s.statementSemantic(sc);
557 result = s;
560 void visitDo(DoStatement ds)
562 /* https://dlang.org/spec/statement.html#do-statement
564 const inLoopSave = sc.inLoop;
565 sc.inLoop = true;
566 if (ds._body)
567 ds._body = ds._body.semanticScope(sc, ds, ds, null);
568 sc.inLoop = inLoopSave;
570 if (ds.condition.op == EXP.dotIdentifier)
571 (cast(DotIdExp)ds.condition).noderef = true;
573 // check in syntax level
574 ds.condition = checkAssignmentAsCondition(ds.condition, sc);
576 ds.condition = ds.condition.expressionSemantic(sc);
577 ds.condition = resolveProperties(sc, ds.condition);
578 if (checkNonAssignmentArrayOp(ds.condition))
579 ds.condition = ErrorExp.get();
580 ds.condition = ds.condition.optimize(WANTvalue);
581 ds.condition = checkGC(sc, ds.condition);
583 ds.condition = ds.condition.toBoolean(sc);
585 if (ds.condition.op == EXP.error)
586 return setError();
587 if (ds._body && ds._body.isErrorStatement())
589 result = ds._body;
590 return;
593 result = ds;
596 void visitFor(ForStatement fs)
598 /* https://dlang.org/spec/statement.html#for-statement
600 //printf("ForStatement::semantic %s\n", fs.toChars());
602 if (fs._init)
604 /* Rewrite:
605 * for (auto v1 = i1, v2 = i2; condition; increment) { ... }
606 * to:
607 * { auto v1 = i1, v2 = i2; for (; condition; increment) { ... } }
608 * then lowered to:
609 * auto v1 = i1;
610 * try {
611 * auto v2 = i2;
612 * try {
613 * for (; condition; increment) { ... }
614 * } finally { v2.~this(); }
615 * } finally { v1.~this(); }
617 auto ainit = new Statements();
618 ainit.push(fs._init);
619 fs._init = null;
620 ainit.push(fs);
621 Statement s = new CompoundStatement(fs.loc, ainit);
622 s = new ScopeStatement(fs.loc, s, fs.endloc);
623 s = s.statementSemantic(sc);
624 if (!s.isErrorStatement())
626 if (LabelStatement ls = checkLabeledLoop(sc, fs))
627 ls.gotoTarget = fs;
628 fs.relatedLabeled = s;
630 result = s;
631 return;
633 assert(fs._init is null);
635 auto sym = new ScopeDsymbol();
636 sym.parent = sc.scopesym;
637 sym.endlinnum = fs.endloc.linnum;
638 sc = sc.push(sym);
639 sc.inLoop = true;
641 if (fs.condition)
643 if (fs.condition.op == EXP.dotIdentifier)
644 (cast(DotIdExp)fs.condition).noderef = true;
646 // check in syntax level
647 fs.condition = checkAssignmentAsCondition(fs.condition, sc);
649 fs.condition = fs.condition.expressionSemantic(sc);
650 fs.condition = resolveProperties(sc, fs.condition);
651 if (checkNonAssignmentArrayOp(fs.condition))
652 fs.condition = ErrorExp.get();
653 fs.condition = fs.condition.optimize(WANTvalue);
654 fs.condition = checkGC(sc, fs.condition);
656 fs.condition = fs.condition.toBoolean(sc);
658 if (fs.increment)
660 CommaExp.allow(fs.increment);
661 fs.increment = fs.increment.expressionSemantic(sc);
662 fs.increment = resolveProperties(sc, fs.increment);
663 // @@@DEPRECATED_2.112@@@
664 // remove gagging and deprecation() to turn deprecation into an error when
665 // deprecation cycle is over
666 const olderrors = global.startGagging();
667 discardValue(fs.increment);
668 if (global.endGagging(olderrors))
669 deprecation(fs.increment.loc, "`%s` has no effect", fs.increment.toChars());
670 if (checkNonAssignmentArrayOp(fs.increment))
671 fs.increment = ErrorExp.get();
672 fs.increment = fs.increment.optimize(WANTvalue);
673 fs.increment = checkGC(sc, fs.increment);
676 sc.sbreak = fs;
677 sc.scontinue = fs;
678 if (fs._body)
679 fs._body = fs._body.semanticNoScope(sc);
681 sc.pop();
683 if (fs.condition && fs.condition.op == EXP.error ||
684 fs.increment && fs.increment.op == EXP.error ||
685 fs._body && fs._body.isErrorStatement())
686 return setError();
687 result = fs;
690 void visitForeach(ForeachStatement fs)
692 /* https://dlang.org/spec/statement.html#foreach-statement
695 //printf("ForeachStatement::semantic() %p\n", fs);
697 /******
698 * Issue error if any of the ForeachTypes were not supplied and could not be inferred.
699 * Returns:
700 * true if error issued
702 static bool checkForArgTypes(ForeachStatement fs)
704 bool result = false;
705 foreach (p; *fs.parameters)
707 if (!p.type)
709 error(fs.loc, "cannot infer type for `foreach` variable `%s`, perhaps set it explicitly", p.ident.toChars());
710 p.type = Type.terror;
711 result = true;
714 return result;
717 const loc = fs.loc;
718 const dim = fs.parameters.length;
720 fs.func = sc.func;
721 if (fs.func.fes)
722 fs.func = fs.func.fes.func;
724 VarDeclaration vinit = null;
725 fs.aggr = fs.aggr.expressionSemantic(sc);
726 fs.aggr = resolveProperties(sc, fs.aggr);
727 fs.aggr = fs.aggr.optimize(WANTvalue);
728 if (fs.aggr.op == EXP.error)
729 return setError();
730 Expression oaggr = fs.aggr; // remember original for error messages
731 if (fs.aggr.type && fs.aggr.type.toBasetype().ty == Tstruct &&
732 (cast(TypeStruct)(fs.aggr.type.toBasetype())).sym.dtor &&
733 !fs.aggr.isTypeExp() && !fs.aggr.isLvalue())
735 // https://issues.dlang.org/show_bug.cgi?id=14653
736 // Extend the life of rvalue aggregate till the end of foreach.
737 vinit = copyToTemp(STC.rvalue, "__aggr", fs.aggr);
738 vinit.endlinnum = fs.endloc.linnum;
739 vinit.dsymbolSemantic(sc);
740 fs.aggr = new VarExp(fs.aggr.loc, vinit);
743 /* If aggregate is a vector type, add the .array to make it a static array
745 if (fs.aggr.type)
746 if (auto tv = fs.aggr.type.toBasetype().isTypeVector())
748 auto vae = new VectorArrayExp(fs.aggr.loc, fs.aggr);
749 vae.type = tv.basetype;
750 fs.aggr = vae;
753 Dsymbol sapply = null; // the inferred opApply() or front() function
754 if (!inferForeachAggregate(sc, fs.op == TOK.foreach_, fs.aggr, sapply))
756 assert(oaggr.type);
758 error(fs.loc, "invalid `%s` aggregate `%s` of type `%s`",
759 Token.toChars(fs.op), oaggr.toChars(), oaggr.type.toPrettyChars());
761 if (auto ad = isAggregate(fs.aggr.type))
763 if (fs.op == TOK.foreach_reverse_)
765 fs.loc.errorSupplemental("`foreach_reverse` works with bidirectional ranges"~
766 " (implementing `back` and `popBack`), aggregates implementing" ~
767 " `opApplyReverse`, or the result of an aggregate's `.tupleof` property");
768 fs.loc.errorSupplemental("https://dlang.org/phobos/std_range_primitives.html#isBidirectionalRange");
770 else
772 fs.loc.errorSupplemental("`foreach` works with input ranges"~
773 " (implementing `front` and `popFront`), aggregates implementing" ~
774 " `opApply`, or the result of an aggregate's `.tupleof` property");
775 fs.loc.errorSupplemental("https://dlang.org/phobos/std_range_primitives.html#isInputRange");
779 return setError();
782 Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
784 /* Check for inference errors
786 if (!inferApplyArgTypes(fs, sc, sapply))
789 Try and extract the parameter count of the opApply callback function, e.g.:
790 int opApply(int delegate(int, float)) => 2 args
792 bool foundMismatch = false;
793 size_t foreachParamCount = 0;
794 if (sapplyOld)
796 if (FuncDeclaration fd = sapplyOld.isFuncDeclaration())
798 auto fparameters = fd.getParameterList();
800 if (fparameters.length == 1)
802 // first param should be the callback function
803 Parameter fparam = fparameters[0];
804 if ((fparam.type.ty == Tpointer ||
805 fparam.type.ty == Tdelegate) &&
806 fparam.type.nextOf().ty == Tfunction)
808 TypeFunction tf = cast(TypeFunction)fparam.type.nextOf();
809 foreachParamCount = tf.parameterList.length;
810 foundMismatch = true;
816 //printf("dim = %d, parameters.length = %d\n", dim, parameters.length);
817 if (foundMismatch && dim != foreachParamCount)
819 const(char)* plural = foreachParamCount > 1 ? "s" : "";
820 error(fs.loc, "cannot infer argument types, expected %llu argument%s, not %llu",
821 cast(ulong) foreachParamCount, plural, cast(ulong) dim);
823 else
824 error(fs.loc, "cannot uniquely infer `foreach` argument types");
826 return setError();
829 Type tab = fs.aggr.type.toBasetype();
831 if (tab.ty == Ttuple) // don't generate new scope for tuple loops
833 Statement s = makeTupleForeach(sc, false, false, fs, null, false).statement;
834 if (vinit)
835 s = new CompoundStatement(loc, new ExpStatement(loc, vinit), s);
836 result = s.statementSemantic(sc);
837 return;
840 auto sym = new ScopeDsymbol();
841 sym.parent = sc.scopesym;
842 sym.endlinnum = fs.endloc.linnum;
843 auto sc2 = sc.push(sym);
844 sc2.inLoop = true;
846 foreach (Parameter p; *fs.parameters)
848 if (p.storageClass & STC.manifest)
850 error(fs.loc, "cannot declare `enum` loop variables for non-unrolled foreach");
852 if (p.storageClass & STC.alias_)
854 error(fs.loc, "cannot declare `alias` loop variables for non-unrolled foreach");
858 void retError()
860 sc2.pop();
861 result = new ErrorStatement();
864 void rangeError()
866 error(fs.loc, "cannot infer argument types");
867 return retError();
870 void retStmt(Statement s)
872 if (!s)
873 return retError();
874 s = s.statementSemantic(sc2);
875 sc2.pop();
876 result = s;
879 Type tn = null;
880 Type tnv = null;
881 Statement apply()
883 if (checkForArgTypes(fs))
884 return null;
886 TypeFunction tfld = null;
887 if (sapply)
889 if (auto fdapply = sapply.isFuncDeclaration())
891 assert(fdapply.type && fdapply.type.isTypeFunction());
892 tfld = fdapply.type.typeSemantic(loc, sc2).isTypeFunction();
893 goto Lget;
895 else if (tab.isTypeDelegate())
897 tfld = tab.nextOf().isTypeFunction();
898 Lget:
899 //printf("tfld = %s\n", tfld.toChars());
900 if (tfld.parameterList.parameters.length == 1)
902 Parameter p = tfld.parameterList[0];
903 if (p.type && p.type.isTypeDelegate())
905 auto t = p.type.typeSemantic(loc, sc2);
906 assert(t.ty == Tdelegate);
907 tfld = t.nextOf().isTypeFunction();
913 FuncExp flde = foreachBodyToFunction(sc2, fs, tfld);
914 if (!flde)
915 return null;
917 // Resolve any forward referenced goto's
918 foreach (ScopeStatement ss; *fs.gotos)
920 GotoStatement gs = ss.statement.isGotoStatement();
921 if (!gs.label.statement)
923 // 'Promote' it to this scope, and replace with a return
924 fs.cases.push(gs);
925 ss.statement = new ReturnStatement(Loc.initial, new IntegerExp(fs.cases.length + 1));
929 Expression e = null;
930 if (vinit)
932 e = new DeclarationExp(loc, vinit);
933 e = e.expressionSemantic(sc2);
934 if (e.op == EXP.error)
935 return null;
938 Expression ec;
939 switch (tab.ty)
941 case Tarray:
942 case Tsarray: ec = applyArray (fs, flde, tab, sc2, tn, tnv); break;
943 case Tdelegate: ec = applyDelegate (fs, flde, tab, sc2); break;
944 case Taarray: ec = applyAssocArray(fs, flde, tab); break;
945 default: ec = applyOpApply (fs, flde, tab, sc2, sapply); break;
947 if (!ec)
948 return null;
950 e = Expression.combine(e, ec);
951 return loopReturn(e, fs.cases, loc);
954 switch (tab.ty)
956 case Tarray:
957 case Tsarray:
959 if (checkForArgTypes(fs))
960 return retError();
962 if (dim < 1 || dim > 2)
964 error(fs.loc, "only one or two arguments for array `foreach`");
965 return retError();
968 // Finish semantic on all foreach parameter types.
969 foreach (i; 0 .. dim)
971 Parameter p = (*fs.parameters)[i];
972 p.type = p.type.typeSemantic(loc, sc2);
973 p.type = p.type.addStorageClass(p.storageClass);
976 tn = tab.nextOf().toBasetype();
978 if (dim == 2)
980 Type tindex = (*fs.parameters)[0].type;
981 if (!tindex.isintegral())
983 error(fs.loc, "foreach: key cannot be of non-integral type `%s`", tindex.toChars());
984 return retError();
986 /* What cases to deprecate implicit conversions for:
987 * 1. foreach aggregate is a dynamic array
988 * 2. foreach body is lowered to _aApply (see special case below).
990 Type tv = (*fs.parameters)[1].type.toBasetype();
991 if ((tab.isTypeDArray() ||
992 (tn.ty != tv.ty && tn.ty.isSomeChar && tv.ty.isSomeChar)) &&
993 !Type.tsize_t.implicitConvTo(tindex))
995 deprecation(fs.loc, "foreach: loop index implicitly converted from `size_t` to `%s`",
996 tindex.toChars());
1000 /* Look for special case of parsing char types out of char type
1001 * array.
1003 if (tn.ty.isSomeChar)
1005 int i = (dim == 1) ? 0 : 1; // index of value
1006 Parameter p = (*fs.parameters)[i];
1007 tnv = p.type.toBasetype();
1008 if (tnv.ty != tn.ty && tnv.ty.isSomeChar)
1010 if (p.storageClass & STC.ref_)
1012 error(fs.loc, "`foreach`: value of UTF conversion cannot be `ref`");
1013 return retError();
1015 if (dim == 2)
1017 p = (*fs.parameters)[0];
1018 if (p.storageClass & STC.ref_)
1020 error(fs.loc, "`foreach`: key cannot be `ref`");
1021 return retError();
1024 return retStmt(apply());
1028 // Declare the key
1029 if (dim == 2)
1031 Parameter p = (*fs.parameters)[0];
1032 fs.key = new VarDeclaration(loc, p.type.mutableOf(), Identifier.generateId("__key"), null);
1033 fs.key.storage_class |= STC.temp | STC.foreach_;
1034 if (fs.key.isReference())
1035 fs.key.storage_class |= STC.nodtor;
1037 if (p.storageClass & STC.ref_)
1039 if (fs.key.type.constConv(p.type) == MATCH.nomatch)
1041 error(fs.loc, "key type mismatch, `%s` to `ref %s`",
1042 fs.key.type.toChars(), p.type.toChars());
1043 return retError();
1046 if (auto ta = tab.isTypeSArray())
1048 IntRange dimrange = getIntRange(ta.dim);
1049 // https://issues.dlang.org/show_bug.cgi?id=12504
1050 dimrange.imax = SignExtendedNumber(dimrange.imax.value-1);
1051 if (!IntRange.fromType(fs.key.type).contains(dimrange))
1053 error(fs.loc, "index type `%s` cannot cover index range 0..%llu",
1054 p.type.toChars(), ta.dim.toInteger());
1055 return retError();
1057 fs.key.range = new IntRange(SignExtendedNumber(0), dimrange.imax);
1060 // Now declare the value
1062 Parameter p = (*fs.parameters)[dim - 1];
1063 fs.value = new VarDeclaration(loc, p.type, p.ident, null);
1064 fs.value.storage_class |= STC.foreach_;
1065 fs.value.storage_class |= p.storageClass & (STC.scope_ | STC.IOR | STC.TYPECTOR);
1066 if (fs.value.isReference())
1068 fs.value.storage_class |= STC.nodtor;
1070 if (fs.aggr.checkModifiable(sc2, ModifyFlags.noError) == Modifiable.initialization)
1071 fs.value.setInCtorOnly = true;
1073 Type t = tab.nextOf();
1074 if (t.constConv(p.type) == MATCH.nomatch)
1076 error(fs.loc, "argument type mismatch, `%s` to `ref %s`",
1077 t.toChars(), p.type.toChars());
1078 return retError();
1083 /* Convert to a ForStatement
1084 * foreach (key, value; a) body =>
1085 * for (T[] tmp = a[], size_t key; key < tmp.length; ++key)
1086 * { T value = tmp[k]; body }
1088 * foreach_reverse (key, value; a) body =>
1089 * for (T[] tmp = a[], size_t key = tmp.length; key--; )
1090 * { T value = tmp[k]; body }
1092 auto id = Identifier.generateId("__r");
1093 auto ie = new ExpInitializer(loc, new SliceExp(loc, fs.aggr, null, null));
1094 const valueIsRef = (*fs.parameters)[$ - 1].isReference();
1095 VarDeclaration tmp;
1096 if (fs.aggr.isArrayLiteralExp() && !valueIsRef)
1098 auto ale = fs.aggr.isArrayLiteralExp();
1099 size_t edim = ale.elements ? ale.elements.length : 0;
1100 auto telem = (*fs.parameters)[dim - 1].type;
1102 // https://issues.dlang.org/show_bug.cgi?id=12936
1103 // if telem has been specified explicitly,
1104 // converting array literal elements to telem might make it @nogc.
1105 fs.aggr = fs.aggr.implicitCastTo(sc, telem.sarrayOf(edim));
1106 if (fs.aggr.op == EXP.error)
1107 return retError();
1109 // for (T[edim] tmp = a, ...)
1110 tmp = new VarDeclaration(loc, fs.aggr.type, id, ie);
1112 else
1114 tmp = new VarDeclaration(loc, tab.nextOf().arrayOf(), id, ie);
1115 if (!valueIsRef)
1116 tmp.storage_class |= STC.scope_;
1118 tmp.storage_class |= STC.temp;
1120 Expression tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id.length);
1122 if (!fs.key)
1124 Identifier idkey = Identifier.generateId("__key");
1125 fs.key = new VarDeclaration(loc, Type.tsize_t, idkey, null);
1126 fs.key.storage_class |= STC.temp;
1128 else if (fs.key.type.ty != Type.tsize_t.ty)
1130 tmp_length = new CastExp(loc, tmp_length, fs.key.type);
1132 if (fs.op == TOK.foreach_reverse_)
1133 fs.key._init = new ExpInitializer(loc, tmp_length);
1134 else
1135 fs.key._init = new ExpInitializer(loc, new IntegerExp(loc, 0, fs.key.type));
1137 auto cs = new Statements();
1138 if (vinit)
1139 cs.push(new ExpStatement(loc, vinit));
1140 cs.push(new ExpStatement(loc, tmp));
1141 cs.push(new ExpStatement(loc, fs.key));
1142 Statement forinit = new CompoundDeclarationStatement(loc, cs);
1144 Expression cond;
1145 if (fs.op == TOK.foreach_reverse_)
1147 // key--
1148 cond = new PostExp(EXP.minusMinus, loc, new VarExp(loc, fs.key));
1150 else
1152 // key < tmp.length
1153 cond = new CmpExp(EXP.lessThan, loc, new VarExp(loc, fs.key), tmp_length);
1156 Expression increment = null;
1157 if (fs.op == TOK.foreach_)
1159 // key += 1
1160 increment = new AddAssignExp(loc, new VarExp(loc, fs.key), new IntegerExp(loc, 1, fs.key.type));
1163 // T value = tmp[key];
1164 IndexExp indexExp = new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, fs.key));
1165 indexExp.indexIsInBounds = true; // disabling bounds checking in foreach statements.
1166 fs.value._init = new ExpInitializer(loc, indexExp);
1167 Statement ds = new ExpStatement(loc, fs.value);
1169 if (dim == 2)
1171 Parameter p = (*fs.parameters)[0];
1172 if ((p.storageClass & STC.ref_) && p.type.equals(fs.key.type))
1174 fs.key.range = null;
1175 auto v = new AliasDeclaration(loc, p.ident, fs.key);
1176 fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1178 else
1180 auto ei = new ExpInitializer(loc, new IdentifierExp(loc, fs.key.ident));
1181 auto v = new VarDeclaration(loc, p.type, p.ident, ei);
1182 v.storage_class |= STC.foreach_ | (p.storageClass & STC.ref_);
1183 fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1184 if (fs.key.range && !p.type.isMutable())
1186 /* Limit the range of the key to the specified range
1188 v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
1192 fs._body = new CompoundStatement(loc, ds, fs._body);
1194 Statement s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
1195 if (auto ls = checkLabeledLoop(sc, fs)) // https://issues.dlang.org/show_bug.cgi?id=15450
1196 // don't use sc2
1197 ls.gotoTarget = s;
1198 return retStmt(s);
1200 case Taarray:
1201 if (fs.op == TOK.foreach_reverse_)
1202 warning(fs.loc, "cannot use `foreach_reverse` with an associative array");
1203 if (checkForArgTypes(fs))
1204 return retError();
1206 if (dim < 1 || dim > 2)
1208 error(fs.loc, "only one or two arguments for associative array `foreach`");
1209 return retError();
1211 return retStmt(apply());
1213 case Tclass:
1214 case Tstruct:
1215 /* Prefer using opApply, if it exists
1217 if (sapply)
1218 return retStmt(apply());
1220 /* Look for range iteration, i.e. the properties
1221 * .empty, .popFront, .popBack, .front and .back
1222 * foreach (e; aggr) { ... }
1223 * translates to:
1224 * for (auto __r = aggr[]; !__r.empty; __r.popFront()) {
1225 * auto e = __r.front;
1226 * ...
1229 auto ad = (tab.ty == Tclass) ?
1230 cast(AggregateDeclaration)tab.isTypeClass().sym :
1231 cast(AggregateDeclaration)tab.isTypeStruct().sym;
1232 Identifier idfront;
1233 Identifier idpopFront;
1234 if (fs.op == TOK.foreach_)
1236 idfront = Id.Ffront;
1237 idpopFront = Id.FpopFront;
1239 else
1241 idfront = Id.Fback;
1242 idpopFront = Id.FpopBack;
1244 auto sfront = ad.search(Loc.initial, idfront);
1245 if (!sfront)
1246 return retStmt(apply());
1248 /* Generate a temporary __r and initialize it with the aggregate.
1250 VarDeclaration r;
1251 Statement _init;
1252 if (vinit && fs.aggr.isVarExp() && fs.aggr.isVarExp().var == vinit)
1254 r = vinit;
1255 _init = new ExpStatement(loc, vinit);
1257 else
1259 r = copyToTemp(0, "__r", fs.aggr);
1260 r.dsymbolSemantic(sc);
1261 _init = new ExpStatement(loc, r);
1262 if (vinit)
1263 _init = new CompoundStatement(loc, new ExpStatement(loc, vinit), _init);
1266 // !__r.empty
1267 Expression e = new VarExp(loc, r);
1268 e = new DotIdExp(loc, e, Id.Fempty);
1269 Expression condition = new NotExp(loc, e);
1271 // __r.idpopFront()
1272 e = new VarExp(loc, r);
1273 Expression increment = new CallExp(loc, new DotIdExp(loc, e, idpopFront));
1275 /* Declaration statement for e:
1276 * auto e = __r.idfront;
1278 e = new VarExp(loc, r);
1279 Expression einit = new DotIdExp(loc, e, idfront);
1280 Statement makeargs, forbody;
1281 bool ignoreRef = false; // If a range returns a non-ref front we ignore ref on foreach
1283 Type tfront;
1284 if (auto fd = sfront.isFuncDeclaration())
1286 if (!fd.functionSemantic())
1287 return rangeError();
1288 tfront = fd.type;
1290 else if (auto td = sfront.isTemplateDeclaration())
1292 if (auto f = resolveFuncCall(loc, sc, td, null, tab, ArgumentList(), FuncResolveFlag.quiet))
1293 tfront = f.type;
1295 else if (auto d = sfront.toAlias().isDeclaration())
1297 tfront = d.type;
1299 if (!tfront || tfront.ty == Terror)
1300 return rangeError();
1301 if (auto ftt = tfront.toBasetype().isTypeFunction())
1303 tfront = tfront.toBasetype().nextOf();
1304 if (!ftt.isref)
1306 // .front() does not return a ref. We ignore ref on foreach arg.
1307 // see https://issues.dlang.org/show_bug.cgi?id=11934
1308 if (tfront.needsDestruction()) ignoreRef = true;
1311 if (tfront.ty == Tvoid)
1313 error(fs.loc, "`%s.front` is `void` and has no value", oaggr.toChars());
1314 return retError();
1317 if (dim == 1)
1319 auto p = (*fs.parameters)[0];
1320 auto ve = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, einit));
1321 ve.storage_class |= STC.foreach_;
1322 ve.storage_class |= p.storageClass & (STC.scope_ | STC.IOR | STC.TYPECTOR);
1324 if (ignoreRef)
1325 ve.storage_class &= ~STC.ref_;
1327 makeargs = new ExpStatement(loc, ve);
1329 else
1331 auto vd = copyToTemp(STC.ref_, "__front", einit);
1332 vd.dsymbolSemantic(sc);
1333 makeargs = new ExpStatement(loc, vd);
1335 // Resolve inout qualifier of front type
1336 tfront = tfront.substWildTo(tab.mod);
1338 Expression ve = new VarExp(loc, vd);
1339 ve.type = tfront;
1341 auto exps = new Expressions();
1342 exps.push(ve);
1343 int pos = 0;
1344 while (exps.length < dim)
1346 pos = expandAliasThisTuples(exps, pos);
1347 if (pos == -1)
1348 break;
1350 if (exps.length != dim)
1352 const(char)* plural = exps.length > 1 ? "s" : "";
1353 error(fs.loc, "cannot infer argument types, expected %llu argument%s, not %llu",
1354 cast(ulong) exps.length, plural, cast(ulong) dim);
1355 return retError();
1358 foreach (i; 0 .. dim)
1360 auto p = (*fs.parameters)[i];
1361 auto exp = (*exps)[i];
1362 version (none)
1364 printf("[%lu] p = %s %s, exp = %s %s\n", i,
1365 p.type ? p.type.toChars() : "?", p.ident.toChars(),
1366 exp.type.toChars(), exp.toChars());
1368 if (!p.type)
1369 p.type = exp.type;
1371 auto sc = p.storageClass;
1372 if (ignoreRef) sc &= ~STC.ref_;
1373 p.type = p.type.addStorageClass(sc).typeSemantic(loc, sc2);
1374 if (!exp.implicitConvTo(p.type))
1376 error(fs.loc, "cannot implicilty convert range element of type `%s` to variable `%s` of type `%s`",
1377 exp.type.toChars(), p.toChars(), p.type.toChars());
1378 return retError();
1381 auto var = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, exp));
1382 var.storage_class |= STC.ctfe | STC.ref_ | STC.foreach_;
1383 makeargs = new CompoundStatement(loc, makeargs, new ExpStatement(loc, var));
1387 forbody = new CompoundStatement(loc, makeargs, fs._body);
1389 Statement s = new ForStatement(loc, _init, condition, increment, forbody, fs.endloc);
1390 if (auto ls = checkLabeledLoop(sc, fs))
1391 ls.gotoTarget = s;
1393 version (none)
1395 printf("init: %s\n", toChars(_init));
1396 printf("condition: %s\n", condition.toChars());
1397 printf("increment: %s\n", increment.toChars());
1398 printf("body: %s\n", forbody.toChars());
1400 return retStmt(s);
1402 case Tdelegate:
1403 if (fs.op == TOK.foreach_reverse_)
1404 deprecation(fs.loc, "cannot use `foreach_reverse` with a delegate");
1405 return retStmt(apply());
1406 case Terror:
1407 return retError();
1408 default:
1409 error(fs.loc, "`foreach`: `%s` is not an aggregate type", fs.aggr.type.toChars());
1410 return retError();
1414 void visitForeachRange(ForeachRangeStatement fs)
1416 /* https://dlang.org/spec/statement.html#foreach-range-statement
1419 //printf("ForeachRangeStatement::semantic() %p\n", fs);
1420 auto loc = fs.loc;
1421 fs.lwr = fs.lwr.expressionSemantic(sc);
1422 fs.lwr = resolveProperties(sc, fs.lwr);
1423 fs.lwr = fs.lwr.optimize(WANTvalue);
1424 if (!fs.lwr.type)
1426 error(fs.loc, "invalid range lower bound `%s`", fs.lwr.toChars());
1427 return setError();
1430 fs.upr = fs.upr.expressionSemantic(sc);
1431 fs.upr = resolveProperties(sc, fs.upr);
1432 fs.upr = fs.upr.optimize(WANTvalue);
1433 if (!fs.upr.type)
1435 error(fs.loc, "invalid range upper bound `%s`", fs.upr.toChars());
1436 return setError();
1439 if (fs.prm.type)
1441 fs.prm.type = fs.prm.type.typeSemantic(loc, sc);
1442 fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
1443 fs.lwr = fs.lwr.implicitCastTo(sc, fs.prm.type);
1445 if (fs.upr.implicitConvTo(fs.prm.type) || (fs.prm.storageClass & STC.ref_))
1447 fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
1449 else
1451 // See if upr-1 fits in prm.type
1452 Expression limit = new MinExp(loc, fs.upr, IntegerExp.literal!1);
1453 limit = limit.expressionSemantic(sc);
1454 limit = limit.optimize(WANTvalue);
1455 if (!limit.implicitConvTo(fs.prm.type))
1457 fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
1461 else
1463 /* Must infer types from lwr and upr
1465 Type tlwr = fs.lwr.type.toBasetype();
1466 if (tlwr.ty == Tstruct || tlwr.ty == Tclass)
1468 /* Just picking the first really isn't good enough.
1470 fs.prm.type = fs.lwr.type;
1472 else if (fs.lwr.type == fs.upr.type)
1474 /* Same logic as CondExp ?lwr:upr
1476 fs.prm.type = fs.lwr.type;
1478 else
1480 scope AddExp ea = new AddExp(loc, fs.lwr, fs.upr);
1481 if (typeCombine(ea, sc))
1482 return setError();
1483 fs.prm.type = ea.type;
1484 fs.lwr = ea.e1;
1485 fs.upr = ea.e2;
1487 fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
1489 if (fs.prm.type.ty == Terror || fs.lwr.op == EXP.error || fs.upr.op == EXP.error)
1491 return setError();
1494 /* Convert to a for loop:
1495 * foreach (key; lwr .. upr) =>
1496 * for (auto key = lwr, auto tmp = upr; key < tmp; ++key)
1498 * foreach_reverse (key; lwr .. upr) =>
1499 * for (auto tmp = lwr, auto key = upr; key-- > tmp;)
1501 auto ie = new ExpInitializer(loc, (fs.op == TOK.foreach_) ? fs.lwr : fs.upr);
1502 fs.key = new VarDeclaration(loc, fs.upr.type.mutableOf(), Identifier.generateId("__key"), ie);
1503 fs.key.storage_class |= STC.temp;
1504 SignExtendedNumber lower = getIntRange(fs.lwr).imin;
1505 SignExtendedNumber upper = getIntRange(fs.upr).imax;
1506 if (lower <= upper)
1508 fs.key.range = new IntRange(lower, upper);
1511 Identifier id = Identifier.generateId("__limit");
1512 ie = new ExpInitializer(loc, (fs.op == TOK.foreach_) ? fs.upr : fs.lwr);
1513 auto tmp = new VarDeclaration(loc, fs.upr.type, id, ie);
1514 tmp.storage_class |= STC.temp;
1516 auto cs = new Statements();
1517 // Keep order of evaluation as lwr, then upr
1518 if (fs.op == TOK.foreach_)
1520 cs.push(new ExpStatement(loc, fs.key));
1521 cs.push(new ExpStatement(loc, tmp));
1523 else
1525 cs.push(new ExpStatement(loc, tmp));
1526 cs.push(new ExpStatement(loc, fs.key));
1528 Statement forinit = new CompoundDeclarationStatement(loc, cs);
1530 Expression cond;
1531 if (fs.op == TOK.foreach_reverse_)
1533 cond = new PostExp(EXP.minusMinus, loc, new VarExp(loc, fs.key));
1534 if (fs.prm.type.isscalar())
1536 // key-- > tmp
1537 cond = new CmpExp(EXP.greaterThan, loc, cond, new VarExp(loc, tmp));
1539 else
1541 // key-- != tmp
1542 cond = new EqualExp(EXP.notEqual, loc, cond, new VarExp(loc, tmp));
1545 else
1547 if (fs.prm.type.isscalar())
1549 // key < tmp
1550 cond = new CmpExp(EXP.lessThan, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
1552 else
1554 // key != tmp
1555 cond = new EqualExp(EXP.notEqual, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
1559 Expression increment = null;
1560 if (fs.op == TOK.foreach_)
1562 // key += 1
1563 //increment = new AddAssignExp(loc, new VarExp(loc, fs.key), IntegerExp.literal!1);
1564 increment = new PreExp(EXP.prePlusPlus, loc, new VarExp(loc, fs.key));
1566 if ((fs.prm.storageClass & STC.ref_) && fs.prm.type.equals(fs.key.type))
1568 fs.key.range = null;
1569 auto v = new AliasDeclaration(loc, fs.prm.ident, fs.key);
1570 fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1572 else
1574 ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs.key), fs.prm.type));
1575 auto v = new VarDeclaration(loc, fs.prm.type, fs.prm.ident, ie);
1576 v.storage_class |= STC.temp | STC.foreach_ | (fs.prm.storageClass & STC.ref_);
1577 fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1578 if (fs.key.range && !fs.prm.type.isMutable())
1580 /* Limit the range of the key to the specified range
1582 v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
1585 if (fs.prm.storageClass & STC.ref_)
1587 if (fs.key.type.constConv(fs.prm.type) == MATCH.nomatch)
1589 error(fs.loc, "argument type mismatch, `%s` to `ref %s`", fs.key.type.toChars(), fs.prm.type.toChars());
1590 return setError();
1594 auto s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
1595 if (LabelStatement ls = checkLabeledLoop(sc, fs))
1596 ls.gotoTarget = s;
1597 result = s.statementSemantic(sc);
1600 void visitIf(IfStatement ifs)
1602 /* https://dlang.org/spec/statement.html#IfStatement
1605 // check in syntax level
1606 ifs.condition = checkAssignmentAsCondition(ifs.condition, sc);
1608 auto sym = new ScopeDsymbol();
1609 sym.parent = sc.scopesym;
1610 sym.endlinnum = ifs.endloc.linnum;
1611 Scope* scd = sc.push(sym);
1612 if (ifs.prm)
1614 /* Declare prm, which we will set to be the
1615 * result of condition.
1617 auto ei = new ExpInitializer(ifs.loc, ifs.condition);
1618 ifs.match = new VarDeclaration(ifs.loc, ifs.prm.type, ifs.prm.ident, ei);
1619 ifs.match.parent = scd.func;
1620 ifs.match.storage_class |= ifs.prm.storageClass;
1621 ifs.match.dsymbolSemantic(scd);
1623 auto de = new DeclarationExp(ifs.loc, ifs.match);
1624 auto ve = new VarExp(ifs.loc, ifs.match);
1625 ifs.condition = new CommaExp(ifs.loc, de, ve);
1626 ifs.condition = ifs.condition.expressionSemantic(scd);
1628 if (ifs.match.edtor)
1630 Statement sdtor = new DtorExpStatement(ifs.loc, ifs.match.edtor, ifs.match);
1631 sdtor = new ScopeGuardStatement(ifs.loc, TOK.onScopeExit, sdtor);
1632 ifs.ifbody = new CompoundStatement(ifs.loc, sdtor, ifs.ifbody);
1633 ifs.match.storage_class |= STC.nodtor;
1635 // the destructor is always called
1636 // whether the 'ifbody' is executed or not
1637 Statement sdtor2 = new DtorExpStatement(ifs.loc, ifs.match.edtor, ifs.match);
1638 if (ifs.elsebody)
1639 ifs.elsebody = new CompoundStatement(ifs.loc, sdtor2, ifs.elsebody);
1640 else
1641 ifs.elsebody = sdtor2;
1644 else
1646 if (ifs.condition.op == EXP.dotIdentifier)
1647 (cast(DotIdExp)ifs.condition).noderef = true;
1649 ifs.condition = ifs.condition.expressionSemantic(scd);
1650 ifs.condition = resolveProperties(scd, ifs.condition);
1651 ifs.condition = ifs.condition.addDtorHook(scd);
1653 if (checkNonAssignmentArrayOp(ifs.condition))
1654 ifs.condition = ErrorExp.get();
1656 // Convert to boolean after declaring prm so this works:
1657 // if (S prm = S()) {}
1658 // where S is a struct that defines opCast!bool.
1659 ifs.condition = ifs.condition.toBoolean(scd);
1661 // If we can short-circuit evaluate the if statement, don't do the
1662 // semantic analysis of the skipped code.
1663 // This feature allows a limited form of conditional compilation.
1664 ifs.condition = ifs.condition.optimize(WANTvalue);
1666 // checkGC after optimizing the condition so that
1667 // compile time constants are reduced.
1668 ifs.condition = checkGC(scd, ifs.condition);
1670 // Save 'root' of two branches (then and else) at the point where it forks
1671 CtorFlow ctorflow_root = scd.ctorflow.clone();
1673 /* Rewrite `if (!__ctfe) A else B` as `if (__ctfe) B else A`
1675 NotExp notExp;
1676 if (ifs.elsebody &&
1677 (notExp = ifs.condition.isNotExp()) !is null &&
1678 notExp.e1.isVarExp() &&
1679 notExp.e1.isVarExp().var.ident == Id.ctfe)
1681 ifs.condition = notExp.e1;
1682 auto sbody = ifs.ifbody;
1683 ifs.ifbody = ifs.elsebody;
1684 ifs.elsebody = sbody;
1687 /* Detect `if (__ctfe)`
1689 if (ifs.isIfCtfeBlock())
1691 Scope* scd2 = scd.push();
1692 scd2.flags |= SCOPE.ctfeBlock;
1693 ifs.ifbody = ifs.ifbody.semanticNoScope(scd2);
1694 scd2.pop();
1696 else
1697 ifs.ifbody = ifs.ifbody.semanticNoScope(scd);
1698 scd.pop();
1700 CtorFlow ctorflow_then = sc.ctorflow; // move flow results
1701 sc.ctorflow = ctorflow_root; // reset flow analysis back to root
1702 if (ifs.elsebody)
1703 ifs.elsebody = ifs.elsebody.semanticScope(sc, null, null, null);
1705 // Merge 'then' results into 'else' results
1706 sc.merge(ifs.loc, ctorflow_then);
1708 ctorflow_then.freeFieldinit(); // free extra copy of the data
1710 if (ifs.condition.op == EXP.error ||
1711 (ifs.ifbody && ifs.ifbody.isErrorStatement()) ||
1712 (ifs.elsebody && ifs.elsebody.isErrorStatement()))
1714 return setError();
1716 result = ifs;
1719 void visitConditional(ConditionalStatement cs)
1721 //printf("ConditionalStatement::semantic()\n");
1723 // If we can short-circuit evaluate the if statement, don't do the
1724 // semantic analysis of the skipped code.
1725 // This feature allows a limited form of conditional compilation.
1726 if (cs.condition.include(sc))
1728 DebugCondition dc = cs.condition.isDebugCondition();
1729 if (dc)
1731 sc = sc.push();
1732 sc.flags |= SCOPE.debug_;
1733 cs.ifbody = cs.ifbody.statementSemantic(sc);
1734 sc.pop();
1736 else
1737 cs.ifbody = cs.ifbody.statementSemantic(sc);
1738 result = cs.ifbody;
1740 else
1742 if (cs.elsebody)
1743 cs.elsebody = cs.elsebody.statementSemantic(sc);
1744 result = cs.elsebody;
1748 void visitPragma(PragmaStatement ps)
1750 /* https://dlang.org/spec/statement.html#pragma-statement
1752 // Should be merged with PragmaDeclaration
1754 //printf("PragmaStatement::semantic() %s\n", ps.toChars());
1755 //printf("body = %p\n", ps._body);
1756 if (ps.ident == Id.msg)
1758 if (!pragmaMsgSemantic(ps.loc, sc, ps.args))
1759 return setError();
1761 else if (ps.ident == Id.lib)
1763 version (all)
1765 /* Should this be allowed?
1767 error(ps.loc, "`pragma(lib)` not allowed as statement");
1768 return setError();
1770 else
1772 if (!ps.args || ps.args.length != 1)
1774 error(ps.loc, "`string` expected for library name");
1775 return setError();
1777 else
1779 auto se = semanticString(sc, (*ps.args)[0], "library name");
1780 if (!se)
1781 return setError();
1783 if (global.params.v.verbose)
1785 message("library %.*s", cast(int)se.len, se.string);
1790 else if (ps.ident == Id.linkerDirective)
1792 /* Should this be allowed?
1794 error(ps.loc, "`pragma(linkerDirective)` not allowed as statement");
1795 return setError();
1797 else if (ps.ident == Id.startaddress)
1799 if (!pragmaStartAddressSemantic(ps.loc, sc, ps.args))
1800 return setError();
1802 else if (ps.ident == Id.Pinline)
1804 if (auto fd = sc.func)
1806 fd.inlining = evalPragmaInline(ps.loc, sc, ps.args);
1808 else
1810 error(ps.loc, "`pragma(inline)` is not inside a function");
1811 return setError();
1814 else if (ps.ident == Id.mangle)
1816 auto es = ps._body ? ps._body.isExpStatement() : null;
1817 auto de = es ? es.exp.isDeclarationExp() : null;
1818 if (!de)
1820 error(ps.loc, "`pragma(mangle)` must be attached to a declaration");
1821 return setError();
1823 const se = ps.args && (*ps.args).length == 1 ? semanticString(sc, (*ps.args)[0], "pragma mangle argument") : null;
1824 if (!se)
1826 error(ps.loc, "`pragma(mangle)` takes a single argument that must be a string literal");
1827 return setError();
1829 const cnt = setMangleOverride(de.declaration, cast(const(char)[])se.peekData());
1830 if (cnt != 1)
1831 assert(0);
1833 else if (!global.params.ignoreUnsupportedPragmas)
1835 error(ps.loc, "unrecognized `pragma(%s)`", ps.ident.toChars());
1836 return setError();
1839 if (ps._body)
1841 if (ps.ident == Id.msg || ps.ident == Id.startaddress)
1843 error(ps.loc, "`pragma(%s)` is missing a terminating `;`", ps.ident.toChars());
1844 return setError();
1846 ps._body = ps._body.statementSemantic(sc);
1848 result = ps._body;
1851 void visitStaticAssert(StaticAssertStatement s)
1853 s.sa.semantic2(sc);
1854 if (s.sa.errors)
1855 return setError();
1858 void visitSwitch(SwitchStatement ss)
1860 /* https://dlang.org/spec/statement.html#switch-statement
1863 //printf("SwitchStatement::semantic(%p)\n", ss);
1864 ss.tryBody = sc.tryBody;
1865 ss.tf = sc.tf;
1866 if (ss.cases)
1868 result = ss; // already run
1869 return;
1872 if (ss.param)
1875 * If the switch statement is of form `switch(auto a = exp) { body }`,
1876 * rewrite to the following inside it's own scope:
1878 * auto a = exp
1879 * switch(a)
1880 * { body }
1882 auto statements = new Statements();
1883 auto vardecl = new VarDeclaration(ss.param.loc,
1884 ss.param.type,
1885 ss.param.ident,
1886 new ExpInitializer(ss.condition.loc, ss.condition),
1887 ss.param.storageClass);
1889 statements.push(new ExpStatement(ss.param.loc, vardecl));
1891 ss.condition = new VarExp(ss.param.loc, vardecl, false);
1892 ss.param = null;
1894 statements.push(ss);
1896 Statement s = new CompoundStatement(ss.loc, statements);
1897 s = new ScopeStatement(ss.loc, s, ss.endloc);
1898 s = s.statementSemantic(sc);
1899 result = s;
1900 return;
1903 bool conditionError = false;
1904 ss.condition = ss.condition.expressionSemantic(sc);
1905 ss.condition = resolveProperties(sc, ss.condition);
1907 Type att = null;
1908 TypeEnum te = null;
1909 while (!ss.condition.isErrorExp())
1911 // preserve enum type for final switches
1912 if (ss.condition.type.ty == Tenum)
1913 te = cast(TypeEnum)ss.condition.type;
1914 if (ss.condition.type.isString())
1916 // If it's not an array, cast it to one
1917 if (ss.condition.type.ty != Tarray)
1919 ss.condition = ss.condition.implicitCastTo(sc, ss.condition.type.nextOf().arrayOf());
1921 ss.condition.type = ss.condition.type.constOf();
1922 break;
1924 ss.condition = integralPromotions(ss.condition, sc);
1925 if (!ss.condition.isErrorExp() && ss.condition.type.isintegral())
1926 break;
1928 auto ad = isAggregate(ss.condition.type);
1929 if (ad && ad.aliasthis && !isRecursiveAliasThis(att, ss.condition.type))
1931 if (auto e = resolveAliasThis(sc, ss.condition, true))
1933 ss.condition = e;
1934 continue;
1938 if (!ss.condition.isErrorExp())
1940 error(ss.loc, "`%s` must be of integral or string type, it is a `%s`",
1941 ss.condition.toChars(), ss.condition.type.toChars());
1942 conditionError = true;
1943 break;
1946 if (checkNonAssignmentArrayOp(ss.condition))
1947 ss.condition = ErrorExp.get();
1948 ss.condition = ss.condition.optimize(WANTvalue);
1949 ss.condition = checkGC(sc, ss.condition);
1950 if (ss.condition.op == EXP.error)
1951 conditionError = true;
1953 bool needswitcherror = false;
1955 ss.lastVar = sc.lastVar;
1957 sc = sc.push();
1958 sc.sbreak = ss;
1959 sc.sw = ss;
1961 ss.cases = new CaseStatements();
1962 const inLoopSave = sc.inLoop;
1963 sc.inLoop = true; // BUG: should use Scope::mergeCallSuper() for each case instead
1964 ss._body = ss._body.statementSemantic(sc);
1965 sc.inLoop = inLoopSave;
1967 if (conditionError || (ss._body && ss._body.isErrorStatement()))
1969 sc.pop();
1970 return setError();
1973 // Resolve any goto case's with exp
1974 Lgotocase:
1975 foreach (gcs; ss.gotoCases)
1977 if (!gcs.exp)
1979 error(gcs.loc, "no `case` statement following `goto case;`");
1980 sc.pop();
1981 return setError();
1984 for (Scope* scx = sc; scx; scx = scx.enclosing)
1986 if (!scx.sw)
1987 continue;
1988 foreach (cs; *scx.sw.cases)
1990 if (cs.exp.equals(gcs.exp))
1992 gcs.cs = cs;
1993 continue Lgotocase;
1997 error(gcs.loc, "`case %s` not found", gcs.exp.toChars());
1998 sc.pop();
1999 return setError();
2002 if (ss.isFinal)
2004 Type t = ss.condition.type;
2005 Dsymbol ds;
2006 EnumDeclaration ed = null;
2007 if (t && ((ds = t.toDsymbol(sc)) !is null))
2008 ed = ds.isEnumDeclaration(); // typedef'ed enum
2009 if (!ed && te && ((ds = te.toDsymbol(sc)) !is null))
2010 ed = ds.isEnumDeclaration();
2011 if (ed && ss.cases.length < ed.members.length)
2013 int missingMembers = 0;
2014 const maxShown = global.params.v.errorSupplementCount();
2015 Lmembers:
2016 foreach (es; *ed.members)
2018 EnumMember em = es.isEnumMember();
2019 if (em)
2021 foreach (cs; *ss.cases)
2023 if (cs.exp.equals(em.value) || (!cs.exp.type.isString() &&
2024 !em.value.type.isString() && cs.exp.toInteger() == em.value.toInteger()))
2025 continue Lmembers;
2027 if (missingMembers == 0)
2028 error(ss.loc, "missing cases for `enum` members in `final switch`:");
2030 if (missingMembers < maxShown)
2031 errorSupplemental(ss.loc, "`%s`", em.toChars());
2032 missingMembers++;
2035 if (missingMembers > 0)
2037 if (missingMembers > maxShown)
2038 errorSupplemental(ss.loc, "... (%d more, -v to show) ...", missingMembers - maxShown);
2039 sc.pop();
2040 return setError();
2043 else
2044 needswitcherror = true;
2047 ss.hasDefault = sc.sw.sdefault ||
2048 !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe);
2049 if (!ss.hasDefault)
2051 if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !(sc.flags & SCOPE.Cfile))
2052 error(ss.loc, "`switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`");
2054 // Generate runtime error if the default is hit
2055 auto a = new Statements();
2056 CompoundStatement cs;
2057 Statement s;
2059 if (sc.flags & SCOPE.Cfile)
2061 s = new BreakStatement(ss.loc, null); // default for C is `default: break;`
2063 else if (!sc.needsCodegen())
2065 // something for the interpreter to deal with
2066 s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0));
2068 else if (global.params.useSwitchError == CHECKENABLE.on &&
2069 global.params.checkAction != CHECKACTION.halt)
2071 if (global.params.checkAction == CHECKACTION.C)
2073 /* Rewrite as an assert(0) and let e2ir generate
2074 * the call to the C assert failure function
2076 s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0));
2078 else
2080 if (!verifyHookExist(ss.loc, *sc, Id.__switch_error, "generating assert messages"))
2081 return setError();
2083 Expression sl = new IdentifierExp(ss.loc, Id.empty);
2084 sl = new DotIdExp(ss.loc, sl, Id.object);
2085 sl = new DotIdExp(ss.loc, sl, Id.__switch_error);
2087 Expressions* args = new Expressions(2);
2088 (*args)[0] = new StringExp(ss.loc, ss.loc.filename.toDString());
2089 (*args)[1] = new IntegerExp(ss.loc.linnum);
2091 sl = new CallExp(ss.loc, sl, args);
2092 sl = sl.expressionSemantic(sc);
2094 s = new SwitchErrorStatement(ss.loc, sl);
2097 else
2098 s = new ExpStatement(ss.loc, new HaltExp(ss.loc));
2100 a.reserve(2);
2101 sc.sw.sdefault = new DefaultStatement(ss.loc, s);
2102 a.push(ss._body);
2103 if (ss._body.blockExit(sc.func, null) & BE.fallthru)
2104 a.push(new BreakStatement(Loc.initial, null));
2105 a.push(sc.sw.sdefault);
2106 cs = new CompoundStatement(ss.loc, a);
2107 ss._body = cs;
2110 if (!(sc.flags & SCOPE.Cfile) && ss.checkLabel())
2112 sc.pop();
2113 return setError();
2117 if (!(ss.condition.type.isString() && sc.needsCodegen()))
2119 sc.pop();
2120 result = ss;
2121 return;
2124 // Transform a switch with string labels into a switch with integer labels.
2126 // The integer value of each case corresponds to the index of each label
2127 // string in the sorted array of label strings.
2129 // The value of the integer condition is obtained by calling the druntime template
2130 // switch(object.__switch(cond, options...)) {0: {...}, 1: {...}, ...}
2132 // We sort a copy of the array of labels because we want to do a binary search in object.__switch,
2133 // without modifying the order of the case blocks here in the compiler.
2135 if (!verifyHookExist(ss.loc, *sc, Id.__switch, "switch cases on strings"))
2136 return setError();
2138 size_t numcases = 0;
2139 if (ss.cases)
2140 numcases = ss.cases.length;
2142 for (size_t i = 0; i < numcases; i++)
2144 CaseStatement cs = (*ss.cases)[i];
2145 cs.index = cast(int)i;
2148 // Make a copy of all the cases so that qsort doesn't scramble the actual
2149 // data we pass to codegen (the order of the cases in the switch).
2150 CaseStatements *csCopy = (*ss.cases).copy();
2152 if (numcases)
2154 static int sort_compare(in CaseStatement* x, in CaseStatement* y) @trusted
2156 auto se1 = x.exp.isStringExp();
2157 auto se2 = y.exp.isStringExp();
2158 return (se1 && se2) ? se1.compare(se2) : 0;
2160 // Sort cases for efficient lookup
2161 csCopy.sort!sort_compare;
2164 // The actual lowering
2165 auto arguments = new Expressions();
2166 arguments.push(ss.condition);
2168 auto compileTimeArgs = new Objects();
2170 // The type & label no.
2171 compileTimeArgs.push(new TypeExp(ss.loc, ss.condition.type.nextOf()));
2173 // The switch labels
2174 foreach (caseString; *csCopy)
2176 compileTimeArgs.push(caseString.exp);
2179 Expression sl = new IdentifierExp(ss.loc, Id.empty);
2180 sl = new DotIdExp(ss.loc, sl, Id.object);
2181 sl = new DotTemplateInstanceExp(ss.loc, sl, Id.__switch, compileTimeArgs);
2183 sl = new CallExp(ss.loc, sl, arguments);
2184 sl = sl.expressionSemantic(sc);
2185 ss.condition = sl;
2187 auto i = 0;
2188 foreach (c; *csCopy)
2190 (*ss.cases)[c.index].exp = new IntegerExp(i++);
2193 //printf("%s\n", ss._body.toChars());
2194 ss.statementSemantic(sc);
2196 sc.pop();
2197 result = ss;
2200 void visitCase(CaseStatement cs)
2202 SwitchStatement sw = sc.sw;
2203 bool errors = false;
2205 //printf("CaseStatement::semantic() %s\n", toChars());
2206 sc = sc.startCTFE();
2207 cs.exp = cs.exp.expressionSemantic(sc);
2208 cs.exp = resolveProperties(sc, cs.exp);
2209 sc = sc.endCTFE();
2211 if (sw)
2213 Expression initialExp = cs.exp;
2215 // The switch'ed value has errors and doesn't provide the actual type
2216 // Omit the cast to enable further semantic (exluding the check for matching types)
2217 if (sw.condition.type && !sw.condition.type.isTypeError())
2218 cs.exp = cs.exp.implicitCastTo(sc, sw.condition.type);
2219 cs.exp = cs.exp.optimize(WANTvalue | WANTexpand);
2221 Expression e = cs.exp;
2222 // Remove all the casts the user and/or implicitCastTo may introduce
2223 // otherwise we'd sometimes fail the check below.
2224 while (e.op == EXP.cast_)
2225 e = (cast(CastExp)e).e1;
2227 /* This is where variables are allowed as case expressions.
2229 if (e.op == EXP.variable)
2231 VarExp ve = cast(VarExp)e;
2232 VarDeclaration v = ve.var.isVarDeclaration();
2233 Type t = cs.exp.type.toBasetype();
2234 if (v && (t.isintegral() || t.ty == Tclass))
2236 /* Flag that we need to do special code generation
2237 * for this, i.e. generate a sequence of if-then-else
2239 sw.hasVars = true;
2241 /* TODO check if v can be uninitialized at that point.
2243 if (!v.isConst() && !v.isImmutable())
2245 error(cs.loc, "`case` variables have to be `const` or `immutable`");
2248 if (sw.isFinal)
2250 error(cs.loc, "`case` variables not allowed in `final switch` statements");
2251 errors = true;
2254 /* Find the outermost scope `scx` that set `sw`.
2255 * Then search scope `scx` for a declaration of `v`.
2257 for (Scope* scx = sc; scx; scx = scx.enclosing)
2259 if (scx.enclosing && scx.enclosing.sw == sw)
2260 continue;
2261 assert(scx.sw == sw);
2263 if (!scx.search(cs.exp.loc, v.ident, null))
2265 error(cs.loc, "`case` variable `%s` declared at %s cannot be declared in `switch` body",
2266 v.toChars(), v.loc.toChars());
2267 errors = true;
2269 break;
2271 goto L1;
2274 else
2275 cs.exp = cs.exp.ctfeInterpret();
2277 if (StringExp se = cs.exp.toStringExp())
2278 cs.exp = se;
2279 else if (!cs.exp.isIntegerExp() && !cs.exp.isErrorExp())
2281 error(cs.loc, "`case` expression must be a compile-time `string` or an integral constant, not `%s`", cs.exp.toChars());
2282 errors = true;
2286 // // Don't check other cases if this has errors
2287 if (!cs.exp.isErrorExp())
2288 foreach (cs2; *sw.cases)
2290 //printf("comparing '%s' with '%s'\n", exp.toChars(), cs.exp.toChars());
2291 if (cs2.exp.equals(cs.exp))
2293 // https://issues.dlang.org/show_bug.cgi?id=15909
2294 error(cs.loc, "duplicate `case %s` in `switch` statement", initialExp.toChars());
2295 errors = true;
2296 break;
2300 sw.cases.push(cs);
2302 // Resolve any goto case's with no exp to this case statement
2303 for (size_t i = 0; i < sw.gotoCases.length;)
2305 GotoCaseStatement gcs = sw.gotoCases[i];
2306 if (!gcs.exp)
2308 gcs.cs = cs;
2309 sw.gotoCases.remove(i); // remove from array
2310 continue;
2312 i++;
2315 if (sc.sw.tf != sc.tf)
2317 error(cs.loc, "`switch` and `case` are in different `finally` blocks");
2318 errors = true;
2320 if (sc.sw.tryBody != sc.tryBody)
2322 error(cs.loc, "case cannot be in different `try` block level from `switch`");
2323 errors = true;
2326 else
2328 error(cs.loc, "`case` not in `switch` statement");
2329 errors = true;
2332 sc.ctorflow.orCSX(CSX.label);
2333 cs.statement = cs.statement.statementSemantic(sc);
2334 if (cs.statement.isErrorStatement())
2336 result = cs.statement;
2337 return;
2339 if (errors || cs.exp.op == EXP.error)
2340 return setError();
2342 cs.lastVar = sc.lastVar;
2343 result = cs;
2346 void visitCaseRange(CaseRangeStatement crs)
2348 SwitchStatement sw = sc.sw;
2349 if (sw is null)
2351 error(crs.loc, "case range not in `switch` statement");
2352 return setError();
2355 //printf("CaseRangeStatement::semantic() %s\n", toChars());
2356 bool errors = false;
2357 if (sw.isFinal)
2359 error(crs.loc, "case ranges not allowed in `final switch`");
2360 errors = true;
2363 sc = sc.startCTFE();
2364 crs.first = crs.first.expressionSemantic(sc);
2365 crs.first = resolveProperties(sc, crs.first);
2366 sc = sc.endCTFE();
2367 crs.first = crs.first.implicitCastTo(sc, sw.condition.type);
2368 crs.first = crs.first.ctfeInterpret();
2370 sc = sc.startCTFE();
2371 crs.last = crs.last.expressionSemantic(sc);
2372 crs.last = resolveProperties(sc, crs.last);
2373 sc = sc.endCTFE();
2374 crs.last = crs.last.implicitCastTo(sc, sw.condition.type);
2375 crs.last = crs.last.ctfeInterpret();
2377 if (crs.first.op == EXP.error || crs.last.op == EXP.error || errors)
2379 if (crs.statement)
2380 crs.statement.statementSemantic(sc);
2381 return setError();
2384 uinteger_t fval = crs.first.toInteger();
2385 uinteger_t lval = crs.last.toInteger();
2386 if ((crs.first.type.isunsigned() && fval > lval) || (!crs.first.type.isunsigned() && cast(sinteger_t)fval > cast(sinteger_t)lval))
2388 error(crs.loc, "first `case %s` is greater than last `case %s`", crs.first.toChars(), crs.last.toChars());
2389 errors = true;
2390 lval = fval;
2393 if (lval - fval > 256)
2395 error(crs.loc, "had %llu cases which is more than 257 cases in case range", 1 + lval - fval);
2396 errors = true;
2397 lval = fval + 256;
2400 if (errors)
2401 return setError();
2403 /* This works by replacing the CaseRange with an array of Case's.
2405 * case a: .. case b: s;
2406 * =>
2407 * case a:
2408 * [...]
2409 * case b:
2410 * s;
2413 auto statements = new Statements();
2414 for (uinteger_t i = fval; i != lval + 1; i++)
2416 Statement s = crs.statement;
2417 if (i != lval) // if not last case
2418 s = new ExpStatement(crs.loc, cast(Expression)null);
2419 Expression e = new IntegerExp(crs.loc, i, crs.first.type);
2420 Statement cs = new CaseStatement(crs.loc, e, s);
2421 statements.push(cs);
2423 Statement s = new CompoundStatement(crs.loc, statements);
2424 sc.ctorflow.orCSX(CSX.label);
2425 s = s.statementSemantic(sc);
2426 result = s;
2429 void visitDefault(DefaultStatement ds)
2431 //printf("DefaultStatement::semantic()\n");
2432 bool errors = false;
2433 if (sc.sw)
2435 if (sc.sw.sdefault)
2437 error(ds.loc, "`switch` statement already has a default");
2438 errors = true;
2440 sc.sw.sdefault = ds;
2442 if (sc.sw.tf != sc.tf)
2444 error(ds.loc, "`switch` and `default` are in different `finally` blocks");
2445 errors = true;
2447 if (sc.sw.tryBody != sc.tryBody)
2449 error(ds.loc, "default cannot be in different `try` block level from `switch`");
2450 errors = true;
2452 if (sc.sw.isFinal)
2454 error(ds.loc, "`default` statement not allowed in `final switch` statement");
2455 errors = true;
2458 else
2460 error(ds.loc, "`default` not in `switch` statement");
2461 errors = true;
2464 sc.ctorflow.orCSX(CSX.label);
2465 ds.statement = ds.statement.statementSemantic(sc);
2466 if (errors || ds.statement.isErrorStatement())
2467 return setError();
2469 ds.lastVar = sc.lastVar;
2470 result = ds;
2473 void visitGotoDefault(GotoDefaultStatement gds)
2475 /* https://dlang.org/spec/statement.html#goto-statement
2478 gds.sw = sc.sw;
2479 if (!gds.sw)
2481 error(gds.loc, "`goto default` not in `switch` statement");
2482 return setError();
2484 if (gds.sw.isFinal)
2486 error(gds.loc, "`goto default` not allowed in `final switch` statement");
2487 return setError();
2489 result = gds;
2492 void visitGotoCase(GotoCaseStatement gcs)
2494 /* https://dlang.org/spec/statement.html#goto-statement
2497 if (!sc.sw)
2499 error(gcs.loc, "`goto case` not in `switch` statement");
2500 return setError();
2503 if (gcs.exp)
2505 gcs.exp = gcs.exp.expressionSemantic(sc);
2506 gcs.exp = gcs.exp.implicitCastTo(sc, sc.sw.condition.type);
2507 gcs.exp = gcs.exp.optimize(WANTvalue);
2508 if (gcs.exp.op == EXP.error)
2509 return setError();
2512 sc.sw.gotoCases.push(gcs);
2513 result = gcs;
2516 void visitReturn(ReturnStatement rs)
2518 /* https://dlang.org/spec/statement.html#return-statement
2521 //printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars());
2523 FuncDeclaration fd = sc.parent.isFuncDeclaration();
2524 if (fd.fes)
2525 fd = fd.fes.func; // fd is now function enclosing foreach
2527 TypeFunction tf = cast(TypeFunction)fd.type;
2528 assert(tf.ty == Tfunction);
2530 if (rs.exp && rs.exp.op == EXP.variable && (cast(VarExp)rs.exp).var == fd.vresult)
2532 // return vresult;
2533 if (sc.fes)
2535 assert(rs.caseDim == 0);
2536 sc.fes.cases.push(rs);
2537 result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.length + 1));
2538 return;
2540 if (fd.returnLabel)
2542 auto gs = new GotoStatement(rs.loc, Id.returnLabel);
2543 gs.label = fd.returnLabel;
2544 result = gs;
2545 return;
2548 if (!fd.returns)
2549 fd.returns = new ReturnStatements();
2550 fd.returns.push(rs);
2551 result = rs;
2552 return;
2555 Type tret = tf.next;
2556 Type tbret = tret ? tret.toBasetype() : null;
2558 bool inferRef = (tf.isref && (fd.storage_class & STC.auto_));
2559 Expression e0 = null;
2561 bool errors = false;
2562 if (sc.flags & SCOPE.contract)
2564 error(rs.loc, "`return` statements cannot be in contracts");
2565 errors = true;
2567 if (sc.os)
2569 // @@@DEPRECATED_2.112@@@
2570 // Deprecated in 2.100, transform into an error in 2.112
2571 if (sc.os.tok == TOK.onScopeFailure)
2573 deprecation(rs.loc, "`return` statements cannot be in `scope(failure)` bodies.");
2574 deprecationSupplemental(rs.loc, "Use try-catch blocks for this purpose");
2576 else
2578 error(rs.loc, "`return` statements cannot be in `%s` bodies", Token.toChars(sc.os.tok));
2579 errors = true;
2582 if (sc.tf)
2584 error(rs.loc, "`return` statements cannot be in `finally` bodies");
2585 errors = true;
2588 if (fd.isCtorDeclaration())
2590 if (rs.exp)
2592 error(rs.loc, "cannot return expression from constructor");
2593 errors = true;
2596 // Constructors implicitly do:
2597 // return this;
2598 rs.exp = new ThisExp(Loc.initial);
2599 rs.exp.type = tret;
2601 else if (rs.exp)
2603 fd.hasReturnExp |= (fd.hasReturnExp & 1 ? 16 : 1);
2605 FuncLiteralDeclaration fld = fd.isFuncLiteralDeclaration();
2606 if (tret)
2607 rs.exp = inferType(rs.exp, tret);
2608 else if (fld && fld.treq)
2609 rs.exp = inferType(rs.exp, fld.treq.nextOf().nextOf());
2611 rs.exp = rs.exp.expressionSemantic(sc);
2612 rs.exp = rs.exp.arrayFuncConv(sc);
2613 // If we're returning by ref, allow the expression to be `shared`
2614 const returnSharedRef = (tf.isref && (fd.inferRetType || tret.isShared()));
2615 rs.exp.checkSharedAccess(sc, returnSharedRef);
2617 // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
2618 if (rs.exp.op == EXP.type)
2619 rs.exp = resolveAliasThis(sc, rs.exp);
2621 rs.exp = resolveProperties(sc, rs.exp);
2622 if (rs.exp.checkType())
2623 rs.exp = ErrorExp.get();
2624 if (auto f = isFuncAddress(rs.exp))
2626 if (fd.inferRetType && f.checkForwardRef(rs.exp.loc))
2627 rs.exp = ErrorExp.get();
2629 if (checkNonAssignmentArrayOp(rs.exp))
2630 rs.exp = ErrorExp.get();
2632 // Extract side-effect part
2633 rs.exp = Expression.extractLast(rs.exp, e0);
2634 if (rs.exp.op == EXP.call)
2635 rs.exp = valueNoDtor(rs.exp);
2637 /* Void-return function can have void / noreturn typed expression
2638 * on return statement.
2640 auto texp = rs.exp.type;
2641 const convToVoid = texp.ty == Tvoid || texp.ty == Tnoreturn;
2643 if (tbret && tbret.ty == Tvoid || convToVoid)
2645 if (!convToVoid)
2647 error(rs.loc, "cannot return non-void from `void` function");
2648 errors = true;
2649 rs.exp = new CastExp(rs.loc, rs.exp, Type.tvoid);
2650 rs.exp = rs.exp.expressionSemantic(sc);
2653 // https://issues.dlang.org/show_bug.cgi?id=23063
2654 rs.exp = checkNoreturnVarAccess(rs.exp);
2656 // @@@DEPRECATED_2.111@@@
2657 const olderrors = global.startGagging();
2658 // uncomment to turn deprecation into an error when
2659 // deprecation cycle is over
2660 if (discardValue(rs.exp))
2662 //errors = true;
2664 if (global.endGagging(olderrors))
2665 deprecation(rs.exp.loc, "`%s` has no effect", rs.exp.toChars());
2667 /* Replace:
2668 * return exp;
2669 * with:
2670 * exp; return;
2672 e0 = Expression.combine(e0, rs.exp);
2673 rs.exp = null;
2675 if (e0)
2677 e0 = e0.optimize(WANTvalue);
2678 e0 = checkGC(sc, e0);
2682 if (rs.exp)
2684 if (fd.inferRetType) // infer return type
2686 if (!tret)
2688 tf.next = rs.exp.type;
2690 else if (tret.ty != Terror && !rs.exp.type.equals(tret))
2692 int m1 = rs.exp.type.implicitConvTo(tret);
2693 int m2 = tret.implicitConvTo(rs.exp.type);
2694 //printf("exp.type = %s m2<-->m1 tret %s\n", exp.type.toChars(), tret.toChars());
2695 //printf("m1 = %d, m2 = %d\n", m1, m2);
2697 if (m1 && m2)
2700 else if (!m1 && m2)
2701 tf.next = rs.exp.type;
2702 else if (m1 && !m2)
2705 else if (!rs.exp.isErrorExp())
2707 error(rs.loc, "expected return type of `%s`, not `%s`:",
2708 tret.toChars(),
2709 rs.exp.type.toChars());
2710 errorSupplemental((fd.returns) ? (*fd.returns)[0].loc : fd.loc,
2711 "Return type of `%s` inferred here.",
2712 tret.toChars());
2714 errors = true;
2715 tf.next = Type.terror;
2719 tret = tf.next;
2720 tbret = tret.toBasetype();
2723 if (inferRef) // deduce 'auto ref'
2725 /* Determine "refness" of function return:
2726 * if it's an lvalue, return by ref, else return by value
2727 * https://dlang.org/spec/function.html#auto-ref-functions
2730 void turnOffRef(scope void delegate() supplemental)
2732 tf.isref = false; // return by value
2733 tf.isreturn = false; // ignore 'return' attribute, whether explicit or inferred
2734 fd.storage_class &= ~STC.return_;
2736 // If we previously assumed the function could be ref when
2737 // checking for `shared`, make sure we were right
2738 if (global.params.noSharedAccess == FeatureState.enabled && rs.exp.type.isShared())
2740 .error(fd.loc, "%s `%s` function returns `shared` but cannot be inferred `ref`", fd.kind, fd.toPrettyChars);
2741 supplemental();
2745 if (rs.exp.isLvalue())
2747 /* May return by ref
2749 Scope* sc2 = sc.push();
2750 sc2.eSink = global.errorSinkNull;
2751 bool err = checkReturnEscapeRef(sc2, rs.exp, true);
2752 sc2.pop();
2754 if (err)
2755 turnOffRef(() { checkReturnEscapeRef(sc, rs.exp, false); });
2756 else if (!rs.exp.type.constConv(tf.next))
2757 turnOffRef(
2758 () => rs.loc.errorSupplemental("cannot implicitly convert `%s` of type `%s` to `%s`",
2759 rs.exp.toChars(), rs.exp.type.toChars(), tf.next.toChars())
2762 else
2763 turnOffRef(
2764 () => rs.loc.errorSupplemental("return value `%s` is not an lvalue", rs.exp.toChars())
2767 /* The "refness" is determined by all of return statements.
2768 * This means:
2769 * return 3; return x; // ok, x can be a value
2770 * return x; return 3; // ok, x can be a value
2774 else
2776 // Type of the returned expression (if any), might've been moved to e0
2777 auto resType = e0 ? e0.type : Type.tvoid;
2779 // infer return type
2780 if (fd.inferRetType)
2782 // 1. First `return <noreturn exp>?`
2783 // 2. Potentially found a returning branch, update accordingly
2784 if (!tf.next || tf.next.toBasetype().isTypeNoreturn())
2786 tf.next = resType; // infer void or noreturn
2788 // Found an actual return value before
2789 else if (tf.next.ty != Tvoid && !resType.toBasetype().isTypeNoreturn())
2791 if (tf.next.ty != Terror)
2793 error(rs.loc, "mismatched function return type inference of `void` and `%s`", tf.next.toChars());
2795 errors = true;
2796 tf.next = Type.terror;
2799 tret = tf.next;
2800 tbret = tret.toBasetype();
2803 // https://issues.dlang.org/show_bug.cgi?id=23914
2804 if (inferRef && !resType.isTypeNoreturn()) // deduce 'auto ref'
2805 tf.isref = false;
2807 if (tbret.ty != Tvoid && !resType.isTypeNoreturn()) // if non-void return
2809 if (tbret.ty != Terror)
2811 if (e0)
2812 error(rs.loc, "expected return type of `%s`, not `%s`", tret.toChars(), resType.toChars());
2813 else if (tbret.isTypeNoreturn())
2815 error(rs.loc, "cannot return from `noreturn` function");
2816 .errorSupplemental(rs.loc,
2817 "Consider adding an endless loop, `assert(0)`, or another `noreturn` expression");
2819 else
2820 error(rs.loc, "`return` expression expected");
2822 errors = true;
2824 else if (fd.isMain())
2826 // main() returns 0, even if it returns void
2827 rs.exp = IntegerExp.literal!0;
2831 // If any branches have called a ctor, but this branch hasn't, it's an error
2832 if (sc.ctorflow.callSuper & CSX.any_ctor && !(sc.ctorflow.callSuper & (CSX.this_ctor | CSX.super_ctor)))
2834 error(rs.loc, "`return` without calling constructor");
2835 errors = true;
2838 if (sc.ctorflow.fieldinit.length) // if aggregate fields are being constructed
2840 auto ad = fd.isMemberLocal();
2841 assert(ad);
2842 foreach (i, v; ad.fields)
2844 bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested());
2845 if (mustInit && !(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor))
2847 error(rs.loc, "an earlier `return` statement skips field `%s` initialization", v.toChars());
2848 errors = true;
2852 sc.ctorflow.orCSX(CSX.return_);
2854 if (errors)
2855 return setError();
2857 if (sc.fes)
2859 if (!rs.exp)
2861 // Send out "case receiver" statement to the foreach.
2862 // return exp;
2863 Statement s = new ReturnStatement(Loc.initial, rs.exp);
2864 sc.fes.cases.push(s);
2866 // Immediately rewrite "this" return statement as:
2867 // return cases.length+1;
2868 rs.exp = new IntegerExp(sc.fes.cases.length + 1);
2869 if (e0)
2871 result = new CompoundStatement(rs.loc, new ExpStatement(rs.loc, e0), rs);
2872 return;
2874 result = rs;
2875 return;
2877 else
2879 fd.buildResultVar(null, rs.exp.type);
2880 bool r = fd.vresult.checkNestedReference(sc, Loc.initial);
2881 assert(!r); // vresult should be always accessible
2883 // Send out "case receiver" statement to the foreach.
2884 // return vresult;
2885 Statement s = new ReturnStatement(Loc.initial, new VarExp(Loc.initial, fd.vresult));
2886 sc.fes.cases.push(s);
2888 // Save receiver index for the later rewriting from:
2889 // return exp;
2890 // to:
2891 // vresult = exp; retrun caseDim;
2892 rs.caseDim = sc.fes.cases.length + 1;
2895 if (rs.exp)
2897 if (!fd.returns)
2898 fd.returns = new ReturnStatements();
2899 fd.returns.push(rs);
2901 if (e0)
2903 if (e0.op == EXP.declaration || e0.op == EXP.comma)
2905 rs.exp = Expression.combine(e0, rs.exp);
2907 else
2909 auto es = new ExpStatement(rs.loc, e0);
2910 if (e0.type.isTypeNoreturn())
2911 result = es; // Omit unreachable return;
2912 else
2913 result = new CompoundStatement(rs.loc, es, rs);
2915 return;
2918 result = rs;
2921 void visitBreak(BreakStatement bs)
2923 /* https://dlang.org/spec/statement.html#break-statement
2926 //printf("BreakStatement::semantic()\n");
2928 // If:
2929 // break Identifier;
2930 if (bs.ident)
2932 bs.ident = fixupLabelName(sc, bs.ident);
2934 FuncDeclaration thisfunc = sc.func;
2936 for (Scope* scx = sc; scx; scx = scx.enclosing)
2938 if (scx.func != thisfunc) // if in enclosing function
2940 if (sc.fes) // if this is the body of a foreach
2942 /* Post this statement to the fes, and replace
2943 * it with a return value that caller will put into
2944 * a switch. Caller will figure out where the break
2945 * label actually is.
2946 * Case numbers start with 2, not 0, as 0 is continue
2947 * and 1 is break.
2949 sc.fes.cases.push(bs);
2950 result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.length + 1));
2951 return;
2953 break; // can't break to it
2956 LabelStatement ls = scx.slabel;
2957 if (ls && ls.ident == bs.ident)
2959 Statement s = ls.statement;
2960 if (!s || !s.hasBreak())
2961 error(bs.loc, "label `%s` has no `break`", bs.ident.toChars());
2962 else if (ls.tf != sc.tf)
2963 error(bs.loc, "cannot break out of `finally` block");
2964 else
2966 ls.breaks = true;
2967 result = bs;
2968 return;
2970 return setError();
2973 error(bs.loc, "enclosing label `%s` for `break` not found", bs.ident.toChars());
2974 return setError();
2976 else if (!sc.sbreak)
2978 if (sc.os && sc.os.tok != TOK.onScopeFailure)
2980 error(bs.loc, "`break` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok));
2982 else if (sc.fes)
2984 // Replace break; with return 1;
2985 result = new ReturnStatement(Loc.initial, IntegerExp.literal!1);
2986 return;
2988 else
2989 error(bs.loc, "`break` is not inside a loop or `switch`");
2990 return setError();
2992 else if (sc.sbreak.isForwardingStatement())
2994 error(bs.loc, "must use labeled `break` within `static foreach`");
2996 result = bs;
2999 void visitContinue(ContinueStatement cs)
3001 /* https://dlang.org/spec/statement.html#continue-statement
3004 //printf("ContinueStatement::semantic() %p\n", cs);
3005 if (cs.ident)
3007 cs.ident = fixupLabelName(sc, cs.ident);
3009 Scope* scx;
3010 FuncDeclaration thisfunc = sc.func;
3012 for (scx = sc; scx; scx = scx.enclosing)
3014 LabelStatement ls;
3015 if (scx.func != thisfunc) // if in enclosing function
3017 if (sc.fes) // if this is the body of a foreach
3019 for (; scx; scx = scx.enclosing)
3021 ls = scx.slabel;
3022 if (ls && ls.ident == cs.ident && ls.statement == sc.fes)
3024 // Replace continue ident; with return 0;
3025 result = new ReturnStatement(Loc.initial, IntegerExp.literal!0);
3026 return;
3030 /* Post this statement to the fes, and replace
3031 * it with a return value that caller will put into
3032 * a switch. Caller will figure out where the break
3033 * label actually is.
3034 * Case numbers start with 2, not 0, as 0 is continue
3035 * and 1 is break.
3037 sc.fes.cases.push(cs);
3038 result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.length + 1));
3039 return;
3041 break; // can't continue to it
3044 ls = scx.slabel;
3045 if (ls && ls.ident == cs.ident)
3047 Statement s = ls.statement;
3048 if (!s || !s.hasContinue())
3049 error(cs.loc, "label `%s` has no `continue`", cs.ident.toChars());
3050 else if (ls.tf != sc.tf)
3051 error(cs.loc, "cannot continue out of `finally` block");
3052 else
3054 result = cs;
3055 return;
3057 return setError();
3060 error(cs.loc, "enclosing label `%s` for `continue` not found", cs.ident.toChars());
3061 return setError();
3063 else if (!sc.scontinue)
3065 if (sc.os && sc.os.tok != TOK.onScopeFailure)
3067 error(cs.loc, "`continue` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok));
3069 else if (sc.fes)
3071 // Replace continue; with return 0;
3072 result = new ReturnStatement(Loc.initial, IntegerExp.literal!0);
3073 return;
3075 else
3076 error(cs.loc, "`continue` is not inside a loop");
3077 return setError();
3079 else if (sc.scontinue.isForwardingStatement())
3081 error(cs.loc, "must use labeled `continue` within `static foreach`");
3083 result = cs;
3086 void visitSynchronized(SynchronizedStatement ss)
3088 /* https://dlang.org/spec/statement.html#synchronized-statement
3091 if (ss.exp)
3093 ss.exp = ss.exp.expressionSemantic(sc);
3094 ss.exp = resolveProperties(sc, ss.exp);
3095 ss.exp = ss.exp.optimize(WANTvalue);
3096 ss.exp = checkGC(sc, ss.exp);
3097 if (ss.exp.op == EXP.error)
3099 if (ss._body)
3100 ss._body = ss._body.statementSemantic(sc);
3101 return setError();
3104 ClassDeclaration cd = ss.exp.type.isClassHandle();
3105 if (!cd)
3107 error(ss.loc, "can only `synchronize` on class objects, not `%s`", ss.exp.type.toChars());
3108 return setError();
3110 else if (cd.isInterfaceDeclaration())
3112 /* Cast the interface to an object, as the object has the monitor,
3113 * not the interface.
3115 if (!ClassDeclaration.object)
3117 error(ss.loc, "missing or corrupt object.d");
3118 fatal();
3121 Type t = ClassDeclaration.object.type;
3122 t = t.typeSemantic(Loc.initial, sc).toBasetype();
3123 assert(t.ty == Tclass);
3125 ss.exp = new CastExp(ss.loc, ss.exp, t);
3126 ss.exp = ss.exp.expressionSemantic(sc);
3128 version (all)
3130 /* Rewrite as:
3131 * auto tmp = exp;
3132 * _d_monitorenter(tmp);
3133 * try { body } finally { _d_monitorexit(tmp); }
3135 auto tmp = copyToTemp(0, "__sync", ss.exp);
3136 tmp.dsymbolSemantic(sc);
3138 auto cs = new Statements();
3139 cs.push(new ExpStatement(ss.loc, tmp));
3141 auto args = new Parameters();
3142 args.push(new Parameter(Loc.initial, 0, ClassDeclaration.object.type, null, null, null));
3144 FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter);
3145 Expression e = new CallExp(ss.loc, fdenter, new VarExp(ss.loc, tmp));
3146 e.type = Type.tvoid; // do not run semantic on e
3148 cs.push(new ExpStatement(ss.loc, e));
3149 FuncDeclaration fdexit = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorexit);
3150 e = new CallExp(ss.loc, fdexit, new VarExp(ss.loc, tmp));
3151 e.type = Type.tvoid; // do not run semantic on e
3152 Statement s = new ExpStatement(ss.loc, e);
3153 s = new TryFinallyStatement(ss.loc, ss._body, s);
3154 cs.push(s);
3156 s = new CompoundStatement(ss.loc, cs);
3157 result = s.statementSemantic(sc);
3160 else
3162 /* Generate our own critical section, then rewrite as:
3163 * static shared void* __critsec;
3164 * _d_criticalenter2(&__critsec);
3165 * try { body } finally { _d_criticalexit(__critsec); }
3167 auto id = Identifier.generateId("__critsec");
3168 auto t = Type.tvoidptr;
3169 auto tmp = new VarDeclaration(ss.loc, t, id, null);
3170 tmp.storage_class |= STC.temp | STC.shared_ | STC.static_;
3171 Expression tmpExp = new VarExp(ss.loc, tmp);
3173 auto cs = new Statements();
3174 cs.push(new ExpStatement(ss.loc, tmp));
3176 /* This is just a dummy variable for "goto skips declaration" error.
3177 * Backend optimizer could remove this unused variable.
3179 auto v = new VarDeclaration(ss.loc, Type.tvoidptr, Identifier.generateId("__sync"), null);
3180 v.dsymbolSemantic(sc);
3181 cs.push(new ExpStatement(ss.loc, v));
3183 auto enterArgs = new Parameters();
3184 enterArgs.push(new Parameter(Loc.initial, 0, t.pointerTo(), null, null, null));
3186 FuncDeclaration fdenter = FuncDeclaration.genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_);
3187 Expression e = new AddrExp(ss.loc, tmpExp);
3188 e = e.expressionSemantic(sc);
3189 e = new CallExp(ss.loc, fdenter, e);
3190 e.type = Type.tvoid; // do not run semantic on e
3191 cs.push(new ExpStatement(ss.loc, e));
3193 auto exitArgs = new Parameters();
3194 exitArgs.push(new Parameter(Loc.initial, 0, t, null, null, null));
3196 FuncDeclaration fdexit = FuncDeclaration.genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_);
3197 e = new CallExp(ss.loc, fdexit, tmpExp);
3198 e.type = Type.tvoid; // do not run semantic on e
3199 Statement s = new ExpStatement(ss.loc, e);
3200 s = new TryFinallyStatement(ss.loc, ss._body, s);
3201 cs.push(s);
3203 s = new CompoundStatement(ss.loc, cs);
3204 result = s.statementSemantic(sc);
3208 void visitWith(WithStatement ws)
3210 /* https://dlang.org/spec/statement.html#with-statement
3213 ScopeDsymbol sym;
3214 Initializer _init;
3216 //printf("WithStatement::semantic()\n");
3217 ws.exp = ws.exp.expressionSemantic(sc);
3218 ws.exp = resolveProperties(sc, ws.exp);
3219 ws.exp = ws.exp.optimize(WANTvalue);
3220 ws.exp = checkGC(sc, ws.exp);
3221 if (ws.exp.op == EXP.error)
3222 return setError();
3223 if (ws.exp.op == EXP.scope_)
3225 sym = new WithScopeSymbol(ws);
3226 sym.parent = sc.scopesym;
3227 sym.endlinnum = ws.endloc.linnum;
3229 else if (ws.exp.op == EXP.type)
3231 Dsymbol s = (cast(TypeExp)ws.exp).type.toDsymbol(sc);
3232 if (!s || !s.isScopeDsymbol())
3234 error(ws.loc, "`with` type `%s` has no members", ws.exp.toChars());
3235 return setError();
3237 sym = new WithScopeSymbol(ws);
3238 sym.parent = sc.scopesym;
3239 sym.endlinnum = ws.endloc.linnum;
3241 else
3243 Type texp = ws.exp.type;
3244 Type t = texp.toBasetype();
3246 Expression olde = ws.exp;
3247 if (t.ty == Tpointer)
3249 ws.exp = new PtrExp(ws.loc, ws.exp);
3250 ws.exp = ws.exp.expressionSemantic(sc);
3251 texp = ws.exp.type;
3252 t = texp.toBasetype();
3255 assert(t);
3256 t = t.toBasetype();
3257 if (t.isClassHandle())
3259 _init = new ExpInitializer(ws.loc, ws.exp);
3260 ws.wthis = new VarDeclaration(ws.loc, ws.exp.type, Id.withSym, _init);
3261 ws.wthis.storage_class |= STC.temp;
3262 ws.wthis.dsymbolSemantic(sc);
3264 sym = new WithScopeSymbol(ws);
3265 sym.parent = sc.scopesym;
3266 sym.endlinnum = ws.endloc.linnum;
3268 else if (t.ty == Tstruct)
3270 if (!ws.exp.isLvalue())
3272 /* Re-write to
3274 * auto __withtmp = exp
3275 * with(__withtmp)
3277 * ...
3281 auto tmp = copyToTemp(0, "__withtmp", ws.exp);
3282 tmp.dsymbolSemantic(sc);
3283 auto es = new ExpStatement(ws.loc, tmp);
3284 ws.exp = new VarExp(ws.loc, tmp);
3285 Statement ss = new ScopeStatement(ws.loc, new CompoundStatement(ws.loc, es, ws), ws.endloc);
3286 result = ss.statementSemantic(sc);
3287 return;
3289 Expression e = ws.exp.addressOf();
3290 _init = new ExpInitializer(ws.loc, e);
3291 ws.wthis = new VarDeclaration(ws.loc, e.type, Id.withSym, _init);
3292 ws.wthis.storage_class |= STC.temp;
3293 ws.wthis.dsymbolSemantic(sc);
3294 sym = new WithScopeSymbol(ws);
3295 // Need to set the scope to make use of resolveAliasThis
3296 sym.setScope(sc);
3297 sym.parent = sc.scopesym;
3298 sym.endlinnum = ws.endloc.linnum;
3300 else if (auto tenum = texp.isTypeEnum())
3302 ws.exp = new TypeExp(ws.exp.loc, tenum);
3303 sym = new WithScopeSymbol(ws);
3304 sym.parent = sc.scopesym;
3305 sym.endlinnum = ws.endloc.linnum;
3307 else
3309 error(ws.loc, "`with` expression types must be enums or aggregates or pointers to them, not `%s`", olde.type.toChars());
3310 return setError();
3314 if (ws._body)
3316 sym._scope = sc;
3317 sc = sc.push(sym);
3318 sc.insert(sym);
3319 ws._body = ws._body.statementSemantic(sc);
3320 sc.pop();
3321 if (ws._body && ws._body.isErrorStatement())
3323 result = ws._body;
3324 return;
3328 result = ws;
3331 // https://dlang.org/spec/statement.html#TryStatement
3332 void visitTryCatch(TryCatchStatement tcs)
3334 //printf("TryCatchStatement.semantic()\n");
3336 if (!global.params.useExceptions)
3338 error(tcs.loc, "cannot use try-catch statements with %s", global.params.betterC ? "-betterC".ptr : "-nothrow".ptr);
3339 return setError();
3342 if (!ClassDeclaration.throwable)
3344 error(tcs.loc, "cannot use try-catch statements because `object.Throwable` was not declared");
3345 return setError();
3348 uint flags;
3349 enum FLAGcpp = 1;
3350 enum FLAGd = 2;
3352 tcs.tryBody = sc.tryBody; // chain on the in-flight tryBody
3353 tcs._body = tcs._body.semanticScope(sc, null, null, tcs);
3355 /* Even if body is empty, still do semantic analysis on catches
3357 bool catchErrors = false;
3358 foreach (i, c; *tcs.catches)
3360 c.catchSemantic(sc);
3361 if (c.errors)
3363 catchErrors = true;
3364 continue;
3366 auto cd = c.type.toBasetype().isClassHandle();
3367 flags |= cd.isCPPclass() ? FLAGcpp : FLAGd;
3369 // Determine if current catch 'hides' any previous catches
3370 foreach (j; 0 .. i)
3372 Catch cj = (*tcs.catches)[j];
3373 const si = c.loc.toChars();
3374 const sj = cj.loc.toChars();
3375 if (c.type.toBasetype().implicitConvTo(cj.type.toBasetype()))
3377 error(tcs.loc, "`catch` at %s hides `catch` at %s", sj, si);
3378 catchErrors = true;
3383 if (sc.func)
3385 sc.func.hasCatches = true;
3386 if (flags == (FLAGcpp | FLAGd))
3388 error(tcs.loc, "cannot mix catching D and C++ exceptions in the same try-catch");
3389 catchErrors = true;
3393 if (catchErrors)
3394 return setError();
3396 // No actual code in the try (i.e. omitted any conditionally compiled code)
3397 // Could also be extended to check for hasCode
3398 if (!tcs._body)
3399 return;
3401 if (tcs._body.isErrorStatement())
3403 result = tcs._body;
3404 return;
3407 /* If the try body never throws, we can eliminate any catches
3408 * of recoverable exceptions.
3410 if (!(tcs._body.blockExit(sc.func, null) & BE.throw_) && ClassDeclaration.exception)
3412 foreach_reverse (i; 0 .. tcs.catches.length)
3414 Catch c = (*tcs.catches)[i];
3416 /* If catch exception type is derived from Exception
3418 if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) &&
3419 (!c.handler || !c.handler.comeFrom()) && !(sc.flags & SCOPE.debug_))
3421 // Remove c from the array of catches
3422 tcs.catches.remove(i);
3427 if (tcs.catches.length == 0)
3429 result = tcs._body.hasCode() ? tcs._body : null;
3430 return;
3433 result = tcs;
3436 void visitTryFinally(TryFinallyStatement tfs)
3438 //printf("TryFinallyStatement::semantic()\n");
3439 tfs.tryBody = sc.tryBody; // chain on in-flight tryBody
3440 tfs._body = tfs._body.semanticScope(sc, null, null, tfs);
3442 sc = sc.push();
3443 sc.tf = tfs;
3444 sc.sbreak = null;
3445 sc.scontinue = null; // no break or continue out of finally block
3446 tfs.finalbody = tfs.finalbody.semanticNoScope(sc);
3447 sc.pop();
3449 if (!tfs._body)
3451 result = tfs.finalbody;
3452 return;
3454 if (!tfs.finalbody)
3456 result = tfs._body;
3457 return;
3460 auto blockexit = tfs._body.blockExit(sc.func, null);
3462 // if not worrying about exceptions
3463 if (!(global.params.useExceptions && ClassDeclaration.throwable))
3464 blockexit &= ~BE.throw_; // don't worry about paths that otherwise may throw
3466 // Don't care about paths that halt, either
3467 if ((blockexit & ~BE.halt) == BE.fallthru)
3469 result = new CompoundStatement(tfs.loc, tfs._body, tfs.finalbody);
3470 return;
3472 tfs.bodyFallsThru = (blockexit & BE.fallthru) != 0;
3473 result = tfs;
3476 void visitScopeGuard(ScopeGuardStatement oss)
3478 /* https://dlang.org/spec/statement.html#scope-guard-statement
3481 if (oss.tok != TOK.onScopeExit)
3483 // https://issues.dlang.org/show_bug.cgi?id=23159
3484 if (!global.params.useExceptions)
3486 error(oss.loc, "`%s` cannot be used with -betterC", Token.toChars(oss.tok));
3487 return setError();
3490 // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement,
3491 // so the generated catch block cannot be placed in finally block.
3492 // See also Catch::semantic.
3493 if (sc.os && sc.os.tok != TOK.onScopeFailure)
3495 // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
3496 error(oss.loc, "cannot put `%s` statement inside `%s`", Token.toChars(oss.tok), Token.toChars(sc.os.tok));
3497 return setError();
3499 if (sc.tf)
3501 error(oss.loc, "cannot put `%s` statement inside `finally` block", Token.toChars(oss.tok));
3502 return setError();
3506 sc = sc.push();
3507 sc.tf = null;
3508 sc.os = oss;
3509 if (oss.tok != TOK.onScopeFailure)
3511 // Jump out from scope(failure) block is allowed.
3512 sc.sbreak = null;
3513 sc.scontinue = null;
3515 oss.statement = oss.statement.semanticNoScope(sc);
3516 sc.pop();
3518 if (!oss.statement || oss.statement.isErrorStatement())
3520 result = oss.statement;
3521 return;
3523 result = oss;
3526 void visitThrow(ThrowStatement ts)
3528 /* https://dlang.org/spec/statement.html#throw-statement
3531 //printf("ThrowStatement::semantic()\n");
3532 if (throwSemantic(ts.loc, ts.exp, sc))
3533 result = ts;
3534 else
3535 setError();
3539 void visitDebug(DebugStatement ds)
3541 if (ds.statement)
3543 sc = sc.push();
3544 sc.flags |= SCOPE.debug_;
3545 ds.statement = ds.statement.statementSemantic(sc);
3546 sc.pop();
3548 result = ds.statement;
3551 void visitGoto(GotoStatement gs)
3553 /* https://dlang.org/spec/statement.html#goto-statement
3556 //printf("GotoStatement::semantic()\n");
3557 FuncDeclaration fd = sc.func;
3559 gs.ident = fixupLabelName(sc, gs.ident);
3560 gs.label = fd.searchLabel(gs.ident, gs.loc);
3561 gs.tryBody = sc.tryBody;
3562 gs.tf = sc.tf;
3563 gs.os = sc.os;
3564 gs.lastVar = sc.lastVar;
3565 gs.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0;
3567 if (!gs.label.statement && sc.fes)
3569 /* Either the goto label is forward referenced or it
3570 * is in the function that the enclosing foreach is in.
3571 * Can't know yet, so wrap the goto in a scope statement
3572 * so we can patch it later, and add it to a 'look at this later'
3573 * list.
3575 gs.label.deleted = true;
3576 auto ss = new ScopeStatement(gs.loc, gs, gs.loc);
3577 sc.fes.gotos.push(ss); // 'look at this later' list
3578 result = ss;
3579 return;
3582 // Add to fwdref list to check later
3583 if (!gs.label.statement)
3585 if (!fd.gotos)
3586 fd.gotos = new GotoStatements();
3587 fd.gotos.push(gs);
3589 else if (!(sc.flags & SCOPE.Cfile) && gs.checkLabel())
3590 return setError();
3592 result = gs;
3595 void visitLabel(LabelStatement ls)
3597 //printf("LabelStatement::semantic()\n");
3598 FuncDeclaration fd = sc.parent.isFuncDeclaration();
3600 ls.ident = fixupLabelName(sc, ls.ident);
3601 ls.tryBody = sc.tryBody;
3602 ls.tf = sc.tf;
3603 ls.os = sc.os;
3604 ls.lastVar = sc.lastVar;
3605 ls.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0;
3607 LabelDsymbol ls2 = fd.searchLabel(ls.ident, ls.loc);
3608 if (ls2.statement && !ls2.duplicated)
3610 if (ls.loc == ls2.loc)
3612 ls2.duplicated = true;
3613 error(ls.loc, "label `%s` is duplicated", ls2.toChars());
3614 .errorSupplemental(ls2.loc, "labels cannot be used in a static foreach with more than 1 iteration");
3616 else
3618 error(ls.loc, "label `%s` is already defined", ls2.toChars());
3619 .errorSupplemental(ls2.loc, "first definition is here");
3621 return setError();
3623 else
3624 ls2.statement = ls;
3626 sc = sc.push();
3627 sc.scopesym = sc.enclosing.scopesym;
3629 sc.ctorflow.orCSX(CSX.label);
3631 sc.slabel = ls;
3632 if (ls.statement)
3633 ls.statement = ls.statement.statementSemantic(sc);
3634 sc.pop();
3636 result = ls;
3639 void visitAsm(AsmStatement s)
3641 /* https://dlang.org/spec/statement.html#asm
3644 //printf("AsmStatement()::semantic()\n");
3645 result = asmSemantic(s, sc);
3648 void visitCompoundAsm(CompoundAsmStatement cas)
3650 //printf("CompoundAsmStatement()::semantic()\n");
3651 // Apply postfix attributes of the asm block to each statement.
3652 sc = sc.push();
3653 sc.stc |= cas.stc;
3655 /* Go through the statements twice, first to declare any labels,
3656 * second for anything else.
3659 foreach (ref s; *cas.statements)
3661 if (s)
3663 if (auto ls = s.isLabelStatement())
3665 sc.func.searchLabel(ls.ident, ls.loc);
3670 foreach (ref s; *cas.statements)
3672 s = s ? s.statementSemantic(sc) : null;
3675 assert(sc.func);
3676 if (!(cas.stc & STC.pure_) && sc.func.setImpure(cas.loc, "`asm` statement is assumed to be impure - mark it with `pure` if it is not"))
3677 error(cas.loc, "`asm` statement is assumed to be impure - mark it with `pure` if it is not");
3678 if (!(cas.stc & STC.nogc) && sc.func.setGC(cas.loc, "`asm` statement in %s `%s` is assumed to use the GC - mark it with `@nogc` if it does not"))
3679 error(cas.loc, "`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not");
3680 // @@@DEPRECATED_2.114@@@
3681 // change deprecation() to error(), add `else` and remove `| STC.safe`
3682 // to turn deprecation into an error when deprecation cycle is over
3683 if (cas.stc & STC.safe)
3684 deprecation(cas.loc, "`asm` statement cannot be marked `@safe`, use `@system` or `@trusted` instead");
3685 if (!(cas.stc & (STC.trusted | STC.safe)))
3687 sc.setUnsafe(false, cas.loc, "`asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not");
3690 sc.pop();
3691 result = cas;
3694 void visitImport(ImportStatement imps)
3696 /* https://dlang.org/spec/module.html#ImportDeclaration
3699 foreach (i; 0 .. imps.imports.length)
3701 Import s = (*imps.imports)[i].isImport();
3702 assert(!s.aliasdecls.length);
3703 foreach (j, name; s.names)
3705 Identifier _alias = s.aliases[j];
3706 if (!_alias)
3707 _alias = name;
3709 auto tname = new TypeIdentifier(s.loc, name);
3710 auto ad = new AliasDeclaration(s.loc, _alias, tname);
3711 ad._import = s;
3712 s.aliasdecls.push(ad);
3715 s.dsymbolSemantic(sc);
3717 // https://issues.dlang.org/show_bug.cgi?id=19942
3718 // If the module that's being imported doesn't exist, don't add it to the symbol table
3719 // for the current scope.
3720 if (s.mod !is null)
3722 Module.addDeferredSemantic2(s); // https://issues.dlang.org/show_bug.cgi?id=14666
3723 sc.insert(s);
3725 foreach (aliasdecl; s.aliasdecls)
3727 sc.insert(aliasdecl);
3731 result = imps;
3734 mixin VisitStatement!void visit;
3735 visit.VisitStatement(s);
3736 return result;
3740 * Run semantic on `throw <exp>`.
3742 * Params:
3743 * loc = location of the `throw`
3744 * exp = value to be thrown
3745 * sc = enclosing scope
3747 * Returns: true if the `throw` is valid, or false if an error was found
3749 public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc)
3751 if (!global.params.useExceptions)
3753 loc.error("cannot use `throw` statements with %s", global.params.betterC ? "-betterC".ptr : "-nothrow".ptr);
3754 return false;
3757 if (!ClassDeclaration.throwable)
3759 loc.error("cannot use `throw` statements because `object.Throwable` was not declared");
3760 return false;
3763 if (FuncDeclaration fd = sc.parent.isFuncDeclaration())
3764 fd.hasReturnExp |= 2;
3766 if (exp.op == EXP.new_)
3768 NewExp ne = cast(NewExp) exp;
3769 ne.thrownew = true;
3772 exp = exp.expressionSemantic(sc);
3773 exp = resolveProperties(sc, exp);
3774 exp = checkGC(sc, exp);
3775 if (exp.op == EXP.error)
3776 return false;
3777 if (!exp.type.isNaked())
3779 // @@@DEPRECATED_2.112@@@
3780 // Deprecated in 2.102, change into an error & return false in 2.112
3781 exp.loc.deprecation("cannot throw object of qualified type `%s`", exp.type.toChars());
3782 //return false;
3784 checkThrowEscape(sc, exp, false);
3786 ClassDeclaration cd = exp.type.toBasetype().isClassHandle();
3787 if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null)))
3789 loc.error("can only throw class objects derived from `Throwable`, not type `%s`", exp.type.toChars());
3790 return false;
3792 return true;
3795 private extern(D) Expression applyOpApply(ForeachStatement fs, Expression flde,
3796 Type tab, Scope* sc2, Dsymbol sapply)
3798 version (none)
3800 if (global.params.useDIP1000 == FeatureState.enabled)
3802 message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`");
3804 (cast(FuncExp)flde).fd.tookAddressOf = 1;
3806 else
3808 if (global.params.useDIP1000 == FeatureState.enabled)
3809 ++(cast(FuncExp)flde).fd.tookAddressOf; // allocate a closure unless the opApply() uses 'scope'
3811 assert(tab.ty == Tstruct || tab.ty == Tclass);
3812 assert(sapply);
3813 /* Call:
3814 * aggr.apply(flde)
3816 Expression ec;
3817 ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident);
3818 ec = new CallExp(fs.loc, ec, flde);
3819 ec = ec.expressionSemantic(sc2);
3820 if (ec.op == EXP.error)
3821 return null;
3822 if (ec.type != Type.tint32)
3824 error(fs.loc, "`opApply()` function for `%s` must return an `int`", tab.toChars());
3825 return null;
3827 return ec;
3830 private extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde,
3831 Type tab, Scope* sc2)
3833 Expression ec;
3834 /* Call:
3835 * aggr(flde)
3837 if (fs.aggr.op == EXP.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() &&
3838 !(cast(DelegateExp)fs.aggr).func.needThis())
3840 // https://issues.dlang.org/show_bug.cgi?id=3560
3841 fs.aggr = (cast(DelegateExp)fs.aggr).e1;
3843 ec = new CallExp(fs.loc, fs.aggr, flde);
3844 ec = ec.expressionSemantic(sc2);
3845 if (ec.op == EXP.error)
3846 return null;
3847 if (ec.type != Type.tint32)
3849 error(fs.loc, "`opApply()` function for `%s` must return an `int`", tab.toChars());
3850 return null;
3852 return ec;
3855 private extern(D) Expression applyArray(ForeachStatement fs, Expression flde,
3856 Type tab, Scope* sc2, Type tn, Type tnv)
3858 Expression ec;
3859 const dim = fs.parameters.length;
3860 const loc = fs.loc;
3861 /* Call:
3862 * _aApply(aggr, flde)
3864 static immutable fntab =
3866 "cc", "cw", "cd",
3867 "wc", "cc", "wd",
3868 "dc", "dw", "dd"
3871 const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1;
3872 char[BUFFER_LEN] fdname;
3873 int flag;
3875 switch (tn.ty)
3877 case Tchar: flag = 0; break;
3878 case Twchar: flag = 3; break;
3879 case Tdchar: flag = 6; break;
3880 default:
3881 assert(0);
3883 switch (tnv.ty)
3885 case Tchar: flag += 0; break;
3886 case Twchar: flag += 1; break;
3887 case Tdchar: flag += 2; break;
3888 default:
3889 assert(0);
3891 const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : "";
3892 int j = snprintf(fdname.ptr, BUFFER_LEN, "_aApply%s%.*s%llu", r, 2, fntab[flag].ptr, cast(ulong)dim);
3893 assert(j < BUFFER_LEN);
3895 FuncDeclaration fdapply;
3896 TypeDelegate dgty;
3897 auto params = new Parameters();
3898 params.push(new Parameter(Loc.initial, STC.in_, tn.arrayOf(), null, null, null));
3899 auto dgparams = new Parameters();
3900 dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null));
3901 if (dim == 2)
3902 dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null));
3903 dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
3904 params.push(new Parameter(Loc.initial, 0, dgty, null, null, null));
3905 fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);
3907 if (tab.isTypeSArray())
3908 fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf());
3909 // paint delegate argument to the type runtime expects
3910 Expression fexp = flde;
3911 if (!dgty.equals(flde.type))
3913 fexp = new CastExp(loc, flde, flde.type);
3914 fexp.type = dgty;
3916 ec = new VarExp(Loc.initial, fdapply, false);
3917 ec = new CallExp(loc, ec, fs.aggr, fexp);
3918 ec.type = Type.tint32; // don't run semantic() on ec
3919 return ec;
3922 private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab)
3924 auto taa = tab.isTypeAArray();
3925 Expression ec;
3926 const dim = fs.parameters.length;
3927 // Check types
3928 Parameter p = (*fs.parameters)[0];
3929 bool isRef = (p.storageClass & STC.ref_) != 0;
3930 Type ta = p.type;
3931 if (dim == 2)
3933 Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index);
3934 if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta))
3936 error(fs.loc, "`foreach`: index must be type `%s`, not `%s`",
3937 ti.toChars(), ta.toChars());
3938 return null;
3940 p = (*fs.parameters)[1];
3941 isRef = (p.storageClass & STC.ref_) != 0;
3942 ta = p.type;
3944 Type taav = taa.nextOf();
3945 if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
3947 error(fs.loc, "`foreach`: value must be type `%s`, not `%s`",
3948 taav.toChars(), ta.toChars());
3949 return null;
3952 /* Call:
3953 * extern(C) int _aaApply(void*, in size_t, int delegate(void*))
3954 * _aaApply(aggr, keysize, flde)
3956 * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
3957 * _aaApply2(aggr, keysize, flde)
3959 __gshared FuncDeclaration* fdapply = [null, null];
3960 __gshared TypeDelegate* fldeTy = [null, null];
3961 ubyte i = (dim == 2 ? 1 : 0);
3962 if (!fdapply[i])
3964 auto params = new Parameters();
3965 params.push(new Parameter(Loc.initial, 0, Type.tvoid.pointerTo(), null, null, null));
3966 params.push(new Parameter(Loc.initial, STC.const_, Type.tsize_t, null, null, null));
3967 auto dgparams = new Parameters();
3968 dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null));
3969 if (dim == 2)
3970 dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null));
3971 fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
3972 params.push(new Parameter(Loc.initial, 0, fldeTy[i], null, null, null));
3973 fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply);
3976 auto exps = new Expressions();
3977 exps.push(fs.aggr);
3978 auto keysize = taa.index.size();
3979 if (keysize == SIZE_INVALID)
3980 return null;
3981 assert(keysize < keysize.max - target.ptrsize);
3982 keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1);
3983 // paint delegate argument to the type runtime expects
3984 Expression fexp = flde;
3985 if (!fldeTy[i].equals(flde.type))
3987 fexp = new CastExp(fs.loc, flde, flde.type);
3988 fexp.type = fldeTy[i];
3990 exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t));
3991 exps.push(fexp);
3992 ec = new VarExp(Loc.initial, fdapply[i], false);
3993 ec = new CallExp(fs.loc, ec, exps);
3994 ec.type = Type.tint32; // don't run semantic() on ec
3995 return ec;
3998 private extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc)
4000 if (!cases.length)
4002 // Easy case, a clean exit from the loop
4003 e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899
4004 return new ExpStatement(loc, e);
4006 // Construct a switch statement around the return value
4007 // of the apply function.
4008 Statement s;
4009 auto a = new Statements();
4011 // default: break; takes care of cases 0 and 1
4012 s = new BreakStatement(Loc.initial, null);
4013 s = new DefaultStatement(Loc.initial, s);
4014 a.push(s);
4016 // cases 2...
4017 foreach (i, c; *cases)
4019 s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c);
4020 a.push(s);
4023 s = new CompoundStatement(loc, a);
4024 return new SwitchStatement(loc, null, e, s, false, loc);
4027 /*************************************
4028 * Turn foreach body into the function literal:
4029 * int delegate(ref T param) { body }
4030 * Params:
4031 * sc = context
4032 * fs = ForeachStatement
4033 * tfld = type of function literal to be created (type of opApply() function if any), can be null
4034 * Returns:
4035 * Function literal created, as an expression
4036 * null if error.
4038 private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld)
4040 auto params = new Parameters();
4041 foreach (i, p; *fs.parameters)
4043 StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_);
4044 Identifier id;
4046 p.type = p.type.typeSemantic(fs.loc, sc);
4047 p.type = p.type.addStorageClass(p.storageClass);
4048 if (tfld)
4050 Parameter prm = tfld.parameterList[i];
4051 //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars());
4052 stc = (prm.storageClass & STC.ref_) | (p.storageClass & STC.scope_);
4053 if ((p.storageClass & STC.ref_) != (prm.storageClass & STC.ref_))
4055 if (!(prm.storageClass & STC.ref_))
4057 error(fs.loc, "`foreach`: cannot make `%s` `ref`", p.ident.toChars());
4058 return null;
4060 goto LcopyArg;
4062 id = p.ident; // argument copy is not need.
4064 else if (p.storageClass & STC.ref_)
4066 // default delegate parameters are marked as ref, then
4067 // argument copy is not need.
4068 id = p.ident;
4070 else
4072 // Make a copy of the ref argument so it isn't
4073 // a reference.
4074 LcopyArg:
4075 id = Identifier.generateId("__applyArg", cast(int)i);
4077 Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id));
4078 auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie);
4079 v.storage_class |= STC.temp | (stc & STC.scope_);
4080 Statement s = new ExpStatement(fs.loc, v);
4081 fs._body = new CompoundStatement(fs.loc, s, fs._body);
4083 params.push(new Parameter(fs.loc, stc, p.type, id, null, null));
4085 // https://issues.dlang.org/show_bug.cgi?id=13840
4086 // Throwable nested function inside nothrow function is acceptable.
4087 StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func);
4088 auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc);
4089 fs.cases = new Statements();
4090 fs.gotos = new ScopeStatements();
4091 auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs);
4092 fld.fbody = fs._body;
4093 Expression flde = new FuncExp(fs.loc, fld);
4094 flde = flde.expressionSemantic(sc);
4095 fld.tookAddressOf = 0;
4096 if (flde.op == EXP.error)
4097 return null;
4098 return cast(FuncExp)flde;
4102 void catchSemantic(Catch c, Scope* sc)
4104 //printf("Catch::semantic(%s)\n", ident.toChars());
4106 if (sc.os && sc.os.tok != TOK.onScopeFailure)
4108 // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
4109 error(c.loc, "cannot put `catch` statement inside `%s`", Token.toChars(sc.os.tok));
4110 c.errors = true;
4112 if (sc.tf)
4114 /* This is because the _d_local_unwind() gets the stack munged
4115 * up on this. The workaround is to place any try-catches into
4116 * a separate function, and call that.
4117 * To fix, have the compiler automatically convert the finally
4118 * body into a nested function.
4120 error(c.loc, "cannot put `catch` statement inside `finally` block");
4121 c.errors = true;
4124 auto sym = new ScopeDsymbol();
4125 sym.parent = sc.scopesym;
4126 sc = sc.push(sym);
4128 if (!c.type)
4130 error(c.loc, "`catch` statement without an exception specification is deprecated");
4131 errorSupplemental(c.loc, "use `catch(Throwable)` for old behavior");
4132 c.errors = true;
4134 // reference .object.Throwable
4135 c.type = getThrowable();
4137 else if (!c.type.isNaked() && !c.type.isConst())
4139 // @@@DEPRECATED_2.115@@@
4140 // Deprecated in 2.105, change into an error & uncomment assign in 2.115
4141 deprecation(c.loc, "can only catch mutable or const qualified types, not `%s`", c.type.toChars());
4142 //c.errors = true;
4144 c.type = c.type.typeSemantic(c.loc, sc);
4145 if (c.type == Type.terror)
4147 c.errors = true;
4148 sc.pop();
4149 return;
4152 StorageClass stc;
4153 auto cd = c.type.toBasetype().isClassHandle();
4154 if (!cd)
4156 error(c.loc, "can only catch class objects, not `%s`", c.type.toChars());
4157 c.errors = true;
4159 else if (cd.isCPPclass())
4161 if (!target.cpp.exceptions)
4163 error(c.loc, "catching C++ class objects not supported for this target");
4164 c.errors = true;
4166 if (!c.internalCatch)
4168 if (sc.setUnsafe(false, c.loc, "cannot catch C++ class objects in `@safe` code"))
4169 c.errors = true;
4172 else if (cd != ClassDeclaration.throwable && !ClassDeclaration.throwable.isBaseOf(cd, null))
4174 error(c.loc, "can only catch class objects derived from `Throwable`, not `%s`", c.type.toChars());
4175 c.errors = true;
4177 else if (!c.internalCatch && ClassDeclaration.exception &&
4178 cd != ClassDeclaration.exception && !ClassDeclaration.exception.isBaseOf(cd, null) &&
4179 sc.setUnsafe(false, c.loc,
4180 "can only catch class objects derived from `Exception` in `@safe` code, not `%s`", c.type))
4182 c.errors = true;
4184 else if (global.params.ehnogc)
4186 stc |= STC.scope_;
4189 // DIP1008 requires destruction of the Throwable, even if the user didn't specify an identifier
4190 auto ident = c.ident;
4191 if (!ident && global.params.ehnogc)
4192 ident = Identifier.generateAnonymousId("var");
4194 if (ident)
4196 c.var = new VarDeclaration(c.loc, c.type, ident, null, stc);
4197 c.var.iscatchvar = true;
4198 c.var.dsymbolSemantic(sc);
4199 sc.insert(c.var);
4201 if (global.params.ehnogc && stc & STC.scope_)
4203 /* Add a destructor for c.var
4204 * try { handler } finally { if (!__ctfe) _d_delThrowable(var); }
4206 assert(!c.var.edtor); // ensure we didn't create one in callScopeDtor()
4208 Loc loc = c.loc;
4209 Expression e = new VarExp(loc, c.var);
4210 e = new CallExp(loc, new IdentifierExp(loc, Id._d_delThrowable), e);
4212 Expression ec = new IdentifierExp(loc, Id.ctfe);
4213 ec = new NotExp(loc, ec);
4214 Statement s = new IfStatement(loc, null, ec, new ExpStatement(loc, e), null, loc);
4215 c.handler = new TryFinallyStatement(loc, c.handler, s);
4219 c.handler = c.handler.statementSemantic(sc);
4220 if (c.handler && c.handler.isErrorStatement())
4221 c.errors = true;
4223 sc.pop();
4226 Statement semanticNoScope(Statement s, Scope* sc)
4228 //printf("Statement::semanticNoScope() %s\n", toChars());
4229 if (!s.isCompoundStatement() && !s.isScopeStatement())
4231 s = new CompoundStatement(s.loc, s); // so scopeCode() gets called
4233 s = s.statementSemantic(sc);
4234 return s;
4237 // Same as semanticNoScope(), but do create a new scope
4238 private Statement semanticScope(Statement s, Scope* sc, Statement sbreak, Statement scontinue, Statement tryBody)
4240 auto sym = new ScopeDsymbol();
4241 sym.parent = sc.scopesym;
4242 Scope* scd = sc.push(sym);
4243 if (sbreak)
4244 scd.sbreak = sbreak;
4245 if (scontinue)
4246 scd.scontinue = scontinue;
4247 if (tryBody)
4248 scd.tryBody = tryBody;
4249 s = s.semanticNoScope(scd);
4250 scd.pop();
4251 return s;
4255 /****************************************
4256 * If `statement` has code that needs to run in a finally clause
4257 * at the end of the current scope, return that code in the form of
4258 * a Statement.
4259 * Params:
4260 * statement = the statement
4261 * sc = context
4262 * sentry = set to code executed upon entry to the scope
4263 * sexception = set to code executed upon exit from the scope via exception
4264 * sfinally = set to code executed in finally block
4265 * Returns:
4266 * code to be run in the finally clause
4268 Statement scopeCode(Statement statement, Scope* sc, out Statement sentry, out Statement sexception, out Statement sfinally)
4270 if (auto es = statement.isExpStatement())
4272 if (es.exp && es.exp.op == EXP.declaration)
4274 auto de = cast(DeclarationExp)es.exp;
4275 auto v = de.declaration.isVarDeclaration();
4276 if (v && !v.isDataseg())
4278 if (v.needsScopeDtor())
4280 sfinally = new DtorExpStatement(es.loc, v.edtor, v);
4281 v.storage_class |= STC.nodtor; // don't add in dtor again
4285 return es;
4288 else if (auto sgs = statement.isScopeGuardStatement())
4290 Statement s = new PeelStatement(sgs.statement);
4292 switch (sgs.tok)
4294 case TOK.onScopeExit:
4295 sfinally = s;
4296 break;
4298 case TOK.onScopeFailure:
4299 sexception = s;
4300 break;
4302 case TOK.onScopeSuccess:
4304 /* Create:
4305 * sentry: bool x = false;
4306 * sexception: x = true;
4307 * sfinally: if (!x) statement;
4309 auto v = copyToTemp(0, "__os", IntegerExp.createBool(false));
4310 v.dsymbolSemantic(sc);
4311 sentry = new ExpStatement(statement.loc, v);
4313 Expression e = IntegerExp.createBool(true);
4314 e = new AssignExp(Loc.initial, new VarExp(Loc.initial, v), e);
4315 sexception = new ExpStatement(Loc.initial, e);
4317 e = new VarExp(Loc.initial, v);
4318 e = new NotExp(Loc.initial, e);
4319 sfinally = new IfStatement(Loc.initial, null, e, s, null, Loc.initial);
4321 break;
4323 default:
4324 assert(0);
4326 return null;
4328 else if (auto ls = statement.isLabelStatement())
4330 if (ls.statement)
4331 ls.statement = ls.statement.scopeCode(sc, sentry, sexception, sfinally);
4332 return ls;
4335 return statement;
4338 /*******************
4339 * Type check and unroll `foreach` over an expression tuple as well
4340 * as `static foreach` statements and `static foreach`
4341 * declarations. For `static foreach` statements and `static
4342 * foreach` declarations, the visitor interface is used (and the
4343 * result is written into the `result` field.) For `static
4344 * foreach` declarations, the resulting Dsymbols* are returned
4345 * directly.
4347 * The unrolled body is wrapped into a
4348 * - UnrolledLoopStatement, for `foreach` over an expression tuple.
4349 * - ForwardingStatement, for `static foreach` statements.
4350 * - ForwardingAttribDeclaration, for `static foreach` declarations.
4352 * `static foreach` variables are declared as `STC.local`, such
4353 * that they are inserted into the local symbol tables of the
4354 * forwarding constructs instead of forwarded. For `static
4355 * foreach` with multiple foreach loop variables whose aggregate
4356 * has been lowered into a sequence of tuples, this function
4357 * expands the tuples into multiple `STC.local` `static foreach`
4358 * variables.
4360 public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachStatement fs, Dsymbols* dbody, bool needExpansion)
4362 // Voldemort return type
4363 union U
4365 Statement statement;
4366 Dsymbols* decl;
4369 U result;
4371 auto returnEarly()
4373 if (isDecl)
4374 result.decl = null;
4375 else
4376 result.statement = new ErrorStatement();
4377 return result;
4380 auto loc = fs.loc;
4381 size_t dim = fs.parameters.length;
4382 const bool skipCheck = isStatic && needExpansion;
4383 if (!skipCheck && (dim < 1 || dim > 2))
4385 error(fs.loc, "only one (value) or two (key,value) arguments allowed for sequence `foreach`");
4386 return returnEarly();
4389 Type paramtype = (*fs.parameters)[dim - 1].type;
4390 if (paramtype)
4392 paramtype = paramtype.typeSemantic(loc, sc);
4393 if (paramtype.ty == Terror)
4395 return returnEarly();
4399 Type tab = fs.aggr.type.toBasetype();
4400 TypeTuple tuple = cast(TypeTuple)tab;
4402 Statements* statements;
4403 Dsymbols* declarations;
4404 if (isDecl)
4405 declarations = new Dsymbols();
4406 else
4407 statements = new Statements();
4409 //printf("aggr: op = %d, %s\n", fs.aggr.op, fs.aggr.toChars());
4410 size_t n;
4411 TupleExp te = null;
4412 if (fs.aggr.op == EXP.tuple) // expression tuple
4414 te = cast(TupleExp)fs.aggr;
4415 n = te.exps.length;
4417 else if (fs.aggr.op == EXP.type) // type tuple
4419 n = Parameter.dim(tuple.arguments);
4421 else
4422 assert(0);
4423 foreach (j; 0 .. n)
4425 size_t k = (fs.op == TOK.foreach_) ? j : n - 1 - j;
4426 Expression e = null;
4427 Type t = null;
4428 if (te)
4429 e = (*te.exps)[k];
4430 else
4431 t = Parameter.getNth(tuple.arguments, k).type;
4432 Parameter p = (*fs.parameters)[0];
4434 Statements* stmts;
4435 Dsymbols* decls;
4436 if (isDecl)
4437 decls = new Dsymbols();
4438 else
4439 stmts = new Statements();
4441 const bool skip = isStatic && needExpansion;
4442 if (!skip && dim == 2)
4444 // Declare key
4445 if (p.isReference() || p.isLazy())
4447 error(fs.loc, "no storage class for key `%s`", p.ident.toChars());
4448 return returnEarly();
4451 if (isStatic)
4453 if (!p.type)
4455 p.type = Type.tsize_t;
4458 p.type = p.type.typeSemantic(loc, sc);
4460 if (!p.type.isintegral())
4462 error(fs.loc, "foreach: key cannot be of non-integral type `%s`",
4463 p.type.toChars());
4464 return returnEarly();
4467 const length = te ? te.exps.length : tuple.arguments.length;
4468 IntRange dimrange = IntRange(SignExtendedNumber(length))._cast(Type.tsize_t);
4469 // https://issues.dlang.org/show_bug.cgi?id=12504
4470 dimrange.imax = SignExtendedNumber(dimrange.imax.value-1);
4471 if (!IntRange.fromType(p.type).contains(dimrange))
4473 error(fs.loc, "index type `%s` cannot cover index range 0..%llu",
4474 p.type.toChars(), cast(ulong)length);
4475 return returnEarly();
4477 Initializer ie = new ExpInitializer(Loc.initial, new IntegerExp(k));
4478 auto var = new VarDeclaration(loc, p.type, p.ident, ie);
4479 var.storage_class |= STC.foreach_ | STC.manifest;
4480 if (isStatic)
4481 var.storage_class |= STC.local;
4483 if (isDecl)
4484 decls.push(var);
4485 else
4486 stmts.push(new ExpStatement(loc, var));
4488 p = (*fs.parameters)[1]; // value
4490 /***********************
4491 * Declares a unrolled `foreach` loop variable or a `static foreach` variable.
4493 * Params:
4494 * storageClass = The storage class of the variable.
4495 * type = The declared type of the variable.
4496 * ident = The name of the variable.
4497 * e = The initializer of the variable (i.e. the current element of the looped over aggregate).
4498 * t = The type of the initializer.
4499 * Returns:
4500 * `true` iff the declaration was successful.
4502 bool declareVariable(StorageClass storageClass, Type type, Identifier ident, Expression e, Type t)
4504 if (storageClass & (STC.out_ | STC.lazy_) ||
4505 storageClass & STC.ref_ && !te)
4507 error(fs.loc, "no storage class for value `%s`", ident.toChars());
4508 return false;
4510 Declaration var;
4511 if (e)
4513 Type tb = e.type.toBasetype();
4514 Dsymbol ds = null;
4515 if (!(storageClass & STC.manifest))
4517 if (isStatic || tb.ty == Tfunction || storageClass & STC.alias_)
4519 if (auto ve = e.isVarExp())
4520 ds = ve.var;
4521 else if (auto dve = e.isDotVarExp())
4522 ds = dve.var;
4524 if (auto te = e.isTemplateExp())
4525 ds = te.td;
4526 else if (auto se = e.isScopeExp())
4527 ds = se.sds;
4528 else if (auto fe = e.isFuncExp())
4529 ds = fe.td ? fe.td : fe.fd;
4530 else if (auto oe = e.isOverExp())
4531 ds = oe.vars;
4533 else if (storageClass & STC.alias_)
4535 error(fs.loc, "`foreach` loop variable cannot be both `enum` and `alias`");
4536 return false;
4539 if (ds)
4541 var = new AliasDeclaration(loc, ident, ds);
4542 if (storageClass & STC.ref_)
4544 error(fs.loc, "symbol `%s` cannot be `ref`", ds.toChars());
4545 return false;
4547 if (paramtype)
4549 error(fs.loc, "cannot specify element type for symbol `%s`", ds.toChars());
4550 return false;
4553 else if (e.op == EXP.type)
4555 var = new AliasDeclaration(loc, ident, e.type);
4556 if (paramtype)
4558 error(fs.loc, "cannot specify element type for type `%s`", e.type.toChars());
4559 return false;
4562 else
4564 e = resolveProperties(sc, e);
4565 Initializer ie = new ExpInitializer(Loc.initial, e);
4566 auto v = new VarDeclaration(loc, type, ident, ie, storageClass);
4567 v.storage_class |= STC.foreach_;
4568 if (storageClass & STC.ref_)
4569 v.storage_class |= STC.ref_;
4570 if (isStatic || storageClass&STC.manifest || e.isConst() ||
4571 e.op == EXP.string_ ||
4572 e.op == EXP.structLiteral ||
4573 e.op == EXP.arrayLiteral)
4575 if (v.storage_class & STC.ref_)
4577 if (!isStatic)
4579 error(fs.loc, "constant value `%s` cannot be `ref`", toChars(ie));
4581 else
4583 if (!needExpansion)
4585 error(fs.loc, "constant value `%s` cannot be `ref`", toChars(ie));
4587 else
4589 error(fs.loc, "constant value `%s` cannot be `ref`", ident.toChars());
4592 return false;
4594 else
4595 v.storage_class |= STC.manifest;
4597 var = v;
4600 else
4602 var = new AliasDeclaration(loc, ident, t);
4603 if (paramtype)
4605 error(fs.loc, "cannot specify element type for symbol `%s`", fs.toChars());
4606 return false;
4609 if (isStatic)
4611 var.storage_class |= STC.local;
4614 if (isDecl)
4615 decls.push(var);
4616 else
4617 stmts.push(new ExpStatement(loc, var));
4618 return true;
4621 if (!isStatic)
4623 // Declare value
4624 if (!declareVariable(p.storageClass, p.type, p.ident, e, t))
4626 return returnEarly();
4629 else
4631 if (!needExpansion)
4633 // Declare value
4634 if (!declareVariable(p.storageClass, p.type, p.ident, e, t))
4636 return returnEarly();
4639 else
4640 { // expand tuples into multiple `static foreach` variables.
4641 assert(e && !t);
4642 auto ident = Identifier.generateId("__value");
4643 declareVariable(0, e.type, ident, e, null);
4644 import dmd.cond: StaticForeach;
4645 auto field = Identifier.idPool(StaticForeach.tupleFieldName.ptr,StaticForeach.tupleFieldName.length);
4646 Expression access = new DotIdExp(loc, e, field);
4647 access = expressionSemantic(access, sc);
4648 access = access.optimize(WANTvalue);
4649 if (!tuple) return returnEarly();
4650 //printf("%s\n",tuple.toChars());
4651 foreach (l; 0 .. dim)
4653 auto cp = (*fs.parameters)[l];
4654 Expression init_ = new IndexExp(loc, access, new IntegerExp(loc, l, Type.tsize_t));
4655 init_ = init_.expressionSemantic(sc);
4656 assert(init_.type);
4657 declareVariable(p.storageClass, init_.type, cp.ident, init_, null);
4662 Statement s;
4663 Dsymbol d;
4664 if (isDecl)
4665 decls.append(Dsymbol.arraySyntaxCopy(dbody));
4666 else
4668 stmts.push(fs._body.syntaxCopy());
4669 s = new CompoundStatement(loc, stmts);
4672 if (!isStatic)
4674 s = new ScopeStatement(loc, s, fs.endloc);
4676 else if (isDecl)
4678 import dmd.attrib: ForwardingAttribDeclaration;
4679 d = new ForwardingAttribDeclaration(decls);
4681 else
4683 s = new ForwardingStatement(loc, s);
4686 if (isDecl)
4687 declarations.push(d);
4688 else
4689 statements.push(s);
4692 if (!isStatic)
4694 Statement res = new UnrolledLoopStatement(loc, statements);
4695 if (LabelStatement ls = checkLabeledLoop(sc, fs))
4696 ls.gotoTarget = res;
4697 if (te && te.e0)
4698 res = new CompoundStatement(loc, new ExpStatement(te.e0.loc, te.e0), res);
4699 result.statement = res;
4701 else if (isDecl)
4702 result.decl = declarations;
4703 else
4704 result.statement = new CompoundStatement(loc, statements);
4706 return result;
4709 /*********************************
4710 * Flatten out the scope by presenting `statement`
4711 * as an array of statements.
4712 * Params:
4713 * statement = the statement to flatten
4714 * sc = context
4715 * Returns:
4716 * The array of `Statements`, or `null` if no flattening necessary
4718 private Statements* flatten(Statement statement, Scope* sc)
4720 static auto errorStatements()
4722 auto a = new Statements();
4723 a.push(new ErrorStatement());
4724 return a;
4728 /*compound and expression statements have classes that inherit from them with the same
4729 *flattening behavior, so the isXXX methods won't work
4731 switch(statement.stmt)
4733 case STMT.Compound:
4734 case STMT.CompoundDeclaration:
4735 return (cast(CompoundStatement)statement).statements;
4737 case STMT.Exp:
4738 case STMT.DtorExp:
4739 auto es = cast(ExpStatement)statement;
4740 /* https://issues.dlang.org/show_bug.cgi?id=14243
4741 * expand template mixin in statement scope
4742 * to handle variable destructors.
4744 if (!es.exp || !es.exp.isDeclarationExp())
4745 return null;
4747 Dsymbol d = es.exp.isDeclarationExp().declaration;
4748 auto tm = d.isTemplateMixin();
4749 if (!tm)
4750 return null;
4752 Expression e = es.exp.expressionSemantic(sc);
4753 if (e.op == EXP.error || tm.errors)
4754 return errorStatements();
4755 assert(tm.members);
4757 Statement s = toStatement(tm);
4758 version (none)
4760 OutBuffer buf;
4761 buf.doindent = 1;
4762 HdrGenState hgs;
4763 hgs.hdrgen = true;
4764 toCBuffer(s, &buf, &hgs);
4765 printf("tm ==> s = %s\n", buf.peekChars());
4767 auto a = new Statements();
4768 a.push(s);
4769 return a;
4771 case STMT.Forwarding:
4772 /***********************
4773 * ForwardingStatements are distributed over the flattened
4774 * sequence of statements. This prevents flattening to be
4775 * "blocked" by a ForwardingStatement and is necessary, for
4776 * example, to support generating scope guards with `static
4777 * foreach`:
4779 * static foreach(i; 0 .. 10) scope(exit) writeln(i);
4780 * writeln("this is printed first");
4781 * // then, it prints 10, 9, 8, 7, ...
4783 auto fs = statement.isForwardingStatement();
4784 if (!fs.statement)
4786 return null;
4788 sc = sc.push(fs.sym);
4789 auto a = fs.statement.flatten(sc);
4790 sc = sc.pop();
4791 if (!a)
4793 return a;
4795 auto b = new Statements(a.length);
4796 foreach (i, s; *a)
4798 (*b)[i] = s ? new ForwardingStatement(s.loc, fs.sym, s) : null;
4800 return b;
4802 case STMT.Conditional:
4803 auto cs = statement.isConditionalStatement();
4804 Statement s;
4806 //printf("ConditionalStatement::flatten()\n");
4807 if (cs.condition.include(sc))
4809 DebugCondition dc = cs.condition.isDebugCondition();
4810 if (dc)
4812 s = new DebugStatement(cs.loc, cs.ifbody);
4813 debugThrowWalker(cs.ifbody);
4815 else
4816 s = cs.ifbody;
4818 else
4819 s = cs.elsebody;
4821 auto a = new Statements();
4822 a.push(s);
4823 return a;
4825 case STMT.StaticForeach:
4826 auto sfs = statement.isStaticForeachStatement();
4827 sfs.sfe.prepare(sc);
4828 if (sfs.sfe.ready())
4830 Statement s = makeTupleForeach(sc, true, false, sfs.sfe.aggrfe, null, sfs.sfe.needExpansion).statement;
4831 auto result = s.flatten(sc);
4832 if (result)
4834 return result;
4836 result = new Statements();
4837 result.push(s);
4838 return result;
4840 else
4841 return errorStatements();
4843 case STMT.Debug:
4844 auto ds = statement.isDebugStatement();
4845 Statements* a = ds.statement ? ds.statement.flatten(sc) : null;
4846 if (!a)
4847 return null;
4849 foreach (ref s; *a)
4851 s = new DebugStatement(ds.loc, s);
4853 return a;
4855 case STMT.Label:
4856 auto ls = statement.isLabelStatement();
4857 if (!ls.statement)
4858 return null;
4860 Statements* a = null;
4861 a = ls.statement.flatten(sc);
4862 if (!a)
4863 return null;
4865 if (!a.length)
4867 a.push(new ExpStatement(ls.loc, cast(Expression)null));
4870 // reuse 'this' LabelStatement
4871 ls.statement = (*a)[0];
4872 (*a)[0] = ls;
4873 return a;
4875 case STMT.Mixin:
4876 auto cs = statement.isMixinStatement();
4879 OutBuffer buf;
4880 if (expressionsToString(buf, sc, cs.exps))
4881 return errorStatements();
4883 const errors = global.errors;
4884 const len = buf.length;
4885 buf.writeByte(0);
4886 const str = buf.extractSlice()[0 .. len];
4887 const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
4888 auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut);
4889 scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
4890 p.transitionIn = global.params.v.vin;
4891 p.nextToken();
4893 auto a = new Statements();
4894 while (p.token.value != TOK.endOfFile)
4896 Statement s = p.parseStatement(ParseStatementFlags.curlyScope);
4897 if (!s || global.errors != errors)
4898 return errorStatements();
4899 a.push(s);
4901 return a;
4902 default:
4903 return null;
4907 /***********************************************************
4908 * Convert TemplateMixin members (which are Dsymbols) to Statements.
4909 * Params:
4910 * s = the symbol to convert to a Statement
4911 * Returns:
4912 * s redone as a Statement
4914 private Statement toStatement(Dsymbol s)
4916 Statement result;
4918 if (auto tm = s.isTemplateMixin())
4920 auto a = new Statements();
4921 foreach (m; *tm.members)
4923 if (Statement sx = toStatement(m))
4924 a.push(sx);
4926 result = new CompoundStatement(tm.loc, a);
4928 else if (s.isVarDeclaration() ||
4929 s.isAggregateDeclaration() ||
4930 s.isFuncDeclaration() ||
4931 s.isEnumDeclaration() ||
4932 s.isAliasDeclaration() ||
4933 s.isTemplateDeclaration())
4935 /* Perhaps replace the above with isScopeDsymbol() || isDeclaration()
4937 /* An actual declaration symbol will be converted to DeclarationExp
4938 * with ExpStatement.
4940 auto de = new DeclarationExp(s.loc, s);
4941 de.type = Type.tvoid; // avoid repeated semantic
4942 result = new ExpStatement(s.loc, de);
4944 else if (auto d = s.isAttribDeclaration())
4946 /* All attributes have been already picked by the semantic analysis of
4947 * 'bottom' declarations (function, struct, class, etc).
4948 * So we don't have to copy them.
4950 if (Dsymbols* a = d.include(null))
4952 auto statements = new Statements();
4953 foreach (sx; *a)
4955 statements.push(toStatement(sx));
4957 result = new CompoundStatement(d.loc, statements);
4960 else if (s.isStaticAssert() ||
4961 s.isImport())
4963 /* Ignore as they are not Statements
4966 else
4968 .error(Loc.initial, "internal compiler error: cannot mixin %s `%s`\n", s.kind(), s.toChars());
4969 result = new ErrorStatement();
4972 return result;
4976 Marks all occurring ThrowStatements as internalThrows.
4977 This is intended to be called from a DebugStatement as it allows
4978 to mark all its nodes as nothrow.
4980 Params:
4981 s = AST Node to traverse
4983 private void debugThrowWalker(Statement s)
4986 extern(C++) final class DebugWalker : SemanticTimeTransitiveVisitor
4988 alias visit = SemanticTimeTransitiveVisitor.visit;
4989 public:
4991 override void visit(ThrowStatement s)
4993 s.internalThrow = true;
4996 override void visit(CallExp s)
4998 s.inDebugStatement = true;
5002 scope walker = new DebugWalker();
5003 s.accept(walker);
5006 /***********************************************************
5007 * Evaluate and print a `pragma(msg, args)`
5009 * Params:
5010 * loc = location for error messages
5011 * sc = scope for argument interpretation
5012 * args = expressions to print
5013 * Returns:
5014 * `true` on success
5016 bool pragmaMsgSemantic(Loc loc, Scope* sc, Expressions* args)
5018 if (!args)
5019 return true;
5020 foreach (arg; *args)
5022 sc = sc.startCTFE();
5023 auto e = arg.expressionSemantic(sc);
5024 e = resolveProperties(sc, e);
5025 sc = sc.endCTFE();
5027 // pragma(msg) is allowed to contain types as well as expressions
5028 e = ctfeInterpretForPragmaMsg(e);
5029 if (e.op == EXP.error)
5031 errorSupplemental(loc, "while evaluating `pragma(msg, %s)`", arg.toChars());
5032 return false;
5034 if (auto se = e.toStringExp())
5036 const slice = se.toUTF8(sc).peekString();
5037 fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr);
5039 else
5040 fprintf(stderr, "%s", e.toChars());
5042 fprintf(stderr, "\n");
5043 return true;
5046 /***********************************************************
5047 * Evaluate `pragma(startAddress, func)` and store the resolved symbol in `args`
5049 * Params:
5050 * loc = location for error messages
5051 * sc = scope for argument interpretation
5052 * args = pragma arguments
5053 * Returns:
5054 * `true` on success
5056 bool pragmaStartAddressSemantic(Loc loc, Scope* sc, Expressions* args)
5058 if (!args || args.length != 1)
5060 .error(loc, "function name expected for start address");
5061 return false;
5063 else
5065 /* https://issues.dlang.org/show_bug.cgi?id=11980
5066 * resolveProperties and ctfeInterpret call are not necessary.
5068 Expression e = (*args)[0];
5069 sc = sc.startCTFE();
5070 e = e.expressionSemantic(sc);
5071 // e = resolveProperties(sc, e);
5072 sc = sc.endCTFE();
5074 // e = e.ctfeInterpret();
5075 (*args)[0] = e;
5076 Dsymbol sa = getDsymbol(e);
5077 if (!sa || !sa.isFuncDeclaration())
5079 .error(loc, "function name expected for start address, not `%s`", e.toChars());
5080 return false;
5083 return true;
5086 /************************************
5087 * Check for skipped variable declarations.
5088 * Params:
5089 * ss = statement to check
5090 * Returns:
5091 * true if error
5093 private bool checkLabel(SwitchStatement ss)
5096 * Checks the scope of a label for existing variable declaration.
5097 * Params:
5098 * vd = last variable declared before this case/default label
5099 * Returns: `true` if the variables declared in this label would be skipped.
5101 bool checkVar(VarDeclaration vd)
5103 for (auto v = vd; v && v != ss.lastVar; v = v.lastVar)
5105 if (v.isDataseg() || (v.storage_class & (STC.manifest | STC.temp) && vd.ident != Id.withSym) || v._init.isVoidInitializer())
5106 continue;
5107 if (vd.ident == Id.withSym)
5108 error(ss.loc, "`switch` skips declaration of `with` temporary");
5109 else
5110 error(ss.loc, "`switch` skips declaration of variable `%s`", v.toPrettyChars());
5111 errorSupplemental(v.loc, "declared here");
5112 return true;
5114 return false;
5117 enum error = true;
5119 if (ss.sdefault && checkVar(ss.sdefault.lastVar))
5120 return !error; // return error once fully deprecated
5122 foreach (scase; *ss.cases)
5124 if (scase && checkVar(scase.lastVar))
5125 return !error; // return error once fully deprecated
5127 return !error;
5131 /**************
5132 * Check for skipped variable declarations.
5133 * Params:
5134 * gs = statement to check
5135 * Returns: true for error
5137 bool checkLabel(GotoStatement gs)
5139 if (!gs.label.statement)
5140 return true; // error should have been issued for this already
5142 if (gs.label.statement.os != gs.os)
5144 if (gs.os && gs.os.tok == TOK.onScopeFailure && !gs.label.statement.os)
5146 // Jump out from scope(failure) block is allowed.
5148 else
5150 if (gs.label.statement.os)
5151 error(gs.loc, "cannot `goto` in to `%s` block", Token.toChars(gs.label.statement.os.tok));
5152 else
5153 error(gs.loc, "cannot `goto` out of `%s` block", Token.toChars(gs.os.tok));
5154 return true;
5158 if (gs.label.statement.tf != gs.tf)
5160 error(gs.loc, "cannot `goto` in or out of `finally` block");
5161 return true;
5164 if (gs.label.statement.inCtfeBlock && !gs.inCtfeBlock)
5166 error(gs.loc, "cannot `goto` into `if (__ctfe)` block");
5167 return true;
5170 Statement stbnext;
5171 for (auto stb = gs.tryBody; stb != gs.label.statement.tryBody; stb = stbnext)
5173 if (!stb)
5175 error(gs.loc, "cannot `goto` into `try` block");
5176 return true;
5178 if (auto stf = stb.isTryFinallyStatement())
5179 stbnext = stf.tryBody;
5180 else if (auto stc = stb.isTryCatchStatement())
5181 stbnext = stc.tryBody;
5182 else
5183 assert(0);
5186 VarDeclaration vd = gs.label.statement.lastVar;
5187 if (!vd || vd.isDataseg() || (vd.storage_class & STC.manifest))
5188 return false;
5190 VarDeclaration last = gs.lastVar;
5191 while (last && last != vd)
5192 last = last.lastVar;
5193 if (last == vd)
5195 // All good, the label's scope has no variables
5197 else if (vd.storage_class & STC.exptemp)
5199 // Lifetime ends at end of expression, so no issue with skipping the statement
5201 else
5203 if (vd.ident == Id.withSym)
5204 error(gs.loc, "`goto` skips declaration of `with` temporary");
5205 else
5206 error(gs.loc, "`goto` skips declaration of variable `%s`", vd.toPrettyChars());
5207 errorSupplemental(vd.loc, "declared here");
5208 return true;
5210 return false;