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