Quuxplusone added inline comments.

================
Comment at: docs/LanguageExtensions.rst:1096
+  equivalent to copying the underlying bytes and then dropping the source 
object
+  on the floor.
 * ``__is_destructible`` (MSVC 2013)
----------------
rjmccall wrote:
> Quuxplusone wrote:
> > rjmccall wrote:
> > > Quuxplusone wrote:
> > > > @rjmccall wrote:
> > > > > trivial_abi permits annotated types to be passed and returned in 
> > > > > registers, which is ABI-breaking. Skimming the blog post, it looks 
> > > > > like trivially_relocatable does not permit this — it merely signifies 
> > > > > that destruction is a no-op after a move construction or assignment.
> > > > 
> > > > Not necessarily a "no-op"; my canonical example is a 
> > > > CopyOnlyCXX03SharedPtr which increments a refcount on construction and 
> > > > decrements on destruction. But move-construction plus destruction 
> > > > should "balance out" and result in no observable side effects.
> > > > 
> > > > > This is usefully different in the design space, since it means you 
> > > > > can safely add the attribute retroactively to e.g. std::unique_ptr, 
> > > > > and other templates can then detect that std::unique_ptr is 
> > > > > trivially-relocatable and optimize themselves to use memcpy or 
> > > > > realloc or whatever it is that they want to do. So in that sense 
> > > > > trivial_abi is a *stronger* attribute, not a *weaker* one: the 
> > > > > property it determines ought to imply trivially_relocatable.
> > > > 
> > > > `trivial_abi` is an "orthogonal" attribute: you can have `trivial_abi` 
> > > > types with non-trivial constructors and destructors, which can have 
> > > > observable side effects. For example,
> > > > ```
> > > > struct [[clang::trivial_abi]] DestructionAnnouncer {
> > > >     ~DestructionAnnouncer() { puts("hello!"); }
> > > > };
> > > > ```
> > > > is `trivial_abi` (because of the annotation) yet not trivially 
> > > > relocatable, because its "move plus destroy" operation has observable 
> > > > side effects.
> > > > 
> > > > > The only interesting question in the language design that I know of 
> > > > > is what happens if you put the attribute on a template that's 
> > > > > instantiated to contain a sub-object that is definitely not trivially 
> > > > > relocatable / trivial-ABI. For trivial_abi, we decided that the 
> > > > > attribute is simply ignored — it implicitly only applies to 
> > > > > specializations where the attribute would be legal. I haven't dug 
> > > > > into the design enough to know what trivially_relocatable decides in 
> > > > > this situation, but the three basic options are:
> > > > >
> > > > > - the attribute always has effect and allows trivial relocation 
> > > > > regardless of the subobject types; this is obviously unsafe, so it 
> > > > > limits the safe applicability of the attribute to templates
> > > > > - the attribute is ignored, like trivial_abi is
> > > > > - the attribute is ill-formed, and you'll need to add a 
> > > > > [[trivially_relocatable(bool)]] version to support templates
> > > > 
> > > > What happens is basically the first thing you said, except that I 
> > > > disagree that it's "obviously unsafe." Right now, conditionally trivial 
> > > > relocation is possible via template metaprogramming; see the libcxx 
> > > > patch at e.g.
> > > > https://github.com/Quuxplusone/libcxx/commit/6524822c009e#diff-38adc80cec663f2f29c22e9ffc0de912
> > > > Since the attribute is an opt-in mechanism, it makes perfect sense to 
> > > > me that if you put it on a class (or class template), then it applies 
> > > > to the class, without any further sanity-checking by the compiler. The 
> > > > compiler has no reason to second-guess the programmer here.
> > > > 
> > > > However, there's one more interesting case. Suppose the programmer puts 
> > > > the attribute on a class that isn't relocatable at all! (For example, 
> > > > the union case @erichkeane mentioned, or a class type with a deleted 
> > > > destructor.) In that case, this patch *does* give an error... *unless* 
> > > > the class was produced by instantiating a template, in which case we 
> > > > *don't* give an error, because it's not the template-writer's fault.
> > > > https://p1144.godbolt.org/z/wSZPba
> > > > trivial_abi is an "orthogonal" attribute: you can have trivial_abi 
> > > > types with non-trivial constructors and destructors, which can have 
> > > > observable side effects. 
> > > 
> > > Let me cut this conversation short.  `trivial_abi` is not such an old and 
> > > widely-established attribute that we are unable to revise its definition. 
> > >  I am comfortable making the same semantic guarantees for `trivial_abi` 
> > > that you're making for `trivially_relocatable`, because I think it is in 
> > > the language's interest for `trivial_abi` to be strictly stronger than 
> > > `trivially_relocatable`.
> > > 
> > > > What happens is basically the first thing you said, except that I 
> > > > disagree that it's "obviously unsafe." 
> > > 
> > > Under your semantics, the attribute is an unchecked assertion about all 
> > > of a class's subobjects.  A class template which fails to correctly apply 
> > > the template metaprogramming trick to all of its dependently-typed 
> > > subobjects — which can be quite awkward because it creates an extra 
> > > dimension of partial specialization, and which breaks ABI by adding extra 
> > > template parameters — will be silently miscompiled to allow objects to be 
> > > memcpy'ed when they're potentially not legal to memcpy.  That is a 
> > > footgun, and it is indeed "obviously unsafe".
> > > 
> > > Now, it's fair to say that it's unsafe in a useful way: because the 
> > > attribute isn't checked, you can wrap a type you don't control in a 
> > > `trivially_relocatable` struct and thereby get the advantages of 
> > > triviality on the wrapper.  The model used by `trivial_abi` doesn't allow 
> > > that.  But I feel pretty strongly that that is not the right default 
> > > behavior for the language.
> > > Under your semantics, the attribute is an unchecked assertion about all 
> > > of a class's subobjects.
> > 
> > The attribute is an unchecked assertion about the class's //special member 
> > functions//. The attribute doesn't have anything to do with subobjects, 
> > period.
> > Vice versa, the property currently expressed by 
> > "IsNaturallyTriviallyRelocatable" is deduced from all of the class's 
> > subobjects. The programmer can overrule the "natural" property in an 
> > "unnatural" way by annotating their class with the attribute.
> > 
> > And we know this is true because it is possible to make a 
> > trivially-relocatable class type containing non-trivially-relocatable 
> > members (e.g. a class having a member of type 
> > boost::interprocess::offset_ptr), and vice versa it is possible to make a 
> > non-trivially-relocatable class containing trivially-relocatable members 
> > (e.g. boost::interprocess::offset_ptr itself, which has only one member, of 
> > integral type).
> > 
> > > A class template which fails to correctly apply the template 
> > > metaprogramming trick to all of its dependently-typed subobjects — which 
> > > can be quite awkward because it creates an extra dimension of partial 
> > > specialization
> > 
> > Agreed that it's awkward. The libc++ implementation was awkward, but 
> > definitely not challenging. The only thing that makes it at all tricky in 
> > the STL is that the STL allocator model permits fancy "pointer" types that 
> > can make e.g. std::vector non-trivially relocatable. If it weren't for 
> > fancy pointers, you wouldn't need the extra dimension.
> > 
> > > and which breaks ABI by adding extra template parameters
> > 
> > The libc++ implementation does not break ABI. The extra template parameter 
> > is concealed in a private base class.
> > https://github.com/Quuxplusone/libcxx/commit/6524822c009e#diff-38adc80cec663f2f29c22e9ffc0de912
> > 
> > > I feel pretty strongly that that is not the right default behavior for 
> > > the language.
> > 
> > Can you elaborate on that feeling (maybe in private email)? My intent with 
> > P1144 is that no industry programmer should ever see this attribute; the 
> > right default for industry programmers is to use the Rule of Zero. The 
> > reason we need the attribute is as an opt-in mechanism for the implementor 
> > of `unique_ptr`, `shared_ptr`, `vector`, and so on, //so that// the 
> > end-user can just use the Rule of Zero and everything will work fine. 
> > End-users shouldn't be messing with attributes.
> > And we know this is true because it is possible to make a 
> > trivially-relocatable class type containing non-trivially-relocatable 
> > members (e.g. a class having a member of type 
> > boost::interprocess::offset_ptr), and vice versa it is possible to make a 
> > non-trivially-relocatable class containing trivially-relocatable members 
> > (e.g. boost::interprocess::offset_ptr itself, which has only one member, of 
> > integral type).
> 
> Why would a class containing a member of type 
> `boost::interprocess::offset_ptr` be trivially-relocatable?  If you actually 
> trivially relocate an object of the class, the pointer will not be rebased 
> and so will be invalidated.  It would have to be an `offset_ptr` where you 
> happen to know that the referent will always be copied simultaneously, e.g. 
> because it's a member of the object itself.  Of course that's possible, but 
> it's also such a corner case that we shouldn't balk at saying that the 
> programmer ought to be more explicit about recognizing it.
> 
> > Agreed that it's awkward. The libc++ implementation was awkward, but 
> > definitely not challenging. The only thing that makes it at all tricky in 
> > the STL is that the STL allocator model permits fancy "pointer" types that 
> > can make e.g. std::vector non-trivially relocatable. If it weren't for 
> > fancy pointers, you wouldn't need the extra dimension.
> 
> Sure.  My point about the awkwardness is quite narrow: making the attribute 
> take a `bool` argument is just a superior way of managing this over requiring 
> a partial specialization.  Several other language attributes have been 
> heading in this same direction.
> 
> > The libc++ implementation does not break ABI. The extra template parameter 
> > is concealed in a private base class.
> 
> Ah, apologies.
> 
> >  My intent with P1144 is that no industry programmer should ever see this 
> > attribute; the right default for industry programmers is to use the Rule of 
> > Zero. ...  End-users shouldn't be messing with attributes.
> 
> Neither of these statements matches my experience.  This is an "expert" 
> feature to be sure, but the C++ community is full of experts who write their 
> own rule-of-five types and who will happily use whatever attributes are 
> available to them to make them faster.
> 
> Also, I assume you are intending for this attribute to be standardized 
> eventually, which will greatly expand its reach.
> Why would a class containing a member of type 
> `boost::interprocess::offset_ptr` be trivially-relocatable? If you actually 
> trivially relocate an object of the class, the pointer will not be rebased 
> and so will be invalidated. It would have to be an offset_ptr where you 
> happen to know that the referent will always be copied simultaneously, e.g. 
> because it's a member of the object itself.

Exactly! (And to preserve the class invariant, you'd have to add a 
copy-constructor.)

> Of course that's possible, but it's also such a corner case that we shouldn't 
> balk at saying that the programmer ought to be more explicit about 
> recognizing it.

Exactly — and the way for the programmer to explicitly recognize (or I say 
"warrant") that their class has the property is for them to annotate it with 
`[[trivially_relocatable]]`. So I guess maybe I don't understand what you mean 
by "more explicit"?

> making the attribute take a `bool` argument is just a superior way of 
> managing this

That's possible, but it's also possible that it would increase the complexity 
of parsing attributes for some implementations. I mean, we're talking about 
something like the following, right? (Using the libc++ patch as the example, 
but I've de-uglified some of the names.) So I think it's a tradeoff and I'm 
ambivalent about it, so far. (This is one of the [[ 
https://quuxplusone.github.io/blog/2018/11/11/trivially-relocatable-in-san-diego/#if-you-feel-comfortable-respondi
 | straw poll questions in P1144R0 ]].)
```
template <class T, class A = allocator<T>>
class [[trivially_relocatable(__deque_base<T, 
A>::__allow_trivial_relocation::value)]] deque
    : private __deque_base<T, A>
```

> This is an "expert" feature to be sure, but the C++ community is full of 
> experts who write their own rule-of-five types and who will happily use 
> whatever attributes are available to them to make them faster.

Agreed. But the C++ community is //also// full of working programmers who just 
write simple code with strings and vectors. :) I want 
`[[trivially_relocatable]]` to be approximately as frequently seen in real 
codebases as `[[no_unique_address]]` — i.e. maybe a couple times in that 
smart-pointer library the contractor wrote, but nowhere near the user code. If 
it's seen frequently in user code, then we've failed those users.


Repository:
  rC Clang

https://reviews.llvm.org/D50119



_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to