diff --git a/src/core/internal/dassert.d b/src/core/internal/dassert.d index fe7ba0dffd..afb9136a6b 100644 --- a/src/core/internal/dassert.d +++ b/src/core/internal/dassert.d @@ -75,7 +75,21 @@ private string miniFormat(V)(const scope ref V v) import core.internal.traits: isAggregateType; import core.stdc.stdio : sprintf; import core.stdc.string : strlen; - static if (is(V : bool)) + + static if (is(V == shared T, T)) + { + // Use atomics to avoid race conditions whenever possible + static if (__traits(compiles, atomicLoad(v))) + { + T tmp = cast(T) atomicLoad(v); + return miniFormat(tmp); + } + else + { // Fall back to a simple cast - we're violating the type system anyways + return miniFormat(*cast(T*) &v); + } + } + else static if (is(V == bool)) { return v ? "true" : "false"; } @@ -105,14 +119,21 @@ private string miniFormat(V)(const scope ref V v) { return "`null`"; } - else static if (__traits(compiles, { string s = v.toString(); })) - { - return v.toString(); - } - // Non-const toString(), e.g. classes inheriting from Object + // toString() isn't always const, e.g. classes inheriting from Object else static if (__traits(compiles, { string s = V.init.toString(); })) { - return (cast() v).toString(); + // Object references / struct pointers may be null + static if (is(V == class) || is(V == interface) || is(V == U*, U)) + { + if (v is null) + return "`null`"; + } + + // Prefer const overload of toString + static if (__traits(compiles, { string s = v.toString(); })) + return v.toString(); + else + return (cast() v).toString(); } // Static arrays or slices (but not aggregates with `alias this`) else static if (is(V : U[], U) && !isAggregateType!V) @@ -197,6 +218,11 @@ private string miniFormat(V)(const scope ref V v) } } +// This should be a local import in miniFormat but fails with a cyclic dependency error +// core.thread.osthread -> core.time -> object -> core.internal.array.capacity +// -> core.atomic -> core.thread -> core.thread.osthread +import core.atomic : atomicLoad; + // Inverts a comparison token for use in _d_assert_fail private string invertCompToken(string comp) { diff --git a/test/exceptions/src/assert_fail.d b/test/exceptions/src/assert_fail.d index 22dbf09e31..d7de373b5b 100644 --- a/test/exceptions/src/assert_fail.d +++ b/test/exceptions/src/assert_fail.d @@ -29,6 +29,7 @@ void testIntegers()() test(uint.min, uint.max, "0 != 4294967295"); test(long.min, long.max, "-9223372036854775808 != 9223372036854775807"); test(ulong.min, ulong.max, "0 != 18446744073709551615"); + test(shared(ulong).min, shared(ulong).max, "0 != 18446744073709551615"); int testFun() { return 1; } test(testFun(), 2, "1 != 2"); @@ -79,6 +80,9 @@ void testToString()() } test(new Foo("a"), new Foo("b"), "Foo(a) != Foo(b)"); + scope f = cast(shared) new Foo("a"); + test!"!="(f, f, "Foo(a) == Foo(a)"); + // Verifiy that the const toString is selected if present static struct Overloaded { @@ -94,6 +98,9 @@ void testToString()() } test!"!="(Overloaded(), Overloaded(), "Const == Const"); + + Foo fnull = null; + test!"!is"(fnull, fnull, "`null` is `null`"); } @@ -135,6 +142,9 @@ void testStruct()() NoCopy n; test(_d_assert_fail!"!="(n, n), "NoCopy() == NoCopy()"); + + shared NoCopy sn; + test(_d_assert_fail!"!="(sn, sn), "NoCopy() == NoCopy()"); } void testAA()()