> On Jan 2, 2018, at 10:43 PM, Richard Smith <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.
> 
>       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
>               - 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?

>       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.

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
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to