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         // We don't ever append to the vector so it will never invalidate the slice.
69         // Changing _msg to be `const` doesn't compile though.
70         super(() @trusted { return _msg[]; }(), file, line);
71     }
72 
73     /**
74        Throws a new NoGcException allowing to adjust the file name and line number
75      */
76     static void throw_(T = typeof(this), Args...)(in File file, in Line line, scope auto ref Args args) {
77         import std.functional: forward;
78         throw new T(Dummy(), file.value, line.value, forward!args);
79     }
80 
81     /// Because DIP1008 doesn't do what it should yet
82     final void free() @safe @nogc scope {
83         _msg.free;
84     }
85 }
86 
87 struct File { string value; }
88 struct Line { size_t value; }
89 
90 
91 mixin template NoGcExceptionCtors() {
92 
93     import nogc.exception: File, Line;
94     import automem.traits: isGlobal;
95     import std.meta: anySatisfy;
96 
97     this(Args...)
98         (auto ref Args args, string file = __FILE__, size_t line = __LINE__)
99         if(isGlobal!Allocator && !anySatisfy!(isDummy, Args))
100     {
101         import std.functional: forward;
102         super(forward!args, file, line);
103     }
104 
105     this(Args...)
106         (Allocator allocator, auto ref Args args, string file = __FILE__, size_t line = __LINE__)
107         if(!anySatisfy!(isDummy, Args))
108     {
109         import std.functional: forward;
110         super(allocator, forward!args, file, line);
111     }
112 
113 
114     // exists to be used from throw_
115     protected this(Args...)
116                   (in Dummy _, in string file, in size_t line, scope auto ref Args args)
117         if(isGlobal!Allocator)
118     {
119         import std.functional: forward;
120         super(_, file, line, forward!args);
121     }
122 
123     /**
124        Throws a new NoGcException allowing to adjust the file name and line number
125      */
126      static void throw_(T = typeof(this), Args...)(in File file, in Line line, scope auto ref Args args) {
127         import std.functional: forward;
128         typeof(super).throw_!T(file, line, forward!args);
129     }
130 }