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