r304376 - PR33232: implement support for MSVC's __is_trivially_destructible trait.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Fri Jun 2 15:11:11 PDT 2017


I think it comes down to a question of whether we want to guarantee that
these traits are treated as not being identifiers. In some sense, it's an
implementation detail that we model them as keywords -- and as it happens,
there are some circumstances in which we *don't* model them as keywords.
For example:

// Starts off as a keyword
const bool a = __is_trivial(int);
#if __is_identifier(__is_trivial)
#error not an identifier
#endif

// Downgraded to a simple identifier
template<typename> struct __is_trivial {};
#if !__is_identifier(__is_trivial)
#error is an identifier
#endif

// Can still be used as a trait, though
const bool b = __is_trivial(int);

The implementation of the above would be simpler if we *never* treated
these traits as keywords, but that would break libc++ if it relies on
__is_identifier to check this.

Ultimately, though if people are already relying on using __is_identifier
for this, then maybe we should just say that's the supported way to check
for these things (and keep it working that way if we ever do change the
internal representation to something else).

On 2 June 2017 at 13:44, Eric Fiselier <eric at efcs.ca> wrote:

> I've been using !__is_identifier to test for things like that. It seems to
> be the most consistent way.
> Is there some problem with this?
>
> /Eric
>
> On Thu, Jun 1, 2017 at 6:46 PM, Richard Smith <richard at metafoo.co.uk>
> wrote:
>
>> On 31 May 2017 at 17:41, Eric Fiselier <eric at efcs.ca> wrote:
>>
>>> I'm assuming libc++ should move to this trait instead?
>>>
>>
>> Yes, that'd be a good idea. Though now that you mention it, I'm not sure
>> we have a good feature detection story for these builtins. Looks like a
>> bunch of the existing ones are (oddly) covered by __has_feature, and they
>> can all be discovered through !__is_identifier, but __has_extension or
>> __has_builtin seem like the right detection mechanism and neither of them
>> works here. ;-(
>>
>> Thoughts?
>>
>>
>>> /Eric
>>>
>>> On Wed, May 31, 2017 at 6:28 PM, Richard Smith via cfe-commits <
>>> cfe-commits at lists.llvm.org> wrote:
>>>
>>>> Author: rsmith
>>>> Date: Wed May 31 19:28:16 2017
>>>> New Revision: 304376
>>>>
>>>> URL: http://llvm.org/viewvc/llvm-project?rev=304376&view=rev
>>>> Log:
>>>> PR33232: implement support for MSVC's __is_trivially_destructible trait.
>>>>
>>>> Unlike the GCC-compatible __has_trivial_destructor trait, this one
>>>> computes the
>>>> right answer rather than performing the quirky set of checks described
>>>> in GCC's
>>>> documentation (https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html).
>>>>
>>>> MSVC also has a __has_trivial_destructor trait which is the same as its
>>>> (and
>>>> now Clang's) __is_trivially_destructible trait; we might want to
>>>> consider
>>>> changing the behavior of __has_trivial_destructor if we're targeting an
>>>> MSVC
>>>> platform, but I'm not doing so for now.
>>>>
>>>> While implementing this I found that we were incorrectly rejecting
>>>> __is_destructible queries on arrays of unknown bound of incomplete
>>>> types; that
>>>> too is fixed, and I've added similar tests for other traits for good
>>>> measure.
>>>>
>>>> Modified:
>>>>     cfe/trunk/include/clang/Basic/TokenKinds.def
>>>>     cfe/trunk/include/clang/Basic/TypeTraits.h
>>>>     cfe/trunk/lib/Sema/SemaExprCXX.cpp
>>>>     cfe/trunk/test/SemaCXX/type-traits.cpp
>>>>
>>>> Modified: cfe/trunk/include/clang/Basic/TokenKinds.def
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/
>>>> Basic/TokenKinds.def?rev=304376&r1=304375&r2=304376&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/include/clang/Basic/TokenKinds.def (original)
>>>> +++ cfe/trunk/include/clang/Basic/TokenKinds.def Wed May 31 19:28:16
>>>> 2017
>>>> @@ -411,6 +411,7 @@ TYPE_TRAIT_1(__is_sealed, IsSealed, KEYM
>>>>
>>>>  // MSVC12.0 / VS2013 Type Traits
>>>>  TYPE_TRAIT_1(__is_destructible, IsDestructible, KEYMS)
>>>> +TYPE_TRAIT_1(__is_trivially_destructible, IsTriviallyDestructible,
>>>> KEYCXX)
>>>>  TYPE_TRAIT_1(__is_nothrow_destructible, IsNothrowDestructible, KEYMS)
>>>>  TYPE_TRAIT_2(__is_nothrow_assignable, IsNothrowAssignable, KEYCXX)
>>>>  TYPE_TRAIT_N(__is_constructible, IsConstructible, KEYCXX)
>>>> @@ -439,7 +440,6 @@ TYPE_TRAIT_2(__is_convertible_to, IsConv
>>>>  TYPE_TRAIT_1(__is_empty, IsEmpty, KEYCXX)
>>>>  TYPE_TRAIT_1(__is_enum, IsEnum, KEYCXX)
>>>>  TYPE_TRAIT_1(__is_final, IsFinal, KEYCXX)
>>>> -// Tentative name - there's no implementation of std::is_literal_type
>>>> yet.
>>>>  TYPE_TRAIT_1(__is_literal, IsLiteral, KEYCXX)
>>>>  // Name for GCC 4.6 compatibility - people have already written
>>>> libraries using
>>>>  // this name unfortunately.
>>>>
>>>> Modified: cfe/trunk/include/clang/Basic/TypeTraits.h
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/
>>>> Basic/TypeTraits.h?rev=304376&r1=304375&r2=304376&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/include/clang/Basic/TypeTraits.h (original)
>>>> +++ cfe/trunk/include/clang/Basic/TypeTraits.h Wed May 31 19:28:16 2017
>>>> @@ -65,6 +65,7 @@ namespace clang {
>>>>      UTT_IsStandardLayout,
>>>>      UTT_IsTrivial,
>>>>      UTT_IsTriviallyCopyable,
>>>> +    UTT_IsTriviallyDestructible,
>>>>      UTT_IsUnion,
>>>>      UTT_IsUnsigned,
>>>>      UTT_IsVoid,
>>>>
>>>> Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaE
>>>> xprCXX.cpp?rev=304376&r1=304375&r2=304376&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
>>>> +++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Wed May 31 19:28:16 2017
>>>> @@ -4080,24 +4080,23 @@ static bool CheckUnaryTypeTraitTypeCompl
>>>>            Loc, ArgTy, diag::err_incomplete_type_used
>>>> _in_type_trait_expr);
>>>>      return true;
>>>>
>>>> -  // C++0x [meta.unary.prop] Table 49 requires the following traits to
>>>> be
>>>> -  // applied to a complete type.
>>>> +  // C++1z [meta.unary.prop]:
>>>> +  //   remove_all_extents_t<T> shall be a complete type or cv void.
>>>>    case UTT_IsAggregate:
>>>>    case UTT_IsTrivial:
>>>>    case UTT_IsTriviallyCopyable:
>>>>    case UTT_IsStandardLayout:
>>>>    case UTT_IsPOD:
>>>>    case UTT_IsLiteral:
>>>> +    ArgTy = QualType(ArgTy->getBaseElementTypeUnsafe(), 0);
>>>> +    LLVM_FALLTHROUGH;
>>>>
>>>> +  // C++1z [meta.unary.prop]:
>>>> +  //   T shall be a complete type, cv void, or an array of unknown
>>>> bound.
>>>>    case UTT_IsDestructible:
>>>>    case UTT_IsNothrowDestructible:
>>>> -    // Fall-through
>>>> -
>>>> -    // These trait expressions are designed to help implement
>>>> predicates in
>>>> -    // [meta.unary.prop] despite not being named the same. They are
>>>> specified
>>>> -    // by both GCC and the Embarcadero C++ compiler, and require the
>>>> complete
>>>> -    // type due to the overarching C++0x type predicates being
>>>> implemented
>>>> -    // requiring the complete type.
>>>> +  case UTT_IsTriviallyDestructible:
>>>> +  // Per the GCC type traits documentation, the same constraints apply
>>>> to these.
>>>>    case UTT_HasNothrowAssign:
>>>>    case UTT_HasNothrowMoveAssign:
>>>>    case UTT_HasNothrowConstructor:
>>>> @@ -4109,17 +4108,11 @@ static bool CheckUnaryTypeTraitTypeCompl
>>>>    case UTT_HasTrivialCopy:
>>>>    case UTT_HasTrivialDestructor:
>>>>    case UTT_HasVirtualDestructor:
>>>> -    // Arrays of unknown bound are expressly allowed.
>>>> -    QualType ElTy = ArgTy;
>>>> -    if (ArgTy->isIncompleteArrayType())
>>>> -      ElTy = S.Context.getAsArrayType(ArgTy)->getElementType();
>>>> -
>>>> -    // The void type is expressly allowed.
>>>> -    if (ElTy->isVoidType())
>>>> +    if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType())
>>>>        return true;
>>>>
>>>>      return !S.RequireCompleteType(
>>>> -      Loc, ElTy, diag::err_incomplete_type_used_in_type_trait_expr);
>>>> +        Loc, ArgTy, diag::err_incomplete_type_used
>>>> _in_type_trait_expr);
>>>>    }
>>>>  }
>>>>
>>>> @@ -4356,6 +4349,7 @@ static bool EvaluateUnaryTypeTrait(Sema
>>>>               !RD->hasNonTrivialCopyAssignment();
>>>>      return false;
>>>>    case UTT_IsDestructible:
>>>> +  case UTT_IsTriviallyDestructible:
>>>>    case UTT_IsNothrowDestructible:
>>>>      // C++14 [meta.unary.prop]:
>>>>      //   For reference types, is_destructible<T>::value is true.
>>>> @@ -4373,6 +4367,11 @@ static bool EvaluateUnaryTypeTrait(Sema
>>>>      if (T->isIncompleteType() || T->isFunctionType())
>>>>        return false;
>>>>
>>>> +    // A type that requires destruction (via a non-trivial destructor
>>>> or ARC
>>>> +    // lifetime semantics) is not trivially-destructible.
>>>> +    if (UTT == UTT_IsTriviallyDestructible && T.isDestructedType())
>>>> +      return false;
>>>> +
>>>>      // C++14 [meta.unary.prop]:
>>>>      //   For object types and given U equal to
>>>> remove_all_extents_t<T>, if the
>>>>      //   expression std::declval<U&>().~U() is well-formed when
>>>> treated as an
>>>>
>>>> Modified: cfe/trunk/test/SemaCXX/type-traits.cpp
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/t
>>>> ype-traits.cpp?rev=304376&r1=304375&r2=304376&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/test/SemaCXX/type-traits.cpp (original)
>>>> +++ cfe/trunk/test/SemaCXX/type-traits.cpp Wed May 31 19:28:16 2017
>>>> @@ -252,6 +252,11 @@ void is_pod()
>>>>    { int arr[F(__is_pod(void))]; }
>>>>    { int arr[F(__is_pod(cvoid))]; }
>>>>  // { int arr[F(__is_pod(NonPODUnion))]; }
>>>> +
>>>> +  { int arr[T(__is_pod(ACompleteType))]; }
>>>> +  { int arr[F(__is_pod(AnIncompleteType))]; } // expected-error
>>>> {{incomplete type}}
>>>> +  { int arr[F(__is_pod(AnIncompleteType[]))]; } // expected-error
>>>> {{incomplete type}}
>>>> +  { int arr[F(__is_pod(AnIncompleteType[1]))]; } // expected-error
>>>> {{incomplete type}}
>>>>  }
>>>>
>>>>  typedef Empty EmptyAr[10];
>>>> @@ -287,6 +292,11 @@ void is_empty()
>>>>    { int arr[F(__is_empty(IntArNB))]; }
>>>>    { int arr[F(__is_empty(HasAnonymousUnion))]; }
>>>>  //  { int arr[F(__is_empty(DerivesVirt))]; }
>>>> +
>>>> +  { int arr[T(__is_empty(ACompleteType))]; }
>>>> +  { int arr[F(__is_empty(AnIncompleteType))]; } // expected-error
>>>> {{incomplete type}}
>>>> +  { int arr[F(__is_empty(AnIncompleteType[]))]; }
>>>> +  { int arr[F(__is_empty(AnIncompleteType[1]))]; }
>>>>  }
>>>>
>>>>  typedef Derives ClassType;
>>>> @@ -511,6 +521,8 @@ void is_aggregate()
>>>>    constexpr bool TrueAfterCpp14 = __cplusplus > 201402L;
>>>>
>>>>    __is_aggregate(AnIncompleteType); // expected-error {{incomplete
>>>> type}}
>>>> +  __is_aggregate(AnIncompleteType[]); // expected-error {{incomplete
>>>> type}}
>>>> +  __is_aggregate(AnIncompleteType[1]); // expected-error {{incomplete
>>>> type}}
>>>>    __is_aggregate(AnIncompleteTypeAr); // expected-error {{incomplete
>>>> type}}
>>>>    __is_aggregate(AnIncompleteTypeArNB); // expected-error
>>>> {{incomplete type}}
>>>>    __is_aggregate(AnIncompleteTypeArMB); // expected-error
>>>> {{incomplete type}}
>>>> @@ -1220,6 +1232,13 @@ void is_trivial2()
>>>>    int t32[F(__is_trivial(SuperNonTrivialStruct))];
>>>>    int t33[F(__is_trivial(NonTCStruct))];
>>>>    int t34[F(__is_trivial(ExtDefaulted))];
>>>> +
>>>> +  int t40[T(__is_trivial(ACompleteType))];
>>>> +  int t41[F(__is_trivial(AnIncompleteType))]; // expected-error
>>>> {{incomplete type}}
>>>> +  int t42[F(__is_trivial(AnIncompleteType[]))]; // expected-error
>>>> {{incomplete type}}
>>>> +  int t43[F(__is_trivial(AnIncompleteType[1]))]; // expected-error
>>>> {{incomplete type}}
>>>> +  int t44[F(__is_trivial(void))];
>>>> +  int t45[F(__is_trivial(const volatile void))];
>>>>  }
>>>>
>>>>  void is_trivially_copyable2()
>>>> @@ -1257,6 +1276,13 @@ void is_trivially_copyable2()
>>>>
>>>>    int t34[T(__is_trivially_copyable(const int))];
>>>>    int t35[T(__is_trivially_copyable(volatile int))];
>>>> +
>>>> +  int t40[T(__is_trivially_copyable(ACompleteType))];
>>>> +  int t41[F(__is_trivially_copyable(AnIncompleteType))]; //
>>>> expected-error {{incomplete type}}
>>>> +  int t42[F(__is_trivially_copyable(AnIncompleteType[]))]; //
>>>> expected-error {{incomplete type}}
>>>> +  int t43[F(__is_trivially_copyable(AnIncompleteType[1]))]; //
>>>> expected-error {{incomplete type}}
>>>> +  int t44[F(__is_trivially_copyable(void))];
>>>> +  int t45[F(__is_trivially_copyable(const volatile void))];
>>>>  }
>>>>
>>>>  struct CStruct {
>>>> @@ -1320,6 +1346,13 @@ void is_standard_layout()
>>>>    int t15[F(__is_standard_layout(CppStructNonStandardByBaseAr))];
>>>>    int t16[F(__is_standard_layout(CppStructNonStandardBySameBase))];
>>>>    int t17[F(__is_standard_layout(CppStructNonStandardBy2ndVirtBase))];
>>>> +
>>>> +  int t40[T(__is_standard_layout(ACompleteType))];
>>>> +  int t41[F(__is_standard_layout(AnIncompleteType))]; //
>>>> expected-error {{incomplete type}}
>>>> +  int t42[F(__is_standard_layout(AnIncompleteType[]))]; //
>>>> expected-error {{incomplete type}}
>>>> +  int t43[F(__is_standard_layout(AnIncompleteType[1]))]; //
>>>> expected-error {{incomplete type}}
>>>> +  int t44[F(__is_standard_layout(void))];
>>>> +  int t45[F(__is_standard_layout(const volatile void))];
>>>>  }
>>>>
>>>>  void is_signed()
>>>> @@ -2133,6 +2166,13 @@ void trivial_checks()
>>>>                                 TrivialMoveButNotCopy)))]; }
>>>>    { int arr[T((__is_assignable(TrivialMoveButNotCopy &,
>>>>                                 TrivialMoveButNotCopy &&)))]; }
>>>> +
>>>> +  { int arr[T(__is_assignable(ACompleteType, ACompleteType))]; }
>>>> +  { int arr[F(__is_assignable(AnIncompleteType, AnIncompleteType))];
>>>> } // expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_assignable(AnIncompleteType[],
>>>> AnIncompleteType[]))]; }
>>>> +  { int arr[F(__is_assignable(AnIncompleteType[1],
>>>> AnIncompleteType[1]))]; } // expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_assignable(void, void))]; }
>>>> +  { int arr[F(__is_assignable(const volatile void, const volatile
>>>> void))]; }
>>>>  }
>>>>
>>>>  void constructible_checks() {
>>>> @@ -2164,6 +2204,19 @@ void constructible_checks() {
>>>>
>>>>    // PR25513
>>>>    { int arr[F(__is_constructible(int(int)))]; }
>>>> +
>>>> +  { int arr[T(__is_constructible(ACompleteType))]; }
>>>> +  { int arr[T(__is_nothrow_constructible(ACompleteType))]; }
>>>> +  { int arr[F(__is_constructible(AnIncompleteType))]; } //
>>>> expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_nothrow_constructible(AnIncompleteType))]; } //
>>>> expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_constructible(AnIncompleteType[]))]; }
>>>> +  { int arr[F(__is_nothrow_constructible(AnIncompleteType[]))]; }
>>>> +  { int arr[F(__is_constructible(AnIncompleteType[1]))]; } //
>>>> expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_nothrow_constructible(AnIncompleteType[1]))]; } //
>>>> expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_constructible(void))]; }
>>>> +  { int arr[F(__is_nothrow_constructible(void))]; }
>>>> +  { int arr[F(__is_constructible(const volatile void))]; }
>>>> +  { int arr[F(__is_nothrow_constructible(const volatile void))]; }
>>>>  }
>>>>
>>>>  // Instantiation of __is_trivially_constructible
>>>> @@ -2192,6 +2245,13 @@ void is_trivially_constructible_test() {
>>>>    { int arr[F((is_trivially_constructible<NonTrivialDefault>::value))];
>>>> }
>>>>    { int arr[F((is_trivially_constructible<ThreeArgCtor, int*, char*,
>>>> int&>::value))]; }
>>>>    { int arr[F((is_trivially_constructible<Abstract>::value))]; } //
>>>> PR19178
>>>> +
>>>> +  { int arr[T(__is_trivially_constructible(ACompleteType))]; }
>>>> +  { int arr[F(__is_trivially_constructible(AnIncompleteType))]; } //
>>>> expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_trivially_constructible(AnIncompleteType[]))]; }
>>>> +  { int arr[F(__is_trivially_constructible(AnIncompleteType[1]))]; }
>>>> // expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_trivially_constructible(void))]; }
>>>> +  { int arr[F(__is_trivially_constructible(const volatile void))]; }
>>>>  }
>>>>
>>>>  void array_rank() {
>>>> @@ -2218,6 +2278,13 @@ void is_destructible_test() {
>>>>    { int arr[F(__is_destructible(AllDeleted))]; }
>>>>    { int arr[T(__is_destructible(ThrowingDtor))]; }
>>>>    { int arr[T(__is_destructible(NoThrowDtor))]; }
>>>> +
>>>> +  { int arr[T(__is_destructible(ACompleteType))]; }
>>>> +  { int arr[F(__is_destructible(AnIncompleteType))]; } //
>>>> expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_destructible(AnIncompleteType[]))]; }
>>>> +  { int arr[F(__is_destructible(AnIncompleteType[1]))]; } //
>>>> expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_destructible(void))]; }
>>>> +  { int arr[F(__is_destructible(const volatile void))]; }
>>>>  }
>>>>
>>>>  void is_nothrow_destructible_test() {
>>>> @@ -2234,4 +2301,33 @@ void is_nothrow_destructible_test() {
>>>>    { int arr[F(__is_nothrow_destructible(ThrowingDtor))]; }
>>>>    { int arr[T(__is_nothrow_destructible(NoExceptDtor))]; }
>>>>    { int arr[T(__is_nothrow_destructible(NoThrowDtor))]; }
>>>> +
>>>> +  { int arr[T(__is_nothrow_destructible(ACompleteType))]; }
>>>> +  { int arr[F(__is_nothrow_destructible(AnIncompleteType))]; } //
>>>> expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_nothrow_destructible(AnIncompleteType[]))]; }
>>>> +  { int arr[F(__is_nothrow_destructible(AnIncompleteType[1]))]; } //
>>>> expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_nothrow_destructible(void))]; }
>>>> +  { int arr[F(__is_nothrow_destructible(const volatile void))]; }
>>>> +}
>>>> +
>>>> +void is_trivially_destructible_test() {
>>>> +  { int arr[T(__is_trivially_destructible(int))]; }
>>>> +  { int arr[T(__is_trivially_destructible(int[2]))]; }
>>>> +  { int arr[F(__is_trivially_destructible(int[]))]; }
>>>> +  { int arr[F(__is_trivially_destructible(void))]; }
>>>> +  { int arr[T(__is_trivially_destructible(int &))]; }
>>>> +  { int arr[F(__is_trivially_destructible(HasDest))]; }
>>>> +  { int arr[F(__is_trivially_destructible(AllPrivate))]; }
>>>> +  { int arr[F(__is_trivially_destructible(SuperNonTrivialStruct))]; }
>>>> +  { int arr[T(__is_trivially_destructible(AllDefaulted))]; }
>>>> +  { int arr[F(__is_trivially_destructible(AllDeleted))]; }
>>>> +  { int arr[F(__is_trivially_destructible(ThrowingDtor))]; }
>>>> +  { int arr[F(__is_trivially_destructible(NoThrowDtor))]; }
>>>> +
>>>> +  { int arr[T(__is_trivially_destructible(ACompleteType))]; }
>>>> +  { int arr[F(__is_trivially_destructible(AnIncompleteType))]; } //
>>>> expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_trivially_destructible(AnIncompleteType[]))]; }
>>>> +  { int arr[F(__is_trivially_destructible(AnIncompleteType[1]))]; }
>>>> // expected-error {{incomplete type}}
>>>> +  { int arr[F(__is_trivially_destructible(void))]; }
>>>> +  { int arr[F(__is_trivially_destructible(const volatile void))]; }
>>>>  }
>>>>
>>>>
>>>> _______________________________________________
>>>> cfe-commits mailing list
>>>> cfe-commits at lists.llvm.org
>>>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>>>
>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20170602/4beafe26/attachment-0001.html>


More information about the cfe-commits mailing list