// 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.

    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);
    }
    --------------------

    Macros:
        WIKI = Phobos/StdException

    Copyright: Copyright Andrei Alexandrescu 2008-, Jonathan M Davis 2011-.
    License:   $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0)
    Authors:   $(WEB erdani.org, Andrei Alexandrescu) and Jonathan M Davis
    Source:    $(PHOBOSSRC std/_exception.d)

 +/
module std.exception;

import std.range;
import std.traits;

import core.stdc.errno;
import core.stdc.string;

/++
    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.
 +/
void assertNotThrown(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 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);
    }
}
///
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!`);
}
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.`);
}

unittest
{
    import core.exception : AssertError;

    void throwEx(Throwable t) { throw t; }
    void nothrowEx() { }

    try
    {
        assertNotThrown!Exception(nothrowEx());
    }
    catch (AssertError) assert(0);

    try
    {
        assertNotThrown!Exception(nothrowEx(), "It's a message");
    }
    catch (AssertError) assert(0);

    try
    {
        assertNotThrown!AssertError(nothrowEx());
    }
    catch (AssertError) assert(0);

    try
    {
        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);
}
///
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.`);
}

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);
    }
}


/++
    If $(D !!value) is true, $(D value) is returned. 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;
}

/++
   $(RED Deprecated. If passing the file or line number explicitly, please use
         the overload of enforce which takes them as function arguments. Taking
         them as template arguments causes unnecessary template bloat. This
         overload will be removed in June 2015.)
 +/
deprecated("Use the overload of enforce that takes file and line as function arguments.")
T enforce(T, string file, size_t line = __LINE__)
    (T value, lazy const(char)[] msg = null)
    if (is(typeof({ if (!value) {} })))
{
    if (!value) bailOut(file, line, msg);
    return value;
}

/++
    If $(D !!value) is true, $(D value) is returned. Otherwise, the given
    delegate is called.

    The whole safety and purity 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.ptr ? 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));
    }
}

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);
    }
}

unittest
{
    // Issue 10510
    extern(C) void cFoo() { }
    enforce(false, &cFoo);
}

// purity and safety inference test
unittest
{
    import std.typetuple;

    foreach (EncloseSafe; TypeTuple!(false, true))
    foreach (EnclosePure; TypeTuple!(false, true))
    {
        foreach (BodySafe; TypeTuple!(false, true))
        foreach (BodyPure; TypeTuple!(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
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);
}

deprecated 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, __FILE__, __LINE__)(s, "");
}

unittest
{
    // Issue 14685

    class E : Exception
    {
        this() { super("Not found"); }
    }
    static assert(!__traits(compiles, { enforce!E(false); }));
}

/++
    If $(D !!value) is true, $(D value) is returned. 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;
}

unittest
{
    assertNotThrown(enforce(true, new Exception("this should not be thrown")));
    assertThrown(enforce(false, new Exception("this should be thrown")));
}

/++
    If $(D !!value) is true, $(D value) is returned. Otherwise,
    $(D new ErrnoException(msg)) is thrown. $(D ErrnoException) assumes that the
    last operation set $(D errno) to an error code.

    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 true), $(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;
    }
}

unittest
{
    import std.array : empty;
    import core.exception : OutOfMemoryError;
    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))));
}

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;
}
///
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;
}

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;
}
///
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.
 *
 * 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
 * $(WEB 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;
}

unittest
{
    int[] arr = new int[1];
    auto arr1 = assumeUnique(arr);
    assert(is(typeof(arr1) == immutable(int)[]) && arr == null);
}

immutable(T[U]) assumeUnique(T, U)(ref T[U] array) pure nothrow
{
    auto result = cast(immutable(T[U])) array;
    array = null;
    return result;
}

// @@@BUG@@@
version(none) 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.)
 */
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);
    }
}

///
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);
}

unittest
{
    import core.exception : AssertError;

    void alwaysThrows()
    {
        throw new Exception("I threw up");
    }
    void bad() nothrow
    {
        assumeWontThrow(alwaysThrows());
    }
    assertThrown!AssertError(bad());
}

/**
The "pointsTo" functions, $(D doesPointTo) and $(D mayPointTo).

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 pointsTo will handle it 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 pointsTo(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;
    }
}

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
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);
}
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);
}

deprecated ("pointsTo is ambiguous. Please use either of doesPointTo or mayPointTo")
alias pointsTo = doesPointTo;

/+
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;
}
//
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;
}

/// Pointers
unittest
{
    int  i = 0;
    int* p = null;
    assert(!p.doesPointTo(i));
    p = &i;
    assert( p.doesPointTo(i));
}

/// Structs and Unions
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)
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 itslef.

    //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
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:
    ()
    {
        foreach (index, _; FieldTypeTuple!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
}

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]));
}

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.
}


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));
}

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
}
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));
}
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));
}

/*********************
 * Thrown if errors that set $(D errno) occur.
 */
class ErrnoException : Exception
{
    final @property uint errno() { return _errno; } /// Operating system error code.
    private uint _errno;
    this(string msg, string file = null, size_t line = 0) @trusted
    {
        _errno = .errno;
        version (CRuntime_Glibc)
        {
            char[1024] buf = void;
            auto s = core.stdc.string.strerror_r(errno, buf.ptr, buf.length);
        }
        else
        {
            auto s = core.stdc.string.strerror(errno);
        }
        super(msg ~ " (" ~ s[0..s.strlen].idup ~ ")", file, line);
    }
}

/++
    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.

    Examples:
    --------------------
    //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.

    Examples:
    --------------------
    //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
unittest
{
    import std.string;
    import std.conv;
    //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");
}

unittest
{
    import std.string;
    import std.conv;
    import core.exception;
    //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).

opSlice:
Infinite ranges with slicing support must return an instance of
$(XREF range, Take) when sliced with a specific lower and upper
bound (see $(XREF_PACK range,primitives,hasSlicing)); $(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 .. $])))
            {
                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 : equal, 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 : 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
    {
        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
}
