On Sun, 22 May 2022 at 17:46, Thiago Macieira via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
On Sunday, 22 May 2022 16:32:30 PDT Edward Catmur via Std-Proposals wrote:
> Next, it opts *out* of the ABI
> change, by annotating the relocation operation with an attribute. This
> opt-out means that the class is passed into functions as if it did not
> declare the relocation operation, so this is compatible with previously
> compiled code and with code compiled to an earlier standard version with
> the relocation operation #if defined out.

I repeat that every function must be annotated for their choice too, not the
type. Types are used in libraries other than that which defined them. And that
should be opt-in.

Uh, why? If you use another library's types in your API, then you're making your library dependent on their ABI. After all, they can at any time add or remove data members, alignment, virtual bases etc.

> Both opt-in and opt-out propagate to derived and composing classes as
> necessary to maintain the Rule of Zero. ABI break (opt-in without opt-out)
> also propagates (a class that breaks ABI for itself is assumed responsible
> for breaking the ABI of derived and composing classes) but can be canceled
> by the derived/composing class itself opting out.

The problem is that we have actually a three-way question here for Rule of
Zero.

You can break the Rule of Zero to add your own (non-trivial) destructor and
then opt-in to relocation.

You can keep the Rule of Zero and if your type is already trivial, it gets
trivial relocation. That requires no ABI break because it doesn't matter how
many times you do no-op, it's still no-op.

The problem is when you keep the Rule of Zero on a type that is not trivial
because one of its bases or member sub-objects isn't trivially destructible.
That means the choice of this other type of adding a relocation operator
affects the current type, as per the discussion on synthesising relocation
operators. Unless I misunderstood it, of course.

Making ABI-affecting changes to a base or member subobject affects ABI of the derived or composing object, yes. This is part of the C++ experience.

> If the mechanism is opt-out per type, then libc++ will opt out
> std::unique_ptr in the standard Library headers

Why would they opt-out?

To preserve (function argument passing) ABI.
 
> But a further criterion is rule-of-zero: classes that do not define special
> member functions (destroy, move, copy) should not need to do anything to
> gain the benefit of relocation. So those classes should not need to either
> opt in or opt out, but this should happen automatically.

See other email: does this include non-trivial rule-of-zero types?

Absolutely, yes. 

This does mean that a non-trivial rule-of-zero class that is relocatable by memberwise move-and-destroy on its (non-trivial) members will need to be passed by move-and-destroy en bloc. That is, since it did not explicitly opt into relocation, it is implicitly opted out of ABI break (under the scheme I'm suggesting).

Then if the class type of a data member adds its own relocation operation, thereby opting into relocation, without opting out of the concomitant ABI break, that class is considered to have broken ABI for itself and for the rule-of-zero type, so the latter is then passed by relocation and optional callee-destroy, in an ABI break.

This is a relatively aggressive approach; a more conservative approach would be to only break ABI in the rule-of-zero case only when either necessary (if a member is immovable, relocatable, and not opted-out of ABI break) or when it carries a clear performance benefit (if the rule-of-zero type is trivially relocatable by virtue of its members, and small enough to be passed in registers), and to use the caller-destroy ABI in all other cases.  This would be entirely by choice of the Itanium C++ ABI group; the language would make it clear that the implementation is permitted to use caller-destroy ABI for everything other than immovable relocatable types.

A type that is immovable, relocatable and opted out of ABI break becomes nonrelocatable as a function parameter; this is fine since it means that adding the (nonstandard) no-abi-break attribute has the effect of taking a well-formed program and making it ill-formed, which is an acceptable behavior for an attribute by Timur's ignorability rule.