[clang] [clang] Implement `__builtin_is_implicit_lifetime()` (PR #101807)

Vlad Serebrennikov via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 13 03:42:19 PDT 2024


https://github.com/Endilll updated https://github.com/llvm/llvm-project/pull/101807

>From 9c4e7ccf47d5ede2b6169effb2a09668f512a182 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 3 Aug 2024 13:05:21 +0300
Subject: [PATCH 1/6] [clang] Implement `__builtin_is_implicit_lifetime()`

This intrinsic supports [P2647R1](https://wg21.link/p2674r1) "A trait for implicit lifetime types".
---
 clang/docs/LanguageExtensions.rst        |  1 +
 clang/docs/ReleaseNotes.rst              |  3 +
 clang/include/clang/Basic/TokenKinds.def |  1 +
 clang/lib/Sema/SemaExprCXX.cpp           | 22 +++++++
 clang/test/SemaCXX/type-traits.cpp       | 81 +++++++++++++++++++++++-
 5 files changed, 107 insertions(+), 1 deletion(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index a747464582e77d..f04e6b0057b512 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1546,6 +1546,7 @@ The following type trait primitives are supported by Clang. Those traits marked
 * ``__array_extent(type, dim)`` (Embarcadero):
   The ``dim``'th array bound in the type ``type``, or ``0`` if
   ``dim >= __array_rank(type)``.
+* ``__builtin_is_implicit_lifetime`` (C++, GNU, Microsoft)
 * ``__builtin_is_virtual_base_of`` (C++, GNU, Microsoft)
 * ``__can_pass_in_regs`` (C++)
   Returns whether a class can be passed in registers under the current
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 25f5bd37bbe94f..6854a321e17206 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -86,6 +86,9 @@ C++23 Feature Support
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
+- Add ``__builtin_is_implicit_lifetime`` intrinsic, which supports
+  `P2647R1 A trait for implicit lifetime types <https://wg21.link/p2674r1>`_
+
 - Add ``__builtin_is_virtual_base_of`` intrinsic, which supports
   `P2985R0 A type trait for detecting virtual base classes <https://wg21.link/p2985r0>`_
 
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 7e638dc1ddcdba..7505c5a1a1f27c 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -501,6 +501,7 @@ TYPE_TRAIT_1(__has_trivial_move_assign, HasTrivialMoveAssign, KEYCXX)
 TYPE_TRAIT_1(__has_trivial_move_constructor, HasTrivialMoveConstructor, KEYCXX)
 
 // GNU and MS Type Traits
+TYPE_TRAIT_1(__builtin_is_implicit_lifetime, IsImplicitLifetime, KEYCXX)
 TYPE_TRAIT_2(__builtin_is_virtual_base_of, IsVirtualBaseOf, KEYCXX)
 TYPE_TRAIT_1(__has_nothrow_assign, HasNothrowAssign, KEYCXX)
 TYPE_TRAIT_1(__has_nothrow_copy, HasNothrowCopy, KEYCXX)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index c5003d9ac02549..504dc93316db50 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5039,6 +5039,7 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
 
   // LWG3823: T shall be an array type, a complete type, or cv void.
   case UTT_IsAggregate:
+  case UTT_IsImplicitLifetime:
     if (ArgTy->isArrayType() || ArgTy->isVoidType())
       return true;
 
@@ -5637,6 +5638,27 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
     return false;
   case UTT_IsTriviallyEqualityComparable:
     return isTriviallyEqualityComparableType(Self, T, KeyLoc);
+  case UTT_IsImplicitLifetime: {
+    DiagnoseVLAInCXXTypeTrait(Self, TInfo,
+                              tok::kw___builtin_is_implicit_lifetime);
+    QualType UnqualT = T->getCanonicalTypeUnqualified();
+    if (UnqualT->isScalarType())
+      return true;
+    if (UnqualT->isArrayType())
+      return true;
+
+    CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
+    if (!RD)
+      return false;
+    if (UnqualT->isAggregateType())
+      if (!RD->getDestructor()->isUserProvided())
+        return true;
+    if (RD->hasTrivialDestructor())
+      if (RD->hasTrivialDefaultConstructor() ||
+          RD->hasTrivialCopyConstructor() || RD->hasTrivialMoveConstructor())
+        return true;
+    return false;
+  }
   }
 }
 
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index 4acb3d6c9eebea..11041414c1bbab 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -18,7 +18,7 @@ enum class SignedEnumClass : signed int {};
 enum class UnsignedEnumClass : unsigned int {};
 struct POD { Enum e; int i; float f; NonPOD* p; };
 struct Empty {};
-struct IncompleteStruct;
+struct IncompleteStruct; // expected-note {{forward declaration of 'IncompleteStruct'}}
 typedef Empty EmptyAr[10];
 typedef Empty EmptyArNB[];
 typedef Empty EmptyArMB[1][2];
@@ -1944,6 +1944,85 @@ void is_pointer_interconvertible_base_of(int n)
 }
 }
 
+struct NoEligibleTrivialContructor {
+  NoEligibleTrivialContructor() {};
+  NoEligibleTrivialContructor(const NoEligibleTrivialContructor&) {}
+  NoEligibleTrivialContructor(NoEligibleTrivialContructor&&) {}
+};
+
+struct OnlyDefaultConstructorIsTrivial {
+  OnlyDefaultConstructorIsTrivial() = default;
+  OnlyDefaultConstructorIsTrivial(const OnlyDefaultConstructorIsTrivial&) {}
+  OnlyDefaultConstructorIsTrivial(OnlyDefaultConstructorIsTrivial&&) {}
+};
+
+struct AllContstructorsAreTrivial {
+  AllContstructorsAreTrivial() = default;
+  AllContstructorsAreTrivial(const AllContstructorsAreTrivial&) = default;
+  AllContstructorsAreTrivial(AllContstructorsAreTrivial&&) = default;
+};
+
+struct InheritedNoEligibleTrivialConstructor : NoEligibleTrivialContructor {
+  using NoEligibleTrivialContructor::NoEligibleTrivialContructor;
+};
+
+struct InheritedOnlyDefaultConstructorIsTrivial : OnlyDefaultConstructorIsTrivial {
+  using OnlyDefaultConstructorIsTrivial::OnlyDefaultConstructorIsTrivial;
+};
+
+struct InheritedAllContstructorsAreTrivial : AllContstructorsAreTrivial {
+  using AllContstructorsAreTrivial::AllContstructorsAreTrivial;
+};
+
+struct UserDeclaredDestructor {
+  ~UserDeclaredDestructor() = default;
+};
+
+struct UserProvidedDestructor {
+  ~UserProvidedDestructor() {}
+};
+
+void is_implicit_lifetime(int n) {
+  static_assert(!__builtin_is_implicit_lifetime(void));
+  static_assert(!__builtin_is_implicit_lifetime(const void));
+  static_assert(!__builtin_is_implicit_lifetime(volatile void));
+  static_assert(__builtin_is_implicit_lifetime(int));
+  static_assert(!__builtin_is_implicit_lifetime(int&));
+  static_assert(!__builtin_is_implicit_lifetime(int&&));
+  static_assert(__builtin_is_implicit_lifetime(int*));
+  static_assert(__builtin_is_implicit_lifetime(int[]));
+  static_assert(__builtin_is_implicit_lifetime(int[5]));
+  static_assert(__builtin_is_implicit_lifetime(int[n]));
+  // expected-error at -1 {{variable length arrays are not supported in '__builtin_is_implicit_lifetime'}}
+  static_assert(__builtin_is_implicit_lifetime(Enum));
+  static_assert(__builtin_is_implicit_lifetime(EnumClass));
+  static_assert(!__builtin_is_implicit_lifetime(void()));
+  static_assert(!__builtin_is_implicit_lifetime(void() &));
+  static_assert(!__builtin_is_implicit_lifetime(void() const));
+  static_assert(!__builtin_is_implicit_lifetime(void(&)()));
+  static_assert(__builtin_is_implicit_lifetime(void(*)()));
+  static_assert(__builtin_is_implicit_lifetime(decltype(nullptr)));
+  static_assert(__builtin_is_implicit_lifetime(int UserDeclaredDestructor::*));
+  static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)()));
+  static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)() const));
+  static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)() &));
+  static_assert(__builtin_is_implicit_lifetime(int (UserDeclaredDestructor::*)() &&));
+  static_assert(!__builtin_is_implicit_lifetime(IncompleteStruct));
+  // expected-error at -1 {{incomplete type 'IncompleteStruct' used in type trait expression}}
+  static_assert(__builtin_is_implicit_lifetime(IncompleteStruct[]));
+  static_assert(__builtin_is_implicit_lifetime(IncompleteStruct[5]));
+  static_assert(__builtin_is_implicit_lifetime(UserDeclaredDestructor));
+  static_assert(__builtin_is_implicit_lifetime(const UserDeclaredDestructor));
+  static_assert(__builtin_is_implicit_lifetime(volatile UserDeclaredDestructor));
+  static_assert(!__builtin_is_implicit_lifetime(UserProvidedDestructor));
+  static_assert(!__builtin_is_implicit_lifetime(NoEligibleTrivialContructor));
+  static_assert(__builtin_is_implicit_lifetime(OnlyDefaultConstructorIsTrivial));
+  static_assert(__builtin_is_implicit_lifetime(AllContstructorsAreTrivial));
+  static_assert(!__builtin_is_implicit_lifetime(InheritedNoEligibleTrivialConstructor));
+  static_assert(__builtin_is_implicit_lifetime(InheritedOnlyDefaultConstructorIsTrivial));
+  static_assert(__builtin_is_implicit_lifetime(InheritedAllContstructorsAreTrivial));
+}
+
 void is_signed()
 {
   //static_assert(__is_signed(char));

>From ad0abb5ae0332c4de83723a96e3f765891993dc5 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 3 Aug 2024 16:38:08 +0400
Subject: [PATCH 2/6] Apply Timm's suggestion

Co-authored-by: Timm Baeder <tbaeder at redhat.com>
---
 clang/lib/Sema/SemaExprCXX.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 504dc93316db50..6f7e4203a17142 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5647,7 +5647,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
     if (UnqualT->isArrayType())
       return true;
 
-    CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
+    const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
     if (!RD)
       return false;
     if (UnqualT->isAggregateType())

>From 49de16a0126a3fe3284f017ee4a4969d41bb4094 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sun, 11 Aug 2024 21:46:58 +0300
Subject: [PATCH 3/6] Address feedback

---
 clang/lib/Sema/SemaExprCXX.cpp     | 17 ++++++++++++---
 clang/test/SemaCXX/type-traits.cpp | 34 ++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 6f7e4203a17142..5fb735600fac5a 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5641,19 +5641,30 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
   case UTT_IsImplicitLifetime: {
     DiagnoseVLAInCXXTypeTrait(Self, TInfo,
                               tok::kw___builtin_is_implicit_lifetime);
+    
+    // [basic.types.general] p9
+    // Scalar types, implicit-lifetime class types ([class.prop]),
+    // array types, and cv-qualified versions of these types
+    // are collectively called implicit-lifetime types.
     QualType UnqualT = T->getCanonicalTypeUnqualified();
     if (UnqualT->isScalarType())
       return true;
     if (UnqualT->isArrayType())
       return true;
-
     const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
     if (!RD)
       return false;
+    
+    // [class.prop] p9
+    // A class S is an implicit-lifetime class if
+    //   - it is an aggregate whose destructor is not user-provided or
+    //   - it has at least one trivial eligible constructor and a trivial,
+    //     non-deleted destructor.
+    const CXXDestructorDecl *Dtor = RD->getDestructor();
     if (UnqualT->isAggregateType())
-      if (!RD->getDestructor()->isUserProvided())
+      if (Dtor && !Dtor->isUserProvided())
         return true;
-    if (RD->hasTrivialDestructor())
+    if (RD->hasTrivialDestructor() && (!Dtor || !Dtor->isDeleted()))
       if (RD->hasTrivialDefaultConstructor() ||
           RD->hasTrivialCopyConstructor() || RD->hasTrivialMoveConstructor())
         return true;
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index 11041414c1bbab..fea6bec7476783 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -1982,6 +1982,31 @@ struct UserProvidedDestructor {
   ~UserProvidedDestructor() {}
 };
 
+struct UserDeletedDestructorInAggregate {
+  ~UserDeletedDestructorInAggregate() = delete;
+};
+
+struct UserDeletedDestructorInNonAggregate {
+  virtual void NonAggregate();
+  ~UserDeletedDestructorInNonAggregate() = delete;
+};
+
+struct DeletedDestructorViaBaseInAggregate : UserDeletedDestructorInAggregate {};
+struct DeletedDestructorViaBaseInNonAggregate : UserDeletedDestructorInNonAggregate {};
+
+#if __cplusplus >= 202002L
+template<bool B>
+struct ConstrainedUserDeclaredDefaultConstructor{
+  ConstrainedUserDeclaredDefaultConstructor() requires B = default;
+  ConstrainedUserDeclaredDefaultConstructor(const ConstrainedUserDeclaredDefaultConstructor&) {}
+};
+
+template<bool B>
+struct ConstrainedUserProvidedDestructor {
+  ~ConstrainedUserProvidedDestructor() requires B {}
+};
+#endif
+
 void is_implicit_lifetime(int n) {
   static_assert(!__builtin_is_implicit_lifetime(void));
   static_assert(!__builtin_is_implicit_lifetime(const void));
@@ -2021,6 +2046,15 @@ void is_implicit_lifetime(int n) {
   static_assert(!__builtin_is_implicit_lifetime(InheritedNoEligibleTrivialConstructor));
   static_assert(__builtin_is_implicit_lifetime(InheritedOnlyDefaultConstructorIsTrivial));
   static_assert(__builtin_is_implicit_lifetime(InheritedAllContstructorsAreTrivial));
+  static_assert(__builtin_is_implicit_lifetime(UserDeletedDestructorInAggregate));
+  static_assert(!__builtin_is_implicit_lifetime(UserDeletedDestructorInNonAggregate));
+  static_assert(__builtin_is_implicit_lifetime(DeletedDestructorViaBaseInAggregate) == __cplusplus >= 201703L);
+  static_assert(!__builtin_is_implicit_lifetime(DeletedDestructorViaBaseInNonAggregate));
+#if __cplusplus >= 202002L
+  static_assert(__builtin_is_implicit_lifetime(ConstrainedUserDeclaredDefaultConstructor<true>));
+  static_assert(!__builtin_is_implicit_lifetime(ConstrainedUserDeclaredDefaultConstructor<false>));
+  static_assert(!__builtin_is_implicit_lifetime(ConstrainedUserProvidedDestructor<true>));
+#endif
 }
 
 void is_signed()

>From 5dbfdf514503c45c7071348125c809b6ef13bcce Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sun, 11 Aug 2024 21:55:03 +0300
Subject: [PATCH 4/6] Remove whitespaces

---
 clang/lib/Sema/SemaExprCXX.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 5fb735600fac5a..17f21f1e650732 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5641,7 +5641,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
   case UTT_IsImplicitLifetime: {
     DiagnoseVLAInCXXTypeTrait(Self, TInfo,
                               tok::kw___builtin_is_implicit_lifetime);
-    
+
     // [basic.types.general] p9
     // Scalar types, implicit-lifetime class types ([class.prop]),
     // array types, and cv-qualified versions of these types
@@ -5654,7 +5654,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
     const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
     if (!RD)
       return false;
-    
+
     // [class.prop] p9
     // A class S is an implicit-lifetime class if
     //   - it is an aggregate whose destructor is not user-provided or

>From 5221a00354b294289323151c462c83b585e80656 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Tue, 13 Aug 2024 11:54:41 +0300
Subject: [PATCH 5/6] Handle vector types, add tests for various extensions

---
 clang/lib/Sema/SemaExprCXX.cpp     |  2 +-
 clang/test/SemaCXX/type-traits.cpp | 26 ++++++++++++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 17f21f1e650732..d8bca391173749 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5649,7 +5649,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
     QualType UnqualT = T->getCanonicalTypeUnqualified();
     if (UnqualT->isScalarType())
       return true;
-    if (UnqualT->isArrayType())
+    if (UnqualT->isArrayType() || UnqualT->isVectorType())
       return true;
     const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
     if (!RD)
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index fea6bec7476783..1cd34c263d495e 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -2007,6 +2007,17 @@ struct ConstrainedUserProvidedDestructor {
 };
 #endif
 
+struct StructWithFAM {
+  int a[];
+};
+
+struct StructWithZeroSizedArray {
+  int a[0];
+};
+
+typedef float float4 __attribute__((vector_type(4)));
+typedef float ext_float4 __attribute__((ext_vector_type(4)));
+
 void is_implicit_lifetime(int n) {
   static_assert(!__builtin_is_implicit_lifetime(void));
   static_assert(!__builtin_is_implicit_lifetime(const void));
@@ -2014,6 +2025,9 @@ void is_implicit_lifetime(int n) {
   static_assert(__builtin_is_implicit_lifetime(int));
   static_assert(!__builtin_is_implicit_lifetime(int&));
   static_assert(!__builtin_is_implicit_lifetime(int&&));
+  static_assert(__builtin_is_implicit_lifetime(float));
+  static_assert(__builtin_is_implicit_lifetime(double));
+  static_assert(__builtin_is_implicit_lifetime(long double));
   static_assert(__builtin_is_implicit_lifetime(int*));
   static_assert(__builtin_is_implicit_lifetime(int[]));
   static_assert(__builtin_is_implicit_lifetime(int[5]));
@@ -2055,6 +2069,18 @@ void is_implicit_lifetime(int n) {
   static_assert(!__builtin_is_implicit_lifetime(ConstrainedUserDeclaredDefaultConstructor<false>));
   static_assert(!__builtin_is_implicit_lifetime(ConstrainedUserProvidedDestructor<true>));
 #endif
+
+  static_assert(__builtin_is_implicit_lifetime(__int128));
+  static_assert(__builtin_is_implicit_lifetime(_BitInt(8)));
+  static_assert(__builtin_is_implicit_lifetime(_BitInt(128)));
+  static_assert(__builtin_is_implicit_lifetime(int[0]));
+  static_assert(__builtin_is_implicit_lifetime(StructWithFAM));
+  static_assert(__builtin_is_implicit_lifetime(StructWithZeroSizedArray));
+  static_assert(__builtin_is_implicit_lifetime(__fp16));
+  static_assert(__builtin_is_implicit_lifetime(__bf16));
+  static_assert(__builtin_is_implicit_lifetime(_Complex double));
+  static_assert(__builtin_is_implicit_lifetime(float4));
+  static_assert(__builtin_is_implicit_lifetime(ext_float4));
 }
 
 void is_signed()

>From 318cc3bd41f3cbf022805874edaacd421dc4de5a Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Tue, 13 Aug 2024 13:40:31 +0300
Subject: [PATCH 6/6] So `vector_type` is not a thing

---
 clang/test/SemaCXX/type-traits.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index 1cd34c263d495e..22a4e3f02e764c 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -2015,8 +2015,7 @@ struct StructWithZeroSizedArray {
   int a[0];
 };
 
-typedef float float4 __attribute__((vector_type(4)));
-typedef float ext_float4 __attribute__((ext_vector_type(4)));
+typedef float float4 __attribute__((ext_vector_type(4)));
 
 void is_implicit_lifetime(int n) {
   static_assert(!__builtin_is_implicit_lifetime(void));
@@ -2080,7 +2079,6 @@ void is_implicit_lifetime(int n) {
   static_assert(__builtin_is_implicit_lifetime(__bf16));
   static_assert(__builtin_is_implicit_lifetime(_Complex double));
   static_assert(__builtin_is_implicit_lifetime(float4));
-  static_assert(__builtin_is_implicit_lifetime(ext_float4));
 }
 
 void is_signed()



More information about the cfe-commits mailing list