The OpenD Programming Language

1 /++
2 `@nogc` exceptions and errors definitions.
3 
4 Most of the API Requires DIP1008.
5 +/
6 module mir.exception;
7 
8 version(D_Exceptions):
9 
10 version(D_Ddoc)
11     private enum _version_D_Ddoc = true;
12 else
13     private enum _version_D_Ddoc = false;
14 
15 private enum NOGCEXP = __traits(compiles, (()@nogc {throw new Exception("");})());
16 private enum HASFORMAT = __traits(compiles, (()@nogc {import mir.format;})());
17 
18 package template staticException(string fmt, string file, int line)
19 {
20     static immutable staticException = new Exception(fmt, file, line);
21 }
22 
23 @trusted pure nothrow @nogc
24 Exception toMutable()(immutable Exception e)
25 {
26     return cast() e;
27 }
28 
29 @trusted pure nothrow @nogc
30 Error toMutable()(immutable Error e)
31 {
32     return cast() e;
33 }
34 
35 @trusted pure nothrow @nogc
36 Exception toMutable()(const Exception e)
37 {
38     return cast() e;
39 }
40 
41 @trusted pure nothrow @nogc
42 Error toMutable()(const Error e)
43 {
44     return cast() e;
45 }
46 
47 ///
48 auto ref enforce(string fmt, string file = __FILE__, int line = __LINE__, Expr)(scope auto return ref Expr arg) @trusted
49 {
50     version(LDC) pragma(inline, true);
51     import core.lifetime: forward;
52     import mir.utility: _expect;
53     static if (__traits(compiles, arg !is null))
54     {
55         if (_expect(arg !is null, true))
56             return forward!arg;
57     }
58     else
59     {
60         if (_expect(cast(bool)arg, true))
61             return forward!arg;
62     }
63     throw staticException!(fmt, file, line).toMutable;
64 }
65 
66 ///
67 @safe pure nothrow @nogc
68 version (mir_core_test) unittest
69 {
70     import mir.exception;
71     try enforce!"Msg"(false);
72     catch(Exception e) assert(e.msg == "Msg");
73 }
74 
75 /++
76 +/
77 class MirException : Exception
78 {
79     ///
80     mixin MirThrowableImpl;
81 }
82 
83 /// Generic style
84 version (mir_test) static if (NOGCEXP && HASFORMAT)
85 @safe pure nothrow @nogc
86 unittest
87 {
88     static if (__traits(compiles, (()@nogc {import mir.format;})()))
89     {
90         import mir.exception;
91         try throw new MirException("Hi D", 2, "!");
92         catch(MirException e) assert(e.scopeMessage == "Hi D2!");
93     }
94 }
95 
96 /// Generic style, GC allocated MSG
97 version (mir_test) static if (NOGCEXP && HASFORMAT)
98 @safe pure nothrow @nogc
99 unittest
100 {
101     static if (__traits(compiles, (()@nogc {import mir.format;})()))
102     {
103         import mir.exception;
104         try throw new MirException("Hi D", 2, "!");
105         catch(Exception e) assert(e.message == "Hi D2!");
106     }
107 }
108 
109 /// C++ style
110 version (mir_test) static if (NOGCEXP && HASFORMAT)
111 @safe pure nothrow @nogc
112 unittest
113 {
114     static if (__traits(compiles, (()@nogc {import mir.format;})()))
115     {
116         import mir.exception;
117         import mir.format;
118         try throw new MirException(stringBuf() << "Hi D" << 2 << "!" << getData);
119         catch(Exception e) assert(e.scopeMessage == "Hi D2!");
120     }
121 }
122 
123 /// Low-level style
124 version (mir_test) static if (NOGCEXP && HASFORMAT)
125 @safe pure nothrow @nogc
126 unittest
127 {
128     static if (__traits(compiles, (()@nogc {import mir.format;})()))
129     {
130         import mir.exception;
131         import mir.format;
132         auto buffer = stringBuf();
133         try throw new MirException(buf.print( "Hi D", 2, "!").data);
134         catch(Exception e) assert(e.msg == "Hi D2!");
135     }
136 }
137 
138 ///
139 version (mir_core_test) static if (NOGCEXP)
140 @safe pure nothrow @nogc
141 unittest
142 {
143     @safe pure nothrow @nogc 
144     bool func(scope const(char)[] msg)
145     {
146         /// scope messages are copied
147         try throw new MirException(msg);
148         catch(Exception e) assert(e.msg == msg);
149 
150         /// immutable strings are not copied
151         static immutable char[] gmsg = "global msg";
152         try throw new MirException(gmsg);
153         catch(Exception e) assert(e.msg is gmsg);
154 
155         return __ctfe;
156     }
157 
158     assert(func("runtime-time check") == 0);
159 
160     static assert(func("compile-time check") == 1);
161 }
162 
163 // ///
164 // auto ref enforce(T, Args...)(scope auto return ref T arg, lazy @nogc Args args, string file = __FILE__, int line = __LINE__) @nogc
165 //     if (Args.length)
166 // {
167 //     import mir.utility: _expect;
168 //     static if (__traits(compiles, arg !is null))
169 //     {
170 //         if (_expect(arg !is null, true))
171 //             return arg;
172 //     }
173 //     else
174 //     {
175 //         if (_expect(cast(bool)arg, true))
176 //             return arg;
177 //     }
178 //     import mir.format;
179 //     stringBuf buf;
180 //     throw new MirException(buf.print(args).data, file, line);
181 // }
182 
183 // ///
184 // @safe pure nothrow @nogc
185 // version (mir_core_test) unittest static if (NOGCEXP && HASFORMAT)
186 // {
187 //     import mir.exception;
188 //     try enforce(false, "Hi D", 2, "!");
189 //     catch(Exception e) assert(e.msg == "Hi D2!");
190 // }
191 
192 // ///
193 // auto ref enforce(T)(scope auto return ref T arg, lazy scope const(char)[] msg, string file = __FILE__, int line = __LINE__) @nogc
194 // {
195 //     import core.lifetime: forward;
196 //     import mir.utility: _expect;
197 //     static if (__traits(compiles, arg !is null))
198 //     {
199 //         if (_expect(arg !is null, true))
200 //             return forward!arg[0];
201 //     }
202 //     else
203 //     {
204 //         if (_expect(cast(bool)arg, true))
205 //             return forward!arg[0];
206 //     }
207 //     throw new MirException(msg, file, line);
208 // }
209 
210 // ///
211 // @safe pure nothrow @nogc
212 // version (mir_core_test) unittest static if (NOGCEXP && HASFORMAT)
213 // {
214 //     import mir.exception;
215 //     try enforce(false, "Msg");
216 //     catch(Exception e) assert(e.msg == "Msg");
217 // }
218 
219 
220 /++
221 +/
222 class MirError : Error
223 {
224     ///
225     mixin MirThrowableImpl;
226 }
227 
228 ///
229 @system pure nothrow @nogc
230 version (mir_test) static if (NOGCEXP)
231 unittest
232 {
233     @system pure nothrow @nogc 
234     bool func(scope const(char)[] msg)
235     {
236         /// scope messages are copied
237         try throw new MirException(msg);
238         catch(Exception e) assert(e.msg == msg);
239     
240         /// immutable strings are not copied
241         static immutable char[] gmsg = "global msg";
242         try throw new MirError(gmsg);
243         catch(Error e) assert(e.msg is gmsg);
244     
245         return __ctfe;
246     }
247 
248     assert(func("runtime-time check") == 0);
249 
250     static assert(func("compile-time check") == 1);
251 }
252 
253 /++
254 +/
255 mixin template MirThrowableImpl()
256 {
257     private bool _global;
258     private char[maxMirExceptionMsgLen] _payload = void;
259     import mir.exception: maxMirExceptionMsgLen, mirExceptionInitilizePayloadImpl;
260 
261     const(char)[] _msg;
262 
263     override string message() const @safe pure nothrow
264     {
265         return _msg ? _msg.idup : msg;
266     }
267 
268     const(char)[] scopeMessage() scope const @safe pure nothrow @nogc
269     {
270         return _msg ? _msg : msg;
271     }
272 
273     /++
274     Params:
275         msg = message. No-scope `msg` is assumed to have the same lifetime as the throwable. scope strings are copied to internal buffer.
276         file = file name, zero terminated global string
277         line = line number
278         nextInChain = next exception in the chain (optional)
279     +/
280     @nogc @trusted pure nothrow this(scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
281     {
282         this._msg = mirExceptionInitilizePayloadImpl(_payload, msg);
283         super(cast(immutable)this._msg, file, line, nextInChain);
284     }
285 
286     /// ditto
287     @nogc @trusted pure nothrow this(scope const(char)[] msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
288     {
289         this._msg = mirExceptionInitilizePayloadImpl(_payload, msg);
290         super(cast(immutable)this._msg, file, line, nextInChain);
291     }
292 
293     /// ditto
294     @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
295     {
296         this._global = true;
297         super(msg, file, line, nextInChain);
298     }
299 
300     /// ditto
301     @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
302     {
303         this._global = true;
304         super(msg, file, line, nextInChain);
305     }
306 
307     ///
308     ~this() @trusted
309     {
310         import mir.internal.memory: free;
311         if (!_global && msg.ptr != _payload.ptr)
312             free(cast(void*)msg.ptr);
313     }
314 
315     /++
316     Generic multiargument overload.
317     Constructs a string using the `print` function.
318     +/
319     this(Args...)(auto ref scope const Args args, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) pure
320         if (Args.length > 1 && !is(Args[$ - 1] == Throwable))
321     {
322         static assert (__traits(compiles, {import mir.format;}), "MirThrowableImpl needs mir-algorithm for mir.format and exception formatting.");
323         import mir.format;
324         auto buf = stringBuf();
325         foreach(ref arg; args)
326             buf.print(arg);
327         this(buf.data, file, line, nextInChain);
328     }
329 
330     this(Args...)(auto ref scope const Args args) pure @trusted
331         if (Args.length > 4 && is(Args[$ - 1] == Throwable))
332     {
333         static assert (__traits(compiles, {import mir.format;}), "MirThrowableImpl needs mir-algorithm for mir.format and exception formatting.");
334         import mir.format;
335         auto buf = stringBuf();
336         foreach(ref arg; args[0 .. $ - 3])
337             buf.print(arg);
338         this(buf.data, args[$ - 3 .. $ - 1], cast() args[$ - 1]);
339     }
340 }
341 
342 ///
343 enum maxMirExceptionMsgLen = 447;
344 
345 pragma(inline, false)
346 pure nothrow @nogc @safe
347 const(char)[] mirExceptionInitilizePayloadImpl(ref return char[maxMirExceptionMsgLen] payload, scope const(char)[] msg)
348 {
349     import mir.internal.memory: malloc;
350     import core.stdc.string: memcpy;
351     if (msg.length > payload.length)
352     {
353         if (auto ret = (() @trusted
354             {
355                 if (__ctfe)
356                     return null;
357                 if (auto ptr = malloc(msg.length))
358                 {
359                     memcpy(ptr, msg.ptr, msg.length);
360                     return cast(const(char)[]) ptr[0 .. msg.length];
361                 }
362                 return null;
363             })())
364             return ret;
365         msg = msg[0 .. payload.length];
366         // remove tail UTF-8 symbol chunk if any
367         uint c = msg[$-1];
368         if (c > 0b_0111_1111)
369         {
370             do {
371                 c = msg[$-1];
372                 msg = msg[0 .. $ - 1];
373             }
374             while (msg.length && c < 0b_1100_0000);
375         }
376     }
377     if (__ctfe)
378         payload[][0 .. msg.length] = msg;
379     else
380         (() @trusted => memcpy(payload.ptr, msg.ptr, msg.length))();
381     return payload[0 .. msg.length];
382 }