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