1 /**
2    This module implements utility code to throw exceptions in @nogc code.
3  */
4 module nogc.exception;
5 
6 
7 import std.experimental.allocator.mallocator: Mallocator;
8 
9 
10 T enforce(E = NoGcException, T, Args...)
11          (T value, auto ref Args args, in string file = __FILE__, in size_t line = __LINE__)
12     if (is(typeof({ if (!value) {} })))
13 {
14     import std.functional: forward;
15     if(!value) throw new NoGcException(NoGcException.Dummy(), file, line, forward!args);
16     return value;
17 }
18 
19 alias NoGcException = NoGcExceptionImpl!Mallocator;
20 
21 class NoGcExceptionImpl(A): Exception {
22 
23     import automem.traits: isGlobal;
24     import automem.vector: StringA;
25     import std.meta: anySatisfy;
26 
27     alias Allocator = A;
28 
29     // just to let enforce pass the right arguments to the constructor
30     protected static struct Dummy {}
31     protected enum isDummy(T) = is(T == Dummy);
32 
33     private StringA!Allocator _msg;
34     static if(!isGlobal!Allocator) private Allocator _allocator;
35 
36     this(Args...)
37         (auto ref Args args, string file = __FILE__, size_t line = __LINE__)
38         if(isGlobal!Allocator && !anySatisfy!(isDummy, Args))
39     {
40         import std.functional: forward;
41         this(Dummy(), file, line, forward!args);
42     }
43 
44     ///
45     @("exception can be constructed in @nogc code")
46     @safe @nogc pure unittest {
47         static const exception = new NoGcException();
48     }
49 
50     this(Args...)
51         (Allocator allocator, auto ref Args args, string file = __FILE__, size_t line = __LINE__)
52         if(!anySatisfy!(isDummy, Args))
53     {
54         import std.functional: forward;
55         this(Dummy(), file, line, forward!args);
56         this._allocator = allocator;
57     }
58 
59     // exists to be used from throw_
60     protected this(Args...)
61                   (in Dummy _, in string file, in size_t line, scope auto ref Args args)
62         if(isGlobal!Allocator)
63     {
64         import nogc.conv: text, BUFFER_SIZE;
65         import std.functional: forward;
66 
67         _msg = text!(BUFFER_SIZE, A)(forward!args);
68         super(_msg[], file, line);
69     }
70 
71     /**
72        Throws a new NoGcException allowing to adjust the file name and line number
73      */
74     static void throw_(T = typeof(this), Args...)(in File file, in Line line, scope auto ref Args args) {
75         import std.functional: forward;
76         throw new T(Dummy(), file.value, line.value, forward!args);
77     }
78 
79     /// Because DIP1008 doesn't do what it should yet
80     final void free() @safe @nogc scope {
81         _msg.free;
82     }
83 }
84 
85 struct File { string value; }
86 struct Line { size_t value; }
87 
88 
89 mixin template NoGcExceptionCtors() {
90 
91     import nogc.exception: File, Line;
92     import automem.traits: isGlobal;
93     import std.meta: anySatisfy;
94 
95     this(Args...)
96         (auto ref Args args, string file = __FILE__, size_t line = __LINE__)
97         if(isGlobal!Allocator && !anySatisfy!(isDummy, Args))
98     {
99         import std.functional: forward;
100         super(forward!args, file, line);
101     }
102 
103     this(Args...)
104         (Allocator allocator, auto ref Args args, string file = __FILE__, size_t line = __LINE__)
105         if(!anySatisfy!(isDummy, Args))
106     {
107         import std.functional: forward;
108         super(allocator, forward!args, file, line);
109     }
110 
111 
112     // exists to be used from throw_
113     protected this(Args...)
114                   (in Dummy _, in string file, in size_t line, scope auto ref Args args)
115         if(isGlobal!Allocator)
116     {
117         import std.functional: forward;
118         super(_, file, line, forward!args);
119     }
120 
121     /**
122        Throws a new NoGcException allowing to adjust the file name and line number
123      */
124      static void throw_(T = typeof(this), Args...)(in File file, in Line line, scope auto ref Args args) {
125         import std.functional: forward;
126         typeof(super).throw_!T(file, line, forward!args);
127     }
128 }