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

Give TypeInfo_Class/TypeInfo_Interface.isBaseOf like C#/Java isAssignableFrom #2770

Merged
merged 1 commit into from Apr 16, 2020

Conversation

n8sh
Copy link
Member

@n8sh n8sh commented Aug 29, 2019

This is 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 (and I would find it convenient since it's one fewer difference to remember between languages) 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.

I used the name "isDerivedFrom" since the phrase "derives from" appears in the D online documentation. Swapping the argument order is not ideal, so if someone can think of a sensible name that would work with the same argument order as "isAssignableFrom" please suggest one.

@dlang-bot
Copy link
Contributor

dlang-bot commented Aug 29, 2019

Thanks for your pull request, @n8sh!

Bugzilla references

Auto-close Bugzilla Severity Description
20178 enhancement Add TypeInfo_Class/TypeInfo_Interface.isBaseOf (equivalent to C#/Java isAssignableFrom)

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + druntime#2770"

@dlang-bot dlang-bot added the Enhancement New functionality label Aug 29, 2019
src/object.d Outdated Show resolved Hide resolved
src/object.d Outdated
{
if (parent is null)
return false;
if (parent is this || parent.name == this.name)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use if (parent is this || parent.name == this.name) instead of opEquals because TypeInfo_Class.opEquals isn't marked as any of nogc/nothrow/pure/safe and isn't callable on a const argument or with a const receiver.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dynamic casting just compares TypeInfo pointers, so if you don't expect copies of TypeInfo being made at runtime, you don't have to compare names. You might also just call rt.cast_._d_isbaseof.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If pointer comparison is good enough for dynamic casting then it is good enough for this purpose too. Thank you for the tip regarding _d_isbaseof.

src/object.d Outdated Show resolved Hide resolved
src/object.d Outdated Show resolved Hide resolved
@JinShil
Copy link
Contributor

JinShil commented Aug 29, 2019

Seems like a good idea to me. I'm not sure about the implementation though; is name reliable for comparisons? I think the tests could be filled out with more corner cases (e.g. packages, nested types, multiple interface inheritance, etc.)

@n8sh n8sh force-pushed the issue-20178 branch 3 times, most recently from 976fea2 to e0229ca Compare August 29, 2019 02:41
@Geod24
Copy link
Member

Geod24 commented Aug 29, 2019

What's the use case for this ? Java / C# reflection is primarily runtime, while D's is primarily compile time.

@n8sh
Copy link
Member Author

n8sh commented Aug 29, 2019

My personal use case was implementing multimethods when the classes involved aren't known at compile time.

@PetarKirov
Copy link
Member

@n8sh Perhaps rebasing on top of master should fix the error on BuildKite.

@n8sh
Copy link
Member Author

n8sh commented Aug 30, 2019

Changing to use _d_isbaseof and renaming to isBaseOf with the same argument order as isAssignableFrom.

@n8sh n8sh changed the title Give TypeInfo_Class/TypeInfo_Interface.isDerivedFrom like C#/Java isAssignableFrom Give TypeInfo_Class/TypeInfo_Interface.isBaseOf like C#/Java isAssignableFrom Aug 30, 2019
src/object.d Outdated
for (auto ti = cast() child; ti !is null; ti = ti.base)
if (ti is this)
return true;
return false;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's some case for classes where this gives the wrong answer but _d_isbaseof gives the right answer I haven't found it. Unless such a case can be found I'll leave it here since _d_isbaseof potentially does a lot more work and extern(C) function calls are opaque to the optimizer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking back at this, checking straight up the inheritance chain rather than using _d_isbaseof means that the receiver TypeInfo_Class and argument TypeInfo_Class must represent actual classes rather than being the .info field of some TypeInfo_Interface.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solution: detect an interface's ClassInfo (TypeInfo_Class that does not represent a class) by checking if the initializer has length zero.

@@ -14,6 +14,9 @@
module rt.cast_;

extern (C):
@nogc:
nothrow:
pure:
Copy link
Member Author

@n8sh n8sh Aug 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding some attributes here so the compiler enforces the same attributes I claim in the private extern(C) declaration in object.d.

@n8sh n8sh force-pushed the issue-20178 branch 2 times, most recently from 125559e to 84d1ed5 Compare August 30, 2019 23:25
@thewilsonator
Copy link
Contributor

@n8sh whats the status of this?

@n8sh
Copy link
Member Author

n8sh commented Feb 15, 2020

@thewilsonator The code is ready but discussion is open regarding whether this should be added.

src/object.d Outdated Show resolved Hide resolved
@thewilsonator
Copy link
Contributor

It could also do with a changelog entry.

@12345swordy
Copy link
Contributor

I don't see any reason to reject this giving that there are attempts to call c# code from d.

src/object.d Outdated Show resolved Hide resolved
src/object.d Show resolved Hide resolved
@12345swordy
Copy link
Contributor

@n8sh ping. Address rainers comments.

@n8sh n8sh force-pushed the issue-20178 branch 6 times, most recently from 283cfaf to 8008f6f Compare February 27, 2020 18:13
@n8sh n8sh force-pushed the issue-20178 branch 2 times, most recently from 6d6a791 to 7d20e09 Compare March 7, 2020 17:07
@12345swordy
Copy link
Contributor

12345swordy commented Mar 9, 2020

@thewilsonator Any objections so far?

@thewilsonator
Copy link
Contributor

None from me, but it doesn't seem to pass.

@12345swordy
Copy link
Contributor

@n8sh the test are not passing, could you rebase?

@n8sh n8sh force-pushed the issue-20178 branch 2 times, most recently from 7c636d0 to 23dea06 Compare March 26, 2020 20:07
@12345swordy
Copy link
Contributor

12345swordy commented Mar 27, 2020

@thewilsonator the tests have pass, need to add auto merge label to this as I don't have the authority to do so

@Geod24
Copy link
Member

Geod24 commented Mar 28, 2020

Does this work with shared druntime ?

@n8sh
Copy link
Member Author

n8sh commented Mar 28, 2020

@Geod24 I don't know the answer to that but it works the same as rt.cast_._d_isbaseof.

Copy link
Member

@Geod24 Geod24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay in review. A few nits, otherwise LGTM.
Regarding the shared question, I asked because identity comparison can break in the presence of multiple TypeInfo, which IIRC can happen when using dynamic loading. But if _d_isbaseof does it, it should be fine 🤷‍♂

src/object.d Outdated Show resolved Hide resolved
src/object.d Outdated Show resolved Hide resolved
@@ -101,7 +104,7 @@ int _d_isbaseof2(ClassInfo oc, ClassInfo c, ref size_t offset)
return false;
}

int _d_isbaseof(ClassInfo oc, ClassInfo c)
int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use tail recursion and make this const, in order to avoid the cast.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not mess with the implementation of _d_isbaseof in this PR.

@@ -74,7 +77,7 @@ void* _d_dynamic_cast(Object o, ClassInfo c)
return res;
}

int _d_isbaseof2(ClassInfo oc, ClassInfo c, ref size_t offset)
int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto recursion

test/typeinfo/src/isbaseof.d Show resolved Hide resolved
@Geod24
Copy link
Member

Geod24 commented Mar 31, 2020

Ah I found it: #2828

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.
@12345swordy
Copy link
Contributor

What is the hold up here?

@n8sh n8sh added the 72h no objection -> merge The PR will be merged if there are no objections raised. label Apr 12, 2020
@12345swordy
Copy link
Contributor

The objection time limit is up
cc @thewilsonator

@dlang-bot dlang-bot merged commit cd643e2 into dlang:master Apr 16, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
72h no objection -> merge The PR will be merged if there are no objections raised. Enhancement New functionality
Projects
None yet
8 participants