// 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. 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); } } /// 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; } 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); } /// 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); } } /++ 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; } // Explicitly undocumented. It will be removed in August 2016. @@@DEPRECATED_2016-08@@@ 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; } /++ 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.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.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 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); })); } /++ 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; } 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; } } 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 = ""; /** * 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 * $(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.) * * 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); } } /// 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()); } /** 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*) ⌖ 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*) ⌖ 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 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 // 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 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, _; 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 } 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)); } 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; } /********************* * 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. Returns: expression, if it does not throw. Otherwise, returns the result of errorHandler. 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 }