From 1a76c5becfa9cfb69d1bee015460f4c95af62d8d Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Mon, 27 Jul 2020 23:12:09 -0400 Subject: [PATCH 01/19] Templated TypeInfo for int --- src/object.d | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/object.d b/src/object.d index 308cb87ae1..7acbdaa528 100644 --- a/src/object.d +++ b/src/object.d @@ -424,6 +424,93 @@ class TypeInfo @property immutable(void)* rtInfo() nothrow pure const @safe @nogc { return rtinfoHasPointers; } // better safe than sorry } +/* +Run-time type information for scalar types (int for now). +*/ +template RTTypeid(T) +if (is(T == int)) +{ + class Impl : TypeInfo + { + const: nothrow: pure: @safe: + + override bool opEquals(const Object rhs) @nogc + { + return this is rhs + // Instance may be duplicated across DLLs, but has the same type. + || cast(const Impl) rhs !is null; + } + + override int opCmp(const Object rhs) + { + if (this is rhs) + return 0; + if (auto ti = cast(const TypeInfo) rhs) + return __cmp(this.toString, ti.toString); + return 1; + } + + override string toString() const pure nothrow @safe { return T.stringof; } + + override size_t getHash(scope const void* p) @nogc @trusted + { + return *cast(const T *)p; + } + + override bool equals(in void* p1, in void* p2) @nogc @trusted + { + return *cast(const T *)p1 == *cast(const T *)p2; + } + + override int compare(in void* p1, in void* p2) @nogc @trusted + { + auto lhs = *cast(const T*) p1, rhs = *cast(const T*) p2; + return int(lhs > rhs) - int(lhs < rhs); + } + + override @property size_t tsize() @nogc + { + return T.sizeof; + } + + override const(void)[] initializer() @trusted @nogc + { + static immutable T data; + return (cast(const void *)&data)[0 .. T.sizeof]; + } + + override void swap(void *p1, void *p2) @nogc @trusted + { + T t = *cast(T *)p1; + *cast(T *)p1 = *cast(T *)p2; + *cast(T *)p2 = t; + } + + override @property immutable(void)* rtInfo() { return rtinfoNoPointers; } + } + + // On-demand singleton object in static storage + immutable RTTypeid = new Impl; +} + +unittest +{ + alias id = RTTypeid!int; + static assert(id == id && id <= id && id >= id); + static assert(id.toString == "int"); + int a = 42, b = 42, c = 43; + assert(id.getHash(&a) == 42); + assert(id.equals(&a, &b)); + assert(!id.equals(&a, &c)); + assert(id.compare(&a, &b) == 0); + assert(id.compare(&a, &c) == -1); + assert(id.compare(&c, &a) == 1); + static assert(id.tsize == 4); + assert(cast(ubyte[]) id.initializer() == [ 0, 0, 0, 0 ]); + id.swap(&a, &c); + assert(a == 43 && c == 42); +} + class TypeInfo_Enum : TypeInfo { override string toString() const { return name; } From fc0de5b1b49fcc00ac3f830bd66450c10320d451 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Mon, 27 Jul 2020 23:17:35 -0400 Subject: [PATCH 02/19] Change name from RTTypeid to __typeid --- src/object.d | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/object.d b/src/object.d index 7acbdaa528..74fb880429 100644 --- a/src/object.d +++ b/src/object.d @@ -427,7 +427,7 @@ class TypeInfo /* Run-time type information for scalar types (int for now). */ -template RTTypeid(T) +template __typeid(T) if (is(T == int)) { class Impl : TypeInfo @@ -450,11 +450,11 @@ if (is(T == int)) return 1; } - override string toString() const pure nothrow @safe { return T.stringof; } + override string toString() { return T.stringof; } override size_t getHash(scope const void* p) @nogc @trusted { - return *cast(const T *)p; + return *cast(const T *) p; } override bool equals(in void* p1, in void* p2) @nogc @trusted @@ -490,12 +490,12 @@ if (is(T == int)) } // On-demand singleton object in static storage - immutable RTTypeid = new Impl; + immutable __typeid = new Impl; } unittest { - alias id = RTTypeid!int; + alias id = __typeid!int; static assert(id == id && id <= id && id >= id); static assert(id.toString == "int"); int a = 42, b = 42, c = 43; From 933a5711417adfad5a39f0d7fd70ce54a2d4a1e8 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 28 Jul 2020 09:38:33 -0400 Subject: [PATCH 03/19] Add statically-informed versions of override functions --- src/object.d | 81 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/src/object.d b/src/object.d index 74fb880429..1df6b65f17 100644 --- a/src/object.d +++ b/src/object.d @@ -430,7 +430,10 @@ Run-time type information for scalar types (int for now). template __typeid(T) if (is(T == int)) { - class Impl : TypeInfo + // On-demand singleton object in static storage + immutable __typeid = new Impl; + + private class Impl : TypeInfo { const: nothrow: pure: @safe: @@ -452,20 +455,43 @@ if (is(T == int)) override string toString() { return T.stringof; } + final static size_t getHash(scope const T* p) @nogc @trusted + { + pragma(inline, true); + return *p; + } + override size_t getHash(scope const void* p) @nogc @trusted { - return *cast(const T *) p; + return getHash(cast(const T *) p); + } + + final static bool equals(in T* p1, in T* p2) @nogc @trusted + { + pragma(inline, true); + return *p1 == *p2; } override bool equals(in void* p1, in void* p2) @nogc @trusted { - return *cast(const T *)p1 == *cast(const T *)p2; + return equals(cast(const T *)p1, cast(const T *)p2); + } + + final static int compare(in T* lhs, in T* rhs) @nogc @trusted + { + pragma(inline, true); + return int(*lhs > *rhs) - int(*lhs < *rhs); } override int compare(in void* p1, in void* p2) @nogc @trusted { - auto lhs = *cast(const T*) p1, rhs = *cast(const T*) p2; - return int(lhs > rhs) - int(lhs < rhs); + return compare(cast(const T*) p1, cast(const T*) p2); + } + + final static @property size_t tsize() @nogc + { + pragma(inline, true); + return T.sizeof; } override @property size_t tsize() @nogc @@ -473,42 +499,69 @@ if (is(T == int)) return T.sizeof; } + private static immutable T data; + + final static immutable(T)[] initializer() @trusted @nogc + { + pragma(inline, true); + return (&data)[0 .. 1]; + } + override const(void)[] initializer() @trusted @nogc { - static immutable T data; - return (cast(const void *)&data)[0 .. T.sizeof]; + return (&data)[0 .. 1]; + } + + final static void swap(T *p1, T *p2) @nogc @trusted + { + pragma(inline, true); + T t = *p1; + *p1 = *p2; + *p2 = t; } override void swap(void *p1, void *p2) @nogc @trusted { - T t = *cast(T *)p1; - *cast(T *)p1 = *cast(T *)p2; - *cast(T *)p2 = t; + return swap(cast(T *) p1, cast(T *) p2); } - override @property immutable(void)* rtInfo() { return rtinfoNoPointers; } - } + final static @property immutable(void)* rtInfo() @nogc + { + pragma(inline, true); + return rtinfoNoPointers; + } - // On-demand singleton object in static storage - immutable __typeid = new Impl; + override @property immutable(void)* rtInfo() @nogc { return rtinfoNoPointers; } + } } unittest { alias id = __typeid!int; + immutable TypeInfo id2 = id; // Implicitly convert to base, losing static type information static assert(id == id && id <= id && id >= id); static assert(id.toString == "int"); int a = 42, b = 42, c = 43; assert(id.getHash(&a) == 42); + assert(id2.getHash(&a) == 42); assert(id.equals(&a, &b)); + assert(id2.equals(&a, &b)); assert(!id.equals(&a, &c)); + assert(!id2.equals(&a, &c)); assert(id.compare(&a, &b) == 0); + assert(id2.compare(&a, &b) == 0); assert(id.compare(&a, &c) == -1); + assert(id2.compare(&a, &c) == -1); assert(id.compare(&c, &a) == 1); + assert(id2.compare(&c, &a) == 1); static assert(id.tsize == 4); + assert(id2.tsize == 4); assert(cast(ubyte[]) id.initializer() == [ 0, 0, 0, 0 ]); + assert(cast(ubyte[]) id2.initializer() == [ 0, 0, 0, 0 ]); id.swap(&a, &c); assert(a == 43 && c == 42); + id2.swap(&a, &c); + assert(a == 42 && c == 43); } class TypeInfo_Enum : TypeInfo From 9328c8675d3187bcef2084f0b3973140e02fda7e Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 28 Jul 2020 10:17:28 -0400 Subject: [PATCH 04/19] immutable is better than const --- src/object.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/object.d b/src/object.d index 1df6b65f17..d456ae5714 100644 --- a/src/object.d +++ b/src/object.d @@ -507,7 +507,7 @@ if (is(T == int)) return (&data)[0 .. 1]; } - override const(void)[] initializer() @trusted @nogc + override immutable(void)[] initializer() @trusted @nogc { return (&data)[0 .. 1]; } From 34ad3682e1264e732042b98e045d42952a2a5254 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 28 Jul 2020 10:21:45 -0400 Subject: [PATCH 05/19] More tests --- src/object.d | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/object.d b/src/object.d index d456ae5714..3c8d2851a7 100644 --- a/src/object.d +++ b/src/object.d @@ -540,7 +540,10 @@ unittest alias id = __typeid!int; immutable TypeInfo id2 = id; // Implicitly convert to base, losing static type information static assert(id == id && id <= id && id >= id); + static assert(id2 == id && id2 <= id && id2 >= id); + static assert(id == id2 && id <= id2 && id >= id2); static assert(id.toString == "int"); + static assert(id2.toString == "int"); int a = 42, b = 42, c = 43; assert(id.getHash(&a) == 42); assert(id2.getHash(&a) == 42); From 79f42c7826d37cc7dfc01e849b06e619fea4ff3e Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 28 Jul 2020 10:34:18 -0400 Subject: [PATCH 06/19] overloads for opEquals and opCmp. Also make toString @nogc --- src/object.d | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/object.d b/src/object.d index 3c8d2851a7..eb9d2f148a 100644 --- a/src/object.d +++ b/src/object.d @@ -437,6 +437,9 @@ if (is(T == int)) { const: nothrow: pure: @safe: + // On the off chance someone calls typeid(T) == typeid(U) with T == U + final bool opEquals(const Impl rhs) @nogc { pragma(inline, true); return true; } + override bool opEquals(const Object rhs) @nogc { return this is rhs @@ -444,6 +447,9 @@ if (is(T == int)) || cast(const Impl) rhs !is null; } + // On the off chance someone calls typeid(T) < typeid(U) etc. with T == U + final int opCmp(const Impl rhs) @nogc { pragma(inline, true); return 0; } + override int opCmp(const Object rhs) { if (this is rhs) @@ -453,7 +459,7 @@ if (is(T == int)) return 1; } - override string toString() { return T.stringof; } + override string toString() @nogc { return T.stringof; } final static size_t getHash(scope const T* p) @nogc @trusted { From fe5d07f61634a8a7ab697f600d7ee1fcacd9822d Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 28 Jul 2020 10:52:29 -0400 Subject: [PATCH 07/19] Better hash function --- src/object.d | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/object.d b/src/object.d index eb9d2f148a..7d8ae16fa1 100644 --- a/src/object.d +++ b/src/object.d @@ -464,7 +464,8 @@ if (is(T == int)) final static size_t getHash(scope const T* p) @nogc @trusted { pragma(inline, true); - return *p; + // Knuth's multiplicative hash with the golden ratio of 2^64 + return *p * 11400714819323198485UL; } override size_t getHash(scope const void* p) @nogc @trusted @@ -551,8 +552,8 @@ unittest static assert(id.toString == "int"); static assert(id2.toString == "int"); int a = 42, b = 42, c = 43; - assert(id.getHash(&a) == 42); - assert(id2.getHash(&a) == 42); + assert(id.getHash(&a) == 17661420568835545970UL); + assert(id2.getHash(&a) == 17661420568835545970UL); assert(id.equals(&a, &b)); assert(id2.equals(&a, &b)); assert(!id.equals(&a, &c)); From 816cc09f79b46b6cc5ee4e328581e76a991a437a Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 28 Jul 2020 11:22:38 -0400 Subject: [PATCH 08/19] Hash on 32-bit platforms --- src/object.d | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/object.d b/src/object.d index 7d8ae16fa1..a635a5008e 100644 --- a/src/object.d +++ b/src/object.d @@ -464,8 +464,11 @@ if (is(T == int)) final static size_t getHash(scope const T* p) @nogc @trusted { pragma(inline, true); - // Knuth's multiplicative hash with the golden ratio of 2^64 - return *p * 11400714819323198485UL; + // Knuth's multiplicative hash with the golden ratio of 2^32 or 2^64 + static if (size_t.sizeof == 4) + return *p * 2654435761U; + else + return *p * 11400714819323198485UL; } override size_t getHash(scope const void* p) @nogc @trusted @@ -552,8 +555,12 @@ unittest static assert(id.toString == "int"); static assert(id2.toString == "int"); int a = 42, b = 42, c = 43; - assert(id.getHash(&a) == 17661420568835545970UL); - assert(id2.getHash(&a) == 17661420568835545970UL); + static if (size_t.sizeof == 4) + enum size_t h = 4112119562; + else + enum size_t h = 17661420568835545970UL; + assert(id.getHash(&a) == h); + assert(id2.getHash(&a) == h); assert(id.equals(&a, &b)); assert(id2.equals(&a, &b)); assert(!id.equals(&a, &c)); From 9ac16472b106222030b727ac0ecad5059f67dd4d Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 28 Jul 2020 11:50:51 -0400 Subject: [PATCH 09/19] Add a Type definition to statically access the type name. --- src/object.d | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/object.d b/src/object.d index a635a5008e..6bd49b0944 100644 --- a/src/object.d +++ b/src/object.d @@ -437,6 +437,9 @@ if (is(T == int)) { const: nothrow: pure: @safe: + // Accessible in statically-typed contexts. + alias Type = T; + // On the off chance someone calls typeid(T) == typeid(U) with T == U final bool opEquals(const Impl rhs) @nogc { pragma(inline, true); return true; } @@ -548,6 +551,7 @@ if (is(T == int)) unittest { alias id = __typeid!int; + static assert(is(id.Type == int)); immutable TypeInfo id2 = id; // Implicitly convert to base, losing static type information static assert(id == id && id <= id && id >= id); static assert(id2 == id && id2 <= id && id2 >= id); From 7333714dfbc29ffd569b9ec6eaf70bbf5075d94f Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Fri, 31 Jul 2020 18:05:04 -0400 Subject: [PATCH 10/19] Extemd __typeid to more types --- posix.mak | 3 +- src/core/internal/dassert.d | 7 +- src/object.d | 452 +++++++++++++++++++++++++++--------- src/rt/util/typeinfo.d | 232 +++++++++++++++++- 4 files changed, 570 insertions(+), 124 deletions(-) diff --git a/posix.mak b/posix.mak index 335625aea4..9c1ee2d20a 100644 --- a/posix.mak +++ b/posix.mak @@ -276,11 +276,10 @@ endif .PHONY : unittest ifeq (1,$(BUILD_WAS_SPECIFIED)) unittest : $(UT_MODULES) $(addsuffix /.run,$(ADDITIONAL_TESTS)) - @echo done else unittest : unittest-debug unittest-release unittest-%: target - $(MAKE) -f $(MAKEFILE) unittest OS=$(OS) MODEL=$(MODEL) DMD=$(DMD) BUILD=$* + $(MAKE) -s -f $(MAKEFILE) unittest OS=$(OS) MODEL=$(MODEL) DMD=$(DMD) BUILD=$* endif ifeq ($(OS),linux) diff --git a/src/core/internal/dassert.d b/src/core/internal/dassert.d index e019677389..998cfe48cc 100644 --- a/src/core/internal/dassert.d +++ b/src/core/internal/dassert.d @@ -84,7 +84,12 @@ private string miniFormat(V)(const scope ref V v) import core.stdc.stdio : sprintf; import core.stdc.string : strlen; - static if (is(V == shared T, T)) + static if (is(V E == enum)) + { + const E base = v; + return miniFormat(base); + } + else static if (is(V == shared T, T)) { // Use atomics to avoid race conditions whenever possible static if (__traits(compiles, atomicLoad(v))) diff --git a/src/object.d b/src/object.d index 6bd49b0944..d0cda92417 100644 --- a/src/object.d +++ b/src/object.d @@ -413,7 +413,7 @@ class TypeInfo /** Return internal info on arguments fitting into 8byte. * See X86-64 ABI 3.2.3 */ - version (WithArgTypes) int argTypes(out TypeInfo arg1, out TypeInfo arg2) @safe nothrow + version (WithArgTypes) int argTypes(out TypeInfo arg1, out TypeInfo arg2) pure @safe nothrow @nogc { arg1 = this; return 0; @@ -425,164 +425,398 @@ class TypeInfo } /* -Run-time type information for scalar types (int for now). +Generic implementation of TypeInfo. T must be unqualified. */ -template __typeid(T) -if (is(T == int)) +private class TypeInfoImpl(T) : TypeInfo { - // On-demand singleton object in static storage - immutable __typeid = new Impl; + version (WithArgTypes) static if (is(T U1 == enum)) + override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted + { + return (cast() __typeid!U1).argTypes(arg1, arg2); + } - private class Impl : TypeInfo - { - const: nothrow: pure: @safe: + const: nothrow: pure: @safe: - // Accessible in statically-typed contexts. - alias Type = T; + import core.internal.traits : Unqual; + alias UnqualifiedType = Unqual!T; - // On the off chance someone calls typeid(T) == typeid(U) with T == U - final bool opEquals(const Impl rhs) @nogc { pragma(inline, true); return true; } + // Accessible in statically-typed contexts. + alias Type = T; - override bool opEquals(const Object rhs) @nogc - { - return this is rhs + // On the off chance someone calls typeid(T) == typeid(U) with T == U + final bool opEquals(const TypeInfoImpl rhs) @nogc { pragma(inline, true); return true; } + + // Is this equal to another TypeInfo? + // WARNING: relies on a 1:1 mapping Type : TypeInfoImpl!Type + override bool opEquals(const Object rhs) @nogc + { + return this is rhs + // Instance may be duplicated across DLLs, but has the same type. + || cast(const TypeInfoImpl) rhs !is null; + } + + // On the off chance someone calls typeid(T) < typeid(U) etc. with T == U + final int opCmp(const TypeInfoImpl rhs) @nogc { pragma(inline, true); return 0; } + + // Is this less than/equal/greater than another TypeInfo? + // WARNING: relies on a 1:1 mapping Type : TypeInfoImpl!Type + override int opCmp(const Object rhs) + { + if (this is rhs // Instance may be duplicated across DLLs, but has the same type. - || cast(const Impl) rhs !is null; - } + || cast(const TypeInfoImpl) rhs !is null) + return 0; + if (auto ti = cast(const TypeInfo) rhs) + return __cmp(this.toString, ti.toString); + // TypeInfo objects are all "greater" than all other types. + // TODO: devise a principled way to define/justify this. + return 1; + } - // On the off chance someone calls typeid(T) < typeid(U) etc. with T == U - final int opCmp(const Impl rhs) @nogc { pragma(inline, true); return 0; } + override string toString() @nogc { return T.stringof; } - override int opCmp(const Object rhs) + final static size_t getHash(scope const T* p) @nogc @trusted + { + pragma(inline, true); + static if (is(T U == enum)) { - if (this is rhs) - return 0; - if (auto ti = cast(const TypeInfo) rhs) - return __cmp(this.toString, ti.toString); - return 1; + return __typeid!U.getHash(cast(const U*) p); } - - override string toString() @nogc { return T.stringof; } - - final static size_t getHash(scope const T* p) @nogc @trusted + else static if (is(T == U[], U)) + { + size_t result = 0; + foreach (ref e; *p) + result = hashOf(e, result); + return result; + } + else static if (__traits(isIntegral, T)) { - pragma(inline, true); - // Knuth's multiplicative hash with the golden ratio of 2^32 or 2^64 - static if (size_t.sizeof == 4) - return *p * 2654435761U; + static if (is(T : bool)) + { + return *p; + } else - return *p * 11400714819323198485UL; + { + // Knuth's multiplicative hash with the golden ratio of 2^32 or 2^64 + static if (size_t.sizeof == 4) + return *p * 2_654_435_761U; + else + return *p * 11_400_714_819_323_198_485UL; + } } - - override size_t getHash(scope const void* p) @nogc @trusted + else static if (__traits(isFloating, T)) { - return getHash(cast(const T *) p); + import rt.util.typeinfo; + return Floating!UnqualifiedType.hashOf(*p); } - - final static bool equals(in T* p1, in T* p2) @nogc @trusted + else static if (is(T == U*, U)) { - pragma(inline, true); - return *p1 == *p2; + return __typeid!size_t.getHash(cast(const size_t*) p); } - - override bool equals(in void* p1, in void* p2) @nogc @trusted + else static if (is(T == U[n], U, size_t n)) { - return equals(cast(const T *)p1, cast(const T *)p2); + const U[] t = (*p)[]; + return __typeid!(U[]).getHash(&t); } - - final static int compare(in T* lhs, in T* rhs) @nogc @trusted + else { - pragma(inline, true); - return int(*lhs > *rhs) - int(*lhs < *rhs); + static assert(0, T.stringof); } + } - override int compare(in void* p1, in void* p2) @nogc @trusted + override size_t getHash(scope const void* p) @nogc @trusted + { + return getHash(cast(const T *) p); + } + + final static bool equals(in T* p1, in T* p2) @nogc @trusted + { + pragma(inline, true); + return *p1 == *p2; + } + + override bool equals(in void* p1, in void* p2) @nogc @trusted + { + return equals(cast(const T*)p1, cast(const T*)p2); + } + + final static int compare(in T* lhs, in T* rhs) @nogc @trusted + { + pragma(inline, true); + static if (is(T U == enum)) { - return compare(cast(const T*) p1, cast(const T*) p2); + return __typeid!U.compare(cast(const U*) lhs, cast(const U*) rhs); } - - final static @property size_t tsize() @nogc + else static if (__traits(isFloating, T)) { - pragma(inline, true); - return T.sizeof; + import rt.util.typeinfo; + return Floating!UnqualifiedType.compare(*lhs, *rhs); } - - override @property size_t tsize() @nogc + else static if (is(typeof(__cmp(*lhs, *rhs)) == int)) { - return T.sizeof; + // Avoid comparing twice for arrays + return __cmp(*lhs, *rhs); } + else + { + return int(*lhs > *rhs) - int(*lhs < *rhs); + } + } - private static immutable T data; + override int compare(in void* p1, in void* p2) @nogc @trusted + { + return compare(cast(const T*) p1, cast(const T*) p2); + } - final static immutable(T)[] initializer() @trusted @nogc + final static @property size_t tsize() @nogc + { + pragma(inline, true); + return T.sizeof; + } + + override @property size_t tsize() @nogc + { + return T.sizeof; + } + + final static @property size_t talign() @nogc + { + pragma(inline, true); + return T.alignof; + } + + override @property size_t talign() @nogc { return T.alignof; } + + private final static auto initializerImpl() @trusted @nogc + { + pragma(inline, true); + static if (is(T == U[n], U, size_t n)) { - pragma(inline, true); - return (&data)[0 .. 1]; + // Static arrays only return the element initializer + return __typeid!U.initializerImpl; } - - override immutable(void)[] initializer() @trusted @nogc + else static if (__traits(isZeroInit, T)) { - return (&data)[0 .. 1]; + // BUG - this won't work: + // return (cast(immutable T*) null)[0 .. 1]; + immutable(T)[] result = (cast(immutable T*) null)[0 .. 1]; + return result; } + else + { + static immutable T data; + immutable(T)[] result = (&data)[0 .. 1]; + return result; + } + } - final static void swap(T *p1, T *p2) @nogc @trusted + alias initializer = initializerImpl; + + override immutable(void)[] initializer() @trusted @nogc + { + return initializerImpl; + } + + final static void swap(UnqualifiedType *p1, UnqualifiedType *p2) @nogc @trusted + { + pragma(inline, true); + static if (is(UnqualifiedType == U[n], U, size_t n)) + { + // Avoid creating a large temporary on the stack. + foreach (i, ref e; *p1) + { + auto t = e; + e = (*p2)[i]; + (*p2)[i] = t; + } + } + else { - pragma(inline, true); - T t = *p1; + auto t = *p1; *p1 = *p2; *p2 = t; } + } + + override void swap(void *p1, void *p2) @nogc @trusted + { + return swap(cast(UnqualifiedType *) p1, cast(UnqualifiedType *) p2); + } - override void swap(void *p1, void *p2) @nogc @trusted + final static @property auto rtInfo() @nogc + { + pragma(inline, true); + return RTInfo!T; + } + + override @property immutable(void)* rtInfo() @nogc + { + return RTInfo!T; + } + + static if (is(T == U*, U)) + { + private alias NextType = U; + private enum bool hasPointers = true; + } + else static if (is(T == U[], U)) + { + private alias NextType = U; + private enum bool hasPointers = true; + } + else static if (is(T U == enum)) + { + private alias NextType = U; + private enum bool hasPointers = __typeid!U.hasPointers; + } + else + { + private alias NextType = T; + private enum bool hasPointers = false; + } + + static if (!is(T == NextType)) + override @property inout(TypeInfo) next() nothrow pure inout { - return swap(cast(T *) p1, cast(T *) p2); + return __typeid!NextType; } - final static @property immutable(void)* rtInfo() @nogc + static if (hasPointers) + override @property uint flags() @nogc { - pragma(inline, true); - return rtinfoNoPointers; + return 1; } - override @property immutable(void)* rtInfo() @nogc { return rtinfoNoPointers; } - } + //const(OffsetTypeInfo)[] offTi() const { return null; } + //void destroy(void* p) const {} + //void postblit(void* p) const {} +} + +/* +Run-time type information for scalar types (int for now). +*/ +template __typeid(T) +{ + // On-demand singleton object in static storage + immutable __typeid = new TypeInfoImpl!T; } unittest { - alias id = __typeid!int; - static assert(is(id.Type == int)); - immutable TypeInfo id2 = id; // Implicitly convert to base, losing static type information - static assert(id == id && id <= id && id >= id); - static assert(id2 == id && id2 <= id && id2 >= id); - static assert(id == id2 && id <= id2 && id >= id2); - static assert(id.toString == "int"); - static assert(id2.toString == "int"); - int a = 42, b = 42, c = 43; - static if (size_t.sizeof == 4) - enum size_t h = 4112119562; - else - enum size_t h = 17661420568835545970UL; - assert(id.getHash(&a) == h); - assert(id2.getHash(&a) == h); - assert(id.equals(&a, &b)); - assert(id2.equals(&a, &b)); - assert(!id.equals(&a, &c)); - assert(!id2.equals(&a, &c)); - assert(id.compare(&a, &b) == 0); - assert(id2.compare(&a, &b) == 0); - assert(id.compare(&a, &c) == -1); - assert(id2.compare(&a, &c) == -1); - assert(id.compare(&c, &a) == 1); - assert(id2.compare(&c, &a) == 1); - static assert(id.tsize == 4); - assert(id2.tsize == 4); - assert(cast(ubyte[]) id.initializer() == [ 0, 0, 0, 0 ]); - assert(cast(ubyte[]) id2.initializer() == [ 0, 0, 0, 0 ]); - id.swap(&a, &c); - assert(a == 43 && c == 42); - id2.swap(&a, &c); - assert(a == 42 && c == 43); + static void test(Ts...)() + { + static if (Ts.length > 1) + { + test!(Ts[0]); + test!(Ts[1 .. $]); + } + else + { + alias T = Ts[0]; + alias id = __typeid!T; + static assert(is(id.Type == T)); + immutable TypeInfo id2 = id; // Implicitly convert to base, losing static type information + + static assert(id == id && id <= id && id >= id); + static assert(id2 == id && id2 <= id && id2 >= id); + static assert(id == id2 && id <= id2 && id >= id2); + static assert(id.toString == T.stringof); + static assert(id2.toString == T.stringof); + + static if (is(T : int) && T.sizeof == 4) + { + int a = 42, b = 42, c = 43; + static if (size_t.sizeof == 4) + enum size_t h = 4112119562; + else + enum size_t h = 17661420568835545970UL; + assert(id.getHash(&a) == h); + assert(id2.getHash(&a) == h); + assert(id.equals(&a, &b)); + assert(id2.equals(&a, &b)); + assert(!id.equals(&a, &c)); + assert(!id2.equals(&a, &c)); + assert(id.compare(&a, &b) == 0); + assert(id2.compare(&a, &b) == 0); + assert(id.compare(&a, &c) == -1); + assert(id2.compare(&a, &c) == -1); + assert(id.compare(&c, &a) == 1); + assert(id2.compare(&c, &a) == 1); + assert(id.initializer.ptr is null, T.stringof); + assert(id2.initializer.ptr is null); + id.swap(&a, &c); + assert(a == 43 && c == 42); + id2.swap(&a, &c); + assert(a == 42 && c == 43); + static assert(id.flags == 0); + static assert(id2.flags == 0); + } + else + { + T x; + assert(id2.getHash(&x) == id.getHash(&x)); + assert(id.equals(&x, &x) || __traits(isFloating, T)); + assert(id2.equals(&x, &x) || __traits(isFloating, T)); + assert(id.compare(&x, &x) == 0); + assert(id2.compare(&x, &x) == 0); + } + + static assert(id.tsize == T.sizeof); + assert(id2.tsize == T.sizeof); + + static if (is(T == U*, U)) + { + static assert(id.next == __typeid!U); + static assert(id.flags == 1); + static assert(id2.flags == 1); + } + else static if (is(T == int[n], size_t n)) + { + static assert(is(typeof(id.initializer()) == immutable(int)[])); + assert(id.initializer().ptr is null); + assert(id.initializer().length == 1); + } + else static if (is(T == U[], U)) + { + static assert(id.next == __typeid!U); + static if (is(U == int)) + { + auto a = [ 1, 2, 3 ], b = [ 0, 1, 2, 3, 4 ]; + assert(!id.equals(&a, &b)); + assert(!id2.equals(&a, &b)); + b = b[1 .. 4]; + assert(id.equals(&a, &b)); + assert(id2.equals(&a, &b)); + } + static assert(id.flags == 1); + static assert(id2.flags == 1); + } + else static if (is(T U == enum)) + { + static assert(id.next == __typeid!U); + if (id.initializer.ptr) + { + T x1; + T x2 = *cast(T*) id.initializer.ptr; + assert(x1 == x2);// || __traits(isFloating, T)); + } + } + + assert(id.rtInfo == RTInfo!T); + assert(id2.rtInfo == RTInfo!T); + } + } + enum E1 { a, b } + enum E2 : float { a, b } + enum E3 : cdouble { a = 0 + 42i, b } + enum E4 { a = 42, b } + test!( + int, const(int), shared(int), //inout(int), + inout(const int), shared(const int), inout(shared int), const(inout(shared int)), + cdouble, ifloat, + int*, int[], E1, E2, E3, E4, + int[42], + ); } class TypeInfo_Enum : TypeInfo @@ -614,7 +848,7 @@ class TypeInfo_Enum : TypeInfo override @property size_t talign() nothrow pure const { return base.talign; } - version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) + version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) nothrow { return base.argTypes(arg1, arg2); } diff --git a/src/rt/util/typeinfo.d b/src/rt/util/typeinfo.d index 641c721716..ac135e4fc0 100644 --- a/src/rt/util/typeinfo.d +++ b/src/rt/util/typeinfo.d @@ -9,9 +9,10 @@ module rt.util.typeinfo; static import core.internal.hash; template Floating(T) -if (is(T == float) || is(T == double) || is(T == real)) +if (is(T == float) || is(T == double) || is(T == real) + || is(T == ifloat) || is(T == idouble) || is(T == ireal)) { - pure nothrow @safe: + pure nothrow @safe @nogc: bool equals(T f1, T f2) { @@ -20,17 +21,13 @@ if (is(T == float) || is(T == double) || is(T == real)) int compare(T d1, T d2) { - if (d1 != d1 || d2 != d2) // if either are NaN + auto d = d1 - d2; + auto result = int(d > 0) - int(d < 0); + if (!result && d != d) // if either are NaN { - if (d1 != d1) - { - if (d2 != d2) - return 0; - return -1; - } - return 1; + return int(d1 == d1) - int(d2 == d2); } - return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1); + return result; } public alias hashOf = core.internal.hash.hashOf; @@ -38,7 +35,7 @@ if (is(T == float) || is(T == double) || is(T == real)) template Floating(T) if (is(T == cfloat) || is(T == cdouble) || is(T == creal)) { - pure nothrow @safe: + pure nothrow @safe @nogc: bool equals(T f1, T f2) { @@ -266,3 +263,214 @@ unittest } }(); } + +// unittests for the new __typeid +unittest +{ + // Bugzilla 13052 + + static struct SX(F) { F f; } + + // real types + foreach (F; TypeTuple!(float, double, real)) + (){ // workaround #2396 + alias S = SX!F; + F f1 = +0.0, + f2 = -0.0; + + assert(f1 == f2); + assert(f1 !is f2); + + F[] a1 = [f1, f1, f1]; + F[] a2 = [f2, f2, f2]; + + { + auto ti = __typeid!F; + assert(ti.getHash(&f1) == ti.getHash(&f2)); + + assert(a1 == a2); + assert(a1 !is a2); + } + + F[][] aa1 = [a1, a1, a1]; + F[][] aa2 = [a2, a2, a2]; + { + auto ti = __typeid!(F[]); + assert(ti.getHash(&a1) == ti.getHash(&a2)); + + assert(aa1 == aa2); + assert(aa1 !is aa2); + } + + { + auto ti = __typeid!(F[][]); + assert(ti.getHash(&aa1) == ti.getHash(&aa2)); + + S s1 = {f1}, + s2 = {f2}; + assert(s1 == s2); + assert(s1 !is s2); + } + + S[] da1 = [S(f1), S(f1), S(f1)], + da2 = [S(f2), S(f2), S(f2)]; + + version(none) + { + auto ti = __typeid!(S); + assert(ti.getHash(&s1) == ti.getHash(&s2)); + + assert(da1 == da2); + assert(da1 !is da2); + } + + version(none) + { + auto ti = __typeid!(S[]); + assert(ti.getHash(&da1) == ti.getHash(&da2)); + + S[3] sa1 = {f1}, + sa2 = {f2}; + assert(sa1 == sa2); + assert(sa1[] !is sa2[]); + } + + version(none) + { + auto ti = __typeid!(S[3]); + assert(ti.getHash(&sa1) == ti.getHash(&sa2)); + } + }(); + + // imaginary types + foreach (F; TypeTuple!(ifloat, idouble, ireal)) + (){ // workaround #2396 + alias S = SX!F; + F f1 = +0.0i, + f2 = -0.0i; + F[] a1 = [f1, f1, f1]; + F[] a2 = [f2, f2, f2]; + { + assert(f1 == f2); + assert(f1 !is f2); + auto ti = __typeid!(F); + assert(ti.getHash(&f1) == ti.getHash(&f2)); + + assert(a1 == a2); + assert(a1 !is a2); + } + F[][] aa1 = [a1, a1, a1]; + F[][] aa2 = [a2, a2, a2]; + { + auto ti = __typeid!(F[]); + assert(ti.getHash(&a1) == ti.getHash(&a2)); + + assert(aa1 == aa2); + assert(aa1 !is aa2); + } + { + auto ti = __typeid!(F[][]); + assert(ti.getHash(&aa1) == ti.getHash(&aa2)); + + S s1 = {f1}, + s2 = {f2}; + assert(s1 == s2); + assert(s1 !is s2); + } + S[] da1 = [S(f1), S(f1), S(f1)], + da2 = [S(f2), S(f2), S(f2)]; + version(none) + { + auto ti = __typeid!(S); + assert(ti.getHash(&s1) == ti.getHash(&s2)); + + assert(da1 == da2); + assert(da1 !is da2); + } + + version(none) + { + auto ti = __typeid!(S[]); + assert(ti.getHash(&da1) == ti.getHash(&da2)); + + S[3] sa1 = {f1}, + sa2 = {f2}; + assert(sa1 == sa2); + assert(sa1[] !is sa2[]); + } + version(none) + { + auto ti = __typeid!(S[3]); + assert(ti.getHash(&sa1) == ti.getHash(&sa2)); + }}(); + + // complex types + foreach (F; TypeTuple!(cfloat, cdouble, creal)) + (){ // workaround #2396 + alias S = SX!F; + F[4] f = [+0.0 + 0.0i, + +0.0 - 0.0i, + -0.0 + 0.0i, + -0.0 - 0.0i]; + + foreach (i, f1; f) foreach (j, f2; f) if (i != j) + { + F[] a1 = [f1, f1, f1]; + F[] a2 = [f2, f2, f2]; + { + assert(f1 == 0 + 0i); + + assert(f1 == f2); + assert(f1 !is f2); + auto ti = __typeid!(F); + assert(ti.getHash(&f1) == ti.getHash(&f2)); + + assert(a1 == a2); + assert(a1 !is a2); + } + F[][] aa1 = [a1, a1, a1]; + F[][] aa2 = [a2, a2, a2]; + { + auto ti = __typeid!(F[]); + assert(ti.getHash(&a1) == ti.getHash(&a2)); + + assert(aa1 == aa2); + assert(aa1 !is aa2); + } + { + auto ti = __typeid!(F[][]); + assert(ti.getHash(&aa1) == ti.getHash(&aa2)); + + S s1 = {f1}, + s2 = {f2}; + assert(s1 == s2); + assert(s1 !is s2); + } + S[] da1 = [S(f1), S(f1), S(f1)], + da2 = [S(f2), S(f2), S(f2)]; + version(none) + { + auto ti = __typeid!(S); + assert(ti.getHash(&s1) == ti.getHash(&s2)); + + assert(da1 == da2); + assert(da1 !is da2); + } + S[3] sa1 = {f1}, + sa2 = {f2}; + version(none) + { + auto ti = __typeid!(S[]); + assert(ti.getHash(&da1) == ti.getHash(&da2)); + + assert(sa1 == sa2); + assert(sa1[] !is sa2[]); + } + version(none) + { + auto ti = __typeid!(S[3]); + assert(ti.getHash(&sa1) == ti.getHash(&sa2)); + } + } + }(); +} From 7a5b48a2c891b6a8509876bd23a0a337de64be11 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Fri, 31 Jul 2020 22:14:29 -0400 Subject: [PATCH 11/19] More types supported --- src/object.d | 346 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 249 insertions(+), 97 deletions(-) diff --git a/src/object.d b/src/object.d index d0cda92417..5c81749f8a 100644 --- a/src/object.d +++ b/src/object.d @@ -357,7 +357,7 @@ class TypeInfo * Bugs: * fix https://issues.dlang.org/show_bug.cgi?id=12516 e.g. by changing this to a truly safe interface. */ - size_t getHash(scope const void* p) @trusted nothrow const + size_t getHash(scope const void* p) /*@trusted*/ nothrow const { return hashOf(p); } @@ -429,38 +429,95 @@ Generic implementation of TypeInfo. T must be unqualified. */ private class TypeInfoImpl(T) : TypeInfo { - version (WithArgTypes) static if (is(T U1 == enum)) - override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted + const: // because this type has no state in the first place + + // Original type, accessible in statically-typed contexts. + alias Type = T; + import core.internal.traits : Unqual; + // Type stripped of qualifiers, accessible in statically-typed contexts. + alias UnqualifiedType = Unqual!T; + + // argTypes implementation + version (WithArgTypes) + { + static if (is(T U1 == enum)) + override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted + { + return (cast() __typeid!U1).argTypes(arg1, arg2); + } + else static if (__traits(isStaticArray, T) || __traits(isAssociativeArray, T)) + override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted + { + arg1 = cast() __typeid!(void*); // TODO: revisit + return 0; + } + else static if (is(T == __vector(U2), U2)) + override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted + { + return (cast() __typeid!U2).argTypes(arg1, arg2); + } + } + + // The postblit API (static and dynamic) + static if (is(typeof(&T.__postblit))) + static void postblit(UnqualifiedType* p) + { + p.__postblit(); + } + else static if (__traits(isStaticArray, T) && is(typeof(&UnqualifiedType.init[0].__postblit))) + static void postblit(UnqualifiedType* p) { - return (cast() __typeid!U1).argTypes(arg1, arg2); + foreach (i, ref e; *p) + e.__postblit(); } + else + alias postblit = doNothing; - const: nothrow: pure: @safe: + // The destroy API (static and dynamic) + static if (is(typeof(&T.__dtor))) + static void destroy(UnqualifiedType* p) + { + p.__dtor(); + } + else static if (__traits(isStaticArray, T) && is(typeof(&UnqualifiedType.init[0].__dtor))) + static void destroy(UnqualifiedType* p) + { + foreach_reverse (i, ref e; *p) + e.__dtor(); + } + else + alias destroy = doNothing; - import core.internal.traits : Unqual; - alias UnqualifiedType = Unqual!T; + nothrow: pure: - // Accessible in statically-typed contexts. - alias Type = T; + private final void doNothing(UnqualifiedType*) @nogc @safe {} - // On the off chance someone calls typeid(T) == typeid(U) with T == U - final bool opEquals(const TypeInfoImpl rhs) @nogc { pragma(inline, true); return true; } + // On the off chance someone calls typeid(T) == typeid(U) + final bool opEquals(U)(const TypeInfoImpl!U rhs) @nogc @safe + { + pragma(inline, true); + return is(T == U); + } // Is this equal to another TypeInfo? // WARNING: relies on a 1:1 mapping Type : TypeInfoImpl!Type - override bool opEquals(const Object rhs) @nogc + override bool opEquals(const Object rhs) @nogc @safe { return this is rhs // Instance may be duplicated across DLLs, but has the same type. || cast(const TypeInfoImpl) rhs !is null; } - // On the off chance someone calls typeid(T) < typeid(U) etc. with T == U - final int opCmp(const TypeInfoImpl rhs) @nogc { pragma(inline, true); return 0; } + // On the off chance someone calls typeid(T) < typeid(U) etc. + final int opCmp(U)(const TypeInfoImpl!U rhs) @nogc @safe + { + pragma(inline, true); + return is(T == U) ? 0 : __cmp(T.stringof, U.stringof); + } // Is this less than/equal/greater than another TypeInfo? // WARNING: relies on a 1:1 mapping Type : TypeInfoImpl!Type - override int opCmp(const Object rhs) + override int opCmp(const Object rhs) @safe { if (this is rhs // Instance may be duplicated across DLLs, but has the same type. @@ -473,9 +530,9 @@ private class TypeInfoImpl(T) : TypeInfo return 1; } - override string toString() @nogc { return T.stringof; } + override string toString() @nogc @safe { return T.stringof; } - final static size_t getHash(scope const T* p) @nogc @trusted + final static size_t getHash(scope const T* p) @nogc { pragma(inline, true); static if (is(T U == enum)) @@ -495,6 +552,10 @@ private class TypeInfoImpl(T) : TypeInfo { return *p; } + else static if (is(T == __vector(U), U)) + { + return __typeid!U.getHash(cast(const U*) p); + } else { // Knuth's multiplicative hash with the golden ratio of 2^32 or 2^64 @@ -518,89 +579,139 @@ private class TypeInfoImpl(T) : TypeInfo const U[] t = (*p)[]; return __typeid!(U[]).getHash(&t); } + else static if (is(T == V[K], V, K)) + { + if (!*p) return 0; + alias F = hash_t function(scope const AA* aa, scope const TypeInfo tiRaw) nothrow pure @nogc; + auto f = cast(F) &_aaGetHash; + return f(cast(AA*)p, typeid(T)); + } + else static if (is(T == function)) + { + return hashOf(*cast(void function()*) p); + } + else static if (is(T == delegate)) + { + return hashOf(*cast(void delegate()*) p); + } + else static if (is(T == void)) + { + return 0; + } else { static assert(0, T.stringof); } } - override size_t getHash(scope const void* p) @nogc @trusted + override size_t getHash(scope const void* p) @nogc { return getHash(cast(const T *) p); } - final static bool equals(in T* p1, in T* p2) @nogc @trusted + static if (!is(T == function)) + final static bool equals(in T* lhs, in T* rhs) @nogc { pragma(inline, true); - return *p1 == *p2; + static if (is(T == void)) + return true; + else static if (is(T == __vector(U), U)) + return __typeid!U.equals(cast(const U*) lhs, cast(const U*) rhs); + else + return *lhs == *rhs; } - override bool equals(in void* p1, in void* p2) @nogc @trusted + static if (!is(T == function)) + override bool equals(in void* p1, in void* p2) @nogc { return equals(cast(const T*)p1, cast(const T*)p2); } - final static int compare(in T* lhs, in T* rhs) @nogc @trusted + static if (!__traits(isAssociativeArray, T) && !is(T == function)) { - pragma(inline, true); - static if (is(T U == enum)) - { - return __typeid!U.compare(cast(const U*) lhs, cast(const U*) rhs); - } - else static if (__traits(isFloating, T)) - { - import rt.util.typeinfo; - return Floating!UnqualifiedType.compare(*lhs, *rhs); - } - else static if (is(typeof(__cmp(*lhs, *rhs)) == int)) + final static int compare(in T* lhs, in T* rhs) @nogc { - // Avoid comparing twice for arrays - return __cmp(*lhs, *rhs); + pragma(inline, true); + static if (is(T U == enum)) + { + return __typeid!U.compare(cast(const U*) lhs, cast(const U*) rhs); + } + else static if (__traits(isFloating, T)) + { + import rt.util.typeinfo; + return Floating!UnqualifiedType.compare(*lhs, *rhs); + } + else static if (is(typeof(__cmp(*lhs, *rhs)) == int)) + { + // Avoid comparing twice for arrays + return __cmp(*lhs, *rhs); + } + else static if (is(T == void)) + { + return 0; + } + else static if (is(T == __vector(U), U)) + return __typeid!U.compare(cast(const U*) lhs, cast(const U*) rhs); + else + return int(*lhs > *rhs) - int(*lhs < *rhs); } - else + + override int compare(in void* p1, in void* p2) @nogc { - return int(*lhs > *rhs) - int(*lhs < *rhs); + return compare(cast(const T*) p1, cast(const T*) p2); } } - override int compare(in void* p1, in void* p2) @nogc @trusted - { - return compare(cast(const T*) p1, cast(const T*) p2); - } - - final static @property size_t tsize() @nogc + final static @property size_t tsizeImpl() @nogc @safe { pragma(inline, true); - return T.sizeof; + static if (is(T == function)) + return 0; + else + return T.sizeof; } - override @property size_t tsize() @nogc + alias tsize = tsizeImpl; + + override @property size_t tsize() @nogc @safe { - return T.sizeof; + return tsizeImpl; } - final static @property size_t talign() @nogc + final static @property size_t talignImpl() @nogc @safe { pragma(inline, true); - return T.alignof; + static if (is(T == function)) + return 0; + else + return T.alignof; } - override @property size_t talign() @nogc { return T.alignof; } + alias talign = talignImpl; - private final static auto initializerImpl() @trusted @nogc + override @property size_t talign() @nogc @safe + { + return talignImpl; + } + + private final static @property auto initializerImpl() @nogc @trusted { pragma(inline, true); static if (is(T == U[n], U, size_t n)) { // Static arrays only return the element initializer - return __typeid!U.initializerImpl; + return __typeid!U.initializerImpl(); } else static if (__traits(isZeroInit, T)) { // BUG - this won't work: // return (cast(immutable T*) null)[0 .. 1]; - immutable(T)[] result = (cast(immutable T*) null)[0 .. 1]; - return result; + immutable(T)[] result = null; + return result.ptr[0 .. 1]; + } + else static if (is(T == function)) + { + return null; } else { @@ -612,83 +723,104 @@ private class TypeInfoImpl(T) : TypeInfo alias initializer = initializerImpl; - override immutable(void)[] initializer() @trusted @nogc + override @property immutable(void)[] initializer() @nogc @safe { return initializerImpl; } - final static void swap(UnqualifiedType *p1, UnqualifiedType *p2) @nogc @trusted + static if (!is(T == function)) { - pragma(inline, true); - static if (is(UnqualifiedType == U[n], U, size_t n)) + final static void swap(UnqualifiedType *p1, UnqualifiedType *p2) @nogc @trusted { - // Avoid creating a large temporary on the stack. - foreach (i, ref e; *p1) + pragma(inline, true); + static if (is(UnqualifiedType == U[n], U, size_t n)) + { + // Avoid creating a large temporary on the stack. + foreach (i, ref e; *p1) + { + __typeid!U.swap(&e, &(*p2)[i]); + } + } + else { - auto t = e; - e = (*p2)[i]; - (*p2)[i] = t; + ubyte[T.sizeof] buf; + import core.stdc.string : memcpy; + memcpy(&buf, p1, T.sizeof); + memcpy(p1, p2, T.sizeof); + memcpy(p2, &buf, T.sizeof); } } - else + + override void swap(void *p1, void *p2) @nogc { - auto t = *p1; - *p1 = *p2; - *p2 = t; + return swap(cast(UnqualifiedType *) p1, cast(UnqualifiedType *) p2); } } - override void swap(void *p1, void *p2) @nogc @trusted - { - return swap(cast(UnqualifiedType *) p1, cast(UnqualifiedType *) p2); - } - - final static @property auto rtInfo() @nogc + final static @property auto rtInfoImpl() @nogc @safe { pragma(inline, true); - return RTInfo!T; + static if (is(T == function)) + return null; + else + return RTInfo!T; } - override @property immutable(void)* rtInfo() @nogc + alias rtInfo = rtInfoImpl; + + override @property immutable(void)* rtInfo() @nogc @safe { - return RTInfo!T; + return rtInfoImpl; } static if (is(T == U*, U)) { private alias NextType = U; - private enum bool hasPointers = true; + private enum _flags = 1; } else static if (is(T == U[], U)) { private alias NextType = U; - private enum bool hasPointers = true; + private enum _flags = 1; + } + else static if (is(T == U[n], U, size_t n)) + { + private alias NextType = U; + private enum _flags = __typeid!U._flags; + } + else static if (is(T == V[K], K, V)) + { + private alias NextType = V; + private enum _flags = 1; } else static if (is(T U == enum)) { private alias NextType = U; - private enum bool hasPointers = __typeid!U.hasPointers; + private enum _flags = __typeid!U._flags; + } + else static if (is(T == __vector(U), U)) + { + private alias NextType = U; + private enum _flags = 2; } else { private alias NextType = T; - private enum bool hasPointers = false; + private enum _flags = 0; } static if (!is(T == NextType)) - override @property inout(TypeInfo) next() nothrow pure inout + override @property inout(TypeInfo) next() nothrow pure inout @safe { return __typeid!NextType; } - static if (hasPointers) - override @property uint flags() @nogc - { - return 1; - } + override @property uint flags() @nogc @safe + { + return _flags; + } //const(OffsetTypeInfo)[] offTi() const { return null; } - //void destroy(void* p) const {} //void postblit(void* p) const {} } @@ -701,7 +833,7 @@ template __typeid(T) immutable __typeid = new TypeInfoImpl!T; } -unittest +version(none) unittest { static void test(Ts...)() { @@ -736,13 +868,16 @@ unittest assert(id2.equals(&a, &b)); assert(!id.equals(&a, &c)); assert(!id2.equals(&a, &c)); - assert(id.compare(&a, &b) == 0); - assert(id2.compare(&a, &b) == 0); - assert(id.compare(&a, &c) == -1); - assert(id2.compare(&a, &c) == -1); - assert(id.compare(&c, &a) == 1); - assert(id2.compare(&c, &a) == 1); - assert(id.initializer.ptr is null, T.stringof); + static if (!__traits(isAssociativeArray, T)) + { + assert(id.compare(&a, &b) == 0); + assert(id2.compare(&a, &b) == 0); + assert(id.compare(&a, &c) == -1); + assert(id2.compare(&a, &c) == -1); + assert(id.compare(&c, &a) == 1); + assert(id2.compare(&c, &a) == 1); + } + assert(id.initializer.ptr is null); assert(id2.initializer.ptr is null); id.swap(&a, &c); assert(a == 43 && c == 42); @@ -757,8 +892,11 @@ unittest assert(id2.getHash(&x) == id.getHash(&x)); assert(id.equals(&x, &x) || __traits(isFloating, T)); assert(id2.equals(&x, &x) || __traits(isFloating, T)); - assert(id.compare(&x, &x) == 0); - assert(id2.compare(&x, &x) == 0); + static if (!__traits(isAssociativeArray, T)) + { + assert(id.compare(&x, &x) == 0); + assert(id2.compare(&x, &x) == 0); + } } static assert(id.tsize == T.sizeof); @@ -772,6 +910,7 @@ unittest } else static if (is(T == int[n], size_t n)) { + static assert(id.next == __typeid!int); static assert(is(typeof(id.initializer()) == immutable(int)[])); assert(id.initializer().ptr is null); assert(id.initializer().length == 1); @@ -815,8 +954,15 @@ unittest inout(const int), shared(const int), inout(shared int), const(inout(shared int)), cdouble, ifloat, int*, int[], E1, E2, E3, E4, - int[42], + int[42], int[int], __vector(int[4]) ); + + // Test a static array with types with destructor + // static uint x = 0; + // static struct A { ~this() { ++x; } } + // A[42] a; + // __typeid!(A[42]).destroy(&a); + // assert(x == 42); } class TypeInfo_Enum : TypeInfo @@ -1290,6 +1436,10 @@ class TypeInfo_Function : TypeInfo assert(typeid(functionTypes[0]).toString() == "void function()"); assert(typeid(functionTypes[1]).toString() == "void function(int)"); assert(typeid(functionTypes[2]).toString() == "int function(int, int)"); + + static assert(__typeid!(functionTypes[0]).toString() == "void()"); + static assert(__typeid!(functionTypes[1]).toString() == "void(int)"); + static assert(__typeid!(functionTypes[2]).toString() == "int(int, int)"); } class TypeInfo_Delegate : TypeInfo @@ -1308,8 +1458,10 @@ class TypeInfo_Delegate : TypeInfo { double sqr(double x) { return x * x; } assert(typeid(typeof(&sqr)).toString() == "double delegate(double) pure nothrow @nogc @safe"); + assert(__typeid!(typeof(&sqr)).toString() == "double delegate(double) pure nothrow @nogc @safe"); int g; assert(typeid(typeof((int a, int b) => a + b + g)).toString() == "int delegate(int, int) pure nothrow @nogc @safe"); + assert(__typeid!(typeof((int a, int b) => a + b + g)).toString() == "int delegate(int, int) pure nothrow @nogc @safe"); } override bool opEquals(Object o) From ec78cc65cb7cfd2dee68130975027279d2150bb9 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sat, 1 Aug 2020 11:06:06 -0400 Subject: [PATCH 12/19] Added support for functions and delegates --- src/core/internal/array/operations.d | 1 - src/object.d | 61 ++++++++++++++++++---------- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/core/internal/array/operations.d b/src/core/internal/array/operations.d index 240d1807d8..0e38d2cefa 100644 --- a/src/core/internal/array/operations.d +++ b/src/core/internal/array/operations.d @@ -228,7 +228,6 @@ else version (X86_64) unittest { - pragma(msg, vectorizeable!(double[], const(double)[], double[], "+", "=")); static assert(vectorizeable!(double[], const(double)[], double[], "+", "=")); static assert(!vectorizeable!(double[], const(ulong)[], double[], "+", "=")); // Vector type are (atm.) not implicitly convertible and would require diff --git a/src/object.d b/src/object.d index 5c81749f8a..796985d132 100644 --- a/src/object.d +++ b/src/object.d @@ -456,6 +456,20 @@ private class TypeInfoImpl(T) : TypeInfo { return (cast() __typeid!U2).argTypes(arg1, arg2); } + else static if (is(T == U3[], U3)) + override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted + { + arg1 = cast() __typeid!size_t; // TODO: revisit + arg1 = cast() __typeid!U3; // TODO: revisit + return 0; + } + else static if (is(T == delegate)) + override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted + { + arg1 = cast() __typeid!(void*); // TODO: revisit + arg1 = cast() __typeid!(void*); // TODO: revisit + return 0; + } } // The postblit API (static and dynamic) @@ -579,6 +593,10 @@ private class TypeInfoImpl(T) : TypeInfo const U[] t = (*p)[]; return __typeid!(U[]).getHash(&t); } + else static if (is(T == class) || is(T == interface)) + { + return *p ? p.toHash : 0; + } else static if (is(T == V[K], V, K)) { if (!*p) return 0; @@ -586,13 +604,9 @@ private class TypeInfoImpl(T) : TypeInfo auto f = cast(F) &_aaGetHash; return f(cast(AA*)p, typeid(T)); } - else static if (is(T == function)) - { - return hashOf(*cast(void function()*) p); - } - else static if (is(T == delegate)) + else static if (is(T == function) || is(T == delegate)) { - return hashOf(*cast(void delegate()*) p); + return hashOf(p); } else static if (is(T == void)) { @@ -622,9 +636,9 @@ private class TypeInfoImpl(T) : TypeInfo } static if (!is(T == function)) - override bool equals(in void* p1, in void* p2) @nogc + override bool equals(in void* lhs, in void* rhs) @nogc { - return equals(cast(const T*)p1, cast(const T*)p2); + return equals(cast(const T*) lhs, cast(const T*) rhs); } static if (!__traits(isAssociativeArray, T) && !is(T == function)) @@ -776,37 +790,42 @@ private class TypeInfoImpl(T) : TypeInfo static if (is(T == U*, U)) { private alias NextType = U; - private enum _flags = 1; + private enum uint _flags = 1; } else static if (is(T == U[], U)) { private alias NextType = U; - private enum _flags = 1; + private enum uint _flags = 1; } else static if (is(T == U[n], U, size_t n)) { private alias NextType = U; - private enum _flags = __typeid!U._flags; + private enum uint uint _flags = __typeid!U._flags; } else static if (is(T == V[K], K, V)) { private alias NextType = V; - private enum _flags = 1; + private enum uint _flags = 1; } else static if (is(T U == enum)) { private alias NextType = U; - private enum _flags = __typeid!U._flags; + private enum uint _flags = __typeid!U._flags; } else static if (is(T == __vector(U), U)) { private alias NextType = U; - private enum _flags = 2; + private enum uint _flags = 2; + } + else static if (is(T == delegate)) + { + private alias NextType = T; + private enum uint _flags = 1; } else { private alias NextType = T; - private enum _flags = 0; + private enum uint _flags = 0; } static if (!is(T == NextType)) @@ -815,10 +834,9 @@ private class TypeInfoImpl(T) : TypeInfo return __typeid!NextType; } - override @property uint flags() @nogc @safe - { - return _flags; - } + static @property uint flags() @nogc @safe { return _flags; } + + override @property uint flags() @nogc @safe { return _flags; } //const(OffsetTypeInfo)[] offTi() const { return null; } //void postblit(void* p) const {} @@ -833,7 +851,7 @@ template __typeid(T) immutable __typeid = new TypeInfoImpl!T; } -version(none) unittest +unittest { static void test(Ts...)() { @@ -954,7 +972,8 @@ version(none) unittest inout(const int), shared(const int), inout(shared int), const(inout(shared int)), cdouble, ifloat, int*, int[], E1, E2, E3, E4, - int[42], int[int], __vector(int[4]) + int[42], int[int], __vector(int[4]), + int function(double), string delegate(int[]), ); // Test a static array with types with destructor From e5bd5535e7f2befd22ddeee6404b781572d4d400 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sat, 1 Aug 2020 15:43:18 -0400 Subject: [PATCH 13/19] Defer to getHash for all hashing work --- src/object.d | 79 +++++----------------------------------------------- 1 file changed, 7 insertions(+), 72 deletions(-) diff --git a/src/object.d b/src/object.d index 796985d132..a896a0c3ad 100644 --- a/src/object.d +++ b/src/object.d @@ -550,72 +550,11 @@ private class TypeInfoImpl(T) : TypeInfo { pragma(inline, true); static if (is(T U == enum)) - { - return __typeid!U.getHash(cast(const U*) p); - } - else static if (is(T == U[], U)) - { - size_t result = 0; - foreach (ref e; *p) - result = hashOf(e, result); - return result; - } - else static if (__traits(isIntegral, T)) - { - static if (is(T : bool)) - { - return *p; - } - else static if (is(T == __vector(U), U)) - { - return __typeid!U.getHash(cast(const U*) p); - } - else - { - // Knuth's multiplicative hash with the golden ratio of 2^32 or 2^64 - static if (size_t.sizeof == 4) - return *p * 2_654_435_761U; - else - return *p * 11_400_714_819_323_198_485UL; - } - } - else static if (__traits(isFloating, T)) - { - import rt.util.typeinfo; - return Floating!UnqualifiedType.hashOf(*p); - } - else static if (is(T == U*, U)) - { - return __typeid!size_t.getHash(cast(const size_t*) p); - } - else static if (is(T == U[n], U, size_t n)) - { - const U[] t = (*p)[]; - return __typeid!(U[]).getHash(&t); - } - else static if (is(T == class) || is(T == interface)) - { - return *p ? p.toHash : 0; - } - else static if (is(T == V[K], V, K)) - { - if (!*p) return 0; - alias F = hash_t function(scope const AA* aa, scope const TypeInfo tiRaw) nothrow pure @nogc; - auto f = cast(F) &_aaGetHash; - return f(cast(AA*)p, typeid(T)); - } - else static if (is(T == function) || is(T == delegate)) - { - return hashOf(p); - } - else static if (is(T == void)) - { - return 0; - } + return hashOf(*cast(const U*) p); + else static if (is(T == void) || is(T == function)) + return 0; // these aren't hashable else - { - static assert(0, T.stringof); - } + return hashOf(*p); } override size_t getHash(scope const void* p) @nogc @@ -800,7 +739,7 @@ private class TypeInfoImpl(T) : TypeInfo else static if (is(T == U[n], U, size_t n)) { private alias NextType = U; - private enum uint uint _flags = __typeid!U._flags; + private enum uint _flags = __typeid!U._flags; } else static if (is(T == V[K], K, V)) { @@ -876,12 +815,8 @@ unittest static if (is(T : int) && T.sizeof == 4) { int a = 42, b = 42, c = 43; - static if (size_t.sizeof == 4) - enum size_t h = 4112119562; - else - enum size_t h = 17661420568835545970UL; - assert(id.getHash(&a) == h); - assert(id2.getHash(&a) == h); + assert(id.getHash(&a) == a); + assert(id2.getHash(&a) == a); assert(id.equals(&a, &b)); assert(id2.equals(&a, &b)); assert(!id.equals(&a, &c)); From a20cd9eb979515a94169138113a670e9a32b5e47 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 2 Aug 2020 15:53:13 -0400 Subject: [PATCH 14/19] Add struct support. Also make __typeid not immutable, but still store it in read-only memory --- posix.mak | 4 +- src/object.d | 254 ++++++++++++++++++++++++++++------------- src/rt/util/typeinfo.d | 229 ++++++++++++++++--------------------- 3 files changed, 274 insertions(+), 213 deletions(-) diff --git a/posix.mak b/posix.mak index 9c1ee2d20a..dc4825eaf3 100644 --- a/posix.mak +++ b/posix.mak @@ -327,7 +327,9 @@ $(ROOT)/unittest/% : $(ROOT)/unittest/test_runner # make the file very old so it builds and runs again if it fails @touch -t 197001230123 $@ # run unittest in its own directory - $(QUIET)$(TIMELIMIT)$< $(call moduleName,$*) +# output PASS info on the same line if running in the console to cut noise + $(QUIET)$(TIMELIMIT)$< $(call moduleName,$*) \ + | ([ -t 1 ] && while read line; do printf '%-80s\r' "$$line"; done || cat) # succeeded, render the file new again @touch $@ diff --git a/src/object.d b/src/object.d index a896a0c3ad..4ef7f4dcbb 100644 --- a/src/object.d +++ b/src/object.d @@ -443,31 +443,31 @@ private class TypeInfoImpl(T) : TypeInfo static if (is(T U1 == enum)) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted { - return (cast() __typeid!U1).argTypes(arg1, arg2); + return __typeid!U1.argTypes(arg1, arg2); } else static if (__traits(isStaticArray, T) || __traits(isAssociativeArray, T)) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted { - arg1 = cast() __typeid!(void*); // TODO: revisit + arg1 = __typeid!(void*); // TODO: revisit return 0; } else static if (is(T == __vector(U2), U2)) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted { - return (cast() __typeid!U2).argTypes(arg1, arg2); + return __typeid!U2.argTypes(arg1, arg2); } else static if (is(T == U3[], U3)) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted { - arg1 = cast() __typeid!size_t; // TODO: revisit - arg1 = cast() __typeid!U3; // TODO: revisit + arg1 = __typeid!size_t; // TODO: revisit + arg1 = __typeid!U3; // TODO: revisit return 0; } else static if (is(T == delegate)) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @nogc @trusted { - arg1 = cast() __typeid!(void*); // TODO: revisit - arg1 = cast() __typeid!(void*); // TODO: revisit + arg1 = __typeid!(void*); // TODO: revisit + arg1 = __typeid!(void*); // TODO: revisit return 0; } } @@ -476,38 +476,52 @@ private class TypeInfoImpl(T) : TypeInfo static if (is(typeof(&T.__postblit))) static void postblit(UnqualifiedType* p) { + pragma(inline, true); + assert(p); p.__postblit(); } else static if (__traits(isStaticArray, T) && is(typeof(&UnqualifiedType.init[0].__postblit))) static void postblit(UnqualifiedType* p) { + assert(p); foreach (i, ref e; *p) e.__postblit(); } else - alias postblit = doNothing; + private alias NoPostblit = void; + + static if (!is(NoPostblit)) + override void postblit(void* p) + { + postblit(cast(UnqualifiedType*) p); + } // The destroy API (static and dynamic) - static if (is(typeof(&T.__dtor))) + static if (is(typeof(&UnqualifiedType.__dtor))) static void destroy(UnqualifiedType* p) { + pragma(inline, true); + assert(p); p.__dtor(); } else static if (__traits(isStaticArray, T) && is(typeof(&UnqualifiedType.init[0].__dtor))) static void destroy(UnqualifiedType* p) { + assert(p); foreach_reverse (i, ref e; *p) e.__dtor(); } else - alias destroy = doNothing; - - nothrow: pure: + private alias NoDestroy = void; - private final void doNothing(UnqualifiedType*) @nogc @safe {} + static if (!is(NoDestroy)) + override void destroy(void* p) + { + destroy(cast(UnqualifiedType*) p); + } - // On the off chance someone calls typeid(T) == typeid(U) - final bool opEquals(U)(const TypeInfoImpl!U rhs) @nogc @safe + // Supports typeid(T) == typeid(U) during compilation + static bool opEquals(U)(const TypeInfoImpl!U rhs) @nogc nothrow pure @safe { pragma(inline, true); return is(T == U); @@ -515,15 +529,15 @@ private class TypeInfoImpl(T) : TypeInfo // Is this equal to another TypeInfo? // WARNING: relies on a 1:1 mapping Type : TypeInfoImpl!Type - override bool opEquals(const Object rhs) @nogc @safe + override bool opEquals(const Object rhs) @nogc nothrow pure @safe { return this is rhs // Instance may be duplicated across DLLs, but has the same type. || cast(const TypeInfoImpl) rhs !is null; } - // On the off chance someone calls typeid(T) < typeid(U) etc. - final int opCmp(U)(const TypeInfoImpl!U rhs) @nogc @safe + // Support typeid(T) < typeid(U) etc. during compilation. + static int opCmp(U)(const TypeInfoImpl!U rhs) @nogc nothrow pure @safe { pragma(inline, true); return is(T == U) ? 0 : __cmp(T.stringof, U.stringof); @@ -539,45 +553,47 @@ private class TypeInfoImpl(T) : TypeInfo return 0; if (auto ti = cast(const TypeInfo) rhs) return __cmp(this.toString, ti.toString); - // TypeInfo objects are all "greater" than all other types. + // TypeInfo objects are all "smaller" than all other types. // TODO: devise a principled way to define/justify this. - return 1; + return -1; } override string toString() @nogc @safe { return T.stringof; } - final static size_t getHash(scope const T* p) @nogc + final static size_t getHash(scope const UnqualifiedType* p) @nogc pure nothrow { pragma(inline, true); + assert(p); static if (is(T U == enum)) return hashOf(*cast(const U*) p); - else static if (is(T == void) || is(T == function)) + else static if (is(immutable T == immutable void) || is(T == function)) return 0; // these aren't hashable else return hashOf(*p); } - override size_t getHash(scope const void* p) @nogc + override size_t getHash(scope const void* p) @nogc pure nothrow { - return getHash(cast(const T *) p); + return getHash(cast(const UnqualifiedType *) p); } static if (!is(T == function)) - final static bool equals(in T* lhs, in T* rhs) @nogc { - pragma(inline, true); - static if (is(T == void)) - return true; - else static if (is(T == __vector(U), U)) - return __typeid!U.equals(cast(const U*) lhs, cast(const U*) rhs); - else - return *lhs == *rhs; - } + static bool equals(in T* lhs, in T* rhs) + { + pragma(inline, true); + static if (is(immutable T == immutable void)) + return lhs is rhs; + else static if (is(T == __vector(U), U)) + return __typeid!U.equals(cast(const U*) lhs, cast(const U*) rhs); + else + return *lhs == *rhs; + } - static if (!is(T == function)) - override bool equals(in void* lhs, in void* rhs) @nogc - { - return equals(cast(const T*) lhs, cast(const T*) rhs); + override bool equals(in void* lhs, in void* rhs) + { + return equals(cast(const T*) lhs, cast(const T*) rhs); + } } static if (!__traits(isAssociativeArray, T) && !is(T == function)) @@ -585,7 +601,17 @@ private class TypeInfoImpl(T) : TypeInfo final static int compare(in T* lhs, in T* rhs) @nogc { pragma(inline, true); - static if (is(T U == enum)) + static if (is(T == struct)) + { + // Avoid comparing twice for structs + return .__cmp(*lhs, *rhs); + } + else static if (is(typeof(.__cmp(*lhs, *rhs)) == int)) + { + // Avoid comparing twice for arrays + return .__cmp(*lhs, *rhs); + } + else static if (is(T U == enum)) { return __typeid!U.compare(cast(const U*) lhs, cast(const U*) rhs); } @@ -594,12 +620,7 @@ private class TypeInfoImpl(T) : TypeInfo import rt.util.typeinfo; return Floating!UnqualifiedType.compare(*lhs, *rhs); } - else static if (is(typeof(__cmp(*lhs, *rhs)) == int)) - { - // Avoid comparing twice for arrays - return __cmp(*lhs, *rhs); - } - else static if (is(T == void)) + else static if (is(immutable T == immutable void)) { return 0; } @@ -615,7 +636,7 @@ private class TypeInfoImpl(T) : TypeInfo } } - final static @property size_t tsizeImpl() @nogc @safe + final static @property size_t tsizeImpl() @nogc nothrow pure @safe { pragma(inline, true); static if (is(T == function)) @@ -626,12 +647,12 @@ private class TypeInfoImpl(T) : TypeInfo alias tsize = tsizeImpl; - override @property size_t tsize() @nogc @safe + override @property size_t tsize() @nogc nothrow pure @safe { return tsizeImpl; } - final static @property size_t talignImpl() @nogc @safe + final static @property size_t talignImpl() @nogc nothrow pure @safe { pragma(inline, true); static if (is(T == function)) @@ -642,12 +663,12 @@ private class TypeInfoImpl(T) : TypeInfo alias talign = talignImpl; - override @property size_t talign() @nogc @safe + override @property size_t talign() @nogc nothrow pure @safe { return talignImpl; } - private final static @property auto initializerImpl() @nogc @trusted + private final static @property auto initializerImpl() @nogc nothrow pure @trusted { pragma(inline, true); static if (is(T == U[n], U, size_t n)) @@ -676,14 +697,14 @@ private class TypeInfoImpl(T) : TypeInfo alias initializer = initializerImpl; - override @property immutable(void)[] initializer() @nogc @safe + override @property immutable(void)[] initializer() @nogc nothrow pure @safe { return initializerImpl; } static if (!is(T == function)) { - final static void swap(UnqualifiedType *p1, UnqualifiedType *p2) @nogc @trusted + final static void swap(UnqualifiedType *p1, UnqualifiedType *p2) @nogc nothrow pure @trusted { pragma(inline, true); static if (is(UnqualifiedType == U[n], U, size_t n)) @@ -704,13 +725,13 @@ private class TypeInfoImpl(T) : TypeInfo } } - override void swap(void *p1, void *p2) @nogc + override void swap(void *p1, void *p2) @nogc nothrow pure { return swap(cast(UnqualifiedType *) p1, cast(UnqualifiedType *) p2); } } - final static @property auto rtInfoImpl() @nogc @safe + final static @property auto rtInfoImpl() @nogc nothrow pure @safe { pragma(inline, true); static if (is(T == function)) @@ -761,6 +782,21 @@ private class TypeInfoImpl(T) : TypeInfo private alias NextType = T; private enum uint _flags = 1; } + else static if (is(T == struct)) + { + private alias NextType = T; + private enum uint _flags = () { + // If at least one field has pointers, the struct has pointers. + // This loop goes all the way. Otherwise: "unreachable code" warnings. + uint result = 0; + static foreach (const i; 0 .. T.tupleof.length) + { + static if (__typeid!(typeof(T.tupleof[i])).flags) + result = 1; + } + return result; + }(); + } else { private alias NextType = T; @@ -768,14 +804,14 @@ private class TypeInfoImpl(T) : TypeInfo } static if (!is(T == NextType)) - override @property inout(TypeInfo) next() nothrow pure inout @safe + override @property inout(TypeInfo) next() nothrow @nogc pure inout @safe { return __typeid!NextType; } - static @property uint flags() @nogc @safe { return _flags; } + static @property uint flags() nothrow @nogc pure @safe { return _flags; } - override @property uint flags() @nogc @safe { return _flags; } + override @property uint flags() nothrow @nogc pure @safe { return _flags; } //const(OffsetTypeInfo)[] offTi() const { return null; } //void postblit(void* p) const {} @@ -787,7 +823,12 @@ Run-time type information for scalar types (int for now). template __typeid(T) { // On-demand singleton object in static storage - immutable __typeid = new TypeInfoImpl!T; + private immutable singleton = new TypeInfoImpl!T; + // Get rid of the immutable qualifier for convenience; the type has no state anyway + @property @trusted pure @nogc nothrow TypeInfoImpl!T __typeid() + { + return cast() singleton; + } } unittest @@ -802,9 +843,11 @@ unittest else { alias T = Ts[0]; + import core.internal.traits : Unqual; + alias UnqualifiedT = Unqual!T; alias id = __typeid!T; static assert(is(id.Type == T)); - immutable TypeInfo id2 = id; // Implicitly convert to base, losing static type information + const TypeInfo id2 = id; // Implicitly convert to base, losing static type information static assert(id == id && id <= id && id >= id); static assert(id2 == id && id2 <= id && id2 >= id); @@ -841,14 +884,17 @@ unittest } else { - T x; - assert(id2.getHash(&x) == id.getHash(&x)); - assert(id.equals(&x, &x) || __traits(isFloating, T)); - assert(id2.equals(&x, &x) || __traits(isFloating, T)); - static if (!__traits(isAssociativeArray, T)) + static if (is(typeof({ UnqualifiedT x; }))) { - assert(id.compare(&x, &x) == 0); - assert(id2.compare(&x, &x) == 0); + UnqualifiedT x; + assert(id2.getHash(&x) == id.getHash(&x)); + assert(id.equals(&x, &x) || __traits(isFloating, T)); + assert(id2.equals(&x, &x) || __traits(isFloating, T)); + static if (!__traits(isAssociativeArray, T)) + { + assert(id.compare(&x, &x) == 0); + assert(id2.compare(&x, &x) == 0); + } } } @@ -902,21 +948,34 @@ unittest enum E2 : float { a, b } enum E3 : cdouble { a = 0 + 42i, b } enum E4 { a = 42, b } + struct S1 { double a = 0; } + struct S2 { int a; S2* next; } + struct S3 { int a; S2[] array; } + struct S4 { int a; S2 b; } test!( - int, const(int), shared(int), //inout(int), + int, const(int), shared(int), inout(int), immutable(int), inout(const int), shared(const int), inout(shared int), const(inout(shared int)), + int*, const(int*), shared(int*), inout(int*), immutable(int*), + inout(const int*), shared(const int*), inout(shared int*), const(inout(shared int*)), cdouble, ifloat, int*, int[], E1, E2, E3, E4, int[42], int[int], __vector(int[4]), int function(double), string delegate(int[]), + S1, const S1, shared S1, const inout shared S1, immutable S1, + S2, const S2, shared S2, const inout shared S2, immutable S2 ); - // Test a static array with types with destructor - // static uint x = 0; - // static struct A { ~this() { ++x; } } - // A[42] a; - // __typeid!(A[42]).destroy(&a); - // assert(x == 42); + static assert(__typeid!S1.flags == 0); + static assert(__typeid!S2.flags == 1); + static assert(__typeid!S3.flags == 1); + static assert(__typeid!S4.flags == 1); + + // Test a static array with types with destructor. + static uint x = 0; + static struct A { ~this() { ++x; } } + A[42] a; + __typeid!(A[42]).destroy(&a); + assert(x == 42); } class TypeInfo_Enum : TypeInfo @@ -1930,6 +1989,7 @@ class TypeInfo_Struct : TypeInfo } S s; assert(!typeid(S).equals(&s, &s)); + assert(!__typeid!S.equals(&s, &s)); } class TypeInfo_Tuple : TypeInfo @@ -4521,17 +4581,28 @@ public import core.internal.switch_: __switch_error; public @trusted @nogc nothrow pure extern (C) void _d_delThrowable(scope Throwable); // Compare class and interface objects for ordering. -private int __cmp(Obj)(Obj lhs, Obj rhs) -if (is(Obj : Object)) +private int __cmp(C1, C2)(C1 lhs, C2 rhs) +if (is(C1 : Object) && is(C2 : Object)) { - if (lhs is rhs) - return 0; - // Regard null references as always being "less than" - if (!lhs) - return -1; - if (!rhs) - return 1; - return lhs.opCmp(rhs); + static if (is(C1 == typeof(null))) + static if (is(C2 == typeof(null))) + return 0; + else + return -int(rhs !is null); + else + static if (is(C2 == typeof(null))) + return lhs !is null; + else + { + if (lhs is rhs) + return 0; + // Regard null references as always being "less than" + if (!lhs) + return -1; + if (!rhs) + return 1; + return lhs.opCmp(rhs); + } } // objects @@ -4593,6 +4664,25 @@ if (is(Obj : Object)) assert(a < "я"); } +// Compare struct objects for ordering. +private auto __cmp(T)(auto ref T lhs, auto ref T rhs) +if (is(T == struct)) +{ + static if (is(typeof(lhs.opCmp(rhs)))) + return lhs.opCmp(rhs); + else + { + // Lexicographical compare on members + static foreach (const i; 0 .. lhs.tupleof.length) + {{ + alias F = typeof(lhs.tupleof[i]); + if (int r = __typeid!F.compare(&lhs.tupleof[i], &rhs.tupleof[i])) + return r; + }} + return 0; + } +} + // Used in Exception Handling LSDA tables to 'wrap' C++ type info // so it can be distinguished from D TypeInfo class __cpp_type_info_ptr diff --git a/src/rt/util/typeinfo.d b/src/rt/util/typeinfo.d index ac135e4fc0..f669ee3418 100644 --- a/src/rt/util/typeinfo.d +++ b/src/rt/util/typeinfo.d @@ -284,62 +284,47 @@ unittest F[] a1 = [f1, f1, f1]; F[] a2 = [f2, f2, f2]; - { - auto ti = __typeid!F; - assert(ti.getHash(&f1) == ti.getHash(&f2)); + TypeInfo ti = __typeid!F; + assert(ti.getHash(&f1) == ti.getHash(&f2)); - assert(a1 == a2); - assert(a1 !is a2); - } + assert(a1 == a2); + assert(a1 !is a2); F[][] aa1 = [a1, a1, a1]; F[][] aa2 = [a2, a2, a2]; - { - auto ti = __typeid!(F[]); - assert(ti.getHash(&a1) == ti.getHash(&a2)); + ti = __typeid!(F[]); + assert(ti.getHash(&a1) == ti.getHash(&a2)); - assert(aa1 == aa2); - assert(aa1 !is aa2); - } + assert(aa1 == aa2); + assert(aa1 !is aa2); - { - auto ti = __typeid!(F[][]); - assert(ti.getHash(&aa1) == ti.getHash(&aa2)); + ti = __typeid!(F[][]); + assert(ti.getHash(&aa1) == ti.getHash(&aa2)); - S s1 = {f1}, - s2 = {f2}; - assert(s1 == s2); - assert(s1 !is s2); - } + S s1 = {f1}, + s2 = {f2}; + assert(s1 == s2); + assert(s1 !is s2); S[] da1 = [S(f1), S(f1), S(f1)], da2 = [S(f2), S(f2), S(f2)]; - version(none) - { - auto ti = __typeid!(S); - assert(ti.getHash(&s1) == ti.getHash(&s2)); + ti = __typeid!(S); + assert(ti.getHash(&s1) == ti.getHash(&s2)); - assert(da1 == da2); - assert(da1 !is da2); - } + assert(da1 == da2); + assert(da1 !is da2); - version(none) - { - auto ti = __typeid!(S[]); - assert(ti.getHash(&da1) == ti.getHash(&da2)); + ti = __typeid!(S[]); + assert(ti.getHash(&da1) == ti.getHash(&da2)); - S[3] sa1 = {f1}, - sa2 = {f2}; - assert(sa1 == sa2); - assert(sa1[] !is sa2[]); - } + S[3] sa1 = {f1}, + sa2 = {f2}; + assert(sa1 == sa2); + assert(sa1[] !is sa2[]); - version(none) - { - auto ti = __typeid!(S[3]); - assert(ti.getHash(&sa1) == ti.getHash(&sa2)); - } + ti = __typeid!(S[3]); + assert(ti.getHash(&sa1) == ti.getHash(&sa2)); }(); // imaginary types @@ -350,59 +335,50 @@ unittest f2 = -0.0i; F[] a1 = [f1, f1, f1]; F[] a2 = [f2, f2, f2]; - { - assert(f1 == f2); - assert(f1 !is f2); - auto ti = __typeid!(F); - assert(ti.getHash(&f1) == ti.getHash(&f2)); + assert(f1 == f2); + assert(f1 !is f2); + TypeInfo ti = __typeid!(F); + assert(ti.getHash(&f1) == ti.getHash(&f2)); + + assert(a1 == a2); + assert(a1 !is a2); - assert(a1 == a2); - assert(a1 !is a2); - } F[][] aa1 = [a1, a1, a1]; F[][] aa2 = [a2, a2, a2]; - { - auto ti = __typeid!(F[]); - assert(ti.getHash(&a1) == ti.getHash(&a2)); - assert(aa1 == aa2); - assert(aa1 !is aa2); - } - { - auto ti = __typeid!(F[][]); - assert(ti.getHash(&aa1) == ti.getHash(&aa2)); + ti = __typeid!(F[]); + assert(ti.getHash(&a1) == ti.getHash(&a2)); + + assert(aa1 == aa2); + assert(aa1 !is aa2); + ti = __typeid!(F[][]); + assert(ti.getHash(&aa1) == ti.getHash(&aa2)); + + S s1 = {f1}, + s2 = {f2}; + assert(s1 == s2); + assert(s1 !is s2); - S s1 = {f1}, - s2 = {f2}; - assert(s1 == s2); - assert(s1 !is s2); - } S[] da1 = [S(f1), S(f1), S(f1)], da2 = [S(f2), S(f2), S(f2)]; - version(none) - { - auto ti = __typeid!(S); - assert(ti.getHash(&s1) == ti.getHash(&s2)); - assert(da1 == da2); - assert(da1 !is da2); - } + ti = __typeid!(S); + assert(ti.getHash(&s1) == ti.getHash(&s2)); - version(none) - { - auto ti = __typeid!(S[]); - assert(ti.getHash(&da1) == ti.getHash(&da2)); + assert(da1 == da2); + assert(da1 !is da2); - S[3] sa1 = {f1}, - sa2 = {f2}; - assert(sa1 == sa2); - assert(sa1[] !is sa2[]); - } - version(none) - { - auto ti = __typeid!(S[3]); - assert(ti.getHash(&sa1) == ti.getHash(&sa2)); - }}(); + ti = __typeid!(S[]); + assert(ti.getHash(&da1) == ti.getHash(&da2)); + + S[3] sa1 = {f1}, + sa2 = {f2}; + assert(sa1 == sa2); + assert(sa1[] !is sa2[]); + + ti = __typeid!(S[3]); + assert(ti.getHash(&sa1) == ti.getHash(&sa2)); + }(); // complex types foreach (F; TypeTuple!(cfloat, cdouble, creal)) @@ -417,60 +393,53 @@ unittest { F[] a1 = [f1, f1, f1]; F[] a2 = [f2, f2, f2]; - { - assert(f1 == 0 + 0i); + assert(f1 == 0 + 0i); - assert(f1 == f2); - assert(f1 !is f2); - auto ti = __typeid!(F); - assert(ti.getHash(&f1) == ti.getHash(&f2)); + assert(f1 == f2); + assert(f1 !is f2); + TypeInfo ti = __typeid!(F); + assert(ti.getHash(&f1) == ti.getHash(&f2)); + + assert(a1 == a2); + assert(a1 !is a2); - assert(a1 == a2); - assert(a1 !is a2); - } F[][] aa1 = [a1, a1, a1]; F[][] aa2 = [a2, a2, a2]; - { - auto ti = __typeid!(F[]); - assert(ti.getHash(&a1) == ti.getHash(&a2)); - - assert(aa1 == aa2); - assert(aa1 !is aa2); - } - { - auto ti = __typeid!(F[][]); - assert(ti.getHash(&aa1) == ti.getHash(&aa2)); - - S s1 = {f1}, - s2 = {f2}; - assert(s1 == s2); - assert(s1 !is s2); - } + + ti = __typeid!(F[]); + assert(ti.getHash(&a1) == ti.getHash(&a2)); + + assert(aa1 == aa2); + assert(aa1 !is aa2); + + ti = __typeid!(F[][]); + assert(ti.getHash(&aa1) == ti.getHash(&aa2)); + + S s1 = {f1}, + s2 = {f2}; + assert(s1 == s2); + assert(s1 !is s2); + S[] da1 = [S(f1), S(f1), S(f1)], da2 = [S(f2), S(f2), S(f2)]; - version(none) - { - auto ti = __typeid!(S); - assert(ti.getHash(&s1) == ti.getHash(&s2)); - - assert(da1 == da2); - assert(da1 !is da2); - } + + ti = __typeid!(S); + assert(ti.getHash(&s1) == ti.getHash(&s2)); + + assert(da1 == da2); + assert(da1 !is da2); + S[3] sa1 = {f1}, sa2 = {f2}; - version(none) - { - auto ti = __typeid!(S[]); - assert(ti.getHash(&da1) == ti.getHash(&da2)); - - assert(sa1 == sa2); - assert(sa1[] !is sa2[]); - } - version(none) - { - auto ti = __typeid!(S[3]); - assert(ti.getHash(&sa1) == ti.getHash(&sa2)); - } + + ti = __typeid!(S[]); + assert(ti.getHash(&da1) == ti.getHash(&da2)); + + assert(sa1 == sa2); + assert(sa1[] !is sa2[]); + + ti = __typeid!(S[3]); + assert(ti.getHash(&sa1) == ti.getHash(&sa2)); } }(); } From 250e486fb979f9e54913fac6fa048184286da62e Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Mon, 3 Aug 2020 11:28:56 -0400 Subject: [PATCH 15/19] Some support for classes --- src/object.d | 91 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 14 deletions(-) diff --git a/src/object.d b/src/object.d index 4ef7f4dcbb..631756f106 100644 --- a/src/object.d +++ b/src/object.d @@ -425,7 +425,7 @@ class TypeInfo } /* -Generic implementation of TypeInfo. T must be unqualified. +Generic implementation of TypeInfo. Each distinct type gets its own instantiation. */ private class TypeInfoImpl(T) : TypeInfo { @@ -553,18 +553,23 @@ private class TypeInfoImpl(T) : TypeInfo return 0; if (auto ti = cast(const TypeInfo) rhs) return __cmp(this.toString, ti.toString); - // TypeInfo objects are all "smaller" than all other types. + // TypeInfo objects are all "greater" than all other types. // TODO: devise a principled way to define/justify this. - return -1; + return 1; } + static string toString() @nogc nothrow pure @safe { return T.stringof; } + override string toString() @nogc @safe { return T.stringof; } - final static size_t getHash(scope const UnqualifiedType* p) @nogc pure nothrow + final static size_t getHash(scope const UnqualifiedType* p) nothrow { pragma(inline, true); assert(p); - static if (is(T U == enum)) + static if (is(UnqualifiedType : Object)) + // Cheat constness away + return *p ? (cast(UnqualifiedType) *p).toHash() : 0; + else static if (is(T U == enum)) return hashOf(*cast(const U*) p); else static if (is(immutable T == immutable void) || is(T == function)) return 0; // these aren't hashable @@ -572,7 +577,7 @@ private class TypeInfoImpl(T) : TypeInfo return hashOf(*p); } - override size_t getHash(scope const void* p) @nogc pure nothrow + override size_t getHash(scope const void* p) nothrow { return getHash(cast(const UnqualifiedType *) p); } @@ -582,12 +587,13 @@ private class TypeInfoImpl(T) : TypeInfo static bool equals(in T* lhs, in T* rhs) { pragma(inline, true); + assert(lhs && rhs); static if (is(immutable T == immutable void)) return lhs is rhs; else static if (is(T == __vector(U), U)) return __typeid!U.equals(cast(const U*) lhs, cast(const U*) rhs); else - return *lhs == *rhs; + return *lhs == *rhs; // works for classes, too } override bool equals(in void* lhs, in void* rhs) @@ -598,17 +604,24 @@ private class TypeInfoImpl(T) : TypeInfo static if (!__traits(isAssociativeArray, T) && !is(T == function)) { - final static int compare(in T* lhs, in T* rhs) @nogc + final static int compare(in T* lhs, in T* rhs) { pragma(inline, true); + assert(lhs && rhs); static if (is(T == struct)) { - // Avoid comparing twice for structs + // Avoid comparing twice for structs. Keep this case separate from the one + // below because error messages are clearer. return .__cmp(*lhs, *rhs); } + else static if (is(UnqualifiedType : Object)) + { + // Cheat constness away. + return .__cmp(cast(UnqualifiedType) *lhs, cast(UnqualifiedType) *rhs); + } else static if (is(typeof(.__cmp(*lhs, *rhs)) == int)) { - // Avoid comparing twice for arrays + // This will catch arrays return .__cmp(*lhs, *rhs); } else static if (is(T U == enum)) @@ -630,7 +643,7 @@ private class TypeInfoImpl(T) : TypeInfo return int(*lhs > *rhs) - int(*lhs < *rhs); } - override int compare(in void* p1, in void* p2) @nogc + override int compare(in void* p1, in void* p2) { return compare(cast(const T*) p1, cast(const T*) p2); } @@ -683,6 +696,13 @@ private class TypeInfoImpl(T) : TypeInfo immutable(T)[] result = null; return result.ptr[0 .. 1]; } + else static if (is(T == class)) + { + // Credit: http://dpldocs.info/this-week-in-d/Blog.Posted_2020_07_27.html + pragma(mangle, "_D" ~ T.mangleof[1 .. $] ~ "6__initZ") + extern immutable ubyte[__traits(classInstanceSize, T)] initial; + return initial[]; + } else static if (is(T == function)) { return null; @@ -797,6 +817,11 @@ private class TypeInfoImpl(T) : TypeInfo return result; }(); } + else static if (is(T == class)) + { + private alias NextType = T; + private enum uint _flags = 1; + } else { private alias NextType = T; @@ -813,8 +838,42 @@ private class TypeInfoImpl(T) : TypeInfo override @property uint flags() nothrow @nogc pure @safe { return _flags; } - //const(OffsetTypeInfo)[] offTi() const { return null; } - //void postblit(void* p) const {} + static if (is(T == class)) + override const(OffsetTypeInfo)[] offTi() const + { + assert(0); + } + + @property auto info() @safe nothrow pure const return { return this; } + @property auto typeinfo() @safe nothrow pure const return { return this; } + + // void*[] vtbl; /// virtual function pointer table + // Interface[] interfaces; /// interfaces this class implements + static if (is(T Bases == super)) + { + static if (Bases.length) + { + @property auto base() { return __typeid!(Bases[0]); } + } + } + // void* destructor; + // void function(Object) classInvariant; + // enum ClassFlags : uint + // { + // isCOMclass = 0x1, + // noPointers = 0x2, + // hasOffTi = 0x4, + // hasCtor = 0x8, + // hasGetMembers = 0x10, + // hasTypeInfo = 0x20, + // isAbstract = 0x40, + // isCPPclass = 0x80, + // hasDtor = 0x100, + // } + // ClassFlags m_flags; + // void* deallocator; + // OffsetTypeInfo[] m_offTi; + // void function(Object) defaultConstructor; // default Constructor } /* @@ -952,6 +1011,9 @@ unittest struct S2 { int a; S2* next; } struct S3 { int a; S2[] array; } struct S4 { int a; S2 b; } + + class C1 {} + test!( int, const(int), shared(int), inout(int), immutable(int), inout(const int), shared(const int), inout(shared int), const(inout(shared int)), @@ -962,7 +1024,8 @@ unittest int[42], int[int], __vector(int[4]), int function(double), string delegate(int[]), S1, const S1, shared S1, const inout shared S1, immutable S1, - S2, const S2, shared S2, const inout shared S2, immutable S2 + S2, const S2, shared S2, const inout shared S2, immutable S2, + C1, ); static assert(__typeid!S1.flags == 0); From a129a7f1a24aa2af174302f75a0b8bc76ca03108 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 9 Aug 2020 09:19:22 -0400 Subject: [PATCH 16/19] Implemented as far as I could go for classes --- src/object.d | 264 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 230 insertions(+), 34 deletions(-) diff --git a/src/object.d b/src/object.d index 631756f106..501543f336 100644 --- a/src/object.d +++ b/src/object.d @@ -424,10 +424,68 @@ class TypeInfo @property immutable(void)* rtInfo() nothrow pure const @safe @nogc { return rtinfoHasPointers; } // better safe than sorry } +// For TypeInfoImpl +struct Interface2 +{ + TypeInfo classinfo; /// .classinfo for this interface (not for containing class) + void*[] vtbl; + size_t offset; /// offset to Interface 'this' from Object 'this' +} + +abstract class TypeInfo_Class2 : TypeInfo +{ + @property const(void*)[] vtbl() const @nogc nothrow pure @safe; + @property const(Interface2)[] interfaces() const @nogc nothrow pure @safe; + @property const(TypeInfo_Class2) base() const @nogc nothrow pure @safe; + @property const(void)* destructor() const @nogc nothrow pure @safe; + @property void function(Object) classInvariant() const @nogc nothrow pure @safe; + + enum ClassFlags : uint + { + isCOMclass = 0x1, + noPointers = 0x2, + hasOffTi = 0x4, + hasCtor = 0x8, + hasGetMembers = 0x10, + hasTypeInfo = 0x20, + isAbstract = 0x40, + isCPPclass = 0x80, + hasDtor = 0x100, + } + @property ClassFlags classflags() const @nogc nothrow pure @safe; + + @property const(void)* deallocator() const @nogc nothrow pure @safe; + @property void function(Object) defaultConstructor() const @nogc nothrow pure @safe; + + static const(TypeInfo_Class) find(const scope char[] classname); + Object create() const; + // { + // auto flags = classflags; + // if ((flags & ClassFlags.hasCtor) && !defaultConstructor) + // return null; + // if (flags & ClassFlags.isAbstract) + // return null; + // Object result = _d_newclass(this); + // if ((flags & ClassFlags.hasCtor) && defaultConstructor) + // { + // defaultConstructor(result); + // } + // return result; + // } + + bool isBaseOf(scope const TypeInfo_Class2 child) const @nogc nothrow pure @trusted; +} + +private template Select(bool condition, T1, T2) +{ + static if (condition) alias Select = T1; + else alias Select = T2; +} + /* Generic implementation of TypeInfo. Each distinct type gets its own instantiation. */ -private class TypeInfoImpl(T) : TypeInfo +private class TypeInfoImpl(T) : Select!(is(T == class), TypeInfo_Class2, TypeInfo) { const: // because this type has no state in the first place @@ -614,11 +672,15 @@ private class TypeInfoImpl(T) : TypeInfo // below because error messages are clearer. return .__cmp(*lhs, *rhs); } - else static if (is(UnqualifiedType : Object)) + else static if (is(T == interface) || is(T == class)) { // Cheat constness away. return .__cmp(cast(UnqualifiedType) *lhs, cast(UnqualifiedType) *rhs); } + else static if (is(T == class)) + { + return .__cmp(cast(UnqualifiedType) *lhs, cast(UnqualifiedType) *rhs); + } else static if (is(typeof(.__cmp(*lhs, *rhs)) == int)) { // This will catch arrays @@ -638,9 +700,13 @@ private class TypeInfoImpl(T) : TypeInfo return 0; } else static if (is(T == __vector(U), U)) + { return __typeid!U.compare(cast(const U*) lhs, cast(const U*) rhs); + } else + { return int(*lhs > *rhs) - int(*lhs < *rhs); + } } override int compare(in void* p1, in void* p2) @@ -829,51 +895,145 @@ private class TypeInfoImpl(T) : TypeInfo } static if (!is(T == NextType)) - override @property inout(TypeInfo) next() nothrow @nogc pure inout @safe + override @property inout(TypeInfo) next() nothrow @nogc pure inout @trusted { - return __typeid!NextType; + return cast(typeof(return)) __typeid!NextType; } static @property uint flags() nothrow @nogc pure @safe { return _flags; } override @property uint flags() nothrow @nogc pure @safe { return _flags; } + //////////////////////////////////////////////////////////////////////////// + // Class-specific stuff + //////////////////////////////////////////////////////////////////////////// + static if (is(T == class)) - override const(OffsetTypeInfo)[] offTi() const + { + override Object create() const { - assert(0); + assert(0, "Not implemented"); } - @property auto info() @safe nothrow pure const return { return this; } - @property auto typeinfo() @safe nothrow pure const return { return this; } + const: @nogc: nothrow: pure: @safe: - // void*[] vtbl; /// virtual function pointer table - // Interface[] interfaces; /// interfaces this class implements - static if (is(T Bases == super)) - { - static if (Bases.length) + override const(OffsetTypeInfo)[] offTi() + { + assert(0, "Not implemented"); + } + + override @property const(void*)[] vtbl() { - @property auto base() { return __typeid!(Bases[0]); } + assert(0, "Not implemented"); } + + override @property immutable(Interface2)[] interfaces() + { + static if (is(T Bases == super)) + { + static if (Bases.length > 1) + { + static immutable Interface2[Bases.length - 1] result = { + Interface2[Bases.length - 1] result; + static foreach (i; 1 .. Bases.length) + { + result[i - 1].classinfo = __typeid!(Bases[i]); + // result[i - 1].vtbl = ? + // result[i - 1].offset = ? + } + return result; + }(); + return result; + } + else + { + return null; + } + } + else + { + return null; + } + } + + override @property const(TypeInfo_Class2) base() + { + static if (is(T Bases == super)) + { + static if (Bases.length) + return __typeid!(Bases[0]); + else + return null; + } + } + + override @property const(void)* destructor() + { + assert(0, "Not implemented"); + } + + override @property void function(Object) classInvariant() + { + assert(0, "Not implemented"); + } + + override @property TypeInfo_Class2.ClassFlags classflags() + { + assert(0, "Not implemented"); + } + + override @property const(void)* deallocator() + { + assert(0, "Not implemented"); + } + + override @property void function(Object) defaultConstructor() + { + assert(0, "Not implemented"); + } + override bool isBaseOf(scope const TypeInfo_Class2 child) + { + assert(0, "Not implemented"); + } + + // Class flags + static if (is(T == class) && is(typeof(T.tupleof.length))) + private enum bool noPointers = { + // If at least one field has pointers, the class has pointers. + // This loop goes all the way. Otherwise: "unreachable code" warnings. + uint result = true; + static foreach (const i; 0 .. T.tupleof.length) + {{ + alias U = typeof(T.tupleof[i]); + static if (is(U == class) || is(U == interface)) + result = false; + else static if (__typeid!U.flags) + result = false; + }} + return result; + }(); + else + private enum bool noPointers = _flags & 1; + //private enum bool hasOffTi = ? + private enum bool hasCtor = is(typeof(&T.__ctor)); + //private enum bool hasGetMembers = ?; + //private enum bool hasTypeInfo = ?; + //private enum bool isAbstract = ?; + private enum bool isCPPclass = is(T == class) && !is(UnqualifiedType : Object); + private enum bool hasDtor = is(T == class) && is(typeof(&T.__dtor)); + + static enum ClassFlags m_flags = cast(ClassFlags) ( + //(-isCOMclass & ClassFlags.isCOMclass) | + (-uint(noPointers) & ClassFlags.noPointers) | + //(-uint(hasOffTi) & ClassFlags.hasOffTi) | + (-uint(hasCtor) & ClassFlags.hasCtor) | + //(-hasGetMembers & ClassFlags.hasGetMembers) | + //(-hasTypeInfo & ClassFlags.hasTypeInfo) | + //(-isAbstract & ClassFlags.isAbstract) | + (-uint(isCPPclass) & ClassFlags.isCPPclass) | + (-uint(hasDtor) & ClassFlags.hasDtor) + ); } - // void* destructor; - // void function(Object) classInvariant; - // enum ClassFlags : uint - // { - // isCOMclass = 0x1, - // noPointers = 0x2, - // hasOffTi = 0x4, - // hasCtor = 0x8, - // hasGetMembers = 0x10, - // hasTypeInfo = 0x20, - // isAbstract = 0x40, - // isCPPclass = 0x80, - // hasDtor = 0x100, - // } - // ClassFlags m_flags; - // void* deallocator; - // OffsetTypeInfo[] m_offTi; - // void function(Object) defaultConstructor; // default Constructor } /* @@ -1001,6 +1161,14 @@ unittest assert(id.rtInfo == RTInfo!T); assert(id2.rtInfo == RTInfo!T); + + static if (is(T == class)) + { + auto cid = __typeid!T; + TypeInfo_Class2 cid2 = __typeid!T; + assert(cid.base == cid2.base); + assert(cid.interfaces == cid2.interfaces); + } } } enum E1 { a, b } @@ -1013,6 +1181,19 @@ unittest struct S4 { int a; S2 b; } class C1 {} + extern(C++) class C2 {} + + interface I1 { + void fun(); + } + interface I2 { + void gun(); + } + class C3 : I1, I2 { + void fun() {} + void gun() {} + double x; + } test!( int, const(int), shared(int), inout(int), immutable(int), @@ -1025,7 +1206,7 @@ unittest int function(double), string delegate(int[]), S1, const S1, shared S1, const inout shared S1, immutable S1, S2, const S2, shared S2, const inout shared S2, immutable S2, - C1, + C1, C3, //C2, ); static assert(__typeid!S1.flags == 0); @@ -4643,7 +4824,7 @@ public import core.internal.switch_: __switch_error; public @trusted @nogc nothrow pure extern (C) void _d_delThrowable(scope Throwable); -// Compare class and interface objects for ordering. +// Compare class objects for ordering. private int __cmp(C1, C2)(C1 lhs, C2 rhs) if (is(C1 : Object) && is(C2 : Object)) { @@ -4694,6 +4875,21 @@ if (is(C1 : Object) && is(C2 : Object)) assert(__cmp([c2, c2], [c1, c1]) > 0); } +// Compare interfaces for ordering +private int __cmp(C1, C2)(C1 lhs, C2 rhs) +if (is(C1 == interface) || is(C2 == interface)) +{ + static if (is(C1 == interface)) + auto lhso = cast(Object) lhs; + else + alias lhso = lhs; + static if (is(C2 == interface)) + auto rhso = cast(Object) rhs; + else + alias rhso = rhs; + return .__cmp(lhso, rhso); +} + // structs @safe unittest { From 19e0358f72fd2505d623339edbda78f1ee58220f Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 9 Aug 2020 20:42:30 -0400 Subject: [PATCH 17/19] Some interfaces testing --- src/object.d | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/object.d b/src/object.d index 501543f336..22230439d3 100644 --- a/src/object.d +++ b/src/object.d @@ -383,8 +383,17 @@ class TypeInfo } } - /** Get TypeInfo for 'next' type, as defined by what kind of type this is, - null if none. */ + /** + Get TypeInfo for 'next' type, as defined by what kind of type this is, + null if none. + + For `enum` types, `next()` returns the `typeid` of the base type of the `enum`. + For pointer types, `next()` returns the `typeid` of the pointee type. + For array types (static and dynamic), `next()` returns the `typeid` of the element type. + For associative arrays types, `next()` returns the `typeid` of the value type. + For vector types, `next()` returns the `typeid` of the underlying type. + For qualified types, `next()` returns the `typeid` of the unqualified type. + */ @property inout(TypeInfo) next() nothrow pure inout @nogc { return null; } /** @@ -627,6 +636,8 @@ private class TypeInfoImpl(T) : Select!(is(T == class), TypeInfo_Class2, TypeInf static if (is(UnqualifiedType : Object)) // Cheat constness away return *p ? (cast(UnqualifiedType) *p).toHash() : 0; + else static if (is(T == interface)) + return *p ? (cast(Object) *p).toHash() : 0; else static if (is(T U == enum)) return hashOf(*cast(const U*) p); else static if (is(immutable T == immutable void) || is(T == function)) @@ -651,7 +662,7 @@ private class TypeInfoImpl(T) : Select!(is(T == class), TypeInfo_Class2, TypeInf else static if (is(T == __vector(U), U)) return __typeid!U.equals(cast(const U*) lhs, cast(const U*) rhs); else - return *lhs == *rhs; // works for classes, too + return *lhs == *rhs; // works for classes and interfaces, too } override bool equals(in void* lhs, in void* rhs) @@ -883,7 +894,7 @@ private class TypeInfoImpl(T) : Select!(is(T == class), TypeInfo_Class2, TypeInf return result; }(); } - else static if (is(T == class)) + else static if (is(T == class) || is(T == interface)) { private alias NextType = T; private enum uint _flags = 1; @@ -1042,7 +1053,7 @@ Run-time type information for scalar types (int for now). template __typeid(T) { // On-demand singleton object in static storage - private immutable singleton = new TypeInfoImpl!T; + private immutable singleton = new immutable TypeInfoImpl!T; // Get rid of the immutable qualifier for convenience; the type has no state anyway @property @trusted pure @nogc nothrow TypeInfoImpl!T __typeid() { @@ -1206,7 +1217,7 @@ unittest int function(double), string delegate(int[]), S1, const S1, shared S1, const inout shared S1, immutable S1, S2, const S2, shared S2, const inout shared S2, immutable S2, - C1, C3, //C2, + C1, C3, I1, I2, ); static assert(__typeid!S1.flags == 0); From 007b307a3d9b886e61cd76c255af94359f0968b6 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 9 Aug 2020 21:55:51 -0400 Subject: [PATCH 18/19] Implement isAbstract --- src/object.d | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/object.d b/src/object.d index 22230439d3..d832fefbbc 100644 --- a/src/object.d +++ b/src/object.d @@ -1008,6 +1008,7 @@ private class TypeInfoImpl(T) : Select!(is(T == class), TypeInfo_Class2, TypeInf } // Class flags + //private enum bool isCOMclass = ? static if (is(T == class) && is(typeof(T.tupleof.length))) private enum bool noPointers = { // If at least one field has pointers, the class has pointers. @@ -1029,18 +1030,18 @@ private class TypeInfoImpl(T) : Select!(is(T == class), TypeInfo_Class2, TypeInf private enum bool hasCtor = is(typeof(&T.__ctor)); //private enum bool hasGetMembers = ?; //private enum bool hasTypeInfo = ?; - //private enum bool isAbstract = ?; + private enum bool isAbstract = __traits(isAbstractClass, T); private enum bool isCPPclass = is(T == class) && !is(UnqualifiedType : Object); private enum bool hasDtor = is(T == class) && is(typeof(&T.__dtor)); static enum ClassFlags m_flags = cast(ClassFlags) ( - //(-isCOMclass & ClassFlags.isCOMclass) | + //(-uint(isCOMclass) & ClassFlags.isCOMclass) | (-uint(noPointers) & ClassFlags.noPointers) | //(-uint(hasOffTi) & ClassFlags.hasOffTi) | (-uint(hasCtor) & ClassFlags.hasCtor) | - //(-hasGetMembers & ClassFlags.hasGetMembers) | - //(-hasTypeInfo & ClassFlags.hasTypeInfo) | - //(-isAbstract & ClassFlags.isAbstract) | + //(-uint(hasGetMembers) & ClassFlags.hasGetMembers) | + //(-uint(hasTypeInfo) & ClassFlags.hasTypeInfo) | + (-uint(isAbstract) & ClassFlags.isAbstract) | (-uint(isCPPclass) & ClassFlags.isCPPclass) | (-uint(hasDtor) & ClassFlags.hasDtor) ); From bb59bf37f9e86e66fbc6953b8cbf2abebb5ab00f Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Mon, 10 Aug 2020 14:03:28 -0400 Subject: [PATCH 19/19] classflags implementation is trivial --- src/object.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/object.d b/src/object.d index d832fefbbc..42f58b6f16 100644 --- a/src/object.d +++ b/src/object.d @@ -990,7 +990,7 @@ private class TypeInfoImpl(T) : Select!(is(T == class), TypeInfo_Class2, TypeInf override @property TypeInfo_Class2.ClassFlags classflags() { - assert(0, "Not implemented"); + return m_flags; } override @property const(void)* deallocator()