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