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

Commit 7d20e09

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 7d20e09

File tree

5 files changed

+134
-3
lines changed

5 files changed

+134
-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();
17+
18+
assert(typeid(a).isBaseOf(typeid(a)));
19+
assert(typeid(a).isBaseOf(typeid(b)));
20+
assert(!typeid(b).isBaseOf(typeid(a)));
21+
-------

src/object.d

+64
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,36 @@ 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+
if (m_init.length)
1122+
{
1123+
// If this TypeInfo_Class represents an actual class we only need
1124+
// to check the child and its direct ancestors.
1125+
for (auto ti = cast() child; ti !is null; ti = ti.base)
1126+
if (ti is this)
1127+
return true;
1128+
return false;
1129+
}
1130+
else
1131+
{
1132+
// If this TypeInfo_Class is the .info field of a TypeInfo_Interface
1133+
// we also need to recursively check the child's interfaces.
1134+
return child !is null && _d_isbaseof(cast() child, this);
1135+
}
1136+
}
11051137
}
11061138

11071139
alias ClassInfo = TypeInfo_Class;
@@ -1191,6 +1223,38 @@ class TypeInfo_Interface : TypeInfo
11911223
override @property uint flags() nothrow pure const { return 1; }
11921224

11931225
TypeInfo_Class info;
1226+
1227+
/**
1228+
* Returns true if the class described by `child` derives from the
1229+
* interface described by this `TypeInfo_Interface`. Always returns
1230+
* false if the argument is null.
1231+
*
1232+
* Params:
1233+
* child = TypeInfo for some class
1234+
* Returns:
1235+
* true if the class described by `child` derives from the
1236+
* interface described by this `TypeInfo_Interface`.
1237+
*/
1238+
final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
1239+
{
1240+
return child !is null && cast(bool) _d_isbaseof(cast() child, this.info);
1241+
}
1242+
1243+
/**
1244+
* Returns true if the interface described by `child` derives from
1245+
* or is the interface described by this `TypeInfo_Interface`.
1246+
* Always returns false if the argument is null.
1247+
*
1248+
* Params:
1249+
* child = TypeInfo for some interface
1250+
* Returns:
1251+
* true if the interface described by `child` derives from or is
1252+
* the interface described by this `TypeInfo_Interface`.
1253+
*/
1254+
final bool isBaseOf(scope const TypeInfo_Interface child) const @nogc nothrow pure @trusted
1255+
{
1256+
return child !is null && cast(bool) _d_isbaseof(cast() child.info, this.info);
1257+
}
11941258
}
11951259

11961260
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)