@@ -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()
{
new file mode 100644
@@ -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();
+}
new file mode 100644
@@ -0,0 +1,10 @@
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18828
+
+struct S18828 { }
+
+extern(C) void main()
+{
+ S18828 s;
+ destroy(s);
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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[];
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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");
+}
new file mode 100644
@@ -0,0 +1,5 @@
+void main (string[] args)
+{
+ assert(args.length == 5);
+ assert(args[1 .. $] == [ "foo", "bar", "--", "--DRT-gcopts=profile:1" ]);
+}
new file mode 100644
@@ -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();
new file mode 100644
@@ -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");
+}
new file mode 100644
@@ -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");
+}
new file mode 100644
@@ -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);
+ }
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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");
+}
new file mode 100644
@@ -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();
+}
@@ -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();
}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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");
+ }
+}
new file mode 100644
@@ -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();
+}
new file mode 100644
@@ -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
new file mode 100644
@@ -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);
+ }
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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);
+ }
+}
new file mode 100644
@@ -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();
+}
@@ -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);
new file mode 100644
@@ -0,0 +1,4 @@
+// { dg-options "-fversion=Shared" }
+// { dg-do compile }
+import core.runtime;
+import core.thread;
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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;
+}
@@ -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;
}
deleted file mode 100644
@@ -1,5 +0,0 @@
-module lib; // module collides with lib.so
-
-void main()
-{
-}
@@ -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;
@@ -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)
deleted file mode 100644
@@ -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);
-}
new file mode 100644
@@ -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);
+ }
+}
@@ -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
new file mode 100644
@@ -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();
+}
new file mode 100644
@@ -0,0 +1,7 @@
+// https://issues.dlang.org/show_bug.cgi?id=20447
+void main()
+{
+ import core.thread;
+ int[] x;
+ auto b = x.dup;
+}
@@ -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();
}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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)));
+}
new file mode 100644
@@ -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");
+}