On Mon, 2 May 2022 at 14:23, Sébastien Bini <sebastien.bini@gmail.com> wrote:
> A destructor may call arbitrary methods on the class under destruction,
> which will have no way to tell that the instance they are invoked on was
> previously relocated. How will you ensure safety in this scenario? It seems
> that there will be a considerable burden on the class author to ensure that
> all methods called from the destructor are safe to be called on a relocated
> instance, since the language will not ensure this, and a considerable
> maintenance burden going forward.

I don't see how that's different from the destructor call on a moved instance.

Here you're explicitly adding another state to the object for the (original and subsequent) authors to have to keep track of. Currently an object (e.g. a container) has: - default (empty) state; - value-containing state; - moved-from state (usually the same as empty state). Adding a relocated state means more work and more potential bugs. If instead relocation destroys the object then there are no additional states and quite often fewer,; the moved-from state can be made the same as the empty state, since there is no need for a singular moved-from state.

In fact the first version of the paper worked this way: the relocation destructor acted as a constructor for the new instance and as a destructor for the relocated instance. As such, the destructor of the relocated instance was not called, as the instance was already considered destructed.

However, as others have pointed it out, this leads to an ABI break. Consider:

void sink(T z);

void fwd_to_sink(T y)
{
sink(reloc y); // oops
}

void foo()
{
T x;
fwd_to_sink(reloc x);
}

In fwd_to_sink, reloc cannot omit the call to the destructor of 'y', as it is called by 'foo'. For this to work:
- fwd_to_sink would need to somehow return some information on the destruction state of its parameters.
- or change the call convention so that the parameters are destructed in the called function body instead of the callee site.

Yes, I recognize the issue. This can be resolved by stating that implementations may refuse to relocate function parameters, and providing a mechanism (e.g. an attribute) for the author to switch ABI to callee-destroy.

I thought of something similar:
struct T {
    operator reloc(); // return a new instance of T, like a constructor would do
    // or as a static variant with signature:
    operator reloc(T&& self);
};

But I found it very inconvenient to write the function body:

struct T : public B {
    operator reloc() {
        // how to construct the B part of T into the new T using B's reloc?
        // how to initialise the data-members of T using the reloc
    }
}

This can hardly reuse the constructor syntax (with base class and data-member initialisers). I couldn't find anything convincing in that path, but I can still give it more thought.

Yes, this is a bit tricky (base classes in particular), though I have some ideas about possible syntaxes. However, much of the time `= default` will be sufficient, and in the remainder it may be acceptable to treat the source object as an xvalue, on the proviso that it will be immediately destructed. In other words I'm not convinced that this will be a problem in practice; some examples of classes that would need a user-provided relocation operation might help.