DIP 1014
Shachar Shemesh
shachar at weka.io
Wed Oct 3 18:58:37 UTC 2018
On 03/10/18 20:43, Stanislav Blinov wrote:
> On Wednesday, 3 October 2018 at 15:33:00 UTC, Shachar Shemesh wrote:
>> I.e. - I am asserting if a move was not caught. The program fails to
>> run on either ldc or dmd. To me, this makes perfect sense as for the
>> way D is built. In essence, opAssign isn't guaranteed to run. Feel
>> free to build a struct where that assert passes to convince me.
>
> That's a slightly different issue here.
Well, I view this issue as a deal breaker. If you need to move the
object *in order* to pass it to your move hook, then anything that
requires knowing the address of the old instance will, by definition,
not work.
> Look at the output. The operator is being run, it can't *not* run,
Sure it can. Just look at the example I posted on the other thread
(https://forum.dlang.org/post/pp2v16$1014$1@digitalmars.com). The hook
you mention is downright @disabled there.
In fact, had that not been the case, this DIP would never have happened.
>
>> Here is the flaw in your logic:
>>
>> void opAssign(Tracker rhs)
>>
>> rhs is passed by value. This means that already at the point opAssign
>> is called, rhs *already* has a different address than the one it was
>> passed in with.
>
> Currently that is only true if you define a destructor.
No, that's not true. Try printing the instance's address in the
constructor and again in your operator.
> In the presence of a move hook, 'rhs'
> would first have to pass through that hook, which will not take
> destructors into account at all.
I'm sorry, I'm not following. What is the difference between what you're
proposing and opPostMove as defined?
> Consider your own DIP: what you're suggesting is the ability to take the
> address of the original when a move is taking place. My example shows
> that in the simplest case even today, address of the original is already
> the address of the argument.
This is the run I got:
$ ./movetest2
Address of temporary is 'b382e390', counter points to 'b382e390'
... which is '0' bytes from the address of temporary.
Address of temporary is 'b382e390', counter points to '8eb82b60'
... which is '-966984906800' bytes from the address of temporary.
Address of temporary is 'b382e390', counter points to 'b382e390'
... which is '0' bytes from the address of temporary.
Address of temporary is 'b382e390', counter points to '8eb82b60'
... which is '-966984906800' bytes from the address of temporary.
I'm not sure what I should have seen, or what I should have concluded
from it. This is your original program, unmodified.
> The changes are literally the same as the ones you're proposing:
>
> "When moving a struct's instance, the compiler MUST call __move_post_blt
> giving it both new and old instances' addresses."
>
> That is the same that would have to happen with this(typeof(this) rhs),
> where &this is the address of new instance, and &rhs is the address of
> old instance, but there's no need for opPostMove then. I guess what I
> should've said from the start is that the semantics you're proposing fit
> nicely within one special function, instead of two.
Except, like I said, it's not working for me, and I find it hard to
understand how it *can* work (inlining notwithstanding), which is why I
did not propose it.
>
> this(typeof(this)), of course, would need to be special in the ABI, but
> again, that's one special function instead of two.
No. My proposal requires one amendment to argument passing in the ABI,
but no special cases at all. Changes to the ABI are not the same as
changes to the run time library.
>
> Let's take a step back for a moment and look at what should actually be
> happening for this hook to work (which you briefly mention in the DIP):
>
> 1. The compiler constructs the value. In your case, it constructs two:
It does not. It copies the bits from one to the other.
This also means that a struct where some of its members have a hook is
copied wholesale and patched, which is typically faster than copying in
parts.
> the original and the new one. In my case, it constructs the original and
> then passes it over to the move ctor (one blit potentially avoided).
> 2. It calls the hook (move ctor).
I'm not sure I follow you on that one. What did you mean?
> 3. In your case, it calls the opPostMove.
In all cases, you need to call the hook, whatever it is, for those
structs that have it, and do some default handling for those that don't.
> 4. In any case, it *doesn't* destruct the original. Ever. The
> alternative would be to force the programmer to put the original back
> into valid state, and suddenly we're back to C++ with all it's
> pleasantries.
>
> That last part is quite different from the current model, in which the
> compiler always destructs function arguments. That's why my example
> fails when a destructor is present.
Like I said above, I don't think that's correct.
>
> The other thing to note (again something that you mention but don't
> expand on), and that's nodding back to my comment about making move()
> and emplace() intrinsics, is that creating such a hook *will* invalidate
> current behavior of move(). Which is perhaps more easily fixed with your
> implementation, actually, *except* for the part about eliding
> destruction.
Convince me that the pointers indeed don't change when passed to the
function, and then we can discuss whether this point is correct.
Shachar
More information about the Digitalmars-d
mailing list