[clang] [clang] Implement `__is_pointer_interconvertible_base_of()` (PR #88473)

Vlad Serebrennikov via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 12 00:02:11 PDT 2024


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

>From 84907542cecbe76b1971a50642d39ec4d1078687 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 12 Apr 2024 08:18:06 +0300
Subject: [PATCH 1/3] [clang] Implement
 `__is_pointer_interconvertible_base_of()`

---
 clang/include/clang/Basic/TokenKinds.def |  1 +
 clang/include/clang/Sema/Sema.h          |  2 +
 clang/lib/Sema/SemaChecking.cpp          | 21 ++++++
 clang/lib/Sema/SemaExprCXX.cpp           | 16 +++++
 clang/test/SemaCXX/type-traits.cpp       | 86 +++++++++++++++++++++++-
 5 files changed, 125 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 800af0e6d04480..a27fbed358a60c 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -521,6 +521,7 @@ TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX)
 TYPE_TRAIT_1(__has_unique_object_representations,
              HasUniqueObjectRepresentations, KEYCXX)
 TYPE_TRAIT_2(__is_layout_compatible, IsLayoutCompatible, KEYCXX)
+TYPE_TRAIT_2(__is_pointer_interconvertible_base_of, IsPointerInterconvertibleBaseOf, KEYCXX)
 
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) KEYWORD(__##Trait, KEYCXX)
 #include "clang/Basic/TransformTypeTraits.def"
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 0ee4f3c8e127f6..397c5ae8eee777 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1975,6 +1975,8 @@ class Sema final : public SemaBase {
   };
 
   bool IsLayoutCompatible(QualType T1, QualType T2) const;
+  bool IsPointerInterconvertibleBaseOf(const TypeSourceInfo *Base,
+                                       const TypeSourceInfo *Derived);
 
   bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
                          const FunctionProtoType *Proto);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index abfd9a3031577b..bbe82672183508 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -19710,6 +19710,27 @@ bool Sema::IsLayoutCompatible(QualType T1, QualType T2) const {
   return isLayoutCompatible(getASTContext(), T1, T2);
 }
 
+//===-------------- Pointer interconvertibility ----------------------------//
+
+bool Sema::IsPointerInterconvertibleBaseOf(const TypeSourceInfo *Base,
+                                           const TypeSourceInfo *Derived) {
+  QualType BaseT = Base->getType().getCanonicalType().getUnqualifiedType();
+  QualType DerivedT =
+      Derived->getType().getCanonicalType().getUnqualifiedType();
+
+  if (!BaseT->isUnionType() && !DerivedT->isUnionType() &&
+      getASTContext().hasSameType(BaseT, DerivedT))
+    return true;
+
+  if (!IsDerivedFrom(Derived->getTypeLoc().getBeginLoc(), DerivedT, BaseT))
+    return false;
+
+  if (DerivedT->getAsCXXRecordDecl()->isStandardLayout())
+    return true;
+
+  return false;
+}
+
 //===--- CHECK: pointer_with_type_tag attribute: datatypes should match ----//
 
 /// Given a type tag expression find the type tag itself.
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 12f42f66e5e21b..1416dab9eb3a0e 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6082,6 +6082,22 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
       Self.Diag(Rhs->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported)
           << 1 << tok::kw___is_layout_compatible;
     return Self.IsLayoutCompatible(LhsT, RhsT);
+  }
+  case BTT_IsPointerInterconvertibleBaseOf: {
+    if (!LhsT->isUnionType() && !RhsT->isUnionType() &&
+        !Self.getASTContext().hasSameUnqualifiedType(LhsT, RhsT)) {
+      Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
+                               diag::err_incomplete_type);
+    }
+
+    if (LhsT->isVariableArrayType())
+      Self.Diag(Lhs->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported)
+          << 1 << tok::kw___is_pointer_interconvertible_base_of;
+    if (RhsT->isVariableArrayType())
+      Self.Diag(Rhs->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported)
+          << 1 << tok::kw___is_pointer_interconvertible_base_of;
+
+    return Self.IsPointerInterconvertibleBaseOf(Lhs, Rhs);
   }
     default: llvm_unreachable("not a BTT");
   }
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index 421d3007d27ffe..7c22a7659de9e8 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -1740,7 +1740,7 @@ void is_layout_compatible(int n)
   static_assert(!__is_layout_compatible(void, int));
   static_assert(__is_layout_compatible(void, const void));
   static_assert(__is_layout_compatible(void, volatile void));
-  static_assert(__is_layout_compatible(const int, volatile int));
+  static_assert(__is_layout_compatible(const void, volatile void));
   static_assert(__is_layout_compatible(int, int));
   static_assert(__is_layout_compatible(int, const int));
   static_assert(__is_layout_compatible(int, volatile int));
@@ -1839,6 +1839,90 @@ void is_layout_compatible(int n)
   static_assert(!__is_layout_compatible(EnumClassForward, int));
 }
 
+namespace IPIBO {
+struct Base {};
+struct Base2 {};
+struct Base3 : Base {};
+struct Base3Virtual : virtual Base {};
+struct Derived : Base {};
+struct DerivedIndirect : Base3 {};
+struct DerivedMultiple : Base, Base2 {};
+struct DerivedAmbiguous : Base, Base3 {};
+/* expected-warning at -1 {{direct base 'Base' is inaccessible due to ambiguity:
+    struct IPIBO::DerivedAmbiguous -> Base
+    struct IPIBO::DerivedAmbiguous -> Base3 -> Base}} */
+struct DerivedPrivate : private Base {};
+struct DerivedVirtual : virtual Base {};
+
+union Union {};
+union UnionIncomplete;
+struct StructIncomplete;
+
+void is_pointer_interconvertible_base_of(int n)
+{
+  static_assert(__is_pointer_interconvertible_base_of(Base, Derived));
+  static_assert(!__is_pointer_interconvertible_base_of(Base2, Derived));
+  static_assert(__is_pointer_interconvertible_base_of(Base, DerivedIndirect));
+  static_assert(__is_pointer_interconvertible_base_of(Base, DerivedMultiple));
+  static_assert(!__is_pointer_interconvertible_base_of(Base3, DerivedMultiple));
+  static_assert(!__is_pointer_interconvertible_base_of(Base, DerivedAmbiguous));
+  static_assert(__is_pointer_interconvertible_base_of(Base, DerivedPrivate));
+  static_assert(!__is_pointer_interconvertible_base_of(Base, DerivedVirtual));
+  static_assert(!__is_pointer_interconvertible_base_of(Union, Union));
+  static_assert(!__is_pointer_interconvertible_base_of(UnionIncomplete, UnionIncomplete));
+  static_assert(__is_pointer_interconvertible_base_of(StructIncomplete, StructIncomplete));
+  static_assert(__is_pointer_interconvertible_base_of(StructIncomplete, const StructIncomplete));
+  static_assert(__is_pointer_interconvertible_base_of(StructIncomplete, volatile StructIncomplete));
+  static_assert(__is_pointer_interconvertible_base_of(const StructIncomplete, volatile StructIncomplete));
+  static_assert(!__is_pointer_interconvertible_base_of(CStruct2, CppStructNonStandardByBase2));
+  static_assert(__is_pointer_interconvertible_base_of(void, void));
+  static_assert(!__is_pointer_interconvertible_base_of(void, int));
+  static_assert(__is_pointer_interconvertible_base_of(void, const void));
+  static_assert(__is_pointer_interconvertible_base_of(void, volatile void));
+  static_assert(__is_pointer_interconvertible_base_of(const void, volatile void));
+  static_assert(__is_pointer_interconvertible_base_of(int, int));
+  static_assert(__is_pointer_interconvertible_base_of(int, const int));
+  static_assert(__is_pointer_interconvertible_base_of(int, volatile int));
+  static_assert(__is_pointer_interconvertible_base_of(const int, volatile int));
+  static_assert(__is_pointer_interconvertible_base_of(int *, int * __restrict));
+  static_assert(!__is_pointer_interconvertible_base_of(int, _Atomic int));
+  static_assert(__is_pointer_interconvertible_base_of(_Atomic(int), _Atomic int));
+  static_assert(!__is_pointer_interconvertible_base_of(int, unsigned int));
+  static_assert(!__is_pointer_interconvertible_base_of(char, unsigned char));
+  static_assert(!__is_pointer_interconvertible_base_of(char, signed char));
+  static_assert(!__is_pointer_interconvertible_base_of(unsigned char, signed char));
+  using function_type = void();
+  using function_type2 = void(char);
+  static_assert(__is_pointer_interconvertible_base_of(const function_type, const function_type));
+  // expected-warning at -1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
+  // expected-warning at -2 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
+  static_assert(__is_pointer_interconvertible_base_of(function_type, const function_type));
+  // expected-warning at -1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
+  static_assert(!__is_pointer_interconvertible_base_of(const function_type, const function_type2));
+  // expected-warning at -1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
+  // expected-warning at -2 {{'const' qualifier on function type 'function_type2' (aka 'void (char)') has no effect}}
+  static_assert(__is_pointer_interconvertible_base_of(int CStruct2::*, int CStruct2::*));
+  static_assert(!__is_pointer_interconvertible_base_of(int CStruct2::*, char CStruct2::*));
+  static_assert(__is_pointer_interconvertible_base_of(void(CStruct2::*)(int), void(CStruct2::*)(int)));
+  static_assert(!__is_pointer_interconvertible_base_of(void(CStruct2::*)(int), void(CStruct2::*)(char)));
+  static_assert(__is_pointer_interconvertible_base_of(int[], int[]));
+  static_assert(__is_pointer_interconvertible_base_of(int[2], int[2]));
+  static_assert(!__is_pointer_interconvertible_base_of(int[n], int[2]));
+  // expected-error at -1 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}}
+  static_assert(!__is_pointer_interconvertible_base_of(int[n], int[n]));
+  // expected-error at -1 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}}
+  // expected-error at -2 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}}
+  static_assert(__is_pointer_interconvertible_base_of(int&, int&));
+  static_assert(!__is_pointer_interconvertible_base_of(int&, char&));
+  static_assert(__is_pointer_interconvertible_base_of(void(int), void(int)));
+  static_assert(!__is_pointer_interconvertible_base_of(void(int), void(char)));
+  static_assert(__is_pointer_interconvertible_base_of(void(&)(int), void(&)(int)));
+  static_assert(!__is_pointer_interconvertible_base_of(void(&)(int), void(&)(char)));
+  static_assert(__is_pointer_interconvertible_base_of(void(*)(int), void(*)(int)));
+  static_assert(!__is_pointer_interconvertible_base_of(void(*)(int), void(*)(char)));
+}
+}
+
 void is_signed()
 {
   //static_assert(__is_signed(char));

>From 0f7b465f316c9db823cadba5ebdc6fc9a75cb764 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 12 Apr 2024 09:58:00 +0300
Subject: [PATCH 2/3] Yield `false` for non-class types

---
 clang/lib/Sema/SemaChecking.cpp    |  2 +-
 clang/test/SemaCXX/type-traits.cpp | 40 +++++++++++++++---------------
 2 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index bbe82672183508..c4525d660f902f 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -19718,7 +19718,7 @@ bool Sema::IsPointerInterconvertibleBaseOf(const TypeSourceInfo *Base,
   QualType DerivedT =
       Derived->getType().getCanonicalType().getUnqualifiedType();
 
-  if (!BaseT->isUnionType() && !DerivedT->isUnionType() &&
+  if (BaseT->isStructureOrClassType() && DerivedT->isStructureOrClassType() &&
       getASTContext().hasSameType(BaseT, DerivedT))
     return true;
 
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index 7c22a7659de9e8..2dd00886e60074 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -1875,50 +1875,50 @@ void is_pointer_interconvertible_base_of(int n)
   static_assert(__is_pointer_interconvertible_base_of(StructIncomplete, volatile StructIncomplete));
   static_assert(__is_pointer_interconvertible_base_of(const StructIncomplete, volatile StructIncomplete));
   static_assert(!__is_pointer_interconvertible_base_of(CStruct2, CppStructNonStandardByBase2));
-  static_assert(__is_pointer_interconvertible_base_of(void, void));
+  static_assert(!__is_pointer_interconvertible_base_of(void, void));
   static_assert(!__is_pointer_interconvertible_base_of(void, int));
-  static_assert(__is_pointer_interconvertible_base_of(void, const void));
-  static_assert(__is_pointer_interconvertible_base_of(void, volatile void));
-  static_assert(__is_pointer_interconvertible_base_of(const void, volatile void));
-  static_assert(__is_pointer_interconvertible_base_of(int, int));
-  static_assert(__is_pointer_interconvertible_base_of(int, const int));
-  static_assert(__is_pointer_interconvertible_base_of(int, volatile int));
-  static_assert(__is_pointer_interconvertible_base_of(const int, volatile int));
-  static_assert(__is_pointer_interconvertible_base_of(int *, int * __restrict));
+  static_assert(!__is_pointer_interconvertible_base_of(void, const void));
+  static_assert(!__is_pointer_interconvertible_base_of(void, volatile void));
+  static_assert(!__is_pointer_interconvertible_base_of(const void, volatile void));
+  static_assert(!__is_pointer_interconvertible_base_of(int, int));
+  static_assert(!__is_pointer_interconvertible_base_of(int, const int));
+  static_assert(!__is_pointer_interconvertible_base_of(int, volatile int));
+  static_assert(!__is_pointer_interconvertible_base_of(const int, volatile int));
+  static_assert(!__is_pointer_interconvertible_base_of(int *, int * __restrict));
   static_assert(!__is_pointer_interconvertible_base_of(int, _Atomic int));
-  static_assert(__is_pointer_interconvertible_base_of(_Atomic(int), _Atomic int));
+  static_assert(!__is_pointer_interconvertible_base_of(_Atomic(int), _Atomic int));
   static_assert(!__is_pointer_interconvertible_base_of(int, unsigned int));
   static_assert(!__is_pointer_interconvertible_base_of(char, unsigned char));
   static_assert(!__is_pointer_interconvertible_base_of(char, signed char));
   static_assert(!__is_pointer_interconvertible_base_of(unsigned char, signed char));
   using function_type = void();
   using function_type2 = void(char);
-  static_assert(__is_pointer_interconvertible_base_of(const function_type, const function_type));
+  static_assert(!__is_pointer_interconvertible_base_of(const function_type, const function_type));
   // expected-warning at -1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
   // expected-warning at -2 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
-  static_assert(__is_pointer_interconvertible_base_of(function_type, const function_type));
+  static_assert(!__is_pointer_interconvertible_base_of(function_type, const function_type));
   // expected-warning at -1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
   static_assert(!__is_pointer_interconvertible_base_of(const function_type, const function_type2));
   // expected-warning at -1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
   // expected-warning at -2 {{'const' qualifier on function type 'function_type2' (aka 'void (char)') has no effect}}
-  static_assert(__is_pointer_interconvertible_base_of(int CStruct2::*, int CStruct2::*));
+  static_assert(!__is_pointer_interconvertible_base_of(int CStruct2::*, int CStruct2::*));
   static_assert(!__is_pointer_interconvertible_base_of(int CStruct2::*, char CStruct2::*));
-  static_assert(__is_pointer_interconvertible_base_of(void(CStruct2::*)(int), void(CStruct2::*)(int)));
+  static_assert(!__is_pointer_interconvertible_base_of(void(CStruct2::*)(int), void(CStruct2::*)(int)));
   static_assert(!__is_pointer_interconvertible_base_of(void(CStruct2::*)(int), void(CStruct2::*)(char)));
-  static_assert(__is_pointer_interconvertible_base_of(int[], int[]));
-  static_assert(__is_pointer_interconvertible_base_of(int[2], int[2]));
+  static_assert(!__is_pointer_interconvertible_base_of(int[], int[]));
+  static_assert(!__is_pointer_interconvertible_base_of(int[2], int[2]));
   static_assert(!__is_pointer_interconvertible_base_of(int[n], int[2]));
   // expected-error at -1 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}}
   static_assert(!__is_pointer_interconvertible_base_of(int[n], int[n]));
   // expected-error at -1 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}}
   // expected-error at -2 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}}
-  static_assert(__is_pointer_interconvertible_base_of(int&, int&));
+  static_assert(!__is_pointer_interconvertible_base_of(int&, int&));
   static_assert(!__is_pointer_interconvertible_base_of(int&, char&));
-  static_assert(__is_pointer_interconvertible_base_of(void(int), void(int)));
+  static_assert(!__is_pointer_interconvertible_base_of(void(int), void(int)));
   static_assert(!__is_pointer_interconvertible_base_of(void(int), void(char)));
-  static_assert(__is_pointer_interconvertible_base_of(void(&)(int), void(&)(int)));
+  static_assert(!__is_pointer_interconvertible_base_of(void(&)(int), void(&)(int)));
   static_assert(!__is_pointer_interconvertible_base_of(void(&)(int), void(&)(char)));
-  static_assert(__is_pointer_interconvertible_base_of(void(*)(int), void(*)(int)));
+  static_assert(!__is_pointer_interconvertible_base_of(void(*)(int), void(*)(int)));
   static_assert(!__is_pointer_interconvertible_base_of(void(*)(int), void(*)(char)));
 }
 }

>From cc1aca56480eb5d02580a15e949c5ae85bf44542 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 12 Apr 2024 10:01:56 +0300
Subject: [PATCH 3/3] Add a release note

---
 clang/docs/ReleaseNotes.rst | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c4a4893aec5cd6..dd3a2d80dbcdce 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -98,7 +98,8 @@ C++20 Feature Support
   behavior can use the flag '-Xclang -fno-skip-odr-check-in-gmf'.
   (#GH79240).
 
-- Implemented the `__is_layout_compatible` intrinsic to support
+- Implemented the `__is_layout_compatible` and `__is_pointer_interconvertible_base_of`
+  intrinsics to support
   `P0466R5: Layout-compatibility and Pointer-interconvertibility Traits <https://wg21.link/P0466R5>`_.
 
 - Clang now implements [module.import]p7 fully. Clang now will import module



More information about the cfe-commits mailing list