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

Commit bc10972

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 3cb4a8c commit bc10972

File tree

5 files changed

+137
-3
lines changed

5 files changed

+137
-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
@@ -960,6 +960,8 @@ class TypeInfo_Delegate : TypeInfo
960960
}
961961

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

964966
/**
965967
* Runtime type information about a class.
@@ -1103,6 +1105,36 @@ class TypeInfo_Class : TypeInfo
11031105
}
11041106
return o;
11051107
}
1108+
1109+
/**
1110+
* Returns true if the class described by `child` derives from or is
1111+
* the class described by this `TypeInfo_Class`. Always returns false
1112+
* if the argument is null.
1113+
*
1114+
* Params:
1115+
* child = TypeInfo for some class
1116+
* Returns:
1117+
* true if the class described by `child` derives from or is the
1118+
* class described by this `TypeInfo_Class`.
1119+
*/
1120+
final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
1121+
{
1122+
if (m_init.length)
1123+
{
1124+
// If this TypeInfo_Class represents an actual class we only need
1125+
// to check the child and its direct ancestors.
1126+
for (auto ti = cast() child; ti !is null; ti = ti.base)
1127+
if (ti is this)
1128+
return true;
1129+
return false;
1130+
}
1131+
else
1132+
{
1133+
// If this TypeInfo_Class is the .info field of a TypeInfo_Interface
1134+
// we also need to recursively check the child's interfaces.
1135+
return child !is null && _d_isbaseof(cast() child, this);
1136+
}
1137+
}
11061138
}
11071139

11081140
alias ClassInfo = TypeInfo_Class;
@@ -1192,6 +1224,38 @@ class TypeInfo_Interface : TypeInfo
11921224
override @property uint flags() nothrow pure const { return 1; }
11931225

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

11971261
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

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// https://issues.dlang.org/show_bug.cgi?id=20178
2+
3+
interface I {}
4+
interface J : I {}
5+
interface K(T) {}
6+
class C1 : I {}
7+
class C2 : C1 {}
8+
class C3 : J {}
9+
class C4(T) : C3, K!T {}
10+
class C5(T) : C4!T {}
11+
12+
void main() @nogc nothrow pure @safe
13+
{
14+
assert(typeid(C1).isBaseOf(typeid(C1)));
15+
assert(typeid(C1).isBaseOf(typeid(C2)));
16+
17+
assert(!typeid(C2).isBaseOf(typeid(C1)));
18+
assert(typeid(C2).isBaseOf(typeid(C2)));
19+
20+
assert(!typeid(C1).isBaseOf(typeid(Object)));
21+
assert(!typeid(C2).isBaseOf(typeid(Object)));
22+
assert(typeid(Object).isBaseOf(typeid(C1)));
23+
assert(typeid(Object).isBaseOf(typeid(C2)));
24+
25+
assert(typeid(I).isBaseOf(typeid(I)));
26+
assert(typeid(I).isBaseOf(typeid(J)));
27+
assert(typeid(I).isBaseOf(typeid(C1)));
28+
assert(typeid(I).isBaseOf(typeid(C2)));
29+
assert(typeid(I).isBaseOf(typeid(C3)));
30+
assert(!typeid(I).isBaseOf(typeid(Object)));
31+
32+
assert(!typeid(J).isBaseOf(typeid(I)));
33+
assert(typeid(J).isBaseOf(typeid(J)));
34+
assert(!typeid(J).isBaseOf(typeid(C1)));
35+
assert(!typeid(J).isBaseOf(typeid(C2)));
36+
assert(typeid(J).isBaseOf(typeid(C3)));
37+
assert(!typeid(J).isBaseOf(typeid(Object)));
38+
39+
assert(typeid(C4!int).isBaseOf(typeid(C5!int)));
40+
assert(typeid(K!int).isBaseOf(typeid(C5!int)));
41+
assert(!typeid(C4!Object).isBaseOf(typeid(C5!int)));
42+
assert(!typeid(K!Object).isBaseOf(typeid(C5!int)));
43+
44+
static assert(!__traits(compiles, TypeInfo.init.isBaseOf(typeid(C1))));
45+
static assert(!__traits(compiles, typeid(C1).isBaseOf(TypeInfo.init)));
46+
}

0 commit comments

Comments
 (0)