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)
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("No appropriate switch clause found", 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     }
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     }
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
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 {
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         info = ci;
282     }
283 
284     override string toString() const @safe
285     {
286         return "An exception was thrown while finalizing an instance of " ~ info.toString();
287     }
288 }
289 
290 unittest
291 {
292     ClassInfo info = new ClassInfo;
293     info.name = "testInfo";
294 
295     {
296         auto fe = new FinalizeError(info);
297         assert(fe.file == __FILE__);
298         assert(fe.line == __LINE__ - 2);
299         assert(fe.next is null);
300         assert(fe.msg == "Finalization error");
301         assert(fe.info == info);
302     }
303 
304     {
305         auto fe = new FinalizeError(info, new Exception("It's an Exception!"));
306         assert(fe.file == __FILE__);
307         assert(fe.line == __LINE__ - 2);
308         assert(fe.next !is null);
309         assert(fe.msg == "Finalization error");
310         assert(fe.info == info);
311     }
312 
313     {
314         auto fe = new FinalizeError(info, "hello", 42);
315         assert(fe.file == "hello");
316         assert(fe.line == 42);
317         assert(fe.next is null);
318         assert(fe.msg == "Finalization error");
319         assert(fe.info == info);
320     }
321 
322     {
323         auto fe = new FinalizeError(info, "hello", 42, new Exception("It's an Exception!"));
324         assert(fe.file == "hello");
325         assert(fe.line == 42);
326         assert(fe.next !is null);
327         assert(fe.msg == "Finalization error");
328         assert(fe.info == info);
329     }
330 }
331 
332 /**
333  * Thrown on an out of memory error.
334  */
335 class OutOfMemoryError : Error
336 {
337     this(string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @safe pure nothrow @nogc
338     {
339         this(true, file, line, next);
340     }
341 
342     this(bool trace, string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @safe pure nothrow @nogc
343     {
344         super("Memory allocation failed", file, line, next);
345         if (!trace)
346             this.info = SuppressTraceInfo.instance;
347     }
348 
349     override string toString() const @trusted
350     {
351         return msg.length ? (cast()this).superToString() : "Memory allocation failed";
352     }
353 
354     // kludge to call non-const super.toString
355     private string superToString() @trusted
356     {
357         return super.toString();
358     }
359 }
360 
361 unittest
362 {
363     {
364         auto oome = new OutOfMemoryError();
365         assert(oome.file == __FILE__);
366         assert(oome.line == __LINE__ - 2);
367         assert(oome.next is null);
368         assert(oome.msg == "Memory allocation failed");
369         assert(oome.toString.length);
370     }
371 
372     {
373         auto oome = new OutOfMemoryError("hello", 42, new Exception("It's an Exception!"));
374         assert(oome.file == "hello");
375         assert(oome.line == 42);
376         assert(oome.next !is null);
377         assert(oome.msg == "Memory allocation failed");
378     }
379 }
380 
381 
382 /**
383  * Thrown on an invalid memory operation.
384  *
385  * An invalid memory operation error occurs in circumstances when the garbage
386  * collector has detected an operation it cannot reliably handle. The default
387  * D GC is not re-entrant, so this can happen due to allocations done from
388  * within finalizers called during a garbage collection cycle.
389  */
390 class InvalidMemoryOperationError : Error
391 {
392     this(string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @safe pure nothrow @nogc
393     {
394         super( "Invalid memory operation", file, line, next );
395     }
396 
397     override string toString() const @trusted
398     {
399         return msg.length ? (cast()this).superToString() : "Invalid memory operation";
400     }
401 
402     // kludge to call non-const super.toString
403     private string superToString() @trusted
404     {
405         return super.toString();
406     }
407 }
408 
409 unittest
410 {
411     {
412         auto oome = new InvalidMemoryOperationError();
413         assert(oome.file == __FILE__);
414         assert(oome.line == __LINE__ - 2);
415         assert(oome.next is null);
416         assert(oome.msg == "Invalid memory operation");
417         assert(oome.toString.length);
418     }
419 
420     {
421         auto oome = new InvalidMemoryOperationError("hello", 42, new Exception("It's an Exception!"));
422         assert(oome.file == "hello");
423         assert(oome.line == 42);
424         assert(oome.next !is null);
425         assert(oome.msg == "Invalid memory operation");
426     }
427 }
428 
429 
430 /**
431 * Thrown on a configuration error.
432 */
433 class ForkError : Error
434 {
435     this( string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @nogc nothrow pure @safe
436     {
437         super( "fork() failed", file, line, next );
438     }
439 }
440 
441 
442 /**
443  * Thrown on a switch error.
444  */
445 class SwitchError : Error
446 {
447     @safe pure nothrow @nogc this( string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null )
448     {
449         super( msg, file, line, next );
450     }
451 }
452 
453 unittest
454 {
455     {
456         auto se = new SwitchError("No appropriate switch clause found");
457         assert(se.file == __FILE__);
458         assert(se.line == __LINE__ - 2);
459         assert(se.next is null);
460         assert(se.msg == "No appropriate switch clause found");
461     }
462 
463     {
464         auto se = new SwitchError("No appropriate switch clause found", "hello", 42, new Exception("It's an Exception!"));
465         assert(se.file == "hello");
466         assert(se.line == 42);
467         assert(se.next !is null);
468         assert(se.msg == "No appropriate switch clause found");
469     }
470 }
471 
472 
473 /**
474  * Thrown on a unicode conversion error.
475  */
476 class UnicodeException : Exception
477 {
478     size_t idx;
479 
480     this( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @safe pure nothrow @nogc
481     {
482         super( msg, file, line, next );
483         this.idx = idx;
484     }
485 }
486 
487 unittest
488 {
489     {
490         auto ue = new UnicodeException("msg", 2);
491         assert(ue.file == __FILE__);
492         assert(ue.line == __LINE__ - 2);
493         assert(ue.next is null);
494         assert(ue.msg == "msg");
495         assert(ue.idx == 2);
496     }
497 
498     {
499         auto ue = new UnicodeException("msg", 2, "hello", 42, new Exception("It's an Exception!"));
500         assert(ue.file == "hello");
501         assert(ue.line == 42);
502         assert(ue.next !is null);
503         assert(ue.msg == "msg");
504         assert(ue.idx == 2);
505     }
506 }
507 
508 
509 ///////////////////////////////////////////////////////////////////////////////
510 // Overrides
511 ///////////////////////////////////////////////////////////////////////////////
512 
513 
514 // NOTE: One assert handler is used for all threads.  Thread-local
515 //       behavior should occur within the handler itself.  This delegate
516 //       is __gshared for now based on the assumption that it will only
517 //       set by the main thread during program initialization.
518 private __gshared AssertHandler _assertHandler = null;
519 
520 
521 /**
522 Gets/sets assert hander. null means the default handler is used.
523 */
524 alias AssertHandler = void function(string file, size_t line, string msg) nothrow;
525 
526 /// ditto
527 @property AssertHandler assertHandler() @trusted nothrow @nogc
528 {
529     return _assertHandler;
530 }
531 
532 /// ditto
533 @property void assertHandler(AssertHandler handler) @trusted nothrow @nogc
534 {
535     _assertHandler = handler;
536 }
537 
538 
539 ///////////////////////////////////////////////////////////////////////////////
540 // Overridable Callbacks
541 ///////////////////////////////////////////////////////////////////////////////
542 
543 
544 /**
545  * A callback for assert errors in D.  The user-supplied assert handler will
546  * be called if one has been supplied, otherwise an $(LREF AssertError) will be
547  * thrown.
548  *
549  * Params:
550  *  file = The name of the file that signaled this error.
551  *  line = The line number on which this error occurred.
552  */
553 extern (C) void onAssertError( string file = __FILE__, size_t line = __LINE__ ) nothrow
554 {
555     if ( _assertHandler is null )
556         throw staticError!AssertError(file, line);
557     _assertHandler( file, line, null);
558 }
559 
560 
561 /**
562  * A callback for assert errors in D.  The user-supplied assert handler will
563  * be called if one has been supplied, otherwise an $(LREF AssertError) will be
564  * thrown.
565  *
566  * Params:
567  *  file = The name of the file that signaled this error.
568  *  line = The line number on which this error occurred.
569  *  msg  = An error message supplied by the user.
570  */
571 extern (C) void onAssertErrorMsg( string file, size_t line, string msg ) nothrow
572 {
573     if ( _assertHandler is null )
574         throw staticError!AssertError(msg, file, line);
575     _assertHandler( file, line, msg );
576 }
577 
578 
579 /**
580  * A callback for unittest errors in D.  The user-supplied unittest handler
581  * will be called if one has been supplied, otherwise the error will be
582  * written to stderr.
583  *
584  * Params:
585  *  file = The name of the file that signaled this error.
586  *  line = The line number on which this error occurred.
587  *  msg  = An error message supplied by the user.
588  */
589 extern (C) void onUnittestErrorMsg( string file, size_t line, string msg ) nothrow
590 {
591     onAssertErrorMsg( file, line, msg );
592 }
593 
594 
595 ///////////////////////////////////////////////////////////////////////////////
596 // Internal Error Callbacks
597 ///////////////////////////////////////////////////////////////////////////////
598 
599 /**
600  * A callback for general array bounds errors in D. A $(LREF RangeError) will be thrown.
601  *
602  * Params:
603  *  file = The name of the file that signaled this error.
604  *  line = The line number on which this error occurred.
605  *
606  * Throws:
607  *  $(LREF RangeError).
608  */
609 extern (C) noreturn onRangeError( string file = __FILE__, size_t line = __LINE__ ) @trusted pure nothrow @nogc
610 {
611     throw staticError!RangeError(file, line, null);
612 }
613 
614 /**
615  * A callback for array slice out of bounds errors in D.
616  *
617  * Params:
618  *  lower  = the lower bound of the index passed of a slice
619  *  upper  = the upper bound of the index passed of a slice or the index if not a slice
620  *  length = length of the array
621  *  file = The name of the file that signaled this error.
622  *  line = The line number on which this error occurred.
623  *
624  * Throws:
625  *  $(LREF ArraySliceError).
626  */
627 extern (C) noreturn onArraySliceError( size_t lower = 0, size_t upper = 0, size_t length = 0,
628                               string file = __FILE__, size_t line = __LINE__ ) @trusted pure nothrow @nogc
629 {
630     throw staticError!ArraySliceError(lower, upper, length, file, line, null);
631 }
632 
633 /**
634  * A callback for array index out of bounds errors in D.
635  *
636  * Params:
637  *  index  = index in the array
638  *  length = length of the array
639  *  file = The name of the file that signaled this error.
640  *  line = The line number on which this error occurred.
641  *
642  * Throws:
643  *  $(LREF ArrayIndexError).
644  */
645 extern (C) noreturn onArrayIndexError( size_t index = 0, size_t length = 0,
646                               string file = __FILE__, size_t line = __LINE__ ) @trusted pure nothrow @nogc
647 {
648     throw staticError!ArrayIndexError(index, length, file, line, null);
649 }
650 
651 /**
652  * A callback for finalize errors in D.  A $(LREF FinalizeError) will be thrown.
653  *
654  * Params:
655  *  info = The TypeInfo instance for the object that failed finalization.
656  *  e = The exception thrown during finalization.
657  *  file = The name of the file that signaled this error.
658  *  line = The line number on which this error occurred.
659  *
660  * Throws:
661  *  $(LREF FinalizeError).
662  */
663 extern (C) noreturn onFinalizeError( TypeInfo info, Throwable e, string file = __FILE__, size_t line = __LINE__ ) @trusted nothrow
664 {
665     // This error is thrown during a garbage collection, so no allocation must occur while
666     //  generating this object. So we use a preallocated instance
667     throw staticError!FinalizeError(info, e, file, line);
668 }
669 
670 version (D_BetterC)
671 {
672     // When compiling with -betterC we use template functions so if they are
673     // used the bodies are copied into the user's program so there is no need
674     // for the D runtime during linking.
675 
676     // In the future we might want to convert all functions in this module to
677     // templates even for ordinary builds instead of providing them as an
678     // extern(C) library.
679 
680     noreturn onOutOfMemoryError()(void* pretend_sideffect = null) @nogc nothrow pure @trusted
681     {
682         assert(0, "Memory allocation failed");
683     }
684     alias onOutOfMemoryErrorNoGC = onOutOfMemoryError;
685 
686     noreturn onInvalidMemoryOperationError()(void* pretend_sideffect = null) @nogc nothrow pure @trusted
687     {
688         assert(0, "Invalid memory operation");
689     }
690 }
691 else
692 {
693     /**
694      * A callback for out of memory errors in D.  An $(LREF OutOfMemoryError) will be
695      * thrown.
696      *
697      * Throws:
698      *  $(LREF OutOfMemoryError).
699      */
700     extern (C) noreturn onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow @nogc /* dmd @@@BUG11461@@@ */
701     {
702         // NOTE: Since an out of memory condition exists, no allocation must occur
703         //       while generating this object.
704         throw staticError!OutOfMemoryError();
705     }
706 
707     extern (C) noreturn onOutOfMemoryErrorNoGC() @trusted nothrow @nogc
708     {
709         // suppress stacktrace until they are @nogc
710         throw staticError!OutOfMemoryError(false);
711     }
712 }
713 
714 /**
715  * A callback for invalid memory operations in D.  An
716  * $(LREF InvalidMemoryOperationError) will be thrown.
717  *
718  * Throws:
719  *  $(LREF InvalidMemoryOperationError).
720  */
721 extern (C) noreturn onInvalidMemoryOperationError(void* pretend_sideffect = null) @trusted pure nothrow @nogc /* dmd @@@BUG11461@@@ */
722 {
723     // The same restriction applies as for onOutOfMemoryError. The GC is in an
724     // undefined state, thus no allocation must occur while generating this object.
725     throw staticError!InvalidMemoryOperationError();
726 }
727 
728 
729 /**
730  * A callback for errors in the case of a failed fork in D.  A $(LREF ForkError) will be thrown.
731  *
732  * Params:
733  *  file = The name of the file that signaled this error.
734  *  line = The line number on which this error occurred.
735  *
736  * Throws:
737  *  $(LREF ConfigurationError).
738  */
739 extern (C) noreturn onForkError( string file = __FILE__, size_t line = __LINE__ ) @trusted pure nothrow @nogc
740 {
741     throw staticError!ForkError( file, line, null );
742 }
743 
744 /**
745  * A callback for unicode errors in D.  A $(LREF UnicodeException) will be thrown.
746  *
747  * Params:
748  *  msg = Information about the error.
749  *  idx = String index where this error was detected.
750  *  file = The name of the file that signaled this error.
751  *  line = The line number on which this error occurred.
752  *
753  * Throws:
754  *  $(LREF UnicodeException).
755  */
756 extern (C) noreturn onUnicodeError( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__ ) @safe pure
757 {
758     throw new UnicodeException( msg, idx, file, line );
759 }
760 
761 /***********************************
762  * These functions must be defined for any D program linked
763  * against this library.
764  */
765 /+
766 extern (C) void onAssertError(string file, size_t line);
767 extern (C) void onAssertErrorMsg(string file, size_t line, string msg);
768 extern (C) void onUnittestErrorMsg(string file, size_t line, string msg);
769 extern (C) void onRangeError(string file, size_t line);
770 extern (C) void onHiddenFuncError(Object o);
771 +/
772 
773 /***********************************
774  * Function calls to these are generated by the compiler and inserted into
775  * the object code.
776  */
777 
778 extern (C)
779 {
780     /* One of these three is called upon an assert() fail.
781      */
782     void _d_assertp(immutable(char)* file, uint line)
783     {
784         import core.stdc.string : strlen;
785         onAssertError(file[0 .. strlen(file)], line);
786     }
787 
788     void _d_assert_msg(string msg, string file, uint line)
789     {
790         onAssertErrorMsg(file, line, msg);
791     }
792 
793     void _d_assert(string file, uint line)
794     {
795         onAssertError(file, line);
796     }
797 
798     /* One of these three is called upon an assert() fail inside of a unittest block
799      */
800     void _d_unittestp(immutable(char)* file, uint line)
801     {
802         import core.stdc.string : strlen;
803         _d_unittest(file[0 .. strlen(file)], line);
804     }
805 
806     void _d_unittest_msg(string msg, string file, uint line)
807     {
808         onUnittestErrorMsg(file, line, msg);
809     }
810 
811     void _d_unittest(string file, uint line)
812     {
813         _d_unittest_msg("unittest failure", file, line);
814     }
815 
816     /// Called when an invalid array index/slice or associative array key is accessed
817     void _d_arrayboundsp(immutable(char*) file, uint line)
818     {
819         import core.stdc.string : strlen;
820         onRangeError(file[0 .. strlen(file)], line);
821     }
822 
823     /// ditto
824     void _d_arraybounds(string file, uint line)
825     {
826         onRangeError(file, line);
827     }
828 
829     /// Called when an out of range slice of an array is created
830     void _d_arraybounds_slicep(immutable(char*) file, uint line, size_t lower, size_t upper, size_t length)
831     {
832         import core.stdc.string : strlen;
833         onArraySliceError(lower, upper, length, file[0 .. strlen(file)], line);
834     }
835 
836     /// ditto
837     void _d_arraybounds_slice(string file, uint line, size_t lower, size_t upper, size_t length)
838     {
839         onArraySliceError(lower, upper, length, file, line);
840     }
841 
842     /// Called when an out of range array index is accessed
843     void _d_arraybounds_indexp(immutable(char*) file, uint line, size_t index, size_t length)
844     {
845         import core.stdc.string : strlen;
846         onArrayIndexError(index, length, file[0 .. strlen(file)], line);
847     }
848 
849     /// ditto
850     void _d_arraybounds_index(string file, uint line, size_t index, size_t length)
851     {
852         onArrayIndexError(index, length, file, line);
853     }
854 }
855 
856 // TLS storage shared for all errors, chaining might create circular reference
857 private align(2 * size_t.sizeof) void[256] _store;
858 
859 // only Errors for now as those are rarely chained
860 package T staticError(T, Args...)(auto ref Args args)
861     if (is(T : Error))
862 {
863     // pure hack, what we actually need is @noreturn and allow to call that in pure functions
864     static T get()
865     {
866         static assert(__traits(classInstanceSize, T) <= _store.length,
867                       T.stringof ~ " is too large for staticError()");
868 
869         return cast(T) _store.ptr;
870     }
871     auto res = (cast(T function() @trusted pure nothrow @nogc) &get)();
872     import core.lifetime : emplace;
873     emplace(res, args);
874     return res;
875 }
876 
877 // Suppress traceinfo generation when the GC cannot be used.  Workaround for
878 // Bugzilla 14993. We should make stack traces @nogc instead.
879 package class SuppressTraceInfo : Throwable.TraceInfo
880 {
881     override int opApply(scope int delegate(ref const(char[]))) const { return 0; }
882     override int opApply(scope int delegate(ref size_t, ref const(char[]))) const { return 0; }
883     override string toString() const { return null; }
884     static SuppressTraceInfo instance() @trusted @nogc pure nothrow
885     {
886         static immutable SuppressTraceInfo it = new SuppressTraceInfo;
887         return cast(SuppressTraceInfo)it;
888     }
889 }