Possible D2 solution to the upcasting array problem, and a related problem with const and nested arrays
Stewart Gordon
smjg_1998 at yahoo.com
Fri Jan 2 04:51:46 PST 2009
I was just looking at this
http://d.puremagic.com/issues/show_bug.cgi?id=2544
which describes how it's possible to bypass const by doing this:
const(int)[] answers = [42];
int[][] unconsted = [[]];
const(int)[][] unsafe = unconsted;
unsafe[0] = answers;
unconsted[0][0] = 43;
The problem is that converting from int[][] to const(int)[][] isn't
safe, even though the language/compiler seems to think it is.
Really, it's another version of how you can use a DerivedClass[] as a
BaseClass[] and thereby place in it an object that isn't of type
DerivedClass.
There's actually a simple solution to this: specify that, where
DerivedClass derives from BaseClass, DerivedClass[] cannot be implicitly
converted to BaseClass[], but only to const(BaseClass)[].
Java has had something like this for a while, albeit not with arrays.
That is, IIRC, you can assign a DataStructure<DerivedClass> to a
variable of type DataStructure<? extends BaseClass> (or even
DataStructure<? extends DerivedClass>) - this creates a read-only view
of the data structure. My proposal implements the same basic concept as
this, but in a simpler way. (Java also supports write-only 'views' with
DataStructure<? super FurtherDerivedClass>, but I'm not sure we need
anything like this in D at the moment.)
Now let's apply the same principle to the example in the bug report.
Try defining that, in general, T[][] can be converted to const(T[])[]
but not const(T)[][]. Then
const(int)[] answers = [42];
int[][] unconsted = [[]];
const(int)[][] unsafe = unconsted;
would be illegal. One would have to do
const(int[])[] safe = unconsted;
and now
safe[0] = answers;
is illegal.
In order to deal with the original, slightly more complex testcase, the
principle needs to be applied in the same way to further levels of array
nesting. It would need applying to pointer types as well as array types
- so, for example,
int[]*[]
would be implicitly convertible to
const(int[]*)[]
but not
const(int[])*[]
const(int)[]*[]
We could combine two applications of the principle, and get
DerivedClass[][]
convertible to
const(DerivedClass[])[]
const(BaseClass[])[]
but not
const(DerivedClass)[][]
const(BaseClass)[][]
BaseClass[][]
To summarise, the rules would be:
- Generalise the definition of an upcast to be any of the following:
-- conversion of a class type to a class type further up the hierarchy
-- conversion of a type to a const version of that type
-- a legal implicit conversion according to the following rule
- If U is an upcast of T, then a legal implicit conversion is from T[]
to const(U)[], or T* to const(U)*. In particular, conversion from T[]
to U[] or T* to U* is illegal, except in the cases where T and const(T)
are exactly the same.
If I've worked it out right, then these rules'll be fix the const system
to be safe, while at the same time making upcasting of object arrays
safe. At least, before you consider invariant - a little more thought
is needed to work out how this would be dealt with.
And it shouldn't break too much existing code. Code that is already
broken will just promote this breakage from runtime to compiletime, and
those uses that were already 'correct' will be easily fixed by updating
the declarations. The unsafe conversions could be deprecated before
being removed altogether, with messages such as
conversion from int[][] to const(int)[][] is unsafe and deprecated,
use const(int[])[] instead
conversion from DerivedClass[] to BaseClass[] is unsafe and
deprecated, use const(BaseClass)[] instead
These unsafe conversions could still be allowed by explicit casts,
should they be needed for something, IWC you'd be expected to know what
you're doing.
What does everyone think? Even better, can anyone find any holes that
my proposal misses?
Stewart.
More information about the Digitalmars-d
mailing list