[cfe-dev] The state of clang type traits

Howard Hinnant hhinnant at apple.com
Wed May 11 17:26:36 PDT 2011


On May 11, 2011, at 8:05 PM, Douglas Gregor wrote:

> 
> On May 11, 2011, at 8:36 AM, Howard Hinnant wrote:
> 
>> On May 9, 2011, at 3:27 PM, Howard Hinnant wrote:
>> 
>>> Good work has been done on the clang type traits since the last time I did a survey.  I wanted to re-do the survey based on the good clang documentation at:
>>> 
>>> http://clang.llvm.org/docs/LanguageExtensions.html#checking_type_traits
>>> 
>>> I've put the updated results here:
>>> 
>>> http://libcxx.llvm.org/type_traits_design.html
>>> 
>>> The big needs are shown in the boxes with the red background.  The "nothrow" traits are especially needed in light of the recent work done for noexcept.  From the client's point of view, the only behavior change a noexcept spec gives to a member function is whether or not the associated is_nothrow_ trait returns true or not.  We currently can not detect nothrow destructors, nothrow constructors other than the default and copy constructors, nor nothrow assignment operators other than the copy assignment operator.
>>> 
>>> Three of the traits I can do in the library if CWG 1170 is implemented:
>>> 
>>> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1170
>>> 
>>> This is the one where access checks should be done as part of the substitution process.  However I'd be happier with compiler type traits for those three.
>> 
>> I've learned that if CWG 1170 is implemented, I can also do library implementations of all of the nothrow traits by putting expressions into noexcept.  For example:
>> 
>> // is_nothrow_destructible
>> 
>> template <bool, class _Tp> struct __is_nothrow_destructible;
>> 
>> template <class _Tp>
>> struct __is_nothrow_destructible<false, _Tp>
>>   : public false_type
>> {
>> };
>> 
>> template <class _Tp>
>> struct __is_nothrow_destructible<true, _Tp>
>>   : public integral_constant<bool, noexcept(_STD::declval<_Tp>().~_Tp()) >
>> {
>> };
>> 
>> template <class _Tp>
>> struct is_nothrow_destructible
>>   : public __is_nothrow_destructible<is_destructible<_Tp>::value, _Tp>
>> {
>> };
>> 
>> This reduces the highest priority needs for clang type trait support to:
>> 
>> 1.  Implement CWG 1170
> 
> Done in Clang r131209.

OH COOL!!! :-)


> 
>> 2. __is_trivially_constructible(T, Args...)
>> 3. __is_trivially_assignable(T, U)
> 
> Interesting. I don't know what a "trivial operation" is, but I suspect that this means something like "no calls to anything that isn't a trivial constructor (in the first case) or a trivial assignment operator (in the second case)".

Correct (e.g. [class.copy]/12) with scalars thrown in, cv-qualifers and arrays-of thrown out for good measure ([basic.types]/9).

> 
>> 4. __is_trivial(T)
> 
> This is already implemented in mainline.

Wow, just now?!  Same syntax?  Does it cover scalars?

> 
>> 5. __is_trivially_copyable(T)
> 
> Should be "trivial", since the Clang AST already exposes this information.
> 
>> 6. __is_standard_layout(T)
> 
> This is already implemented on mainline.

This really feels like Christmas in May! :-)

> 
>> 7. __underlying_type(T)
> 
> Interesting. The C++0x std::underlying_type is only defined for enumeration types. However, the various character types (wchar_t, char16_t, char32_t) also have underlying types. I suggest that implement the __underlying_type primitive also do the right thing for the character types.

Sounds good to me.  We're allowed to give reasonable answers for the undefined behavior when T is not an enum.  And if we later decide that's a bad idea, it is easy enough to filter out at the library level.

> 
> Note that implementing this one correctly is significantly more difficult than any of the others, since it impacts the type system.

Ok.

> 
>> An alternative to 2-5 is to implement a __trivial(expression) that can be tested similar to noexcept(expression).  This would allow me to use techniques as shown above for is_nothrow_destructible.
> 
> … except that I don't know what the a "trivial" expression is. The standard doesn't define this notion at all. I think we should keep our trivial type traits fairly narrow.

I'm confused with your point 4.  I don't know what is already implemented in mainline.  But some example code would go a long way towards my education.

For the purpose of the std::traits, "trivial" will only be applied to special members, "pseudo" special members of scalars, everything possibly cv-qualified ([basic.types]/9), arrays of those types, and void.  I can do special casing for void, arrays and cv-qualifers and scalars in the library.  There are two hard parts I really need help with:

1.  Detecting if a specific special member (default ctor, copy ctor, move ctor, copy assignment, move assignment, dtor) of a class is trivial.

2.  Detecting if a class has /any/ non-trivial special member ([class]/6).  E.g. if a class has a trivial A(const A&) and a non-trivial A(volatile A&), then it has a trivial copy constructor, but the class is not trivial.  (I didn't make these rules up. ;-))

Howard





More information about the cfe-dev mailing list