Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit 283cfaf

Browse files
committed
Fix Issue 20178 - Add TypeInfo_Class/TypeInfo_Interface.isBaseOf
Equivalent to C#/Java isAssignableFrom. Naming the method "isAssignableFrom" would be more familiar to people coming from C#/Java but is potentially misleading: "alias this" and overloadable opAssign mean that this would not actually indicate whether values of one type could be assigned to another. Adding qualifiers to rt.cast_ functions.
1 parent 90eca1f commit 283cfaf

File tree

5 files changed

+123
-3
lines changed

5 files changed

+123
-3
lines changed

changelog/isbaseof.dd

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Added TypeInfo_Class/TypeInfo_Interface.isBaseOf that works like C#/Java isAssignableFrom.
2+
3+
`TypeInfo_Class.isBaseOf` returns true if the argument and the receiver
4+
are equal or if the class represented by the argument inherits from the
5+
class represented by the receiver. This is called `isBaseOf` instead of
6+
`isAssignableFrom` to avoid confusion for classes that overload
7+
`opAssign` and so may allow assignment from classes outside their
8+
inheritance hierarchy and to match existing terminology in the D
9+
runtime. `TypeInfo_Interface.isBaseOf` is similar with the addition
10+
that the argument may be either `TypeInfo_Class` or
11+
`TypeInfo_Interface`.
12+
-------
13+
class ClassA {}
14+
class ClassB : ClassA {}
15+
16+
auto a = new ClassA(), b = new ClassB(), c = new Object();
17+
18+
assert(typeid(a).isBaseOf(typeid(a)));
19+
assert(typeid(a).isBaseOf(typeid(b)));
20+
assert(!typeid(a).isBaseOf(typeid(c)));
21+
-------

src/object.d

+53
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,8 @@ class TypeInfo_Delegate : TypeInfo
959959
}
960960

961961
private extern (C) Object _d_newclass(const TypeInfo_Class ci);
962+
private extern (C) int _d_isbaseof(scope TypeInfo_Class child,
963+
scope const TypeInfo_Class parent) @nogc nothrow pure @safe; // rt.cast_
962964

963965
/**
964966
* Runtime type information about a class.
@@ -1102,6 +1104,25 @@ class TypeInfo_Class : TypeInfo
11021104
}
11031105
return o;
11041106
}
1107+
1108+
/**
1109+
* Returns true if the class described by `child` derives from or is
1110+
* the class described by this `TypeInfo_Class`. Always returns false
1111+
* if the argument is null.
1112+
*
1113+
* Params:
1114+
* child = TypeInfo for some class
1115+
* Returns:
1116+
* true if the class described by `child` derives from or is the
1117+
* class described by this `TypeInfo_Class`.
1118+
*/
1119+
final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
1120+
{
1121+
for (auto ti = cast() child; ti !is null; ti = ti.base)
1122+
if (ti is this)
1123+
return true;
1124+
return false;
1125+
}
11051126
}
11061127

11071128
alias ClassInfo = TypeInfo_Class;
@@ -1191,6 +1212,38 @@ class TypeInfo_Interface : TypeInfo
11911212
override @property uint flags() nothrow pure const { return 1; }
11921213

11931214
TypeInfo_Class info;
1215+
1216+
/**
1217+
* Returns true if the class described by `child` derives from the
1218+
* interface described by this `TypeInfo_Interface`. Always returns
1219+
* false if the argument is null.
1220+
*
1221+
* Params:
1222+
* child = TypeInfo for some class
1223+
* Returns:
1224+
* true if the class described by `child` derives from the
1225+
* interface described by this `TypeInfo_Interface`.
1226+
*/
1227+
final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
1228+
{
1229+
return child !is null && cast(bool) _d_isbaseof(cast() child, this.info);
1230+
}
1231+
1232+
/**
1233+
* Returns true if the interface described by `child` derives from
1234+
* or is the interface described by this `TypeInfo_Interface`.
1235+
* Always returns false if the argument is null.
1236+
*
1237+
* Params:
1238+
* child = TypeInfo for some interface
1239+
* Returns:
1240+
* true if the interface described by `child` derives from or is
1241+
* the interface described by this `TypeInfo_Interface`.
1242+
*/
1243+
final bool isBaseOf(scope const TypeInfo_Interface child) const @nogc nothrow pure @trusted
1244+
{
1245+
return child !is null && cast(bool) _d_isbaseof(cast() child.info, this.info);
1246+
}
11941247
}
11951248

11961249
class TypeInfo_Struct : TypeInfo

src/rt/cast_.d

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
module rt.cast_;
1515

1616
extern (C):
17+
@nogc:
18+
nothrow:
19+
pure:
1720

1821
/******************************************
1922
* Given a pointer:
@@ -74,7 +77,7 @@ void* _d_dynamic_cast(Object o, ClassInfo c)
7477
return res;
7578
}
7679

77-
int _d_isbaseof2(ClassInfo oc, ClassInfo c, ref size_t offset)
80+
int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe
7881
{
7982
if (oc is c)
8083
return true;
@@ -101,7 +104,7 @@ int _d_isbaseof2(ClassInfo oc, ClassInfo c, ref size_t offset)
101104
return false;
102105
}
103106

104-
int _d_isbaseof(ClassInfo oc, ClassInfo c)
107+
int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe
105108
{
106109
if (oc is c)
107110
return true;

test/typeinfo/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
include ../common.mak
22

3-
TESTS:=comparison
3+
TESTS:=comparison isbaseof
44

55
.PHONY: all clean
66
all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS)))

test/typeinfo/src/isbaseof.d

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// https://issues.dlang.org/show_bug.cgi?id=20178
2+
3+
interface I {}
4+
interface J : I {}
5+
class C1 : I {}
6+
class C2 : C1 {}
7+
class C3 : J {}
8+
9+
void main() @nogc nothrow pure @safe
10+
{
11+
assert(typeid(C1).isBaseOf(typeid(C1)));
12+
assert(typeid(C1).isBaseOf(typeid(C2)));
13+
14+
assert(!typeid(C2).isBaseOf(typeid(C1)));
15+
assert(typeid(C2).isBaseOf(typeid(C2)));
16+
17+
assert(!typeid(C1).isBaseOf(typeid(Object)));
18+
assert(!typeid(C2).isBaseOf(typeid(Object)));
19+
assert(typeid(Object).isBaseOf(typeid(C1)));
20+
assert(typeid(Object).isBaseOf(typeid(C2)));
21+
22+
assert(typeid(I).isBaseOf(typeid(I)));
23+
assert(typeid(I).isBaseOf(typeid(J)));
24+
assert(typeid(I).isBaseOf(typeid(C1)));
25+
assert(typeid(I).isBaseOf(typeid(C2)));
26+
assert(typeid(I).isBaseOf(typeid(C3)));
27+
assert(!typeid(I).isBaseOf(typeid(Object)));
28+
29+
assert(!typeid(J).isBaseOf(typeid(I)));
30+
assert(typeid(J).isBaseOf(typeid(J)));
31+
assert(!typeid(J).isBaseOf(typeid(C1)));
32+
assert(!typeid(J).isBaseOf(typeid(C2)));
33+
assert(typeid(J).isBaseOf(typeid(C3)));
34+
assert(!typeid(J).isBaseOf(typeid(Object)));
35+
36+
// Because isBaseOf is final it currently doesn't automatically
37+
// segfault when the receiver is null. Verify it doesn't return
38+
// true when the receiver and argument are both null. (Segfaulting
39+
// would also be an acceptable behavior.)
40+
assert(!(cast(TypeInfo_Class) null).isBaseOf(null));
41+
assert(!(cast(TypeInfo_Interface) null).isBaseOf(cast(TypeInfo_Interface) null));
42+
assert(!(cast(TypeInfo_Interface) null).isBaseOf(cast(TypeInfo_Class) null));
43+
}

0 commit comments

Comments
 (0)