Date: Mon, 19 Dec 2022 16:48:16 +0000
On Mon, 19 Dec 2022 at 12:33, Sébastien Bini <sebastien.bini_at_[hidden]>
wrote:
> Hello all,
>
> This post is mainly for the brave ones that followed the discussion about
> relocation with the putative reloc keyword. I am in the process of writing
> the proposal, and I find that the ABI stability section is the weakest
> point.
>
> For everyone to follow, here is a brief recap: we want to provide full
> relocation support in the language. relocation happens through its own
> constructor, which destructively steals the resources of the source object.
> The destructor on the source object is then never called.
>
> Problem is enabling relocation from function parameters passed by value.
> For this to work, we need the ABI of the function to be callee-destroy (the
> function itself is responsible for destroying its parameters). Otherwise if
> the ABI were caller-destroy, then the function could not avoid the call to
> the destructor on its input parameters, should some be passed to a
> relocation constructor.
>
> The ABI break is planned to be opt-in and opt-out. Declaring a relocation
> constructor on a class-type will force each function that takes that class
> as value parameter to be callee-destroy (potential ABI break, depending on
> compilers).
>
> class T { T(T); /* reloc ctor declaration */ };
> void foo(T); //< now forcibly callee-destroy
>
> It can also be opted-out, by adding a new implementation-defined attribute
> on the class definition, so that despite the fact that a relocation
> constructor may be declared, the ABI of functions are not impacted:
>
> class [[no_parameter_reloc]] T { T(T); /* reloc ctor declaration */ };
> void foo(T); //< callee-destroy constraint no longer applies
>
I agree with the need for the attribute, but wouldn't it be better to
attach it to the relocating constructor?
class T { public: T(T) [[no_parameter_reloc]]; };
That way code that wants to be backwards compatible has fewer places where
a feature-test macro check is required.
This works well for many cases. Library vendors migrating to this proposal
> can simply carefully pick which classes they choose to declare a relocation
> ctor in, and then also add the opt-out attribute in the class definition if
> they wish to provide ABI stability.
>
> The problem I foresee is that this has terrible composability. If I
> declare a relocation ctor to my MyString class-type, and opt-out of the
> ABI break, what happens to all the functions where std::pair<MyString,
> int> is passed by value? We have many good reasons not to add the opt-out
> parameter to std::pair. Hence, with the given rules, that would mean that
> there is no way of ensuring ABI stability on such functions.
>
Good spot, I hadn't thought of this. However, it's only a problem for
non-aggregate class templates that place the contained value(s) directly
within the storage of the class, right? pair and tuple, optional, expected
and variant; are there any others in the library?
Aggregates should be fine since the attribute can propagate to the class
automatically. For example, std::array should propagate the attribute from
T, since it has a single non-static data member of type T[N].
I'm thinking of tweaking the rules a little bit. First, we keep the opt-out
> attribute on the class definition, but it is no longer
> implementation-defined. Second, the opt-out attribute also provides an
> (auto) syntax: class [[no_parameter_reloc(auto)]] that will inspect the
> class subobjects (direct or virtual bases and non-static data-members) and
> decide accordingly. Here are the deduction rules:
>
> - If one of the class subobjects hasn't opted-out, and (maybe
> implicitly) declares a relocation constructor and is not trivial (trivial
> implies destructor is a no-op) then the class opts-in for the ABI break ;
> - Otherwise if one of the class subobjects opted-out, and that it
> (maybe implicitly) declares a relocation constructor, and it is not
> trivial, then the class opts-out of the ABI break ;
> - Otherwise the class opts-in by default.
>
> With the updated rules, if std::pair has the [[no_parameter_reloc(auto)]]
> attribute, then std::pair<MyString,int> opts-out, while
> std::pair<MyString,UserDefinedOptInType> opts-in.
>
It's not clear that `auto` would be sufficient. For example, an
implementation of optional/variant that uses a byte buffer instead of a
union would not contain a non-static data member manifest subobject of type
T.
Instead, I think it would be better to provide a way to query the
attribute, and let library code compute propagation appropriately:
optional(optional) [[no_parameter_reloc(is_no_parameter_reloc<T>)]];
variant(variant) [[no_parameter_reloc(is_no_parameter_reloc<Types> or
...)]];
How much third-party code do you think would make use of this attribute?
Boost, abseil, folly, sure; who else is writing utility components that
need to honor ABI like this?
I am also thinking of allowing a function attribute to opt-out on function
> level for tricky cases.
>
Such as?
Anyone have some thoughts on the matter?
>
> Regards,
> Sébastien Bini
>
wrote:
> Hello all,
>
> This post is mainly for the brave ones that followed the discussion about
> relocation with the putative reloc keyword. I am in the process of writing
> the proposal, and I find that the ABI stability section is the weakest
> point.
>
> For everyone to follow, here is a brief recap: we want to provide full
> relocation support in the language. relocation happens through its own
> constructor, which destructively steals the resources of the source object.
> The destructor on the source object is then never called.
>
> Problem is enabling relocation from function parameters passed by value.
> For this to work, we need the ABI of the function to be callee-destroy (the
> function itself is responsible for destroying its parameters). Otherwise if
> the ABI were caller-destroy, then the function could not avoid the call to
> the destructor on its input parameters, should some be passed to a
> relocation constructor.
>
> The ABI break is planned to be opt-in and opt-out. Declaring a relocation
> constructor on a class-type will force each function that takes that class
> as value parameter to be callee-destroy (potential ABI break, depending on
> compilers).
>
> class T { T(T); /* reloc ctor declaration */ };
> void foo(T); //< now forcibly callee-destroy
>
> It can also be opted-out, by adding a new implementation-defined attribute
> on the class definition, so that despite the fact that a relocation
> constructor may be declared, the ABI of functions are not impacted:
>
> class [[no_parameter_reloc]] T { T(T); /* reloc ctor declaration */ };
> void foo(T); //< callee-destroy constraint no longer applies
>
I agree with the need for the attribute, but wouldn't it be better to
attach it to the relocating constructor?
class T { public: T(T) [[no_parameter_reloc]]; };
That way code that wants to be backwards compatible has fewer places where
a feature-test macro check is required.
This works well for many cases. Library vendors migrating to this proposal
> can simply carefully pick which classes they choose to declare a relocation
> ctor in, and then also add the opt-out attribute in the class definition if
> they wish to provide ABI stability.
>
> The problem I foresee is that this has terrible composability. If I
> declare a relocation ctor to my MyString class-type, and opt-out of the
> ABI break, what happens to all the functions where std::pair<MyString,
> int> is passed by value? We have many good reasons not to add the opt-out
> parameter to std::pair. Hence, with the given rules, that would mean that
> there is no way of ensuring ABI stability on such functions.
>
Good spot, I hadn't thought of this. However, it's only a problem for
non-aggregate class templates that place the contained value(s) directly
within the storage of the class, right? pair and tuple, optional, expected
and variant; are there any others in the library?
Aggregates should be fine since the attribute can propagate to the class
automatically. For example, std::array should propagate the attribute from
T, since it has a single non-static data member of type T[N].
I'm thinking of tweaking the rules a little bit. First, we keep the opt-out
> attribute on the class definition, but it is no longer
> implementation-defined. Second, the opt-out attribute also provides an
> (auto) syntax: class [[no_parameter_reloc(auto)]] that will inspect the
> class subobjects (direct or virtual bases and non-static data-members) and
> decide accordingly. Here are the deduction rules:
>
> - If one of the class subobjects hasn't opted-out, and (maybe
> implicitly) declares a relocation constructor and is not trivial (trivial
> implies destructor is a no-op) then the class opts-in for the ABI break ;
> - Otherwise if one of the class subobjects opted-out, and that it
> (maybe implicitly) declares a relocation constructor, and it is not
> trivial, then the class opts-out of the ABI break ;
> - Otherwise the class opts-in by default.
>
> With the updated rules, if std::pair has the [[no_parameter_reloc(auto)]]
> attribute, then std::pair<MyString,int> opts-out, while
> std::pair<MyString,UserDefinedOptInType> opts-in.
>
It's not clear that `auto` would be sufficient. For example, an
implementation of optional/variant that uses a byte buffer instead of a
union would not contain a non-static data member manifest subobject of type
T.
Instead, I think it would be better to provide a way to query the
attribute, and let library code compute propagation appropriately:
optional(optional) [[no_parameter_reloc(is_no_parameter_reloc<T>)]];
variant(variant) [[no_parameter_reloc(is_no_parameter_reloc<Types> or
...)]];
How much third-party code do you think would make use of this attribute?
Boost, abseil, folly, sure; who else is writing utility components that
need to honor ABI like this?
I am also thinking of allowing a function attribute to opt-out on function
> level for tricky cases.
>
Such as?
Anyone have some thoughts on the matter?
>
> Regards,
> Sébastien Bini
>
Received on 2022-12-19 16:48:29