D casting broke?

ag0aep6g via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Jun 19 16:00:03 PDT 2016


On 06/19/2016 11:19 PM, Joerg Joergonson wrote:
> On Sunday, 19 June 2016 at 20:21:35 UTC, ag0aep6g wrote:
[...]
>> No. B!b is derived from A!b, not from A!a. `b` being derived from `a`
>> does not make A!b derived from A!a.
>
> why not? This doesn't seem logical!

Template parameters simply don't work like that. A template can result 
in completely unrelated types based on template parameters.

For example:

     template C(T)
     {
         static if (is(T == A)) class C {}
         else static if(is(T == B)) alias C = int;
         else struct C {int x;}
     }

As you see there can't be any inheritance relation between the different 
instantiations of the C template here. Having such a relation for 
different instantiation that result in classes would be a weird special 
case.

There's probably a really simple and obvious reason why that special 
would be a bad idea in itself, but I'm not able to point it out. Maybe 
make a thread in the General group. I think the language people tend to 
focus their attention there.

Criticism and improvement proposals are also better directed to the 
General group.

>> Here is the full inheritance tree:
>>
>> X
>> ├─x
>> │ └─a
>> │   └─b
>> ├─A!a
>> └─A!b
>>   └─B!b
>
>
> But b is derived from a.

Yeah, that's right there in the middle.

> Your tree completely ignores under A.

Clearly, there's B!b under A!b. That's it. Nothing exists below B!b. B!a 
doesn't exist either. A!a is on a different branch. I don't think I've 
missed anything.

[...]
> Just because D doesn't understand this logical consistency between
> inheritance doesn't mean D is right. (Hence, why D's type system is broke)
>
>
> In fact, the tree should look like this:
>
>> X
>> ├─x
>> │ └─a
>> │   └─b
>> └─A!x
>      │  \
>      └─A!a
>        │  \
>        └─A!b
>          │  \
>          └─B!b

I'm having trouble reading this. A!x isn't valid, as the constraint on A 
says `T : a`, but x doesn't satisfy that.

I also don't understand what the backslashes mean. They just repeat the 
other lines, don't they? Or do they connect x, a, and b? That's already 
expressed in the upper section.

As for A!b being below A!a, I can only repeat that this inheritance is 
not implied. You would have to spell it out explicitly for the compiler 
to pick it up.

> Basically you are treating A!a and A!b as if a and be have no
> relationship. BUT THEY DO!

Well, to the compiler they don't.

> If you don't take that into account then your
> wrong.
>
> Simply stating how D behaves is not proof of why it is right or wrong.

For discussions about how D should behave, please post to General. Here 
in the Learn group I (and I think we) tend to focus on how D works or is 
meant to work. The way you think it should behave here is not how it's 
meant to work by the designers and implementors.

[...]
> import std.stdio;
> class a { }
> class b : a { }
>
> class A(T : a)
> {
>     T x;
> }
>
>
>
> void main(string[] argv)
> {
>      auto _A = new A!a();
>      auto _C = new A!b();
>
>      auto p = cast(A!a)_C;
> }
>
> p is null. My example with B is irrelevant. The issue is with the
> parameter.
>
> As you can see, D thinks that A!b and A!a are completely unrelated... as
> do you and arsd.
>
> Do you seriously think this is the case? That
>
> class b : a { }
>
> and
>
> class b { }
>
> effectively mean the same with regards to A?

Yes. A!a is a class like this: `class A!a {a x;}`. A!b is this: `class 
A!b {b x;}`. The generated classes have members that have an inheritance 
relationship, but that doesn't imply an inheritance relationship between 
A!a and A!b.

> The whole problem comes about at this line:
>
> auto p = cast(A!a)_C;
>
> We are trying to cast `T x` in C, which is effectively `b x` to `a x`.

No. You try to cast from one class type to another, that's different 
from casting the members.

You can have two classes with exactly the same members, yet they're not 
(directly) castable to each other when there's no inheritance relation:

     class A {int x;}
     class B {int x;}
     void main()
     {
         assert((cast(B) new A) is null); /* holds */
         assert((cast(A) new B) is null); /* holds */
     }

Maybe this is the crux? I'd expect you to be of the opinion that those 
casts should work.

Of course, you can force an unsafe conversion by casting to void* first:

     cast(B) cast(void*) new A

A reinterpret-style cast would also work:

     A a = new A;
     B b = * cast(B*) &a;

I suppose a simple cast doesn't do that because it's deemed more useful 
to get null when the inheritance doesn't work out, even when the member 
layout would work out. This way `cast` can be used for downcasts in a 
safe manner.

Because, when there is no inheritance relation, that means there is no 
guarantee that a class can be used in place of another one, even when 
the fields are compatible. Consider A and B having methods with the same 
name that do completely unrelated things. Converting accidentally from 
one to the other would be bad.


More information about the Digitalmars-d-learn mailing list