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