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