1 /** 2 The exception module defines all system-level exceptions and provides a 3 mechanism to alter system-level error handling. 4 5 Copyright: Copyright Sean Kelly 2005 - 2013. 6 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 7 Authors: Sean Kelly and $(HTTP jmdavisprog.com, Jonathan M Davis) 8 Source: $(DRUNTIMESRC core/_exception.d) 9 */ 10 module core.exception; 11 12 import core.internal.stdio; 13 14 // version (CRuntime_LIBWASM) Everything was redirected to onAssertErrorMsg for Javascript handling 15 16 // Compiler lowers final switch default case to this (which is a runtime error) 17 void __switch_errorT()(string file = __FILE__, size_t line = __LINE__) @trusted 18 { 19 // Consider making this a compile time check. 20 version (D_Exceptions) 21 throw staticError!SwitchError("No appropriate switch clause found", file, line, null); 22 else 23 assert(0, "No appropriate switch clause found"); 24 } 25 26 /** 27 * Thrown on a range error. 28 */ 29 class RangeError : Error 30 { 31 this( string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @nogc pure @safe 32 { 33 super( "Range violation", file, line, next ); 34 } 35 36 protected this( string msg, string file, size_t line, Throwable next = null ) @nogc pure @safe 37 { 38 super( msg, file, line, next ); 39 } 40 } 41 42 unittest 43 { 44 { 45 auto re = new RangeError(); 46 assert(re.file == __FILE__); 47 assert(re.line == __LINE__ - 2); 48 assert(re.next is null); 49 assert(re.msg == "Range violation"); 50 } 51 52 { 53 auto re = new RangeError("hello", 42, new Exception("It's an Exception!")); 54 assert(re.file == "hello"); 55 assert(re.line == 42); 56 assert(re.next !is null); 57 assert(re.msg == "Range violation"); 58 } 59 } 60 61 /** 62 * Thrown when an out of bounds array index is accessed. 63 */ 64 class ArrayIndexError : RangeError 65 { 66 /// Index into array 67 const size_t index; 68 /// Length of indexed array 69 const size_t length; 70 71 // Buffer to avoid GC allocations 72 private immutable char[100] msgBuf = '\0'; 73 74 this(size_t index, size_t length, string file = __FILE__, 75 size_t line = __LINE__, Throwable next = null) @nogc pure @safe 76 { 77 this.index = index; 78 this.length = length; 79 80 // Constructing the message is a bit clumsy: 81 // It's essentially `printf("index [%zu] is out of bounds for array of length [%zu]", index, length)`, 82 // but even `snprintf` isn't `pure`. 83 // Also string concatenation isn't `@nogc`, and casting to/from immutable isn't `@safe` 84 import core.internal.string : unsignedToTempString; 85 char[msgBuf.length] buf = void; 86 char[20] tmpBuf = void; 87 char[] sink = buf[]; 88 sink.rangeMsgPut("index ["); 89 sink.rangeMsgPut(unsignedToTempString!10(index, tmpBuf)); 90 sink.rangeMsgPut("] is out of bounds for array of length "); 91 sink.rangeMsgPut(unsignedToTempString!10(length, tmpBuf)); 92 this.msgBuf = buf; 93 super(msgBuf[0..$-sink.length], file, line, next); 94 } 95 } 96 97 @safe pure unittest 98 { 99 assert(new ArrayIndexError(900, 700).msg == "index [900] is out of bounds for array of length 700"); 100 // Ensure msg buffer doesn't overflow on large numbers 101 assert(new ArrayIndexError(size_t.max, size_t.max-1).msg); 102 } 103 104 unittest 105 { 106 try 107 { 108 _d_arraybounds_indexp("test", 400, 9, 3); 109 assert(0, "no ArrayIndexError thrown"); 110 } 111 catch (ArrayIndexError re) 112 { 113 assert(re.file == "test"); 114 assert(re.line == 400); 115 assert(re.index == 9); 116 assert(re.length == 3); 117 } 118 } 119 120 /** 121 * Thrown when an out of bounds array slice is created 122 */ 123 class ArraySliceError : RangeError 124 { 125 /// Lower/upper bound passed to slice: `array[lower .. upper]` 126 const size_t lower, upper; 127 /// Length of sliced array 128 const size_t length; 129 130 private immutable char[120] msgBuf = '\0'; 131 132 this(size_t lower, size_t upper, size_t length, string file = __FILE__, 133 size_t line = __LINE__, Throwable next = null) @nogc pure @safe 134 { 135 this.lower = lower; 136 this.upper = upper; 137 this.length = length; 138 139 // Constructing the message is a bit clumsy for the same reasons as ArrayIndexError 140 import core.internal.string : unsignedToTempString; 141 char[msgBuf.length] buf = void; 142 char[20] tmpBuf = void; 143 char[] sink = buf; 144 sink.rangeMsgPut("slice ["); 145 sink.rangeMsgPut(unsignedToTempString!10(lower, tmpBuf)); 146 sink.rangeMsgPut(" .. "); 147 sink.rangeMsgPut(unsignedToTempString!10(upper, tmpBuf)); 148 sink.rangeMsgPut("] "); 149 if (lower > upper) 150 { 151 sink.rangeMsgPut("has a larger lower index than upper index"); 152 } 153 else 154 { 155 sink.rangeMsgPut("extends past source array of length "); 156 sink.rangeMsgPut(unsignedToTempString!10(length, tmpBuf)); 157 } 158 159 this.msgBuf = buf; 160 super(msgBuf[0..$-sink.length], file, line, next); 161 } 162 } 163 164 @safe pure unittest 165 { 166 assert(new ArraySliceError(40, 80, 20).msg == "slice [40 .. 80] extends past source array of length 20"); 167 assert(new ArraySliceError(90, 70, 20).msg == "slice [90 .. 70] has a larger lower index than upper index"); 168 // Ensure msg buffer doesn't overflow on large numbers 169 assert(new ArraySliceError(size_t.max, size_t.max, size_t.max-1).msg); 170 } 171 172 unittest 173 { 174 try 175 { 176 _d_arraybounds_slicep("test", 400, 1, 7, 3); 177 assert(0, "no ArraySliceError thrown"); 178 } 179 catch (ArraySliceError re) 180 { 181 assert(re.file == "test"); 182 assert(re.line == 400); 183 assert(re.lower == 1); 184 assert(re.upper == 7); 185 assert(re.length == 3); 186 } 187 } 188 189 /// Mini `std.range.primitives: put` for constructor of ArraySliceError / ArrayIndexError 190 private void rangeMsgPut(ref scope char[] r, scope const(char)[] e) @nogc pure @safe 191 { 192 assert(r.length >= e.length); // don't throw ArraySliceError inside ArrayIndexError ctor 193 r[0 .. e.length] = e[]; 194 r = r[e.length .. $]; 195 } 196 197 /** 198 * Thrown on an assert error. 199 */ 200 class AssertError : Error 201 { 202 @safe pure @nogc this( string file, size_t line ) 203 { 204 this(cast(Throwable)null, file, line); 205 } 206 207 @safe pure @nogc this( Throwable next, string file = __FILE__, size_t line = __LINE__ ) 208 { 209 this( "Assertion failure", file, line, next); 210 } 211 212 @safe pure @nogc this( string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null ) 213 { 214 super( msg, file, line, next ); 215 } 216 } 217 218 unittest 219 { 220 { 221 auto ae = new AssertError("hello", 42); 222 assert(ae.file == "hello"); 223 assert(ae.line == 42); 224 assert(ae.next is null); 225 assert(ae.msg == "Assertion failure"); 226 } 227 228 { 229 auto ae = new AssertError(new Exception("It's an Exception!")); 230 assert(ae.file == __FILE__); 231 assert(ae.line == __LINE__ - 2); 232 assert(ae.next !is null); 233 assert(ae.msg == "Assertion failure"); 234 } 235 236 { 237 auto ae = new AssertError(new Exception("It's an Exception!"), "hello", 42); 238 assert(ae.file == "hello"); 239 assert(ae.line == 42); 240 assert(ae.next !is null); 241 assert(ae.msg == "Assertion failure"); 242 } 243 244 { 245 auto ae = new AssertError("msg"); 246 assert(ae.file == __FILE__); 247 assert(ae.line == __LINE__ - 2); 248 assert(ae.next is null); 249 assert(ae.msg == "msg"); 250 } 251 252 { 253 auto ae = new AssertError("msg", "hello", 42); 254 assert(ae.file == "hello"); 255 assert(ae.line == 42); 256 assert(ae.next is null); 257 assert(ae.msg == "msg"); 258 } 259 260 { 261 auto ae = new AssertError("msg", "hello", 42, new Exception("It's an Exception!")); 262 assert(ae.file == "hello"); 263 assert(ae.line == 42); 264 assert(ae.next !is null); 265 assert(ae.msg == "msg"); 266 } 267 } 268 269 270 /** 271 * Thrown on finalize error. 272 */ 273 class FinalizeError : Error 274 { 275 TypeInfo info; 276 277 this( TypeInfo ci, Throwable next, string file = __FILE__, size_t line = __LINE__ ) @safe pure @nogc 278 { 279 this(ci, file, line, next); 280 } 281 282 this( TypeInfo ci, string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @safe pure @nogc 283 { 284 super( "Finalization error", file, line, next ); 285 info = ci; 286 } 287 288 override string toString() const @safe 289 { 290 return "An exception was thrown while finalizing an instance of " ~ info.toString(); 291 } 292 } 293 294 unittest 295 { 296 ClassInfo info = new ClassInfo; 297 info.name = "testInfo"; 298 299 { 300 auto fe = new FinalizeError(info); 301 assert(fe.file == __FILE__); 302 assert(fe.line == __LINE__ - 2); 303 assert(fe.next is null); 304 assert(fe.msg == "Finalization error"); 305 assert(fe.info == info); 306 } 307 308 { 309 auto fe = new FinalizeError(info, new Exception("It's an Exception!")); 310 assert(fe.file == __FILE__); 311 assert(fe.line == __LINE__ - 2); 312 assert(fe.next !is null); 313 assert(fe.msg == "Finalization error"); 314 assert(fe.info == info); 315 } 316 317 { 318 auto fe = new FinalizeError(info, "hello", 42); 319 assert(fe.file == "hello"); 320 assert(fe.line == 42); 321 assert(fe.next is null); 322 assert(fe.msg == "Finalization error"); 323 assert(fe.info == info); 324 } 325 326 { 327 auto fe = new FinalizeError(info, "hello", 42, new Exception("It's an Exception!")); 328 assert(fe.file == "hello"); 329 assert(fe.line == 42); 330 assert(fe.next !is null); 331 assert(fe.msg == "Finalization error"); 332 assert(fe.info == info); 333 } 334 } 335 336 /** 337 * Thrown on an out of memory error. 338 */ 339 class OutOfMemoryError : Error 340 { 341 this(string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @safe pure @nogc 342 { 343 this(true, file, line, next); 344 } 345 346 this(bool trace, string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @safe pure @nogc 347 { 348 super("Memory allocation failed", file, line, next); 349 if (!trace) 350 this.info = SuppressTraceInfo.instance; 351 } 352 353 override string toString() const @trusted 354 { 355 return msg.length ? (cast()this).superToString() : "Memory allocation failed"; 356 } 357 358 // kludge to call non-const super.toString 359 private string superToString() @trusted 360 { 361 return super.toString(); 362 } 363 } 364 365 unittest 366 { 367 { 368 auto oome = new OutOfMemoryError(); 369 assert(oome.file == __FILE__); 370 assert(oome.line == __LINE__ - 2); 371 assert(oome.next is null); 372 assert(oome.msg == "Memory allocation failed"); 373 assert(oome.toString.length); 374 } 375 376 { 377 auto oome = new OutOfMemoryError("hello", 42, new Exception("It's an Exception!")); 378 assert(oome.file == "hello"); 379 assert(oome.line == 42); 380 assert(oome.next !is null); 381 assert(oome.msg == "Memory allocation failed"); 382 } 383 } 384 385 386 /** 387 * Thrown on an invalid memory operation. 388 * 389 * An invalid memory operation error occurs in circumstances when the garbage 390 * collector has detected an operation it cannot reliably handle. The default 391 * D GC is not re-entrant, so this can happen due to allocations done from 392 * within finalizers called during a garbage collection cycle. 393 */ 394 class InvalidMemoryOperationError : Error 395 { 396 this(string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @safe pure @nogc 397 { 398 super( "Invalid memory operation", file, line, next ); 399 } 400 401 override string toString() const @trusted 402 { 403 return msg.length ? (cast()this).superToString() : "Invalid memory operation"; 404 } 405 406 // kludge to call non-const super.toString 407 private string superToString() @trusted 408 { 409 return super.toString(); 410 } 411 } 412 413 unittest 414 { 415 { 416 auto oome = new InvalidMemoryOperationError(); 417 assert(oome.file == __FILE__); 418 assert(oome.line == __LINE__ - 2); 419 assert(oome.next is null); 420 assert(oome.msg == "Invalid memory operation"); 421 assert(oome.toString.length); 422 } 423 424 { 425 auto oome = new InvalidMemoryOperationError("hello", 42, new Exception("It's an Exception!")); 426 assert(oome.file == "hello"); 427 assert(oome.line == 42); 428 assert(oome.next !is null); 429 assert(oome.msg == "Invalid memory operation"); 430 } 431 } 432 433 434 /** 435 * Thrown on a configuration error. 436 */ 437 class ForkError : Error 438 { 439 this( string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @nogc pure @safe 440 { 441 super( "fork() failed", file, line, next ); 442 } 443 } 444 445 446 /** 447 * Thrown on a switch error. 448 */ 449 class SwitchError : Error 450 { 451 @safe pure @nogc this( string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null ) 452 { 453 super( msg, file, line, next ); 454 } 455 } 456 457 unittest 458 { 459 { 460 auto se = new SwitchError("No appropriate switch clause found"); 461 assert(se.file == __FILE__); 462 assert(se.line == __LINE__ - 2); 463 assert(se.next is null); 464 assert(se.msg == "No appropriate switch clause found"); 465 } 466 467 { 468 auto se = new SwitchError("No appropriate switch clause found", "hello", 42, new Exception("It's an Exception!")); 469 assert(se.file == "hello"); 470 assert(se.line == 42); 471 assert(se.next !is null); 472 assert(se.msg == "No appropriate switch clause found"); 473 } 474 } 475 476 477 /** 478 * Thrown on a unicode conversion error. 479 */ 480 class UnicodeException : Exception 481 { 482 size_t idx; 483 484 this( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @safe pure @nogc 485 { 486 super( msg, file, line, next ); 487 this.idx = idx; 488 } 489 } 490 491 unittest 492 { 493 { 494 auto ue = new UnicodeException("msg", 2); 495 assert(ue.file == __FILE__); 496 assert(ue.line == __LINE__ - 2); 497 assert(ue.next is null); 498 assert(ue.msg == "msg"); 499 assert(ue.idx == 2); 500 } 501 502 { 503 auto ue = new UnicodeException("msg", 2, "hello", 42, new Exception("It's an Exception!")); 504 assert(ue.file == "hello"); 505 assert(ue.line == 42); 506 assert(ue.next !is null); 507 assert(ue.msg == "msg"); 508 assert(ue.idx == 2); 509 } 510 } 511 512 513 /** 514 * A callback for assert errors in D. The user-supplied assert handler will 515 * be called if one has been supplied, otherwise an $(LREF AssertError) will be 516 * thrown. 517 * 518 * Params: 519 * file = The name of the file that signaled this error. 520 * line = The line number on which this error occurred. 521 */ 522 extern (C) void onAssertError( string file = __FILE__, size_t line = __LINE__ ) 523 { 524 onAssertErrorMsg( file, line, null); 525 } 526 527 528 /** 529 * A callback for assert errors in D. The user-supplied assert handler will 530 * be called if one has been supplied, otherwise an $(LREF AssertError) will be 531 * thrown. 532 * 533 * Params: 534 * file = The name of the file that signaled this error. 535 * line = The line number on which this error occurred. 536 * msg = An error message supplied by the user. 537 // */ 538 extern (C) void onAssertErrorMsg( string file, size_t line, string msg ) pure @nogc; 539 // { 540 // if ( _assertHandler is null ) 541 // throw staticError!AssertError(msg, file, line); 542 // _assertHandler( file, line, msg ); 543 // } 544 545 546 /** 547 * A callback for unittest errors in D. The user-supplied unittest handler 548 * will be called if one has been supplied, otherwise the error will be 549 * written to stderr. 550 * 551 * Params: 552 * file = The name of the file that signaled this error. 553 * line = The line number on which this error occurred. 554 * msg = An error message supplied by the user. 555 */ 556 extern (C) void onUnittestErrorMsg( string file, size_t line, string msg ) 557 { 558 onAssertErrorMsg( file, line, msg ); 559 } 560 561 562 /////////////////////////////////////////////////////////////////////////////// 563 // Internal Error Callbacks 564 /////////////////////////////////////////////////////////////////////////////// 565 566 /** 567 * A callback for general array bounds errors in D. A $(LREF RangeError) will be thrown. 568 * 569 * Params: 570 * file = The name of the file that signaled this error. 571 * line = The line number on which this error occurred. 572 * 573 * Throws: 574 * $(LREF RangeError). 575 */ 576 extern (C) noreturn onRangeError( string file = __FILE__, size_t line = __LINE__ ) @trusted pure @nogc 577 { 578 onAssertErrorMsg(file, line, "Range Error"); 579 assert(0); 580 } 581 582 /** 583 * A callback for array slice out of bounds errors in D. 584 * 585 * Params: 586 * lower = the lower bound of the index passed of a slice 587 * upper = the upper bound of the index passed of a slice or the index if not a slice 588 * length = length of the array 589 * file = The name of the file that signaled this error. 590 * line = The line number on which this error occurred. 591 * 592 * Throws: 593 * $(LREF ArraySliceError). 594 */ 595 extern (C) noreturn onArraySliceError( size_t lower = 0, size_t upper = 0, size_t length = 0, 596 string file = __FILE__, size_t line = __LINE__ ) @trusted pure @nogc 597 { 598 char[256] buf; 599 auto msg = cast(string)"Slice out of bounds: array[%d .. %d] of length %d".format(buf.ptr[0 .. 256], lower, upper, length); 600 601 onAssertErrorMsg(file, line, msg); 602 assert(0); 603 } 604 605 /** 606 * A callback for array index out of bounds errors in D. 607 * 608 * Params: 609 * index = index in the array 610 * length = length of the array 611 * file = The name of the file that signaled this error. 612 * line = The line number on which this error occurred. 613 * 614 * Throws: 615 * $(LREF ArrayIndexError). 616 */ 617 extern (C) noreturn onArrayIndexError( size_t index = 0, size_t length = 0, 618 string file = __FILE__, size_t line = __LINE__ ) @trusted pure @nogc 619 { 620 char[256] buf; 621 auto msg = cast(string)"index %d is out of bounds for array of length %d".format(buf.ptr[0 .. 256], index, length); 622 onAssertErrorMsg(file, line, msg); 623 assert(0); 624 } 625 626 /** 627 * A callback for finalize errors in D. A $(LREF FinalizeError) will be thrown. 628 * 629 * Params: 630 * info = The TypeInfo instance for the object that failed finalization. 631 * e = The exception thrown during finalization. 632 * file = The name of the file that signaled this error. 633 * line = The line number on which this error occurred. 634 * 635 * Throws: 636 * $(LREF FinalizeError). 637 */ 638 extern (C) noreturn onFinalizeError( TypeInfo info, Throwable e, string file = __FILE__, size_t line = __LINE__ ) @trusted nothrow 639 { 640 assert(0, "onFinalizeError"); 641 } 642 643 version (D_BetterC) 644 { 645 // When compiling with -betterC we use template functions so if they are 646 // used the bodies are copied into the user's program so there is no need 647 // for the D runtime during linking. 648 649 // In the future we might want to convert all functions in this module to 650 // templates even for ordinary builds instead of providing them as an 651 // extern(C) library. 652 653 noreturn onOutOfMemoryError()(void* pretend_sideffect = null) @nogc pure @trusted nothrow 654 { 655 assert(0, "Memory allocation failed"); 656 } 657 alias onOutOfMemoryErrorNoGC = onOutOfMemoryError; 658 659 noreturn onInvalidMemoryOperationError()(void* pretend_sideffect = null) @nogc pure @trusted nothrow 660 { 661 assert(0, "Invalid memory operation"); 662 } 663 } 664 else 665 { 666 /** 667 * A callback for out of memory errors in D. An $(LREF OutOfMemoryError) will be 668 * thrown. 669 * 670 * Throws: 671 * $(LREF OutOfMemoryError). 672 */ 673 extern (C) noreturn onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure @nogc nothrow /* dmd @@@BUG11461@@@ */ 674 { 675 // NOTE: Since an out of memory condition exists, no allocation must occur 676 // while generating this object. 677 assert(0, "Memory allocation failed"); 678 } 679 680 extern (C) noreturn onOutOfMemoryErrorNoGC() @trusted @nogc nothrow 681 { 682 // suppress stacktrace until they are @nogc 683 assert(0, "Invalid memory operation"); 684 } 685 } 686 687 /** 688 * A callback for invalid memory operations in D. An 689 * $(LREF InvalidMemoryOperationError) will be thrown. 690 * 691 * Throws: 692 * $(LREF InvalidMemoryOperationError). 693 */ 694 extern (C) noreturn onInvalidMemoryOperationError(void* pretend_sideffect = null) @trusted pure @nogc nothrow /* dmd @@@BUG11461@@@ */ 695 { 696 // The same restriction applies as for onOutOfMemoryError. The GC is in an 697 // undefined state, thus no allocation must occur while generating this object. 698 assert(0, "onInvalidMemoryOperationError"); 699 } 700 701 702 /** 703 * A callback for errors in the case of a failed fork in D. A $(LREF ForkError) will be thrown. 704 * 705 * Params: 706 * file = The name of the file that signaled this error. 707 * line = The line number on which this error occurred. 708 * 709 * Throws: 710 * $(LREF ConfigurationError). 711 */ 712 extern (C) noreturn onForkError( string file = __FILE__, size_t line = __LINE__ ) @trusted pure @nogc 713 { 714 onAssertErrorMsg(file, line, "onForkError"); 715 assert(0); 716 } 717 718 /** 719 * A callback for unicode errors in D. A $(LREF UnicodeException) will be thrown. 720 * 721 * Params: 722 * msg = Information about the error. 723 * idx = String index where this error was detected. 724 * file = The name of the file that signaled this error. 725 * line = The line number on which this error occurred. 726 * 727 * Throws: 728 * $(LREF UnicodeException). 729 */ 730 extern (C) noreturn onUnicodeError( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__ ) @trusted pure 731 { 732 char[512] buf; 733 auto msg2 = cast(string)"onUnicodeError %s at idx %d".format(buf.ptr[0 .. 512], msg, idx); 734 735 onAssertErrorMsg( file, line, msg2 ); 736 assert(0); 737 } 738 739 /*********************************** 740 * These functions must be defined for any D program linked 741 * against this library. 742 */ 743 /+ 744 extern (C) void onAssertError(string file, size_t line); 745 extern (C) void onAssertErrorMsg(string file, size_t line, string msg); 746 extern (C) void onUnittestErrorMsg(string file, size_t line, string msg); 747 extern (C) void onRangeError(string file, size_t line); 748 extern (C) void onHiddenFuncError(Object o); 749 +/ 750 751 /*********************************** 752 * Function calls to these are generated by the compiler and inserted into 753 * the object code. 754 */ 755 756 extern (C) 757 { 758 /* One of these three is called upon an assert() fail. 759 */ 760 void _d_assertp(immutable(char)* file, uint line) 761 { 762 import core.stdc.string : strlen; 763 onAssertError(file[0 .. strlen(file)], line); 764 } 765 766 void _d_assert_msg(string msg, string file, uint line) 767 { 768 onAssertErrorMsg(file, line, msg); 769 } 770 771 void _d_assert(string file, uint line) 772 { 773 onAssertError(file, line); 774 } 775 776 /* One of these three is called upon an assert() fail inside of a unittest block 777 */ 778 void _d_unittestp(immutable(char)* file, uint line) 779 { 780 import core.stdc.string : strlen; 781 _d_unittest(file[0 .. strlen(file)], line); 782 } 783 784 void _d_unittest_msg(string msg, string file, uint line) 785 { 786 onUnittestErrorMsg(file, line, msg); 787 } 788 789 void _d_unittest(string file, uint line) 790 { 791 _d_unittest_msg("unittest failure", file, line); 792 } 793 794 /// Called when an invalid array index/slice or associative array key is accessed 795 void _d_arrayboundsp(immutable(char*) file, uint line) 796 { 797 import core.stdc.string : strlen; 798 onRangeError(file[0 .. strlen(file)], line); 799 } 800 801 /// ditto 802 void _d_arraybounds(string file, uint line) 803 { 804 onRangeError(file, line); 805 } 806 807 /// Called when an out of range slice of an array is created 808 void _d_arraybounds_slicep(immutable(char*) file, uint line, size_t lower, size_t upper, size_t length) 809 { 810 import core.stdc.string : strlen; 811 onArraySliceError(lower, upper, length, file[0 .. strlen(file)], line); 812 } 813 814 /// ditto 815 void _d_arraybounds_slice(string file, uint line, size_t lower, size_t upper, size_t length) 816 { 817 onArraySliceError(lower, upper, length, file, line); 818 } 819 820 /// Called when an out of range array index is accessed 821 void _d_arraybounds_indexp(immutable(char*) file, uint line, size_t index, size_t length) 822 { 823 import core.stdc.string : strlen; 824 onArrayIndexError(index, length, file[0 .. strlen(file)], line); 825 } 826 827 /// ditto 828 void _d_arraybounds_index(string file, uint line, size_t index, size_t length) 829 { 830 onArrayIndexError(index, length, file, line); 831 } 832 } 833 834 // TLS storage shared for all errors, chaining might create circular reference 835 private align(2 * size_t.sizeof) void[256] _store; 836 837 version (LDC) version (Windows) 838 { 839 version = LDC_Windows; 840 841 // cannot access TLS globals directly across DLL boundaries, e.g., 842 // when instantiating `staticError` below in another DLL 843 pragma(inline, false) // could be safely inlined in the binary containing druntime only 844 private ref getStore() { return _store; } 845 } 846 847 // only Errors for now as those are rarely chained 848 package T staticError(T, Args...)(auto ref Args args) 849 if (is(T : Error)) 850 { 851 // pure hack, what we actually need is @noreturn and allow to call that in pure functions 852 static T get() 853 { 854 static assert(__traits(classInstanceSize, T) <= _store.length, 855 T.stringof ~ " is too large for staticError()"); 856 857 version (LDC_Windows) 858 auto store = &getStore(); 859 else 860 auto store = &_store; 861 862 return cast(T) store.ptr; 863 } 864 auto res = (cast(T function() @trusted pure @nogc) &get)(); 865 import core.lifetime : emplace; 866 emplace(res, args); 867 return res; 868 } 869 870 // Suppress traceinfo generation when the GC cannot be used. Workaround for 871 // Bugzilla 14993. We should make stack traces @nogc instead. 872 package class SuppressTraceInfo : Throwable.TraceInfo 873 { 874 override int opApply(scope int delegate(ref const(char[]))) const { return 0; } 875 override int opApply(scope int delegate(ref size_t, ref const(char[]))) const { return 0; } 876 override string toString() const { return null; } 877 static SuppressTraceInfo instance() @trusted @nogc pure nothrow 878 { 879 static immutable SuppressTraceInfo it = new SuppressTraceInfo; 880 return cast(SuppressTraceInfo)it; 881 } 882 }