On Mon, 20 Jun 2011 21:59:49 -0400, Michel Fortin
<[email protected]> wrote:
On 2011-06-20 18:12:11 -0400, "Steven Schveighoffer"
<[email protected]> said:
On Mon, 20 Jun 2011 16:45:44 -0400, Michel Fortin
<[email protected]> 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