> 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