Thread overview
Missing destructor call using clear and base interface ref.
Aug 06, 2012
Roberto Delfiore
Aug 06, 2012
Ali Çehreli
Aug 09, 2012
Roberto Delfiore
Aug 09, 2012
Denis Shelomovskij
August 06, 2012
See the following code:

interface A{
}

class B : A{
    this(string name){this.name = name;}
    ~this(){
        writefln("Destructor %s", name);
    }
    string name;
}

void main(){
    B b0 = new B("b0");
    B b1 = new B("b1");

    A a = b0;
    clear(a);

    clear(b1);
}

Output:
Destructor b1

dmd 2.059

Why is the B destructor not invoked in the first clear?

Expected output:
Destructor b0
Destructor b1

August 06, 2012
On 08/06/2012 06:59 AM, Roberto Delfiore wrote:
> See the following code:
>
> interface A{
> }
>
> class B : A{
> this(string name){this.name = name;}
> ~this(){
> writefln("Destructor %s", name);
> }
> string name;
> }
>
> void main(){
> B b0 = new B("b0");
> B b1 = new B("b1");
>
> A a = b0;
> clear(a);
>
> clear(b1);
> }
>
> Output:
> Destructor b1
>
> dmd 2.059
>
> Why is the B destructor not invoked in the first clear?
>
> Expected output:
> Destructor b0
> Destructor b1
>

Interesting.

I've tested the code with 2.060 after replacing 'clear' with 'destroy' (not required here, but because clear will be deprecated):

import std.stdio;

interface A{
}

class B : A{
    this(string name){this.name = name;}
    ~this(){
        writefln("Destructor %s", name);
    }
    string name;
}

void main(){
    B b0 = new B("b0");
    B b1 = new B("b1");

    A a = b0;
    writeln("Before clear(a)");
    destroy(a);

    writeln("Before clear(b1)");
    destroy(b1);

    writeln("Leaving main");
}

I see both of the destructor calls but the first one is executed out of order:

Before clear(a)
Before clear(b1)
Destructor b1
Leaving main
Destructor b0    <-- Here

Making 'a' a B produces the expected output:

    B a = b0;

Before clear(a)
Destructor b0    <-- Now at expected time
Before clear(b1)
Destructor b1
Leaving main

I guess destroy() is a no-op on an interface because your not seeing the destructor's effect and my seeing it can be explained by the non-deterministic behavior of the GC regarding destructor calls: If it is up to the GC, the destructor calls are not guaranteed.

Do others know? Shouldn't destroy() work on an interface?

Ali

August 09, 2012
Thank you for your analysis, it's a very strange behavior. I
still can not figure out if there is something I don't know or if
it's is simply a bug.

Good answer: Shouldn't destroy() work on an interface?

On Monday, 6 August 2012 at 20:46:45 UTC, Ali Çehreli wrote:
> On 08/06/2012 06:59 AM, Roberto Delfiore wrote:
> > See the following code:
> >
> > interface A{
> > }
> >
> > class B : A{
> > this(string name){this.name = name;}
> > ~this(){
> > writefln("Destructor %s", name);
> > }
> > string name;
> > }
> >
> > void main(){
> > B b0 = new B("b0");
> > B b1 = new B("b1");
> >
> > A a = b0;
> > clear(a);
> >
> > clear(b1);
> > }
> >
> > Output:
> > Destructor b1
> >
> > dmd 2.059
> >
> > Why is the B destructor not invoked in the first clear?
> >
> > Expected output:
> > Destructor b0
> > Destructor b1
> >
>
> Interesting.
>
> I've tested the code with 2.060 after replacing 'clear' with 'destroy' (not required here, but because clear will be deprecated):
>
> import std.stdio;
>
> interface A{
> }
>
> class B : A{
>     this(string name){this.name = name;}
>     ~this(){
>         writefln("Destructor %s", name);
>     }
>     string name;
> }
>
> void main(){
>     B b0 = new B("b0");
>     B b1 = new B("b1");
>
>     A a = b0;
>     writeln("Before clear(a)");
>     destroy(a);
>
>     writeln("Before clear(b1)");
>     destroy(b1);
>
>     writeln("Leaving main");
> }
>
> I see both of the destructor calls but the first one is executed out of order:
>
> Before clear(a)
> Before clear(b1)
> Destructor b1
> Leaving main
> Destructor b0    <-- Here
>
> Making 'a' a B produces the expected output:
>
>     B a = b0;
>
> Before clear(a)
> Destructor b0    <-- Now at expected time
> Before clear(b1)
> Destructor b1
> Leaving main
>
> I guess destroy() is a no-op on an interface because your not seeing the destructor's effect and my seeing it can be explained by the non-deterministic behavior of the GC regarding destructor calls: If it is up to the GC, the destructor calls are not guaranteed.
>
> Do others know? Shouldn't destroy() work on an interface?
>
> Ali
August 09, 2012
09.08.2012 12:36, Roberto Delfiore пишет:
> Thank you for your analysis, it's a very strange behavior. I
> still can not figure out if there is something I don't know or if
> it's is simply a bug.
>
> Good answer: Shouldn't destroy() work on an interface?
>

Filled an issue:
http://d.puremagic.com/issues/show_bug.cgi?id=8527

-- 
Денис В. Шеломовский
Denis V. Shelomovskij