aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/exception.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/src/std/exception.d')
-rw-r--r--libphobos/src/std/exception.d2316
1 files changed, 2316 insertions, 0 deletions
diff --git a/libphobos/src/std/exception.d b/libphobos/src/std/exception.d
new file mode 100644
index 0000000..73afadc
--- /dev/null
+++ b/libphobos/src/std/exception.d
@@ -0,0 +1,2316 @@
+// Written in the D programming language.
+
+/++
+ This module defines functions related to exceptions and general error
+ handling. It also defines functions intended to aid in unit testing.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Functions))
+$(TR $(TD Assumptions) $(TD
+ $(LREF assertNotThrown)
+ $(LREF assertThrown)
+ $(LREF assumeUnique)
+ $(LREF assumeWontThrow)
+ $(LREF mayPointTo)
+))
+$(TR $(TD Enforce) $(TD
+ $(LREF doesPointTo)
+ $(LREF enforce)
+ $(LREF enforceEx)
+ $(LREF errnoEnforce)
+))
+$(TR $(TD Handlers) $(TD
+ $(LREF collectException)
+ $(LREF collectExceptionMsg)
+ $(LREF ifThrown)
+ $(LREF handle)
+))
+$(TR $(TD Other) $(TD
+ $(LREF basicExceptionCtors)
+ $(LREF emptyExceptionMsg)
+ $(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
+
+ Exception e = collectException(write(g, readln(f)));
+ if (e)
+ {
+ ... an exception occurred...
+ ... We have the exception to play around with...
+ }
+
+ string msg = collectExceptionMsg(write(g, readln(f)));
+ if (msg)
+ {
+ ... an exception occurred...
+ ... We have the message from the exception but not the exception...
+ }
+
+ char[] line;
+ enforce(readln(f, line));
+ return assumeUnique(line);
+ }
+ --------------------
+
+ 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)
+
+ +/
+module std.exception;
+
+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,
+ it is caught and does not escape assertNotThrown. Rather, an
+ $(D AssertError) is thrown. However, any other $(D Throwable)s will escape.
+
+ Params:
+ T = The $(D 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__).
+ line = The line where the error occurred.
+ Defaults to $(D __LINE__).
+
+ Throws:
+ $(D AssertError) if the given $(D Throwable) is thrown.
+
+ Returns:
+ the result of `expression`.
+ +/
+auto assertNotThrown(T : Throwable = Exception, E)
+ (lazy E expression,
+ string msg = null,
+ string file = __FILE__,
+ size_t line = __LINE__)
+{
+ import core.exception : AssertError;
+ try
+ {
+ return expression();
+ }
+ catch (T t)
+ {
+ immutable message = msg.length == 0 ? t.msg : msg;
+ immutable tail = message.length == 0 ? "." : ": " ~ message;
+ throw new AssertError("assertNotThrown failed: " ~ T.stringof ~ " was thrown" ~ tail, file, line, t);
+ }
+}
+///
+@system unittest
+{
+ import core.exception : AssertError;
+
+ import std.string;
+ assertNotThrown!StringException(enforce!StringException(true, "Error!"));
+
+ //Exception is the default.
+ assertNotThrown(enforce!StringException(true, "Error!"));
+
+ assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
+ enforce!StringException(false, "Error!"))) ==
+ `assertNotThrown failed: StringException was thrown: Error!`);
+}
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.string;
+ assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
+ enforce!StringException(false, ""), "Error!")) ==
+ `assertNotThrown failed: StringException was thrown: Error!`);
+
+ assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
+ enforce!StringException(false, ""))) ==
+ `assertNotThrown failed: StringException was thrown.`);
+
+ assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
+ enforce!StringException(false, ""), "")) ==
+ `assertNotThrown failed: StringException was thrown.`);
+}
+
+@system unittest
+{
+ import core.exception : AssertError;
+
+ void throwEx(Throwable t) { throw t; }
+ bool nothrowEx() { return true; }
+
+ try
+ {
+ assert(assertNotThrown!Exception(nothrowEx()));
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assert(assertNotThrown!Exception(nothrowEx(), "It's a message"));
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assert(assertNotThrown!AssertError(nothrowEx()));
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assert(assertNotThrown!AssertError(nothrowEx(), "It's a message"));
+ }
+ catch (AssertError) assert(0);
+
+ {
+ bool thrown = false;
+ try
+ {
+ assertNotThrown!Exception(
+ throwEx(new Exception("It's an Exception")));
+ }
+ catch (AssertError) thrown = true;
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ {
+ assertNotThrown!Exception(
+ throwEx(new Exception("It's an Exception")), "It's a message");
+ }
+ catch (AssertError) thrown = true;
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ {
+ assertNotThrown!AssertError(
+ throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__)));
+ }
+ catch (AssertError) thrown = true;
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ {
+ assertNotThrown!AssertError(
+ throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__)),
+ "It's a message");
+ }
+ catch (AssertError) thrown = true;
+ assert(thrown);
+ }
+}
+
+/++
+ 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.
+
+ Params:
+ T = The $(D 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__).
+ line = The line where the error occurred.
+ Defaults to $(D __LINE__).
+
+ Throws:
+ $(D AssertError) if the given $(D Throwable) is not thrown.
+ +/
+void assertThrown(T : Throwable = Exception, E)
+ (lazy E expression,
+ string msg = null,
+ string file = __FILE__,
+ size_t line = __LINE__)
+{
+ import core.exception : AssertError;
+
+ try
+ expression();
+ catch (T)
+ return;
+ throw new AssertError("assertThrown failed: No " ~ T.stringof ~ " was thrown"
+ ~ (msg.length == 0 ? "." : ": ") ~ msg,
+ file, line);
+}
+///
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.string;
+
+ assertThrown!StringException(enforce!StringException(false, "Error!"));
+
+ //Exception is the default.
+ assertThrown(enforce!StringException(false, "Error!"));
+
+ assert(collectExceptionMsg!AssertError(assertThrown!StringException(
+ enforce!StringException(true, "Error!"))) ==
+ `assertThrown failed: No StringException was thrown.`);
+}
+
+@system unittest
+{
+ import core.exception : AssertError;
+
+ void throwEx(Throwable t) { throw t; }
+ void nothrowEx() { }
+
+ try
+ {
+ assertThrown!Exception(throwEx(new Exception("It's an Exception")));
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assertThrown!Exception(throwEx(new Exception("It's an Exception")),
+ "It's a message");
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assertThrown!AssertError(throwEx(new AssertError("It's an AssertError",
+ __FILE__, __LINE__)));
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assertThrown!AssertError(throwEx(new AssertError("It's an AssertError",
+ __FILE__, __LINE__)),
+ "It's a message");
+ }
+ catch (AssertError) assert(0);
+
+
+ {
+ bool thrown = false;
+ try
+ assertThrown!Exception(nothrowEx());
+ catch (AssertError)
+ thrown = true;
+
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ assertThrown!Exception(nothrowEx(), "It's a message");
+ catch (AssertError)
+ thrown = true;
+
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ assertThrown!AssertError(nothrowEx());
+ catch (AssertError)
+ thrown = true;
+
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ assertThrown!AssertError(nothrowEx(), "It's a message");
+ catch (AssertError)
+ thrown = true;
+
+ assert(thrown);
+ }
+}
+
+
+/++
+ Enforces that the given value is true.
+
+ Params:
+ value = The value to test.
+ E = Exception type to throw if the value evalues to false.
+ msg = The error message to put in the exception if it is thrown.
+ 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.
+
+ Note:
+ $(D 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.");
+ --------------------
+ +/
+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) {} })))
+{
+ 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.
+ +/
+T enforce(T, Dg, string file = __FILE__, size_t line = __LINE__)
+ (T value, scope Dg dg)
+if (isSomeFunction!Dg && is(typeof( dg() )) &&
+ is(typeof({ if (!value) {} })))
+{
+ if (!value) dg();
+ return value;
+}
+
+private void bailOut(E : Throwable = Exception)(string file, size_t line, in 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));
+ }
+}
+
+@safe unittest
+{
+ assert(enforce(123) == 123);
+
+ try
+ {
+ enforce(false, "error");
+ assert(false);
+ }
+ catch (Exception e)
+ {
+ assert(e.msg == "error");
+ assert(e.file == __FILE__);
+ assert(e.line == __LINE__-7);
+ }
+}
+
+@safe unittest
+{
+ // Issue 10510
+ extern(C) void cFoo() { }
+ enforce(false, &cFoo);
+}
+
+// purity and safety inference test
+@system unittest
+{
+ import std.meta : AliasSeq;
+
+ foreach (EncloseSafe; AliasSeq!(false, true))
+ foreach (EnclosePure; AliasSeq!(false, true))
+ {
+ foreach (BodySafe; AliasSeq!(false, true))
+ foreach (BodyPure; AliasSeq!(false, true))
+ {
+ enum code =
+ "delegate void() " ~
+ (EncloseSafe ? "@safe " : "") ~
+ (EnclosePure ? "pure " : "") ~
+ "{ enforce(true, { " ~
+ "int n; " ~
+ (BodySafe ? "" : "auto p = &n + 10; " ) ~ // unsafe code
+ (BodyPure ? "" : "static int g; g = 10; ") ~ // impure code
+ "}); " ~
+ "}";
+ enum expect =
+ (BodySafe || !EncloseSafe) && (!EnclosePure || BodyPure);
+
+ version (none)
+ pragma(msg, "safe = ", EncloseSafe?1:0, "/", BodySafe?1:0, ", ",
+ "pure = ", EnclosePure?1:0, "/", BodyPure?1:0, ", ",
+ "expect = ", expect?"OK":"NG", ", ",
+ "code = ", code);
+
+ static assert(__traits(compiles, mixin(code)()) == expect);
+ }
+ }
+}
+
+// Test for bugzilla 8637
+@system unittest
+{
+ struct S
+ {
+ static int g;
+ ~this() {} // impure & unsafe destructor
+ bool opCast(T:bool)() {
+ int* p = cast(int*) 0; // unsafe operation
+ int n = g; // impure operation
+ return true;
+ }
+ }
+ S s;
+
+ enforce(s);
+ enforce(s, {});
+ enforce(s, new Exception(""));
+
+ errnoEnforce(s);
+
+ alias E1 = Exception;
+ static class E2 : Exception
+ {
+ this(string fn, size_t ln) { super("", fn, ln); }
+ }
+ static class E3 : Exception
+ {
+ this(string msg) { super(msg, __FILE__, __LINE__); }
+ }
+ enforce!E1(s);
+ enforce!E2(s);
+}
+
+@safe unittest
+{
+ // Issue 14685
+
+ class E : Exception
+ {
+ this() { super("Not found"); }
+ }
+ static assert(!__traits(compiles, { enforce!E(false); }));
+}
+
+/++
+ 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.
+
+ Params:
+ 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,
+ $(D new ErrnoException(msg)) is thrown. It is assumed that the last
+ operation set $(D 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;
+ }
+}
+
+@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);
+ }
+
+ {
+ 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))));
+}
+
+@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
+ 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
+ do.
+
+ Params:
+ T = The type of exception to catch.
+ expression = The expression which may throw an exception.
+ result = The result of the expression if no exception is thrown.
++/
+T collectException(T = Exception, E)(lazy E expression, ref E result)
+{
+ try
+ {
+ result = expression();
+ }
+ catch (T e)
+ {
+ return e;
+ }
+ return null;
+}
+///
+@system unittest
+{
+ int b;
+ int foo() { throw new Exception("blah"); }
+ assert(collectException(foo(), b));
+
+ int[] a = new int[3];
+ import core.exception : RangeError;
+ assert(collectException!RangeError(a[4], b));
+}
+
+/++
+ 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
+ do.
+
+ Params:
+ T = The type of exception to catch.
+ expression = The expression which may throw an exception.
++/
+T collectException(T : Throwable = Exception, E)(lazy E expression)
+{
+ try
+ {
+ expression();
+ }
+ catch (T t)
+ {
+ return t;
+ }
+ return null;
+}
+
+@safe unittest
+{
+ int foo() { throw new Exception("blah"); }
+ assert(collectException(foo()));
+}
+
+/++
+ 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).
+
+ If an exception is thrown but it has an empty message, then
+ $(D 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
+ do.
+
+ Params:
+ T = The type of exception to catch.
+ expression = The expression which may throw an exception.
++/
+string collectExceptionMsg(T = Exception, E)(lazy E expression)
+{
+ import std.array : empty;
+ try
+ {
+ expression();
+
+ return cast(string) null;
+ }
+ catch (T e)
+ return e.msg.empty ? emptyExceptionMsg : e.msg;
+}
+///
+@safe unittest
+{
+ void throwFunc() { throw new Exception("My Message."); }
+ assert(collectExceptionMsg(throwFunc()) == "My Message.");
+
+ void nothrowFunc() {}
+ assert(collectExceptionMsg(nothrowFunc()) is null);
+
+ void throwEmptyFunc() { throw new Exception(""); }
+ assert(collectExceptionMsg(throwEmptyFunc()) == emptyExceptionMsg);
+}
+
+/++
+ Value that collectExceptionMsg returns when it catches an exception
+ with an empty exception message.
+ +/
+enum emptyExceptionMsg = "<Empty Exception Message>";
+
+/**
+ * Casts a mutable array to an immutable array in an idiomatic
+ * manner. Technically, $(D assumeUnique) just inserts a cast,
+ * but its name documents assumptions on the part of the
+ * caller. $(D 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
+ * assumeUnique(arr)) indicates commitment from the caller that there
+ * is no more mutable access to any of $(D arr)'s elements
+ * (transitively), and that all future accesses will be done through
+ * the immutable array returned by $(D assumeUnique).
+ *
+ * Typically, $(D assumeUnique) is used to return arrays from
+ * functions that have allocated and built them.
+ *
+ * Params:
+ * array = The array to cast to immutable.
+ *
+ * Returns: The immutable array.
+ *
+ * Example:
+ *
+ * ----
+ * string letters()
+ * {
+ * char[] result = new char['z' - 'a' + 1];
+ * foreach (i, ref e; result)
+ * {
+ * e = cast(char)('a' + i);
+ * }
+ * return assumeUnique(result);
+ * }
+ * ----
+ *
+ * The use in the example above is correct because $(D result)
+ * was private to $(D letters) and is inaccessible in writing
+ * after the function returns. The following example shows an
+ * incorrect use of $(D assumeUnique).
+ *
+ * Bad:
+ *
+ * ----
+ * private char[] buffer;
+ * string letters(char first, char last)
+ * {
+ * if (first >= last) return null; // fine
+ * auto sneaky = buffer;
+ * sneaky.length = last - first + 1;
+ * foreach (i, ref e; sneaky)
+ * {
+ * e = cast(char)('a' + i);
+ * }
+ * 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
+ * the last line with:
+ * ----
+ * return to!(string)(sneaky); // not that sneaky anymore
+ * ----
+ *
+ * The call will duplicate the array appropriately.
+ *
+ * Note that checking for uniqueness during compilation is
+ * possible in certain cases, especially when a function is
+ * 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:
+ * ----
+ * string letters() pure
+ * {
+ * char[] result = new char['z' - 'a' + 1];
+ * foreach (i, ref e; result)
+ * {
+ * e = cast(char)('a' + i);
+ * }
+ * 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)
+ * language.
+ *
+ * The downside of using $(D 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
+ * simple and rare enough to be tolerable.
+ *
+ */
+immutable(T)[] assumeUnique(T)(T[] array) pure nothrow
+{
+ return .assumeUnique(array); // call ref version
+}
+/// ditto
+immutable(T)[] assumeUnique(T)(ref T[] array) pure nothrow
+{
+ auto result = cast(immutable(T)[]) array;
+ array = null;
+ return result;
+}
+/// ditto
+immutable(T[U]) assumeUnique(T, U)(ref T[U] array) pure nothrow
+{
+ auto result = cast(immutable(T[U])) array;
+ array = null;
+ 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);
+}
+
+// @@@BUG@@@
+version (none) @system unittest
+{
+ int[string] arr = ["a":1];
+ auto arr1 = assumeUnique(arr);
+ assert(is(typeof(arr1) == immutable(int[string])) && arr == null);
+}
+
+/**
+ * Wraps a possibly-throwing expression in a $(D nothrow) wrapper so that it
+ * can be called by a $(D 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
+ * that the expression $(I does) throw at runtime, the wrapper will throw an
+ * $(D AssertError).
+ *
+ * (Note that $(D Throwable) objects such as $(D AssertError) that do not
+ * subclass $(D Exception) may be thrown even from $(D nothrow) functions,
+ * since they are considered to be serious runtime problems that cannot be
+ * recovered from.)
+ *
+ * Params:
+ * expr = The expression asserted not to throw.
+ * msg = The message to include in the `AssertError` if the assumption turns
+ * out to be false.
+ * file = The source file name of the caller.
+ * line = The line number of the caller.
+ *
+ * Returns:
+ * The value of `expr`, if any.
+ */
+T assumeWontThrow(T)(lazy T expr,
+ string msg = null,
+ string file = __FILE__,
+ size_t line = __LINE__) nothrow
+{
+ import core.exception : AssertError;
+ try
+ {
+ return expr;
+ }
+ catch (Exception e)
+ {
+ import std.range.primitives : empty;
+ immutable tail = msg.empty ? "." : ": " ~ msg;
+ throw new AssertError("assumeWontThrow failed: Expression did throw" ~
+ tail, file, line);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.math : sqrt;
+
+ // This function may throw.
+ int squareRoot(int x)
+ {
+ if (x < 0)
+ throw new Exception("Tried to take root of negative number");
+ return cast(int) sqrt(cast(double) x);
+ }
+
+ // This function never throws.
+ int computeLength(int x, int y) nothrow
+ {
+ // Since x*x + y*y is always positive, we can safely assume squareRoot
+ // won't throw, and use it to implement this nothrow function. If it
+ // does throw (e.g., if x*x + y*y overflows a 32-bit value), then the
+ // program will terminate.
+ return assumeWontThrow(squareRoot(x*x + y*y));
+ }
+
+ assert(computeLength(3, 4) == 5);
+}
+
+@system unittest
+{
+ import core.exception : AssertError;
+
+ void alwaysThrows()
+ {
+ throw new Exception("I threw up");
+ }
+ void bad() nothrow
+ {
+ assumeWontThrow(alwaysThrows());
+ }
+ assertThrown!AssertError(bad());
+}
+
+/**
+Checks whether a given source object contains pointers or references to a given
+target object.
+
+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
+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 $(D 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 $(D source) is or contains a union, 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
+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
+false negatives. This function should be prefered for defensively choosing a
+code path.
+
+Note: Evaluating $(D doesPointTo(x, x)) checks whether $(D 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
+if (__traits(isRef, source) || isDynamicArray!S ||
+ isPointer!S || is(S == class))
+{
+ static if (isPointer!S || is(S == class) || is(S == interface))
+ {
+ const m = *cast(void**) &source;
+ const b = cast(void*) &target;
+ const e = b + target.sizeof;
+ return b <= m && m < e;
+ }
+ else static if (is(S == struct) || is(S == union))
+ {
+ foreach (i, Subobj; typeof(source.tupleof))
+ static if (!isUnionAliased!(S, i))
+ if (doesPointTo(source.tupleof[i], target)) return true;
+ return false;
+ }
+ else static if (isStaticArray!S)
+ {
+ foreach (size_t i; 0 .. S.length)
+ if (doesPointTo(source[i], target)) return true;
+ return false;
+ }
+ else static if (isDynamicArray!S)
+ {
+ import std.array : overlap;
+ return overlap(cast(void[]) source, cast(void[])(&target)[0 .. 1]).length != 0;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// for shared objects
+/// ditto
+bool doesPointTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow
+{
+ return doesPointTo!(shared S, shared T, void)(source, target);
+}
+
+/// ditto
+bool mayPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow
+if (__traits(isRef, source) || isDynamicArray!S ||
+ isPointer!S || is(S == class))
+{
+ static if (isPointer!S || is(S == class) || is(S == interface))
+ {
+ const m = *cast(void**) &source;
+ const b = cast(void*) &target;
+ const e = b + target.sizeof;
+ return b <= m && m < e;
+ }
+ else static if (is(S == struct) || is(S == union))
+ {
+ foreach (i, Subobj; typeof(source.tupleof))
+ if (mayPointTo(source.tupleof[i], target)) return true;
+ return false;
+ }
+ else static if (isStaticArray!S)
+ {
+ foreach (size_t i; 0 .. S.length)
+ if (mayPointTo(source[i], target)) return true;
+ return false;
+ }
+ else static if (isDynamicArray!S)
+ {
+ import std.array : overlap;
+ return overlap(cast(void[]) source, cast(void[])(&target)[0 .. 1]).length != 0;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// for shared objects
+/// ditto
+bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow
+{
+ return mayPointTo!(shared S, shared T, void)(source, target);
+}
+
+/// Pointers
+@system unittest
+{
+ int i = 0;
+ int* p = null;
+ assert(!p.doesPointTo(i));
+ p = &i;
+ assert( p.doesPointTo(i));
+}
+
+/// Structs and Unions
+@system unittest
+{
+ struct S
+ {
+ int v;
+ int* p;
+ }
+ int i;
+ auto s = S(0, &i);
+
+ // structs and unions "own" their members
+ // pointsTo will answer true if one of the members pointsTo.
+ assert(!s.doesPointTo(s.v)); //s.v is just v member of s, so not pointed.
+ assert( s.p.doesPointTo(i)); //i is pointed by s.p.
+ assert( s .doesPointTo(i)); //which means i is pointed by s itself.
+
+ // Unions will behave exactly the same. Points to will check each "member"
+ // individually, even if they share the same memory
+}
+
+/// Arrays (dynamic and static)
+@system unittest
+{
+ int i;
+ int[] slice = [0, 1, 2, 3, 4];
+ int[5] arr = [0, 1, 2, 3, 4];
+ int*[] slicep = [&i];
+ int*[1] arrp = [&i];
+
+ // A slice points to all of its members:
+ assert( slice.doesPointTo(slice[3]));
+ assert(!slice[0 .. 2].doesPointTo(slice[3])); // Object 3 is outside of the
+ // slice [0 .. 2]
+
+ // Note that a slice will not take into account what its members point to.
+ assert( slicep[0].doesPointTo(i));
+ assert(!slicep .doesPointTo(i));
+
+ // static arrays are objects that own their members, just like structs:
+ assert(!arr.doesPointTo(arr[0])); // arr[0] is just a member of arr, so not
+ // pointed.
+ assert( arrp[0].doesPointTo(i)); // i is pointed by arrp[0].
+ assert( arrp .doesPointTo(i)); // which means i is pointed by arrp
+ // itself.
+
+ // Notice the difference between static and dynamic arrays:
+ assert(!arr .doesPointTo(arr[0]));
+ assert( arr[].doesPointTo(arr[0]));
+ assert( arrp .doesPointTo(i));
+ assert(!arrp[].doesPointTo(i));
+}
+
+/// Classes
+@system unittest
+{
+ class C
+ {
+ this(int* p){this.p = p;}
+ int* p;
+ }
+ int i;
+ C a = new C(&i);
+ C b = a;
+
+ // Classes are a bit particular, as they are treated like simple pointers
+ // to a class payload.
+ assert( a.p.doesPointTo(i)); // a.p points to i.
+ assert(!a .doesPointTo(i)); // Yet a itself does not point i.
+
+ //To check the class payload itself, iterate on its members:
+ ()
+ {
+ import std.traits : Fields;
+
+ foreach (index, _; Fields!C)
+ if (doesPointTo(a.tupleof[index], i))
+ return;
+ assert(0);
+ }();
+
+ // To check if a class points a specific payload, a direct memmory check
+ // can be done:
+ auto aLoc = cast(ubyte[__traits(classInstanceSize, C)]*) a;
+ assert(b.doesPointTo(*aLoc)); // b points to where a is pointing
+}
+
+@system unittest
+{
+ struct S1 { int a; S1 * b; }
+ S1 a1;
+ S1 * p = &a1;
+ assert(doesPointTo(p, a1));
+
+ S1 a2;
+ a2.b = &a1;
+ assert(doesPointTo(a2, a1));
+
+ struct S3 { int[10] a; }
+ S3 a3;
+ auto a4 = a3.a[2 .. 3];
+ assert(doesPointTo(a4, a3));
+
+ auto a5 = new double[4];
+ auto a6 = a5[1 .. 2];
+ assert(!doesPointTo(a5, a6));
+
+ auto a7 = new double[3];
+ auto a8 = new double[][1];
+ a8[0] = a7;
+ assert(!doesPointTo(a8[0], a8[0]));
+
+ // don't invoke postblit on subobjects
+ {
+ static struct NoCopy { this(this) { assert(0); } }
+ static struct Holder { NoCopy a, b, c; }
+ Holder h;
+ cast(void) doesPointTo(h, h);
+ }
+
+ shared S3 sh3;
+ shared sh3sub = sh3.a[];
+ assert(doesPointTo(sh3sub, sh3));
+
+ int[] darr = [1, 2, 3, 4];
+
+ //dynamic arrays don't point to each other, or slices of themselves
+ assert(!doesPointTo(darr, darr));
+ assert(!doesPointTo(darr[0 .. 1], darr));
+
+ //But they do point their elements
+ foreach (i; 0 .. 4)
+ assert(doesPointTo(darr, darr[i]));
+ assert(doesPointTo(darr[0 .. 3], darr[2]));
+ assert(!doesPointTo(darr[0 .. 3], darr[3]));
+}
+
+@system unittest
+{
+ //tests with static arrays
+ //Static arrays themselves are just objects, and don't really *point* to anything.
+ //They aggregate their contents, much the same way a structure aggregates its attributes.
+ //*However* The elements inside the static array may themselves point to stuff.
+
+ //Standard array
+ int[2] k;
+ assert(!doesPointTo(k, k)); //an array doesn't point to itself
+ //Technically, k doesn't point its elements, although it does alias them
+ assert(!doesPointTo(k, k[0]));
+ assert(!doesPointTo(k, k[1]));
+ //But an extracted slice will point to the same array.
+ assert(doesPointTo(k[], k));
+ assert(doesPointTo(k[], k[1]));
+
+ //An array of pointers
+ int*[2] pp;
+ int a;
+ int b;
+ pp[0] = &a;
+ assert( doesPointTo(pp, a)); //The array contains a pointer to a
+ assert(!doesPointTo(pp, b)); //The array does NOT contain a pointer to b
+ assert(!doesPointTo(pp, pp)); //The array does not point itslef
+
+ //A struct containing a static array of pointers
+ static struct S
+ {
+ int*[2] p;
+ }
+ S s;
+ s.p[0] = &a;
+ assert( doesPointTo(s, a)); //The struct contains an array that points a
+ assert(!doesPointTo(s, b)); //But doesn't point b
+ assert(!doesPointTo(s, s)); //The struct doesn't actually point itslef.
+
+ //An array containing structs that have pointers
+ static struct SS
+ {
+ int* p;
+ }
+ SS[2] ss = [SS(&a), SS(null)];
+ 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.
+}
+
+
+@system unittest //Unions
+{
+ int i;
+ union U //Named union
+ {
+ size_t asInt = 0;
+ int* asPointer;
+ }
+ struct S
+ {
+ union //Anonymous union
+ {
+ size_t asInt = 0;
+ int* asPointer;
+ }
+ }
+
+ U u;
+ S s;
+ assert(!doesPointTo(u, i));
+ assert(!doesPointTo(s, i));
+ assert(!mayPointTo(u, i));
+ assert(!mayPointTo(s, i));
+
+ u.asPointer = &i;
+ s.asPointer = &i;
+ assert(!doesPointTo(u, i));
+ assert(!doesPointTo(s, i));
+ assert( mayPointTo(u, i));
+ assert( mayPointTo(s, i));
+
+ u.asInt = cast(size_t)&i;
+ s.asInt = cast(size_t)&i;
+ assert(!doesPointTo(u, i));
+ assert(!doesPointTo(s, i));
+ assert( mayPointTo(u, i));
+ assert( mayPointTo(s, i));
+}
+
+@system unittest //Classes
+{
+ int i;
+ static class A
+ {
+ int* p;
+ }
+ A a = new A, b = a;
+ assert(!doesPointTo(a, b)); //a does not point to b
+ a.p = &i;
+ assert(!doesPointTo(a, i)); //a does not point to i
+}
+@safe unittest //alias this test
+{
+ static int i;
+ static int j;
+ struct S
+ {
+ int* p;
+ @property int* foo(){return &i;}
+ alias foo this;
+ }
+ assert(is(S : int*));
+ S s = S(&j);
+ assert(!doesPointTo(s, i));
+ assert( doesPointTo(s, j));
+ assert( doesPointTo(cast(int*) s, i));
+ assert(!doesPointTo(cast(int*) s, j));
+}
+@safe unittest //more alias this opCast
+{
+ void* p;
+ class A
+ {
+ void* opCast(T)() if (is(T == void*))
+ {
+ return p;
+ }
+ alias foo = opCast!(void*);
+ alias foo this;
+ }
+ assert(!doesPointTo(A.init, p));
+ assert(!mayPointTo(A.init, p));
+}
+
+/+
+Returns true if the field at index $(D 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.
++/
+package enum isUnionAliased(T, size_t i) = isUnionAliasedImpl!T(T.tupleof[i].offsetof);
+private bool isUnionAliasedImpl(T)(size_t offset)
+{
+ int count = 0;
+ foreach (i, U; typeof(T.tupleof))
+ if (T.tupleof[i].offsetof == offset)
+ ++count;
+ return count >= 2;
+}
+//
+@safe unittest
+{
+ static struct S
+ {
+ int a0; //Not aliased
+ union
+ {
+ int a1; //Not aliased
+ }
+ union
+ {
+ int a2; //Aliased
+ int a3; //Aliased
+ }
+ union A4
+ {
+ int b0; //Not aliased
+ }
+ A4 a4;
+ union A5
+ {
+ int b0; //Aliased
+ int b1; //Aliased
+ }
+ A5 a5;
+ }
+
+ static assert(!isUnionAliased!(S, 0)); //a0;
+ static assert(!isUnionAliased!(S, 1)); //a1;
+ static assert( isUnionAliased!(S, 2)); //a2;
+ static assert( isUnionAliased!(S, 3)); //a3;
+ static assert(!isUnionAliased!(S, 4)); //a4;
+ static assert(!isUnionAliased!(S.A4, 0)); //a4.b0;
+ static assert(!isUnionAliased!(S, 5)); //a5;
+ static assert( isUnionAliased!(S.A5, 0)); //a5.b0;
+ static assert( isUnionAliased!(S.A5, 1)); //a5.b1;
+}
+
+package string errnoString(int errno) nothrow @trusted
+{
+ import core.stdc.string : strlen;
+ version (CRuntime_Glibc)
+ {
+ import core.stdc.string : strerror_r;
+ char[1024] buf = void;
+ auto s = strerror_r(errno, buf.ptr, buf.length);
+ }
+ else version (Posix)
+ {
+ // XSI-compliant
+ import core.stdc.string : strerror_r;
+ char[1024] buf = void;
+ const(char)* s;
+ if (strerror_r(errno, buf.ptr, buf.length) == 0)
+ s = buf.ptr;
+ else
+ return "Unknown error";
+ }
+ else
+ {
+ import core.stdc.string : strerror;
+ auto s = strerror(errno);
+ }
+ return s[0 .. s.strlen].idup;
+}
+
+/*********************
+ * Thrown if errors that set $(D errno) occur.
+ */
+class ErrnoException : Exception
+{
+ final @property uint errno() { return _errno; } /// Operating system error code.
+ 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
+ {
+ 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
+ {
+ _errno = errno;
+ super(msg ~ " (" ~ errnoString(errno) ~ ")", file, line);
+ }
+
+ @system unittest
+ {
+ import core.stdc.errno : errno, EAGAIN;
+
+ auto old = errno;
+ scope(exit) errno = old;
+
+ errno = EAGAIN;
+ auto ex = new ErrnoException("oh no");
+ assert(ex.errno == EAGAIN);
+ }
+
+ @system unittest
+ {
+ import core.stdc.errno : EAGAIN;
+ auto ex = new ErrnoException("oh no", EAGAIN);
+ 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
+ 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)
+ T1 = The type of the expression.
+ T2 = The return type of the error handler.
+ expression = The expression to run and return its result.
+ errorHandler = The handler to run if the expression throwed.
+
+ 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)
+{
+ static assert(!is(typeof(return) == void),
+ "The error handler's return value("
+ ~ T2.stringof ~
+ ") does not have a common type with the expression("
+ ~ T1.stringof ~
+ ")."
+ );
+ try
+ {
+ return expression();
+ }
+ catch (E)
+ {
+ return errorHandler();
+ }
+}
+
+///ditto
+//delegate version
+CommonType!(T1, T2) ifThrown(E : Throwable, T1, T2)(lazy scope T1 expression, scope T2 delegate(E) errorHandler)
+{
+ static assert(!is(typeof(return) == void),
+ "The error handler's return value("
+ ~ T2.stringof ~
+ ") does not have a common type with the expression("
+ ~ T1.stringof ~
+ ")."
+ );
+ try
+ {
+ return expression();
+ }
+ catch (E e)
+ {
+ return errorHandler(e);
+ }
+}
+
+///ditto
+//delegate version, general overload to catch any Exception
+CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate(Exception) errorHandler)
+{
+ static assert(!is(typeof(return) == void),
+ "The error handler's return value("
+ ~ T2.stringof ~
+ ") does not have a common type with the expression("
+ ~ T1.stringof ~
+ ")."
+ );
+ try
+ {
+ return expression();
+ }
+ catch (Exception e)
+ {
+ return errorHandler(e);
+ }
+}
+
+//Verify Examples
+@system unittest
+{
+ import std.conv;
+ import std.string;
+ //Revert to a default value upon an error:
+ assert("x".to!int().ifThrown(0) == 0);
+
+ //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");
+
+ //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.
+ assert("%s".format().ifThrown(e => e.classinfo.name) == "std.format.FormatException");
+}
+
+@system unittest
+{
+ import core.exception;
+ import std.conv;
+ import std.string;
+ //Basic behaviour - all versions.
+ assert("1".to!int().ifThrown(0) == 1);
+ assert("x".to!int().ifThrown(0) == 0);
+ assert("1".to!int().ifThrown!ConvException(0) == 1);
+ assert("x".to!int().ifThrown!ConvException(0) == 0);
+ assert("1".to!int().ifThrown(e=>0) == 1);
+ assert("x".to!int().ifThrown(e=>0) == 0);
+ static if (__traits(compiles, 0.ifThrown!Exception(e => 0))) //This will only work with a fix that was not yet pulled
+ {
+ assert("1".to!int().ifThrown!ConvException(e=>0) == 1);
+ assert("x".to!int().ifThrown!ConvException(e=>0) == 0);
+ }
+
+ //Exceptions other than stated not caught.
+ assert("x".to!int().ifThrown!StringException(0).collectException!ConvException() !is null);
+ static if (__traits(compiles, 0.ifThrown!Exception(e => 0))) //This will only work with a fix that was not yet pulled
+ {
+ assert("x".to!int().ifThrown!StringException(e=>0).collectException!ConvException() !is null);
+ }
+
+ //Default does not include errors.
+ int throwRangeError() { throw new RangeError; }
+ assert(throwRangeError().ifThrown(0).collectException!RangeError() !is null);
+ assert(throwRangeError().ifThrown(e=>0).collectException!RangeError() !is null);
+
+ //Incompatible types are not accepted.
+ static assert(!__traits(compiles, 1.ifThrown(new Object())));
+ static assert(!__traits(compiles, (new Object()).ifThrown(1)));
+ static assert(!__traits(compiles, 1.ifThrown(e=>new Object())));
+ static assert(!__traits(compiles, (new Object()).ifThrown(e=>1)));
+}
+
+version (unittest) package
+@property 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
+ select multiple primitives to be handled.
+
+ $(D RangePrimitive.access) is a shortcut for the access primitives; $(D front),
+ $(D back) and $(D opIndex).
+
+ $(D RangePrimitive.pop) is a shortcut for the mutating primitives;
+ $(D popFront) and $(D popBack).
+ */
+enum RangePrimitive
+{
+ front = 0b00_0000_0001, ///
+ back = 0b00_0000_0010, /// Ditto
+ popFront = 0b00_0000_0100, /// Ditto
+ popBack = 0b00_0000_1000, /// Ditto
+ empty = 0b00_0001_0000, /// Ditto
+ save = 0b00_0010_0000, /// Ditto
+ length = 0b00_0100_0000, /// Ditto
+ opDollar = 0b00_1000_0000, /// Ditto
+ opIndex = 0b01_0000_0000, /// Ditto
+ opSlice = 0b10_0000_0000, /// Ditto
+ access = front | back | opIndex, /// Ditto
+ pop = popFront | popBack, /// Ditto
+}
+
+/** 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).
+All handled primitives must have return types or values compatible with the
+user-supplied handler.
+
+Params:
+ E = The type of $(D 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
+ 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
+ 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).
+
+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
+returning that when an exception is caught.
+*/
+auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Range)(Range input)
+if (isInputRange!Range)
+{
+ static struct Handler
+ {
+ private Range range;
+
+ static if (isForwardRange!Range)
+ {
+ @property typeof(this) save()
+ {
+ static if (primitivesToHandle & RangePrimitive.save)
+ {
+ try
+ {
+ return typeof(this)(range.save);
+ }
+ catch (E exception)
+ {
+ return typeof(this)(handler(exception, this.range));
+ }
+ }
+ else
+ return typeof(this)(range.save);
+ }
+ }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ static if (primitivesToHandle & RangePrimitive.empty)
+ {
+ try
+ {
+ return this.range.empty;
+ }
+ catch (E exception)
+ {
+ return handler(exception, this.range);
+ }
+ }
+ else
+ return this.range.empty;
+ }
+ }
+
+ @property auto ref front()
+ {
+ static if (primitivesToHandle & RangePrimitive.front)
+ {
+ try
+ {
+ return this.range.front;
+ }
+ catch (E exception)
+ {
+ return handler(exception, this.range);
+ }
+ }
+ else
+ return this.range.front;
+ }
+
+ void popFront()
+ {
+ static if (primitivesToHandle & RangePrimitive.popFront)
+ {
+ try
+ {
+ this.range.popFront();
+ }
+ catch (E exception)
+ {
+ handler(exception, this.range);
+ }
+ }
+ else
+ this.range.popFront();
+ }
+
+ static if (isBidirectionalRange!Range)
+ {
+ @property auto ref back()
+ {
+ static if (primitivesToHandle & RangePrimitive.back)
+ {
+ try
+ {
+ return this.range.back;
+ }
+ catch (E exception)
+ {
+ return handler(exception, this.range);
+ }
+ }
+ else
+ return this.range.back;
+ }
+
+ void popBack()
+ {
+ static if (primitivesToHandle & RangePrimitive.popBack)
+ {
+ try
+ {
+ this.range.popBack();
+ }
+ catch (E exception)
+ {
+ handler(exception, this.range);
+ }
+ }
+ else
+ this.range.popBack();
+ }
+ }
+
+ static if (isRandomAccessRange!Range)
+ {
+ auto ref opIndex(size_t index)
+ {
+ static if (primitivesToHandle & RangePrimitive.opIndex)
+ {
+ try
+ {
+ return this.range[index];
+ }
+ catch (E exception)
+ {
+ static if (__traits(compiles, handler(exception, this.range, index)))
+ return handler(exception, this.range, index);
+ else
+ return handler(exception, this.range);
+ }
+ }
+ else
+ return this.range[index];
+ }
+ }
+
+ static if (hasLength!Range)
+ {
+ @property auto length()
+ {
+ static if (primitivesToHandle & RangePrimitive.length)
+ {
+ try
+ {
+ return this.range.length;
+ }
+ catch (E exception)
+ {
+ return handler(exception, this.range);
+ }
+ }
+ else
+ return this.range.length;
+ }
+ }
+
+ static if (hasSlicing!Range)
+ {
+ static if (hasLength!Range)
+ {
+ typeof(this) opSlice(size_t lower, size_t upper)
+ {
+ static if (primitivesToHandle & RangePrimitive.opSlice)
+ {
+ try
+ {
+ return typeof(this)(this.range[lower .. upper]);
+ }
+ catch (E exception)
+ {
+ return typeof(this)(handler(exception, this.range));
+ }
+ }
+ else
+ return typeof(this)(this.range[lower .. upper]);
+ }
+ }
+ else static if (is(typeof(Range.init[size_t.init .. $])))
+ {
+ import std.range : Take, takeExactly;
+ static struct DollarToken {}
+ enum opDollar = DollarToken.init;
+
+ typeof(this) opSlice(size_t lower, DollarToken)
+ {
+ static if (primitivesToHandle & RangePrimitive.opSlice)
+ {
+ try
+ {
+ return typeof(this)(this.range[lower .. $]);
+ }
+ catch (E exception)
+ {
+ return typeof(this)(handler(exception, this.range));
+ }
+ }
+ else
+ return typeof(this)(this.range[lower .. $]);
+ }
+
+ Take!Handler opSlice(size_t lower, size_t upper)
+ {
+ static if (primitivesToHandle & RangePrimitive.opSlice)
+ {
+ try
+ {
+ return takeExactly(typeof(this)(this.range[lower .. $]), upper - 1);
+ }
+ catch (E exception)
+ {
+ return takeExactly(typeof(this)(handler(exception, this.range)), 0);
+ }
+ }
+ else
+ return takeExactly(typeof(this)(this.range[lower .. $]), upper - 1);
+ }
+ }
+ }
+ }
+
+ return Handler(input);
+}
+
+///
+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`
+}
+
+pure nothrow @safe unittest
+{
+ static struct ThrowingRange
+ {
+ pure @safe:
+ @property bool empty()
+ {
+ throw new Exception("empty has thrown");
+ }
+
+ @property int front()
+ {
+ throw new Exception("front has thrown");
+ }
+
+ @property int back()
+ {
+ throw new Exception("back has thrown");
+ }
+
+ void popFront()
+ {
+ throw new Exception("popFront has thrown");
+ }
+
+ void popBack()
+ {
+ throw new Exception("popBack has thrown");
+ }
+
+ int opIndex(size_t)
+ {
+ throw new Exception("opIndex has thrown");
+ }
+
+ ThrowingRange opSlice(size_t, size_t)
+ {
+ throw new Exception("opSlice has thrown");
+ }
+
+ @property size_t length()
+ {
+ throw new Exception("length has thrown");
+ }
+
+ alias opDollar = length;
+
+ @property ThrowingRange save()
+ {
+ throw new Exception("save has thrown");
+ }
+ }
+
+ static assert(isInputRange!ThrowingRange);
+ static assert(isForwardRange!ThrowingRange);
+ static assert(isBidirectionalRange!ThrowingRange);
+ static assert(hasSlicing!ThrowingRange);
+ static assert(hasLength!ThrowingRange);
+
+ auto f = ThrowingRange();
+ auto fb = f.handle!(Exception, RangePrimitive.front | RangePrimitive.back,
+ (e, r) => -1)();
+ assert(fb.front == -1);
+ assert(fb.back == -1);
+ assertThrown(fb.popFront());
+ assertThrown(fb.popBack());
+ assertThrown(fb.empty);
+ assertThrown(fb.save);
+ assertThrown(fb[0]);
+
+ auto accessRange = f.handle!(Exception, RangePrimitive.access,
+ (e, r) => -1);
+ assert(accessRange.front == -1);
+ assert(accessRange.back == -1);
+ assert(accessRange[0] == -1);
+ assertThrown(accessRange.popFront());
+ assertThrown(accessRange.popBack());
+
+ auto pfb = f.handle!(Exception, RangePrimitive.pop, (e, r) => -1)();
+
+ pfb.popFront(); // this would throw otherwise
+ pfb.popBack(); // this would throw otherwise
+
+ auto em = f.handle!(Exception,
+ RangePrimitive.empty, (e, r) => false)();
+
+ assert(!em.empty);
+
+ auto arr = f.handle!(Exception,
+ RangePrimitive.opIndex, (e, r) => 1337)();
+
+ assert(arr[0] == 1337);
+
+ auto arr2 = f.handle!(Exception,
+ RangePrimitive.opIndex, (e, r, i) => i)();
+
+ assert(arr2[0] == 0);
+ assert(arr2[1337] == 1337);
+
+ auto save = f.handle!(Exception,
+ RangePrimitive.save,
+ function(Exception e, ref ThrowingRange r) {
+ return ThrowingRange();
+ })();
+
+ save.save;
+
+ auto slice = f.handle!(Exception,
+ RangePrimitive.opSlice, (e, r) => ThrowingRange())();
+
+ auto sliced = slice[0 .. 1337]; // this would throw otherwise
+
+ static struct Infinite
+ {
+ import std.range : Take;
+ pure @safe:
+ enum bool empty = false;
+ int front() { assert(false); }
+ void popFront() { assert(false); }
+ Infinite save() @property { assert(false); }
+ static struct DollarToken {}
+ enum opDollar = DollarToken.init;
+ Take!Infinite opSlice(size_t, size_t) { assert(false); }
+ Infinite opSlice(size_t, DollarToken)
+ {
+ throw new Exception("opSlice has thrown");
+ }
+ }
+
+ static assert(isInputRange!Infinite);
+ static assert(isInfinite!Infinite);
+ static assert(hasSlicing!Infinite);
+
+ assertThrown(Infinite()[0 .. $]);
+
+ auto infinite = Infinite.init.handle!(Exception,
+ RangePrimitive.opSlice, (e, r) => Infinite())();
+
+ auto infSlice = infinite[0 .. $]; // this would throw otherwise
+}
+
+
+/++
+ Convenience mixin for trivially sub-classing exceptions
+
+ Even trivially sub-classing an exception involves writing boilerplate code
+ for the constructor to: 1$(RPAREN) correctly pass in the source file and line number
+ the exception was thrown from; 2$(RPAREN) be usable with $(LREF enforce) which
+ expects exception constructors to take arguments in a fixed order. This
+ mixin provides that boilerplate code.
+
+ Note however that you need to mark the $(B mixin) line with at least a
+ minimal (i.e. just $(B ///)) DDoc comment if you want the mixed-in
+ constructors to be documented in the newly created Exception subclass.
+
+ $(RED Current limitation): Due to
+ $(LINK2 https://issues.dlang.org/show_bug.cgi?id=11500, bug #11500),
+ currently the constructors specified in this mixin cannot be overloaded with
+ any other custom constructors. Thus this mixin can currently only be used
+ when no such custom constructors need to be explicitly specified.
+ +/
+mixin template basicExceptionCtors()
+{
+ /++
+ Params:
+ msg = The message for the exception.
+ file = The file where the exception occurred.
+ line = The line number where the exception occurred.
+ next = The previous exception in the chain of exceptions, if any.
+ +/
+ this(string msg, string file = __FILE__, size_t line = __LINE__,
+ Throwable next = null) @nogc @safe pure nothrow
+ {
+ super(msg, file, line, next);
+ }
+
+ /++
+ Params:
+ msg = The message for the exception.
+ next = The previous exception in the chain of exceptions.
+ file = The file where the exception occurred.
+ line = The line number where the exception occurred.
+ +/
+ this(string msg, Throwable next, string file = __FILE__,
+ size_t line = __LINE__) @nogc @safe pure nothrow
+ {
+ super(msg, file, line, next);
+ }
+}
+
+///
+@safe unittest
+{
+ class MeaCulpa: Exception
+ {
+ ///
+ mixin basicExceptionCtors;
+ }
+
+ try
+ throw new MeaCulpa("test");
+ catch (MeaCulpa e)
+ {
+ assert(e.msg == "test");
+ assert(e.file == __FILE__);
+ assert(e.line == __LINE__ - 5);
+ }
+}
+
+@safe pure nothrow unittest
+{
+ class TestException : Exception { mixin basicExceptionCtors; }
+ auto e = new Exception("msg");
+ auto te1 = new TestException("foo");
+ auto te2 = new TestException("foo", e);
+}
+
+@safe unittest
+{
+ class TestException : Exception { mixin basicExceptionCtors; }
+ auto e = new Exception("!!!");
+
+ auto te1 = new TestException("message", "file", 42, e);
+ assert(te1.msg == "message");
+ assert(te1.file == "file");
+ assert(te1.line == 42);
+ assert(te1.next is e);
+
+ auto te2 = new TestException("message", e, "file", 42);
+ assert(te2.msg == "message");
+ assert(te2.file == "file");
+ assert(te2.line == 42);
+ assert(te2.next is e);
+
+ auto te3 = new TestException("foo");
+ assert(te3.msg == "foo");
+ assert(te3.file == __FILE__);
+ assert(te3.line == __LINE__ - 3);
+ assert(te3.next is null);
+
+ auto te4 = new TestException("foo", e);
+ assert(te4.msg == "foo");
+ assert(te4.file == __FILE__);
+ assert(te4.line == __LINE__ - 3);
+ assert(te4.next is e);
+}