array cast should be supported corrected

Robert Fraser fraserofthenight at gmail.com
Thu Aug 7 12:04:32 PDT 2008


Steven Schveighoffer Wrote:

> "Robert Fraser" wrote
> > Frank Benoit Wrote:
> >
> >> interface I{}
> >> class C : I {}
> >>
> >> C[] carray = getInstance();
> >> I[] iarray = carray; // compile error
> >> I[] iarray = cast(I[])carray; // runtime error (1)
> >>
> >> // correct way:
> >> I[] iarray = new I[carray.length];
> >> foreach( idx, c; carray ){
> >> iarray[idx] = c; // implicit cast
> >> }
> >>
> >> I use a template for doing this, but this looks so ugly.
> >> I[] iarray = arraycast!(I)(carray);
> >>
> >> I think the D compiler should call a runtime method in (1) to do the
> >> cast in a loop, instead of doing a simple type change that is not
> >> working correctly.
> >
> >
> > The only way to do this is if the cast performed a copy. Consider:
> >
> > interface I { }
> > class A : I { }
> > class B : I { }
> >
> > A[] aarray;
> > I[] iarray = aarray;
> > iarray ~= new B();
> > A a = aarray[0]; // you BROKE my TYPE SYSTEM -- this is an instance of B
> >
> > When upcasting an array you NEED to copy; you cannot alias. And a cast
> > should never make a copy, since that's unexpected behavior.
> 
> Aliasing is possible.  The problem is that for interfaces, the pointer is 
> changed.  If you change I to be a class, not an interface, then your code 
> will compile (of course, it has the same problem as you have added a B to 
> the end of an A array, but in general, casting from A[] to B[] where A is 
> implicitly castable to B and both A and B use the same amount of space, is 
> allowed.  Except for Interfaces :) )

If you can replace "I" with an abstract class and get it to
compile right, that's a good old-fashioned bug. It doesn't matter if A and
B are the same size (unless you're using scope classes which might lead to
slicing), the problem is that the bits are being interpreted as different
types than they are.

// Note these definitions -- A and B are not type-compatible!
class Base { }
class A : Base { char[] str; }
class B : Base { int x, int y; }

A[] a_array;
Base[] base_array = a_array;
base_array ~= new B(5, 10);
A a = a_array[0];
writefln(a.str); // Segfault!

I don't have a D compiler on this computer so I can't test it out, but if
the compiler can implicitly cast A[] to Base[], then this is a hole in the type
system. The issue with interfaces is tangential.

It's also extremely easy to overlook this error. Say you're passing an array
of A[] to a function that modifies an array of Base[] by possibly adding
things. In a large system, it can be a tricky bug to track down since the
error may not manifest itself until a while after it happens, and may not
always be a segfault (if they're the same size & it's just the bits being
interpreted as the wrong type, things could get very ugly -- it may not
always just segfault).



More information about the Digitalmars-d mailing list