diff mbox series

[committed,17/19] libphobos: Import druntime testsuite v2.098.0-beta.1 (e6caaab9)

Message ID 20211130151112.631900-1-ibuclaw@gdcproject.org
State Committed
Headers show
Series None | expand

Commit Message

Iain Buclaw Nov. 30, 2021, 3:11 p.m. UTC
This is the updated D runtime library testsuite.

Bootstrapped, regression tested, and committed to mainline.

Regards,
Iain.

---
libphobos/ChangeLog:

	* testsuite/libphobos.aa/test_aa.d: Update test.
	* testsuite/libphobos.exceptions/unknown_gc.d: Likewise.
	* testsuite/libphobos.hash/test_hash.d: Likewise.
	* testsuite/libphobos.shared/host.c: Likewise.
	* testsuite/libphobos.shared/load.d: Likewise.
	* testsuite/libphobos.shared/load_13414.d: Likewise.
	* testsuite/libphobos.thread/fiber_guard_page.d: Likewise.
	* testsuite/libphobos.thread/tlsgc_sections.d: Likewise.
	* testsuite/libphobos.shared/link_mod_collision.d: Removed.
	* testsuite/libphobos.shared/load_mod_collision.d: Removed.
	* testsuite/libphobos.allocations/alloc_from_assert.d: New test.
	* testsuite/libphobos.betterc/test18828.d: New test.
	* testsuite/libphobos.betterc/test19416.d: New test.
	* testsuite/libphobos.betterc/test19421.d: New test.
	* testsuite/libphobos.betterc/test19561.d: New test.
	* testsuite/libphobos.betterc/test19924.d: New test.
	* testsuite/libphobos.betterc/test20088.d: New test.
	* testsuite/libphobos.betterc/test20613.d: New test.
	* testsuite/libphobos.config/test19433.d: New test.
	* testsuite/libphobos.config/test20459.d: New test.
	* testsuite/libphobos.exceptions/assert_fail.d: New test.
	* testsuite/libphobos.exceptions/catch_in_finally.d: New test.
	* testsuite/libphobos.exceptions/future_message.d: New test.
	* testsuite/libphobos.exceptions/long_backtrace_trunc.d: New test.
	* testsuite/libphobos.exceptions/refcounted.d: New test.
	* testsuite/libphobos.exceptions/rt_trap_exceptions.d: New test.
	* testsuite/libphobos.exceptions/rt_trap_exceptions_drt.d: New test.
	* testsuite/libphobos.gc/attributes.d: New test.
	* testsuite/libphobos.gc/forkgc.d: New test.
	* testsuite/libphobos.gc/forkgc2.d: New test.
	* testsuite/libphobos.gc/nocollect.d: New test.
	* testsuite/libphobos.gc/precisegc.d: New test.
	* testsuite/libphobos.gc/recoverfree.d: New test.
	* testsuite/libphobos.gc/sigmaskgc.d: New test.
	* testsuite/libphobos.gc/startbackgc.d: New test.
	* testsuite/libphobos.imports/bug18193.d: New test.
	* testsuite/libphobos.init_fini/custom_gc.d: New test.
	* testsuite/libphobos.init_fini/test18996.d: New test.
	* testsuite/libphobos.lifetime/large_aggregate_destroy_21097.d: New test.
	* testsuite/libphobos.thread/external_threads.d: New test.
	* testsuite/libphobos.thread/join_detach.d: New test.
	* testsuite/libphobos.thread/test_import.d: New test.
	* testsuite/libphobos.thread/tlsstack.d: New test.
	* testsuite/libphobos.typeinfo/enum_.d: New test.
	* testsuite/libphobos.typeinfo/isbaseof.d: New test.
	* testsuite/libphobos.unittest/customhandler.d: New test.
---
 libphobos/testsuite/libphobos.aa/test_aa.d    |  79 ++-
 .../libphobos.allocations/alloc_from_assert.d |  25 +
 .../testsuite/libphobos.betterc/test18828.d   |  10 +
 .../testsuite/libphobos.betterc/test19416.d   |  14 +
 .../testsuite/libphobos.betterc/test19421.d   |  13 +
 .../testsuite/libphobos.betterc/test19561.d   |  16 +
 .../testsuite/libphobos.betterc/test19924.d   |  15 +
 .../testsuite/libphobos.betterc/test20088.d   |  14 +
 .../testsuite/libphobos.betterc/test20613.d   |  18 +
 .../testsuite/libphobos.config/test19433.d    |   7 +
 .../testsuite/libphobos.config/test20459.d    |   5 +
 .../libphobos.exceptions/assert_fail.d        | 564 ++++++++++++++++++
 .../libphobos.exceptions/catch_in_finally.d   | 191 ++++++
 .../libphobos.exceptions/future_message.d     |  71 +++
 .../long_backtrace_trunc.d                    |  37 ++
 .../libphobos.exceptions/refcounted.d         |  96 +++
 .../libphobos.exceptions/rt_trap_exceptions.d |  15 +
 .../rt_trap_exceptions_drt.d                  |  11 +
 .../libphobos.exceptions/unknown_gc.d         |   4 +
 libphobos/testsuite/libphobos.gc/attributes.d |  30 +
 libphobos/testsuite/libphobos.gc/forkgc.d     |  36 ++
 libphobos/testsuite/libphobos.gc/forkgc2.d    |  22 +
 libphobos/testsuite/libphobos.gc/nocollect.d  |  15 +
 libphobos/testsuite/libphobos.gc/precisegc.d  | 126 ++++
 .../testsuite/libphobos.gc/recoverfree.d      |  13 +
 libphobos/testsuite/libphobos.gc/sigmaskgc.d  |  42 ++
 .../testsuite/libphobos.gc/startbackgc.d      |  22 +
 .../testsuite/libphobos.hash/test_hash.d      | 140 ++++-
 .../testsuite/libphobos.imports/bug18193.d    |   4 +
 .../testsuite/libphobos.init_fini/custom_gc.d | 203 +++++++
 .../testsuite/libphobos.init_fini/test18996.d |  13 +
 .../large_aggregate_destroy_21097.d           |  78 +++
 libphobos/testsuite/libphobos.shared/host.c   |   8 +
 .../libphobos.shared/link_mod_collision.d     |   5 -
 libphobos/testsuite/libphobos.shared/load.d   |   1 -
 .../testsuite/libphobos.shared/load_13414.d   |  13 +-
 .../libphobos.shared/load_mod_collision.d     |  14 -
 .../libphobos.thread/external_threads.d       |  50 ++
 .../libphobos.thread/fiber_guard_page.d       |   4 +
 .../testsuite/libphobos.thread/join_detach.d  |  20 +
 .../testsuite/libphobos.thread/test_import.d  |   7 +
 .../libphobos.thread/tlsgc_sections.d         |  61 +-
 .../testsuite/libphobos.thread/tlsstack.d     |  38 ++
 .../testsuite/libphobos.typeinfo/enum_.d      |  21 +
 .../testsuite/libphobos.typeinfo/isbaseof.d   |  46 ++
 .../libphobos.unittest/customhandler.d        |  21 +
 46 files changed, 2191 insertions(+), 67 deletions(-)
 create mode 100644 libphobos/testsuite/libphobos.allocations/alloc_from_assert.d
 create mode 100644 libphobos/testsuite/libphobos.betterc/test18828.d
 create mode 100644 libphobos/testsuite/libphobos.betterc/test19416.d
 create mode 100644 libphobos/testsuite/libphobos.betterc/test19421.d
 create mode 100644 libphobos/testsuite/libphobos.betterc/test19561.d
 create mode 100644 libphobos/testsuite/libphobos.betterc/test19924.d
 create mode 100644 libphobos/testsuite/libphobos.betterc/test20088.d
 create mode 100644 libphobos/testsuite/libphobos.betterc/test20613.d
 create mode 100644 libphobos/testsuite/libphobos.config/test19433.d
 create mode 100644 libphobos/testsuite/libphobos.config/test20459.d
 create mode 100644 libphobos/testsuite/libphobos.exceptions/assert_fail.d
 create mode 100644 libphobos/testsuite/libphobos.exceptions/catch_in_finally.d
 create mode 100644 libphobos/testsuite/libphobos.exceptions/future_message.d
 create mode 100644 libphobos/testsuite/libphobos.exceptions/long_backtrace_trunc.d
 create mode 100644 libphobos/testsuite/libphobos.exceptions/refcounted.d
 create mode 100644 libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions.d
 create mode 100644 libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions_drt.d
 create mode 100644 libphobos/testsuite/libphobos.gc/attributes.d
 create mode 100644 libphobos/testsuite/libphobos.gc/forkgc.d
 create mode 100644 libphobos/testsuite/libphobos.gc/forkgc2.d
 create mode 100644 libphobos/testsuite/libphobos.gc/nocollect.d
 create mode 100644 libphobos/testsuite/libphobos.gc/precisegc.d
 create mode 100644 libphobos/testsuite/libphobos.gc/recoverfree.d
 create mode 100644 libphobos/testsuite/libphobos.gc/sigmaskgc.d
 create mode 100644 libphobos/testsuite/libphobos.gc/startbackgc.d
 create mode 100644 libphobos/testsuite/libphobos.imports/bug18193.d
 create mode 100644 libphobos/testsuite/libphobos.init_fini/custom_gc.d
 create mode 100644 libphobos/testsuite/libphobos.init_fini/test18996.d
 create mode 100644 libphobos/testsuite/libphobos.lifetime/large_aggregate_destroy_21097.d
 delete mode 100644 libphobos/testsuite/libphobos.shared/link_mod_collision.d
 delete mode 100644 libphobos/testsuite/libphobos.shared/load_mod_collision.d
 create mode 100644 libphobos/testsuite/libphobos.thread/external_threads.d
 create mode 100644 libphobos/testsuite/libphobos.thread/join_detach.d
 create mode 100644 libphobos/testsuite/libphobos.thread/test_import.d
 create mode 100644 libphobos/testsuite/libphobos.thread/tlsstack.d
 create mode 100644 libphobos/testsuite/libphobos.typeinfo/enum_.d
 create mode 100644 libphobos/testsuite/libphobos.typeinfo/isbaseof.d
 create mode 100644 libphobos/testsuite/libphobos.unittest/customhandler.d
diff mbox series

Patch

diff --git a/libphobos/testsuite/libphobos.aa/test_aa.d b/libphobos/testsuite/libphobos.aa/test_aa.d
index d6222b13175..11ad2f90ff1 100644
--- a/libphobos/testsuite/libphobos.aa/test_aa.d
+++ b/libphobos/testsuite/libphobos.aa/test_aa.d
@@ -30,6 +30,8 @@  void main()
     issue15367();
     issue16974();
     issue18071();
+    issue20440();
+    issue21442();
     testIterationWithConst();
     testStructArrayKey();
     miscTests1();
@@ -286,21 +288,20 @@  void testUpdate2()
     assert(updated);
 }
 
-void testByKey1()
+void testByKey1() @safe
 {
-    static assert(!__traits(compiles,
-        () @safe {
-            struct BadValue
-            {
-                int x;
-                this(this) @safe { *(cast(ubyte*)(null) + 100000) = 5; } // not @safe
-                alias x this;
-            }
+    static struct BadValue
+    {
+        int x;
+        this(this) @system { *(cast(ubyte*)(null) + 100000) = 5; } // not @safe
+        alias x this;
+    }
 
-            BadValue[int] aa;
-            () @safe { auto x = aa.byKey.front; } ();
-        }
-    ));
+    BadValue[int] aa;
+
+    // FIXME: Should be @system because of the postblit
+    if (false)
+        auto x = aa.byKey.front;
 }
 
 void testByKey2() nothrow pure
@@ -690,6 +691,58 @@  void issue18071()
     () @safe { assert(f.byKey.empty); }();
 }
 
+/// Test that `require` works even with types whose opAssign
+/// doesn't return a reference to the receiver.
+/// https://issues.dlang.org/show_bug.cgi?id=20440
+void issue20440() @safe
+{
+    static struct S
+    {
+        int value;
+        auto opAssign(S s) {
+            this.value = s.value;
+            return this;
+        }
+    }
+    S[S] aa;
+    assert(aa.require(S(1), S(2)) == S(2));
+    assert(aa[S(1)] == S(2));
+}
+
+///
+void issue21442()
+{
+    import core.memory;
+
+    size_t[size_t] glob;
+
+    class Foo
+    {
+        size_t count;
+
+        this (size_t entries) @safe
+        {
+            this.count = entries;
+            foreach (idx; 0 .. entries)
+                glob[idx] = idx;
+        }
+
+        ~this () @safe
+        {
+            foreach (idx; 0 .. this.count)
+                glob.remove(idx);
+        }
+    }
+
+    void bar () @safe
+    {
+        Foo f = new Foo(16);
+    }
+
+    bar();
+    GC.collect(); // Needs to happen from a GC collection
+}
+
 /// Verify iteration with const.
 void testIterationWithConst()
 {
diff --git a/libphobos/testsuite/libphobos.allocations/alloc_from_assert.d b/libphobos/testsuite/libphobos.allocations/alloc_from_assert.d
new file mode 100644
index 00000000000..a377cd9139d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.allocations/alloc_from_assert.d
@@ -0,0 +1,25 @@ 
+import core.exception;
+import core.memory;
+
+class FailFinalization
+{
+    int magic;
+
+    ~this () @nogc nothrow
+    {
+        try
+            assert(this.magic == 42);
+        catch (AssertError) {}
+    }
+}
+
+void foo ()
+{
+    auto dangling = new FailFinalization();
+}
+
+void main()
+{
+    foo();
+    GC.collect();
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test18828.d b/libphobos/testsuite/libphobos.betterc/test18828.d
new file mode 100644
index 00000000000..db0530dd13c
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test18828.d
@@ -0,0 +1,10 @@ 
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18828
+
+struct S18828 { }
+
+extern(C) void main()
+{
+    S18828 s;
+    destroy(s);
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test19416.d b/libphobos/testsuite/libphobos.betterc/test19416.d
new file mode 100644
index 00000000000..aff93d3aa5f
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test19416.d
@@ -0,0 +1,14 @@ 
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19416
+
+import core.stdc.stdlib : malloc, free;
+import core.exception : onOutOfMemoryError;
+
+extern(C) void main()
+{
+    auto m = malloc(1);
+    if (!m)
+        onOutOfMemoryError();
+    else
+        free(m);
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test19421.d b/libphobos/testsuite/libphobos.betterc/test19421.d
new file mode 100644
index 00000000000..2427c5e9a0c
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test19421.d
@@ -0,0 +1,13 @@ 
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19421
+
+import core.memory;
+
+extern(C) void main() @nogc nothrow pure
+{
+    auto p = pureMalloc(1);
+    p = pureRealloc(p, 2);
+    if (p) pureFree(p);
+    p = pureCalloc(1, 1);
+    if (p) pureFree(p);
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test19561.d b/libphobos/testsuite/libphobos.betterc/test19561.d
new file mode 100644
index 00000000000..96ecec51ae4
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test19561.d
@@ -0,0 +1,16 @@ 
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19561
+
+import core.memory;
+
+extern(C) void main() @nogc nothrow pure
+{
+    int[3] a, b;
+    a[] = 0;
+    a[] = b[];
+    //FIXME: Next line requires compiler change.
+    //a[] = 1; // error: undefined reference to '_memset32'
+    a[] += 1;
+    a[] += b[];
+    int[3] c = a[] + b[];
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test19924.d b/libphobos/testsuite/libphobos.betterc/test19924.d
new file mode 100644
index 00000000000..e9a93cad0ac
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test19924.d
@@ -0,0 +1,15 @@ 
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19924
+
+import core.bitop;
+
+extern(C) void main()
+{
+    uint a = 0x01_23_45_67;
+    a = bswap(a);
+    assert(a == 0x67_45_23_01);
+
+    ulong b = 0x01_23_45_67_89_ab_cd_ef;
+    b = bswap(b);
+    assert(b == 0xef_cd_ab_89_67_45_23_01);
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test20088.d b/libphobos/testsuite/libphobos.betterc/test20088.d
new file mode 100644
index 00000000000..a809041c877
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test20088.d
@@ -0,0 +1,14 @@ 
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20088
+
+struct S {
+    int i;
+}
+
+extern(C) int main() @nogc nothrow pure
+{
+    S[2] s = [S(1),S(2)];
+    void[] v = cast(void[])s;
+    S[] p = cast(S[])v; // cast of void[] to S[] triggers __ArrayCast template function
+    return 0;
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test20613.d b/libphobos/testsuite/libphobos.betterc/test20613.d
new file mode 100644
index 00000000000..b03e2d17b62
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test20613.d
@@ -0,0 +1,18 @@ 
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20613
+
+extern(C) int main() @nogc nothrow pure
+{
+    auto s = "F";
+    final switch(s)
+    {
+    case "A": break;
+    case "B": break;
+    case "C": break;
+    case "D": break;
+    case "E": break;
+    case "F": break;
+    case "G": break;
+    }
+    return 0;
+}
diff --git a/libphobos/testsuite/libphobos.config/test19433.d b/libphobos/testsuite/libphobos.config/test19433.d
new file mode 100644
index 00000000000..1c58145103e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.config/test19433.d
@@ -0,0 +1,7 @@ 
+extern(C) __gshared bool rt_cmdline_enabled = false;
+
+void main(string[] args)
+{
+    assert(args.length == 2);
+    assert(args[1] == "--DRT-dont-eat-me");
+}
diff --git a/libphobos/testsuite/libphobos.config/test20459.d b/libphobos/testsuite/libphobos.config/test20459.d
new file mode 100644
index 00000000000..248720d8f30
--- /dev/null
+++ b/libphobos/testsuite/libphobos.config/test20459.d
@@ -0,0 +1,5 @@ 
+void main (string[] args)
+{
+    assert(args.length == 5);
+    assert(args[1 .. $] == [ "foo", "bar", "--", "--DRT-gcopts=profile:1" ]);
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/assert_fail.d b/libphobos/testsuite/libphobos.exceptions/assert_fail.d
new file mode 100644
index 00000000000..79b3cb8139e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/assert_fail.d
@@ -0,0 +1,564 @@ 
+import core.stdc.stdio : fprintf, stderr;
+import core.internal.dassert : _d_assert_fail;
+
+void test(string comp = "==", A, B)(A a, B b, string msg, size_t line = __LINE__)
+{
+    test(_d_assert_fail!(A)(comp, a, b), msg, line);
+}
+
+void test(const string actual, const string expected, size_t line = __LINE__)
+{
+    import core.exception : AssertError;
+
+    if (actual != expected)
+    {
+        const msg = "Mismatch!\nExpected: <" ~ expected ~ ">\nActual:   <" ~ actual ~ '>';
+        throw new AssertError(msg, __FILE__, line);
+    }
+}
+
+void testIntegers()
+{
+    test(1, 2, "1 != 2");
+    test(-10, 8, "-10 != 8");
+    test(byte.min, byte.max, "-128 != 127");
+    test(ubyte.min, ubyte.max, "0 != 255");
+    test(short.min, short.max, "-32768 != 32767");
+    test(ushort.min, ushort.max, "0 != 65535");
+    test(int.min, int.max, "-2147483648 != 2147483647");
+    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");
+}
+
+void testIntegerComparisons()
+{
+    test!"!="(2, 2, "2 == 2");
+    test!"<"(2, 1, "2 >= 1");
+    test!"<="(2, 1, "2 > 1");
+    test!">"(1, 2, "1 <= 2");
+    test!">="(1, 2, "1 < 2");
+}
+
+void testFloatingPoint()
+{
+    if (__ctfe)
+    {
+        test(float.max, -float.max, "<float not supported> != <float not supported>");
+        test(double.max, -double.max, "<double not supported> != <double not supported>");
+        test(real(1), real(-1), "<real not supported> != <real not supported>");
+    }
+    else
+    {
+        test(1.5, 2.5, "1.5 != 2.5");
+        test(float.max, -float.max, "3.40282e+38 != -3.40282e+38");
+        test(double.max, -double.max, "1.79769e+308 != -1.79769e+308");
+        test(real(1), real(-1), "1 != -1");
+    }
+}
+
+void testPointers()
+{
+    static struct S
+    {
+        string toString() const { return "S(...)"; }
+    }
+
+    static if ((void*).sizeof == 4)
+        enum ptr = "0x12345670";
+    else
+        enum ptr = "0x123456789abcdef0";
+
+    int* p = cast(int*) mixin(ptr);
+    test(cast(S*) p, p, ptr ~ " != " ~ ptr);
+}
+
+void testStrings()
+{
+    test("foo", "bar", `"foo" != "bar"`);
+    test("", "bar", `"" != "bar"`);
+
+    char[] dlang = "dlang".dup;
+    const(char)[] rust = "rust";
+    test(dlang, rust, `"dlang" != "rust"`);
+
+    // https://issues.dlang.org/show_bug.cgi?id=20322
+    test("left"w, "right"w, `"left" != "right"`);
+    test("left"d, "right"d, `"left" != "right"`);
+
+    test('A', 'B', "'A' != 'B'");
+    test(wchar('❤'), wchar('∑'), "'❤' != '∑'");
+    test(dchar('❤'), dchar('∑'), "'❤' != '∑'");
+
+    // Detect invalid code points
+    test(char(255), 'B', "cast(char) 255 != 'B'");
+    test(wchar(0xD888), wchar('∑'), "cast(wchar) 55432 != '∑'");
+    test(dchar(0xDDDD), dchar('∑'), "cast(dchar) 56797 != '∑'");
+}
+
+void testToString()
+{
+    class Foo
+    {
+        this(string payload) {
+            this.payload = payload;
+        }
+
+        string payload;
+        override string toString() {
+            return "Foo(" ~ payload ~ ")";
+        }
+    }
+    test(new Foo("a"), new Foo("b"), "Foo(a) != Foo(b)");
+
+    scope f = cast(shared) new Foo("a");
+    if (!__ctfe) // Ref somehow get's lost in CTFE
+    test!"!="(f, f, "Foo(a) == Foo(a)");
+
+    // Verifiy that the const toString is selected if present
+    static struct Overloaded
+    {
+        string toString()
+        {
+            return "Mutable";
+        }
+
+        string toString() const
+        {
+            return "Const";
+        }
+    }
+
+    test!"!="(Overloaded(), Overloaded(), "Const == Const");
+
+    Foo fnull = null;
+    test!"!is"(fnull, fnull, "`null` is `null`");
+}
+
+
+void testArray()
+{
+    test([1], [0], "[1] != [0]");
+    test([1, 2, 3], [0], "[1, 2, 3] != [0]");
+
+    // test with long arrays
+    int[] arr;
+    foreach (i; 0 .. 100)
+        arr ~= i;
+    test(arr, [0], "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, ...] != [0]");
+
+    // Ignore fake arrays
+    static struct S
+    {
+        int[2] arr;
+        int[] get() return { return arr[]; }
+        alias get this;
+    }
+
+    const a = S([1, 2]);
+    test(a, S([3, 4]), "S([1, 2]) != S([3, 4])");
+}
+
+void testStruct()
+{
+    struct S { int s; }
+    struct T { T[] t; }
+    test(S(0), S(1), "S(0) != S(1)");
+    test(T([T(null)]), T(null), "T([T([])]) != T([])");
+
+    // https://issues.dlang.org/show_bug.cgi?id=20323
+    static struct NoCopy
+    {
+        @disable this(this);
+    }
+
+    NoCopy n;
+    test(_d_assert_fail!(typeof(n))("!=", n, n), "NoCopy() == NoCopy()");
+
+    shared NoCopy sn;
+    test(_d_assert_fail!(typeof(sn))("!=", sn, sn), "NoCopy() == NoCopy()");
+}
+
+void testAA()
+{
+    test([1:"one"], [2: "two"], `[1: "one"] != [2: "two"]`);
+    test!"in"(1, [2: 3], "1 !in [2: 3]");
+    test!"in"("foo", ["bar": true], `"foo" !in ["bar": true]`);
+}
+
+void testAttributes() @safe pure @nogc nothrow
+{
+    int a;
+    string s = _d_assert_fail!(int, char)("==", a, 'c', 1, 'd');
+    assert(s == `(0, 'c') != (1, 'd')`);
+
+    string s2 = _d_assert_fail!int("", a);
+    assert(s2 == `0 != true`);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20066
+void testVoidArray()
+{
+    test!"!is"([], null, (__ctfe ? "<void[] not supported>" : "[]") ~ " is `null`");
+    test!"!is"(null, null, "`null` is `null`");
+    test([1], null, "[1] != `null`");
+    test("s", null, "\"s\" != `null`");
+    test(['c'], null, "\"c\" != `null`");
+    test!"!="(null, null, "`null` == `null`");
+
+    const void[] chunk = [byte(1), byte(2), byte(3)];
+    test(chunk, null, (__ctfe ? "<void[] not supported>" : "[1, 2, 3]") ~ " != `null`");
+}
+
+void testTemporary()
+{
+    static struct Bad
+    {
+        ~this() @system {}
+    }
+
+    test!"!="(Bad(), Bad(), "Bad() == Bad()");
+}
+
+void testEnum()
+{
+    static struct UUID {
+        union
+        {
+            ubyte[] data = [1];
+        }
+    }
+
+    ubyte[] data;
+    enum ctfe = UUID();
+    test(_d_assert_fail!(ubyte[])("==", ctfe.data, data), "[1] != []");
+}
+
+void testUnary()
+{
+    test(_d_assert_fail!int("", 9), "9 != true");
+    test(_d_assert_fail!(int[])("!", [1, 2, 3]), "[1, 2, 3] == true");
+}
+
+void testTuple()
+{
+    test(_d_assert_fail("=="), "() != ()");
+    test(_d_assert_fail("!="), "() == ()");
+    test(_d_assert_fail(">="), "() < ()");
+}
+
+void testStructEquals()
+{
+    struct T {
+        bool b;
+        int i;
+        float f1 = 2.5;
+        float f2 = 0;
+        string s1 = "bar";
+        string s2;
+    }
+
+    T t1;
+    test!"!="(t1, t1, `T(false, 0, 2.5, 0, "bar", "") == T(false, 0, 2.5, 0, "bar", "")`);
+    T t2 = {s1: "bari"};
+    test(t1, t2, `T(false, 0, 2.5, 0, "bar", "") != T(false, 0, 2.5, 0, "bari", "")`);
+}
+
+void testStructEquals2()
+{
+    struct T {
+        bool b;
+        int i;
+        float f1 = 2.5;
+        float f2 = 0;
+    }
+
+    T t1;
+    test!"!="(t1, t1, `T(false, 0, 2.5, 0) == T(false, 0, 2.5, 0)`);
+    T t2 = {i: 2};
+    test(t1, t2, `T(false, 0, 2.5, 0) != T(false, 2, 2.5, 0)`);
+}
+
+void testStructEquals3()
+{
+    struct T {
+        bool b;
+        int i;
+        string s1 = "bar";
+        string s2;
+    }
+
+    T t1;
+    test!"!="(t1, t1, `T(false, 0, "bar", "") == T(false, 0, "bar", "")`);
+    T t2 = {s1: "bari"};
+    test(t1, t2, `T(false, 0, "bar", "") != T(false, 0, "bari", "")`);
+}
+
+void testStructEquals4()
+{
+    struct T {
+        float f1 = 2.5;
+        float f2 = 0;
+        string s1 = "bar";
+        string s2;
+    }
+
+    T t1;
+    test!"!="(t1, t1, `T(2.5, 0, "bar", "") == T(2.5, 0, "bar", "")`);
+    T t2 = {s1: "bari"};
+    test(t1, t2, `T(2.5, 0, "bar", "") != T(2.5, 0, "bari", "")`);
+}
+
+void testStructEquals5()
+{
+    struct T {
+        bool b;
+        int i;
+        float f2 = 0;
+        string s2;
+    }
+
+    T t1;
+    test!"!="(t1, t1, `T(false, 0, 0, "") == T(false, 0, 0, "")`);
+    T t2 = {b: true};
+    test(t1, t2, `T(false, 0, 0, "") != T(true, 0, 0, "")`);
+}
+
+void testStructEquals6()
+{
+    class C { override string toString() { return "C()"; }}
+    struct T {
+        bool b;
+        int i;
+        float f2 = 0;
+        string s2;
+        int[] arr;
+        C c;
+    }
+
+    T t1;
+    test!"!="(t1, t1, "T(false, 0, 0, \"\", [], `null`) == T(false, 0, 0, \"\", [], `null`)");
+    T t2 = {arr: [1]};
+    test(t1, t2, "T(false, 0, 0, \"\", [], `null`) != T(false, 0, 0, \"\", [1], `null`)");
+    T t3 = {c: new C()};
+    test(t1, t3, "T(false, 0, 0, \"\", [], `null`) != T(false, 0, 0, \"\", [], C())");
+}
+
+void testContextPointer()
+{
+    int i;
+    struct T
+    {
+        int j;
+        int get()
+        {
+            return i * j;
+        }
+    }
+    T t = T(1);
+    t.tupleof[$-1] = cast(void*) 0xABCD; // Deterministic context pointer
+    test(t, t, `T(1, <context>: 0xabcd) != T(1, <context>: 0xabcd)`);
+}
+
+void testExternClasses()
+{
+    {
+        extern(C++) static class Cpp
+        {
+            int a;
+            this(int a) { this.a = a; }
+        }
+        scope a = new Cpp(1);
+        scope b = new Cpp(2);
+        test(a, b, "Cpp(1) != Cpp(2)");
+        test(a, Cpp.init, "Cpp(1) != null");
+    }
+    {
+        extern(C++) static class CppToString
+        {
+            int a;
+            this(int a) { this.a = a; }
+            extern(D) string toString() const { return a == 0 ? "hello" : "world"; }
+        }
+        scope a = new CppToString(0);
+        scope b = new CppToString(1);
+        test(a, b, "hello != world");
+    }
+    if (!__ctfe)
+    {
+        extern(C++) static class Opaque;
+        Opaque null_ = null;
+        Opaque notNull = cast(Opaque) &null_;
+        test(null_, notNull, "null != <Opaque>");
+    }
+    {
+        extern(C++) static interface Stuff {}
+        scope Stuff stuff = new class Stuff {};
+        test(stuff, Stuff.init, "Stuff() != null");
+    }
+}
+
+void testShared()
+{
+    static struct Small
+    {
+        int i;
+    }
+
+    auto s1 = shared Small(1);
+    const s2 = shared Small(2);
+    test(s1, s2, "Small(1) != Small(2)");
+
+    static struct Big
+    {
+        long[10] l;
+    }
+
+    auto b1 = shared Big([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+    const b2 = shared Big();
+    test(b1, b2, "Big([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) != Big([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])");
+
+    // Sanity check: Big shouldn't be supported by atomicLoad
+    import core.atomic : atomicLoad;
+    static assert( __traits(compiles, atomicLoad(s1)));
+    static assert(!__traits(compiles, atomicLoad(b1)));
+}
+
+void testException()
+{
+    static struct MayThrow
+    {
+        int i;
+        string toString()
+        {
+            if (i == 1)
+                throw new Exception("Error");
+            return "Some message";
+        }
+    }
+
+    test(MayThrow(0), MayThrow(1), `Some message != <toString() failed: "Error", called on MayThrow(1)>`);
+}
+
+void testOverlappingFields()
+{
+    static struct S
+    {
+        union
+        {
+            double num;
+            immutable(char)[] name;
+        }
+    }
+
+    test(S(1.0), S(2.0), "S(<overlapped field>, <overlapped field>) != S(<overlapped field>, <overlapped field>)");
+
+    static struct S2
+    {
+        int valid;
+        union
+        {
+            double num;
+            immutable(char)[] name;
+        }
+    }
+
+    test(S2(4, 1.0), S2(5, 2.0), "S2(4, <overlapped field>, <overlapped field>) != S2(5, <overlapped field>, <overlapped field>)");
+
+    static struct S3
+    {
+        union
+        {
+            double num;
+            immutable(char)[] name;
+        }
+        int valid;
+    }
+    S3 a = {
+        num: 1.0,
+        valid: 8
+    };
+
+    S3 b = {
+        num: 1.0,
+        valid: 8
+    };
+    test(a, b, "S3(<overlapped field>, <overlapped field>, 8) != S3(<overlapped field>, <overlapped field>, 8)");
+}
+
+void testDestruction()
+{
+    static class Test
+    {
+        __gshared string unary, binary;
+        __gshared bool run;
+
+        ~this()
+        {
+            run = true;
+            unary = _d_assert_fail!int("", 1);
+            binary = _d_assert_fail!int("==", 1, 2);
+        }
+    }
+
+    static void createGarbage()
+    {
+        new Test();
+        new long[100];
+    }
+
+    import core.memory : GC;
+    createGarbage();
+    GC.collect();
+
+    assert(Test.run);
+    assert(Test.unary == "Assertion failed (rich formatting is disabled in finalizers)");
+    assert(Test.binary == "Assertion failed (rich formatting is disabled in finalizers)");
+}
+
+int main()
+{
+    testIntegers();
+    testIntegerComparisons();
+    testFloatingPoint();
+    testPointers();
+    testStrings();
+    testToString();
+    testArray();
+    testStruct();
+    testAA();
+    testAttributes();
+    testVoidArray();
+    testTemporary();
+    testEnum();
+    testUnary();
+    testTuple();
+    if (!__ctfe)
+        testStructEquals();
+    if (!__ctfe)
+        testStructEquals2();
+    testStructEquals3();
+    if (!__ctfe)
+        testStructEquals4();
+    if (!__ctfe)
+        testStructEquals5();
+    if (!__ctfe)
+        testStructEquals6();
+    testContextPointer();
+    testExternClasses();
+    testShared();
+    testException();
+    testOverlappingFields();
+    if (!__ctfe)
+        testDestruction();
+
+    if (!__ctfe)
+        fprintf(stderr, "success.\n");
+    return 0;
+}
+
+enum forceCTFE = main();
diff --git a/libphobos/testsuite/libphobos.exceptions/catch_in_finally.d b/libphobos/testsuite/libphobos.exceptions/catch_in_finally.d
new file mode 100644
index 00000000000..88bd73957dd
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/catch_in_finally.d
@@ -0,0 +1,191 @@ 
+import core.stdc.stdio : fprintf, stderr;
+
+class MyException : Exception
+{
+    this() { super(typeof(this).stringof); }
+}
+
+void throw_catch()
+{
+    try
+    {
+        throw new MyException;
+    }
+    catch (MyException)
+    {
+    }
+    catch (Exception)
+    {
+        assert(false);
+    }
+}
+
+// Test that exceptions that are entirely thrown and caught in finally blocks don't affect exception handling.
+void test1()
+{
+    try
+    {
+        try
+        {
+            throw new Exception("p");
+        }
+        finally
+        {
+            throw_catch();
+        }
+    }
+    catch (Exception e)
+    {
+        assert(e.msg == "p");
+    }
+}
+
+// Test that exceptions that are entirely thrown and caught in finally blocks don't interfere with chaining.
+void test2()
+{
+    try
+    {
+        try
+        {
+            try
+            {
+                throw new Exception("p");
+            }
+            finally
+            {
+                throw new Exception("q");
+            }
+        }
+        finally
+        {
+            throw_catch();
+        }
+    }
+    catch(Exception e)
+    {
+        assert(e.msg == "p");
+        assert(e.next.msg == "q");
+        assert(!e.next.next);
+    }
+}
+
+void test3()
+{
+    try
+    {
+        try
+        {
+            try
+            {
+                throw new Exception("p");
+            }
+            finally
+            {
+                throw_catch();
+            }
+        }
+        finally
+        {
+            throw new Exception("q");
+        }
+    }
+    catch(Exception e)
+    {
+        assert(e.msg == "p");
+        assert(e.next.msg == "q");
+        assert(!e.next.next);
+    }
+}
+
+// Test order of exception handler operations.
+void test4()
+{
+    string result;
+    void throw_catch()
+    {
+        pragma(inline, false);
+        try
+        {
+            result ~= "b";
+            throw new MyException;
+        }
+        catch (MyException)
+        {
+            result ~= "c";
+        }
+        catch (Exception)
+        {
+            assert(false);
+        }
+    }
+    try
+    {
+        try
+        {
+            result ~= "a";
+            throw new Exception("");
+        }
+        finally
+        {
+            throw_catch();
+        }
+    }
+    catch(Exception e)
+    {
+        result ~= "d";
+    }
+    assert(result == "abcd");
+}
+
+void test5()
+{
+    string result;
+    void fail()
+    {
+        result ~= "b";
+        throw new Exception("a");
+    }
+
+    void throw_catch()
+    {
+        pragma(inline, false);
+        try
+        {
+            fail();
+        }
+        catch(Exception e)
+        {
+            assert(e.msg == "a");
+            assert(!e.next);
+            result ~= "c";
+        }
+    }
+    try
+    {
+        try
+        {
+            result ~= "a";
+            throw new Exception("x");
+        }
+        finally
+        {
+            throw_catch();
+        }
+    }
+    catch (Exception e)
+    {
+        assert(e.msg == "x");
+        assert(!e.next);
+        result ~= "d";
+    }
+    assert(result == "abcd");
+}
+
+void main() {
+    test1();
+    test2();
+    test3();
+    test4();
+    test5();
+    fprintf(stderr, "success.\n");
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/future_message.d b/libphobos/testsuite/libphobos.exceptions/future_message.d
new file mode 100644
index 00000000000..61b10348287
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/future_message.d
@@ -0,0 +1,71 @@ 
+// { dg-options "-Wno-deprecated" }
+import core.stdc.stdio;
+
+// Make sure basic stuff works with future Throwable.message
+class NoMessage : Throwable
+{
+    @nogc @safe pure nothrow this(string msg, Throwable next = null)
+    {
+        super(msg, next);
+    }
+}
+
+class WithMessage : Throwable
+{
+    @nogc @safe pure nothrow this(string msg, Throwable next = null)
+    {
+        super(msg, next);
+    }
+
+    override const(char)[] message() const
+    {
+        return "I have a custom message.";
+    }
+}
+
+class WithMessageNoOverride : Throwable
+{
+    @nogc @safe pure nothrow this(string msg, Throwable next = null)
+    {
+        super(msg, next);
+    }
+
+    const(char)[] message() const
+    {
+        return "I have a custom message and no override.";
+    }
+}
+
+class WithMessageNoOverrideAndDifferentSignature : Throwable
+{
+    @nogc @safe pure nothrow this(string msg, Throwable next = null)
+    {
+        super(msg, next);
+    }
+
+    immutable(char)[] message()
+    {
+        return "I have a custom message and I'm nothing like Throwable.message.";
+    }
+}
+
+void test(Throwable t)
+{
+    try
+    {
+        throw t;
+    }
+    catch (Throwable e)
+    {
+        fprintf(stderr, "%.*s ", cast(int)e.message.length, e.message.ptr);
+    }
+}
+
+void main()
+{
+     test(new NoMessage("exception"));
+     test(new WithMessage("exception"));
+     test(new WithMessageNoOverride("exception"));
+     test(new WithMessageNoOverrideAndDifferentSignature("exception"));
+     fprintf(stderr, "\n");
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/long_backtrace_trunc.d b/libphobos/testsuite/libphobos.exceptions/long_backtrace_trunc.d
new file mode 100644
index 00000000000..3ff45e55c87
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/long_backtrace_trunc.d
@@ -0,0 +1,37 @@ 
+struct AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA(T) {
+struct BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB {
+struct CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC {
+struct DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD {
+struct EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE {
+struct FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF {
+struct GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG {
+struct HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH {
+     T tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt(int x) {
+    throw new Exception("test");
+    }
+}
+}
+}
+}
+}
+}
+}
+}
+
+void main() {
+    try {
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!int.
+    BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.
+    CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.
+    DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.
+    EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.
+    FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.
+    GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.
+    HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH x;
+    x.tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt(1);
+    } catch (Exception e) {
+        import core.stdc.stdio;
+        auto str = e.toString();
+        printf("%.*s\n", cast(int)str.length, str.ptr);
+    }
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/refcounted.d b/libphobos/testsuite/libphobos.exceptions/refcounted.d
new file mode 100644
index 00000000000..2b7e79bbf39
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/refcounted.d
@@ -0,0 +1,96 @@ 
+// { dg-options "-fpreview=dip1008" }
+class E : Exception
+{
+    static int instances;
+    this(string msg = "")
+    {
+        super(msg);
+        instances++;
+    }
+
+    ~this()
+    {
+        instances--;
+    }
+}
+
+void main()
+{
+    alias chain = Exception.chainTogether;
+
+    assert(chain(null, null) is null);
+
+    try
+    {
+        throw new E();
+    }
+    catch (E e)
+    {
+        assert(E.instances == 1);
+        assert(e.refcount == 2);
+    }
+
+    assert(E.instances == 0);
+
+    try
+    {
+        throw new E();
+    }
+    catch (E e)
+    {
+        assert(chain(null, e) is e);
+        assert(e.refcount == 2); // "Owned by e" + 1
+    }
+
+    assert(E.instances == 0);
+
+    try
+    {
+        throw new E();
+    }
+    catch (E e)
+    {
+        assert(chain(e, null) is e);
+        assert(e.refcount == 2); // "Owned by e" + 1
+    }
+
+    assert(E.instances == 0);
+
+    try
+    {
+        throw new E("first");
+    }
+    catch (E first)
+    {
+        try
+        {
+            throw new E("second");
+        }
+        catch (E second)
+        {
+            try
+            {
+                throw new E("third");
+            }
+            catch (E third)
+            {
+                assert(chain(first, second) is first);
+                assert(first.next is second);
+                assert(second.next is null);
+
+                assert(chain(first, third) is first);
+                assert(first.next is second);
+                assert(second.next is third);
+                assert(third.next is null);
+
+                assert(first.refcount == 2);
+                assert(second.refcount == 3);
+                assert(third.refcount == 3);
+            }
+        }
+
+        assert(E.instances == 3);
+    }
+
+    assert(E.instances == 0);
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions.d b/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions.d
new file mode 100644
index 00000000000..bd0c227e0f6
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions.d
@@ -0,0 +1,15 @@ 
+// { dg-shouldfail "uncaught exception" }
+// { dg-output "gcc.deh.*: uncaught exception" }
+// Code adapted from
+// http://arsdnet.net/this-week-in-d/2016-aug-07.html
+extern extern(C) __gshared bool rt_trapExceptions;
+extern extern(C) int _d_run_main(int, char**, void*) @system;
+
+extern(C) int main(int argc, char** argv) {
+    rt_trapExceptions = false;
+    return _d_run_main(argc, argv, &_main);
+}
+
+int _main() {
+    throw new Exception("this will abort");
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions_drt.d b/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions_drt.d
new file mode 100644
index 00000000000..fc4448cf0bf
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions_drt.d
@@ -0,0 +1,11 @@ 
+// { dg-shouldfail "uncaught exception" }
+void test()
+{
+    int innerLocal = 20;
+    throw new Exception("foo");
+}
+void main(string[] args)
+{
+    string myLocal = "bar";
+    test();
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/unknown_gc.d b/libphobos/testsuite/libphobos.exceptions/unknown_gc.d
index eb95aeed036..183c0f2969e 100644
--- a/libphobos/testsuite/libphobos.exceptions/unknown_gc.d
+++ b/libphobos/testsuite/libphobos.exceptions/unknown_gc.d
@@ -2,8 +2,12 @@ 
 // { dg-options "-shared-libphobos" }
 // { dg-shouldfail "unknowngc" }
 // { dg-output "No GC was initialized, please recheck the name of the selected GC \\('unknowngc'\\)." }
+import core.memory;
+
 extern(C) __gshared string[] rt_options = [ "gcopt=gc:unknowngc" ];
 
 void main()
 {
+    // GC initialized upon first call -> Unknown GC error is thrown
+    GC.enable();
 }
diff --git a/libphobos/testsuite/libphobos.gc/attributes.d b/libphobos/testsuite/libphobos.gc/attributes.d
new file mode 100644
index 00000000000..a7acd6ce550
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/attributes.d
@@ -0,0 +1,30 @@ 
+import core.memory;
+
+// TODO: The following should work, but L10 (second assert) fails.
+version(none) void dotest(T) (T* ptr)
+{
+    GC.clrAttr(ptr, uint.max);
+    assert(GC.getAttr(ptr) == 0);
+
+    GC.setAttr(ptr, GC.BlkAttr.NO_MOVE);
+    assert(GC.getAttr(ptr) == GC.BlkAttr.NO_MOVE);
+
+    GC.clrAttr(ptr, GC.BlkAttr.NO_MOVE);
+    assert(GC.getAttr(ptr) == 0);
+    GC.clrAttr(ptr, GC.BlkAttr.NO_MOVE);
+    assert(GC.getAttr(ptr) == 0);
+}
+else void dotest(T) (T* ptr)
+{
+    // https://issues.dlang.org/show_bug.cgi?id=21484
+    GC.clrAttr(ptr, uint.max);
+    GC.setAttr(ptr, GC.BlkAttr.NO_MOVE);
+    GC.getAttr(ptr);
+}
+
+void main ()
+{
+    auto ptr = new int;
+    dotest!(const(int))(ptr);
+    dotest!(int)(ptr);
+}
diff --git a/libphobos/testsuite/libphobos.gc/forkgc.d b/libphobos/testsuite/libphobos.gc/forkgc.d
new file mode 100644
index 00000000000..9c18dc296f0
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/forkgc.d
@@ -0,0 +1,36 @@ 
+import core.memory;
+import core.stdc.stdio;
+import core.sys.posix.sys.wait;
+import core.sys.posix.unistd;
+
+void main()
+{
+    printf("[parent] Creating garbage...\n");
+    foreach (n; 0 .. 1_000)
+        new uint[10_000];
+    printf("[parent] Collecting garbage...\n");
+    GC.collect();
+    printf("[parent] Forking...\n");
+    auto i = fork();
+    if (i < 0)
+        assert(false, "Fork failed");
+    if (i == 0)
+    {
+        printf("[child] In fork.\n");
+        printf("[child] Creating garbage...\n");
+        foreach (n; 0 .. 1_000)
+            new uint[10_000];
+        printf("[child] Collecting garbage...\n");
+        GC.collect();
+        printf("[child] Exiting fork.\n");
+    }
+    else
+    {
+        printf("[parent] Waiting for fork (PID %d).\n", i);
+        int status;
+        i = waitpid(i, &status, 0);
+        printf("[parent] Fork %d exited (%d).\n", i, status);
+        if (status != 0)
+            assert(false, "child had errors");
+    }
+}
diff --git a/libphobos/testsuite/libphobos.gc/forkgc2.d b/libphobos/testsuite/libphobos.gc/forkgc2.d
new file mode 100644
index 00000000000..de7796ced72
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/forkgc2.d
@@ -0,0 +1,22 @@ 
+import core.stdc.stdlib : exit;
+import core.sys.posix.sys.wait : waitpid;
+import core.sys.posix.unistd : fork;
+import core.thread : Thread;
+
+void main()
+{
+    foreach (t; 0 .. 10)
+        new Thread({
+            foreach (n; 0 .. 100)
+            {
+                foreach (x; 0 .. 100)
+                    new ubyte[x];
+                auto f = fork();
+                assert(f >= 0);
+                if (f == 0)
+                    exit(0);
+                else
+                    waitpid(f, null, 0);
+            }
+        }).start();
+}
diff --git a/libphobos/testsuite/libphobos.gc/nocollect.d b/libphobos/testsuite/libphobos.gc/nocollect.d
new file mode 100644
index 00000000000..5df1483a284
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/nocollect.d
@@ -0,0 +1,15 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=20567
+
+import core.memory;
+
+void main()
+{
+    auto stats = GC.profileStats();
+    assert(stats.numCollections == 0);
+
+    char[] sbuf = new char[256];  // small pool
+    char[] lbuf = new char[2049]; // large pool
+
+    stats = GC.profileStats();
+    assert(stats.numCollections == 0);
+}
\ No newline at end of file
diff --git a/libphobos/testsuite/libphobos.gc/precisegc.d b/libphobos/testsuite/libphobos.gc/precisegc.d
new file mode 100644
index 00000000000..9bcaf3f4faa
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/precisegc.d
@@ -0,0 +1,126 @@ 
+// precise GC related:
+// https://issues.dlang.org/show_bug.cgi?id=3463
+// https://issues.dlang.org/show_bug.cgi?id=4358
+// https://issues.dlang.org/show_bug.cgi?id=9094
+// https://issues.dlang.org/show_bug.cgi?id=13801
+// https://issues.dlang.org/show_bug.cgi?id=18900
+module testgc;
+
+import core.memory;
+import core.stdc.stdio;
+
+class C
+{
+    __gshared int dtors;
+    ~this() { dtors++; }
+
+    C next;
+    size_t val;
+}
+
+struct S
+{
+    __gshared int dtors;
+    ~this() { dtors++; }
+
+    size_t val;
+    S* next;
+}
+
+struct L
+{
+    __gshared int dtors;
+    ~this() { dtors++; }
+
+    size_t[1000] data;
+    S* node;
+}
+
+struct Roots
+{
+    C c;
+    S *s;
+    L *l;
+};
+
+Roots* roots;
+size_t iroots;
+
+void init()
+{
+    roots = new Roots;
+    roots.c = new C;
+    roots.c.next = new C;
+
+    roots.s = new S;
+    roots.s.next = new S;
+
+    roots.l = new L;
+    roots.l.node = new S;
+}
+
+void verifyPointers()
+{
+    assert(C.dtors == 0);
+    assert(S.dtors == 0);
+    assert(L.dtors == 0);
+}
+
+// compiling with -gx should help eliminating false pointers on the stack
+Roots makeFalsePointers()
+{
+    roots.c.val = cast(size_t) cast(void*) roots.c.next;
+    roots.c.next = null;
+    roots.s.val = cast(size_t) cast(void*) roots.s.next;
+    roots.s.next = null;
+    roots.l.data[7] = cast(size_t) cast(void*) roots.l.node;
+    roots.l.node = null;
+
+    return Roots(null, null, null); // try to spill register contents
+}
+
+Roots moveRoot()
+{
+    iroots = cast(size_t)roots;
+    roots = null;
+
+    return Roots(null, null, null); // try to spill register contents
+}
+
+// compiling with -gx should help eliminating false pointers on the stack
+void verifyFalsePointers()
+{
+    assert(C.dtors <= 1);
+    if (C.dtors < 1) printf ("False pointers? C.dtors = %d, 1 expected\n", C.dtors);
+    assert(S.dtors <= 2);
+    if (S.dtors < 2) printf ("False pointers? S.dtors = %d, 2 expected\n", S.dtors);
+    assert(L.dtors == 0);
+}
+
+extern(C) __gshared string[] rt_options = [ "gcopt=gc:precise", "scanDataSeg=precise" ];
+
+void main()
+{
+    GC.collect(); // cleanup from unittests
+
+    init();
+    GC.collect(); // should collect nothing
+    verifyPointers();
+
+    makeFalsePointers();
+    GC.collect(); // should collect roots.c.next, roots.s.next and roots.l.node
+    verifyFalsePointers();
+
+    moveRoot();
+    GC.collect(); // should collect all
+
+    version(Windows) // precise DATA scanning only implemented on Windows
+    {
+        assert(C.dtors <= 2);
+        if (C.dtors < 2) printf ("False DATA pointers? C.dtors = %d, 2 expected\n", C.dtors);
+        assert(S.dtors <= 3);
+        if (S.dtors < 3) printf ("False DATA pointers? S.dtors = %d, 2 expected\n", S.dtors);
+        assert(L.dtors <= 1);
+        if (L.dtors < 1) printf ("False DATA pointers? L.dtors = %d, 1 expected\n", L.dtors);
+    }
+}
diff --git a/libphobos/testsuite/libphobos.gc/recoverfree.d b/libphobos/testsuite/libphobos.gc/recoverfree.d
new file mode 100644
index 00000000000..59c3b4ab597
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/recoverfree.d
@@ -0,0 +1,13 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=20438
+import core.stdc.stdio;
+import core.memory;
+
+void main()
+{
+    auto used0 = GC.stats.usedSize;
+    void* z = GC.malloc(100);
+    GC.free(z);
+    GC.collect();
+    auto used1 = GC.stats.usedSize;
+    used1 <= used0 || assert(false);
+}
diff --git a/libphobos/testsuite/libphobos.gc/sigmaskgc.d b/libphobos/testsuite/libphobos.gc/sigmaskgc.d
new file mode 100644
index 00000000000..eb46316d18f
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/sigmaskgc.d
@@ -0,0 +1,42 @@ 
+
+// https://issues.dlang.org/show_bug.cgi?id=20256
+
+extern(C) __gshared string[] rt_options = [ "gcopt=parallel:1" ];
+
+void main()
+{
+    version (Posix)
+    {
+        import core.sys.posix.signal;
+        import core.sys.posix.unistd;
+        import core.thread;
+        import core.memory;
+
+        sigset_t m;
+        sigemptyset(&m);
+        sigaddset(&m, SIGHUP);
+
+        auto x = new int[](10000);
+        foreach (i; 0 .. 10000)
+        {
+            x ~= i;
+        }
+        GC.collect();  // GC create thread
+
+        sigprocmask(SIG_BLOCK, &m, null); // block SIGHUP from delivery to main thread
+
+        auto parent_pid = getpid();
+        auto child_pid = fork();
+        assert(child_pid >= 0);
+        if (child_pid == 0)
+        {
+            kill(parent_pid, SIGHUP); // send signal to parent
+            _exit(0);
+        }
+        // parent
+        Thread.sleep(100.msecs);
+        // if we are here, then GC threads didn't receive SIGHUP,
+        // otherwise whole process killed
+        _exit(0);
+    }
+}
diff --git a/libphobos/testsuite/libphobos.gc/startbackgc.d b/libphobos/testsuite/libphobos.gc/startbackgc.d
new file mode 100644
index 00000000000..bf51fbdbefe
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/startbackgc.d
@@ -0,0 +1,22 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=20270
+import core.sys.posix.sys.wait : waitpid;
+import core.sys.posix.unistd : fork, _exit;
+import core.thread : Thread;
+
+void main()
+{
+    foreach (t; 0 .. 10)
+        new Thread({
+            foreach (n; 0 .. 100)
+            {
+                foreach (x; 0 .. 100)
+                    new ubyte[x];
+                auto f = fork();
+                assert(f >= 0);
+                if (f == 0)
+                    _exit(0);
+                else
+                    waitpid(f, null, 0);
+            }
+        }).start();
+}
diff --git a/libphobos/testsuite/libphobos.hash/test_hash.d b/libphobos/testsuite/libphobos.hash/test_hash.d
index b40626659b7..d0a8e5fb809 100644
--- a/libphobos/testsuite/libphobos.hash/test_hash.d
+++ b/libphobos/testsuite/libphobos.hash/test_hash.d
@@ -1,3 +1,5 @@ 
+// { dg-prune-output "Warning: struct HasNonConstToHash has method toHash" }
+// { dg-prune-output "HasNonConstToHash.toHash defined here:" }
 void main()
 {
     issue19562();
@@ -8,8 +10,14 @@  void main()
     issue19005();
     issue19204();
     issue19262();
+    issue19282();
+    issue19332(); // Support might be removed in the future!
     issue19568();
     issue19582();
+    issue20034();
+    issue21642();
+    issue22024();
+    issue22076();
     testTypeInfoArrayGetHash1();
     testTypeInfoArrayGetHash2();
     pr2243();
@@ -130,6 +138,30 @@  void issue19262() nothrow
     h = hashOf(aa, h);
 }
 
+extern(C++) class Issue19282CppClass {}
+
+/// test that hashOf doesn't crash for non-null C++ objects.
+void issue19282()
+{
+    Issue19282CppClass c = new Issue19282CppClass();
+    size_t h = hashOf(c);
+    h = hashOf(c, h);
+}
+
+/// Ensure hashOf works for const struct that has non-const toHash & has all
+/// fields bitwise-hashable. (Support might be removed in the future!)
+void issue19332()
+{
+    static struct HasNonConstToHash
+    {
+        int a;
+        size_t toHash() { return a; }
+    }
+    const HasNonConstToHash val;
+    size_t h = hashOf(val);
+    h = hashOf!(const HasNonConstToHash)(val); // Ensure doesn't match more than one overload.
+}
+
 /// hashOf should not unnecessarily call a struct's fields' postblits & dtors in CTFE
 void issue19568()
 {
@@ -189,11 +221,99 @@  void issue19582()
         }
     }
     enum b2 = () {
-            S[10] a;
-            return ((const S[] a) @nogc nothrow pure @safe => toUbyte(a))(a);
+            return ((const S[] a) @nogc nothrow pure @safe => toUbyte(a))(new S[10]);
         }();
 }
 
+/// Check core.internal.hash.hashOf works with enums of non-scalar values
+void issue20034()
+{
+    enum E
+    {
+        a = "foo"
+    }
+    // should compile
+    assert(hashOf(E.a, 1));
+}
+
+/// [REG 2.084] hashOf will fail to compile for some structs/unions that recursively contain shared enums
+void issue21642() @safe nothrow pure
+{
+    enum C : char { _ = 1, }
+    union U { C c; void[0] _; }
+    shared union V { U u; }
+    cast(void) hashOf(V.init);
+    // Also test the underlying reason the above was failing.
+    import core.internal.convert : toUbyte;
+    shared C c;
+    assert(toUbyte(c) == [ubyte(1)]);
+}
+
+/// Accept enum type whose ultimate base type is a SIMD vector.
+void issue22024() @nogc nothrow pure @safe
+{
+    static if (is(__vector(float[2])))
+    {
+        enum E2 : __vector(float[2]) { a = __vector(float[2]).init, }
+        enum F2 : E2 { a = E2.init, }
+        assert(hashOf(E2.init) == hashOf(F2.init));
+        assert(hashOf(E2.init, 1) == hashOf(F2.init, 1));
+    }
+    static if (is(__vector(float[4])))
+    {
+        enum E4 : __vector(float[4]) { a = __vector(float[4]).init, }
+        enum F4 : E4 { a = E4.init, }
+        assert(hashOf(E4.init) == hashOf(F4.init));
+        assert(hashOf(E4.init, 1) == hashOf(F4.init, 1));
+    }
+}
+
+/// hashOf(S) can segfault if S.toHash is forwarded via `alias this` to a
+/// receiver which may be null.
+void issue22076()
+{
+    static struct S0 { Object a; alias a this; }
+
+    static struct S1
+    {
+        S0 a;
+        inout(S0)* b() inout nothrow { return &a; }
+        alias b this;
+    }
+
+    static struct S2
+    {
+        S0 a;
+        S1 b;
+    }
+
+    extern(C++) static class C0
+    {
+        int foo() { return 0; } // Need at least one function in vtable.
+        S0 a; alias a this;
+    }
+
+    extern(C++) static class C1
+    {
+        S1 a;
+        inout(S1)* b() inout nothrow { return &a; }
+        alias b this;
+    }
+
+    cast(void) hashOf(S0.init);
+    cast(void) hashOf(S0.init, 0);
+    cast(void) hashOf(S1.init);
+    cast(void) hashOf(S1.init, 0);
+    cast(void) hashOf(S2.init);
+    cast(void) hashOf(S2.init, 0);
+    auto c0 = new C0();
+    cast(void) hashOf(c0);
+    cast(void) hashOf(c0, 0);
+    auto c1 = new C1();
+    cast(void) hashOf(c1);
+    cast(void) hashOf(c1, 0);
+}
+
 /// Tests ensure TypeInfo_Array.getHash uses element hash functions instead
 /// of hashing array data.
 void testTypeInfoArrayGetHash1()
@@ -300,11 +420,9 @@  void pr2243()
     enum Bar vsexpr = Bar();
     enum int[int] aaexpr = [99:2, 12:6, 45:4];
     enum Gun eexpr = Gun.A;
-    enum cdouble cexpr = 7+4i;
     enum Foo[] staexpr = [Foo(), Foo(), Foo()];
     enum Bar[] vsaexpr = [Bar(), Bar(), Bar()];
     enum realexpr = 7.88;
-    enum raexpr = [8.99L+86i, 3.12L+99i, 5.66L+12i];
     enum nullexpr = null;
     enum plstr = Plain();
     enum plarrstr = [Plain(), Plain(), Plain()];
@@ -328,7 +446,6 @@  void pr2243()
     enum h10 = vsexpr.hashOf();
     enum h11 = aaexpr.hashOf();
     enum h12 = eexpr.hashOf();
-    enum h13 = cexpr.hashOf();
     enum h14 = hashOf(new Boo);
     enum h15 = staexpr.hashOf();
     enum h16 = hashOf([new Boo, new Boo, new Boo]);
@@ -349,7 +466,6 @@  void pr2243()
     auto h27 = ptrexpr.hashOf();
 
     enum h28 = realexpr.hashOf();
-    enum h29 = raexpr.hashOf();
     enum h30 = nullexpr.hashOf();
     enum h31 = plstr.hashOf();
     enum h32 = plarrstr.hashOf();
@@ -367,7 +483,6 @@  void pr2243()
     auto v10 = vsexpr;
     auto v11 = aaexpr;
     auto v12 = eexpr;
-    auto v13 = cexpr;
     auto v14 = new Boo;
     auto v15 = staexpr;
     auto v16 = [new Boo, new Boo, new Boo];
@@ -389,7 +504,6 @@  void pr2243()
     auto v26 = dgexpr;
     auto v27 = ptrexpr;
     auto v28 = realexpr;
-    auto v29 = raexpr;
 
     //runtime hashes
     auto rth1 = hashOf(v1);
@@ -404,7 +518,6 @@  void pr2243()
     auto rth10 = hashOf(v10);
     auto rth11 = hashOf(v11);
     auto rth12 = hashOf(v12);
-    auto rth13 = hashOf(v13);
     auto rth14 = hashOf(v14);
     auto rth15 = hashOf(v15);
     auto rth16 = hashOf(v16);
@@ -422,7 +535,6 @@  void pr2243()
     auto rth26 = hashOf(v26);
     auto rth27 = hashOf(v27);
     auto rth28 = hashOf(v28);
-    auto rth29 = hashOf(v29);
 
     auto rth31 = hashOf(v31);
     auto rth32 = hashOf(v32);
@@ -440,7 +552,6 @@  void pr2243()
     assert(h10 == rth10);
     assert(h11 == rth11);
     assert(h12 == rth12);
-    assert(h13 == rth13);
     assert(h14 == rth14);
     assert(h15 == rth15);
     assert(h16 == rth16);
@@ -455,8 +566,7 @@  void pr2243()
     assert(h25 == rth25);
     assert(h26 == rth26);
     assert(h27 == rth27);
-    assert(h28 == rth28);
-    assert(h29 == rth29);*/
+    assert(h28 == rth28);*/
     assert(h30 == rth30);
     assert(h31 == rth31);
     assert(h32 == rth32);
@@ -482,7 +592,6 @@  void pr2243()
     auto tih10 = tiHashOf(v10);
     auto tih11 = tiHashOf(v11);
     auto tih12 = tiHashOf(v12);
-    auto tih13 = tiHashOf(v13);
     auto tih14 = tiHashOf(v14);
     auto tih15 = tiHashOf(v15);
     auto tih16 = tiHashOf(v16);
@@ -498,7 +607,6 @@  void pr2243()
     auto tih26 = tiHashOf(v26);
     auto tih27 = tiHashOf(v27);
     auto tih28 = tiHashOf(v28);
-    auto tih29 = tiHashOf(v29);
     auto tih30 = tiHashOf(v30);
     auto tih31 = tiHashOf(v31);
     auto tih32 = tiHashOf(v32);
@@ -516,7 +624,6 @@  void pr2243()
     //assert(tih10 == rth10); // need compiler-generated __xtoHash changes
     assert(tih11 == rth11);
     assert(tih12 == rth12);
-    assert(tih13 == rth13);
     assert(tih14 == rth14);
     assert(tih15 == rth15);
     assert(tih16 == rth16);
@@ -532,7 +639,6 @@  void pr2243()
     assert(tih26 == rth26);
     assert(tih27 == rth27);
     assert(tih28 == rth28);
-    assert(tih29 == rth29);
     assert(tih30 == rth30);
     assert(tih31 == rth31);
     assert(tih32 == rth32);
diff --git a/libphobos/testsuite/libphobos.imports/bug18193.d b/libphobos/testsuite/libphobos.imports/bug18193.d
new file mode 100644
index 00000000000..fc8f5ca6882
--- /dev/null
+++ b/libphobos/testsuite/libphobos.imports/bug18193.d
@@ -0,0 +1,4 @@ 
+// { dg-options "-fversion=Shared" }
+// { dg-do compile }
+import core.runtime;
+import core.thread;
diff --git a/libphobos/testsuite/libphobos.init_fini/custom_gc.d b/libphobos/testsuite/libphobos.init_fini/custom_gc.d
new file mode 100644
index 00000000000..a5e2bf40356
--- /dev/null
+++ b/libphobos/testsuite/libphobos.init_fini/custom_gc.d
@@ -0,0 +1,203 @@ 
+import core.gc.registry;
+import core.gc.gcinterface;
+import core.stdc.stdlib;
+
+static import core.memory;
+
+extern (C) __gshared string[] rt_options = ["gcopt=gc:malloc"];
+
+extern (C) pragma(crt_constructor) void register_mygc()
+{
+    registerGCFactory("malloc", &MallocGC.initialize);
+}
+
+extern (C) void register_default_gcs()
+{
+    // remove default GCs
+}
+
+/** Simple GC that requires any pointers passed to it's API
+    to point to start of the allocation.
+ */
+class MallocGC : GC
+{
+nothrow @nogc:
+    static GC initialize()
+    {
+        import core.stdc.string : memcpy;
+
+        __gshared ubyte[__traits(classInstanceSize, MallocGC)] buf;
+
+        auto init = typeid(MallocGC).initializer();
+        assert(init.length == buf.length);
+        auto instance = cast(MallocGC) memcpy(buf.ptr, init.ptr, init.length);
+        instance.__ctor();
+        return instance;
+    }
+
+    this()
+    {
+    }
+
+    void Dtor()
+    {
+    }
+
+    void enable()
+    {
+    }
+
+    void disable()
+    {
+    }
+
+    void collect() nothrow
+    {
+    }
+
+    void collectNoStack() nothrow
+    {
+    }
+
+    void minimize() nothrow
+    {
+    }
+
+    uint getAttr(void* p) nothrow
+    {
+        return 0;
+    }
+
+    uint setAttr(void* p, uint mask) nothrow
+    {
+        return mask;
+    }
+
+    uint clrAttr(void* p, uint mask) nothrow
+    {
+        return mask;
+    }
+
+    void* malloc(size_t size, uint bits, const TypeInfo ti) nothrow
+    {
+        return sentinelAdd(.malloc(size + sentinelSize), size);
+    }
+
+    BlkInfo qalloc(size_t size, uint bits, const scope TypeInfo ti) nothrow
+    {
+        return BlkInfo(malloc(size, bits, ti), size);
+    }
+
+    void* calloc(size_t size, uint bits, const TypeInfo ti) nothrow
+    {
+        return sentinelAdd(.calloc(1, size + sentinelSize), size);
+    }
+
+    void* realloc(void* p, size_t size, uint bits, const TypeInfo ti) nothrow
+    {
+        return sentinelAdd(.realloc(p - sentinelSize, size + sentinelSize), size);
+    }
+
+    size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow
+    {
+        return 0;
+    }
+
+    size_t reserve(size_t size) nothrow
+    {
+        return 0;
+    }
+
+    void free(void* p) nothrow
+    {
+        free(p - sentinelSize);
+    }
+
+    void* addrOf(void* p) nothrow
+    {
+        return p;
+    }
+
+    size_t sizeOf(void* p) nothrow
+    {
+        return query(p).size;
+    }
+
+    BlkInfo query(void* p) nothrow
+    {
+        return p ? BlkInfo(p, sentinelGet(p)) : BlkInfo.init;
+    }
+
+    core.memory.GC.Stats stats() nothrow
+    {
+        return core.memory.GC.Stats.init;
+    }
+
+    core.memory.GC.ProfileStats profileStats() nothrow
+    {
+        return typeof(return).init;
+    }
+
+    void addRoot(void* p) nothrow @nogc
+    {
+    }
+
+    void removeRoot(void* p) nothrow @nogc
+    {
+    }
+
+    @property RootIterator rootIter() @nogc
+    {
+        return null;
+    }
+
+    void addRange(void* p, size_t sz, const TypeInfo ti) nothrow @nogc
+    {
+    }
+
+    void removeRange(void* p) nothrow @nogc
+    {
+    }
+
+    @property RangeIterator rangeIter() @nogc
+    {
+        return null;
+    }
+
+    void runFinalizers(const scope void[] segment) nothrow
+    {
+    }
+
+    bool inFinalizer() nothrow
+    {
+        return false;
+    }
+
+    ulong allocatedInCurrentThread() nothrow
+    {
+        return stats().allocatedInCurrentThread;
+    }
+
+private:
+    // doesn't care for alignment
+    static void* sentinelAdd(void* p, size_t value)
+    {
+        *cast(size_t*) p = value;
+        return p + sentinelSize;
+    }
+
+    static size_t sentinelGet(void* p)
+    {
+        return *cast(size_t*)(p - sentinelSize);
+    }
+
+    enum sentinelSize = size_t.sizeof;
+}
+
+void main()
+{
+    // test array append cache
+    char[] s;
+    foreach (char c; char.min .. char.max + 1)
+        s ~= c;
+}
diff --git a/libphobos/testsuite/libphobos.init_fini/test18996.d b/libphobos/testsuite/libphobos.init_fini/test18996.d
new file mode 100644
index 00000000000..01d514cd563
--- /dev/null
+++ b/libphobos/testsuite/libphobos.init_fini/test18996.d
@@ -0,0 +1,13 @@ 
+// Issue https://issues.dlang.org/show_bug.cgi?id=18996
+// Array!string calls removeRange without first adding the range, but never
+// initializes the GC. The behavior of the default GC is to ignore removing
+// ranges when the range wasn't added. The ProtoGC originally would crash when
+// this happened.
+
+import core.memory;
+
+void main()
+{
+    GC.removeRange(null);
+    GC.removeRoot(null);
+}
diff --git a/libphobos/testsuite/libphobos.lifetime/large_aggregate_destroy_21097.d b/libphobos/testsuite/libphobos.lifetime/large_aggregate_destroy_21097.d
new file mode 100644
index 00000000000..bc0695ed57d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.lifetime/large_aggregate_destroy_21097.d
@@ -0,0 +1,78 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=21097
+
+// The crucial part of the test cases is testing `destroy`. At the same time, we test
+// `core.internal.lifetime.emplaceInitializer` (which is currently called by `destroy`).
+
+enum SIZE = 10_000_000; // 10 MB should exhaust the stack on most if not all test systems.
+
+import core.internal.lifetime;
+
+void test_largestruct()
+{
+    static struct LargeStruct
+    {
+        int[SIZE/2] a1;
+        int b = 42;
+        int[SIZE/2] a2;
+    }
+    static LargeStruct s = void;
+    emplaceInitializer(s);
+    assert(s.b == 42);
+    s.b = 101;
+    destroy(s);
+    assert(s.b == 42);
+}
+
+void test_largestruct_w_opassign()
+{
+    static struct LargeStructOpAssign
+    {
+        int[SIZE/2] a1;
+        int b = 420;         // non-zero init
+        int[SIZE/2] a2;
+
+        void opAssign(typeof(this)) {} // hasElaborateAssign == true
+    }
+    static LargeStructOpAssign s = void;
+    emplaceInitializer(s);
+    assert(s.b == 420);
+    s.b = 101;
+    destroy(s);
+    assert(s.b == 420);
+}
+
+void test_largearray() {
+    static struct NonZero
+    {
+        int i = 123;
+    }
+    static NonZero[SIZE] s = void;
+    emplaceInitializer(s);
+    assert(s[SIZE/2] == NonZero.init);
+    s[10] = NonZero(101);
+    destroy(s);
+    assert(s[10] == NonZero.init);
+}
+
+void test_largearray_w_opassign() {
+    static struct NonZeroWithOpAssign
+    {
+        int i = 123;
+        void opAssign(typeof(this)) {} // hasElaborateAssign == true
+    }
+    static NonZeroWithOpAssign[SIZE] s = void;
+    emplaceInitializer(s);
+    assert(s[SIZE/2] == NonZeroWithOpAssign.init);
+    s[10] = NonZeroWithOpAssign(101);
+    destroy(s);
+    assert(s[10] == NonZeroWithOpAssign.init);
+}
+
+int main()
+{
+    test_largestruct();
+    test_largestruct_w_opassign();
+    test_largearray();
+    test_largearray_w_opassign();
+    return 0;
+}
diff --git a/libphobos/testsuite/libphobos.shared/host.c b/libphobos/testsuite/libphobos.shared/host.c
index 81e896aa3d8..395ad0c3f55 100644
--- a/libphobos/testsuite/libphobos.shared/host.c
+++ b/libphobos/testsuite/libphobos.shared/host.c
@@ -10,6 +10,11 @@  int main(int argc, char* argv[])
     void *druntime = dlopen(argv[1], RTLD_LAZY); // load druntime
     assert(druntime);
 #endif
+#if defined(__DragonFly__)
+    // workaround for Bugzilla 14824
+    void *druntime = dlopen(argv[1], RTLD_LAZY); // load druntime
+    assert(druntime);
+#endif
 
     const size_t pathlen = strrchr(argv[0], '/') - argv[0] + 1;
     char *name = malloc(pathlen + sizeof("plugin1.so"));
@@ -55,6 +60,9 @@  int main(int argc, char* argv[])
 
 #if defined(__FreeBSD__)
     dlclose(druntime);
+#endif
+#if defined(__DragonFly__)
+    dlclose(druntime);
 #endif
     return EXIT_SUCCESS;
 }
diff --git a/libphobos/testsuite/libphobos.shared/link_mod_collision.d b/libphobos/testsuite/libphobos.shared/link_mod_collision.d
deleted file mode 100644
index 9c3d1c7b235..00000000000
--- a/libphobos/testsuite/libphobos.shared/link_mod_collision.d
+++ /dev/null
@@ -1,5 +0,0 @@ 
-module lib; // module collides with lib.so
-
-void main()
-{
-}
diff --git a/libphobos/testsuite/libphobos.shared/load.d b/libphobos/testsuite/libphobos.shared/load.d
index 5a2dd01b778..0d3ffa65d6e 100644
--- a/libphobos/testsuite/libphobos.shared/load.d
+++ b/libphobos/testsuite/libphobos.shared/load.d
@@ -2,7 +2,6 @@  import core.runtime;
 import core.stdc.stdio;
 import core.stdc.string;
 import core.thread;
-
 import core.sys.posix.dlfcn;
 
 version (DragonFlyBSD) import core.sys.dragonflybsd.dlfcn : RTLD_NOLOAD;
diff --git a/libphobos/testsuite/libphobos.shared/load_13414.d b/libphobos/testsuite/libphobos.shared/load_13414.d
index f7cbf45190a..047d5079937 100644
--- a/libphobos/testsuite/libphobos.shared/load_13414.d
+++ b/libphobos/testsuite/libphobos.shared/load_13414.d
@@ -16,8 +16,17 @@  void runTest(string name)
     *cast(void function()*).dlsym(h, "_D9lib_1341420sharedStaticDtorHookOPFZv") = &sharedStaticDtorHook;
 
     Runtime.unloadLibrary(h);
-    assert(tlsDtor == 1);
-    assert(dtor == 1);
+    version (CRuntime_Musl)
+    {
+        // On Musl, unloadLibrary is a no-op because dlclose is a no-op
+        assert(tlsDtor == 0);
+        assert(dtor == 0);
+    }
+    else
+    {
+        assert(tlsDtor == 1);
+        assert(dtor == 1);
+    }
 }
 
 void main(string[] args)
diff --git a/libphobos/testsuite/libphobos.shared/load_mod_collision.d b/libphobos/testsuite/libphobos.shared/load_mod_collision.d
deleted file mode 100644
index 64243d4b7bb..00000000000
--- a/libphobos/testsuite/libphobos.shared/load_mod_collision.d
+++ /dev/null
@@ -1,14 +0,0 @@ 
-module lib; // module collides with lib.so
-
-import core.runtime;
-import core.stdc.stdio;
-import core.stdc.string;
-import core.sys.posix.dlfcn;
-
-void main(string[] args)
-{
-    auto name = args[0] ~ '\0';
-    const pathlen = strrchr(name.ptr, '/') - name.ptr + 1;
-    name = name[0 .. pathlen] ~ "lib.so";
-    auto lib = Runtime.loadLibrary(name);
-}
diff --git a/libphobos/testsuite/libphobos.thread/external_threads.d b/libphobos/testsuite/libphobos.thread/external_threads.d
new file mode 100644
index 00000000000..9c98a3fa13d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.thread/external_threads.d
@@ -0,0 +1,50 @@ 
+import core.sys.posix.pthread;
+import core.memory;
+import core.thread;
+
+extern (C) void  rt_moduleTlsCtor();
+extern (C) void  rt_moduleTlsDtor();
+
+extern(C)
+void* entry_point1(void*)
+{
+    // try collecting - GC must ignore this call because this thread
+    // is not registered in runtime
+    GC.collect();
+    return null;
+}
+
+extern(C)
+void* entry_point2(void*)
+{
+    // This thread gets registered in druntime, does some work and gets
+    // unregistered to be cleaned up manually
+    thread_attachThis();
+    rt_moduleTlsCtor();
+
+    auto x = new int[10];
+
+    rt_moduleTlsDtor();
+    thread_detachThis();
+    return null;
+}
+
+void main()
+{
+    // allocate some garbage
+    auto x = new int[1000];
+
+    {
+        pthread_t thread;
+        auto status = pthread_create(&thread, null, &entry_point1, null);
+        assert(status == 0);
+        pthread_join(thread, null);
+    }
+
+    {
+        pthread_t thread;
+        auto status = pthread_create(&thread, null, &entry_point2, null);
+        assert(status == 0);
+        pthread_join(thread, null);
+    }
+}
diff --git a/libphobos/testsuite/libphobos.thread/fiber_guard_page.d b/libphobos/testsuite/libphobos.thread/fiber_guard_page.d
index ca54a19857f..dbdd0f9d08d 100644
--- a/libphobos/testsuite/libphobos.thread/fiber_guard_page.d
+++ b/libphobos/testsuite/libphobos.thread/fiber_guard_page.d
@@ -4,6 +4,9 @@  import core.thread;
 import core.sys.posix.signal;
 import core.sys.posix.sys.mman;
 
+version (LDC) import ldc.attributes;
+else struct optStrategy { string a; }
+
 // this should be true for most architectures
 // (taken from core.thread)
 version (GNU_StackGrowsDown)
@@ -12,6 +15,7 @@  version (GNU_StackGrowsDown)
 enum stackSize = MINSIGSTKSZ;
 
 // Simple method that causes a stack overflow
+@optStrategy("none")
 void stackMethod()
 {
     // Over the stack size, so it overflows the stack
diff --git a/libphobos/testsuite/libphobos.thread/join_detach.d b/libphobos/testsuite/libphobos.thread/join_detach.d
new file mode 100644
index 00000000000..f1515190171
--- /dev/null
+++ b/libphobos/testsuite/libphobos.thread/join_detach.d
@@ -0,0 +1,20 @@ 
+import core.thread;
+import core.sync.semaphore;
+
+__gshared Semaphore sem;
+
+void thread_main ()
+{
+    sem.notify();
+}
+
+void main()
+{
+    auto th = new Thread(&thread_main);
+    sem = new Semaphore();
+    th.start();
+    sem.wait();
+    while (th.isRunning()) {}
+    destroy(th); // force detach
+    th.join();
+}
diff --git a/libphobos/testsuite/libphobos.thread/test_import.d b/libphobos/testsuite/libphobos.thread/test_import.d
new file mode 100644
index 00000000000..dfa0487d916
--- /dev/null
+++ b/libphobos/testsuite/libphobos.thread/test_import.d
@@ -0,0 +1,7 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=20447
+void main()
+{
+    import core.thread;
+    int[] x;
+    auto b = x.dup;
+}
diff --git a/libphobos/testsuite/libphobos.thread/tlsgc_sections.d b/libphobos/testsuite/libphobos.thread/tlsgc_sections.d
index 1421d926a38..1bd3f26cffc 100644
--- a/libphobos/testsuite/libphobos.thread/tlsgc_sections.d
+++ b/libphobos/testsuite/libphobos.thread/tlsgc_sections.d
@@ -1,39 +1,70 @@ 
-final class Class
+import core.memory;
+import core.sync.condition;
+import core.sync.mutex;
+import core.thread;
+
+__gshared Condition g_cond;
+__gshared Mutex g_mutex;
+__gshared int g_step = 0;
+
+class C
 {
-    // This gets triggered although the instance always stays referenced.
     ~this()
     {
         import core.stdc.stdlib;
-        abort();
+        abort();    // this gets triggered although the instance always stays referenced
     }
 }
 
-Class obj;
+C c;
 
 static this()
 {
-    obj = new Class;
+    c = new C;
 }
 
 static ~this()
 {
-    // Free without destruction to avoid triggering abort()
     import core.memory;
-    GC.free(cast(void*)obj);
+    GC.free(cast(void*)c); // free without destruction to avoid triggering abort()
 }
 
-void doit()
+void test()
 {
-    foreach (i; 0 .. 10_000)
-        new ubyte[](100_000);
+    assert(c !is null);
+
+    // notify the main thread of the finished initialization
+    synchronized (g_mutex) g_step = 1;
+    g_cond.notifyAll();
+
+    // wait until the GC collection is done
+    synchronized (g_mutex) {
+        while (g_step != 2)
+            g_cond.wait();
+    }
 }
 
+
 void main()
 {
-    import core.thread;
-    auto t = new Thread(&doit);
-    t.start();
+    g_mutex = new Mutex;
+    g_cond = new Condition(g_mutex);
+
+    auto th = new Thread(&test);
+    th.start();
+
+    // wait for thread to be fully initialized
+    synchronized (g_mutex) {
+        while (g_step != 1)
+            g_cond.wait();
+    }
+
+    // this causes the other thread's C instance to be reaped with the bug present
+    GC.collect();
+
+    // allow the thread to shut down
+    synchronized (g_mutex) g_step = 2;
+    g_cond.notifyAll();
 
-    // This triggers the GC that frees the still referenced Class instance.
-    doit();
+    th.join();
 }
diff --git a/libphobos/testsuite/libphobos.thread/tlsstack.d b/libphobos/testsuite/libphobos.thread/tlsstack.d
new file mode 100644
index 00000000000..dbd93213bfe
--- /dev/null
+++ b/libphobos/testsuite/libphobos.thread/tlsstack.d
@@ -0,0 +1,38 @@ 
+module core.thread.test; // needs access to getStackTop()/getStackBottom()
+
+import core.stdc.stdio;
+import core.thread;
+
+ubyte[16384] data;
+
+void showThreadInfo() nothrow
+{
+    try
+    {
+        auto top = getStackTop();
+        auto bottom = getStackBottom();
+        printf("tlsdata:     %p\n", data.ptr);
+        printf("stack top:   %p\n", getStackTop());
+        printf("stack bottom:%p\n", getStackBottom());
+        printf("used stack:  %lld\n", cast(ulong)(bottom - top));
+    }
+    catch(Exception e)
+    {
+        assert(false, e.msg);
+    }
+}
+
+void main()
+{
+    printf("### main\n");
+    showThreadInfo();
+
+    printf("### thread\n");
+    auto th = new Thread(&showThreadInfo, 16384);
+    th.start();
+    th.join();
+
+    printf("### lowlevel thread\n");
+    auto llth = createLowLevelThread(() { showThreadInfo(); });
+    joinLowLevelThread(llth);
+}
diff --git a/libphobos/testsuite/libphobos.typeinfo/enum_.d b/libphobos/testsuite/libphobos.typeinfo/enum_.d
new file mode 100644
index 00000000000..58cfbe34d60
--- /dev/null
+++ b/libphobos/testsuite/libphobos.typeinfo/enum_.d
@@ -0,0 +1,21 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=21441
+
+int dtorCount;
+int postblitCount;
+
+struct S
+{
+    this(this) { ++postblitCount; }
+    ~this() { ++dtorCount; }
+}
+
+enum E : S { _ = S.init }
+
+void main()
+{
+    E e;
+    typeid(e).destroy(&e);
+    assert(dtorCount == 1);
+    typeid(e).postblit(&e);
+    assert(postblitCount == 1);
+}
diff --git a/libphobos/testsuite/libphobos.typeinfo/isbaseof.d b/libphobos/testsuite/libphobos.typeinfo/isbaseof.d
new file mode 100644
index 00000000000..26dd5a66e3e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.typeinfo/isbaseof.d
@@ -0,0 +1,46 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=20178
+
+interface I {}
+interface J : I {}
+interface K(T) {}
+class C1 : I {}
+class C2 : C1 {}
+class C3 : J {}
+class C4(T) : C3, K!T {}
+class C5(T) : C4!T {}
+
+void main() @nogc nothrow pure @safe
+{
+    assert(typeid(C1).isBaseOf(typeid(C1)));
+    assert(typeid(C1).isBaseOf(typeid(C2)));
+
+    assert(!typeid(C2).isBaseOf(typeid(C1)));
+    assert(typeid(C2).isBaseOf(typeid(C2)));
+
+    assert(!typeid(C1).isBaseOf(typeid(Object)));
+    assert(!typeid(C2).isBaseOf(typeid(Object)));
+    assert(typeid(Object).isBaseOf(typeid(C1)));
+    assert(typeid(Object).isBaseOf(typeid(C2)));
+
+    assert(typeid(I).isBaseOf(typeid(I)));
+    assert(typeid(I).isBaseOf(typeid(J)));
+    assert(typeid(I).isBaseOf(typeid(C1)));
+    assert(typeid(I).isBaseOf(typeid(C2)));
+    assert(typeid(I).isBaseOf(typeid(C3)));
+    assert(!typeid(I).isBaseOf(typeid(Object)));
+
+    assert(!typeid(J).isBaseOf(typeid(I)));
+    assert(typeid(J).isBaseOf(typeid(J)));
+    assert(!typeid(J).isBaseOf(typeid(C1)));
+    assert(!typeid(J).isBaseOf(typeid(C2)));
+    assert(typeid(J).isBaseOf(typeid(C3)));
+    assert(!typeid(J).isBaseOf(typeid(Object)));
+
+    assert(typeid(C4!int).isBaseOf(typeid(C5!int)));
+    assert(typeid(K!int).isBaseOf(typeid(C5!int)));
+    assert(!typeid(C4!Object).isBaseOf(typeid(C5!int)));
+    assert(!typeid(K!Object).isBaseOf(typeid(C5!int)));
+
+    static assert(!__traits(compiles, TypeInfo.init.isBaseOf(typeid(C1))));
+    static assert(!__traits(compiles, typeid(C1).isBaseOf(TypeInfo.init)));
+}
diff --git a/libphobos/testsuite/libphobos.unittest/customhandler.d b/libphobos/testsuite/libphobos.unittest/customhandler.d
new file mode 100644
index 00000000000..f5a04350d9c
--- /dev/null
+++ b/libphobos/testsuite/libphobos.unittest/customhandler.d
@@ -0,0 +1,21 @@ 
+import core.runtime;
+
+UnitTestResult customModuleUnitTester()
+{
+    version(GoodTests) return UnitTestResult(100, 100, false, true);
+    version(FailedTests) return UnitTestResult(100, 0, false, true);
+    version(NoTests) return UnitTestResult(0, 0, true, false);
+    version(FailNoPrintout) return UnitTestResult(100, 0, false, false);
+    version(PassNoPrintout) return UnitTestResult(100, 100, false, false);
+}
+
+shared static this()
+{
+    Runtime.extendedModuleUnitTester = &customModuleUnitTester;
+}
+
+void main()
+{
+    import core.stdc.stdio;
+    fprintf(stderr, "main\n");
+}