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