> On Jan 3, 2018, at 5:12 PM, Richard Smith <rich...@metafoo.co.uk> wrote:
> 
> On 2 January 2018 at 20:55, John McCall via cfe-commits 
> <cfe-commits@lists.llvm.org <mailto:cfe-commits@lists.llvm.org>> wrote:
>> On Jan 2, 2018, at 10:43 PM, Richard Smith <rich...@metafoo.co.uk 
>> <mailto:rich...@metafoo.co.uk>> wrote:
>> 
>> On 2 January 2018 at 19:02, John McCall via cfe-commits 
>> <cfe-commits@lists.llvm.org <mailto:cfe-commits@lists.llvm.org>> wrote:
>> 
>>> On Jan 2, 2018, at 9:15 PM, Akira Hatanaka <ahatan...@apple.com 
>>> <mailto:ahatan...@apple.com>> wrote:
>>> 
>>> 
>>> 
>>>> On Jan 2, 2018, at 4:56 PM, Richard Smith via cfe-commits 
>>>> <cfe-commits@lists.llvm.org <mailto:cfe-commits@lists.llvm.org>> wrote:
>>>> 
>>>> On 2 January 2018 at 15:33, John McCall via cfe-commits 
>>>> <cfe-commits@lists.llvm.org <mailto:cfe-commits@lists.llvm.org>> wrote:
>>>> Hey, Richard et al.  Akira and I were talking about the right ABI rule for 
>>>> deciding can-pass-in-registers-ness for structs in the presence of 
>>>> trivial_abi, and I think I like Akira's approach but wanted to get your 
>>>> input.
>>>> 
>>>> The current definition in Itanium is:
>>>> 
>>>>   non-trivial for the purposes of calls <>
>>>>  <>
>>>> A type is considered non-trivial for the purposes of calls if:
>>>> 
>>>> it has a non-trivial copy constructor, move constructor, or destructor, or
>>>>  <>
>>>> I'm assuming we're implicitly excluding deleted functions here. (I'd 
>>>> prefer to make that explicit; this has been the source of a number of ABI 
>>>> mismatches.)
>>>> all of its copy and move constructors are deleted.
>>>>  <>
>>>> 
>>>> I'd suggest modifying this to:
>>>> 
>>>>    A type is considered non-trivial for the purposes of calls if:
>>>>            - if has a copy constructor, move constructor, or destructor 
>>>> which is non-trivial for the purposes of calls, or
>>>>            - all of its copy and move constructors are deleted and it does 
>>>> not have the trivial_abi attribute.
>>>> 
>>>>    A copy/move constructor is considered trivial for the purposes of calls 
>>>> if:
>>>>            - it is user-provided and
>>>>                    - the class has the trivial_abi attribute and
>>>>                    - a defaulted definition of the constructor would be 
>>>> trivial for the purposes of calls; or
>>>> 
>>>> We'd need to say what happens if the function in question cannot validly 
>>>> be defaulted for any of the reasons in [dcl.fct.def.default]. Do we try to 
>>>> infer whether it's a copy or move constructor, and use the rules for a 
>>>> defaulted copy or move constructor? Or do we just say that's never trivial 
>>>> for the purposes of calls? Or something else? Eg:
>>>> 
>>>> struct [[clang::trivial_abi]] A {
>>>>   A(A && = make());
>>>> };
>>>> 
>>>> Here, A::A(A&&) cannot validly be defaulted. Is A trivial for the purpose 
>>>> of calls? Likewise:
>>>> 
>>>> struct [[clang::trivial_abi]] B {
>>>>   B(...);
>>>> };
>>>> struct C {
>>>>   volatile B b;
>>>> };
>>>> 
>>>> Here, C's copy constructor calls B::B(...). Is C trivial for the purpose 
>>>> of calls? (OK, Clang crashes on that example today. But still...)
>>>> 
>>>> I'd be uncomfortable making the rules in [dcl.fct.def.default] part of the 
>>>> ABI; they seem to be changing relatively frequently. Perhaps we could say 
>>>> "if the function is a copy constructor ([class.copy.ctor]/1), then 
>>>> consider what an implicitly-declared defaulted copy constructor would do; 
>>>> if it's a move constructor ([class.copy.ctor]/2), then consider what an 
>>>> implicitly-declared defaulted move constructor would do; otherwise, it's 
>>>> not trivial for the purpose of calls". That'd mean A is trivial for the 
>>>> purpose of calls and C is not, which I think is probably the right answer.
>>>> 
>>>>            - it is not user-provided and
>>>>                    - the class has no virtual functions and no virtual 
>>>> base classes, and
>>>>                    - the constructor used to copy/move each direct base 
>>>> class subobject is trivial for the purposes of calls, and
>>>>                    - for each non-static data member that is of class type 
>>>> (or array thereof), the constructor selected to copy/move that member is 
>>>> trivial for the purposes of calls.
>>>> 
>>>>    A destructor is considered trivial for the purposes of calls if:
>>>>            - it is not user-provided or the class has the trivial_abi 
>>>> attribute, and
>>>>            - the destructor is not virtual, and
>>>>            - all of the direct base classes of its class have destructors 
>>>> that are trivial for the purposes of calls, and
>>>>            - for all of the non-static data members of its class that are 
>>>> of class type (or array thereof), each such class is trivial for the 
>>>> purposes of calls.
>>>> 
>>>>    These definitions are intended to follow [class.copy.ctor]p11 and 
>>>> [class.dtor]p6 except for the special rules applicable to trivial_abi 
>>>> classes.
>>>> 
>>>> If I could rephrase: a *tor is considered trivial for for the purposes of 
>>>> calls if it is either defaulted or the class has the trivial_abi 
>>>> attribute, and the defaulted definition would satisfy the language rule 
>>>> for being trivial but with the word "trivial" replaced by "trivial for the 
>>>> purposes of calls". So only effect of the trivial_abi attribute is to 
>>>> "undo" the non-triviality implied by a user-provided *tor when computing 
>>>> triviality for the purpose of calls.
>>>> 
>>>> I think that's a reasonable rule, if we have a satisfactory notion of 
>>>> "defaulted definition".
>>>> 
>>>> I'm not sure about the "defaulted definition" rule for copy/move 
>>>> constructors in trivial_abi classes.  The intent is to allow class 
>>>> temploids with trivial_abi that are instantiated to contain non-trivial 
>>>> classes to just silently become non-trivial.  I was thinking at first that 
>>>> it would be nice to have a general rule that trivial_abi classes only 
>>>> contain trivial_abi subobjects, but unfortunately that's not consistent 
>>>> with the standard triviality rule in some silly corner cases: a 
>>>> trivially-copyable class can have a non-trivially-copyable subobject if it 
>>>> happens to copy that subobject with a trivial copy constructor.  I 
>>>> couldn't think of a better way of capturing this than the "defaulted 
>>>> definition" rule.  I considered using the actual initializers used by the 
>>>> constructor, but that would introduce a lot of new complexity: suddenly 
>>>> we'd be asking about triviality for an arbitrary constructor, and 
>>>> copy/move elision make the question somewhat ambiguous anyway.
>>>> 
>>>> Per the above examples, I don't think you can escape asking about 
>>>> triviality for an arbitrary constructor if you take this path.
>>>> 
>>>> Another option, similar to your general rule, would be to say that a type 
>>>> is considered trivial for the purpose of calls if either: (1) it is 
>>>> trivial for the purpose of calls under the current Itanium ABI rule, or 
>>>> (2) it has the trivial_abi attribute and all members and base classes have 
>>>> types that are trivial for the purpose of calls. That would sidestep the 
>>>> "defaulted definition" complexity entirely, and while it differs from the 
>>>> way that the language computes triviality normally, it doesn't seem 
>>>> fundamentally unreasonable: when we're thinking about triviality for the 
>>>> purpose of calls, there's notionally a call to the trivial copy/move ctor 
>>>> being elided, not a call to an arbitrary ctor selected by overload 
>>>> resolution, and we'd just be pushing that effect from the class itself to 
>>>> its subobjects with this attribute.
>>>>  
>>> 
>>> It sounds like a class containing a member that has a type annotated with 
>>> “trivial_abi” would not necessarily be considered trivial for the purpose 
>>> of calls according to rule (2)? For example, S1 would not be trivial for 
>>> the purpose of calls because it isn’t annotated with “trivial_abi” in the 
>>> code below:
>>> 
>>> struct [[clang::trivial_abi]] S0 {
>>>   // user-provided special functions declared here.
>>> };
>>> 
>>> struct S1 {
>>>   S0 f0;
>>> };
>>> 
>>> I thought we wanted containing classes (S1 in this case) to be trivial for 
>>> the purpose of calls too?
>> 
>> I would like that, yeah.
>> 
>> OK, I think that's fair. Then we probably need the more complex rule. Which 
>> I think means we're at something equivalent to:
>> 
>>      A type is considered non-trivial for the purposes of calls if:
>>              - if has a copy constructor, move constructor, or destructor 
>> that is not deleted and is non-trivial for the purposes of calls, or
>>              - all of its copy and move constructors are deleted and it does 
>> not have the trivial_abi attribute.
> 
> Hold on... this final "and it does not have the trivial_abi attribute" looks 
> wrong to me; it seems to break the "do what I mean"ness of the attribute. 
> Consider:
> 
> template<typename T, typename U> struct [[clang::trivial_abi]] pair { ... };
> 
> std::pair<ContainsPointerToSelf, int> f(); // returned indirect
> std::pair<ContainsPointerToSelf, NonCopyable> g(); // returned in registers 
> because all copy/move ctors deleted
> 
> That seems like a bug. Can we just strike that addition, or does one of your 
> intended use cases need it?

It was a last-minute addition that seemed like a good idea, but I was just 
thinking about all the copy/move ctors being explicitly deleted on the class, 
not any of the inheritance cases.  I agree with striking it.

The only use cases we really have in mind are
  - simple resource-owning classes like smart pointers, which would adopt the 
attribute, and
  - classes with defaulted copy/destruction semantics, which should propagate 
triviality if possible.

I just think we need to be prepared to make the rule more general than that.

>>      A copy/move constructor is considered trivial for the purposes of calls 
>> if:
>>              - it is user-provided and
>>                      - the class has the trivial_abi attribute and
>>                      - a defaulted definition of a constructor with the 
>> signature of the implicit copy/move constructor for the class would be 
>> trivial for the purposes of calls; or
> 
> One other concern here: what if the defaulted definition would be deleted? I 
> think in that case the constructor we're considering should also be treated 
> as if it were deleted. And that applies recursively: if the implicit 
> copy/move constructor would itself have been deleted, we want to treat the 
> original member of the type we're checking as being deleted. And likewise, if 
> a defaulted copy/move constructor invokes a copy/move constructor of a 
> trivial_abi class, and a defaulted copy/move constructor for that class would 
> have been deleted, we want to act as if the original defaulted copy/move 
> constructor was deleted. That seems awkward to specify in the fashion we've 
> been using until now, since the result of a triviality calculation is now 
> "deleted", "non-trivial", or "trivial", and deletedness can change in either 
> direction as a result of the attribute.

Ugh.  I feel like this problem is mostly a further indictment of the idea of 
basing this on what a defaulted definition would look like.

We could just base it on the overall trivial-for-calls-ness of the subobject 
types.  It's a very different rule from the standard triviality rule, but it's 
okay to differ here because this *only* affects special members of classes with 
the attribute.

> 
> Here's a terse summary of the rule I'm considering:
> 
> """
> For the determination of triviality for the purposes of calls, a modified 
> form of the program is considered. In this modified form, each copy or move 
> constructor or destructor of a class with the trivial_abi attribute is 
> replaced by a defaulted copy or move constructor or destructor (with the 
> signature of an implicit such declaration), and calls to the former are 
> transformed into calls to the latter within the implicit definitions of 
> defaulted special member functions. A function is deleted for the purposes of 
> calls in the original program if the corresponding function is deleted in the 
> modified program, and is otherwise trivial for the purposes of calls in the 
> original program if the corresponding function is trivial in the modified 
> program.
> 
> A type is considered non-trivial for the purposes of calls if:
>       - if has a copy constructor, move constructor, or destructor that is 
> non-deleted and non-trivial for the purposes of calls, or
>       - all of its copy and move constructors are deleted for purposes of 
> calls.
> """

Yikes.  I feel like I would have no ability to explain this rule to a user.

>>              - it is not user-provided and
>>                      - the class has no virtual functions and no virtual 
>> base classes, and
>>                      - the constructor used to copy/move each direct base 
>> class subobject is trivial for the purposes of calls, and
>>                      - for each non-static data member that is of class type 
>> (or array thereof), the constructor selected to copy/move that member is 
>> trivial for the purposes of calls.
>>      A constructor that is neither a copy constructor nor a move constructor 
>> is considered non-trivial for the purposes of calls.
> 
> This clause is there to handle constructors that are copy/move constructors 
> only because of defaulted arguments?  I wonder if this is necessary; I think 
> the allocator-like use cases would prefer that we just ignore the non-initial 
> arguments, wouldn't they?
> 
> This doesn't affect the default argument case: if a constructor has a first 
> parameter of type T / cv T& / cv T&&, and all further parameters (if any) 
> have default arguments, it is still a copy or move constructor. Rather, we 
> reach this clause in any case where "the constructor used/selected to 
> copy/move [...]" has some other first parameter type or is X::X(...); such a 
> constructor is only selected when there is no viable copy/move constructor.

Oh, which can happen even for non-user-provided constructors because it's just 
the ordinary overload rules, of course.

>>      A destructor is considered trivial for the purposes of calls if:
>>              - it is not user-provided or the class has the trivial_abi 
>> attribute, and
>>              - the destructor is not virtual, and
>>              - all of the direct base classes of its class have destructors 
>> that are trivial for the purposes of calls, and
>>              - for all of the non-static data members of its class that are 
>> of class type (or array thereof), each such class is trivial for the 
>> purposes of calls.
>> 
>> Bolded phrases are changed from John's initial email.
> 
> Thank you for the revision; this is much improved.
> 
> I'm concerned about the level of complexity we've discovered to be necessary 
> here, and in particular the necessity of having a side-notion of "trivial for 
> the purpose of calls" for all copy/move ctors and dtors, even in classes that 
> do not directly use the trivial_abi attribute. But I suppose that's 
> fundamental if we want to pass struct S1 (above) directly. I'd like a simpler 
> rule, but I'm not convinced there is one.

Well, I think the adjustment I suggest above would cap the complexity a bit; at 
least we would need these speculative investigation into defaulted definitions 
that don't actually exist.  But we'd still need to track the new kind of 
triviality for each ctor/dtor.

John.

>  
> John.
> 
>>  
>> John.
>> 
>>> 
>>>> I'm also not sure about the right rules about virtual methods.  Should we 
>>>> allow polymorphic classes to be made trivial by application of the 
>>>> attribute?
>>>> 
>>>> I think that it probably doesn't make much sense to pass dynamic classes 
>>>> indirectly unless we can avoid passing the vptr; otherwise I'd expect we'd 
>>>> use too many registers for it to be worthwhile. Perhaps as a compromise, 
>>>> we could make the attribute ill-formed if used on a class definition that 
>>>> introduces any virtual bases or explicitly declares any member functions 
>>>> as 'virtual'. That gives us the room to make this decision later if we 
>>>> find we want to.
>>>> _______________________________________________
>>>> cfe-commits mailing list
>>>> cfe-commits@lists.llvm.org <mailto:cfe-commits@lists.llvm.org>
>>>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits 
>>>> <http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits>
>> 
>> 
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits@lists.llvm.org <mailto:cfe-commits@lists.llvm.org>
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits 
>> <http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits>
>> 
>> 
> 
> 
> _______________________________________________
> cfe-commits mailing list
> cfe-commits@lists.llvm.org <mailto:cfe-commits@lists.llvm.org>
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits 
> <http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits>
> 
> 

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

Reply via email to