rjmccall added a comment.

In D92361#2427652 <https://reviews.llvm.org/D92361#2427652>, @zoecarver wrote:

>> If it is not possible to copy or move the type at all, it is not possible to 
>> copy or move it trivially.
>
> This was a bit confusing to me because I think this changed between C++14 and 
> 17. In C++14, a class was trivially copyable if it had no non-trivial 
> copy/move constructors. But now the standard requires at least one 
> non-trivial move/copy constructor. It looks like 
> <https://godbolt.org/z/47W7qK> the type traits maybe haven't caught up.

Sorry, I know that the standard defines terms in this area, so what I said was 
unnecessarily confusing.

I'm describing the normative considerations that have gone into the language 
design of `trivial_abi`, not trying to state the rule in the standard.   It 
doesn't especially matter what the standard says because this is not a standard 
feature.  We want to fit in with the standard framework as much as possible 
because it simplifies the interactions, but ultimately if we don't think the 
standard permits what we want out of this extension, we'll just diverge.

>> So S0 may representationally depend on its address, and we should 
>> ignore/diagnose trivial_abi on aggregates containing an S0.
>
> I guess the question is, does a type with all copy/move constructors deleted 
> //and the trivial_abi attribute// depend on its address? And the whole point 
> of this patch is to make that answer, "no." But, a type //without// the 
> attribute, //does// depend on its address. And therefore a type holding `S0` 
> (without the attribute) also depends on its address because it implicitly has 
> no copy/move constructors. I'm not sure I understand why we need to diagnose 
> aggregates containing an `S0` or why the trivial_abi attribute couldn't be 
> applied to those types, though.

Okay, so, this is the problem with reasoning about this the C++ way.  I 
apologize if what follows is a little long.

Types have representational needs, and those needs dictate the properties of 
correctly-implemented value operations.  However, the C++ special members are 
designed around the C++ language model; we need to talk briefly about value 
operations on a deeper level.  There are four basic value operations at this 
deeper level: original initialization, copy, destructive move, and destruction. 
 Everything else can be thought of as a combination of these basic operations 
(like copy-assignment combining destruction and copy) or a twist on one (like 
C++'s non-destructive moves, which end up being a sort of optimized copy).  The 
basic representational choices of a type dictate the properties of these four 
basic operations, and they in turn dictate the properties of 
optimally-implemented special members.

So, for example, types that manage ownership of some resource pretty much 
necessarily make initialization and destruction non-trivial, and copy must be 
non-trivial or impossible, but a true destructive move can remain non-trivial.  
(This is why C++'s moves are more like a copy than a move at this conceptual 
level.)  Types with some sort of address sensitivity, like a relative or 
interior pointer, must make copy and destructive move non-trivial or 
impossible, but destruction can remain trivial (as can initialization, if 
you're willing to ignore a possibly invalid value).  Types that register their 
objects externally must make all the operations either non-trivial or 
impossible, and so on.

What `trivial_abi` really does is make a statement about the type's destructive 
move, which is otherwise something that C++ can't talk about at all.  Passing 
something in registers, or just in general copying its bits from one location 
to another and abandoning the old location, is a trivial destructive move, and 
`trivial_abi` says that that's okay for a type irrespective of the type's own 
special members as long it's okay for all its subobjects.  So we have to look 
at every subobject and ask if it allows trivial destructive moves, and if it 
does then we can honor `trivial_abi`.  That query basically turns into asking 
whether the subobjects are either trivially-copyable or themselves are marked 
`trivial_abi`.

>> Similarly, if a subobject type has only non-trivial copy/move constructors 
>> (but a trivial destructor), we should assume that it representationally 
>> depends on its address and prevent aggregates containing it from being 
>> passed directly, even if the containing type deletes all its copy/move 
>> constructors and uses trivial_abi.
>
> I concur with the first part of this, at least. That is, `S3` should //not// 
> be passed directly because it is non-trivially copyable (because of its 
> member) and does not have the attribute. Similar to above, I don't understand 
> why we couldn't pass it directly if it had the attribute, though.

If you have a type that deletes all its copy/move constructors and does not 
declare itself `trivial_abi`, it is saying that it cannot be correctly 
copied/moved at all.  If it deletes all its copy/move constructors and declares 
itself `trivial_abi`, it is saying that it can only be destructively moved, and 
furthermore that that operation is trivial.  Making a containing type 
`trivial_abi` would make it destructively movable, which its subobject doesn't 
permit.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D92361/new/

https://reviews.llvm.org/D92361

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

Reply via email to