diff options
Diffstat (limited to 'libphobos/src/std/exception.d')
-rw-r--r-- | libphobos/src/std/exception.d | 920 |
1 files changed, 529 insertions, 391 deletions
diff --git a/libphobos/src/std/exception.d b/libphobos/src/std/exception.d index 56133c9..5fcee2a 100644 --- a/libphobos/src/std/exception.d +++ b/libphobos/src/std/exception.d @@ -5,6 +5,7 @@ handling. It also defines functions intended to aid in unit testing. $(SCRIPT inhibitQuickIndex = 1;) +$(DIVC quickindex, $(BOOKTABLE, $(TR $(TH Category) $(TH Functions)) $(TR $(TD Assumptions) $(TD @@ -17,7 +18,6 @@ $(TR $(TD Assumptions) $(TD $(TR $(TD Enforce) $(TD $(LREF doesPointTo) $(LREF enforce) - $(LREF enforceEx) $(LREF errnoEnforce) )) $(TR $(TD Handlers) $(TD @@ -32,68 +32,107 @@ $(TR $(TD Other) $(TD $(LREF ErrnoException) $(LREF RangePrimitive) )) -) +)) - Synopsis of some of std.exception's functions: - -------------------- - string synopsis() - { - FILE* f = enforce(fopen("some/file")); - // f is not null from here on - FILE* g = enforce!WriteException(fopen("some/other/file", "w")); - // g is not null from here on + Copyright: Copyright Andrei Alexandrescu 2008-, Jonathan M Davis 2011-. + License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) + Authors: $(HTTP erdani.org, Andrei Alexandrescu) and + $(HTTP jmdavisprog.com, Jonathan M Davis) + Source: $(PHOBOSSRC std/exception.d) - Exception e = collectException(write(g, readln(f))); - if (e) - { - ... an exception occurred... - ... We have the exception to play around with... - } + +/ +module std.exception; - string msg = collectExceptionMsg(write(g, readln(f))); - if (msg) - { - ... an exception occurred... - ... We have the message from the exception but not the exception... - } +/// Synopis +@system unittest +{ + import core.stdc.stdlib : malloc, free; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map, splitter; + import std.algorithm.searching : endsWith; + import std.conv : ConvException, to; + import std.range : front, retro; + + // use enforce like assert + int a = 3; + enforce(a > 2, "a needs to be higher than 2."); + + // enforce can throw a custom exception + enforce!ConvException(a > 2, "a needs to be higher than 2."); + + // enforce will return it's input + enum size = 42; + auto memory = enforce(malloc(size), "malloc failed")[0 .. size]; + scope(exit) free(memory.ptr); + + // collectException can be used to test for exceptions + Exception e = collectException("abc".to!int); + assert(e.file.endsWith("conv.d")); + + // and just for the exception message + string msg = collectExceptionMsg("abc".to!int); + assert(msg == "Unexpected 'a' when converting from type string to type int"); + + // assertThrown can be used to assert that an exception is thrown + assertThrown!ConvException("abc".to!int); - char[] line; - enforce(readln(f, line)); - return assumeUnique(line); + // ifThrown can be used to provide a default value if an exception is thrown + assert("x".to!int().ifThrown(0) == 0); + + // handle is a more advanced version of ifThrown for ranges + auto r = "12,1337z32,54".splitter(',').map!(a => to!int(a)); + auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0); + assert(h.equal([12, 0, 54])); + assertThrown!ConvException(h.retro.equal([54, 0, 12])); + + // basicExceptionCtors avoids the boilerplate when creating custom exceptions + static class MeaCulpa : Exception + { + mixin basicExceptionCtors; } - -------------------- + e = collectException((){throw new MeaCulpa("diagnostic message");}()); + assert(e.msg == "diagnostic message"); + assert(e.file == __FILE__); + assert(e.line == __LINE__ - 3); - Copyright: Copyright Andrei Alexandrescu 2008-, Jonathan M Davis 2011-. - License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) - Authors: $(HTTP erdani.org, Andrei Alexandrescu) and Jonathan M Davis - Source: $(PHOBOSSRC std/_exception.d) + // assumeWontThrow can be used to cast throwing code into `nothrow` + void exceptionFreeCode() nothrow + { + // auto-decoding only throws if an invalid UTF char is given + assumeWontThrow("abc".front); + } - +/ -module std.exception; + // assumeUnique can be used to cast mutable instance to an `immutable` one + // use with care + char[] str = " mutable".dup; + str[0 .. 2] = "im"; + immutable res = assumeUnique(str); + assert(res == "immutable"); +} import std.range.primitives; import std.traits; /++ Asserts that the given expression does $(I not) throw the given type - of $(D Throwable). If a $(D Throwable) of the given type is thrown, + of `Throwable`. If a `Throwable` of the given type is thrown, it is caught and does not escape assertNotThrown. Rather, an - $(D AssertError) is thrown. However, any other $(D Throwable)s will escape. + `AssertError` is thrown. However, any other `Throwable`s will escape. Params: - T = The $(D Throwable) to test for. + T = The `Throwable` to test for. expression = The expression to test. msg = Optional message to output on test failure. If msg is empty, and the thrown exception has a non-empty msg field, the exception's msg field will be output on test failure. file = The file where the error occurred. - Defaults to $(D __FILE__). + Defaults to `__FILE__`. line = The line where the error occurred. - Defaults to $(D __LINE__). + Defaults to `__LINE__`. Throws: - $(D AssertError) if the given $(D Throwable) is thrown. + `AssertError` if the given `Throwable` is thrown. Returns: the result of `expression`. @@ -226,22 +265,22 @@ auto assertNotThrown(T : Throwable = Exception, E) } /++ - Asserts that the given expression throws the given type of $(D Throwable). - The $(D Throwable) is caught and does not escape assertThrown. However, - any other $(D Throwable)s $(I will) escape, and if no $(D Throwable) - of the given type is thrown, then an $(D AssertError) is thrown. + Asserts that the given expression throws the given type of `Throwable`. + The `Throwable` is caught and does not escape assertThrown. However, + any other `Throwable`s $(I will) escape, and if no `Throwable` + of the given type is thrown, then an `AssertError` is thrown. Params: - T = The $(D Throwable) to test for. + T = The `Throwable` to test for. expression = The expression to test. msg = Optional message to output on test failure. file = The file where the error occurred. - Defaults to $(D __FILE__). + Defaults to `__FILE__`. line = The line where the error occurred. - Defaults to $(D __LINE__). + Defaults to `__LINE__`. Throws: - $(D AssertError) if the given $(D Throwable) is not thrown. + `AssertError` if the given `Throwable` is not thrown. +/ void assertThrown(T : Throwable = Exception, E) (lazy E expression, @@ -355,55 +394,52 @@ void assertThrown(T : Throwable = Exception, E) /++ Enforces that the given value is true. + If the given value is false, an exception is thrown. + The + $(UL + $(LI `msg` - error message as a `string`) + $(LI `dg` - custom delegate that return a string and is only called if an exception occurred) + $(LI `ex` - custom exception to be thrown. It is `lazy` and is only created if an exception occurred) + ) Params: value = The value to test. - E = Exception type to throw if the value evalues to false. + E = Exception type to throw if the value evaluates to false. msg = The error message to put in the exception if it is thrown. + dg = The delegate to be called if the value evaluates to false. + ex = The exception to throw if the value evaluates to false. file = The source file of the caller. line = The line number of the caller. - Returns: $(D value), if `cast(bool) value` is true. Otherwise, - $(D new Exception(msg)) is thrown. + Returns: `value`, if `cast(bool) value` is true. Otherwise, + depending on the chosen overload, `new Exception(msg)`, `dg()` or `ex` is thrown. Note: - $(D enforce) is used to throw exceptions and is therefore intended to + `enforce` is used to throw exceptions and is therefore intended to aid in error handling. It is $(I not) intended for verifying the logic - of your program. That is what $(D assert) is for. Also, do not use - $(D enforce) inside of contracts (i.e. inside of $(D in) and $(D out) - blocks and $(D invariant)s), because they will be compiled out when - compiling with $(I -release). Use $(D assert) in contracts. - - Example: - -------------------- - auto f = enforce(fopen("data.txt")); - auto line = readln(f); - enforce(line.length, "Expected a non-empty line."); - -------------------- + of your program. That is what `assert` is for. Also, do not use + `enforce` inside of contracts (i.e. inside of `in` and `out` + blocks and `invariant`s), because contracts are compiled out when + compiling with $(I -release). + + If a delegate is passed, the safety and purity of this function are inferred + from `Dg`'s safety and purity. +/ -T enforce(E : Throwable = Exception, T)(T value, lazy const(char)[] msg = null, -string file = __FILE__, size_t line = __LINE__) -if (is(typeof({ if (!value) {} }))) +template enforce(E : Throwable = Exception) +if (is(typeof(new E("", string.init, size_t.init)) : Throwable) || + is(typeof(new E(string.init, size_t.init)) : Throwable)) { - if (!value) bailOut!E(file, line, msg); - return value; + /// + T enforce(T)(T value, lazy const(char)[] msg = null, + string file = __FILE__, size_t line = __LINE__) + if (is(typeof({ if (!value) {} }))) + { + if (!value) bailOut!E(file, line, msg); + return value; + } } -/++ - Enforces that the given value is true. - - Params: - value = The value to test. - dg = The delegate to be called if the value evaluates to false. - file = The source file of the caller. - line = The line number of the caller. - - Returns: $(D value), if `cast(bool) value` is true. Otherwise, the given - delegate is called. - - The safety and purity of this function are inferred from $(D Dg)'s safety - and purity. - +/ +/// ditto T enforce(T, Dg, string file = __FILE__, size_t line = __LINE__) (T value, scope Dg dg) if (isSomeFunction!Dg && is(typeof( dg() )) && @@ -413,23 +449,40 @@ if (isSomeFunction!Dg && is(typeof( dg() )) && return value; } -private void bailOut(E : Throwable = Exception)(string file, size_t line, in char[] msg) +/// ditto +T enforce(T)(T value, lazy Throwable ex) { - static if (is(typeof(new E(string.init, string.init, size_t.init)))) - { - throw new E(msg ? msg.idup : "Enforcement failed", file, line); - } - else static if (is(typeof(new E(string.init, size_t.init)))) - { - throw new E(file, line); - } - else - { - static assert(0, "Expected this(string, string, size_t) or this(string, size_t)" ~ - " constructor for " ~ __traits(identifier, E)); - } + if (!value) throw ex(); + return value; +} + +/// +@system unittest +{ + import core.stdc.stdlib : malloc, free; + import std.conv : ConvException, to; + + // use enforce like assert + int a = 3; + enforce(a > 2, "a needs to be higher than 2."); + + // enforce can throw a custom exception + enforce!ConvException(a > 2, "a needs to be higher than 2."); + + // enforce will return it's input + enum size = 42; + auto memory = enforce(malloc(size), "malloc failed")[0 .. size]; + scope(exit) free(memory.ptr); +} + +/// +@safe unittest +{ + assertNotThrown(enforce(true, new Exception("this should not be thrown"))); + assertThrown(enforce(false, new Exception("this should be thrown"))); } +/// @safe unittest { assert(enforce(123) == 123); @@ -447,9 +500,35 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha } } +/// Alias your own enforce function +@safe unittest +{ + import std.conv : ConvException; + alias convEnforce = enforce!ConvException; + assertNotThrown(convEnforce(true)); + assertThrown!ConvException(convEnforce(false, "blah")); +} + +private noreturn bailOut(E : Throwable = Exception)(string file, size_t line, scope const(char)[] msg) +{ + static if (is(typeof(new E(string.init, string.init, size_t.init)))) + { + throw new E(msg ? msg.idup : "Enforcement failed", file, line); + } + else static if (is(typeof(new E(string.init, size_t.init)))) + { + throw new E(file, line); + } + else + { + static assert(0, "Expected this(string, string, size_t) or this(string, size_t)" ~ + " constructor for " ~ __traits(identifier, E)); + } +} + +// https://issues.dlang.org/show_bug.cgi?id=10510 @safe unittest { - // Issue 10510 extern(C) void cFoo() { } enforce(false, &cFoo); } @@ -457,14 +536,12 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha // purity and safety inference test @system unittest { - import std.meta : AliasSeq; - - foreach (EncloseSafe; AliasSeq!(false, true)) - foreach (EnclosePure; AliasSeq!(false, true)) + static foreach (EncloseSafe; [false, true]) + static foreach (EnclosePure; [false, true]) { - foreach (BodySafe; AliasSeq!(false, true)) - foreach (BodyPure; AliasSeq!(false, true)) - { + static foreach (BodySafe; [false, true]) + static foreach (BodyPure; [false, true]) + {{ enum code = "delegate void() " ~ (EncloseSafe ? "@safe " : "") ~ @@ -485,11 +562,11 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha "code = ", code); static assert(__traits(compiles, mixin(code)()) == expect); - } + }} } } -// Test for bugzilla 8637 +// Test for https://issues.dlang.org/show_bug.cgi?id=8637 @system unittest { struct S @@ -523,10 +600,9 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha enforce!E2(s); } +// https://issues.dlang.org/show_bug.cgi?id=14685 @safe unittest { - // Issue 14685 - class E : Exception { this() { super("Not found"); } @@ -535,35 +611,6 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha } /++ - Enforces that the given value is true. - - Params: - value = The value to test. - ex = The exception to throw if the value evaluates to false. - - Returns: $(D value), if `cast(bool) value` is true. Otherwise, $(D ex) is - thrown. - - Example: - -------------------- - auto f = enforce(fopen("data.txt")); - auto line = readln(f); - enforce(line.length, new IOException); // expect a non-empty line - -------------------- - +/ -T enforce(T)(T value, lazy Throwable ex) -{ - if (!value) throw ex(); - return value; -} - -@safe unittest -{ - assertNotThrown(enforce(true, new Exception("this should not be thrown"))); - assertThrown(enforce(false, new Exception("this should be thrown"))); -} - -/++ Enforces that the given value is true, throwing an `ErrnoException` if it is not. @@ -571,125 +618,37 @@ T enforce(T)(T value, lazy Throwable ex) value = The value to test. msg = The message to include in the `ErrnoException` if it is thrown. - Returns: $(D value), if `cast(bool) value` is true. Otherwise, + Returns: `value`, if `cast(bool) value` is true. Otherwise, $(D new ErrnoException(msg)) is thrown. It is assumed that the last - operation set $(D errno) to an error code corresponding with the failed + operation set `errno` to an error code corresponding with the failed condition. - - Example: - -------------------- - auto f = errnoEnforce(fopen("data.txt")); - auto line = readln(f); - enforce(line.length); // expect a non-empty line - -------------------- - +/ -T errnoEnforce(T, string file = __FILE__, size_t line = __LINE__) - (T value, lazy string msg = null) -{ - if (!value) throw new ErrnoException(msg, file, line); - return value; -} - - -/++ - If $(D !value) is $(D false), $(D value) is returned. Otherwise, - $(D new E(msg, file, line)) is thrown. Or if $(D E) doesn't take a message - and can be constructed with $(D new E(file, line)), then - $(D new E(file, line)) will be thrown. - - This is legacy name, it is recommended to use $(D enforce!E) instead. - - Example: - -------------------- - auto f = enforceEx!FileMissingException(fopen("data.txt")); - auto line = readln(f); - enforceEx!DataCorruptionException(line.length); - -------------------- +/ -template enforceEx(E : Throwable) -if (is(typeof(new E("", __FILE__, __LINE__)))) -{ - /++ Ditto +/ - T enforceEx(T)(T value, lazy string msg = "", string file = __FILE__, size_t line = __LINE__) - { - if (!value) throw new E(msg, file, line); - return value; - } -} - -/++ Ditto +/ -template enforceEx(E : Throwable) -if (is(typeof(new E(__FILE__, __LINE__))) && !is(typeof(new E("", __FILE__, __LINE__)))) -{ - /++ Ditto +/ - T enforceEx(T)(T value, string file = __FILE__, size_t line = __LINE__) - { - if (!value) throw new E(file, line); - return value; - } -} +alias errnoEnforce = enforce!ErrnoException; +/// @system unittest { - import core.exception : OutOfMemoryError; - import std.array : empty; - assertNotThrown(enforceEx!Exception(true)); - assertNotThrown(enforceEx!Exception(true, "blah")); - assertNotThrown(enforceEx!OutOfMemoryError(true)); - - { - auto e = collectException(enforceEx!Exception(false)); - assert(e !is null); - assert(e.msg.empty); - assert(e.file == __FILE__); - assert(e.line == __LINE__ - 4); - } + import core.stdc.stdio : fclose, fgets, fopen; + import std.file : thisExePath; + import std.string : toStringz; - { - auto e = collectException(enforceEx!Exception(false, "hello", "file", 42)); - assert(e !is null); - assert(e.msg == "hello"); - assert(e.file == "file"); - assert(e.line == 42); - } - - { - auto e = collectException!Error(enforceEx!OutOfMemoryError(false)); - assert(e !is null); - assert(e.msg == "Memory allocation failed"); - assert(e.file == __FILE__); - assert(e.line == __LINE__ - 4); - } - - { - auto e = collectException!Error(enforceEx!OutOfMemoryError(false, "file", 42)); - assert(e !is null); - assert(e.msg == "Memory allocation failed"); - assert(e.file == "file"); - assert(e.line == 42); - } - - static assert(!is(typeof(enforceEx!int(true)))); + auto f = fopen(thisExePath.toStringz, "r").errnoEnforce; + scope(exit) fclose(f); + char[100] buf; + auto line = fgets(buf.ptr, buf.length, f); + enforce(line !is null); // expect a non-empty line } -@safe unittest -{ - alias enf = enforceEx!Exception; - assertNotThrown(enf(true)); - assertThrown(enf(false, "blah")); -} - - /++ Catches and returns the exception thrown from the given expression. - If no exception is thrown, then null is returned and $(D result) is + If no exception is thrown, then null is returned and `result` is set to the result of the expression. - Note that while $(D collectException) $(I can) be used to collect any - $(D Throwable) and not just $(D Exception)s, it is generally ill-advised to - catch anything that is neither an $(D Exception) nor a type derived from - $(D Exception). So, do not use $(D collectException) to collect - non-$(D Exception)s unless you're sure that that's what you really want to + Note that while `collectException` $(I can) be used to collect any + `Throwable` and not just `Exception`s, it is generally ill-advised to + catch anything that is neither an `Exception` nor a type derived from + `Exception`. So, do not use `collectException` to collect + non-`Exception`s unless you're sure that that's what you really want to do. Params: @@ -723,14 +682,14 @@ T collectException(T = Exception, E)(lazy E expression, ref E result) /++ Catches and returns the exception thrown from the given expression. - If no exception is thrown, then null is returned. $(D E) can be - $(D void). - - Note that while $(D collectException) $(I can) be used to collect any - $(D Throwable) and not just $(D Exception)s, it is generally ill-advised to - catch anything that is neither an $(D Exception) nor a type derived from - $(D Exception). So, do not use $(D collectException) to collect - non-$(D Exception)s unless you're sure that that's what you really want to + If no exception is thrown, then null is returned. `E` can be + `void`. + + Note that while `collectException` $(I can) be used to collect any + `Throwable` and not just `Exception`s, it is generally ill-advised to + catch anything that is neither an `Exception` nor a type derived from + `Exception`. So, do not use `collectException` to collect + non-`Exception`s unless you're sure that that's what you really want to do. Params: @@ -750,25 +709,26 @@ T collectException(T : Throwable = Exception, E)(lazy E expression) return null; } +/// @safe unittest { int foo() { throw new Exception("blah"); } - assert(collectException(foo())); + assert(collectException(foo()).msg == "blah"); } /++ Catches the exception thrown from the given expression and returns the msg property of that exception. If no exception is thrown, then null is - returned. $(D E) can be $(D void). + returned. `E` can be `void`. If an exception is thrown but it has an empty message, then - $(D emptyExceptionMsg) is returned. + `emptyExceptionMsg` is returned. - Note that while $(D collectExceptionMsg) $(I can) be used to collect any - $(D Throwable) and not just $(D Exception)s, it is generally ill-advised to - catch anything that is neither an $(D Exception) nor a type derived from - $(D Exception). So, do not use $(D collectExceptionMsg) to collect - non-$(D Exception)s unless you're sure that that's what you really want to + Note that while `collectExceptionMsg` $(I can) be used to collect any + `Throwable` and not just `Exception`s, it is generally ill-advised to + catch anything that is neither an `Exception` nor a type derived from + `Exception`. So, do not use `collectExceptionMsg` to collect + non-`Exception`s unless you're sure that that's what you really want to do. Params: @@ -808,18 +768,18 @@ enum emptyExceptionMsg = "<Empty Exception Message>"; /** * Casts a mutable array to an immutable array in an idiomatic - * manner. Technically, $(D assumeUnique) just inserts a cast, + * manner. Technically, `assumeUnique` just inserts a cast, * but its name documents assumptions on the part of the - * caller. $(D assumeUnique(arr)) should only be called when + * caller. `assumeUnique(arr)` should only be called when * there are no more active mutable aliases to elements of $(D - * arr). To strengthen this assumption, $(D assumeUnique(arr)) - * also clears $(D arr) before returning. Essentially $(D + * arr). To strengthen this assumption, `assumeUnique(arr)` + * also clears `arr` before returning. Essentially $(D * assumeUnique(arr)) indicates commitment from the caller that there - * is no more mutable access to any of $(D arr)'s elements + * is no more mutable access to any of `arr`'s elements * (transitively), and that all future accesses will be done through - * the immutable array returned by $(D assumeUnique). + * the immutable array returned by `assumeUnique`. * - * Typically, $(D assumeUnique) is used to return arrays from + * Typically, `assumeUnique` is used to return arrays from * functions that have allocated and built them. * * Params: @@ -829,6 +789,7 @@ enum emptyExceptionMsg = "<Empty Exception Message>"; * * Example: * + * $(RUNNABLE_EXAMPLE * ---- * string letters() * { @@ -840,14 +801,16 @@ enum emptyExceptionMsg = "<Empty Exception Message>"; * return assumeUnique(result); * } * ---- + * ) * - * The use in the example above is correct because $(D result) - * was private to $(D letters) and is inaccessible in writing + * The use in the example above is correct because `result` + * was private to `letters` and is inaccessible in writing * after the function returns. The following example shows an - * incorrect use of $(D assumeUnique). + * incorrect use of `assumeUnique`. * * Bad: * + * $(RUNNABLE_EXAMPLE * ---- * private char[] buffer; * string letters(char first, char last) @@ -862,11 +825,13 @@ enum emptyExceptionMsg = "<Empty Exception Message>"; * return assumeUnique(sneaky); // BAD * } * ---- + * ) * * The example above wreaks havoc on client code because it is * modifying arrays that callers considered immutable. To obtain an - * immutable array from the writable array $(D buffer), replace + * immutable array from the writable array `buffer`, replace * the last line with: + * * ---- * return to!(string)(sneaky); // not that sneaky anymore * ---- @@ -878,6 +843,8 @@ enum emptyExceptionMsg = "<Empty Exception Message>"; * marked as a pure function. The following example does not * need to call assumeUnique because the compiler can infer the * uniqueness of the array in the pure function: + * + * $(RUNNABLE_EXAMPLE * ---- * string letters() pure * { @@ -889,16 +856,17 @@ enum emptyExceptionMsg = "<Empty Exception Message>"; * return result; * } * ---- + * ) * * For more on infering uniqueness see the $(B unique) and * $(B lent) keywords in the - * $(HTTP archjava.fluid.cs.cmu.edu/papers/oopsla02.pdf, ArchJava) + * $(HTTP www.cs.cmu.edu/~aldrich/papers/aldrich-dissertation.pdf, ArchJava) * language. * - * The downside of using $(D assumeUnique)'s + * The downside of using `assumeUnique`'s * convention-based usage is that at this time there is no * formal checking of the correctness of the assumption; - * on the upside, the idiomatic use of $(D assumeUnique) is + * on the upside, the idiomatic use of `assumeUnique` is * simple and rare enough to be tolerable. * */ @@ -921,34 +889,38 @@ immutable(T[U]) assumeUnique(T, U)(ref T[U] array) pure nothrow return result; } +/// @system unittest { - // @system due to assumeUnique int[] arr = new int[1]; - auto arr1 = assumeUnique(arr); - assert(is(typeof(arr1) == immutable(int)[]) && arr == null); + auto arr1 = arr.assumeUnique; + static assert(is(typeof(arr1) == immutable(int)[])); + assert(arr == null); + assert(arr1 == [0]); } -// @@@BUG@@@ -version (none) @system unittest +/// +@system unittest { int[string] arr = ["a":1]; - auto arr1 = assumeUnique(arr); - assert(is(typeof(arr1) == immutable(int[string])) && arr == null); + auto arr1 = arr.assumeUnique; + static assert(is(typeof(arr1) == immutable(int[string]))); + assert(arr == null); + assert(arr1.keys == ["a"]); } /** - * Wraps a possibly-throwing expression in a $(D nothrow) wrapper so that it - * can be called by a $(D nothrow) function. + * Wraps a possibly-throwing expression in a `nothrow` wrapper so that it + * can be called by a `nothrow` function. * * This wrapper function documents commitment on the part of the caller that * the appropriate steps have been taken to avoid whatever conditions may - * trigger an exception during the evaluation of $(D expr). If it turns out + * trigger an exception during the evaluation of `expr`. If it turns out * that the expression $(I does) throw at runtime, the wrapper will throw an - * $(D AssertError). + * `AssertError`. * - * (Note that $(D Throwable) objects such as $(D AssertError) that do not - * subclass $(D Exception) may be thrown even from $(D nothrow) functions, + * (Note that `Throwable` objects such as `AssertError` that do not + * subclass `Exception` may be thrown even from `nothrow` functions, * since they are considered to be serious runtime problems that cannot be * recovered from.) * @@ -984,7 +956,7 @@ T assumeWontThrow(T)(lazy T expr, /// @safe unittest { - import std.math : sqrt; + import std.math.algebraic : sqrt; // This function may throw. int squareRoot(int x) @@ -1030,37 +1002,41 @@ Params: source = The source object target = The target object -Returns: $(D true) if $(D source)'s representation embeds a pointer -that points to $(D target)'s representation or somewhere inside +Bugs: + The function is explicitly annotated `@nogc` because inference could fail, + see $(LINK2 https://issues.dlang.org/show_bug.cgi?id=17084, issue 17084). + +Returns: `true` if `source`'s representation embeds a pointer +that points to `target`'s representation or somewhere inside it. -If $(D source) is or contains a dynamic array, then, then these functions will check -if there is overlap between the dynamic array and $(D target)'s representation. +If `source` is or contains a dynamic array, then, then these functions will check +if there is overlap between the dynamic array and `target`'s representation. -If $(D source) is a class, then it will be handled as a pointer. +If `source` is a class, then it will be handled as a pointer. -If $(D target) is a pointer, a dynamic array or a class, then these functions will only -check if $(D source) points to $(D target), $(I not) what $(D target) references. +If `target` is a pointer, a dynamic array or a class, then these functions will only +check if `source` points to `target`, $(I not) what `target` references. -If $(D source) is or contains a union, then there may be either false positives or +If `source` is or contains a union or `void[n]`, then there may be either false positives or false negatives: -$(D doesPointTo) will return $(D true) if it is absolutely certain -$(D source) points to $(D target). It may produce false negatives, but never +`doesPointTo` will return `true` if it is absolutely certain +`source` points to `target`. It may produce false negatives, but never false positives. This function should be prefered when trying to validate input data. -$(D mayPointTo) will return $(D false) if it is absolutely certain -$(D source) does not point to $(D target). It may produce false positives, but never +`mayPointTo` will return `false` if it is absolutely certain +`source` does not point to `target`. It may produce false positives, but never false negatives. This function should be prefered for defensively choosing a code path. -Note: Evaluating $(D doesPointTo(x, x)) checks whether $(D x) has +Note: Evaluating $(D doesPointTo(x, x)) checks whether `x` has internal pointers. This should only be done as an assertive test, as the language is free to assume objects don't have internal pointers (TDPL 7.1.3.5). */ -bool doesPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow +bool doesPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @nogc @trusted pure nothrow if (__traits(isRef, source) || isDynamicArray!S || isPointer!S || is(S == class)) { @@ -1080,8 +1056,11 @@ if (__traits(isRef, source) || isDynamicArray!S || } else static if (isStaticArray!S) { - foreach (size_t i; 0 .. S.length) - if (doesPointTo(source[i], target)) return true; + static if (!is(S == void[n], size_t n)) + { + foreach (ref s; source) + if (doesPointTo(s, target)) return true; + } return false; } else static if (isDynamicArray!S) @@ -1122,8 +1101,38 @@ if (__traits(isRef, source) || isDynamicArray!S || } else static if (isStaticArray!S) { - foreach (size_t i; 0 .. S.length) - if (mayPointTo(source[i], target)) return true; + static if (is(S == void[n], size_t n)) + { + static if (n >= (void[]).sizeof) + { + // could contain a slice, which could point at anything. + // But a void[N] that is all 0 cannot point anywhere + import std.algorithm.searching : any; + if (__ctfe || any(cast(ubyte[]) source[])) + return true; + } + else static if (n >= (void*).sizeof) + { + // Reinterpreting cast is impossible during ctfe + if (__ctfe) + return true; + + // Only check for properly aligned pointers + enum al = (void*).alignof - 1; + const base = cast(size_t) &source; + const alBase = (base + al) & ~al; + + if ((n - (alBase - base)) >= (void*).sizeof && + mayPointTo(*(cast(void**) alBase), target)) + return true; + } + } + else + { + foreach (size_t i; 0 .. S.length) + if (mayPointTo(source[i], target)) return true; + } + return false; } else static if (isDynamicArray!S) @@ -1179,9 +1188,12 @@ bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target) @system unittest { int i; + // trick the compiler when initializing slice + // https://issues.dlang.org/show_bug.cgi?id=18637 + int* p = &i; int[] slice = [0, 1, 2, 3, 4]; int[5] arr = [0, 1, 2, 3, 4]; - int*[] slicep = [&i]; + int*[] slicep = [p]; int*[1] arrp = [&i]; // A slice points to all of its members: @@ -1241,6 +1253,37 @@ bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target) assert(b.doesPointTo(*aLoc)); // b points to where a is pointing } + +version (StdUnittest) +{ + // https://issues.dlang.org/show_bug.cgi?id=17084 + // the bug doesn't happen if these declarations are in the unittest block + // (static or not). + private struct Page17084 + { + URL17084 url; + int opCmp(P)(P) { return 0; } + int opCmp(P)(shared(P)) shared { return 0; } + } + + private struct URL17084 + { + int[] queryParams; + string toString()() const { return ""; } + alias toString this; + } +} + +// https://issues.dlang.org/show_bug.cgi?id=17084 +@system unittest +{ + import std.algorithm.sorting : sort; + Page17084[] s; + sort(s); + shared(Page17084)[] p; + sort(p); +} + @system unittest { struct S1 { int a; S1 * b; } @@ -1337,6 +1380,57 @@ bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target) assert( doesPointTo(ss, a)); //The array contains a struct that points to a assert(!doesPointTo(ss, b)); //The array doesn't contains a struct that points to b assert(!doesPointTo(ss, ss)); //The array doesn't point itself. + + // https://issues.dlang.org/show_bug.cgi?id=20426 + align((void*).alignof) void[32] voidArr = void; + (cast(void*[]) voidArr[])[] = null; // Ensure no false pointers + + // zeroed void ranges can't point at anything + assert(!mayPointTo(voidArr, a)); + assert(!mayPointTo(voidArr, b)); + + *cast(void**) &voidArr[16] = &a; // Pointers should be found + + alias SA = void[size_t.sizeof + 3]; + SA *smallArr1 = cast(SA*)&voidArr; + SA *smallArr2 = cast(SA*)&(voidArr[16]); + + // But it should only consider properly aligned pointers + // Write single bytes to avoid issues due to misaligned writes + void*[1] tmp = [&b]; + (cast(ubyte[]) voidArr[3 .. 3 + (void*).sizeof])[] = cast(ubyte[]) tmp[]; + + + assert( mayPointTo(*smallArr2, a)); + assert(!mayPointTo(*smallArr1, b)); + + assert(!doesPointTo(voidArr, a)); // Value might be a false pointer + assert(!doesPointTo(voidArr, b)); + + SA *smallArr3 = cast(SA *) &voidArr[13]; // Works for weird sizes/alignments + assert( mayPointTo(*smallArr3, a)); + assert(!mayPointTo(*smallArr3, b)); + + assert(!doesPointTo(*smallArr3, a)); + assert(!doesPointTo(*smallArr3, b)); + + auto v3 = cast(void[3]*) &voidArr[16]; // Arrays smaller than pointers are ignored + assert(!mayPointTo(*v3, a)); + assert(!mayPointTo(*v3, b)); + + assert(!doesPointTo(*v3, a)); + assert(!doesPointTo(*v3, b)); + + assert(mayPointTo(voidArr, a)); // slice-contiaining void[N] might point at anything + assert(mayPointTo(voidArr, b)); + + static assert(() { + void[16] arr1 = void; + void[size_t.sizeof] arr2 = void; + int var; + return mayPointTo(arr1, var) && !doesPointTo(arr1, var) && + mayPointTo(arr2, var) && !doesPointTo(arr2, var); + }()); } @@ -1425,7 +1519,7 @@ bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target) } /+ -Returns true if the field at index $(D i) in ($D T) shares its address with another field. +Returns true if the field at index `i` in ($D T) shares its address with another field. Note: This does not merelly check if the field is a member of an union, but also that it is not a single child. @@ -1510,53 +1604,57 @@ package string errnoString(int errno) nothrow @trusted } /********************* - * Thrown if errors that set $(D errno) occur. + * Thrown if errors that set `errno` occur. */ class ErrnoException : Exception { - final @property uint errno() { return _errno; } /// Operating system error code. + /// Operating system error code. + final @property uint errno() nothrow pure @nogc @safe { return _errno; } private uint _errno; /// Constructor which takes an error message. The current global $(REF errno, core,stdc,errno) value is used as error code. - this(string msg, string file = null, size_t line = 0) @trusted + this(string msg, string file = null, size_t line = 0) @safe { import core.stdc.errno : errno; this(msg, errno, file, line); } /// Constructor which takes an error message and error code. - this(string msg, int errno, string file = null, size_t line = 0) @trusted + this(string msg, int errno, string file = null, size_t line = 0) @safe { _errno = errno; super(msg ~ " (" ~ errnoString(errno) ~ ")", file, line); } +} - @system unittest - { - import core.stdc.errno : errno, EAGAIN; +/// +@safe unittest +{ + import core.stdc.errno : EAGAIN; + auto ex = new ErrnoException("oh no", EAGAIN); + assert(ex.errno == EAGAIN); +} - auto old = errno; - scope(exit) errno = old; +/// errno is used by default if no explicit error code is provided +@safe unittest +{ + import core.stdc.errno : errno, EAGAIN; - errno = EAGAIN; - auto ex = new ErrnoException("oh no"); - assert(ex.errno == EAGAIN); - } + auto old = errno; + scope(exit) errno = old; - @system unittest - { - import core.stdc.errno : EAGAIN; - auto ex = new ErrnoException("oh no", EAGAIN); - assert(ex.errno == EAGAIN); - } + // fake that errno got set by the callee + errno = EAGAIN; + auto ex = new ErrnoException("oh no"); + assert(ex.errno == EAGAIN); } /++ ML-style functional exception handling. Runs the supplied expression and - returns its result. If the expression throws a $(D Throwable), runs the + returns its result. If the expression throws a `Throwable`, runs the supplied error handler instead and return its result. The error handler's type must be the same as the expression's type. Params: - E = The type of $(D Throwable)s to catch. Defaults to $(D Exception) + E = The type of `Throwable`s to catch. Defaults to `Exception` T1 = The type of the expression. T2 = The return type of the error handler. expression = The expression to run and return its result. @@ -1565,54 +1663,7 @@ class ErrnoException : Exception Returns: expression, if it does not throw. Otherwise, returns the result of errorHandler. - - Example: - -------------------- - //Revert to a default value upon an error: - assert("x".to!int().ifThrown(0) == 0); - -------------------- - - You can also chain multiple calls to ifThrown, each capturing errors from the - entire preceding expression. - - Example: - -------------------- - //Chaining multiple calls to ifThrown to attempt multiple things in a row: - string s="true"; - assert(s.to!int(). - ifThrown(cast(int) s.to!double()). - ifThrown(cast(int) s.to!bool()) - == 1); - - //Respond differently to different types of errors - assert(enforce("x".to!int() < 1).to!string() - .ifThrown!ConvException("not a number") - .ifThrown!Exception("number too small") - == "not a number"); - -------------------- - - The expression and the errorHandler must have a common type they can both - be implicitly casted to, and that type will be the type of the compound - expression. - - Example: - -------------------- - //null and new Object have a common type(Object). - static assert(is(typeof(null.ifThrown(new Object())) == Object)); - static assert(is(typeof((new Object()).ifThrown(null)) == Object)); - - //1 and new Object do not have a common type. - static assert(!__traits(compiles, 1.ifThrown(new Object()))); - static assert(!__traits(compiles, (new Object()).ifThrown(1))); - -------------------- - - If you need to use the actual thrown exception, you can use a delegate. - Example: - -------------------- - //Use a lambda to get the thrown object. - assert("%s".format().ifThrown!Exception(e => e.classinfo.name) == "std.format.FormatException"); - -------------------- - +/ ++/ //lazy version CommonType!(T1, T2) ifThrown(E : Throwable = Exception, T1, T2)(lazy scope T1 expression, lazy scope T2 errorHandler) { @@ -1675,6 +1726,59 @@ CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate } } +/// Revert to a default value upon an error: +@safe unittest +{ + import std.conv : to; + assert("x".to!int.ifThrown(0) == 0); +} + +/** +Chain multiple calls to ifThrown, each capturing errors from the +entire preceding expression. +*/ +@safe unittest +{ + import std.conv : ConvException, to; + string s = "true"; + assert(s.to!int.ifThrown(cast(int) s.to!double) + .ifThrown(cast(int) s.to!bool) == 1); + + s = "2.0"; + assert(s.to!int.ifThrown(cast(int) s.to!double) + .ifThrown(cast(int) s.to!bool) == 2); + + // Respond differently to different types of errors + alias orFallback = (lazy a) => a.ifThrown!ConvException("not a number") + .ifThrown!Exception("number too small"); + + assert(orFallback(enforce("x".to!int < 1).to!string) == "not a number"); + assert(orFallback(enforce("2".to!int < 1).to!string) == "number too small"); +} + +/** +The expression and the errorHandler must have a common type they can both +be implicitly casted to, and that type will be the type of the compound +expression. +*/ +@safe unittest +{ + // null and new Object have a common type(Object). + static assert(is(typeof(null.ifThrown(new Object())) == Object)); + static assert(is(typeof((new Object()).ifThrown(null)) == Object)); + + // 1 and new Object do not have a common type. + static assert(!__traits(compiles, 1.ifThrown(new Object()))); + static assert(!__traits(compiles, (new Object()).ifThrown(1))); +} + +/// Use a lambda to get the thrown object. +@system unittest +{ + import std.format : format; + assert("%s".format.ifThrown!Exception(e => e.classinfo.name) == "std.format.FormatException"); +} + //Verify Examples @system unittest { @@ -1745,22 +1849,22 @@ CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate static assert(!__traits(compiles, (new Object()).ifThrown(e=>1))); } -version (unittest) package -@property void assertCTFEable(alias dg)() +version (StdUnittest) package +void assertCTFEable(alias dg)() { static assert({ cast(void) dg(); return true; }()); cast(void) dg(); } -/** This $(D enum) is used to select the primitives of the range to handle by the - $(LREF handle) range wrapper. The values of the $(D enum) can be $(D OR)'d to +/** This `enum` is used to select the primitives of the range to handle by the + $(LREF handle) range wrapper. The values of the `enum` can be `OR`'d to select multiple primitives to be handled. - $(D RangePrimitive.access) is a shortcut for the access primitives; $(D front), - $(D back) and $(D opIndex). + `RangePrimitive.access` is a shortcut for the access primitives; `front`, + `back` and `opIndex`. - $(D RangePrimitive.pop) is a shortcut for the mutating primitives; - $(D popFront) and $(D popBack). + `RangePrimitive.pop` is a shortcut for the mutating primitives; + `popFront` and `popBack`. */ enum RangePrimitive { @@ -1778,31 +1882,65 @@ enum RangePrimitive pop = popFront | popBack, /// Ditto } +/// +pure @safe unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map, splitter; + import std.conv : to, ConvException; + + auto s = "12,1337z32,54,2,7,9,1z,6,8"; + + // The next line composition will throw when iterated + // as some elements of the input do not convert to integer + auto r = s.splitter(',').map!(a => to!int(a)); + + // Substitute 0 for cases of ConvException + auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0); + assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8])); +} + +/// +pure @safe unittest +{ + import std.algorithm.comparison : equal; + import std.range : retro; + import std.utf : UTFException; + + auto str = "hello\xFFworld"; // 0xFF is an invalid UTF-8 code unit + + auto handled = str.handle!(UTFException, RangePrimitive.access, + (e, r) => ' '); // Replace invalid code points with spaces + + assert(handled.equal("hello world")); // `front` is handled, + assert(handled.retro.equal("dlrow olleh")); // as well as `back` +} + /** Handle exceptions thrown from range primitives. Use the $(LREF RangePrimitive) enum to specify which primitives to _handle. -Multiple range primitives can be handled at once by using the $(D OR) operator -or the pseudo-primitives $(D RangePrimitive.access) and $(D RangePrimitive.pop). +Multiple range primitives can be handled at once by using the `OR` operator +or the pseudo-primitives `RangePrimitive.access` and `RangePrimitive.pop`. All handled primitives must have return types or values compatible with the user-supplied handler. Params: - E = The type of $(D Throwable) to _handle. + E = The type of `Throwable` to _handle. primitivesToHandle = Set of range primitives to _handle. handler = The callable that is called when a handled primitive throws a - $(D Throwable) of type $(D E). The handler must accept arguments of + `Throwable` of type `E`. The handler must accept arguments of the form $(D E, ref IRange) and its return value is used as the primitive's - return value whenever $(D E) is thrown. For $(D opIndex), the handler can + return value whenever `E` is thrown. For `opIndex`, the handler can optionally recieve a third argument; the index that caused the exception. input = The range to _handle. -Returns: A wrapper $(D struct) that preserves the range interface of $(D input). +Returns: A wrapper `struct` that preserves the range interface of `input`. Note: Infinite ranges with slicing support must return an instance of $(REF Take, std,range) when sliced with a specific lower and upper -bound (see $(REF hasSlicing, std,range,primitives)); $(D handle) deals with -this by $(D take)ing 0 from the return value of the handler function and +bound (see $(REF hasSlicing, std,range,primitives)); `handle` deals with +this by `take`ing 0 from the return value of the handler function and returning that when an exception is caught. */ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Range)(Range input) |