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 }