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