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