If reloc is called on a function parameter, then relocation can be performed using (1), (2) or (3), in that order of preference. (1) or (2) are no longer enforced as ABI restrictions may prevent from prematurely ending the lifetime of relocated parameters. Which one is picked is left at the discretion of compiler vendors.
Actually, can we rethink this? One of the success criteria here is that we should be able to pass std::unique_ptr (or equivalent) in registers. So that means that we're accepting at least some level of ABI breakage.
Any class that is trivially relocatable or has an accessible relocate operator must either have been trivial already (in which case there is no ABI impact) or have been explicitly opted in to relocation, by having a relocator declared or defaulted. Since this is under the control of the library author and/or user, I don't see it as an issue to say that functions with parameters having trivial or user-defined relocate have a different ABI (callee-destroy, or if necessary having automatic accompanying flags in registers or on the stack). We accept that adding a user-defined destructor, or virtual functions/bases, or changing data members changes ABI, so why shouldn't defaulting/declaring a relocator?
(Yes, aggregate-style types ("plain structs") are also affected - but again, we accept that their ABI is at the mercy of the types they compose.)
So (1) and (2) always apply; ABI is dependent on whether a class is trivially or user relocatable (and the language on callee-destroy will need amending). Since you're specifying that classes with synthesized relocate (from move+destroy) are destroyed at the end of scope, the behavior for function parameters will be uniform with that for variables.
Then a constructor attribute and reloc_wrapper are no longer necessary.
C. Relocator member function
The relocator member function is the special function that is invoked by operator reloc (2). The operator reloc behaves like a constructor: it constructs a new instance and maybe allows for base or member initialisation.
This special function is only needed if the object is not trivially relocatable. The suggested syntaxes are:
struct T: B
{
// data members
D data;
T* self;
// syntax A
operator reloc() { self = this; }
// syntax B
operator reloc(T&& rhs) : self{this} {}
// syntax C (P0023R0)
>>T(T& rhs) : >>B{rhs}, >>data{rhs.data}, self{this} {}
};
Yes, I agree with syntax B.
I would argue that (except for already trivial classes, where it is unnecessary) the syntax for declaring a class to be trivially relocatable should be precisely to default the relocator special member function:
operator reloc(T&& rhs) = default;
This would perform memberwise relocate of bases-and-members, and be trivial if all those relocate operations are trivial. That is, it would have identical behavior to a relocator with empty base-and-member list and body:
operator reloc(T&& rhs) {}
However, the latter is never trivial and is not nothrow unless otherwise specified.
As you will have seen on a previous message, I think that there needs to be a mechanism to prevent leaks if a base-or-member relocator throws, and suggest a function-try-block with initializer:
operator reloc(T&& rhs) /* noexcept(false) */
try (auto p = rhs.y)
/* base-or-member-initializers */
{
/* relocator function-body */
}
catch (...)
{
delete p;
}