April 28, 2007
janderson Wrote:

> DropApple
> DropApple
> DropApple
> EatOrange  //What the hell, I never called this function.
> DropApple
> 
> 
> //2
> 
> //Even worse, remove the EatOrange
> 
> DropApple
> DropApple
> DropApple
> Error: Access Violation
> 
> 
> The problem is the refcopy.  I'm not sure this sort of conversion should be band.  However I'm not sure what the correct type of checking should be used.  Maybe it should only be converted when passed into the function (otherwise require a .ptr qualifier).  It would still have potential issues but I think it would be less error prone.
> 
> -Joel

Bingo.  D does not exhibit this behavior with interfaces, however, so designers are safe using arrays of interfaces (only!).
April 29, 2007
Walter Bright wrote:
> Mark Burnett wrote:
>
> > Treating a container of derived as a container
> > of base is an error.
>
> But that isn't what is happening with D. base[]
> is an array of *references* to base, so the
> slicing problem one has in C++ is not possible in D

I don't think he's talking about slicing, I think he's talking about the type-system hole. It's not unreasonable to assume when reading/debugging code that any members of a Foo[] will be of type Foo or, if not, that a cast will have been required somewhere to indicate that fishy things are afoot (afin?). This conversion subverts that.

@OP: I raised this about a year ago and nobody seemed bothered then; I don't imagine that's changed.

cheers
Mike
April 29, 2007
James Dennett wrote:
> An array of (pointers/references to) derived is usable
> as an *immutable* array of base (for suitable English
> meaning of immutable, matching C++'s notion of the
> array (equivalently, the pointers it contains) being
> const.
> 
> Java has runtime checks required because it allows
> conversion from array of Derived to array of Base,
> and that (as you know) also uses reference semantics.
> The conversion is widely viewed as a mistake in Java;
> if I pass a Derived[] around, the language should
> not silently allow one of its elements to refer to
> a Base object.

But a derived reference can always be implicitly converted to a base reference anyway. That's the point of polymorphism.
April 29, 2007
> > > Treating a container of derived as a container
> > > of base is an error.

> @OP: I raised this about a year ago and nobody seemed bothered then; I don't imagine that's changed.

After playing around a bit more, I've discovered that (at least with gdc) rewriting the addfavoritefruit function to use the ~= operator to append institutes copy on write by default, while this is *not* the case for the previously posted indexed veresion.

If the keyword "in" guaranteed that all internal writes were copies, then it would probably be unnecessary to change the implicit conversion behavior.  No one in their right mind would pass derived[] to a f(inout base[]), so the only time the issue would arise is in the local scope, which at least narrows the potential bug down for the programmer.

So basically, is there a reason for the following distinction?

foo(in baseT [] base)
{
base[27] = new derived2;  // does not create a copy -- not correct, see below

base ~= new derived2;  // creates a copy
}

If not, a consistent change to the implimentation would have practically no effect on the language specification, while (for practical purposes) fixing this problem.

Correction:  as I was experimenting with this some more I noticed that base[i] = ...; *does* seem to create a copy, but it does so *after* the assignment takes place (during the return?).  If this is really the case, it's almost certainly unintended, and changing it would eliminate this problem for many purposes.

Mark


April 29, 2007
Walter Bright wrote:
> James Dennett wrote:
>> An array of (pointers/references to) derived is usable
>> as an *immutable* array of base (for suitable English
>> meaning of immutable, matching C++'s notion of the
>> array (equivalently, the pointers it contains) being
>> const.
>>
>> Java has runtime checks required because it allows
>> conversion from array of Derived to array of Base,
>> and that (as you know) also uses reference semantics.
>> The conversion is widely viewed as a mistake in Java;
>> if I pass a Derived[] around, the language should
>> not silently allow one of its elements to refer to
>> a Base object.
> 
> But a derived reference can always be implicitly converted to a base reference anyway. That's the point of polymorphism.

That's not the issue here either.  One more level of indirection is present when dealing with arrays of references or references to references.

The point is that a reference to a derived reference
must *not* be converted to a reference to a base
reference, just as an array of derived references
must not be converted to an array of base references
in case any is changed to a reference to an object
that is not a derived.

-- James
April 29, 2007
James Dennett Wrote:
> 
> The point is that a reference to a derived reference
> must *not* be converted to a reference to a base
> reference, just as an array of derived references
> must not be converted to an array of base references
> in case any is changed to a reference to an object
> that is not a derived.
> 

Interestingly, given:

class B{}
class D: B{}

void f(inout B x){
    x = new B();
}

void main(){
    D[] a1 = new D[3];
    B[] a2 = a1;

    f(a1[1]);   // Error: cast(B)(a1[1u]) is not an lvalue
    f(a2[1]);   // naturally works

jovo


April 29, 2007
Walter Bright Wrote:

> James Dennett wrote:
> > An array of (pointers/references to) derived is usable
> > as an *immutable* array of base (for suitable English
> > meaning of immutable, matching C++'s notion of the
> > array (equivalently, the pointers it contains) being
> > const.
> > 
> > Java has runtime checks required because it allows
> > conversion from array of Derived to array of Base,
> > and that (as you know) also uses reference semantics.
> > The conversion is widely viewed as a mistake in Java;
> > if I pass a Derived[] around, the language should
> > not silently allow one of its elements to refer to
> > a Base object.
> 
> But a derived reference can always be implicitly converted to a base reference anyway. That's the point of polymorphism.

Java can allow treating D[] as a mutable B[] only because of its runtime checks.  That way it can just throw an exception when you do things like call DerivedA.foo on DerviedB.  D doesn't have this, and so it's arrays are just as type unsafe as C++'s.

You really should have a look at Chapter 24 of Marshall Cline's excellent FAQ again.  He describes the issue perhaps better than I can.

I am actually a little surprised that there is a difference in the way conversions from D[] -> B[] and D[] -> I[] work.

It seems that the easiest way to fix this is to remove the implicit D[] -> B[].  Though as James suggests, it would be safe (and useful) to pass D[] as an immutable B[] *or* immutable I[].

Mark
April 30, 2007
Mark Burnett schrieb am 2007-04-28:
> Still there are one or two things that strike me as odd:  in particular that arrays of a derived type can be converted to an array of a base type.

[...]

Below is a simplified sample:

# import std.stdio;
#
# class Base{
#    int x;
# }
#
# class Derived : Base{
#    long y;
# }
#
# void main(){
#    Derived[] derived = new Derived[1];
#    Base[] base;
#
#    derived[0] = new Derived();
#    derived[0].x = 1;
#    derived[0].y = 2;
#    writefln("derieved[0] -> (x:%s, y:%s)", derived[0].x, derived[0].y);
#
#    base  = derived; // <- this is the issue
#    writefln("base[0] -> (x:%s)", base[0].x);
#
#    base[0] = new Base();
#    base[0].x = 3;
#    writefln("base[0] -> (x:%s)", base[0].x);
#
#    writefln("derieved[0] -> (x:%s, y:%s) !!!random y!!!", derived[0].x, derived[0].y);
# }

Thomas


April 30, 2007
Thomas Kuehne wrote
> Below is a simplified sample:
> #    base  = derived; // <- this is the issue

That assignment above is totally useless, unless one wants to drop some data, that can only be hold by derived.

But if one drops some of that data, every access through derived might behave unpredictable.

The obvious fault is, that derived is not nulled immeditely after that assignment in order to prevent every further access.

Is that what the compiler is supposed to do automatically: null out the RHS as a side effect?

-manfred
1 2
Next ›   Last »