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

Commit 25e93d5

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 396a0ec commit 25e93d5

File tree

3 files changed

+92
-1
lines changed

3 files changed

+92
-1
lines changed

src/object.d

+45
Original file line numberDiff line numberDiff line change
@@ -1736,6 +1736,42 @@ class TypeInfo_Class : TypeInfo
17361736
}
17371737
return o;
17381738
}
1739+
1740+
/**
1741+
* Returns true if the class described by this TypeInfo_Class derives from
1742+
* the interface described by `parent`.
1743+
*/
1744+
bool isDerivedFrom(scope const TypeInfo_Interface parent) const @nogc nothrow pure @safe
1745+
{
1746+
return parent !is null && interfaceConvertibleFrom(parent.info, this);
1747+
}
1748+
1749+
/**
1750+
* Returns true if the class described by this TypeInfo_Class describes a class that
1751+
* derives from the interface described by this TypeInfo_Interface.
1752+
*/
1753+
bool isDerivedFrom(scope const TypeInfo_Class parent) const @nogc nothrow pure @safe
1754+
{
1755+
if (parent is null)
1756+
return false;
1757+
if (parent is this || parent.name == this.name)
1758+
return true;
1759+
return this.base !is null && this.base.isDerivedFrom(parent);
1760+
}
1761+
1762+
private static bool interfaceConvertibleFrom(scope const TypeInfo_Class interfaceInfo,
1763+
scope const TypeInfo_Class ti) @nogc nothrow pure @safe
1764+
{
1765+
if (interfaceInfo is ti || interfaceInfo.name == ti.name)
1766+
return true;
1767+
foreach (interface_; ti.interfaces)
1768+
if (interface_.classinfo is interfaceInfo || interface_.classinfo.name == interfaceInfo.name)
1769+
return true;
1770+
foreach (interface_; ti.interfaces)
1771+
if (interfaceConvertibleFrom(interfaceInfo, interface_.classinfo))
1772+
return true;
1773+
return ti.base is null ? false : interfaceConvertibleFrom(interfaceInfo, ti.base);
1774+
}
17391775
}
17401776

17411777
alias ClassInfo = TypeInfo_Class;
@@ -1825,6 +1861,15 @@ class TypeInfo_Interface : TypeInfo
18251861
override @property uint flags() nothrow pure const { return 1; }
18261862

18271863
TypeInfo_Class info;
1864+
1865+
/**
1866+
* Returns true if the interface described by this TypeInfo_Interface
1867+
* derives from the interface described by `parent`.
1868+
*/
1869+
bool isDerivedFrom(scope const TypeInfo_Interface parent) const @nogc nothrow pure @safe
1870+
{
1871+
return parent !is null && TypeInfo_Class.interfaceConvertibleFrom(parent.info, this.info);
1872+
}
18281873
}
18291874

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