what to do with postblit on the heap?

Steven Schveighoffer schveiguy at yahoo.com
Tue Jun 21 04:34:24 PDT 2011


On Mon, 20 Jun 2011 21:59:49 -0400, Michel Fortin  
<michel.fortin at michelf.com> wrote:

> On 2011-06-20 18:12:11 -0400, "Steven Schveighoffer"  
> <schveiguy at yahoo.com> said:
>
>> On Mon, 20 Jun 2011 16:45:44 -0400, Michel Fortin   
>> <michel.fortin at michelf.com> wrote:
>>
>>> My feeling is that array appending and array assignment should be   
>>> considered a compiler issue first and foremost. The compiler needs to  
>>> be  fixed, and once that's done the runtime will need to be updated  
>>> anyway  to match the changes in the compiler. Your proposed fix for  
>>> array  assignment is a good start for when the compiler will provide  
>>> the  necessary info to the runtime, but applying it at this time will  
>>> just  fix some cases by breaking a few others: net improvement zero.
>>  BTW, I now feel that your request to make a distinction between move  
>> and  copy is not required.  The compiler currently calls the destructor  
>> of  temporaries, so it should also call postblit.  I don't think it can  
>> make  the distinction between array appending and simply calling some  
>> other  function.
>
> Well, if
>
> 	a ~= S();
>
> does result in a temporary which get copied and then destroyed, why have  
> move semantics at all? Move semantics are not just an optimization, they  
> actually change the semantics. If you have a struct with a @disabled  
> postblit, should it still be appendable?

Good question.  I don't even know how the runtime could avoid calling  
postblit, there is no flag saying the postblit is disabled in the typeinfo  
(that I know of).

But think about it this way, if you have a function foo:

foo(S)(ref S s, S[] arr)
{
    arr[0] = s;
}

Isn't this copy semantics?  This is exactly how the D runtime gets the  
data.  The only difference is, the runtime function is allowed to accept a  
temporary as a reference (not possible in a normal function).

Now, you could force move semantics, if you know the argument is an  
rvalue, but I don't know enough about what postblit is used for in order  
to say it's fine to use move semantics to move the struct into the heap.

The reason I say move semantics are an optimization is because:

{
   S tmp;
   arr ~= tmp;
}

is essentially equivalent to:

arr ~= S();

But the former is copy semantics, the latter can be considered move.  It  
seems like a smart compiler during optimization could rewrite the former  
as the latter, unless the semantics truly are different.  Which is why I'm  
trying to figure out how postblit can be used ;)

>> If the issue of array assignment is fixed, do you think it's worth  
>> putting  the change in, and then filing a bug against the GC?  I still  
>> think the  current cases that "work" are fundamentally broken anyways.
>
> That depends. I'm not too sure currently whether the S destructor is  
> called for this code:
>
> 	a ~= S();

It is, I tested it.  I ran this code:


struct Test
{
    this(this) { writeln("copy done"); }
    void opAssign(Test rhs) { writeln("assignment done"); }
    ~this() { writeln("destructor called"); }
}

void main()
{
    Test[] tests = new Test[1];
    {
       // Test test;
       // tests ~= test;
       tests ~= Test();
    }
    writeln("done");
}

and saw "destructor called" in the output, no matter which option was  
commented out.

> All in all, I don't think it's important enough to justify we waste  
> hours debating in what order we should fix those bugs. Do what you think  
> is right. If it becomes a problem or it introduces a bug here or there,  
> we'll adjust, at worse that means a revert of your commit.

OK, then I'll push the change.  I already filed a bug against _d_arraycopy.

>>> As for the issue that destructors aren't called for arrays on the  
>>> heap,  it's a serious problem. But it's also a separate problem that  
>>> concerns  purely the runtime, as far as I am aware of. Is there  
>>> someone working on  it?
>>  I think we need precise scanning to get a complete solution.  Another   
>> option is to increase the information the array runtime stores in the   
>> memory block (currently it only stores the "used" length) and then hook  
>>  the GC to call the dtors.  This might be a quick fix that doesn't  
>> require  precise scanning, but it also fixes the most common case of  
>> allocating a  single struct or an array of structs on the heap.
>
> The GC calling the destructor doesn't require precise scanning. Although  
> it's true that both problems require adding type information to memory  
> blocks, beyond that requirement they're both independent. It'd be really  
> nice if struct destructors were called correctly.

Yes, the more I think about it, the more this solution looks attractive.   
All that is required is to flag the block as having a finalizer, store the  
TypeInfo pointer somewhere, and the GC should call it.

I'll put in a bugzilla enhancement so it's not forgotten.

-Steve


More information about the Digitalmars-d mailing list