> On Jan 8, 2018, at 10:57 AM, David Blaikie <dblai...@gmail.com> wrote:
> (just a side note: perhaps this conversation would've been more suited to 
> cfe-dev? I sort of missed it because I only check commits once a week, unless 
> I'm specifically cc'd on something. All good though :))

I suppose it's more design-and-development than just code review, so yes, 
perhaps cfe-dev would have been more appropriate.  Regardless I should've made 
a point of CC'ing you, since you'd been part of the ongoing conversation.  
Apologies.

John.

> 
> On Wed, Jan 3, 2018 at 4:06 PM Richard Smith via cfe-commits 
> <cfe-commits@lists.llvm.org <mailto:cfe-commits@lists.llvm.org>> wrote:
> On 3 January 2018 at 15:24, John McCall via cfe-commits 
> <cfe-commits@lists.llvm.org <mailto:cfe-commits@lists.llvm.org>> wrote:
>> On Jan 3, 2018, at 5:53 PM, Richard Smith <rich...@metafoo.co.uk 
>> <mailto:rich...@metafoo.co.uk>> wrote:
>> On 3 January 2018 at 14:29, John McCall via cfe-commits 
>> <cfe-commits@lists.llvm.org <mailto:cfe-commits@lists.llvm.org>> wrote:
>> 
>>> On Jan 3, 2018, at 5:12 PM, Richard Smith <rich...@metafoo.co.uk 
>>> <mailto: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.
>> 
>> I like this idea a lot. Here's a concrete suggestion:
>> 
>> """
>> A type has a triviality override if it has the trivial_abi attribute, and it 
>> has no virtual functions nor virtual base classes, and every subobject is 
>> trivial for the purposes of calls. The attribute is ill-formed if applied to 
>> a non-template class that does not meet these criteria; the attribute is 
>> ill-formed, no diagnostic required, if applied to a templated class and no 
>> instantiation of that class can meet these criteria.
> 
> David B. and I were talking about whether this should be a required 
> diagnostic even in the template case, and I think we settled on "no" because 
> it could interfere with portability.  Imagine that std::unique_ptr were made 
> trivial_abi in some STL; classes containing a std::unique_ptr could only be 
> trivial_abi on that target.  On the other hand, I get that it's nice to have 
> a static guarantee that the attribute meant something.
> 
> Maybe we could make it ill-formed, no diagnostic required, if the attribute 
> is present but the class can never have a triviality override (for any 
> instantiation, if a template).  That would give us wide leeway to complain 
> about putting it on a class with a direct virtual base, or when there's a 
> non-trivial subobject whose type is defined in the "same library", or if 
> specifically requested to.
> 
> Besides, we're not actually promising to pass it "directly".  It's a totally 
> legal implementation (right now) to just ignore the attribute.  That wouldn't 
> be ABI-compatible with compilers that implement it, of course, but not 
> everyone cares about that.
> 
> I'd be OK with, say, downgrading this from an error to a warning, or making 
> it an error-with-a-warning-flag. But even in the case of a unique_ptr member, 
> I think a user would typically want to be told that the attribute didn't have 
> the effect they're looking for: that's exactly the case where I would expect 
> people to try to "override" the triviality of a subobject using the 
> attribute, so I think the diagnostic should be enabled by default.
> 
> I suppose the "ill-formed, no diagnostic required" formulation at least gives 
> other implementers of the attribute a hint that they should also consider 
> producing a diagnostic.
>> A type is trivial for the purposes of calls if:
>>   - it has a triviality override, or
>>   - it is trivial for the purposes of calls as specified in the Itanium C++ 
>> ABI, or would be so if all direct or indirect construction and destruction 
>> of types with a triviality override were ignored when computing the 
>> triviality (but not deletedness) of functions
>> """
> 
> I like this wording, since we don't have to actually repeat anything from the 
> standard.
> 
>> So we would still compute both a "trivial" and a "trivial for the purposes 
>> of calls" flag for defaulted copy constructors, move constructors, and 
>> destructors, but we'd only do the overload resolution and deletedness 
>> analysis once; trivial would always imply trivial for the purposes of calls, 
>> and the converse only fails when there is a subobject whose type has a 
>> triviality override.
> 
> Right.
> 
> John.
> 
>> Put another way, we'd have four levels of triviality for special members: 
>> deleted, non-trivial, trivial for purposes of calls, and trivial. The 
>> triviality of a deleted member is "deleted". The triviality of any 
>> trivial_abi member is "trivial for purposes of calls". The triviality of any 
>> other user-provided member is "non-trivial". And the triviality of a 
>> non-user-provided non-deleted member is "deleted" if any subobject call is 
>> ill-formed, otherwise "non-trivial" for the special cases involving virtual 
>> bases and virtual functions, otherwise the mimimum of that value over all 
>> subobject calls. And a type is trivial for the purposes of calls unless any 
>> copy ctor, move ctor or dtor is "non-trivial" or all copy and move 
>> constructors are "deleted".
>>> 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 <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