d: Merge upstream dmd, druntime f1a045928e
[official-gcc.git] / gcc / d / dmd / statementsem.d
blobfcc606b6dd58249e5b483ed4b9ca9dd333be91bb
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.optimize;
59 import dmd.opover;
60 import dmd.parse;
61 import dmd.common.outbuffer;
62 import dmd.root.string;
63 import dmd.semantic2;
64 import dmd.sideeffect;
65 import dmd.statement;
66 import dmd.staticassert;
67 import dmd.target;
68 import dmd.tokens;
69 import dmd.typesem;
70 import dmd.visitor;
72 version (DMDLIB)
74 version = CallbackAPI;
77 /*****************************************
78 * CTFE requires FuncDeclaration::labtab for the interpretation.
79 * So fixing the label name inside in/out contracts is necessary
80 * for the uniqueness in labtab.
81 * Params:
82 * sc = context
83 * ident = statement label name to be adjusted
84 * Returns:
85 * adjusted label name
87 private Identifier fixupLabelName(Scope* sc, Identifier ident)
89 uint flags = (sc.flags & SCOPE.contract);
90 const id = ident.toString();
91 if (flags && flags != SCOPE.invariant_ &&
92 !(id.length >= 2 && id[0] == '_' && id[1] == '_')) // does not start with "__"
94 OutBuffer buf;
95 buf.writestring(flags == SCOPE.require ? "__in_" : "__out_");
96 buf.writestring(ident.toString());
98 ident = Identifier.idPool(buf[]);
100 return ident;
103 /*******************************************
104 * Check to see if statement is the innermost labeled statement.
105 * Params:
106 * sc = context
107 * statement = Statement to check
108 * Returns:
109 * if `true`, then the `LabelStatement`, otherwise `null`
111 private LabelStatement checkLabeledLoop(Scope* sc, Statement statement) @safe
113 if (sc.slabel && sc.slabel.statement == statement)
115 return sc.slabel;
117 return null;
120 /***********************************************************
121 * Check an assignment is used as a condition.
122 * Intended to be use before the `semantic` call on `e`.
123 * Params:
124 * e = condition expression which is not yet run semantic analysis.
125 * Returns:
126 * `e` or ErrorExp.
128 private Expression checkAssignmentAsCondition(Expression e, Scope* sc)
130 if (sc.flags & SCOPE.Cfile)
131 return e;
132 auto ec = lastComma(e);
133 if (ec.op == EXP.assign)
135 error(ec.loc, "assignment cannot be used as a condition, perhaps `==` was meant?");
136 return ErrorExp.get();
138 return e;
141 // Performs semantic analysis in Statement AST nodes
142 extern(C++) Statement statementSemantic(Statement s, Scope* sc)
144 import dmd.compiler;
146 version (CallbackAPI)
147 Compiler.onStatementSemanticStart(s, sc);
149 Statement result = statementSemanticVisit(s, sc);
151 version (CallbackAPI)
152 Compiler.onStatementSemanticDone(s, sc);
154 return result;
157 package (dmd)
158 Statement statementSemanticVisit(Statement s, Scope* sc)
160 Statement result;
162 void setError()
164 result = new ErrorStatement();
167 void visitDefaultCase(Statement s)
169 result = s;
172 void visitError(ErrorStatement s)
174 result = s;
177 void visitPeel(PeelStatement s)
179 /* "peel" off this wrapper, and don't run semantic()
180 * on the result.
182 result = s.s;
185 void visitExp(ExpStatement s)
187 /* https://dlang.org/spec/statement.html#expression-statement
190 if (!s.exp)
192 result = s;
193 return;
195 //printf("ExpStatement::semantic() %s\n", exp.toChars());
197 // Allow CommaExp in ExpStatement because return isn't used
198 CommaExp.allow(s.exp);
200 s.exp = s.exp.expressionSemantic(sc);
201 s.exp = resolveProperties(sc, s.exp);
202 s.exp = s.exp.addDtorHook(sc);
203 if (checkNonAssignmentArrayOp(s.exp))
204 s.exp = ErrorExp.get();
205 if (auto f = isFuncAddress(s.exp))
207 if (f.checkForwardRef(s.exp.loc))
208 s.exp = ErrorExp.get();
210 if (checkMustUse(s.exp, sc))
211 s.exp = ErrorExp.get();
212 if (!(sc.flags & SCOPE.Cfile) && discardValue(s.exp))
213 s.exp = ErrorExp.get();
215 s.exp = s.exp.optimize(WANTvalue);
216 s.exp = checkGC(sc, s.exp);
217 if (s.exp.op == EXP.error)
218 return setError();
219 result = s;
222 void visitDtorExp(DtorExpStatement s)
224 visitExp(s);
227 void visitMixin(MixinStatement cs)
229 /* https://dlang.org/spec/statement.html#mixin-statement
232 //printf("MixinStatement::semantic() %s\n", exp.toChars());
233 Statements* a = cs.flatten(sc);
234 if (!a)
235 return;
236 Statement s = new CompoundStatement(cs.loc, a);
237 result = s.statementSemantic(sc);
240 void visitCompound(CompoundStatement cs)
242 //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc);
243 version (none)
245 foreach (i, s; cs.statements)
247 if (s)
248 printf("[%d]: %s", i, s.toChars());
252 for (size_t i = 0; i < cs.statements.length;)
254 Statement s = (*cs.statements)[i];
255 if (!s)
257 ++i;
258 continue;
261 if (auto flt = s.flatten(sc))
263 cs.statements.remove(i);
264 cs.statements.insert(i, flt);
265 continue;
267 s = s.statementSemantic(sc);
268 (*cs.statements)[i] = s;
269 if (!s)
271 /* Remove NULL statements from the list.
273 cs.statements.remove(i);
274 continue;
276 if (s.isErrorStatement())
278 result = s; // propagate error up the AST
279 ++i;
280 continue; // look for errors in rest of statements
283 // expand tuple variables in order to attach destruction/exception logic
284 if (auto es = s.isExpStatement())
286 if (es.exp && es.exp.isDeclarationExp())
288 auto de = es.exp.isDeclarationExp();
289 auto vd = de.declaration.isVarDeclaration();
290 if (vd && vd.aliasTuple && vd.aliasTuple.objects.length)
292 auto j = i;
293 cs.statements.insert(i, vd.aliasTuple.objects.length - 1, null);
294 vd.aliasTuple.foreachVar((v) { (*cs.statements)[j++] = toStatement(v); });
295 s = (*cs.statements)[i];
300 Statement sentry;
301 Statement sexception;
302 Statement sfinally;
304 (*cs.statements)[i] = s.scopeCode(sc, sentry, sexception, sfinally);
305 if (sentry)
307 sentry = sentry.statementSemantic(sc);
308 cs.statements.insert(i, sentry);
309 i++;
311 if (sexception)
312 sexception = sexception.statementSemantic(sc);
313 if (sexception)
315 /* Returns: true if statements[] are empty statements
317 static bool isEmpty(const Statement[] statements)
319 foreach (s; statements)
321 if (const cs = s.isCompoundStatement())
323 if (!isEmpty((*cs.statements)[]))
324 return false;
326 else
327 return false;
329 return true;
332 if (!sfinally && isEmpty((*cs.statements)[i + 1 .. cs.statements.length]))
335 else
337 /* Rewrite:
338 * s; s1; s2;
339 * As:
340 * s;
341 * try { s1; s2; }
342 * catch (Throwable __o)
343 * { sexception; throw __o; }
345 auto a = new Statements();
346 a.pushSlice((*cs.statements)[i + 1 .. cs.statements.length]);
347 cs.statements.setDim(i + 1);
349 Statement _body = new CompoundStatement(Loc.initial, a);
350 _body = new ScopeStatement(Loc.initial, _body, Loc.initial);
352 Identifier id = Identifier.generateId("__o");
354 Statement handler = new PeelStatement(sexception);
355 if (sexception.blockExit(sc.func, null) & BE.fallthru)
357 auto ts = new ThrowStatement(Loc.initial, new IdentifierExp(Loc.initial, id));
358 ts.internalThrow = true;
359 handler = new CompoundStatement(Loc.initial, handler, ts);
362 auto catches = new Catches();
363 auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
364 ctch.internalCatch = true;
365 catches.push(ctch);
367 Statement st = new TryCatchStatement(Loc.initial, _body, catches);
368 if (sfinally)
369 st = new TryFinallyStatement(Loc.initial, st, sfinally);
370 st = st.statementSemantic(sc);
372 cs.statements.push(st);
373 break;
376 else if (sfinally)
378 if (0 && i + 1 == cs.statements.length)
380 cs.statements.push(sfinally);
382 else
384 /* Rewrite:
385 * s; s1; s2;
386 * As:
387 * s; try { s1; s2; } finally { sfinally; }
389 auto a = new Statements();
390 a.pushSlice((*cs.statements)[i + 1 .. cs.statements.length]);
391 cs.statements.setDim(i + 1);
393 auto _body = new CompoundStatement(Loc.initial, a);
394 Statement stf = new TryFinallyStatement(Loc.initial, _body, sfinally);
395 stf = stf.statementSemantic(sc);
396 cs.statements.push(stf);
397 break;
400 i++;
403 /* Flatten them in place
405 void flattenStatements(ref Statements statements)
407 for (size_t i = 0; i < statements.length;)
409 if (auto s = statements[i])
411 if (auto flt = s.flatten(sc))
413 statements.remove(i);
414 statements.insert(i, flt);
415 continue;
418 ++i;
422 /* https://issues.dlang.org/show_bug.cgi?id=11653
423 * 'semantic' may return another CompoundStatement
424 * (eg. CaseRangeStatement), so flatten it here.
426 flattenStatements(*cs.statements);
428 foreach (s; *cs.statements)
430 if (!s)
431 continue;
433 if (auto se = s.isErrorStatement())
435 result = se;
436 return;
440 if (cs.statements.length == 1)
442 result = (*cs.statements)[0];
443 return;
445 result = cs;
448 void visitUnrolledLoop(UnrolledLoopStatement uls)
450 //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc);
451 Scope* scd = sc.push();
452 scd.sbreak = uls;
453 scd.scontinue = uls;
455 Statement serror = null;
456 foreach (i, ref s; *uls.statements)
458 if (s)
460 //printf("[%d]: %s\n", i, s.toChars());
461 s = s.statementSemantic(scd);
462 if (s && !serror)
463 serror = s.isErrorStatement();
467 scd.pop();
468 result = serror ? serror : uls;
471 void visitScope(ScopeStatement ss)
473 //printf("ScopeStatement::semantic(sc = %p)\n", sc);
474 if (!ss.statement)
476 result = ss;
477 return;
480 ScopeDsymbol sym = new ScopeDsymbol();
481 sym.parent = sc.scopesym;
482 sym.endlinnum = ss.endloc.linnum;
483 sc = sc.push(sym);
485 Statements* a = ss.statement.flatten(sc);
486 if (a)
488 ss.statement = new CompoundStatement(ss.loc, a);
491 ss.statement = ss.statement.statementSemantic(sc);
492 if (ss.statement)
494 if (ss.statement.isErrorStatement())
496 sc.pop();
497 result = ss.statement;
498 return;
501 Statement sentry;
502 Statement sexception;
503 Statement sfinally;
504 ss.statement = ss.statement.scopeCode(sc, sentry, sexception, sfinally);
505 assert(!sentry);
506 assert(!sexception);
507 if (sfinally)
509 //printf("adding sfinally\n");
510 sfinally = sfinally.statementSemantic(sc);
511 ss.statement = new CompoundStatement(ss.loc, ss.statement, sfinally);
514 sc.pop();
515 result = ss;
518 void visitForwarding(ForwardingStatement ss)
520 assert(ss.sym);
521 for (Scope* csc = sc; !ss.sym.parent; csc = csc.enclosing)
523 assert(csc);
524 ss.sym.parent = csc.scopesym;
526 sc = sc.push(ss.sym);
527 sc.sbreak = ss;
528 sc.scontinue = ss;
529 ss.statement = ss.statement.statementSemantic(sc);
530 sc = sc.pop();
531 result = ss.statement;
534 void visitWhile(WhileStatement ws)
536 /* Rewrite as a for(;condition;) loop
537 * https://dlang.org/spec/statement.html#while-statement
539 Expression cond = ws.condition;
540 Statement _body = ws._body;
541 if (ws.param)
544 * If the while loop is of form `while(auto a = exp) { loop_body }`,
545 * rewrite to:
547 * while(true)
548 * if (auto a = exp)
549 * { loop_body }
550 * else
551 * { break; }
553 _body = new IfStatement(ws.loc, ws.param, ws.condition, ws._body, new BreakStatement(ws.loc, null), ws.endloc);
554 cond = IntegerExp.createBool(true);
556 Statement s = new ForStatement(ws.loc, null, cond, null, _body, ws.endloc);
557 s = s.statementSemantic(sc);
558 result = s;
561 void visitDo(DoStatement ds)
563 /* https://dlang.org/spec/statement.html#do-statement
565 const inLoopSave = sc.inLoop;
566 sc.inLoop = true;
567 if (ds._body)
568 ds._body = ds._body.semanticScope(sc, ds, ds, null);
569 sc.inLoop = inLoopSave;
571 if (ds.condition.op == EXP.dotIdentifier)
572 (cast(DotIdExp)ds.condition).noderef = true;
574 // check in syntax level
575 ds.condition = checkAssignmentAsCondition(ds.condition, sc);
577 ds.condition = ds.condition.expressionSemantic(sc);
578 ds.condition = resolveProperties(sc, ds.condition);
579 if (checkNonAssignmentArrayOp(ds.condition))
580 ds.condition = ErrorExp.get();
581 ds.condition = ds.condition.optimize(WANTvalue);
582 ds.condition = checkGC(sc, ds.condition);
584 ds.condition = ds.condition.toBoolean(sc);
586 if (ds.condition.op == EXP.error)
587 return setError();
588 if (ds._body && ds._body.isErrorStatement())
590 result = ds._body;
591 return;
594 result = ds;
597 void visitFor(ForStatement fs)
599 /* https://dlang.org/spec/statement.html#for-statement
601 //printf("ForStatement::semantic %s\n", fs.toChars());
603 if (fs._init)
605 /* Rewrite:
606 * for (auto v1 = i1, v2 = i2; condition; increment) { ... }
607 * to:
608 * { auto v1 = i1, v2 = i2; for (; condition; increment) { ... } }
609 * then lowered to:
610 * auto v1 = i1;
611 * try {
612 * auto v2 = i2;
613 * try {
614 * for (; condition; increment) { ... }
615 * } finally { v2.~this(); }
616 * } finally { v1.~this(); }
618 auto ainit = new Statements();
619 ainit.push(fs._init);
620 fs._init = null;
621 ainit.push(fs);
622 Statement s = new CompoundStatement(fs.loc, ainit);
623 s = new ScopeStatement(fs.loc, s, fs.endloc);
624 s = s.statementSemantic(sc);
625 if (!s.isErrorStatement())
627 if (LabelStatement ls = checkLabeledLoop(sc, fs))
628 ls.gotoTarget = fs;
629 fs.relatedLabeled = s;
631 result = s;
632 return;
634 assert(fs._init is null);
636 auto sym = new ScopeDsymbol();
637 sym.parent = sc.scopesym;
638 sym.endlinnum = fs.endloc.linnum;
639 sc = sc.push(sym);
640 sc.inLoop = true;
642 if (fs.condition)
644 if (fs.condition.op == EXP.dotIdentifier)
645 (cast(DotIdExp)fs.condition).noderef = true;
647 // check in syntax level
648 fs.condition = checkAssignmentAsCondition(fs.condition, sc);
650 fs.condition = fs.condition.expressionSemantic(sc);
651 fs.condition = resolveProperties(sc, fs.condition);
652 if (checkNonAssignmentArrayOp(fs.condition))
653 fs.condition = ErrorExp.get();
654 fs.condition = fs.condition.optimize(WANTvalue);
655 fs.condition = checkGC(sc, fs.condition);
657 fs.condition = fs.condition.toBoolean(sc);
659 if (fs.increment)
661 CommaExp.allow(fs.increment);
662 fs.increment = fs.increment.expressionSemantic(sc);
663 fs.increment = resolveProperties(sc, fs.increment);
664 // @@@DEPRECATED_2.112@@@
665 // remove gagging and deprecation() to turn deprecation into an error when
666 // deprecation cycle is over
667 const olderrors = global.startGagging();
668 discardValue(fs.increment);
669 if (global.endGagging(olderrors))
670 deprecation(fs.increment.loc, "`%s` has no effect", fs.increment.toChars());
671 if (checkNonAssignmentArrayOp(fs.increment))
672 fs.increment = ErrorExp.get();
673 fs.increment = fs.increment.optimize(WANTvalue);
674 fs.increment = checkGC(sc, fs.increment);
677 sc.sbreak = fs;
678 sc.scontinue = fs;
679 if (fs._body)
680 fs._body = fs._body.semanticNoScope(sc);
682 sc.pop();
684 if (fs.condition && fs.condition.op == EXP.error ||
685 fs.increment && fs.increment.op == EXP.error ||
686 fs._body && fs._body.isErrorStatement())
687 return setError();
688 result = fs;
691 void visitForeach(ForeachStatement fs)
693 /* https://dlang.org/spec/statement.html#foreach-statement
696 //printf("ForeachStatement::semantic() %p\n", fs);
698 /******
699 * Issue error if any of the ForeachTypes were not supplied and could not be inferred.
700 * Returns:
701 * true if error issued
703 static bool checkForArgTypes(ForeachStatement fs)
705 bool result = false;
706 foreach (p; *fs.parameters)
708 if (!p.type)
710 error(fs.loc, "cannot infer type for `foreach` variable `%s`, perhaps set it explicitly", p.ident.toChars());
711 p.type = Type.terror;
712 result = true;
715 return result;
718 const loc = fs.loc;
719 const dim = fs.parameters.length;
721 fs.func = sc.func;
722 if (fs.func.fes)
723 fs.func = fs.func.fes.func;
725 VarDeclaration vinit = null;
726 fs.aggr = fs.aggr.expressionSemantic(sc);
727 fs.aggr = resolveProperties(sc, fs.aggr);
728 fs.aggr = fs.aggr.optimize(WANTvalue);
729 if (fs.aggr.op == EXP.error)
730 return setError();
731 Expression oaggr = fs.aggr; // remember original for error messages
732 if (fs.aggr.type && fs.aggr.type.toBasetype().ty == Tstruct &&
733 (cast(TypeStruct)(fs.aggr.type.toBasetype())).sym.dtor &&
734 !fs.aggr.isTypeExp() && !fs.aggr.isLvalue())
736 // https://issues.dlang.org/show_bug.cgi?id=14653
737 // Extend the life of rvalue aggregate till the end of foreach.
738 vinit = copyToTemp(STC.rvalue, "__aggr", fs.aggr);
739 vinit.endlinnum = fs.endloc.linnum;
740 vinit.dsymbolSemantic(sc);
741 fs.aggr = new VarExp(fs.aggr.loc, vinit);
744 /* If aggregate is a vector type, add the .array to make it a static array
746 if (fs.aggr.type)
747 if (auto tv = fs.aggr.type.toBasetype().isTypeVector())
749 auto vae = new VectorArrayExp(fs.aggr.loc, fs.aggr);
750 vae.type = tv.basetype;
751 fs.aggr = vae;
754 Dsymbol sapply = null; // the inferred opApply() or front() function
755 if (!inferForeachAggregate(sc, fs.op == TOK.foreach_, fs.aggr, sapply))
757 assert(oaggr.type);
759 error(fs.loc, "invalid `%s` aggregate `%s` of type `%s`",
760 Token.toChars(fs.op), oaggr.toChars(), oaggr.type.toPrettyChars());
762 if (auto ad = isAggregate(fs.aggr.type))
764 if (fs.op == TOK.foreach_reverse_)
766 fs.loc.errorSupplemental("`foreach_reverse` works with bidirectional ranges"~
767 " (implementing `back` and `popBack`), aggregates implementing" ~
768 " `opApplyReverse`, or the result of an aggregate's `.tupleof` property");
769 fs.loc.errorSupplemental("https://dlang.org/phobos/std_range_primitives.html#isBidirectionalRange");
771 else
773 fs.loc.errorSupplemental("`foreach` works with input ranges"~
774 " (implementing `front` and `popFront`), aggregates implementing" ~
775 " `opApply`, or the result of an aggregate's `.tupleof` property");
776 fs.loc.errorSupplemental("https://dlang.org/phobos/std_range_primitives.html#isInputRange");
780 return setError();
783 Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
785 /* Check for inference errors
787 if (!inferApplyArgTypes(fs, sc, sapply))
790 Try and extract the parameter count of the opApply callback function, e.g.:
791 int opApply(int delegate(int, float)) => 2 args
793 bool foundMismatch = false;
794 size_t foreachParamCount = 0;
795 if (sapplyOld)
797 if (FuncDeclaration fd = sapplyOld.isFuncDeclaration())
799 auto fparameters = fd.getParameterList();
801 if (fparameters.length == 1)
803 // first param should be the callback function
804 Parameter fparam = fparameters[0];
805 if ((fparam.type.ty == Tpointer ||
806 fparam.type.ty == Tdelegate) &&
807 fparam.type.nextOf().ty == Tfunction)
809 TypeFunction tf = cast(TypeFunction)fparam.type.nextOf();
810 foreachParamCount = tf.parameterList.length;
811 foundMismatch = true;
817 //printf("dim = %d, parameters.length = %d\n", dim, parameters.length);
818 if (foundMismatch && dim != foreachParamCount)
820 const(char)* plural = foreachParamCount > 1 ? "s" : "";
821 error(fs.loc, "cannot infer argument types, expected %llu argument%s, not %llu",
822 cast(ulong) foreachParamCount, plural, cast(ulong) dim);
824 else
825 error(fs.loc, "cannot uniquely infer `foreach` argument types");
827 return setError();
830 Type tab = fs.aggr.type.toBasetype();
832 if (tab.ty == Ttuple) // don't generate new scope for tuple loops
834 Statement s = makeTupleForeach(sc, false, false, fs, null, false).statement;
835 if (vinit)
836 s = new CompoundStatement(loc, new ExpStatement(loc, vinit), s);
837 result = s.statementSemantic(sc);
838 return;
841 auto sym = new ScopeDsymbol();
842 sym.parent = sc.scopesym;
843 sym.endlinnum = fs.endloc.linnum;
844 auto sc2 = sc.push(sym);
845 sc2.inLoop = true;
847 foreach (Parameter p; *fs.parameters)
849 if (p.storageClass & STC.manifest)
851 error(fs.loc, "cannot declare `enum` loop variables for non-unrolled foreach");
853 if (p.storageClass & STC.alias_)
855 error(fs.loc, "cannot declare `alias` loop variables for non-unrolled foreach");
859 void retError()
861 sc2.pop();
862 result = new ErrorStatement();
865 void rangeError()
867 error(fs.loc, "cannot infer argument types");
868 return retError();
871 void retStmt(Statement s)
873 if (!s)
874 return retError();
875 s = s.statementSemantic(sc2);
876 sc2.pop();
877 result = s;
880 Type tn = null;
881 Type tnv = null;
882 Statement apply()
884 if (checkForArgTypes(fs))
885 return null;
887 TypeFunction tfld = null;
888 if (sapply)
890 if (auto fdapply = sapply.isFuncDeclaration())
892 assert(fdapply.type && fdapply.type.isTypeFunction());
893 tfld = fdapply.type.typeSemantic(loc, sc2).isTypeFunction();
894 goto Lget;
896 else if (tab.isTypeDelegate())
898 tfld = tab.nextOf().isTypeFunction();
899 Lget:
900 //printf("tfld = %s\n", tfld.toChars());
901 if (tfld.parameterList.parameters.length == 1)
903 Parameter p = tfld.parameterList[0];
904 if (p.type && p.type.isTypeDelegate())
906 auto t = p.type.typeSemantic(loc, sc2);
907 assert(t.ty == Tdelegate);
908 tfld = t.nextOf().isTypeFunction();
914 FuncExp flde = foreachBodyToFunction(sc2, fs, tfld);
915 if (!flde)
916 return null;
918 // Resolve any forward referenced goto's
919 foreach (ScopeStatement ss; *fs.gotos)
921 GotoStatement gs = ss.statement.isGotoStatement();
922 if (!gs.label.statement)
924 // 'Promote' it to this scope, and replace with a return
925 fs.cases.push(gs);
926 ss.statement = new ReturnStatement(Loc.initial, new IntegerExp(fs.cases.length + 1));
930 Expression e = null;
931 if (vinit)
933 e = new DeclarationExp(loc, vinit);
934 e = e.expressionSemantic(sc2);
935 if (e.op == EXP.error)
936 return null;
939 Expression ec;
940 switch (tab.ty)
942 case Tarray:
943 case Tsarray: ec = applyArray (fs, flde, tab, sc2, tn, tnv); break;
944 case Tdelegate: ec = applyDelegate (fs, flde, tab, sc2); break;
945 case Taarray: ec = applyAssocArray(fs, flde, tab); break;
946 default: ec = applyOpApply (fs, flde, tab, sc2, sapply); break;
948 if (!ec)
949 return null;
951 e = Expression.combine(e, ec);
952 return loopReturn(e, fs.cases, loc);
955 switch (tab.ty)
957 case Tarray:
958 case Tsarray:
960 if (checkForArgTypes(fs))
961 return retError();
963 if (dim < 1 || dim > 2)
965 error(fs.loc, "only one or two arguments for array `foreach`");
966 return retError();
969 // Finish semantic on all foreach parameter types.
970 foreach (i; 0 .. dim)
972 Parameter p = (*fs.parameters)[i];
973 p.type = p.type.typeSemantic(loc, sc2);
974 p.type = p.type.addStorageClass(p.storageClass);
977 tn = tab.nextOf().toBasetype();
979 if (dim == 2)
981 Type tindex = (*fs.parameters)[0].type;
982 if (!tindex.isintegral())
984 error(fs.loc, "foreach: key cannot be of non-integral type `%s`", tindex.toChars());
985 return retError();
987 /* What cases to deprecate implicit conversions for:
988 * 1. foreach aggregate is a dynamic array
989 * 2. foreach body is lowered to _aApply (see special case below).
991 Type tv = (*fs.parameters)[1].type.toBasetype();
992 if ((tab.isTypeDArray() ||
993 (tn.ty != tv.ty && tn.ty.isSomeChar && tv.ty.isSomeChar)) &&
994 !Type.tsize_t.implicitConvTo(tindex))
996 deprecation(fs.loc, "foreach: loop index implicitly converted from `size_t` to `%s`",
997 tindex.toChars());
1001 /* Look for special case of parsing char types out of char type
1002 * array.
1004 if (tn.ty.isSomeChar)
1006 int i = (dim == 1) ? 0 : 1; // index of value
1007 Parameter p = (*fs.parameters)[i];
1008 tnv = p.type.toBasetype();
1009 if (tnv.ty != tn.ty && tnv.ty.isSomeChar)
1011 if (p.storageClass & STC.ref_)
1013 error(fs.loc, "`foreach`: value of UTF conversion cannot be `ref`");
1014 return retError();
1016 if (dim == 2)
1018 p = (*fs.parameters)[0];
1019 if (p.storageClass & STC.ref_)
1021 error(fs.loc, "`foreach`: key cannot be `ref`");
1022 return retError();
1025 return retStmt(apply());
1029 // Declare the key
1030 if (dim == 2)
1032 Parameter p = (*fs.parameters)[0];
1033 fs.key = new VarDeclaration(loc, p.type.mutableOf(), Identifier.generateId("__key"), null);
1034 fs.key.storage_class |= STC.temp | STC.foreach_;
1035 if (fs.key.isReference())
1036 fs.key.storage_class |= STC.nodtor;
1038 if (p.storageClass & STC.ref_)
1040 if (fs.key.type.constConv(p.type) == MATCH.nomatch)
1042 error(fs.loc, "key type mismatch, `%s` to `ref %s`",
1043 fs.key.type.toChars(), p.type.toChars());
1044 return retError();
1047 if (auto ta = tab.isTypeSArray())
1049 IntRange dimrange = getIntRange(ta.dim);
1050 // https://issues.dlang.org/show_bug.cgi?id=12504
1051 dimrange.imax = SignExtendedNumber(dimrange.imax.value-1);
1052 if (!IntRange.fromType(fs.key.type).contains(dimrange))
1054 error(fs.loc, "index type `%s` cannot cover index range 0..%llu",
1055 p.type.toChars(), ta.dim.toInteger());
1056 return retError();
1058 fs.key.range = new IntRange(SignExtendedNumber(0), dimrange.imax);
1061 // Now declare the value
1063 Parameter p = (*fs.parameters)[dim - 1];
1064 fs.value = new VarDeclaration(loc, p.type, p.ident, null);
1065 fs.value.storage_class |= STC.foreach_;
1066 fs.value.storage_class |= p.storageClass & (STC.scope_ | STC.IOR | STC.TYPECTOR);
1067 if (fs.value.isReference())
1069 fs.value.storage_class |= STC.nodtor;
1071 if (fs.aggr.checkModifiable(sc2, ModifyFlags.noError) == Modifiable.initialization)
1072 fs.value.setInCtorOnly = true;
1074 Type t = tab.nextOf();
1075 if (t.constConv(p.type) == MATCH.nomatch)
1077 error(fs.loc, "argument type mismatch, `%s` to `ref %s`",
1078 t.toChars(), p.type.toChars());
1079 return retError();
1084 /* Convert to a ForStatement
1085 * foreach (key, value; a) body =>
1086 * for (T[] tmp = a[], size_t key; key < tmp.length; ++key)
1087 * { T value = tmp[k]; body }
1089 * foreach_reverse (key, value; a) body =>
1090 * for (T[] tmp = a[], size_t key = tmp.length; key--; )
1091 * { T value = tmp[k]; body }
1093 auto id = Identifier.generateId("__r");
1094 auto ie = new ExpInitializer(loc, new SliceExp(loc, fs.aggr, null, null));
1095 const valueIsRef = (*fs.parameters)[$ - 1].isReference();
1096 VarDeclaration tmp;
1097 if (fs.aggr.isArrayLiteralExp() && !valueIsRef)
1099 auto ale = fs.aggr.isArrayLiteralExp();
1100 size_t edim = ale.elements ? ale.elements.length : 0;
1101 auto telem = (*fs.parameters)[dim - 1].type;
1103 // https://issues.dlang.org/show_bug.cgi?id=12936
1104 // if telem has been specified explicitly,
1105 // converting array literal elements to telem might make it @nogc.
1106 fs.aggr = fs.aggr.implicitCastTo(sc, telem.sarrayOf(edim));
1107 if (fs.aggr.op == EXP.error)
1108 return retError();
1110 // for (T[edim] tmp = a, ...)
1111 tmp = new VarDeclaration(loc, fs.aggr.type, id, ie);
1113 else
1115 tmp = new VarDeclaration(loc, tab.nextOf().arrayOf(), id, ie);
1116 if (!valueIsRef)
1117 tmp.storage_class |= STC.scope_;
1119 tmp.storage_class |= STC.temp;
1121 Expression tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id.length);
1123 if (!fs.key)
1125 Identifier idkey = Identifier.generateId("__key");
1126 fs.key = new VarDeclaration(loc, Type.tsize_t, idkey, null);
1127 fs.key.storage_class |= STC.temp;
1129 else if (fs.key.type.ty != Type.tsize_t.ty)
1131 tmp_length = new CastExp(loc, tmp_length, fs.key.type);
1133 if (fs.op == TOK.foreach_reverse_)
1134 fs.key._init = new ExpInitializer(loc, tmp_length);
1135 else
1136 fs.key._init = new ExpInitializer(loc, new IntegerExp(loc, 0, fs.key.type));
1138 auto cs = new Statements();
1139 if (vinit)
1140 cs.push(new ExpStatement(loc, vinit));
1141 cs.push(new ExpStatement(loc, tmp));
1142 cs.push(new ExpStatement(loc, fs.key));
1143 Statement forinit = new CompoundDeclarationStatement(loc, cs);
1145 Expression cond;
1146 if (fs.op == TOK.foreach_reverse_)
1148 // key--
1149 cond = new PostExp(EXP.minusMinus, loc, new VarExp(loc, fs.key));
1151 else
1153 // key < tmp.length
1154 cond = new CmpExp(EXP.lessThan, loc, new VarExp(loc, fs.key), tmp_length);
1157 Expression increment = null;
1158 if (fs.op == TOK.foreach_)
1160 // key += 1
1161 increment = new AddAssignExp(loc, new VarExp(loc, fs.key), new IntegerExp(loc, 1, fs.key.type));
1164 // T value = tmp[key];
1165 IndexExp indexExp = new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, fs.key));
1166 indexExp.indexIsInBounds = true; // disabling bounds checking in foreach statements.
1167 fs.value._init = new ExpInitializer(loc, indexExp);
1168 Statement ds = new ExpStatement(loc, fs.value);
1170 if (dim == 2)
1172 Parameter p = (*fs.parameters)[0];
1173 if ((p.storageClass & STC.ref_) && p.type.equals(fs.key.type))
1175 fs.key.range = null;
1176 auto v = new AliasDeclaration(loc, p.ident, fs.key);
1177 fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1179 else
1181 auto ei = new ExpInitializer(loc, new IdentifierExp(loc, fs.key.ident));
1182 auto v = new VarDeclaration(loc, p.type, p.ident, ei);
1183 v.storage_class |= STC.foreach_ | (p.storageClass & STC.ref_);
1184 fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1185 if (fs.key.range && !p.type.isMutable())
1187 /* Limit the range of the key to the specified range
1189 v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
1193 fs._body = new CompoundStatement(loc, ds, fs._body);
1195 Statement s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
1196 if (auto ls = checkLabeledLoop(sc, fs)) // https://issues.dlang.org/show_bug.cgi?id=15450
1197 // don't use sc2
1198 ls.gotoTarget = s;
1199 return retStmt(s);
1201 case Taarray:
1202 if (fs.op == TOK.foreach_reverse_)
1203 warning(fs.loc, "cannot use `foreach_reverse` with an associative array");
1204 if (checkForArgTypes(fs))
1205 return retError();
1207 if (dim < 1 || dim > 2)
1209 error(fs.loc, "only one or two arguments for associative array `foreach`");
1210 return retError();
1212 return retStmt(apply());
1214 case Tclass:
1215 case Tstruct:
1216 /* Prefer using opApply, if it exists
1218 if (sapply)
1219 return retStmt(apply());
1221 /* Look for range iteration, i.e. the properties
1222 * .empty, .popFront, .popBack, .front and .back
1223 * foreach (e; aggr) { ... }
1224 * translates to:
1225 * for (auto __r = aggr[]; !__r.empty; __r.popFront()) {
1226 * auto e = __r.front;
1227 * ...
1230 auto ad = (tab.ty == Tclass) ?
1231 cast(AggregateDeclaration)tab.isTypeClass().sym :
1232 cast(AggregateDeclaration)tab.isTypeStruct().sym;
1233 Identifier idfront;
1234 Identifier idpopFront;
1235 if (fs.op == TOK.foreach_)
1237 idfront = Id.Ffront;
1238 idpopFront = Id.FpopFront;
1240 else
1242 idfront = Id.Fback;
1243 idpopFront = Id.FpopBack;
1245 auto sfront = ad.search(Loc.initial, idfront);
1246 if (!sfront)
1247 return retStmt(apply());
1249 /* Generate a temporary __r and initialize it with the aggregate.
1251 VarDeclaration r;
1252 Statement _init;
1253 if (vinit && fs.aggr.isVarExp() && fs.aggr.isVarExp().var == vinit)
1255 r = vinit;
1256 _init = new ExpStatement(loc, vinit);
1258 else
1260 r = copyToTemp(0, "__r", fs.aggr);
1261 r.dsymbolSemantic(sc);
1262 _init = new ExpStatement(loc, r);
1263 if (vinit)
1264 _init = new CompoundStatement(loc, new ExpStatement(loc, vinit), _init);
1267 // !__r.empty
1268 Expression e = new VarExp(loc, r);
1269 e = new DotIdExp(loc, e, Id.Fempty);
1270 Expression condition = new NotExp(loc, e);
1272 // __r.idpopFront()
1273 e = new VarExp(loc, r);
1274 Expression increment = new CallExp(loc, new DotIdExp(loc, e, idpopFront));
1276 /* Declaration statement for e:
1277 * auto e = __r.idfront;
1279 e = new VarExp(loc, r);
1280 Expression einit = new DotIdExp(loc, e, idfront);
1281 Statement makeargs, forbody;
1282 bool ignoreRef = false; // If a range returns a non-ref front we ignore ref on foreach
1284 Type tfront;
1285 if (auto fd = sfront.isFuncDeclaration())
1287 if (!fd.functionSemantic())
1288 return rangeError();
1289 tfront = fd.type;
1291 else if (auto td = sfront.isTemplateDeclaration())
1293 if (auto f = resolveFuncCall(loc, sc, td, null, tab, ArgumentList(), FuncResolveFlag.quiet))
1294 tfront = f.type;
1296 else if (auto d = sfront.toAlias().isDeclaration())
1298 tfront = d.type;
1300 if (!tfront || tfront.ty == Terror)
1301 return rangeError();
1302 if (auto ftt = tfront.toBasetype().isTypeFunction())
1304 tfront = tfront.toBasetype().nextOf();
1305 if (!ftt.isref)
1307 // .front() does not return a ref. We ignore ref on foreach arg.
1308 // see https://issues.dlang.org/show_bug.cgi?id=11934
1309 if (tfront.needsDestruction()) ignoreRef = true;
1312 if (tfront.ty == Tvoid)
1314 error(fs.loc, "`%s.front` is `void` and has no value", oaggr.toChars());
1315 return retError();
1318 if (dim == 1)
1320 auto p = (*fs.parameters)[0];
1321 auto ve = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, einit));
1322 ve.storage_class |= STC.foreach_;
1323 ve.storage_class |= p.storageClass & (STC.scope_ | STC.IOR | STC.TYPECTOR);
1325 if (ignoreRef)
1326 ve.storage_class &= ~STC.ref_;
1328 makeargs = new ExpStatement(loc, ve);
1330 else
1332 auto vd = copyToTemp(STC.ref_, "__front", einit);
1333 vd.dsymbolSemantic(sc);
1334 makeargs = new ExpStatement(loc, vd);
1336 // Resolve inout qualifier of front type
1337 tfront = tfront.substWildTo(tab.mod);
1339 Expression ve = new VarExp(loc, vd);
1340 ve.type = tfront;
1342 auto exps = new Expressions();
1343 exps.push(ve);
1344 int pos = 0;
1345 while (exps.length < dim)
1347 pos = expandAliasThisTuples(exps, pos);
1348 if (pos == -1)
1349 break;
1351 if (exps.length != dim)
1353 const(char)* plural = exps.length > 1 ? "s" : "";
1354 error(fs.loc, "cannot infer argument types, expected %llu argument%s, not %llu",
1355 cast(ulong) exps.length, plural, cast(ulong) dim);
1356 return retError();
1359 foreach (i; 0 .. dim)
1361 auto p = (*fs.parameters)[i];
1362 auto exp = (*exps)[i];
1363 version (none)
1365 printf("[%lu] p = %s %s, exp = %s %s\n", i,
1366 p.type ? p.type.toChars() : "?", p.ident.toChars(),
1367 exp.type.toChars(), exp.toChars());
1369 if (!p.type)
1370 p.type = exp.type;
1372 auto sc = p.storageClass;
1373 if (ignoreRef) sc &= ~STC.ref_;
1374 p.type = p.type.addStorageClass(sc).typeSemantic(loc, sc2);
1375 if (!exp.implicitConvTo(p.type))
1377 error(fs.loc, "cannot implicilty convert range element of type `%s` to variable `%s` of type `%s`",
1378 exp.type.toChars(), p.toChars(), p.type.toChars());
1379 return retError();
1382 auto var = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, exp));
1383 var.storage_class |= STC.ctfe | STC.ref_ | STC.foreach_;
1384 makeargs = new CompoundStatement(loc, makeargs, new ExpStatement(loc, var));
1388 forbody = new CompoundStatement(loc, makeargs, fs._body);
1390 Statement s = new ForStatement(loc, _init, condition, increment, forbody, fs.endloc);
1391 if (auto ls = checkLabeledLoop(sc, fs))
1392 ls.gotoTarget = s;
1394 version (none)
1396 printf("init: %s\n", toChars(_init));
1397 printf("condition: %s\n", condition.toChars());
1398 printf("increment: %s\n", increment.toChars());
1399 printf("body: %s\n", forbody.toChars());
1401 return retStmt(s);
1403 case Tdelegate:
1404 if (fs.op == TOK.foreach_reverse_)
1405 deprecation(fs.loc, "cannot use `foreach_reverse` with a delegate");
1406 return retStmt(apply());
1407 case Terror:
1408 return retError();
1409 default:
1410 error(fs.loc, "`foreach`: `%s` is not an aggregate type", fs.aggr.type.toChars());
1411 return retError();
1415 void visitForeachRange(ForeachRangeStatement fs)
1417 /* https://dlang.org/spec/statement.html#foreach-range-statement
1420 //printf("ForeachRangeStatement::semantic() %p\n", fs);
1421 auto loc = fs.loc;
1422 fs.lwr = fs.lwr.expressionSemantic(sc);
1423 fs.lwr = resolveProperties(sc, fs.lwr);
1424 fs.lwr = fs.lwr.optimize(WANTvalue);
1425 if (!fs.lwr.type)
1427 error(fs.loc, "invalid range lower bound `%s`", fs.lwr.toChars());
1428 return setError();
1431 fs.upr = fs.upr.expressionSemantic(sc);
1432 fs.upr = resolveProperties(sc, fs.upr);
1433 fs.upr = fs.upr.optimize(WANTvalue);
1434 if (!fs.upr.type)
1436 error(fs.loc, "invalid range upper bound `%s`", fs.upr.toChars());
1437 return setError();
1440 if (fs.prm.type)
1442 fs.prm.type = fs.prm.type.typeSemantic(loc, sc);
1443 fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
1444 fs.lwr = fs.lwr.implicitCastTo(sc, fs.prm.type);
1446 if (fs.upr.implicitConvTo(fs.prm.type) || (fs.prm.storageClass & STC.ref_))
1448 fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
1450 else
1452 // See if upr-1 fits in prm.type
1453 Expression limit = new MinExp(loc, fs.upr, IntegerExp.literal!1);
1454 limit = limit.expressionSemantic(sc);
1455 limit = limit.optimize(WANTvalue);
1456 if (!limit.implicitConvTo(fs.prm.type))
1458 fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
1462 else
1464 /* Must infer types from lwr and upr
1466 Type tlwr = fs.lwr.type.toBasetype();
1467 if (tlwr.ty == Tstruct || tlwr.ty == Tclass)
1469 /* Just picking the first really isn't good enough.
1471 fs.prm.type = fs.lwr.type;
1473 else if (fs.lwr.type == fs.upr.type)
1475 /* Same logic as CondExp ?lwr:upr
1477 fs.prm.type = fs.lwr.type;
1479 else
1481 scope AddExp ea = new AddExp(loc, fs.lwr, fs.upr);
1482 if (typeCombine(ea, sc))
1483 return setError();
1484 fs.prm.type = ea.type;
1485 fs.lwr = ea.e1;
1486 fs.upr = ea.e2;
1488 fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
1490 if (fs.prm.type.ty == Terror || fs.lwr.op == EXP.error || fs.upr.op == EXP.error)
1492 return setError();
1495 /* Convert to a for loop:
1496 * foreach (key; lwr .. upr) =>
1497 * for (auto key = lwr, auto tmp = upr; key < tmp; ++key)
1499 * foreach_reverse (key; lwr .. upr) =>
1500 * for (auto tmp = lwr, auto key = upr; key-- > tmp;)
1502 auto ie = new ExpInitializer(loc, (fs.op == TOK.foreach_) ? fs.lwr : fs.upr);
1503 fs.key = new VarDeclaration(loc, fs.upr.type.mutableOf(), Identifier.generateId("__key"), ie);
1504 fs.key.storage_class |= STC.temp;
1505 SignExtendedNumber lower = getIntRange(fs.lwr).imin;
1506 SignExtendedNumber upper = getIntRange(fs.upr).imax;
1507 if (lower <= upper)
1509 fs.key.range = new IntRange(lower, upper);
1512 Identifier id = Identifier.generateId("__limit");
1513 ie = new ExpInitializer(loc, (fs.op == TOK.foreach_) ? fs.upr : fs.lwr);
1514 auto tmp = new VarDeclaration(loc, fs.upr.type, id, ie);
1515 tmp.storage_class |= STC.temp;
1517 auto cs = new Statements();
1518 // Keep order of evaluation as lwr, then upr
1519 if (fs.op == TOK.foreach_)
1521 cs.push(new ExpStatement(loc, fs.key));
1522 cs.push(new ExpStatement(loc, tmp));
1524 else
1526 cs.push(new ExpStatement(loc, tmp));
1527 cs.push(new ExpStatement(loc, fs.key));
1529 Statement forinit = new CompoundDeclarationStatement(loc, cs);
1531 Expression cond;
1532 if (fs.op == TOK.foreach_reverse_)
1534 cond = new PostExp(EXP.minusMinus, loc, new VarExp(loc, fs.key));
1535 if (fs.prm.type.isscalar())
1537 // key-- > tmp
1538 cond = new CmpExp(EXP.greaterThan, loc, cond, new VarExp(loc, tmp));
1540 else
1542 // key-- != tmp
1543 cond = new EqualExp(EXP.notEqual, loc, cond, new VarExp(loc, tmp));
1546 else
1548 if (fs.prm.type.isscalar())
1550 // key < tmp
1551 cond = new CmpExp(EXP.lessThan, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
1553 else
1555 // key != tmp
1556 cond = new EqualExp(EXP.notEqual, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
1560 Expression increment = null;
1561 if (fs.op == TOK.foreach_)
1563 // key += 1
1564 //increment = new AddAssignExp(loc, new VarExp(loc, fs.key), IntegerExp.literal!1);
1565 increment = new PreExp(EXP.prePlusPlus, loc, new VarExp(loc, fs.key));
1567 if ((fs.prm.storageClass & STC.ref_) && fs.prm.type.equals(fs.key.type))
1569 fs.key.range = null;
1570 auto v = new AliasDeclaration(loc, fs.prm.ident, fs.key);
1571 fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1573 else
1575 ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs.key), fs.prm.type));
1576 auto v = new VarDeclaration(loc, fs.prm.type, fs.prm.ident, ie);
1577 v.storage_class |= STC.temp | STC.foreach_ | (fs.prm.storageClass & STC.ref_);
1578 fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
1579 if (fs.key.range && !fs.prm.type.isMutable())
1581 /* Limit the range of the key to the specified range
1583 v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
1586 if (fs.prm.storageClass & STC.ref_)
1588 if (fs.key.type.constConv(fs.prm.type) == MATCH.nomatch)
1590 error(fs.loc, "argument type mismatch, `%s` to `ref %s`", fs.key.type.toChars(), fs.prm.type.toChars());
1591 return setError();
1595 auto s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
1596 if (LabelStatement ls = checkLabeledLoop(sc, fs))
1597 ls.gotoTarget = s;
1598 result = s.statementSemantic(sc);
1601 void visitIf(IfStatement ifs)
1603 /* https://dlang.org/spec/statement.html#IfStatement
1606 // check in syntax level
1607 ifs.condition = checkAssignmentAsCondition(ifs.condition, sc);
1609 auto sym = new ScopeDsymbol();
1610 sym.parent = sc.scopesym;
1611 sym.endlinnum = ifs.endloc.linnum;
1612 Scope* scd = sc.push(sym);
1613 if (ifs.prm)
1615 /* Declare prm, which we will set to be the
1616 * result of condition.
1618 auto ei = new ExpInitializer(ifs.loc, ifs.condition);
1619 ifs.match = new VarDeclaration(ifs.loc, ifs.prm.type, ifs.prm.ident, ei);
1620 ifs.match.parent = scd.func;
1621 ifs.match.storage_class |= ifs.prm.storageClass;
1622 ifs.match.dsymbolSemantic(scd);
1624 auto de = new DeclarationExp(ifs.loc, ifs.match);
1625 auto ve = new VarExp(ifs.loc, ifs.match);
1626 ifs.condition = new CommaExp(ifs.loc, de, ve);
1627 ifs.condition = ifs.condition.expressionSemantic(scd);
1629 if (ifs.match.edtor)
1631 Statement sdtor = new DtorExpStatement(ifs.loc, ifs.match.edtor, ifs.match);
1632 sdtor = new ScopeGuardStatement(ifs.loc, TOK.onScopeExit, sdtor);
1633 ifs.ifbody = new CompoundStatement(ifs.loc, sdtor, ifs.ifbody);
1634 ifs.match.storage_class |= STC.nodtor;
1636 // the destructor is always called
1637 // whether the 'ifbody' is executed or not
1638 Statement sdtor2 = new DtorExpStatement(ifs.loc, ifs.match.edtor, ifs.match);
1639 if (ifs.elsebody)
1640 ifs.elsebody = new CompoundStatement(ifs.loc, sdtor2, ifs.elsebody);
1641 else
1642 ifs.elsebody = sdtor2;
1645 else
1647 if (ifs.condition.op == EXP.dotIdentifier)
1648 (cast(DotIdExp)ifs.condition).noderef = true;
1650 ifs.condition = ifs.condition.expressionSemantic(scd);
1651 ifs.condition = resolveProperties(scd, ifs.condition);
1652 ifs.condition = ifs.condition.addDtorHook(scd);
1654 if (checkNonAssignmentArrayOp(ifs.condition))
1655 ifs.condition = ErrorExp.get();
1657 // Convert to boolean after declaring prm so this works:
1658 // if (S prm = S()) {}
1659 // where S is a struct that defines opCast!bool.
1660 ifs.condition = ifs.condition.toBoolean(scd);
1662 // If we can short-circuit evaluate the if statement, don't do the
1663 // semantic analysis of the skipped code.
1664 // This feature allows a limited form of conditional compilation.
1665 ifs.condition = ifs.condition.optimize(WANTvalue);
1667 // checkGC after optimizing the condition so that
1668 // compile time constants are reduced.
1669 ifs.condition = checkGC(scd, ifs.condition);
1671 // Save 'root' of two branches (then and else) at the point where it forks
1672 CtorFlow ctorflow_root = scd.ctorflow.clone();
1674 /* Rewrite `if (!__ctfe) A else B` as `if (__ctfe) B else A`
1676 NotExp notExp;
1677 if (ifs.elsebody &&
1678 (notExp = ifs.condition.isNotExp()) !is null &&
1679 notExp.e1.isVarExp() &&
1680 notExp.e1.isVarExp().var.ident == Id.ctfe)
1682 ifs.condition = notExp.e1;
1683 auto sbody = ifs.ifbody;
1684 ifs.ifbody = ifs.elsebody;
1685 ifs.elsebody = sbody;
1688 /* Detect `if (__ctfe)`
1690 if (ifs.isIfCtfeBlock())
1692 Scope* scd2 = scd.push();
1693 scd2.flags |= SCOPE.ctfeBlock;
1694 ifs.ifbody = ifs.ifbody.semanticNoScope(scd2);
1695 scd2.pop();
1697 else
1698 ifs.ifbody = ifs.ifbody.semanticNoScope(scd);
1699 scd.pop();
1701 CtorFlow ctorflow_then = sc.ctorflow; // move flow results
1702 sc.ctorflow = ctorflow_root; // reset flow analysis back to root
1703 if (ifs.elsebody)
1704 ifs.elsebody = ifs.elsebody.semanticScope(sc, null, null, null);
1706 // Merge 'then' results into 'else' results
1707 sc.merge(ifs.loc, ctorflow_then);
1709 ctorflow_then.freeFieldinit(); // free extra copy of the data
1711 if (ifs.condition.op == EXP.error ||
1712 (ifs.ifbody && ifs.ifbody.isErrorStatement()) ||
1713 (ifs.elsebody && ifs.elsebody.isErrorStatement()))
1715 return setError();
1717 result = ifs;
1720 void visitConditional(ConditionalStatement cs)
1722 //printf("ConditionalStatement::semantic()\n");
1724 // If we can short-circuit evaluate the if statement, don't do the
1725 // semantic analysis of the skipped code.
1726 // This feature allows a limited form of conditional compilation.
1727 if (cs.condition.include(sc))
1729 DebugCondition dc = cs.condition.isDebugCondition();
1730 if (dc)
1732 sc = sc.push();
1733 sc.flags |= SCOPE.debug_;
1734 cs.ifbody = cs.ifbody.statementSemantic(sc);
1735 sc.pop();
1737 else
1738 cs.ifbody = cs.ifbody.statementSemantic(sc);
1739 result = cs.ifbody;
1741 else
1743 if (cs.elsebody)
1744 cs.elsebody = cs.elsebody.statementSemantic(sc);
1745 result = cs.elsebody;
1749 void visitPragma(PragmaStatement ps)
1751 /* https://dlang.org/spec/statement.html#pragma-statement
1753 // Should be merged with PragmaDeclaration
1755 //printf("PragmaStatement::semantic() %s\n", ps.toChars());
1756 //printf("body = %p\n", ps._body);
1757 if (ps.ident == Id.msg)
1759 if (!pragmaMsgSemantic(ps.loc, sc, ps.args))
1760 return setError();
1762 else if (ps.ident == Id.lib)
1764 version (all)
1766 /* Should this be allowed?
1768 error(ps.loc, "`pragma(lib)` not allowed as statement");
1769 return setError();
1771 else
1773 if (!ps.args || ps.args.length != 1)
1775 error(ps.loc, "`string` expected for library name");
1776 return setError();
1778 else
1780 auto se = semanticString(sc, (*ps.args)[0], "library name");
1781 if (!se)
1782 return setError();
1784 if (global.params.v.verbose)
1786 message("library %.*s", cast(int)se.len, se.string);
1791 else if (ps.ident == Id.linkerDirective)
1793 /* Should this be allowed?
1795 error(ps.loc, "`pragma(linkerDirective)` not allowed as statement");
1796 return setError();
1798 else if (ps.ident == Id.startaddress)
1800 if (!pragmaStartAddressSemantic(ps.loc, sc, ps.args))
1801 return setError();
1803 else if (ps.ident == Id.Pinline)
1805 if (auto fd = sc.func)
1807 fd.inlining = evalPragmaInline(ps.loc, sc, ps.args);
1809 else
1811 error(ps.loc, "`pragma(inline)` is not inside a function");
1812 return setError();
1815 else if (ps.ident == Id.mangle)
1817 auto es = ps._body ? ps._body.isExpStatement() : null;
1818 auto de = es ? es.exp.isDeclarationExp() : null;
1819 if (!de)
1821 error(ps.loc, "`pragma(mangle)` must be attached to a declaration");
1822 return setError();
1824 const se = ps.args && (*ps.args).length == 1 ? semanticString(sc, (*ps.args)[0], "pragma mangle argument") : null;
1825 if (!se)
1827 error(ps.loc, "`pragma(mangle)` takes a single argument that must be a string literal");
1828 return setError();
1830 const cnt = setMangleOverride(de.declaration, cast(const(char)[])se.peekData());
1831 if (cnt != 1)
1832 assert(0);
1834 else if (!global.params.ignoreUnsupportedPragmas)
1836 error(ps.loc, "unrecognized `pragma(%s)`", ps.ident.toChars());
1837 return setError();
1840 if (ps._body)
1842 if (ps.ident == Id.msg || ps.ident == Id.startaddress)
1844 error(ps.loc, "`pragma(%s)` is missing a terminating `;`", ps.ident.toChars());
1845 return setError();
1847 ps._body = ps._body.statementSemantic(sc);
1849 result = ps._body;
1852 void visitStaticAssert(StaticAssertStatement s)
1854 s.sa.semantic2(sc);
1855 if (s.sa.errors)
1856 return setError();
1859 void visitSwitch(SwitchStatement ss)
1861 /* https://dlang.org/spec/statement.html#switch-statement
1864 //printf("SwitchStatement::semantic(%p)\n", ss);
1865 ss.tryBody = sc.tryBody;
1866 ss.tf = sc.tf;
1867 if (ss.cases)
1869 result = ss; // already run
1870 return;
1873 if (ss.param)
1876 * If the switch statement is of form `switch(auto a = exp) { body }`,
1877 * rewrite to the following inside it's own scope:
1879 * auto a = exp
1880 * switch(a)
1881 * { body }
1883 auto statements = new Statements();
1884 auto vardecl = new VarDeclaration(ss.param.loc,
1885 ss.param.type,
1886 ss.param.ident,
1887 new ExpInitializer(ss.condition.loc, ss.condition),
1888 ss.param.storageClass);
1890 statements.push(new ExpStatement(ss.param.loc, vardecl));
1892 ss.condition = new VarExp(ss.param.loc, vardecl, false);
1893 ss.param = null;
1895 statements.push(ss);
1897 Statement s = new CompoundStatement(ss.loc, statements);
1898 s = new ScopeStatement(ss.loc, s, ss.endloc);
1899 s = s.statementSemantic(sc);
1900 result = s;
1901 return;
1904 bool conditionError = false;
1905 ss.condition = ss.condition.expressionSemantic(sc);
1906 ss.condition = resolveProperties(sc, ss.condition);
1908 Type att = null;
1909 TypeEnum te = null;
1910 while (!ss.condition.isErrorExp())
1912 // preserve enum type for final switches
1913 if (ss.condition.type.ty == Tenum)
1914 te = cast(TypeEnum)ss.condition.type;
1915 if (ss.condition.type.isString())
1917 // If it's not an array, cast it to one
1918 if (ss.condition.type.ty != Tarray)
1920 ss.condition = ss.condition.implicitCastTo(sc, ss.condition.type.nextOf().arrayOf());
1922 ss.condition.type = ss.condition.type.constOf();
1923 break;
1925 ss.condition = integralPromotions(ss.condition, sc);
1926 if (!ss.condition.isErrorExp() && ss.condition.type.isintegral())
1927 break;
1929 auto ad = isAggregate(ss.condition.type);
1930 if (ad && ad.aliasthis && !isRecursiveAliasThis(att, ss.condition.type))
1932 if (auto e = resolveAliasThis(sc, ss.condition, true))
1934 ss.condition = e;
1935 continue;
1939 if (!ss.condition.isErrorExp())
1941 error(ss.loc, "`%s` must be of integral or string type, it is a `%s`",
1942 ss.condition.toChars(), ss.condition.type.toChars());
1943 conditionError = true;
1944 break;
1947 if (checkNonAssignmentArrayOp(ss.condition))
1948 ss.condition = ErrorExp.get();
1949 ss.condition = ss.condition.optimize(WANTvalue);
1950 ss.condition = checkGC(sc, ss.condition);
1951 if (ss.condition.op == EXP.error)
1952 conditionError = true;
1954 bool needswitcherror = false;
1956 ss.lastVar = sc.lastVar;
1958 sc = sc.push();
1959 sc.sbreak = ss;
1960 sc.sw = ss;
1962 ss.cases = new CaseStatements();
1963 const inLoopSave = sc.inLoop;
1964 sc.inLoop = true; // BUG: should use Scope::mergeCallSuper() for each case instead
1965 ss._body = ss._body.statementSemantic(sc);
1966 sc.inLoop = inLoopSave;
1968 if (conditionError || (ss._body && ss._body.isErrorStatement()))
1970 sc.pop();
1971 return setError();
1974 // Resolve any goto case's with exp
1975 Lgotocase:
1976 foreach (gcs; ss.gotoCases)
1978 if (!gcs.exp)
1980 error(gcs.loc, "no `case` statement following `goto case;`");
1981 sc.pop();
1982 return setError();
1985 for (Scope* scx = sc; scx; scx = scx.enclosing)
1987 if (!scx.sw)
1988 continue;
1989 foreach (cs; *scx.sw.cases)
1991 if (cs.exp.equals(gcs.exp))
1993 gcs.cs = cs;
1994 continue Lgotocase;
1998 error(gcs.loc, "`case %s` not found", gcs.exp.toChars());
1999 sc.pop();
2000 return setError();
2003 if (ss.isFinal)
2005 Type t = ss.condition.type;
2006 Dsymbol ds;
2007 EnumDeclaration ed = null;
2008 if (t && ((ds = t.toDsymbol(sc)) !is null))
2009 ed = ds.isEnumDeclaration(); // typedef'ed enum
2010 if (!ed && te && ((ds = te.toDsymbol(sc)) !is null))
2011 ed = ds.isEnumDeclaration();
2012 if (ed && ss.cases.length < ed.members.length)
2014 int missingMembers = 0;
2015 const maxShown = global.params.v.errorSupplementCount();
2016 Lmembers:
2017 foreach (es; *ed.members)
2019 EnumMember em = es.isEnumMember();
2020 if (em)
2022 foreach (cs; *ss.cases)
2024 if (cs.exp.equals(em.value) || (!cs.exp.type.isString() &&
2025 !em.value.type.isString() && cs.exp.toInteger() == em.value.toInteger()))
2026 continue Lmembers;
2028 if (missingMembers == 0)
2029 error(ss.loc, "missing cases for `enum` members in `final switch`:");
2031 if (missingMembers < maxShown)
2032 errorSupplemental(ss.loc, "`%s`", em.toChars());
2033 missingMembers++;
2036 if (missingMembers > 0)
2038 if (missingMembers > maxShown)
2039 errorSupplemental(ss.loc, "... (%d more, -v to show) ...", missingMembers - maxShown);
2040 sc.pop();
2041 return setError();
2044 else
2045 needswitcherror = true;
2048 ss.hasDefault = sc.sw.sdefault ||
2049 !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe);
2050 if (!ss.hasDefault)
2052 if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !(sc.flags & SCOPE.Cfile))
2053 error(ss.loc, "`switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`");
2055 // Generate runtime error if the default is hit
2056 auto a = new Statements();
2057 CompoundStatement cs;
2058 Statement s;
2060 if (sc.flags & SCOPE.Cfile)
2062 s = new BreakStatement(ss.loc, null); // default for C is `default: break;`
2064 else if (!sc.needsCodegen())
2066 // something for the interpreter to deal with
2067 s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0));
2069 else if (global.params.useSwitchError == CHECKENABLE.on &&
2070 global.params.checkAction != CHECKACTION.halt)
2072 if (global.params.checkAction == CHECKACTION.C)
2074 /* Rewrite as an assert(0) and let e2ir generate
2075 * the call to the C assert failure function
2077 s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0));
2079 else
2081 if (!verifyHookExist(ss.loc, *sc, Id.__switch_error, "generating assert messages"))
2082 return setError();
2084 Expression sl = new IdentifierExp(ss.loc, Id.empty);
2085 sl = new DotIdExp(ss.loc, sl, Id.object);
2086 sl = new DotIdExp(ss.loc, sl, Id.__switch_error);
2088 Expressions* args = new Expressions(2);
2089 (*args)[0] = new StringExp(ss.loc, ss.loc.filename.toDString());
2090 (*args)[1] = new IntegerExp(ss.loc.linnum);
2092 sl = new CallExp(ss.loc, sl, args);
2093 sl = sl.expressionSemantic(sc);
2095 s = new SwitchErrorStatement(ss.loc, sl);
2098 else
2099 s = new ExpStatement(ss.loc, new HaltExp(ss.loc));
2101 a.reserve(2);
2102 sc.sw.sdefault = new DefaultStatement(ss.loc, s);
2103 a.push(ss._body);
2104 if (ss._body.blockExit(sc.func, null) & BE.fallthru)
2105 a.push(new BreakStatement(Loc.initial, null));
2106 a.push(sc.sw.sdefault);
2107 cs = new CompoundStatement(ss.loc, a);
2108 ss._body = cs;
2111 if (!(sc.flags & SCOPE.Cfile) && ss.checkLabel())
2113 sc.pop();
2114 return setError();
2118 if (!(ss.condition.type.isString() && sc.needsCodegen()))
2120 sc.pop();
2121 result = ss;
2122 return;
2125 // Transform a switch with string labels into a switch with integer labels.
2127 // The integer value of each case corresponds to the index of each label
2128 // string in the sorted array of label strings.
2130 // The value of the integer condition is obtained by calling the druntime template
2131 // switch(object.__switch(cond, options...)) {0: {...}, 1: {...}, ...}
2133 // We sort a copy of the array of labels because we want to do a binary search in object.__switch,
2134 // without modifying the order of the case blocks here in the compiler.
2136 if (!verifyHookExist(ss.loc, *sc, Id.__switch, "switch cases on strings"))
2137 return setError();
2139 size_t numcases = 0;
2140 if (ss.cases)
2141 numcases = ss.cases.length;
2143 for (size_t i = 0; i < numcases; i++)
2145 CaseStatement cs = (*ss.cases)[i];
2146 cs.index = cast(int)i;
2149 // Make a copy of all the cases so that qsort doesn't scramble the actual
2150 // data we pass to codegen (the order of the cases in the switch).
2151 CaseStatements *csCopy = (*ss.cases).copy();
2153 if (numcases)
2155 static int sort_compare(in CaseStatement* x, in CaseStatement* y) @trusted
2157 auto se1 = x.exp.isStringExp();
2158 auto se2 = y.exp.isStringExp();
2159 return (se1 && se2) ? se1.compare(se2) : 0;
2161 // Sort cases for efficient lookup
2162 csCopy.sort!sort_compare;
2165 // The actual lowering
2166 auto arguments = new Expressions();
2167 arguments.push(ss.condition);
2169 auto compileTimeArgs = new Objects();
2171 // The type & label no.
2172 compileTimeArgs.push(new TypeExp(ss.loc, ss.condition.type.nextOf()));
2174 // The switch labels
2175 foreach (caseString; *csCopy)
2177 compileTimeArgs.push(caseString.exp);
2180 Expression sl = new IdentifierExp(ss.loc, Id.empty);
2181 sl = new DotIdExp(ss.loc, sl, Id.object);
2182 sl = new DotTemplateInstanceExp(ss.loc, sl, Id.__switch, compileTimeArgs);
2184 sl = new CallExp(ss.loc, sl, arguments);
2185 sl = sl.expressionSemantic(sc);
2186 ss.condition = sl;
2188 auto i = 0;
2189 foreach (c; *csCopy)
2191 (*ss.cases)[c.index].exp = new IntegerExp(i++);
2194 //printf("%s\n", ss._body.toChars());
2195 ss.statementSemantic(sc);
2197 sc.pop();
2198 result = ss;
2201 void visitCase(CaseStatement cs)
2203 SwitchStatement sw = sc.sw;
2204 bool errors = false;
2206 //printf("CaseStatement::semantic() %s\n", toChars());
2207 sc = sc.startCTFE();
2208 cs.exp = cs.exp.expressionSemantic(sc);
2209 cs.exp = resolveProperties(sc, cs.exp);
2210 sc = sc.endCTFE();
2212 if (sw)
2214 Expression initialExp = cs.exp;
2216 // The switch'ed value has errors and doesn't provide the actual type
2217 // Omit the cast to enable further semantic (exluding the check for matching types)
2218 if (sw.condition.type && !sw.condition.type.isTypeError())
2219 cs.exp = cs.exp.implicitCastTo(sc, sw.condition.type);
2220 cs.exp = cs.exp.optimize(WANTvalue | WANTexpand);
2222 Expression e = cs.exp;
2223 // Remove all the casts the user and/or implicitCastTo may introduce
2224 // otherwise we'd sometimes fail the check below.
2225 while (e.op == EXP.cast_)
2226 e = (cast(CastExp)e).e1;
2228 /* This is where variables are allowed as case expressions.
2230 if (e.op == EXP.variable)
2232 VarExp ve = cast(VarExp)e;
2233 VarDeclaration v = ve.var.isVarDeclaration();
2234 Type t = cs.exp.type.toBasetype();
2235 if (v && (t.isintegral() || t.ty == Tclass))
2237 /* Flag that we need to do special code generation
2238 * for this, i.e. generate a sequence of if-then-else
2240 sw.hasVars = true;
2242 /* TODO check if v can be uninitialized at that point.
2244 if (!v.isConst() && !v.isImmutable())
2246 error(cs.loc, "`case` variables have to be `const` or `immutable`");
2249 if (sw.isFinal)
2251 error(cs.loc, "`case` variables not allowed in `final switch` statements");
2252 errors = true;
2255 /* Find the outermost scope `scx` that set `sw`.
2256 * Then search scope `scx` for a declaration of `v`.
2258 for (Scope* scx = sc; scx; scx = scx.enclosing)
2260 if (scx.enclosing && scx.enclosing.sw == sw)
2261 continue;
2262 assert(scx.sw == sw);
2264 Dsymbol pscopesym;
2265 if (!scx.search(cs.exp.loc, v.ident, pscopesym))
2267 error(cs.loc, "`case` variable `%s` declared at %s cannot be declared in `switch` body",
2268 v.toChars(), v.loc.toChars());
2269 errors = true;
2271 break;
2273 goto L1;
2276 else
2277 cs.exp = cs.exp.ctfeInterpret();
2279 if (StringExp se = cs.exp.toStringExp())
2280 cs.exp = se;
2281 else if (!cs.exp.isIntegerExp() && !cs.exp.isErrorExp())
2283 error(cs.loc, "`case` expression must be a compile-time `string` or an integral constant, not `%s`", cs.exp.toChars());
2284 errors = true;
2288 // // Don't check other cases if this has errors
2289 if (!cs.exp.isErrorExp())
2290 foreach (cs2; *sw.cases)
2292 //printf("comparing '%s' with '%s'\n", exp.toChars(), cs.exp.toChars());
2293 if (cs2.exp.equals(cs.exp))
2295 // https://issues.dlang.org/show_bug.cgi?id=15909
2296 error(cs.loc, "duplicate `case %s` in `switch` statement", initialExp.toChars());
2297 errors = true;
2298 break;
2302 sw.cases.push(cs);
2304 // Resolve any goto case's with no exp to this case statement
2305 for (size_t i = 0; i < sw.gotoCases.length;)
2307 GotoCaseStatement gcs = sw.gotoCases[i];
2308 if (!gcs.exp)
2310 gcs.cs = cs;
2311 sw.gotoCases.remove(i); // remove from array
2312 continue;
2314 i++;
2317 if (sc.sw.tf != sc.tf)
2319 error(cs.loc, "`switch` and `case` are in different `finally` blocks");
2320 errors = true;
2322 if (sc.sw.tryBody != sc.tryBody)
2324 error(cs.loc, "case cannot be in different `try` block level from `switch`");
2325 errors = true;
2328 else
2330 error(cs.loc, "`case` not in `switch` statement");
2331 errors = true;
2334 sc.ctorflow.orCSX(CSX.label);
2335 cs.statement = cs.statement.statementSemantic(sc);
2336 if (cs.statement.isErrorStatement())
2338 result = cs.statement;
2339 return;
2341 if (errors || cs.exp.op == EXP.error)
2342 return setError();
2344 cs.lastVar = sc.lastVar;
2345 result = cs;
2348 void visitCaseRange(CaseRangeStatement crs)
2350 SwitchStatement sw = sc.sw;
2351 if (sw is null)
2353 error(crs.loc, "case range not in `switch` statement");
2354 return setError();
2357 //printf("CaseRangeStatement::semantic() %s\n", toChars());
2358 bool errors = false;
2359 if (sw.isFinal)
2361 error(crs.loc, "case ranges not allowed in `final switch`");
2362 errors = true;
2365 sc = sc.startCTFE();
2366 crs.first = crs.first.expressionSemantic(sc);
2367 crs.first = resolveProperties(sc, crs.first);
2368 sc = sc.endCTFE();
2369 crs.first = crs.first.implicitCastTo(sc, sw.condition.type);
2370 crs.first = crs.first.ctfeInterpret();
2372 sc = sc.startCTFE();
2373 crs.last = crs.last.expressionSemantic(sc);
2374 crs.last = resolveProperties(sc, crs.last);
2375 sc = sc.endCTFE();
2376 crs.last = crs.last.implicitCastTo(sc, sw.condition.type);
2377 crs.last = crs.last.ctfeInterpret();
2379 if (crs.first.op == EXP.error || crs.last.op == EXP.error || errors)
2381 if (crs.statement)
2382 crs.statement.statementSemantic(sc);
2383 return setError();
2386 uinteger_t fval = crs.first.toInteger();
2387 uinteger_t lval = crs.last.toInteger();
2388 if ((crs.first.type.isunsigned() && fval > lval) || (!crs.first.type.isunsigned() && cast(sinteger_t)fval > cast(sinteger_t)lval))
2390 error(crs.loc, "first `case %s` is greater than last `case %s`", crs.first.toChars(), crs.last.toChars());
2391 errors = true;
2392 lval = fval;
2395 if (lval - fval > 256)
2397 error(crs.loc, "had %llu cases which is more than 257 cases in case range", 1 + lval - fval);
2398 errors = true;
2399 lval = fval + 256;
2402 if (errors)
2403 return setError();
2405 /* This works by replacing the CaseRange with an array of Case's.
2407 * case a: .. case b: s;
2408 * =>
2409 * case a:
2410 * [...]
2411 * case b:
2412 * s;
2415 auto statements = new Statements();
2416 for (uinteger_t i = fval; i != lval + 1; i++)
2418 Statement s = crs.statement;
2419 if (i != lval) // if not last case
2420 s = new ExpStatement(crs.loc, cast(Expression)null);
2421 Expression e = new IntegerExp(crs.loc, i, crs.first.type);
2422 Statement cs = new CaseStatement(crs.loc, e, s);
2423 statements.push(cs);
2425 Statement s = new CompoundStatement(crs.loc, statements);
2426 sc.ctorflow.orCSX(CSX.label);
2427 s = s.statementSemantic(sc);
2428 result = s;
2431 void visitDefault(DefaultStatement ds)
2433 //printf("DefaultStatement::semantic()\n");
2434 bool errors = false;
2435 if (sc.sw)
2437 if (sc.sw.sdefault)
2439 error(ds.loc, "`switch` statement already has a default");
2440 errors = true;
2442 sc.sw.sdefault = ds;
2444 if (sc.sw.tf != sc.tf)
2446 error(ds.loc, "`switch` and `default` are in different `finally` blocks");
2447 errors = true;
2449 if (sc.sw.tryBody != sc.tryBody)
2451 error(ds.loc, "default cannot be in different `try` block level from `switch`");
2452 errors = true;
2454 if (sc.sw.isFinal)
2456 error(ds.loc, "`default` statement not allowed in `final switch` statement");
2457 errors = true;
2460 else
2462 error(ds.loc, "`default` not in `switch` statement");
2463 errors = true;
2466 sc.ctorflow.orCSX(CSX.label);
2467 ds.statement = ds.statement.statementSemantic(sc);
2468 if (errors || ds.statement.isErrorStatement())
2469 return setError();
2471 ds.lastVar = sc.lastVar;
2472 result = ds;
2475 void visitGotoDefault(GotoDefaultStatement gds)
2477 /* https://dlang.org/spec/statement.html#goto-statement
2480 gds.sw = sc.sw;
2481 if (!gds.sw)
2483 error(gds.loc, "`goto default` not in `switch` statement");
2484 return setError();
2486 if (gds.sw.isFinal)
2488 error(gds.loc, "`goto default` not allowed in `final switch` statement");
2489 return setError();
2491 result = gds;
2494 void visitGotoCase(GotoCaseStatement gcs)
2496 /* https://dlang.org/spec/statement.html#goto-statement
2499 if (!sc.sw)
2501 error(gcs.loc, "`goto case` not in `switch` statement");
2502 return setError();
2505 if (gcs.exp)
2507 gcs.exp = gcs.exp.expressionSemantic(sc);
2508 gcs.exp = gcs.exp.implicitCastTo(sc, sc.sw.condition.type);
2509 gcs.exp = gcs.exp.optimize(WANTvalue);
2510 if (gcs.exp.op == EXP.error)
2511 return setError();
2514 sc.sw.gotoCases.push(gcs);
2515 result = gcs;
2518 void visitReturn(ReturnStatement rs)
2520 /* https://dlang.org/spec/statement.html#return-statement
2523 //printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars());
2525 FuncDeclaration fd = sc.parent.isFuncDeclaration();
2526 if (fd.fes)
2527 fd = fd.fes.func; // fd is now function enclosing foreach
2529 auto tf = fd.type.isTypeFunction();
2531 if (rs.exp && rs.exp.isVarExp() && rs.exp.isVarExp().var == fd.vresult)
2533 // return vresult;
2534 if (sc.fes)
2536 assert(rs.caseDim == 0);
2537 sc.fes.cases.push(rs);
2538 result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.length + 1));
2539 return;
2541 if (fd.returnLabel)
2543 auto gs = new GotoStatement(rs.loc, Id.returnLabel);
2544 gs.label = fd.returnLabel;
2545 result = gs;
2546 return;
2549 if (!fd.returns)
2550 fd.returns = new ReturnStatements();
2551 fd.returns.push(rs);
2552 result = rs;
2553 return;
2556 Type tret = tf.next;
2557 Type tbret = tret ? tret.toBasetype() : null;
2559 bool inferRef = (tf.isref && (fd.storage_class & STC.auto_));
2560 Expression e0 = null;
2562 bool errors = false;
2563 if (sc.flags & SCOPE.contract)
2565 error(rs.loc, "`return` statements cannot be in contracts");
2566 errors = true;
2568 if (sc.os)
2570 // @@@DEPRECATED_2.112@@@
2571 // Deprecated in 2.100, transform into an error in 2.112
2572 if (sc.os.tok == TOK.onScopeFailure)
2574 deprecation(rs.loc, "`return` statements cannot be in `scope(failure)` bodies.");
2575 deprecationSupplemental(rs.loc, "Use try-catch blocks for this purpose");
2577 else
2579 error(rs.loc, "`return` statements cannot be in `%s` bodies", Token.toChars(sc.os.tok));
2580 errors = true;
2583 if (sc.tf)
2585 error(rs.loc, "`return` statements cannot be in `finally` bodies");
2586 errors = true;
2589 if (fd.isCtorDeclaration())
2591 if (rs.exp)
2593 error(rs.loc, "cannot return expression from constructor");
2594 errors = true;
2597 // Constructors implicitly do:
2598 // return this;
2599 rs.exp = new ThisExp(Loc.initial);
2600 rs.exp.type = tret;
2602 else if (rs.exp)
2604 fd.hasReturnExp |= (fd.hasReturnExp & 1 ? 16 : 1);
2606 FuncLiteralDeclaration fld = fd.isFuncLiteralDeclaration();
2607 if (tret)
2608 rs.exp = inferType(rs.exp, tret);
2609 else if (fld && fld.treq)
2610 rs.exp = inferType(rs.exp, fld.treq.nextOf().nextOf());
2612 rs.exp = rs.exp.expressionSemantic(sc);
2613 rs.exp = rs.exp.arrayFuncConv(sc);
2614 // If we're returning by ref, allow the expression to be `shared`
2615 const returnSharedRef = (tf.isref && (fd.inferRetType || tret.isShared()));
2616 rs.exp.checkSharedAccess(sc, returnSharedRef);
2618 // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
2619 if (rs.exp.isTypeExp())
2620 rs.exp = resolveAliasThis(sc, rs.exp);
2622 rs.exp = resolveProperties(sc, rs.exp);
2623 if (rs.exp.checkType())
2624 rs.exp = ErrorExp.get();
2625 if (auto f = isFuncAddress(rs.exp))
2627 if (fd.inferRetType && f.checkForwardRef(rs.exp.loc))
2628 rs.exp = ErrorExp.get();
2630 if (checkNonAssignmentArrayOp(rs.exp))
2631 rs.exp = ErrorExp.get();
2633 // Extract side-effect part
2634 rs.exp = Expression.extractLast(rs.exp, e0);
2635 if (rs.exp.isCallExp())
2636 rs.exp = valueNoDtor(rs.exp);
2638 /* Void-return function can have void / noreturn typed expression
2639 * on return statement.
2641 auto texp = rs.exp.type;
2642 const convToVoid = texp.ty == Tvoid || texp.isTypeNoreturn();
2644 if (tbret && tbret.ty == Tvoid || convToVoid)
2646 if (!convToVoid)
2648 error(rs.loc, "cannot return non-void from `void` function");
2649 errors = true;
2650 rs.exp = new CastExp(rs.loc, rs.exp, Type.tvoid);
2651 rs.exp = rs.exp.expressionSemantic(sc);
2654 // https://issues.dlang.org/show_bug.cgi?id=23063
2655 rs.exp = checkNoreturnVarAccess(rs.exp);
2657 // @@@DEPRECATED_2.111@@@
2658 const olderrors = global.startGagging();
2659 // uncomment to turn deprecation into an error when
2660 // deprecation cycle is over
2661 if (discardValue(rs.exp))
2663 //errors = true;
2665 if (global.endGagging(olderrors))
2666 deprecation(rs.exp.loc, "`%s` has no effect", rs.exp.toChars());
2668 /* Replace:
2669 * return exp;
2670 * with:
2671 * exp; return;
2673 e0 = Expression.combine(e0, rs.exp);
2674 rs.exp = null;
2676 if (e0)
2678 e0 = e0.optimize(WANTvalue);
2679 e0 = checkGC(sc, e0);
2683 if (rs.exp)
2685 if (fd.inferRetType) // infer return type
2687 if (!tret)
2689 tf.next = rs.exp.type;
2691 else if (!tret.isTypeError() && !rs.exp.type.equals(tret))
2693 int m1 = rs.exp.type.implicitConvTo(tret);
2694 int m2 = tret.implicitConvTo(rs.exp.type);
2695 //printf("exp.type = %s m2<-->m1 tret %s\n", exp.type.toChars(), tret.toChars());
2696 //printf("m1 = %d, m2 = %d\n", m1, m2);
2698 if (m1 && m2)
2701 else if (!m1 && m2)
2702 tf.next = rs.exp.type;
2703 else if (m1 && !m2)
2706 else if (!rs.exp.isErrorExp())
2708 error(rs.loc, "expected return type of `%s`, not `%s`:",
2709 tret.toChars(),
2710 rs.exp.type.toChars());
2711 errorSupplemental((fd.returns) ? (*fd.returns)[0].loc : fd.loc,
2712 "Return type of `%s` inferred here.",
2713 tret.toChars());
2715 errors = true;
2716 tf.next = Type.terror;
2720 tret = tf.next;
2721 tbret = tret.toBasetype();
2724 if (inferRef) // deduce 'auto ref'
2726 /* Determine "refness" of function return:
2727 * if it's an lvalue, return by ref, else return by value
2728 * https://dlang.org/spec/function.html#auto-ref-functions
2731 void turnOffRef(scope void delegate() supplemental)
2733 tf.isref = false; // return by value
2734 tf.isreturn = false; // ignore 'return' attribute, whether explicit or inferred
2735 fd.storage_class &= ~STC.return_;
2737 // If we previously assumed the function could be ref when
2738 // checking for `shared`, make sure we were right
2739 if (global.params.noSharedAccess == FeatureState.enabled && rs.exp.type.isShared())
2741 .error(fd.loc, "%s `%s` function returns `shared` but cannot be inferred `ref`", fd.kind, fd.toPrettyChars);
2742 supplemental();
2746 if (rs.exp.isLvalue())
2748 /* May return by ref
2750 Scope* sc2 = sc.push();
2751 sc2.eSink = global.errorSinkNull;
2752 bool err = checkReturnEscapeRef(sc2, rs.exp, true);
2753 sc2.pop();
2755 if (err)
2756 turnOffRef(() { checkReturnEscapeRef(sc, rs.exp, false); });
2757 else if (!rs.exp.type.constConv(tf.next))
2758 turnOffRef(
2759 () => rs.loc.errorSupplemental("cannot implicitly convert `%s` of type `%s` to `%s`",
2760 rs.exp.toChars(), rs.exp.type.toChars(), tf.next.toChars())
2763 else
2764 turnOffRef(
2765 () => rs.loc.errorSupplemental("return value `%s` is not an lvalue", rs.exp.toChars())
2768 /* The "refness" is determined by all of return statements.
2769 * This means:
2770 * return 3; return x; // ok, x can be a value
2771 * return x; return 3; // ok, x can be a value
2775 else
2777 // Type of the returned expression (if any), might've been moved to e0
2778 auto resType = e0 ? e0.type : Type.tvoid;
2780 // infer return type
2781 if (fd.inferRetType)
2783 // 1. First `return <noreturn exp>?`
2784 // 2. Potentially found a returning branch, update accordingly
2785 if (!tf.next || tf.next.toBasetype().isTypeNoreturn())
2787 tf.next = resType; // infer void or noreturn
2789 // Found an actual return value before
2790 else if (tf.next.ty != Tvoid && !resType.toBasetype().isTypeNoreturn())
2792 if (!tf.next.isTypeError())
2794 error(rs.loc, "mismatched function return type inference of `void` and `%s`", tf.next.toChars());
2796 errors = true;
2797 tf.next = Type.terror;
2800 tret = tf.next;
2801 tbret = tret.toBasetype();
2804 // https://issues.dlang.org/show_bug.cgi?id=23914
2805 if (inferRef && !resType.isTypeNoreturn()) // deduce 'auto ref'
2806 tf.isref = false;
2808 if (tbret.ty != Tvoid && !resType.isTypeNoreturn()) // if non-void return
2810 if (!tbret.isTypeError())
2812 if (e0)
2813 error(rs.loc, "expected return type of `%s`, not `%s`", tret.toChars(), resType.toChars());
2814 else if (tbret.isTypeNoreturn())
2816 error(rs.loc, "cannot return from `noreturn` function");
2817 .errorSupplemental(rs.loc,
2818 "Consider adding an endless loop, `assert(0)`, or another `noreturn` expression");
2820 else
2821 error(rs.loc, "`return` expression expected");
2823 errors = true;
2825 else if (fd.isMain())
2827 // main() returns 0, even if it returns void
2828 rs.exp = IntegerExp.literal!0;
2832 // If any branches have called a ctor, but this branch hasn't, it's an error
2833 if (sc.ctorflow.callSuper & CSX.any_ctor && !(sc.ctorflow.callSuper & (CSX.this_ctor | CSX.super_ctor)))
2835 error(rs.loc, "`return` without calling constructor");
2836 errors = true;
2839 if (sc.ctorflow.fieldinit.length) // if aggregate fields are being constructed
2841 auto ad = fd.isMemberLocal();
2842 assert(ad);
2843 foreach (i, v; ad.fields)
2845 bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested());
2846 if (mustInit && !(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor))
2848 error(rs.loc, "an earlier `return` statement skips field `%s` initialization", v.toChars());
2849 errors = true;
2853 sc.ctorflow.orCSX(CSX.return_);
2855 if (errors)
2856 return setError();
2858 if (sc.fes)
2860 if (!rs.exp)
2862 // Send out "case receiver" statement to the foreach.
2863 // return exp;
2864 Statement s = new ReturnStatement(Loc.initial, rs.exp);
2865 sc.fes.cases.push(s);
2867 // Immediately rewrite "this" return statement as:
2868 // return cases.length+1;
2869 rs.exp = new IntegerExp(sc.fes.cases.length + 1);
2870 if (e0)
2872 result = new CompoundStatement(rs.loc, new ExpStatement(rs.loc, e0), rs);
2873 return;
2875 result = rs;
2876 return;
2878 else
2880 fd.buildResultVar(null, rs.exp.type);
2881 bool r = fd.vresult.checkNestedReference(sc, Loc.initial);
2882 assert(!r); // vresult should be always accessible
2884 // Send out "case receiver" statement to the foreach.
2885 // return vresult;
2886 Statement s = new ReturnStatement(Loc.initial, new VarExp(Loc.initial, fd.vresult));
2887 sc.fes.cases.push(s);
2889 // Save receiver index for the later rewriting from:
2890 // return exp;
2891 // to:
2892 // vresult = exp; retrun caseDim;
2893 rs.caseDim = sc.fes.cases.length + 1;
2896 if (rs.exp)
2898 if (!fd.returns)
2899 fd.returns = new ReturnStatements();
2900 fd.returns.push(rs);
2902 if (e0)
2904 if (e0.isDeclarationExp() || e0.isCommaExp())
2906 rs.exp = Expression.combine(e0, rs.exp);
2908 else
2910 auto es = new ExpStatement(rs.loc, e0);
2911 if (e0.type.isTypeNoreturn())
2912 result = es; // Omit unreachable return;
2913 else
2914 result = new CompoundStatement(rs.loc, es, rs);
2916 return;
2919 result = rs;
2922 void visitBreak(BreakStatement bs)
2924 /* https://dlang.org/spec/statement.html#break-statement
2927 //printf("BreakStatement::semantic()\n");
2929 // If:
2930 // break Identifier;
2931 if (bs.ident)
2933 bs.ident = fixupLabelName(sc, bs.ident);
2935 FuncDeclaration thisfunc = sc.func;
2937 for (Scope* scx = sc; scx; scx = scx.enclosing)
2939 if (scx.func != thisfunc) // if in enclosing function
2941 if (sc.fes) // if this is the body of a foreach
2943 /* Post this statement to the fes, and replace
2944 * it with a return value that caller will put into
2945 * a switch. Caller will figure out where the break
2946 * label actually is.
2947 * Case numbers start with 2, not 0, as 0 is continue
2948 * and 1 is break.
2950 sc.fes.cases.push(bs);
2951 result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.length + 1));
2952 return;
2954 break; // can't break to it
2957 LabelStatement ls = scx.slabel;
2958 if (ls && ls.ident == bs.ident)
2960 Statement s = ls.statement;
2961 if (!s || !s.hasBreak())
2962 error(bs.loc, "label `%s` has no `break`", bs.ident.toChars());
2963 else if (ls.tf != sc.tf)
2964 error(bs.loc, "cannot break out of `finally` block");
2965 else
2967 ls.breaks = true;
2968 result = bs;
2969 return;
2971 return setError();
2974 error(bs.loc, "enclosing label `%s` for `break` not found", bs.ident.toChars());
2975 return setError();
2977 else if (!sc.sbreak)
2979 if (sc.os && sc.os.tok != TOK.onScopeFailure)
2981 error(bs.loc, "`break` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok));
2983 else if (sc.fes)
2985 // Replace break; with return 1;
2986 result = new ReturnStatement(Loc.initial, IntegerExp.literal!1);
2987 return;
2989 else
2990 error(bs.loc, "`break` is not inside a loop or `switch`");
2991 return setError();
2993 else if (sc.sbreak.isForwardingStatement())
2995 error(bs.loc, "must use labeled `break` within `static foreach`");
2997 result = bs;
3000 void visitContinue(ContinueStatement cs)
3002 /* https://dlang.org/spec/statement.html#continue-statement
3005 //printf("ContinueStatement::semantic() %p\n", cs);
3006 if (cs.ident)
3008 cs.ident = fixupLabelName(sc, cs.ident);
3010 Scope* scx;
3011 FuncDeclaration thisfunc = sc.func;
3013 for (scx = sc; scx; scx = scx.enclosing)
3015 LabelStatement ls;
3016 if (scx.func != thisfunc) // if in enclosing function
3018 if (sc.fes) // if this is the body of a foreach
3020 for (; scx; scx = scx.enclosing)
3022 ls = scx.slabel;
3023 if (ls && ls.ident == cs.ident && ls.statement == sc.fes)
3025 // Replace continue ident; with return 0;
3026 result = new ReturnStatement(Loc.initial, IntegerExp.literal!0);
3027 return;
3031 /* Post this statement to the fes, and replace
3032 * it with a return value that caller will put into
3033 * a switch. Caller will figure out where the break
3034 * label actually is.
3035 * Case numbers start with 2, not 0, as 0 is continue
3036 * and 1 is break.
3038 sc.fes.cases.push(cs);
3039 result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.length + 1));
3040 return;
3042 break; // can't continue to it
3045 ls = scx.slabel;
3046 if (ls && ls.ident == cs.ident)
3048 Statement s = ls.statement;
3049 if (!s || !s.hasContinue())
3050 error(cs.loc, "label `%s` has no `continue`", cs.ident.toChars());
3051 else if (ls.tf != sc.tf)
3052 error(cs.loc, "cannot continue out of `finally` block");
3053 else
3055 result = cs;
3056 return;
3058 return setError();
3061 error(cs.loc, "enclosing label `%s` for `continue` not found", cs.ident.toChars());
3062 return setError();
3064 else if (!sc.scontinue)
3066 if (sc.os && sc.os.tok != TOK.onScopeFailure)
3068 error(cs.loc, "`continue` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok));
3070 else if (sc.fes)
3072 // Replace continue; with return 0;
3073 result = new ReturnStatement(Loc.initial, IntegerExp.literal!0);
3074 return;
3076 else
3077 error(cs.loc, "`continue` is not inside a loop");
3078 return setError();
3080 else if (sc.scontinue.isForwardingStatement())
3082 error(cs.loc, "must use labeled `continue` within `static foreach`");
3084 result = cs;
3087 void visitSynchronized(SynchronizedStatement ss)
3089 /* https://dlang.org/spec/statement.html#synchronized-statement
3092 if (ss.exp)
3094 ss.exp = ss.exp.expressionSemantic(sc);
3095 ss.exp = resolveProperties(sc, ss.exp);
3096 ss.exp = ss.exp.optimize(WANTvalue);
3097 ss.exp = checkGC(sc, ss.exp);
3098 if (ss.exp.op == EXP.error)
3100 if (ss._body)
3101 ss._body = ss._body.statementSemantic(sc);
3102 return setError();
3105 ClassDeclaration cd = ss.exp.type.isClassHandle();
3106 if (!cd)
3108 error(ss.loc, "can only `synchronize` on class objects, not `%s`", ss.exp.type.toChars());
3109 return setError();
3111 else if (cd.isInterfaceDeclaration())
3113 /* Cast the interface to an object, as the object has the monitor,
3114 * not the interface.
3116 if (!ClassDeclaration.object)
3118 error(ss.loc, "missing or corrupt object.d");
3119 fatal();
3122 Type t = ClassDeclaration.object.type;
3123 t = t.typeSemantic(Loc.initial, sc).toBasetype();
3124 assert(t.ty == Tclass);
3126 ss.exp = new CastExp(ss.loc, ss.exp, t);
3127 ss.exp = ss.exp.expressionSemantic(sc);
3129 version (all)
3131 /* Rewrite as:
3132 * auto tmp = exp;
3133 * _d_monitorenter(tmp);
3134 * try { body } finally { _d_monitorexit(tmp); }
3136 auto tmp = copyToTemp(0, "__sync", ss.exp);
3137 tmp.dsymbolSemantic(sc);
3139 auto cs = new Statements();
3140 cs.push(new ExpStatement(ss.loc, tmp));
3142 auto args = new Parameters();
3143 args.push(new Parameter(Loc.initial, 0, ClassDeclaration.object.type, null, null, null));
3145 FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter);
3146 Expression e = new CallExp(ss.loc, fdenter, new VarExp(ss.loc, tmp));
3147 e.type = Type.tvoid; // do not run semantic on e
3149 cs.push(new ExpStatement(ss.loc, e));
3150 FuncDeclaration fdexit = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorexit);
3151 e = new CallExp(ss.loc, fdexit, new VarExp(ss.loc, tmp));
3152 e.type = Type.tvoid; // do not run semantic on e
3153 Statement s = new ExpStatement(ss.loc, e);
3154 s = new TryFinallyStatement(ss.loc, ss._body, s);
3155 cs.push(s);
3157 s = new CompoundStatement(ss.loc, cs);
3158 result = s.statementSemantic(sc);
3161 else
3163 /* Generate our own critical section, then rewrite as:
3164 * static shared void* __critsec;
3165 * _d_criticalenter2(&__critsec);
3166 * try { body } finally { _d_criticalexit(__critsec); }
3168 auto id = Identifier.generateId("__critsec");
3169 auto t = Type.tvoidptr;
3170 auto tmp = new VarDeclaration(ss.loc, t, id, null);
3171 tmp.storage_class |= STC.temp | STC.shared_ | STC.static_;
3172 Expression tmpExp = new VarExp(ss.loc, tmp);
3174 auto cs = new Statements();
3175 cs.push(new ExpStatement(ss.loc, tmp));
3177 /* This is just a dummy variable for "goto skips declaration" error.
3178 * Backend optimizer could remove this unused variable.
3180 auto v = new VarDeclaration(ss.loc, Type.tvoidptr, Identifier.generateId("__sync"), null);
3181 v.dsymbolSemantic(sc);
3182 cs.push(new ExpStatement(ss.loc, v));
3184 auto enterArgs = new Parameters();
3185 enterArgs.push(new Parameter(Loc.initial, 0, t.pointerTo(), null, null, null));
3187 FuncDeclaration fdenter = FuncDeclaration.genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_);
3188 Expression e = new AddrExp(ss.loc, tmpExp);
3189 e = e.expressionSemantic(sc);
3190 e = new CallExp(ss.loc, fdenter, e);
3191 e.type = Type.tvoid; // do not run semantic on e
3192 cs.push(new ExpStatement(ss.loc, e));
3194 auto exitArgs = new Parameters();
3195 exitArgs.push(new Parameter(Loc.initial, 0, t, null, null, null));
3197 FuncDeclaration fdexit = FuncDeclaration.genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_);
3198 e = new CallExp(ss.loc, fdexit, tmpExp);
3199 e.type = Type.tvoid; // do not run semantic on e
3200 Statement s = new ExpStatement(ss.loc, e);
3201 s = new TryFinallyStatement(ss.loc, ss._body, s);
3202 cs.push(s);
3204 s = new CompoundStatement(ss.loc, cs);
3205 result = s.statementSemantic(sc);
3209 void visitWith(WithStatement ws)
3211 /* https://dlang.org/spec/statement.html#with-statement
3214 ScopeDsymbol sym;
3215 Initializer _init;
3217 //printf("WithStatement::semantic()\n");
3218 ws.exp = ws.exp.expressionSemantic(sc);
3219 ws.exp = resolveProperties(sc, ws.exp);
3220 ws.exp = ws.exp.optimize(WANTvalue);
3221 ws.exp = checkGC(sc, ws.exp);
3222 if (ws.exp.op == EXP.error)
3223 return setError();
3224 if (ws.exp.op == EXP.scope_)
3226 sym = new WithScopeSymbol(ws);
3227 sym.parent = sc.scopesym;
3228 sym.endlinnum = ws.endloc.linnum;
3230 else if (ws.exp.op == EXP.type)
3232 Dsymbol s = (cast(TypeExp)ws.exp).type.toDsymbol(sc);
3233 if (!s || !s.isScopeDsymbol())
3235 error(ws.loc, "`with` type `%s` has no members", ws.exp.toChars());
3236 return setError();
3238 sym = new WithScopeSymbol(ws);
3239 sym.parent = sc.scopesym;
3240 sym.endlinnum = ws.endloc.linnum;
3242 else
3244 Type texp = ws.exp.type;
3245 Type t = texp.toBasetype();
3247 Expression olde = ws.exp;
3248 if (t.ty == Tpointer)
3250 ws.exp = new PtrExp(ws.loc, ws.exp);
3251 ws.exp = ws.exp.expressionSemantic(sc);
3252 texp = ws.exp.type;
3253 t = texp.toBasetype();
3256 assert(t);
3257 t = t.toBasetype();
3258 if (t.isClassHandle())
3260 _init = new ExpInitializer(ws.loc, ws.exp);
3261 ws.wthis = new VarDeclaration(ws.loc, ws.exp.type, Id.withSym, _init);
3262 ws.wthis.storage_class |= STC.temp;
3263 ws.wthis.dsymbolSemantic(sc);
3265 sym = new WithScopeSymbol(ws);
3266 sym.parent = sc.scopesym;
3267 sym.endlinnum = ws.endloc.linnum;
3269 else if (t.ty == Tstruct)
3271 if (!ws.exp.isLvalue())
3273 /* Re-write to
3275 * auto __withtmp = exp
3276 * with(__withtmp)
3278 * ...
3282 auto tmp = copyToTemp(0, "__withtmp", ws.exp);
3283 tmp.dsymbolSemantic(sc);
3284 auto es = new ExpStatement(ws.loc, tmp);
3285 ws.exp = new VarExp(ws.loc, tmp);
3286 Statement ss = new ScopeStatement(ws.loc, new CompoundStatement(ws.loc, es, ws), ws.endloc);
3287 result = ss.statementSemantic(sc);
3288 return;
3290 Expression e = ws.exp.addressOf();
3291 _init = new ExpInitializer(ws.loc, e);
3292 ws.wthis = new VarDeclaration(ws.loc, e.type, Id.withSym, _init);
3293 ws.wthis.storage_class |= STC.temp;
3294 ws.wthis.dsymbolSemantic(sc);
3295 sym = new WithScopeSymbol(ws);
3296 // Need to set the scope to make use of resolveAliasThis
3297 sym.setScope(sc);
3298 sym.parent = sc.scopesym;
3299 sym.endlinnum = ws.endloc.linnum;
3301 else if (auto tenum = texp.isTypeEnum())
3303 ws.exp = new TypeExp(ws.exp.loc, tenum);
3304 sym = new WithScopeSymbol(ws);
3305 sym.parent = sc.scopesym;
3306 sym.endlinnum = ws.endloc.linnum;
3308 else
3310 error(ws.loc, "`with` expression types must be enums or aggregates or pointers to them, not `%s`", olde.type.toChars());
3311 return setError();
3315 if (ws._body)
3317 sym._scope = sc;
3318 sc = sc.push(sym);
3319 sc.insert(sym);
3320 ws._body = ws._body.statementSemantic(sc);
3321 sc.pop();
3322 if (ws._body && ws._body.isErrorStatement())
3324 result = ws._body;
3325 return;
3329 result = ws;
3332 // https://dlang.org/spec/statement.html#TryStatement
3333 void visitTryCatch(TryCatchStatement tcs)
3335 //printf("TryCatchStatement.semantic()\n");
3337 if (!global.params.useExceptions)
3339 error(tcs.loc, "cannot use try-catch statements with %s", global.params.betterC ? "-betterC".ptr : "-nothrow".ptr);
3340 return setError();
3343 if (!ClassDeclaration.throwable)
3345 error(tcs.loc, "cannot use try-catch statements because `object.Throwable` was not declared");
3346 return setError();
3349 uint flags;
3350 enum FLAGcpp = 1;
3351 enum FLAGd = 2;
3353 tcs.tryBody = sc.tryBody; // chain on the in-flight tryBody
3354 tcs._body = tcs._body.semanticScope(sc, null, null, tcs);
3356 /* Even if body is empty, still do semantic analysis on catches
3358 bool catchErrors = false;
3359 foreach (i, c; *tcs.catches)
3361 c.catchSemantic(sc);
3362 if (c.errors)
3364 catchErrors = true;
3365 continue;
3367 auto cd = c.type.toBasetype().isClassHandle();
3368 flags |= cd.isCPPclass() ? FLAGcpp : FLAGd;
3370 // Determine if current catch 'hides' any previous catches
3371 foreach (j; 0 .. i)
3373 Catch cj = (*tcs.catches)[j];
3374 const si = c.loc.toChars();
3375 const sj = cj.loc.toChars();
3376 if (c.type.toBasetype().implicitConvTo(cj.type.toBasetype()))
3378 error(tcs.loc, "`catch` at %s hides `catch` at %s", sj, si);
3379 catchErrors = true;
3384 if (sc.func)
3386 sc.func.hasCatches = true;
3387 if (flags == (FLAGcpp | FLAGd))
3389 error(tcs.loc, "cannot mix catching D and C++ exceptions in the same try-catch");
3390 catchErrors = true;
3394 if (catchErrors)
3395 return setError();
3397 // No actual code in the try (i.e. omitted any conditionally compiled code)
3398 // Could also be extended to check for hasCode
3399 if (!tcs._body)
3400 return;
3402 if (tcs._body.isErrorStatement())
3404 result = tcs._body;
3405 return;
3408 /* If the try body never throws, we can eliminate any catches
3409 * of recoverable exceptions.
3411 if (!(tcs._body.blockExit(sc.func, null) & BE.throw_) && ClassDeclaration.exception)
3413 foreach_reverse (i; 0 .. tcs.catches.length)
3415 Catch c = (*tcs.catches)[i];
3417 /* If catch exception type is derived from Exception
3419 if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) &&
3420 (!c.handler || !c.handler.comeFrom()) && !(sc.flags & SCOPE.debug_))
3422 // Remove c from the array of catches
3423 tcs.catches.remove(i);
3428 if (tcs.catches.length == 0)
3430 result = tcs._body.hasCode() ? tcs._body : null;
3431 return;
3434 result = tcs;
3437 void visitTryFinally(TryFinallyStatement tfs)
3439 //printf("TryFinallyStatement::semantic()\n");
3440 tfs.tryBody = sc.tryBody; // chain on in-flight tryBody
3441 tfs._body = tfs._body.semanticScope(sc, null, null, tfs);
3443 sc = sc.push();
3444 sc.tf = tfs;
3445 sc.sbreak = null;
3446 sc.scontinue = null; // no break or continue out of finally block
3447 tfs.finalbody = tfs.finalbody.semanticNoScope(sc);
3448 sc.pop();
3450 if (!tfs._body)
3452 result = tfs.finalbody;
3453 return;
3455 if (!tfs.finalbody)
3457 result = tfs._body;
3458 return;
3461 auto blockexit = tfs._body.blockExit(sc.func, null);
3463 // if not worrying about exceptions
3464 if (!(global.params.useExceptions && ClassDeclaration.throwable))
3465 blockexit &= ~BE.throw_; // don't worry about paths that otherwise may throw
3467 // Don't care about paths that halt, either
3468 if ((blockexit & ~BE.halt) == BE.fallthru)
3470 result = new CompoundStatement(tfs.loc, tfs._body, tfs.finalbody);
3471 return;
3473 tfs.bodyFallsThru = (blockexit & BE.fallthru) != 0;
3474 result = tfs;
3477 void visitScopeGuard(ScopeGuardStatement oss)
3479 /* https://dlang.org/spec/statement.html#scope-guard-statement
3482 if (oss.tok != TOK.onScopeExit)
3484 // https://issues.dlang.org/show_bug.cgi?id=23159
3485 if (!global.params.useExceptions)
3487 version (IN_GCC)
3488 error(oss.loc, "`%s` cannot be used with `-fno-exceptions`", Token.toChars(oss.tok));
3489 else
3490 error(oss.loc, "`%s` cannot be used with -betterC", Token.toChars(oss.tok));
3491 return setError();
3494 // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement,
3495 // so the generated catch block cannot be placed in finally block.
3496 // See also Catch::semantic.
3497 if (sc.os && sc.os.tok != TOK.onScopeFailure)
3499 // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
3500 error(oss.loc, "cannot put `%s` statement inside `%s`", Token.toChars(oss.tok), Token.toChars(sc.os.tok));
3501 return setError();
3503 if (sc.tf)
3505 error(oss.loc, "cannot put `%s` statement inside `finally` block", Token.toChars(oss.tok));
3506 return setError();
3510 sc = sc.push();
3511 sc.tf = null;
3512 sc.os = oss;
3513 if (oss.tok != TOK.onScopeFailure)
3515 // Jump out from scope(failure) block is allowed.
3516 sc.sbreak = null;
3517 sc.scontinue = null;
3519 oss.statement = oss.statement.semanticNoScope(sc);
3520 sc.pop();
3522 if (!oss.statement || oss.statement.isErrorStatement())
3524 result = oss.statement;
3525 return;
3527 result = oss;
3530 void visitThrow(ThrowStatement ts)
3532 /* https://dlang.org/spec/statement.html#throw-statement
3535 //printf("ThrowStatement::semantic()\n");
3536 if (throwSemantic(ts.loc, ts.exp, sc))
3537 result = ts;
3538 else
3539 setError();
3543 void visitDebug(DebugStatement ds)
3545 if (ds.statement)
3547 sc = sc.push();
3548 sc.flags |= SCOPE.debug_;
3549 ds.statement = ds.statement.statementSemantic(sc);
3550 sc.pop();
3552 result = ds.statement;
3555 void visitGoto(GotoStatement gs)
3557 /* https://dlang.org/spec/statement.html#goto-statement
3560 //printf("GotoStatement::semantic()\n");
3561 FuncDeclaration fd = sc.func;
3563 gs.ident = fixupLabelName(sc, gs.ident);
3564 gs.label = fd.searchLabel(gs.ident, gs.loc);
3565 gs.tryBody = sc.tryBody;
3566 gs.tf = sc.tf;
3567 gs.os = sc.os;
3568 gs.lastVar = sc.lastVar;
3569 gs.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0;
3571 if (!gs.label.statement && sc.fes)
3573 /* Either the goto label is forward referenced or it
3574 * is in the function that the enclosing foreach is in.
3575 * Can't know yet, so wrap the goto in a scope statement
3576 * so we can patch it later, and add it to a 'look at this later'
3577 * list.
3579 gs.label.deleted = true;
3580 auto ss = new ScopeStatement(gs.loc, gs, gs.loc);
3581 sc.fes.gotos.push(ss); // 'look at this later' list
3582 result = ss;
3583 return;
3586 // Add to fwdref list to check later
3587 if (!gs.label.statement)
3589 if (!fd.gotos)
3590 fd.gotos = new GotoStatements();
3591 fd.gotos.push(gs);
3593 else if (!(sc.flags & SCOPE.Cfile) && gs.checkLabel())
3594 return setError();
3596 result = gs;
3599 void visitLabel(LabelStatement ls)
3601 //printf("LabelStatement::semantic()\n");
3602 FuncDeclaration fd = sc.parent.isFuncDeclaration();
3604 ls.ident = fixupLabelName(sc, ls.ident);
3605 ls.tryBody = sc.tryBody;
3606 ls.tf = sc.tf;
3607 ls.os = sc.os;
3608 ls.lastVar = sc.lastVar;
3609 ls.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0;
3611 LabelDsymbol ls2 = fd.searchLabel(ls.ident, ls.loc);
3612 if (ls2.statement && !ls2.duplicated)
3614 if (ls.loc == ls2.loc)
3616 ls2.duplicated = true;
3617 error(ls.loc, "label `%s` is duplicated", ls2.toChars());
3618 .errorSupplemental(ls2.loc, "labels cannot be used in a static foreach with more than 1 iteration");
3620 else
3622 error(ls.loc, "label `%s` is already defined", ls2.toChars());
3623 .errorSupplemental(ls2.loc, "first definition is here");
3625 return setError();
3627 else
3628 ls2.statement = ls;
3630 sc = sc.push();
3631 sc.scopesym = sc.enclosing.scopesym;
3633 sc.ctorflow.orCSX(CSX.label);
3635 sc.slabel = ls;
3636 if (ls.statement)
3637 ls.statement = ls.statement.statementSemantic(sc);
3638 sc.pop();
3640 result = ls;
3643 void visitAsm(AsmStatement s)
3645 /* https://dlang.org/spec/statement.html#asm
3648 //printf("AsmStatement()::semantic()\n");
3649 result = asmSemantic(s, sc);
3652 void visitCompoundAsm(CompoundAsmStatement cas)
3654 //printf("CompoundAsmStatement()::semantic()\n");
3655 // Apply postfix attributes of the asm block to each statement.
3656 sc = sc.push();
3657 sc.stc |= cas.stc;
3659 /* Go through the statements twice, first to declare any labels,
3660 * second for anything else.
3663 foreach (ref s; *cas.statements)
3665 if (s)
3667 if (auto ls = s.isLabelStatement())
3669 sc.func.searchLabel(ls.ident, ls.loc);
3674 foreach (ref s; *cas.statements)
3676 s = s ? s.statementSemantic(sc) : null;
3679 assert(sc.func);
3680 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"))
3681 error(cas.loc, "`asm` statement is assumed to be impure - mark it with `pure` if it is not");
3682 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"))
3683 error(cas.loc, "`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not");
3684 // @@@DEPRECATED_2.114@@@
3685 // change deprecation() to error(), add `else` and remove `| STC.safe`
3686 // to turn deprecation into an error when deprecation cycle is over
3687 if (cas.stc & STC.safe)
3688 deprecation(cas.loc, "`asm` statement cannot be marked `@safe`, use `@system` or `@trusted` instead");
3689 if (!(cas.stc & (STC.trusted | STC.safe)))
3691 sc.setUnsafe(false, cas.loc, "`asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not");
3694 sc.pop();
3695 result = cas;
3698 void visitImport(ImportStatement imps)
3700 /* https://dlang.org/spec/module.html#ImportDeclaration
3703 foreach (i; 0 .. imps.imports.length)
3705 Import s = (*imps.imports)[i].isImport();
3706 assert(!s.aliasdecls.length);
3707 foreach (j, name; s.names)
3709 Identifier _alias = s.aliases[j];
3710 if (!_alias)
3711 _alias = name;
3713 auto tname = new TypeIdentifier(s.loc, name);
3714 auto ad = new AliasDeclaration(s.loc, _alias, tname);
3715 ad._import = s;
3716 s.aliasdecls.push(ad);
3719 s.dsymbolSemantic(sc);
3721 // https://issues.dlang.org/show_bug.cgi?id=19942
3722 // If the module that's being imported doesn't exist, don't add it to the symbol table
3723 // for the current scope.
3724 if (s.mod !is null)
3726 Module.addDeferredSemantic2(s); // https://issues.dlang.org/show_bug.cgi?id=14666
3727 sc.insert(s);
3729 foreach (aliasdecl; s.aliasdecls)
3731 sc.insert(aliasdecl);
3735 result = imps;
3738 mixin VisitStatement!void visit;
3739 visit.VisitStatement(s);
3740 return result;
3744 * Run semantic on `throw <exp>`.
3746 * Params:
3747 * loc = location of the `throw`
3748 * exp = value to be thrown
3749 * sc = enclosing scope
3751 * Returns: true if the `throw` is valid, or false if an error was found
3753 public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc)
3755 if (!global.params.useExceptions)
3757 loc.error("cannot use `throw` statements with %s", global.params.betterC ? "-betterC".ptr : "-nothrow".ptr);
3758 return false;
3761 if (!ClassDeclaration.throwable)
3763 loc.error("cannot use `throw` statements because `object.Throwable` was not declared");
3764 return false;
3767 if (FuncDeclaration fd = sc.parent.isFuncDeclaration())
3768 fd.hasReturnExp |= 2;
3770 if (exp.op == EXP.new_)
3772 NewExp ne = cast(NewExp) exp;
3773 ne.thrownew = true;
3776 exp = exp.expressionSemantic(sc);
3777 exp = resolveProperties(sc, exp);
3778 exp = checkGC(sc, exp);
3779 if (exp.op == EXP.error)
3780 return false;
3781 if (!exp.type.isNaked())
3783 // @@@DEPRECATED_2.112@@@
3784 // Deprecated in 2.102, change into an error & return false in 2.112
3785 exp.loc.deprecation("cannot throw object of qualified type `%s`", exp.type.toChars());
3786 //return false;
3788 checkThrowEscape(sc, exp, false);
3790 ClassDeclaration cd = exp.type.toBasetype().isClassHandle();
3791 if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null)))
3793 loc.error("can only throw class objects derived from `Throwable`, not type `%s`", exp.type.toChars());
3794 return false;
3796 return true;
3799 private extern(D) Expression applyOpApply(ForeachStatement fs, Expression flde,
3800 Type tab, Scope* sc2, Dsymbol sapply)
3802 version (none)
3804 if (sc2.useDIP1000 == FeatureState.enabled)
3806 message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`");
3808 (cast(FuncExp)flde).fd.tookAddressOf = 1;
3810 else
3812 if (sc2.useDIP1000 == FeatureState.enabled)
3813 ++(cast(FuncExp)flde).fd.tookAddressOf; // allocate a closure unless the opApply() uses 'scope'
3815 assert(tab.ty == Tstruct || tab.ty == Tclass);
3816 assert(sapply);
3817 /* Call:
3818 * aggr.apply(flde)
3820 Expression ec;
3821 ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident);
3822 ec = new CallExp(fs.loc, ec, flde);
3823 ec = ec.expressionSemantic(sc2);
3824 if (ec.op == EXP.error)
3825 return null;
3826 if (ec.type != Type.tint32)
3828 error(fs.loc, "`opApply()` function for `%s` must return an `int`", tab.toChars());
3829 return null;
3831 return ec;
3834 private extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde,
3835 Type tab, Scope* sc2)
3837 Expression ec;
3838 /* Call:
3839 * aggr(flde)
3841 if (fs.aggr.op == EXP.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() &&
3842 !(cast(DelegateExp)fs.aggr).func.needThis())
3844 // https://issues.dlang.org/show_bug.cgi?id=3560
3845 fs.aggr = (cast(DelegateExp)fs.aggr).e1;
3847 ec = new CallExp(fs.loc, fs.aggr, flde);
3848 ec = ec.expressionSemantic(sc2);
3849 if (ec.op == EXP.error)
3850 return null;
3851 if (ec.type != Type.tint32)
3853 error(fs.loc, "`opApply()` function for `%s` must return an `int`", tab.toChars());
3854 return null;
3856 return ec;
3859 private extern(D) Expression applyArray(ForeachStatement fs, Expression flde,
3860 Type tab, Scope* sc2, Type tn, Type tnv)
3862 Expression ec;
3863 const dim = fs.parameters.length;
3864 const loc = fs.loc;
3865 /* Call:
3866 * _aApply(aggr, flde)
3868 static immutable fntab =
3870 "cc", "cw", "cd",
3871 "wc", "cc", "wd",
3872 "dc", "dw", "dd"
3875 const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1;
3876 char[BUFFER_LEN] fdname;
3877 int flag;
3879 switch (tn.ty)
3881 case Tchar: flag = 0; break;
3882 case Twchar: flag = 3; break;
3883 case Tdchar: flag = 6; break;
3884 default:
3885 assert(0);
3887 switch (tnv.ty)
3889 case Tchar: flag += 0; break;
3890 case Twchar: flag += 1; break;
3891 case Tdchar: flag += 2; break;
3892 default:
3893 assert(0);
3895 const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : "";
3896 int j = snprintf(fdname.ptr, BUFFER_LEN, "_aApply%s%.*s%llu", r, 2, fntab[flag].ptr, cast(ulong)dim);
3897 assert(j < BUFFER_LEN);
3899 FuncDeclaration fdapply;
3900 TypeDelegate dgty;
3901 auto params = new Parameters();
3902 params.push(new Parameter(Loc.initial, STC.in_, tn.arrayOf(), null, null, null));
3903 auto dgparams = new Parameters();
3904 dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null));
3905 if (dim == 2)
3906 dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null));
3907 dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
3908 params.push(new Parameter(Loc.initial, 0, dgty, null, null, null));
3909 fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);
3911 if (tab.isTypeSArray())
3912 fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf());
3913 // paint delegate argument to the type runtime expects
3914 Expression fexp = flde;
3915 if (!dgty.equals(flde.type))
3917 fexp = new CastExp(loc, flde, flde.type);
3918 fexp.type = dgty;
3920 ec = new VarExp(Loc.initial, fdapply, false);
3921 ec = new CallExp(loc, ec, fs.aggr, fexp);
3922 ec.type = Type.tint32; // don't run semantic() on ec
3923 return ec;
3926 private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab)
3928 auto taa = tab.isTypeAArray();
3929 Expression ec;
3930 const dim = fs.parameters.length;
3931 // Check types
3932 Parameter p = (*fs.parameters)[0];
3933 bool isRef = (p.storageClass & STC.ref_) != 0;
3934 Type ta = p.type;
3935 if (dim == 2)
3937 Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index);
3938 if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta))
3940 error(fs.loc, "`foreach`: index must be type `%s`, not `%s`",
3941 ti.toChars(), ta.toChars());
3942 return null;
3944 p = (*fs.parameters)[1];
3945 isRef = (p.storageClass & STC.ref_) != 0;
3946 ta = p.type;
3948 Type taav = taa.nextOf();
3949 if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
3951 error(fs.loc, "`foreach`: value must be type `%s`, not `%s`",
3952 taav.toChars(), ta.toChars());
3953 return null;
3956 /* Call:
3957 * extern(C) int _aaApply(void*, in size_t, int delegate(void*))
3958 * _aaApply(aggr, keysize, flde)
3960 * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
3961 * _aaApply2(aggr, keysize, flde)
3963 __gshared FuncDeclaration* fdapply = [null, null];
3964 __gshared TypeDelegate* fldeTy = [null, null];
3965 ubyte i = (dim == 2 ? 1 : 0);
3966 if (!fdapply[i])
3968 auto params = new Parameters();
3969 params.push(new Parameter(Loc.initial, 0, Type.tvoid.pointerTo(), null, null, null));
3970 params.push(new Parameter(Loc.initial, STC.const_, Type.tsize_t, null, null, null));
3971 auto dgparams = new Parameters();
3972 dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null));
3973 if (dim == 2)
3974 dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null));
3975 fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
3976 params.push(new Parameter(Loc.initial, 0, fldeTy[i], null, null, null));
3977 fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply);
3980 auto exps = new Expressions();
3981 exps.push(fs.aggr);
3982 auto keysize = taa.index.size();
3983 if (keysize == SIZE_INVALID)
3984 return null;
3985 assert(keysize < keysize.max - target.ptrsize);
3986 keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1);
3987 // paint delegate argument to the type runtime expects
3988 Expression fexp = flde;
3989 if (!fldeTy[i].equals(flde.type))
3991 fexp = new CastExp(fs.loc, flde, flde.type);
3992 fexp.type = fldeTy[i];
3994 exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t));
3995 exps.push(fexp);
3996 ec = new VarExp(Loc.initial, fdapply[i], false);
3997 ec = new CallExp(fs.loc, ec, exps);
3998 ec.type = Type.tint32; // don't run semantic() on ec
3999 return ec;
4002 private extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc)
4004 if (!cases.length)
4006 // Easy case, a clean exit from the loop
4007 e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899
4008 return new ExpStatement(loc, e);
4010 // Construct a switch statement around the return value
4011 // of the apply function.
4012 Statement s;
4013 auto a = new Statements();
4015 // default: break; takes care of cases 0 and 1
4016 s = new BreakStatement(Loc.initial, null);
4017 s = new DefaultStatement(Loc.initial, s);
4018 a.push(s);
4020 // cases 2...
4021 foreach (i, c; *cases)
4023 s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c);
4024 a.push(s);
4027 s = new CompoundStatement(loc, a);
4028 return new SwitchStatement(loc, null, e, s, false, loc);
4031 /*************************************
4032 * Turn foreach body into the function literal:
4033 * int delegate(ref T param) { body }
4034 * Params:
4035 * sc = context
4036 * fs = ForeachStatement
4037 * tfld = type of function literal to be created (type of opApply() function if any), can be null
4038 * Returns:
4039 * Function literal created, as an expression
4040 * null if error.
4042 private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld)
4044 auto params = new Parameters();
4045 foreach (i, p; *fs.parameters)
4047 StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_);
4048 Identifier id;
4050 p.type = p.type.typeSemantic(fs.loc, sc);
4051 p.type = p.type.addStorageClass(p.storageClass);
4052 if (tfld)
4054 Parameter prm = tfld.parameterList[i];
4055 //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars());
4056 stc = (prm.storageClass & STC.ref_) | (p.storageClass & STC.scope_);
4057 if ((p.storageClass & STC.ref_) != (prm.storageClass & STC.ref_))
4059 if (!(prm.storageClass & STC.ref_))
4061 error(fs.loc, "`foreach`: cannot make `%s` `ref`", p.ident.toChars());
4062 return null;
4064 goto LcopyArg;
4066 id = p.ident; // argument copy is not need.
4068 else if (p.storageClass & STC.ref_)
4070 // default delegate parameters are marked as ref, then
4071 // argument copy is not need.
4072 id = p.ident;
4074 else
4076 // Make a copy of the ref argument so it isn't
4077 // a reference.
4078 LcopyArg:
4079 id = Identifier.generateId("__applyArg", cast(int)i);
4081 Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id));
4082 auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie);
4083 v.storage_class |= STC.temp | (stc & STC.scope_);
4084 Statement s = new ExpStatement(fs.loc, v);
4085 fs._body = new CompoundStatement(fs.loc, s, fs._body);
4087 params.push(new Parameter(fs.loc, stc, p.type, id, null, null));
4089 // https://issues.dlang.org/show_bug.cgi?id=13840
4090 // Throwable nested function inside nothrow function is acceptable.
4091 StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func);
4092 auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc);
4093 fs.cases = new Statements();
4094 fs.gotos = new ScopeStatements();
4095 auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs);
4096 fld.fbody = fs._body;
4097 Expression flde = new FuncExp(fs.loc, fld);
4098 flde = flde.expressionSemantic(sc);
4099 fld.tookAddressOf = 0;
4100 if (flde.op == EXP.error)
4101 return null;
4102 return cast(FuncExp)flde;
4106 void catchSemantic(Catch c, Scope* sc)
4108 //printf("Catch::semantic(%s)\n", ident.toChars());
4110 if (sc.os && sc.os.tok != TOK.onScopeFailure)
4112 // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
4113 error(c.loc, "cannot put `catch` statement inside `%s`", Token.toChars(sc.os.tok));
4114 c.errors = true;
4116 if (sc.tf)
4118 /* This is because the _d_local_unwind() gets the stack munged
4119 * up on this. The workaround is to place any try-catches into
4120 * a separate function, and call that.
4121 * To fix, have the compiler automatically convert the finally
4122 * body into a nested function.
4124 error(c.loc, "cannot put `catch` statement inside `finally` block");
4125 c.errors = true;
4128 auto sym = new ScopeDsymbol();
4129 sym.parent = sc.scopesym;
4130 sc = sc.push(sym);
4132 if (!c.type)
4134 error(c.loc, "`catch` statement without an exception specification is deprecated");
4135 errorSupplemental(c.loc, "use `catch(Throwable)` for old behavior");
4136 c.errors = true;
4138 // reference .object.Throwable
4139 c.type = getThrowable();
4141 else if (!c.type.isNaked() && !c.type.isConst())
4143 // @@@DEPRECATED_2.115@@@
4144 // Deprecated in 2.105, change into an error & uncomment assign in 2.115
4145 deprecation(c.loc, "can only catch mutable or const qualified types, not `%s`", c.type.toChars());
4146 //c.errors = true;
4148 c.type = c.type.typeSemantic(c.loc, sc);
4149 if (c.type == Type.terror)
4151 c.errors = true;
4152 sc.pop();
4153 return;
4156 StorageClass stc;
4157 auto cd = c.type.toBasetype().isClassHandle();
4158 if (!cd)
4160 error(c.loc, "can only catch class objects, not `%s`", c.type.toChars());
4161 c.errors = true;
4163 else if (cd.isCPPclass())
4165 if (!target.cpp.exceptions)
4167 error(c.loc, "catching C++ class objects not supported for this target");
4168 c.errors = true;
4170 if (!c.internalCatch)
4172 if (sc.setUnsafe(false, c.loc, "cannot catch C++ class objects in `@safe` code"))
4173 c.errors = true;
4176 else if (cd != ClassDeclaration.throwable && !ClassDeclaration.throwable.isBaseOf(cd, null))
4178 error(c.loc, "can only catch class objects derived from `Throwable`, not `%s`", c.type.toChars());
4179 c.errors = true;
4181 else if (!c.internalCatch && ClassDeclaration.exception &&
4182 cd != ClassDeclaration.exception && !ClassDeclaration.exception.isBaseOf(cd, null) &&
4183 sc.setUnsafe(false, c.loc,
4184 "can only catch class objects derived from `Exception` in `@safe` code, not `%s`", c.type))
4186 c.errors = true;
4188 else if (global.params.ehnogc)
4190 stc |= STC.scope_;
4193 // DIP1008 requires destruction of the Throwable, even if the user didn't specify an identifier
4194 auto ident = c.ident;
4195 if (!ident && global.params.ehnogc)
4196 ident = Identifier.generateAnonymousId("var");
4198 if (ident)
4200 c.var = new VarDeclaration(c.loc, c.type, ident, null, stc);
4201 c.var.iscatchvar = true;
4202 c.var.dsymbolSemantic(sc);
4203 sc.insert(c.var);
4205 if (global.params.ehnogc && stc & STC.scope_)
4207 /* Add a destructor for c.var
4208 * try { handler } finally { if (!__ctfe) _d_delThrowable(var); }
4210 assert(!c.var.edtor); // ensure we didn't create one in callScopeDtor()
4212 Loc loc = c.loc;
4213 Expression e = new VarExp(loc, c.var);
4214 e = new CallExp(loc, new IdentifierExp(loc, Id._d_delThrowable), e);
4216 Expression ec = new IdentifierExp(loc, Id.ctfe);
4217 ec = new NotExp(loc, ec);
4218 Statement s = new IfStatement(loc, null, ec, new ExpStatement(loc, e), null, loc);
4219 c.handler = new TryFinallyStatement(loc, c.handler, s);
4223 c.handler = c.handler.statementSemantic(sc);
4224 if (c.handler && c.handler.isErrorStatement())
4225 c.errors = true;
4227 sc.pop();
4230 Statement semanticNoScope(Statement s, Scope* sc)
4232 //printf("Statement::semanticNoScope() %s\n", toChars());
4233 if (!s.isCompoundStatement() && !s.isScopeStatement())
4235 s = new CompoundStatement(s.loc, s); // so scopeCode() gets called
4237 s = s.statementSemantic(sc);
4238 return s;
4241 // Same as semanticNoScope(), but do create a new scope
4242 private Statement semanticScope(Statement s, Scope* sc, Statement sbreak, Statement scontinue, Statement tryBody)
4244 auto sym = new ScopeDsymbol();
4245 sym.parent = sc.scopesym;
4246 Scope* scd = sc.push(sym);
4247 if (sbreak)
4248 scd.sbreak = sbreak;
4249 if (scontinue)
4250 scd.scontinue = scontinue;
4251 if (tryBody)
4252 scd.tryBody = tryBody;
4253 s = s.semanticNoScope(scd);
4254 scd.pop();
4255 return s;
4259 /****************************************
4260 * If `statement` has code that needs to run in a finally clause
4261 * at the end of the current scope, return that code in the form of
4262 * a Statement.
4263 * Params:
4264 * statement = the statement
4265 * sc = context
4266 * sentry = set to code executed upon entry to the scope
4267 * sexception = set to code executed upon exit from the scope via exception
4268 * sfinally = set to code executed in finally block
4269 * Returns:
4270 * code to be run in the finally clause
4272 Statement scopeCode(Statement statement, Scope* sc, out Statement sentry, out Statement sexception, out Statement sfinally)
4274 if (auto es = statement.isExpStatement())
4276 if (es.exp && es.exp.op == EXP.declaration)
4278 auto de = cast(DeclarationExp)es.exp;
4279 auto v = de.declaration.isVarDeclaration();
4280 if (v && !v.isDataseg())
4282 if (v.needsScopeDtor())
4284 sfinally = new DtorExpStatement(es.loc, v.edtor, v);
4285 v.storage_class |= STC.nodtor; // don't add in dtor again
4289 return es;
4292 else if (auto sgs = statement.isScopeGuardStatement())
4294 Statement s = new PeelStatement(sgs.statement);
4296 switch (sgs.tok)
4298 case TOK.onScopeExit:
4299 sfinally = s;
4300 break;
4302 case TOK.onScopeFailure:
4303 sexception = s;
4304 break;
4306 case TOK.onScopeSuccess:
4308 /* Create:
4309 * sentry: bool x = false;
4310 * sexception: x = true;
4311 * sfinally: if (!x) statement;
4313 auto v = copyToTemp(0, "__os", IntegerExp.createBool(false));
4314 v.dsymbolSemantic(sc);
4315 sentry = new ExpStatement(statement.loc, v);
4317 Expression e = IntegerExp.createBool(true);
4318 e = new AssignExp(Loc.initial, new VarExp(Loc.initial, v), e);
4319 sexception = new ExpStatement(Loc.initial, e);
4321 e = new VarExp(Loc.initial, v);
4322 e = new NotExp(Loc.initial, e);
4323 sfinally = new IfStatement(Loc.initial, null, e, s, null, Loc.initial);
4325 break;
4327 default:
4328 assert(0);
4330 return null;
4332 else if (auto ls = statement.isLabelStatement())
4334 if (ls.statement)
4335 ls.statement = ls.statement.scopeCode(sc, sentry, sexception, sfinally);
4336 return ls;
4339 return statement;
4342 /*******************
4343 * Type check and unroll `foreach` over an expression tuple as well
4344 * as `static foreach` statements and `static foreach`
4345 * declarations. For `static foreach` statements and `static
4346 * foreach` declarations, the visitor interface is used (and the
4347 * result is written into the `result` field.) For `static
4348 * foreach` declarations, the resulting Dsymbols* are returned
4349 * directly.
4351 * The unrolled body is wrapped into a
4352 * - UnrolledLoopStatement, for `foreach` over an expression tuple.
4353 * - ForwardingStatement, for `static foreach` statements.
4354 * - ForwardingAttribDeclaration, for `static foreach` declarations.
4356 * `static foreach` variables are declared as `STC.local`, such
4357 * that they are inserted into the local symbol tables of the
4358 * forwarding constructs instead of forwarded. For `static
4359 * foreach` with multiple foreach loop variables whose aggregate
4360 * has been lowered into a sequence of tuples, this function
4361 * expands the tuples into multiple `STC.local` `static foreach`
4362 * variables.
4364 public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachStatement fs, Dsymbols* dbody, bool needExpansion)
4366 // Voldemort return type
4367 union U
4369 Statement statement;
4370 Dsymbols* decl;
4373 U result;
4375 auto returnEarly()
4377 if (isDecl)
4378 result.decl = null;
4379 else
4380 result.statement = new ErrorStatement();
4381 return result;
4384 auto loc = fs.loc;
4385 size_t dim = fs.parameters.length;
4386 const bool skipCheck = isStatic && needExpansion;
4387 if (!skipCheck && (dim < 1 || dim > 2))
4389 error(fs.loc, "only one (value) or two (key,value) arguments allowed for sequence `foreach`");
4390 return returnEarly();
4393 Type paramtype = (*fs.parameters)[dim - 1].type;
4394 if (paramtype)
4396 paramtype = paramtype.typeSemantic(loc, sc);
4397 if (paramtype.ty == Terror)
4399 return returnEarly();
4403 Type tab = fs.aggr.type.toBasetype();
4404 TypeTuple tuple = cast(TypeTuple)tab;
4406 Statements* statements;
4407 Dsymbols* declarations;
4408 if (isDecl)
4409 declarations = new Dsymbols();
4410 else
4411 statements = new Statements();
4413 //printf("aggr: op = %d, %s\n", fs.aggr.op, fs.aggr.toChars());
4414 size_t n;
4415 TupleExp te = null;
4416 if (fs.aggr.op == EXP.tuple) // expression tuple
4418 te = cast(TupleExp)fs.aggr;
4419 n = te.exps.length;
4421 else if (fs.aggr.op == EXP.type) // type tuple
4423 n = Parameter.dim(tuple.arguments);
4425 else
4426 assert(0);
4427 foreach (j; 0 .. n)
4429 size_t k = (fs.op == TOK.foreach_) ? j : n - 1 - j;
4430 Expression e = null;
4431 Type t = null;
4432 if (te)
4433 e = (*te.exps)[k];
4434 else
4435 t = Parameter.getNth(tuple.arguments, k).type;
4436 Parameter p = (*fs.parameters)[0];
4438 Statements* stmts;
4439 Dsymbols* decls;
4440 if (isDecl)
4441 decls = new Dsymbols();
4442 else
4443 stmts = new Statements();
4445 const bool skip = isStatic && needExpansion;
4446 if (!skip && dim == 2)
4448 // Declare key
4449 if (p.isReference() || p.isLazy())
4451 error(fs.loc, "no storage class for key `%s`", p.ident.toChars());
4452 return returnEarly();
4455 if (isStatic)
4457 if (!p.type)
4459 p.type = Type.tsize_t;
4462 p.type = p.type.typeSemantic(loc, sc);
4464 if (!p.type.isintegral())
4466 error(fs.loc, "foreach: key cannot be of non-integral type `%s`",
4467 p.type.toChars());
4468 return returnEarly();
4471 const length = te ? te.exps.length : tuple.arguments.length;
4472 IntRange dimrange = IntRange(SignExtendedNumber(length))._cast(Type.tsize_t);
4473 // https://issues.dlang.org/show_bug.cgi?id=12504
4474 dimrange.imax = SignExtendedNumber(dimrange.imax.value-1);
4475 if (!IntRange.fromType(p.type).contains(dimrange))
4477 error(fs.loc, "index type `%s` cannot cover index range 0..%llu",
4478 p.type.toChars(), cast(ulong)length);
4479 return returnEarly();
4481 Initializer ie = new ExpInitializer(Loc.initial, new IntegerExp(k));
4482 auto var = new VarDeclaration(loc, p.type, p.ident, ie);
4483 var.storage_class |= STC.foreach_ | STC.manifest;
4484 if (isStatic)
4485 var.storage_class |= STC.local;
4487 if (isDecl)
4488 decls.push(var);
4489 else
4490 stmts.push(new ExpStatement(loc, var));
4492 p = (*fs.parameters)[1]; // value
4494 /***********************
4495 * Declares a unrolled `foreach` loop variable or a `static foreach` variable.
4497 * Params:
4498 * storageClass = The storage class of the variable.
4499 * type = The declared type of the variable.
4500 * ident = The name of the variable.
4501 * e = The initializer of the variable (i.e. the current element of the looped over aggregate).
4502 * t = The type of the initializer.
4503 * Returns:
4504 * `true` iff the declaration was successful.
4506 bool declareVariable(StorageClass storageClass, Type type, Identifier ident, Expression e, Type t)
4508 if (storageClass & (STC.out_ | STC.lazy_) ||
4509 storageClass & STC.ref_ && !te)
4511 error(fs.loc, "no storage class for value `%s`", ident.toChars());
4512 return false;
4514 Declaration var;
4515 if (e)
4517 Type tb = e.type.toBasetype();
4518 Dsymbol ds = null;
4519 if (!(storageClass & STC.manifest))
4521 if (isStatic || tb.ty == Tfunction || storageClass & STC.alias_)
4523 if (auto ve = e.isVarExp())
4524 ds = ve.var;
4525 else if (auto dve = e.isDotVarExp())
4526 ds = dve.var;
4528 if (auto te = e.isTemplateExp())
4529 ds = te.td;
4530 else if (auto se = e.isScopeExp())
4531 ds = se.sds;
4532 else if (auto fe = e.isFuncExp())
4533 ds = fe.td ? fe.td : fe.fd;
4534 else if (auto oe = e.isOverExp())
4535 ds = oe.vars;
4537 else if (storageClass & STC.alias_)
4539 error(fs.loc, "`foreach` loop variable cannot be both `enum` and `alias`");
4540 return false;
4543 if (ds)
4545 var = new AliasDeclaration(loc, ident, ds);
4546 if (storageClass & STC.ref_)
4548 error(fs.loc, "symbol `%s` cannot be `ref`", ds.toChars());
4549 return false;
4551 if (paramtype)
4553 error(fs.loc, "cannot specify element type for symbol `%s`", ds.toChars());
4554 return false;
4557 else if (e.op == EXP.type)
4559 var = new AliasDeclaration(loc, ident, e.type);
4560 if (paramtype)
4562 error(fs.loc, "cannot specify element type for type `%s`", e.type.toChars());
4563 return false;
4566 else
4568 e = resolveProperties(sc, e);
4569 Initializer ie = new ExpInitializer(Loc.initial, e);
4570 auto v = new VarDeclaration(loc, type, ident, ie, storageClass);
4571 v.storage_class |= STC.foreach_;
4572 if (storageClass & STC.ref_)
4573 v.storage_class |= STC.ref_;
4574 if (isStatic || storageClass&STC.manifest || e.isConst() ||
4575 e.op == EXP.string_ ||
4576 e.op == EXP.structLiteral ||
4577 e.op == EXP.arrayLiteral)
4579 if (v.storage_class & STC.ref_)
4581 if (!isStatic)
4583 error(fs.loc, "constant value `%s` cannot be `ref`", toChars(ie));
4585 else
4587 if (!needExpansion)
4589 error(fs.loc, "constant value `%s` cannot be `ref`", toChars(ie));
4591 else
4593 error(fs.loc, "constant value `%s` cannot be `ref`", ident.toChars());
4596 return false;
4598 else
4599 v.storage_class |= STC.manifest;
4601 var = v;
4604 else
4606 var = new AliasDeclaration(loc, ident, t);
4607 if (paramtype)
4609 error(fs.loc, "cannot specify element type for symbol `%s`", fs.toChars());
4610 return false;
4613 if (isStatic)
4615 var.storage_class |= STC.local;
4618 if (isDecl)
4619 decls.push(var);
4620 else
4621 stmts.push(new ExpStatement(loc, var));
4622 return true;
4625 if (!isStatic)
4627 // Declare value
4628 if (!declareVariable(p.storageClass, p.type, p.ident, e, t))
4630 return returnEarly();
4633 else
4635 if (!needExpansion)
4637 // Declare value
4638 if (!declareVariable(p.storageClass, p.type, p.ident, e, t))
4640 return returnEarly();
4643 else
4644 { // expand tuples into multiple `static foreach` variables.
4645 assert(e && !t);
4646 auto ident = Identifier.generateId("__value");
4647 declareVariable(0, e.type, ident, e, null);
4648 import dmd.cond: StaticForeach;
4649 auto field = Identifier.idPool(StaticForeach.tupleFieldName.ptr,StaticForeach.tupleFieldName.length);
4650 Expression access = new DotIdExp(loc, e, field);
4651 access = expressionSemantic(access, sc);
4652 access = access.optimize(WANTvalue);
4653 if (!tuple) return returnEarly();
4654 //printf("%s\n",tuple.toChars());
4655 foreach (l; 0 .. dim)
4657 auto cp = (*fs.parameters)[l];
4658 Expression init_ = new IndexExp(loc, access, new IntegerExp(loc, l, Type.tsize_t));
4659 init_ = init_.expressionSemantic(sc);
4660 assert(init_.type);
4661 declareVariable(p.storageClass, init_.type, cp.ident, init_, null);
4666 Statement s;
4667 Dsymbol d;
4668 if (isDecl)
4669 decls.append(Dsymbol.arraySyntaxCopy(dbody));
4670 else
4672 stmts.push(fs._body.syntaxCopy());
4673 s = new CompoundStatement(loc, stmts);
4676 if (!isStatic)
4678 s = new ScopeStatement(loc, s, fs.endloc);
4680 else if (isDecl)
4682 import dmd.attrib: ForwardingAttribDeclaration;
4683 d = new ForwardingAttribDeclaration(decls);
4685 else
4687 s = new ForwardingStatement(loc, s);
4690 if (isDecl)
4691 declarations.push(d);
4692 else
4693 statements.push(s);
4696 if (!isStatic)
4698 Statement res = new UnrolledLoopStatement(loc, statements);
4699 if (LabelStatement ls = checkLabeledLoop(sc, fs))
4700 ls.gotoTarget = res;
4701 if (te && te.e0)
4702 res = new CompoundStatement(loc, new ExpStatement(te.e0.loc, te.e0), res);
4703 result.statement = res;
4705 else if (isDecl)
4706 result.decl = declarations;
4707 else
4708 result.statement = new CompoundStatement(loc, statements);
4710 return result;
4713 /*********************************
4714 * Flatten out the scope by presenting `statement`
4715 * as an array of statements.
4716 * Params:
4717 * statement = the statement to flatten
4718 * sc = context
4719 * Returns:
4720 * The array of `Statements`, or `null` if no flattening necessary
4722 private Statements* flatten(Statement statement, Scope* sc)
4724 static auto errorStatements()
4726 auto a = new Statements();
4727 a.push(new ErrorStatement());
4728 return a;
4732 /*compound and expression statements have classes that inherit from them with the same
4733 *flattening behavior, so the isXXX methods won't work
4735 switch(statement.stmt)
4737 case STMT.Compound:
4738 case STMT.CompoundDeclaration:
4739 return (cast(CompoundStatement)statement).statements;
4741 case STMT.Exp:
4742 case STMT.DtorExp:
4743 auto es = cast(ExpStatement)statement;
4744 /* https://issues.dlang.org/show_bug.cgi?id=14243
4745 * expand template mixin in statement scope
4746 * to handle variable destructors.
4748 if (!es.exp || !es.exp.isDeclarationExp())
4749 return null;
4751 Dsymbol d = es.exp.isDeclarationExp().declaration;
4752 auto tm = d.isTemplateMixin();
4753 if (!tm)
4754 return null;
4756 Expression e = es.exp.expressionSemantic(sc);
4757 if (e.op == EXP.error || tm.errors)
4758 return errorStatements();
4759 assert(tm.members);
4761 Statement s = toStatement(tm);
4762 version (none)
4764 OutBuffer buf;
4765 buf.doindent = 1;
4766 HdrGenState hgs;
4767 hgs.hdrgen = true;
4768 toCBuffer(s, &buf, &hgs);
4769 printf("tm ==> s = %s\n", buf.peekChars());
4771 auto a = new Statements();
4772 a.push(s);
4773 return a;
4775 case STMT.Forwarding:
4776 /***********************
4777 * ForwardingStatements are distributed over the flattened
4778 * sequence of statements. This prevents flattening to be
4779 * "blocked" by a ForwardingStatement and is necessary, for
4780 * example, to support generating scope guards with `static
4781 * foreach`:
4783 * static foreach(i; 0 .. 10) scope(exit) writeln(i);
4784 * writeln("this is printed first");
4785 * // then, it prints 10, 9, 8, 7, ...
4787 auto fs = statement.isForwardingStatement();
4788 if (!fs.statement)
4790 return null;
4792 sc = sc.push(fs.sym);
4793 auto a = fs.statement.flatten(sc);
4794 sc = sc.pop();
4795 if (!a)
4797 return a;
4799 auto b = new Statements(a.length);
4800 foreach (i, s; *a)
4802 (*b)[i] = s ? new ForwardingStatement(s.loc, fs.sym, s) : null;
4804 return b;
4806 case STMT.Conditional:
4807 auto cs = statement.isConditionalStatement();
4808 Statement s;
4810 //printf("ConditionalStatement::flatten()\n");
4811 if (cs.condition.include(sc))
4813 DebugCondition dc = cs.condition.isDebugCondition();
4814 if (dc)
4816 s = new DebugStatement(cs.loc, cs.ifbody);
4817 debugThrowWalker(cs.ifbody);
4819 else
4820 s = cs.ifbody;
4822 else
4823 s = cs.elsebody;
4825 auto a = new Statements();
4826 a.push(s);
4827 return a;
4829 case STMT.StaticForeach:
4830 auto sfs = statement.isStaticForeachStatement();
4831 sfs.sfe.prepare(sc);
4832 if (sfs.sfe.ready())
4834 Statement s = makeTupleForeach(sc, true, false, sfs.sfe.aggrfe, null, sfs.sfe.needExpansion).statement;
4835 auto result = s.flatten(sc);
4836 if (result)
4838 return result;
4840 result = new Statements();
4841 result.push(s);
4842 return result;
4844 else
4845 return errorStatements();
4847 case STMT.Debug:
4848 auto ds = statement.isDebugStatement();
4849 Statements* a = ds.statement ? ds.statement.flatten(sc) : null;
4850 if (!a)
4851 return null;
4853 foreach (ref s; *a)
4855 s = new DebugStatement(ds.loc, s);
4857 return a;
4859 case STMT.Label:
4860 auto ls = statement.isLabelStatement();
4861 if (!ls.statement)
4862 return null;
4864 Statements* a = null;
4865 a = ls.statement.flatten(sc);
4866 if (!a)
4867 return null;
4869 if (!a.length)
4871 a.push(new ExpStatement(ls.loc, cast(Expression)null));
4874 // reuse 'this' LabelStatement
4875 ls.statement = (*a)[0];
4876 (*a)[0] = ls;
4877 return a;
4879 case STMT.Mixin:
4880 auto cs = statement.isMixinStatement();
4883 OutBuffer buf;
4884 if (expressionsToString(buf, sc, cs.exps))
4885 return errorStatements();
4887 const errors = global.errors;
4888 const len = buf.length;
4889 buf.writeByte(0);
4890 const str = buf.extractSlice()[0 .. len];
4891 const bool doUnittests = global.params.parsingUnittestsRequired();
4892 auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut);
4893 scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
4894 p.transitionIn = global.params.v.vin;
4895 p.nextToken();
4897 auto a = new Statements();
4898 while (p.token.value != TOK.endOfFile)
4900 Statement s = p.parseStatement(ParseStatementFlags.curlyScope);
4901 if (!s || global.errors != errors)
4902 return errorStatements();
4903 a.push(s);
4905 return a;
4906 default:
4907 return null;
4911 /***********************************************************
4912 * Convert TemplateMixin members (which are Dsymbols) to Statements.
4913 * Params:
4914 * s = the symbol to convert to a Statement
4915 * Returns:
4916 * s redone as a Statement
4918 private Statement toStatement(Dsymbol s)
4920 Statement result;
4922 if (auto tm = s.isTemplateMixin())
4924 auto a = new Statements();
4925 foreach (m; *tm.members)
4927 if (Statement sx = toStatement(m))
4928 a.push(sx);
4930 result = new CompoundStatement(tm.loc, a);
4932 else if (s.isVarDeclaration() ||
4933 s.isAggregateDeclaration() ||
4934 s.isFuncDeclaration() ||
4935 s.isEnumDeclaration() ||
4936 s.isAliasDeclaration() ||
4937 s.isTemplateDeclaration())
4939 /* Perhaps replace the above with isScopeDsymbol() || isDeclaration()
4941 /* An actual declaration symbol will be converted to DeclarationExp
4942 * with ExpStatement.
4944 auto de = new DeclarationExp(s.loc, s);
4945 de.type = Type.tvoid; // avoid repeated semantic
4946 result = new ExpStatement(s.loc, de);
4948 else if (auto d = s.isAttribDeclaration())
4950 /* All attributes have been already picked by the semantic analysis of
4951 * 'bottom' declarations (function, struct, class, etc).
4952 * So we don't have to copy them.
4954 if (Dsymbols* a = d.include(null))
4956 auto statements = new Statements();
4957 foreach (sx; *a)
4959 statements.push(toStatement(sx));
4961 result = new CompoundStatement(d.loc, statements);
4964 else if (s.isStaticAssert() ||
4965 s.isImport())
4967 /* Ignore as they are not Statements
4970 else
4972 .error(Loc.initial, "internal compiler error: cannot mixin %s `%s`\n", s.kind(), s.toChars());
4973 result = new ErrorStatement();
4976 return result;
4980 Marks all occurring ThrowStatements as internalThrows.
4981 This is intended to be called from a DebugStatement as it allows
4982 to mark all its nodes as nothrow.
4984 Params:
4985 s = AST Node to traverse
4987 private void debugThrowWalker(Statement s)
4990 extern(C++) final class DebugWalker : SemanticTimeTransitiveVisitor
4992 alias visit = SemanticTimeTransitiveVisitor.visit;
4993 public:
4995 override void visit(ThrowStatement s)
4997 s.internalThrow = true;
5000 override void visit(CallExp s)
5002 s.inDebugStatement = true;
5006 scope walker = new DebugWalker();
5007 s.accept(walker);
5010 /***********************************************************
5011 * Evaluate and print a `pragma(msg, args)`
5013 * Params:
5014 * loc = location for error messages
5015 * sc = scope for argument interpretation
5016 * args = expressions to print
5017 * Returns:
5018 * `true` on success
5020 bool pragmaMsgSemantic(Loc loc, Scope* sc, Expressions* args)
5022 if (!args)
5023 return true;
5024 foreach (arg; *args)
5026 sc = sc.startCTFE();
5027 auto e = arg.expressionSemantic(sc);
5028 e = resolveProperties(sc, e);
5029 sc = sc.endCTFE();
5031 // pragma(msg) is allowed to contain types as well as expressions
5032 e = ctfeInterpretForPragmaMsg(e);
5033 if (e.op == EXP.error)
5035 errorSupplemental(loc, "while evaluating `pragma(msg, %s)`", arg.toChars());
5036 return false;
5038 if (auto se = e.toStringExp())
5040 const slice = se.toUTF8(sc).peekString();
5041 fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr);
5043 else
5044 fprintf(stderr, "%s", e.toChars());
5046 fprintf(stderr, "\n");
5047 return true;
5050 /***********************************************************
5051 * Evaluate `pragma(startAddress, func)` and store the resolved symbol in `args`
5053 * Params:
5054 * loc = location for error messages
5055 * sc = scope for argument interpretation
5056 * args = pragma arguments
5057 * Returns:
5058 * `true` on success
5060 bool pragmaStartAddressSemantic(Loc loc, Scope* sc, Expressions* args)
5062 if (!args || args.length != 1)
5064 .error(loc, "function name expected for start address");
5065 return false;
5067 else
5069 /* https://issues.dlang.org/show_bug.cgi?id=11980
5070 * resolveProperties and ctfeInterpret call are not necessary.
5072 Expression e = (*args)[0];
5073 sc = sc.startCTFE();
5074 e = e.expressionSemantic(sc);
5075 // e = resolveProperties(sc, e);
5076 sc = sc.endCTFE();
5078 // e = e.ctfeInterpret();
5079 (*args)[0] = e;
5080 Dsymbol sa = getDsymbol(e);
5081 if (!sa || !sa.isFuncDeclaration())
5083 .error(loc, "function name expected for start address, not `%s`", e.toChars());
5084 return false;
5087 return true;
5090 /************************************
5091 * Check for skipped variable declarations.
5092 * Params:
5093 * ss = statement to check
5094 * Returns:
5095 * true if error
5097 private bool checkLabel(SwitchStatement ss)
5100 * Checks the scope of a label for existing variable declaration.
5101 * Params:
5102 * vd = last variable declared before this case/default label
5103 * Returns: `true` if the variables declared in this label would be skipped.
5105 bool checkVar(VarDeclaration vd)
5107 for (auto v = vd; v && v != ss.lastVar; v = v.lastVar)
5109 if (v.isDataseg() || (v.storage_class & (STC.manifest | STC.temp) && vd.ident != Id.withSym) || v._init.isVoidInitializer())
5110 continue;
5111 if (vd.ident == Id.withSym)
5112 error(ss.loc, "`switch` skips declaration of `with` temporary");
5113 else
5114 error(ss.loc, "`switch` skips declaration of variable `%s`", v.toPrettyChars());
5115 errorSupplemental(v.loc, "declared here");
5116 return true;
5118 return false;
5121 enum error = true;
5123 if (ss.sdefault && checkVar(ss.sdefault.lastVar))
5124 return !error; // return error once fully deprecated
5126 foreach (scase; *ss.cases)
5128 if (scase && checkVar(scase.lastVar))
5129 return !error; // return error once fully deprecated
5131 return !error;
5135 /**************
5136 * Check for skipped variable declarations.
5137 * Params:
5138 * gs = statement to check
5139 * Returns: true for error
5141 bool checkLabel(GotoStatement gs)
5143 if (!gs.label.statement)
5144 return true; // error should have been issued for this already
5146 if (gs.label.statement.os != gs.os)
5148 if (gs.os && gs.os.tok == TOK.onScopeFailure && !gs.label.statement.os)
5150 // Jump out from scope(failure) block is allowed.
5152 else
5154 if (gs.label.statement.os)
5155 error(gs.loc, "cannot `goto` in to `%s` block", Token.toChars(gs.label.statement.os.tok));
5156 else
5157 error(gs.loc, "cannot `goto` out of `%s` block", Token.toChars(gs.os.tok));
5158 return true;
5162 if (gs.label.statement.tf != gs.tf)
5164 error(gs.loc, "cannot `goto` in or out of `finally` block");
5165 return true;
5168 if (gs.label.statement.inCtfeBlock && !gs.inCtfeBlock)
5170 error(gs.loc, "cannot `goto` into `if (__ctfe)` block");
5171 return true;
5174 Statement stbnext;
5175 for (auto stb = gs.tryBody; stb != gs.label.statement.tryBody; stb = stbnext)
5177 if (!stb)
5179 error(gs.loc, "cannot `goto` into `try` block");
5180 return true;
5182 if (auto stf = stb.isTryFinallyStatement())
5183 stbnext = stf.tryBody;
5184 else if (auto stc = stb.isTryCatchStatement())
5185 stbnext = stc.tryBody;
5186 else
5187 assert(0);
5190 VarDeclaration vd = gs.label.statement.lastVar;
5191 if (!vd || vd.isDataseg() || (vd.storage_class & STC.manifest))
5192 return false;
5194 VarDeclaration last = gs.lastVar;
5195 while (last && last != vd)
5196 last = last.lastVar;
5197 if (last == vd)
5199 // All good, the label's scope has no variables
5201 else if (vd.storage_class & STC.exptemp)
5203 // Lifetime ends at end of expression, so no issue with skipping the statement
5205 else
5207 if (vd.ident == Id.withSym)
5208 error(gs.loc, "`goto` skips declaration of `with` temporary");
5209 else
5210 error(gs.loc, "`goto` skips declaration of variable `%s`", vd.toPrettyChars());
5211 errorSupplemental(vd.loc, "declared here");
5212 return true;
5214 return false;