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

Commit d567a8b

Browse files
committed
Fix Issue 20178 - Add TypeInfo_Class/TypeInfo_Interface.isDerivedFrom
Equivalent to C#/Java isAssignableFrom with the argument swapped with the receiver. 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.
1 parent a2ee5cc commit d567a8b

File tree

3 files changed

+117
-1
lines changed

3 files changed

+117
-1
lines changed

src/object.d

+70
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,61 @@ class TypeInfo_Class : TypeInfo
11021102
}
11031103
return o;
11041104
}
1105+
1106+
/**
1107+
* Returns true if the class described by this TypeInfo_Class derives from
1108+
* the interface described by `parent`.
1109+
*
1110+
* Params:
1111+
* parent = TypeInfo for some interface
1112+
* Returns:
1113+
* true if the class described by this TypeInfo_Class derives from the
1114+
* interface described by `parent`, false otherwise.
1115+
*/
1116+
bool isDerivedFrom(scope const TypeInfo_Interface parent) const @nogc nothrow pure @safe
1117+
{
1118+
return parent !is null && interfaceConvertibleFrom(parent.info, this);
1119+
}
1120+
1121+
/**
1122+
* Returns true if the class described by this TypeInfo_Class derives from
1123+
* the class described by `parent`.
1124+
*
1125+
* Params:
1126+
* parent = TypeInfo for some class
1127+
* Returns:
1128+
* true if the class described by this TypeInfo_Class derives from the
1129+
* class described by `parent`, false otherwise.
1130+
*/
1131+
bool isDerivedFrom(scope const TypeInfo_Class parent) const @nogc nothrow pure @safe
1132+
{
1133+
if (parent is null)
1134+
return false;
1135+
if (parent is this || parent.name == this.name)
1136+
return true;
1137+
return this.base !is null && this.base.isDerivedFrom(parent);
1138+
}
1139+
1140+
/*
1141+
* Params:
1142+
* interfaceInfo = the classinfo of the maybe-parent interface
1143+
* ti = the classinfo of the maybe-child interface
1144+
* Returns:
1145+
* true if `ti` is derived from `interfaceInfo`, false otherwise
1146+
*/
1147+
private static bool interfaceConvertibleFrom(scope const TypeInfo_Class interfaceInfo,
1148+
scope const TypeInfo_Class ti) @nogc nothrow pure @safe
1149+
{
1150+
if (interfaceInfo is ti || interfaceInfo.name == ti.name)
1151+
return true;
1152+
foreach (interface_; ti.interfaces)
1153+
if (interface_.classinfo is interfaceInfo || interface_.classinfo.name == interfaceInfo.name)
1154+
return true;
1155+
foreach (interface_; ti.interfaces)
1156+
if (interfaceConvertibleFrom(interfaceInfo, interface_.classinfo))
1157+
return true;
1158+
return ti.base is null ? false : interfaceConvertibleFrom(interfaceInfo, ti.base);
1159+
}
11051160
}
11061161

11071162
alias ClassInfo = TypeInfo_Class;
@@ -1191,6 +1246,21 @@ class TypeInfo_Interface : TypeInfo
11911246
override @property uint flags() nothrow pure const { return 1; }
11921247

11931248
TypeInfo_Class info;
1249+
1250+
/**
1251+
* Returns true if the interface described by this TypeInfo_Interface
1252+
* derives from the interface described by `parent`.
1253+
*
1254+
* Params:
1255+
* parent = TypeInfo for some interface
1256+
* Returns:
1257+
* true if the interface described by this TypeInfo_Interface derives from
1258+
* the interface described by `parent`, false otherwise.
1259+
*/
1260+
bool isDerivedFrom(scope const TypeInfo_Interface parent) const @nogc nothrow pure @safe
1261+
{
1262+
return parent !is null && TypeInfo_Class.interfaceConvertibleFrom(parent.info, this.info);
1263+
}
11941264
}
11951265

11961266
class TypeInfo_Struct : TypeInfo

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 isderivedfrom
44

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

test/typeinfo/src/isderivedfrom.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+
class C1 : I {}
6+
class C2 : C1 {}
7+
class C3 : J {}
8+
9+
// It might seem sensible to give TypeInfo_Class & TypeInfo_Interface
10+
// a method called "isAssignableFrom" instead of "isDerivedFrom" since
11+
// that matches C# and Java. However, the existences of "alias this" and
12+
// overloadable "opAssign" mean that that this would not actually indicate
13+
// whether arguments of the argument type could be assigned to a field
14+
// whose value is the receiver's type.
15+
bool isAssignableFrom(T,U)(T parent, U child)
16+
{
17+
return child.isDerivedFrom(parent);
18+
}
19+
20+
void main() @nogc nothrow pure @safe
21+
{
22+
assert(typeid(C1).isAssignableFrom(typeid(C1)));
23+
assert(typeid(C1).isAssignableFrom(typeid(C2)));
24+
25+
assert(!typeid(C2).isAssignableFrom(typeid(C1)));
26+
assert(typeid(C2).isAssignableFrom(typeid(C2)));
27+
28+
assert(!typeid(C1).isAssignableFrom(typeid(Object)));
29+
assert(!typeid(C2).isAssignableFrom(typeid(Object)));
30+
assert(typeid(Object).isAssignableFrom(typeid(C1)));
31+
assert(typeid(Object).isAssignableFrom(typeid(C2)));
32+
33+
assert(typeid(I).isAssignableFrom(typeid(I)));
34+
assert(typeid(I).isAssignableFrom(typeid(J)));
35+
assert(typeid(I).isAssignableFrom(typeid(C1)));
36+
assert(typeid(I).isAssignableFrom(typeid(C2)));
37+
assert(typeid(I).isAssignableFrom(typeid(C3)));
38+
assert(!typeid(I).isAssignableFrom(typeid(Object)));
39+
40+
assert(!typeid(J).isAssignableFrom(typeid(I)));
41+
assert(typeid(J).isAssignableFrom(typeid(J)));
42+
assert(!typeid(J).isAssignableFrom(typeid(C1)));
43+
assert(!typeid(J).isAssignableFrom(typeid(C2)));
44+
assert(typeid(J).isAssignableFrom(typeid(C3)));
45+
assert(!typeid(J).isAssignableFrom(typeid(Object)));
46+
}

0 commit comments

Comments
 (0)