www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Identity of interfaces (or not)

reply Tomas Lindquist Olsen <tomas famolsen.dk> writes:
Hi all.

I've spent that last weeks rewriting LDC's handling handling of
struct/union/class/interface to 
match DMD, in terms of datalayout, ABI and correctness in general. It's done
now, and seems to 
be working well, however, running DStress on the latest revision, a new
regression showed up!

Test case:
http://dsource.org/projects/dstress/browser/run/opIdentity_01.d

Original NG post with issue:
http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D.bugs&artnum=2676

I've failed to find any replies to the thread so I'm unsure if this test is
even valid.

I'll paste the test case from DStress here as well:

<code>
module dstress.run.opIdentity_01;
interface I {
     I parent();
     void parent(I i);
     void addChild(I i);
}
interface J : I {
}
class A : I {
     private I myParent;

     void addChild(I i) {
         i.parent = this;
         }
     I parent() {
         return myParent;
     }

     void parent(I parent) {
         myParent = parent;
     }
}
class B : A, J {
}
int main() {
     J a = new B;
     J b = new B;
     a.addChild(b);
     if(!(b.parent is a)){ // FAILS
         assert(0);
     }
     return 0;
}
</code>

Here's the problem. DMD and (now) LDC both compile this test fine, but it fails
at runtime on 
the marked line.

The problem is that B has two vtables that implement the interface I. However,
in A.addChild, 
i.parent get's the first one (introduced by A), and in main, it gets the second
one (J , which 
derived from I). The identity check then fails because the pointers aren't the
same, even 
though it's the same object in question.

The easiest way I can see to make this work, would be to disable static
"downcasts" to 
interfaces, this way, in A.addChild, when 'this' is converted to I, it would
have to look 
through the ClassInfo and would always use the same vtable for a single
interface.

The other would be to change the ABI...

But, I'm not even sure if this is a bug or if D interfaces just work this way,
I've not been 
able to find much information...

So is it a bug? Or is identity for interfaces simply not guaranteed to pass,
even when it's the 
same Object in question?

Thanx
- Tomas
Dec 02 2008
next sibling parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Tomas Lindquist Olsen wrote:
 Hi all.
 
 I've spent that last weeks rewriting LDC's handling handling of 
 struct/union/class/interface to match DMD, in terms of datalayout, ABI 
 and correctness in general. It's done now, and seems to be working well, 
 however, running DStress on the latest revision, a new regression showed 
 up!
 
 Test case:
 http://dsource.org/projects/dstress/browser/run/opIdentity_01.d
 
 Original NG post with issue:
 http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmar
.D.bugs&artnum=2676 

For what it's worth: both DMD and GDC also fail that test.
 I've failed to find any replies to the thread so I'm unsure if this test 
 is even valid.

It's also available from <http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.bu s&article_id=2676>, which shows that there were replies. Unfortunately, webnews is broken so if you click any of the links you need to edit the URL (s/digitalmars.d/digitalmars.d.bugs/).
 I'll paste the test case from DStress here as well:
 

 
 Here's the problem. DMD and (now) LDC both compile this test fine, but 
 it fails at runtime on the marked line.
 
 The problem is that B has two vtables that implement the interface I. 
 However, in A.addChild, i.parent get's the first one (introduced by A), 
 and in main, it gets the second one (J , which derived from I). The 
 identity check then fails because the pointers aren't the same, even 
 though it's the same object in question.
 
 The easiest way I can see to make this work, would be to disable static 
 "downcasts" to interfaces, this way, in A.addChild, when 'this' is 
 converted to I, it would have to look through the ClassInfo and would 
 always use the same vtable for a single interface.
 
 The other would be to change the ABI...
 
 But, I'm not even sure if this is a bug or if D interfaces just work 
 this way, I've not been able to find much information...
 
 So is it a bug? Or is identity for interfaces simply not guaranteed to 
 pass, even when it's the same Object in question?

Yeah, the only way to "fix" it would be to make *all* interface->interface casts *implicitly* dynamic, even though the compiler *knows* a way to get a correct I statically. However I don't think that's a good idea. I'd say that if you really want interfaces to compare equal with 'is' you need to (1) make sure no object implements any interface more than once, or (2) cast to Object before comparing any interface that is multiply-implemented.
Dec 02 2008
prev sibling parent reply BCS <ao pathlink.com> writes:
Reply to Tomas,

 The problem is that B has two vtables that implement the interface I.
 However, in A.addChild, i.parent get's the first one (introduced by
 A), and in main, it gets the second one (J , which derived from I).
 The identity check then fails because the pointers aren't the same,
 even though it's the same object in question.

why not make Ia == Ib into cast(Object)Ia == cast(Object)Ib ?
 So is it a bug? Or is identity for interfaces simply not guaranteed to
 pass, even when it's the same Object in question?
 
 Thanx
 - Tomas

Dec 02 2008
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
BCS wrote:
 Reply to Tomas,
 
 The problem is that B has two vtables that implement the interface I.
 However, in A.addChild, i.parent get's the first one (introduced by
 A), and in main, it gets the second one (J , which derived from I).
 The identity check then fails because the pointers aren't the same,
 even though it's the same object in question.

why not make Ia == Ib into cast(Object)Ia == cast(Object)Ib ?

Because he's talking about 'is', not '=='?
Dec 02 2008
parent BCS <ao pathlink.com> writes:
Reply to Frits,

 BCS wrote:
 
 Reply to Tomas,
 
 The problem is that B has two vtables that implement the interface
 I. However, in A.addChild, i.parent get's the first one (introduced
 by A), and in main, it gets the second one (J , which derived from
 I). The identity check then fails because the pointers aren't the
 same, even though it's the same object in question.
 

Ia == Ib into cast(Object)Ia == cast(Object)Ib ?


ok `s/==/is/` now?
Dec 02 2008
prev sibling next sibling parent Christopher Wright <dhasenan gmail.com> writes:
BCS wrote:
 Reply to Tomas,
 
 The problem is that B has two vtables that implement the interface I.
 However, in A.addChild, i.parent get's the first one (introduced by
 A), and in main, it gets the second one (J , which derived from I).
 The identity check then fails because the pointers aren't the same,
 even though it's the same object in question.

why not make Ia == Ib into cast(Object)Ia == cast(Object)Ib ?

That makes sense, and I fully support it. I tend to use interfaces rather often, and anything to improve support for them is good.
Dec 02 2008
prev sibling next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
BCS wrote:
 why not make
  Ia == Ib
 into
  cast(Object)Ia == cast(Object)Ib
 ?

I like, but will this work for COM?
Dec 02 2008
parent BCS <ao pathlink.com> writes:
Reply to Robert,

 BCS wrote:
 
 why not make
 Ia == Ib
 into
 cast(Object)Ia == cast(Object)Ib
 ?


All that would be required is to define casting a non D Com object to Object as something rational and consistent (but only in the special cases of this test). I think this should be easy to do as all it would require, as a result of both how COM and D interfaces work, is using an unchecked cast to object.
Dec 03 2008
prev sibling parent reply Jason House <jason.james.house gmail.com> writes:
BCS wrote:
 
 why not make
   Ia == Ib
 into
   cast(Object)Ia == cast(Object)Ib
 ?

Interfaces are not always castable to Object. At a minimum, it fails for COM and extern(C++)
Dec 03 2008
parent BCS <ao pathlink.com> writes:
Reply to Jason,

 BCS wrote:
 
 why not make
 Ia == Ib
 into
 cast(Object)Ia == cast(Object)Ib
 ?

for COM and extern(C++)

Check my reply to Robert. I think it works if you just do it and ignore some types of errors. It's under the hood anyway.
Dec 03 2008