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 }