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