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