1 module d.semantic.statement; 2 3 import d.semantic.semantic; 4 5 import d.ast.conditional; 6 import d.ast.expression; 7 import d.ast.statement; 8 9 import source.location; 10 import source.name; 11 12 import d.ir.dscope; 13 import d.ir.expression; 14 import d.ir.instruction; 15 import d.ir.symbol; 16 import d.ir.type; 17 18 struct StatementVisitor { 19 private: 20 SemanticPass pass; 21 alias pass this; 22 23 Body fbody; 24 25 BasicBlockRef currentBlockRef; 26 27 ref inout(BasicBlock) currentBlock() inout { 28 return fbody[currentBlockRef]; 29 } 30 31 bool doesTerminate(BasicBlockRef b) const { 32 return !b || fbody[b].terminate; 33 } 34 35 @property terminate() const { 36 return doesTerminate(currentBlockRef); 37 } 38 39 bool allowUnreachable; 40 BreakKind breakKind; 41 42 UnwindInfo[] unwindActions; 43 44 VariableExpression retval; 45 46 struct Label { 47 BasicBlockRef block; 48 uint level; 49 50 alias block this; 51 } 52 53 Label breakLabel; 54 Label continueLabel; 55 56 Label[Name] labels; 57 58 // Forward goto can only be resolved when the label is reached. 59 struct UnresolvedGoto { 60 UnwindInfo[] unwind; 61 BasicBlockRef block; 62 } 63 64 UnresolvedGoto[][Name] inFlightGotos; 65 66 CaseEntry[] cases; 67 68 // Mechanism to detect jump over declaration. 69 // XXX: Kind of clunky, but will do for now. 70 Variable[] varStack; 71 Variable[] switchStack; 72 Variable[][Name] labelStacks; 73 Variable[][][Name] inFlightGotosStacks; 74 75 public: 76 this(SemanticPass pass) { 77 this.pass = pass; 78 } 79 80 void getBody(Function f, BlockStatement b) { 81 auto oldScope = currentScope; 82 scope(exit) currentScope = oldScope; 83 84 scope(failure) { 85 f.fbody = fbody; 86 f.dump(context); 87 } 88 89 currentScope = f; 90 auto entry = startNewBranch(BuiltinName!"entry"); 91 92 flatten(b); 93 f.fbody = fbody; 94 95 auto rt = returnType.getType(); 96 // TODO: Handle auto return by specifying it to this visitor 97 // instead of deducing it in dubious ways. 98 if (rt.kind == TypeKind.Builtin && 99 rt.qualifier == TypeQualifier.Mutable && 100 rt.builtin == BuiltinType.None) { 101 returnType = Type.get(BuiltinType.Void) 102 .getParamType(ParamKind.Regular); 103 } 104 105 if (!terminate) { 106 if (returnType.kind == TypeKind.Builtin && 107 returnType.builtin == BuiltinType.Void) { 108 currentBlock.ret(b.location); 109 } else { 110 import source.exception; 111 throw new CompileException(f.location, "Must return"); 112 } 113 } 114 } 115 116 void visit(Statement s) { 117 if (s is null) { 118 return; 119 } 120 121 if (!terminate) { 122 goto Dispatch; 123 } 124 125 if (auto b = cast(BlockStatement) s) { 126 return visit(b); 127 } 128 129 if (auto c = cast(CaseStatement) s) { 130 return visit(c); 131 } 132 133 if (auto l = cast(LabeledStatement) s) { 134 return visit(l); 135 } 136 137 if (allowUnreachable) { 138 startNewBranch(BuiltinName!"unreachable"); 139 } else { 140 import source.exception; 141 throw new CompileException(s.location, "Unreachable statement"); 142 } 143 144 Dispatch: 145 allowUnreachable = false; 146 this.dispatch(s); 147 } 148 149 private: 150 BasicBlockRef flatten(BlockStatement b) { 151 return buildBlock(b.location, b.statements); 152 } 153 154 BasicBlockRef getUnwindBlock() { 155 foreach_reverse (ref b; unwindActions) { 156 if (!b.isUnwind()) { 157 continue; 158 } 159 160 if (!b.unwindBlock) { 161 b.unwindBlock = fbody.newBasicBlock(BuiltinName!"unwind"); 162 } 163 164 return b.unwindBlock; 165 } 166 167 return BasicBlockRef(); 168 } 169 170 BasicBlockRef makeNewBranch(Name name) { 171 return fbody.newBasicBlock(name, getUnwindBlock()); 172 } 173 174 BasicBlockRef startNewBranch(Name name) { 175 return currentBlockRef = makeNewBranch(name); 176 } 177 178 BasicBlockRef maybeBranchToNewBlock(Location location, Name name) { 179 if (currentBlockRef && currentBlock.empty) { 180 auto unwindBlock = getUnwindBlock(); 181 currentBlock.landingpad = unwindBlock; 182 return currentBlockRef; 183 } 184 185 return maybeBranchTo(location, currentBlockRef, startNewBranch(name)); 186 } 187 188 BasicBlockRef maybeBranchTo(Location location, BasicBlockRef dst) { 189 return maybeBranchTo(location, currentBlockRef, dst); 190 } 191 192 BasicBlockRef maybeBranchTo( 193 Location location, 194 BasicBlockRef src, 195 BasicBlockRef dst, 196 ) { 197 if (!doesTerminate(src)) { 198 fbody[src].branch(location, dst); 199 } 200 201 return dst; 202 } 203 204 BasicBlockRef buildBlock(U...)(Location location, U args) { 205 auto oldScope = currentScope; 206 auto oldVarStack = varStack; 207 scope(exit) { 208 currentScope = oldScope; 209 varStack = oldVarStack; 210 } 211 212 currentScope = new NestedScope(currentScope); 213 214 auto unwindLevel = unwindActions.length; 215 scope(success) unwindTo(unwindLevel); 216 217 process(args); 218 219 return currentBlockRef; 220 } 221 222 void process(Statement[] statements) { 223 foreach(s; statements) { 224 visit(s); 225 } 226 } 227 228 void process(Statement s) { 229 visit(s); 230 } 231 232 BasicBlockRef autoBlock(Statement s) { 233 if (auto b = cast(BlockStatement) s) { 234 return flatten(b); 235 } 236 237 return buildBlock(s.location, s); 238 } 239 240 void alloca(Variable v) { 241 currentBlock.alloca(v.location, v); 242 varStack ~= v; 243 244 auto t = v.type.getCanonical(); 245 if (t.kind != TypeKind.Struct || t.dstruct.isPod) { 246 return; 247 } 248 249 unwindActions ~= UnwindInfo(v); 250 maybeBranchToNewBlock(v.location, BuiltinName!""); 251 } 252 253 Expression check(Expression e) { 254 auto t = e.type; 255 if (t.kind == TypeKind.Error) { 256 import source.exception; 257 throw new CompileException(t.error.location, t.error.message); 258 } 259 260 // FIXME: Update flags. 261 return e; 262 } 263 264 auto buildExpression(AstExpression expr) { 265 import d.semantic.expression; 266 return check(ExpressionVisitor(pass).visit(expr)); 267 } 268 269 auto buildExpression(AstExpression expr, Type type) { 270 import d.semantic.caster, d.semantic.expression; 271 return check(buildExplicitCast( 272 pass, 273 expr.location, 274 type, 275 ExpressionVisitor(pass).visit(expr), 276 )); 277 } 278 279 auto buildCondition(AstExpression expr) { 280 return buildExpression(expr, Type.get(BuiltinType.Bool)); 281 } 282 283 auto buildString(AstExpression expr) { 284 return buildExpression( 285 expr, 286 Type.get(BuiltinType.Char).getSlice(TypeQualifier.Immutable), 287 ); 288 } 289 290 public: 291 void visit(BlockStatement b) { 292 flatten(b); 293 } 294 295 void visit(ExpressionStatement s) { 296 currentBlock.eval(s.location, buildExpression(s.expression)); 297 } 298 299 void visit(DeclarationStatement s) { 300 import d.semantic.declaration; 301 auto syms = DeclarationVisitor(pass).flatten(s.declaration); 302 303 scheduler.require(syms); 304 305 foreach(sym; syms) { 306 if (auto v = cast(Variable) sym) { 307 alloca(v); 308 } else if (cast(Aggregate) sym) { 309 // FIXME: We should get rid of this. 310 currentBlock.declare(sym.location, sym); 311 } 312 } 313 } 314 315 void visit(IdentifierStarNameStatement s) { 316 import d.semantic.identifier; 317 IdentifierResolver(pass) 318 .build(s.identifier) 319 .apply!(delegate void(identified) { 320 alias T = typeof(identified); 321 static if (is(T : Expression)) { 322 assert(0, "expression identifier * identifier are not implemented."); 323 } else static if (is(T : Type)) { 324 auto t = identified.getPointer(); 325 326 import d.semantic.expression; 327 import d.semantic.defaultinitializer : InitBuilder; 328 auto value = s.value 329 ? ExpressionVisitor(pass).visit(s.value) 330 : InitBuilder(pass, s.location).visit(t); 331 332 import d.semantic.caster; 333 auto v = new Variable( 334 s.location, 335 t.getParamType(ParamKind.Regular), 336 s.name, 337 buildImplicitCast(pass, s.location, t, value), 338 ); 339 340 v.step = Step.Processed; 341 pass.currentScope.addSymbol(v); 342 343 currentBlock.alloca(s.location, v); 344 } else { 345 assert(0, "Was not expecting " ~ T.stringof); 346 } 347 })(); 348 } 349 350 void visit(IfStatement s) { 351 auto ifBlock = currentBlockRef; 352 353 auto ifTrue = startNewBranch(BuiltinName!"then"); 354 auto mergeTrue = autoBlock(s.then); 355 356 BasicBlockRef ifFalse, mergeBlock; 357 if (s.elseStatement) { 358 ifFalse = startNewBranch(BuiltinName!"else"); 359 autoBlock(s.elseStatement); 360 if (!terminate) { 361 mergeBlock = maybeBranchToNewBlock( 362 s.elseStatement.location, 363 BuiltinName!"endif", 364 ); 365 } 366 } else { 367 ifFalse = mergeBlock = startNewBranch(BuiltinName!"endif"); 368 } 369 370 if (!doesTerminate(mergeTrue)) { 371 if (!mergeBlock) { 372 mergeBlock = startNewBranch(BuiltinName!"endif"); 373 } 374 375 maybeBranchTo(s.then.location, mergeTrue, mergeBlock); 376 } 377 378 fbody[ifBlock].branch( 379 s.location, 380 buildCondition(s.condition), 381 ifTrue, 382 ifFalse, 383 ); 384 } 385 386 void genLoop( 387 Location location, 388 Expression condition, 389 Statement statement, 390 Expression increment, 391 Variable element = null, 392 bool skipFirstCond = false, 393 ) { 394 auto oldBreakKind = breakKind; 395 auto oldBreakLabel = breakLabel; 396 auto oldContinueLabel = continueLabel; 397 scope(exit) { 398 currentBlockRef = breakLabel.block; 399 400 breakKind = oldBreakKind; 401 breakLabel = oldBreakLabel; 402 continueLabel = oldContinueLabel; 403 } 404 405 breakKind = BreakKind.Loop; 406 407 auto entryBlock = currentBlockRef; 408 auto incBlock = startNewBranch(BuiltinName!"loop.continue"); 409 if (increment) { 410 currentBlock.eval(increment.location, increment); 411 } 412 413 auto testBlock = maybeBranchToNewBlock(location, BuiltinName!"loop.test"); 414 auto bodyBlock = startNewBranch(BuiltinName!"loop.body"); 415 416 continueLabel = Label(incBlock, cast(uint) unwindActions.length); 417 breakLabel = Label( 418 BasicBlockRef.init, 419 cast(uint) unwindActions.length, 420 ); 421 422 if (element !is null) { 423 currentBlock.alloca(element.location, element); 424 } 425 426 autoBlock(statement); 427 maybeBranchTo(location, incBlock); 428 429 fbody[entryBlock].branch(location, skipFirstCond ? bodyBlock : testBlock); 430 if (condition) { 431 auto breakLabel = getBreakLabel(location); 432 fbody[testBlock].branch( 433 location, 434 condition, 435 bodyBlock, 436 breakLabel.block, 437 ); 438 } else { 439 fbody[testBlock].branch(location, bodyBlock); 440 } 441 } 442 443 void genLoop( 444 Location location, 445 AstExpression condition, 446 Statement statement, 447 AstExpression increment = null, 448 Variable element = null, 449 bool skipFirstCond = false, 450 ) { 451 Expression cond, inc; 452 453 if (condition) { 454 cond = buildCondition(condition); 455 } 456 457 if (increment) { 458 inc = buildExpression(increment); 459 } 460 461 genLoop(location, cond, statement, inc, element, skipFirstCond); 462 } 463 464 void visit(WhileStatement w) { 465 genLoop(w.location, w.condition, w.statement); 466 } 467 468 void visit(DoWhileStatement w) { 469 genLoop(w.location, w.condition, w.statement, null, null, true); 470 } 471 472 void visit(ForStatement f) { 473 buildBlock(f.location, f); 474 } 475 476 void process(ForStatement f) { 477 if (f.initialize) { 478 visit(f.initialize); 479 } 480 481 genLoop(f.location, f.condition, f.statement, f.increment); 482 } 483 484 void visit(ForeachStatement f) { 485 buildBlock(f.location, f); 486 } 487 488 void process(ForeachStatement f) { 489 assert(!f.reverse, "foreach_reverse not supported at this point."); 490 491 import d.semantic.expression; 492 auto iterated = ExpressionVisitor(pass).visit(f.iterated); 493 494 import source.name, d.semantic.identifier; 495 auto length = IdentifierResolver(pass) 496 .buildIn(iterated.location, iterated, BuiltinName!"length") 497 .apply!(delegate Expression(e) { 498 static if (is(typeof(e) : Expression)) { 499 return e; 500 } else { 501 import d.ir.error; 502 return new CompileError( 503 iterated.location, 504 typeid(e).toString() ~ " is not a valid length", 505 ).expression; 506 } 507 })(); 508 509 Variable idx; 510 511 auto loc = f.location; 512 switch (f.tupleElements.length) { 513 case 1: 514 import d.semantic.defaultinitializer; 515 idx = new Variable( 516 loc, 517 length.type, 518 BuiltinName!"", 519 InitBuilder(pass, loc).visit(length.type), 520 ); 521 522 idx.step = Step.Processed; 523 break; 524 525 case 2: 526 auto idxDecl = f.tupleElements[0]; 527 if (idxDecl.type.paramKind != ParamKind.Regular) { 528 assert(0, "index can't be ref"); 529 } 530 531 import d.semantic.type; 532 auto t = idxDecl.type.getType().isAuto 533 ? length.type 534 : TypeVisitor(pass).visit(idxDecl.type.getType()); 535 536 auto idxLoc = idxDecl.location; 537 538 import d.semantic.defaultinitializer; 539 idx = new Variable( 540 idxLoc, 541 t, 542 idxDecl.name, 543 InitBuilder(pass, idxLoc).visit(t), 544 ); 545 546 idx.step = Step.Processed; 547 currentScope.addSymbol(idx); 548 549 break; 550 551 default: 552 assert(0, "Wrong number of elements"); 553 } 554 555 assert(idx); 556 currentBlock.alloca(idx.location, idx); 557 558 auto idxExpr = new VariableExpression(idx.location, idx); 559 auto increment = build!UnaryExpression( 560 loc, 561 idx.type, 562 UnaryOp.PreInc, 563 idxExpr, 564 ); 565 566 import d.semantic.caster; 567 length = buildImplicitCast(pass, idx.location, idx.type, length); 568 auto condition = build!ICmpExpression(loc, ICmpOp.Less, idxExpr, length); 569 570 auto iType = iterated.type.getCanonical(); 571 assert(iType.hasElement, "Only array and slice are supported for now."); 572 573 Type et = iType.element; 574 575 auto eDecl = f.tupleElements[$ - 1]; 576 auto eLoc = eDecl.location; 577 578 import d.semantic.expression; 579 auto eVal = ExpressionVisitor(pass).getIndex(eLoc, iterated, idxExpr); 580 auto eType = eVal.type.getParamType(eDecl.type.paramKind); 581 582 if (!eDecl.type.getType().isAuto) { 583 import d.semantic.type; 584 eType = TypeVisitor(pass).visit(eDecl.type); 585 586 import d.semantic.caster; 587 eVal = buildImplicitCast(pass, eLoc, eType.getType(), eVal); 588 } 589 590 auto element = new Variable(eLoc, eType, eDecl.name, eVal); 591 element.step = Step.Processed; 592 currentScope.addSymbol(element); 593 594 genLoop(loc, condition, f.statement, increment, element); 595 } 596 597 void visit(ForeachRangeStatement f) { 598 buildBlock(f.location, f); 599 } 600 601 void process(ForeachRangeStatement f) { 602 import d.semantic.expression; 603 auto start = ExpressionVisitor(pass).visit(f.start); 604 auto stop = ExpressionVisitor(pass).visit(f.stop); 605 606 assert(f.tupleElements.length == 1, "Wrong number of elements"); 607 auto iDecl = f.tupleElements[0]; 608 609 auto loc = f.location; 610 611 import d.semantic.type, d.semantic.typepromotion; 612 auto type = iDecl.type.getType().isAuto 613 ? getPromotedType(pass, loc, start.type, stop.type) 614 : TypeVisitor(pass).visit(iDecl.type).getType(); 615 616 import d.semantic.caster; 617 start = buildImplicitCast(pass, start.location, type, start); 618 stop = buildImplicitCast(pass, stop.location, type, stop); 619 620 if (f.reverse) { 621 auto tmp = start; 622 start = stop; 623 stop = tmp; 624 } 625 626 auto idx = new Variable( 627 iDecl.location, 628 type.getParamType(iDecl.type.paramKind), 629 iDecl.name, 630 start, 631 ); 632 633 idx.step = Step.Processed; 634 currentScope.addSymbol(idx); 635 currentBlock.alloca(idx.location, idx); 636 637 Expression idxExpr = new VariableExpression(idx.location, idx); 638 Expression increment, condition; 639 640 if (f.reverse) { 641 // for(...; idx-- > stop; idx) 642 condition = build!ICmpExpression( 643 loc, 644 ICmpOp.Greater, 645 build!UnaryExpression(loc, type, UnaryOp.PostDec, idxExpr), 646 stop, 647 ); 648 } else { 649 // for(...; idx < stop; idx++) 650 condition = build!ICmpExpression(loc, ICmpOp.Less, idxExpr, stop); 651 increment = build!UnaryExpression(loc, type, UnaryOp.PreInc, idxExpr); 652 } 653 654 genLoop(loc, condition, f.statement, increment); 655 } 656 657 void visit(ReturnStatement s) { 658 // TODO: precompute autotype instead of managing it here. 659 auto rt = returnType.getType(); 660 auto isAutoReturn = 661 rt.kind == TypeKind.Builtin && 662 rt.qualifier == TypeQualifier.Mutable && 663 rt.builtin == BuiltinType.None; 664 665 // return; has no value. 666 if (s.value is null) { 667 if (isAutoReturn) { 668 returnType = Type.get(BuiltinType.Void) 669 .getParamType(ParamKind.Regular); 670 } 671 672 closeBlockTo(0); 673 currentBlock.ret(s.location); 674 return; 675 } 676 677 auto value = buildExpression(s.value); 678 679 // TODO: Handle auto return by specifying it to this visitor 680 // instead of deducing it in dubious ways. 681 if (isAutoReturn) { 682 // TODO: auto ref return. 683 returnType = value.type.getParamType(ParamKind.Regular); 684 } else { 685 import d.semantic.caster; 686 value = buildImplicitCast(pass, s.location, returnType.getType(), value); 687 if (returnType.isRef) { 688 if (!value.isLvalue) { 689 import source.exception; 690 throw new CompileException(s.location, "Cannot ref return lvalues"); 691 } 692 693 value = build!UnaryExpression( 694 s.location, 695 value.type.getPointer(), 696 UnaryOp.AddressOf, 697 value, 698 ); 699 } 700 } 701 702 // If unwind work is needed, store the result in a temporary. 703 if (unwindActions.length) { 704 auto location = value.location; 705 if (retval is null) { 706 auto v = new Variable( 707 location, 708 value.type, 709 BuiltinName!"return", 710 new VoidInitializer(location, value.type), 711 ); 712 713 v.step = Step.Processed; 714 retval = new VariableExpression(location, v); 715 } 716 717 currentBlock.eval(location, check(build!BinaryExpression( 718 location, 719 retval.type, 720 BinaryOp.Assign, 721 retval, 722 value, 723 ))); 724 725 value = retval; 726 } 727 728 closeBlockTo(0); 729 if (!terminate) { 730 currentBlock.ret(s.location, check(value)); 731 } 732 } 733 734 private BasicBlockRef unwindAndBranch(Location location, Label l) in { 735 assert(l, "Invalid label"); 736 } do { 737 closeBlockTo(l.level); 738 return maybeBranchTo(location, l.block); 739 } 740 741 Label getBreakLabel(Location location) { 742 if (breakLabel) { 743 return breakLabel; 744 } 745 746 Name name; 747 final switch(breakKind) with(BreakKind) { 748 case None: 749 import source.exception; 750 throw new CompileException( 751 location, 752 "Cannot break outside of switches and loops", 753 ); 754 755 case Loop: 756 name = BuiltinName!"loop.exit"; 757 break; 758 759 case Switch: 760 name = BuiltinName!"endswitch"; 761 break; 762 } 763 764 breakLabel.block = makeNewBranch(name); 765 return breakLabel; 766 } 767 768 void visit(BreakStatement s) { 769 unwindAndBranch(s.location, getBreakLabel(s.location)); 770 } 771 772 void visit(ContinueStatement s) { 773 if (!continueLabel) { 774 import source.exception; 775 throw new CompileException( 776 s.location, 777 "Cannot continue outside of loops", 778 ); 779 } 780 781 unwindAndBranch(s.location, continueLabel); 782 } 783 784 void visit(SwitchStatement s) { 785 auto oldBreakKind = breakKind; 786 auto oldBreakLabel = breakLabel; 787 auto oldCases = cases; 788 auto oldSwitchStack = switchStack; 789 790 Label oldDefault; 791 if (auto dPtr = BuiltinName!"default" in labels) { 792 oldDefault = *dPtr; 793 labels.remove(BuiltinName!"default"); 794 } 795 796 scope(exit) { 797 currentBlockRef = breakLabel.block; 798 799 breakKind = oldBreakKind; 800 breakLabel = oldBreakLabel; 801 cases = oldCases; 802 switchStack = oldSwitchStack; 803 804 if (oldDefault.block) { 805 labels[BuiltinName!"default"] = oldDefault; 806 } 807 } 808 809 switchStack = varStack; 810 breakKind = BreakKind.Switch; 811 812 auto switchBlock = currentBlockRef; 813 814 cases = [CaseEntry.init]; 815 816 breakLabel = Label( 817 BasicBlockRef.init, 818 cast(uint) unwindActions.length, 819 ); 820 821 currentBlockRef = null; 822 visit(s.statement); 823 824 if (!terminate) { 825 unwindAndBranch(s.location, getBreakLabel(s.location)); 826 } 827 828 if (BuiltinName!"case" in inFlightGotos) { 829 import source.exception; 830 throw new CompileException( 831 s.location, 832 "Reached end of switch statement with unresolved goto case;", 833 ); 834 } 835 836 BasicBlockRef defaultBlock; 837 if (auto defaultLabel = BuiltinName!"default" in labels) { 838 defaultBlock = defaultLabel.block; 839 labels.remove(BuiltinName!"default"); 840 } else { 841 import source.exception; 842 throw new CompileException( 843 s.location, 844 "switch statement without a default; use 'final switch' " 845 ~ "or add 'default: assert(0);' or add 'default: break;'", 846 ); 847 } 848 849 auto v = buildExpression(s.expression); 850 851 auto switchTable = cast(SwitchTable*) cases.ptr; 852 switchTable.entryCount = cast(uint) (cases.length - 1); 853 switchTable.defaultBlock = defaultBlock; 854 855 fbody[switchBlock].doSwitch(s.location, v, switchTable); 856 } 857 858 private void fixupGoto(Location location, Name name, Label label) { 859 if (auto ifgsPtr = name in inFlightGotos) { 860 auto ifgs = *ifgsPtr; 861 inFlightGotos.remove(name); 862 863 foreach(ifg; ifgs) { 864 auto oldCurrentBlock = currentBlockRef; 865 auto oldunwindActions = unwindActions; 866 scope(exit) { 867 currentBlockRef = oldCurrentBlock; 868 unwindActions = oldunwindActions; 869 } 870 871 currentBlockRef = ifg.block; 872 unwindActions = ifg.unwind; 873 874 unwindAndBranch(location, label); 875 } 876 877 scope(success) inFlightGotosStacks.remove(name); 878 foreach(igs; inFlightGotosStacks[name]) { 879 // Check that all inflight goto to thta label are valid. 880 import std.algorithm.searching; 881 bool isValid = igs.startsWith(varStack); 882 if (!isValid) { 883 import source.exception; 884 throw new CompileException( 885 location, 886 "Cannot jump over variable initialization.", 887 ); 888 } 889 } 890 } 891 } 892 893 private void setCaseEntry( 894 Location location, 895 string switchError, 896 string fallthroughError, 897 ) { 898 scope(success) { 899 varStack = switchStack; 900 } 901 902 if (cases.length == 0) { 903 import source.exception; 904 throw new CompileException(location, switchError); 905 } 906 907 if (terminate) { 908 return; 909 } 910 911 // Check for case a: case b: 912 // TODO: consider default: case a: 913 if (cases[$ - 1].block != currentBlockRef || !currentBlock.empty) { 914 import source.exception; 915 throw new CompileException(location, fallthroughError); 916 } 917 } 918 919 void visit(CaseStatement s) { 920 setCaseEntry( 921 s.location, 922 "Case statement can only appear within switch statement.", 923 "Fallthrough is disabled, use goto case.", 924 ); 925 926 auto caseBlock = maybeBranchToNewBlock(s.location, BuiltinName!"case"); 927 fixupGoto( 928 s.location, 929 BuiltinName!"case", 930 Label(caseBlock, cast(uint) unwindActions.length), 931 ); 932 933 foreach (e; s.cases) { 934 auto c = cast(uint) evalIntegral(buildExpression(e)); 935 cases ~= CaseEntry(caseBlock, c); 936 } 937 938 visit(s.statement); 939 } 940 941 void visit(LabeledStatement s) { 942 auto name = s.label; 943 if (name == BuiltinName!"default") { 944 setCaseEntry( 945 s.location, 946 "Default statement can only appear within switch statement.", 947 "Fallthrough is disabled, use goto default.", 948 ); 949 } 950 951 if (name in labels) { 952 import source.exception; 953 throw new CompileException(s.location, "Label is already defined"); 954 } 955 956 auto labelBlock = maybeBranchToNewBlock(s.location, name); 957 auto label = Label(labelBlock, cast(uint) unwindActions.length); 958 labels[name] = label; 959 labelStacks[name] = varStack; 960 961 fixupGoto(s.location, name, label); 962 visit(s.statement); 963 } 964 965 void visit(GotoStatement s) { 966 auto name = s.label; 967 if (auto bPtr = name in labelStacks) { 968 auto labelStack = *bPtr; 969 970 import std.algorithm.searching; 971 bool isValid = varStack.startsWith(labelStack); 972 if (!isValid) { 973 import source.exception; 974 throw new CompileException( 975 s.location, 976 "Cannot goto over variable initialization.", 977 ); 978 } 979 } 980 981 if (auto bPtr = name in inFlightGotosStacks) { 982 auto varStacks = *bPtr; 983 varStacks ~= varStack; 984 *bPtr = varStacks; 985 } else { 986 inFlightGotosStacks[name] = [varStack]; 987 } 988 989 if (auto bPtr = name in labels) { 990 unwindAndBranch(s.location, *bPtr); 991 return; 992 } 993 994 auto unresolvedGoto = UnresolvedGoto(unwindActions, currentBlockRef); 995 if (auto bPtr = name in inFlightGotos) { 996 *bPtr ~= unresolvedGoto; 997 } else { 998 inFlightGotos[name] = [unresolvedGoto]; 999 } 1000 1001 currentBlockRef = null; 1002 } 1003 1004 void visit(ScopeStatement s) { 1005 unwindActions ~= UnwindInfo(s.kind, s.statement); 1006 maybeBranchToNewBlock(s.location, BuiltinName!"scope.entry"); 1007 } 1008 1009 void visit(AssertStatement s) { 1010 Expression msg; 1011 if (s.message) { 1012 msg = buildString(s.message); 1013 } 1014 1015 bool isHalt; 1016 if (auto b = cast(BooleanLiteral) s.condition) { 1017 isHalt = !b.value; 1018 } else if (auto i = cast(IntegerLiteral) s.condition) { 1019 isHalt = !i.value; 1020 } else if (auto n = cast(NullLiteral) s.condition) { 1021 isHalt = true; 1022 } 1023 1024 if (isHalt) { 1025 currentBlock.halt(s.location, msg); 1026 return; 1027 } 1028 1029 auto testBlock = currentBlockRef; 1030 auto failBlock = startNewBranch(BuiltinName!"assert.fail"); 1031 currentBlock.halt(s.location, msg); 1032 1033 auto successBlock = startNewBranch(BuiltinName!"assert.success"); 1034 fbody[testBlock].branch( 1035 s.location, 1036 buildCondition(s.condition), 1037 successBlock, 1038 failBlock, 1039 ); 1040 } 1041 1042 void visit(ThrowStatement s) { 1043 currentBlock.doThrow(s.location, buildExpression( 1044 s.value, 1045 Type.get(pass.object.getThrowable()), 1046 )); 1047 } 1048 1049 void visit(TryStatement s) { 1050 auto unwindLevel = unwindActions.length; 1051 scope(success) unwindTo(unwindLevel); 1052 1053 if (s.finallyBlock) { 1054 unwindActions ~= UnwindInfo(ScopeKind.Exit, s.finallyBlock); 1055 } 1056 1057 // No cath blocks, just bypass the whole thing. 1058 if (s.catches.length == 0) { 1059 maybeBranchToNewBlock(s.location, BuiltinName!"try"); 1060 autoBlock(s.statement); 1061 return; 1062 } 1063 1064 auto preCatchLevel = unwindActions.length; 1065 unwindActions ~= UnwindInfo(ScopeKind.Failure, null); 1066 1067 maybeBranchToNewBlock(s.location, BuiltinName!"try"); 1068 autoBlock(s.statement); 1069 1070 auto preUnwindBlock = currentBlockRef; 1071 assert(unwindActions[$ - 1].kind == UnwindKind.Failure); 1072 assert(unwindActions[$ - 1].statement is null); 1073 1074 auto catchSwitchBlock = unwindActions[$ - 1].unwindBlock; 1075 assert(catchSwitchBlock, "No catch switch block"); 1076 1077 unwindActions = unwindActions[0 .. preCatchLevel]; 1078 1079 CatchPad[] catchpads; 1080 catchpads.reserve(s.catches.length + 1); 1081 catchpads ~= CatchPad(); 1082 1083 BasicBlockRef endCatchBlock; 1084 foreach(c; s.catches) { 1085 import d.semantic.identifier; 1086 auto type = IdentifierResolver(pass) 1087 .resolve(c.type) 1088 .apply!(function Class(identified) { 1089 alias T = typeof(identified); 1090 static if (is(T : Symbol)) { 1091 if (auto c = cast(Class) identified) { 1092 return c; 1093 } 1094 } 1095 1096 static if (is(T : Type)) { 1097 assert(0); 1098 } else { 1099 import source.exception; 1100 throw new CompileException( 1101 identified.location, 1102 typeid(identified).toString() ~ " is not a class.", 1103 ); 1104 } 1105 })(); 1106 1107 auto catchBlock = startNewBranch(BuiltinName!"catch"); 1108 catchpads ~= CatchPad(type, catchBlock); 1109 1110 auto mergeCatchBlock = autoBlock(c.statement); 1111 if (terminate) { 1112 continue; 1113 } 1114 1115 if (!endCatchBlock) { 1116 endCatchBlock = startNewBranch(BuiltinName!"endcatch"); 1117 } 1118 1119 maybeBranchTo(c.location, mergeCatchBlock, endCatchBlock); 1120 } 1121 1122 auto catchTable = cast(CatchTable*) catchpads.ptr; 1123 catchTable.catchCount = catchpads.length - 1; 1124 fbody[catchSwitchBlock].doCatch(s.location, catchTable); 1125 1126 if (doesTerminate(preUnwindBlock)) { 1127 currentBlockRef = endCatchBlock; 1128 return; 1129 } 1130 1131 if (!endCatchBlock) { 1132 endCatchBlock = startNewBranch(BuiltinName!"endcatch"); 1133 } 1134 1135 maybeBranchTo(s.location, preUnwindBlock, endCatchBlock); 1136 currentBlockRef = endCatchBlock; 1137 } 1138 1139 void visit(StaticIf!Statement s) { 1140 auto items = evalIntegral(buildCondition(s.condition)) 1141 ? s.items 1142 : s.elseItems; 1143 1144 foreach(item; items) { 1145 visit(item); 1146 } 1147 1148 // Do not error on unrechable statement after static if. 1149 allowUnreachable = true; 1150 } 1151 1152 void visit(StaticAssert!Statement s) { 1153 if (evalIntegral(buildCondition(s.condition))) { 1154 return; 1155 } 1156 1157 import source.exception; 1158 if (s.message is null) { 1159 throw new CompileException(s.location, "assertion failure"); 1160 } 1161 1162 auto msg = evalString(buildString(s.message)); 1163 throw new CompileException(s.location, "assertion failure: " ~ msg); 1164 } 1165 1166 void visit(Mixin!Statement s) { 1167 auto str = evalString(buildString(s.value)) ~ '\0'; 1168 auto base = context.registerMixin(s.location, str); 1169 1170 import source.dlexer; 1171 auto trange = lex(base, context); 1172 1173 import d.parser.base; 1174 trange.match(TokenType.Begin); 1175 while(trange.front.type != TokenType.End) { 1176 import d.parser.statement; 1177 visit(trange.parseStatement()); 1178 } 1179 } 1180 1181 void destroy(Variable v) { 1182 maybeBranchToNewBlock(v.location, BuiltinName!"destroy"); 1183 currentBlock.destroy(v.location, v); 1184 } 1185 1186 /** 1187 * Unwinding facilities 1188 */ 1189 void closeBlockTo(size_t level) { 1190 auto oldunwindActions = unwindActions; 1191 scope(exit) unwindActions = oldunwindActions; 1192 1193 while(unwindActions.length > level) { 1194 if (terminate) { 1195 break; 1196 } 1197 1198 auto b = unwindActions[$ - 1]; 1199 unwindActions = unwindActions[0 .. $ - 1]; 1200 1201 final switch(b.kind) with(UnwindKind) { 1202 case Success, Exit: 1203 maybeBranchToNewBlock(Location.init, BuiltinName!"cleanup"); 1204 autoBlock(b.statement); 1205 break; 1206 1207 case Failure: 1208 continue; 1209 1210 case Destroy: 1211 destroy(b.var); 1212 break; 1213 } 1214 } 1215 } 1216 1217 void concludeUnwind(Location location) { 1218 if (terminate) { 1219 return; 1220 } 1221 1222 foreach_reverse(ref b; unwindActions) { 1223 if (!b.isUnwind()) { 1224 continue; 1225 } 1226 1227 if (!b.unwindBlock) { 1228 b.unwindBlock = makeNewBranch(BuiltinName!"unwind"); 1229 } 1230 1231 currentBlock.branch(location, b.unwindBlock); 1232 return; 1233 } 1234 1235 if (!terminate) { 1236 currentBlock.doThrow(location); 1237 } 1238 } 1239 1240 void unwindTo(size_t level) in { 1241 assert(unwindActions.length >= level); 1242 } do { 1243 if (unwindActions.length == level) { 1244 // Nothing to unwind, done ! 1245 return; 1246 } 1247 1248 closeBlockTo(level); 1249 1250 auto preUnwindBlock = currentBlockRef; 1251 scope(exit) currentBlockRef = preUnwindBlock; 1252 1253 auto i = unwindActions.length; 1254 while (i --> level) { 1255 auto bPtr = &unwindActions[i]; 1256 auto b = *bPtr; 1257 if (!b.isUnwind()) { 1258 continue; 1259 } 1260 1261 unwindActions = unwindActions[0 .. i]; 1262 1263 // We encountered a scope statement that 1264 // can be reached while unwinding. 1265 scope(success) concludeUnwind(Location.init); 1266 1267 // Emit the exception cleanup code. 1268 currentBlockRef = b.unwindBlock; 1269 final switch(b.kind) with(UnwindKind) { 1270 case Success: 1271 assert(0); 1272 1273 case Exit: 1274 autoBlock(b.statement); 1275 break; 1276 1277 case Failure: 1278 assert( 1279 b.statement !is null, 1280 "Catch blocks must be handled with try statements", 1281 ); 1282 1283 goto case Exit; 1284 1285 case Destroy: 1286 destroy(b.var); 1287 break; 1288 } 1289 } 1290 1291 if (unwindActions.length != level) { 1292 foreach (b; unwindActions[level .. $]) { 1293 assert(!b.isUnwind()); 1294 } 1295 1296 unwindActions = unwindActions[0 .. level]; 1297 concludeUnwind(Location.init); 1298 } 1299 1300 if (!terminate) { 1301 maybeBranchToNewBlock(Location.init, BuiltinName!"resume"); 1302 } 1303 } 1304 } 1305 1306 private: 1307 1308 enum BreakKind { 1309 None, 1310 Loop, 1311 Switch, 1312 } 1313 1314 struct UnwindInfo { 1315 private: 1316 import std.bitmanip; 1317 mixin(taggedClassRef!( 1318 Statement, "_statement", 1319 UnwindKind, "_kind", 2, 1320 )); 1321 1322 public: 1323 @property kind() const { 1324 return _kind; 1325 } 1326 1327 @property statement() inout in { 1328 assert(kind != UnwindKind.Destroy); 1329 } do { 1330 return _statement; 1331 } 1332 1333 @property var() inout in { 1334 assert(kind == UnwindKind.Destroy); 1335 } do { 1336 auto s = _statement; 1337 return *(cast(Variable*) &s); 1338 } 1339 1340 BasicBlockRef unwindBlock; 1341 BasicBlockRef cleanupBlock; 1342 1343 this(ScopeKind kind, Statement statement) { 1344 _kind = cast(UnwindKind) kind; 1345 _statement = statement; 1346 } 1347 1348 this(Variable v) in { 1349 assert(!v.type.dstruct.isPod); 1350 } do { 1351 _kind = UnwindKind.Destroy; 1352 _statement = *(cast(Statement*) &v); 1353 } 1354 1355 bool isCleanup() const { 1356 return .isCleanup(kind); 1357 } 1358 1359 bool isUnwind() const { 1360 return .isUnwind(kind); 1361 } 1362 } 1363 1364 enum UnwindKind { 1365 Success, 1366 Exit, 1367 Failure, 1368 Destroy, 1369 } 1370 1371 bool isCleanup(UnwindKind k) { 1372 return k != UnwindKind.Failure; 1373 } 1374 1375 bool isUnwind(UnwindKind k) { 1376 return k != UnwindKind.Success; 1377 } 1378 1379 unittest { 1380 assert(isCleanup(UnwindKind.Success)); 1381 assert(isCleanup(UnwindKind.Exit)); 1382 assert(!isCleanup(UnwindKind.Failure)); 1383 assert(isCleanup(UnwindKind.Destroy)); 1384 1385 assert(!isUnwind(UnwindKind.Success)); 1386 assert(isUnwind(UnwindKind.Exit)); 1387 assert(isUnwind(UnwindKind.Failure)); 1388 assert(isUnwind(UnwindKind.Destroy)); 1389 1390 import std.conv; 1391 static assert(UnwindKind.Exit.asOriginalType() == ScopeKind.Exit.asOriginalType()); 1392 static assert(UnwindKind.Success.asOriginalType() == ScopeKind.Success.asOriginalType()); 1393 static assert(UnwindKind.Failure.asOriginalType() == ScopeKind.Failure.asOriginalType()); 1394 }