[clang] d5922cf - [clang] Implement `__is_layout_compatible` (#81506)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 20 04:54:54 PST 2024
Author: Vlad Serebrennikov
Date: 2024-02-20T16:54:51+04:00
New Revision: d5922cf72cc18a7ac9f7afd1941ee2f7773d8469
URL: https://github.com/llvm/llvm-project/commit/d5922cf72cc18a7ac9f7afd1941ee2f7773d8469
DIFF: https://github.com/llvm/llvm-project/commit/d5922cf72cc18a7ac9f7afd1941ee2f7773d8469.diff
LOG: [clang] Implement `__is_layout_compatible` (#81506)
This patch implements `__is_layout_compatible` intrinsic, which supports
`std::is_layout_compatible` type trait introduced in C++20
([P0466R5](https://wg21.link/p0466r5) "Layout-compatibility and
Pointer-interconvertibility Traits"). Name matches GCC and MSVC
intrinsic.
Basically, this patch exposes our existing machinery for checking for
layout compatibility and figuring out common initial sequences. Said
machinery is a bit outdated, as it doesn't implement
[CWG1719](https://cplusplus.github.io/CWG/issues/1719.html) "Layout
compatibility and cv-qualification revisited" and
[CWG2759](https://cplusplus.github.io/CWG/issues/2759.html)
"`[[no_unique_address]` and common initial sequence". Those defect
reports are considered out of scope of of this PR, but will be
implemented in subsequent PRs.
Partially addresses #48204
Added:
Modified:
clang/docs/LanguageExtensions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/TokenKinds.def
clang/include/clang/Sema/Sema.h
clang/lib/Parse/ParseExpr.cpp
clang/lib/Sema/SemaChecking.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/test/CXX/drs/dr13xx.cpp
clang/test/CXX/drs/dr17xx.cpp
clang/test/SemaCXX/type-traits.cpp
Removed:
################################################################################
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index fd439662773420..fb4d7a02dd086f 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1582,6 +1582,7 @@ The following type trait primitives are supported by Clang. Those traits marked
* ``__is_integral`` (C++, Embarcadero)
* ``__is_interface_class`` (Microsoft):
Returns ``false``, even for types defined with ``__interface``.
+* ``__is_layout_compatible`` (C++, GNU, Microsoft)
* ``__is_literal`` (Clang):
Synonym for ``__is_literal_type``.
* ``__is_literal_type`` (C++, GNU, Microsoft):
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2095b8a5480322..8ea4a9a5a4256c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -96,6 +96,12 @@ C++20 Feature Support
behavior can use the flag '-Xclang -fno-skip-odr-check-in-gmf'.
(`#79240 <https://github.com/llvm/llvm-project/issues/79240>`_).
+- Implemented the `__is_layout_compatible` intrinsic to support
+ `P0466R5: Layout-compatibility and Pointer-interconvertibility Traits <https://wg21.link/P0466R5>`_.
+ Note: `CWG1719: Layout compatibility and cv-qualification revisited <https://cplusplus.github.io/CWG/issues/1719.html>`_
+ and `CWG2759: [[no_unique_address] and common initial sequence <https://cplusplus.github.io/CWG/issues/2759.html>`_
+ are not yet implemented.
+
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 2046ab9dc0198c..1d16e4843615d9 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -520,6 +520,7 @@ TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX)
TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX)
TYPE_TRAIT_1(__has_unique_object_representations,
HasUniqueObjectRepresentations, KEYCXX)
+TYPE_TRAIT_2(__is_layout_compatible, IsLayoutCompatible, 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 e9cd42ae777df5..89215bf3d1c69a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14078,6 +14078,8 @@ class Sema final {
bool SemaValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum);
public:
+ bool IsLayoutCompatible(QualType T1, QualType T2) const;
+
// Used by C++ template instantiation.
ExprResult SemaBuiltinShuffleVector(CallExpr *TheCall);
ExprResult SemaConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index f1417456ffe510..4bf954b5cc4db5 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1114,6 +1114,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
REVERTIBLE_TYPE_TRAIT(__is_fundamental);
REVERTIBLE_TYPE_TRAIT(__is_integral);
REVERTIBLE_TYPE_TRAIT(__is_interface_class);
+ REVERTIBLE_TYPE_TRAIT(__is_layout_compatible);
REVERTIBLE_TYPE_TRAIT(__is_literal);
REVERTIBLE_TYPE_TRAIT(__is_lvalue_expr);
REVERTIBLE_TYPE_TRAIT(__is_lvalue_reference);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 8e763384774444..d951c0fc2732d2 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -19155,6 +19155,10 @@ static bool isLayoutCompatible(ASTContext &C, QualType T1, QualType T2) {
return false;
}
+bool Sema::IsLayoutCompatible(QualType T1, QualType T2) const {
+ return isLayoutCompatible(getASTContext(), T1, T2);
+}
+
//===--- 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 801ce7b35ac47f..322bd1c87b1d71 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6010,6 +6010,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
llvm_unreachable("unhandled type trait");
return false;
+ }
+ case BTT_IsLayoutCompatible: {
+ return Self.IsLayoutCompatible(LhsT, RhsT);
}
default: llvm_unreachable("not a BTT");
}
diff --git a/clang/test/CXX/drs/dr13xx.cpp b/clang/test/CXX/drs/dr13xx.cpp
index 0ff44ebbc95591..effdc53040d0b0 100644
--- a/clang/test/CXX/drs/dr13xx.cpp
+++ b/clang/test/CXX/drs/dr13xx.cpp
@@ -307,6 +307,8 @@ namespace dr1330 { // dr1330: 4 c++11
E e; // #dr1330-e
}
+// dr1334: sup 1719
+
namespace dr1341 { // dr1341: sup P0683R1
#if __cplusplus >= 202002L
int a;
diff --git a/clang/test/CXX/drs/dr17xx.cpp b/clang/test/CXX/drs/dr17xx.cpp
index 2f7e62da7bb60b..e5cee19337ebd4 100644
--- a/clang/test/CXX/drs/dr17xx.cpp
+++ b/clang/test/CXX/drs/dr17xx.cpp
@@ -46,6 +46,34 @@ namespace dr1715 { // dr1715: 3.9
#endif
}
+namespace dr1719 { // dr1719: no
+#if __cplusplus >= 201103L
+struct CStruct {
+ int one;
+ int two;
+};
+
+struct CStruct2 {
+ int one;
+ int two;
+};
+
+struct CStructWithQualifiers {
+ const int one;
+ volatile int two;
+};
+
+static_assert(__is_layout_compatible(CStruct, const CStruct2), "");
+static_assert(__is_layout_compatible(CStruct, volatile CStruct2), "");
+static_assert(__is_layout_compatible(const CStruct, volatile CStruct2), "");
+// FIXME: all of the following pairs of types are layout-compatible
+static_assert(!__is_layout_compatible(int, const int), "");
+static_assert(!__is_layout_compatible(int, volatile int), "");
+static_assert(!__is_layout_compatible(const int, volatile int), "");
+static_assert(!__is_layout_compatible(CStruct, CStructWithQualifiers), "");
+#endif
+} // namespace dr1719
+
namespace dr1722 { // dr1722: 9
#if __cplusplus >= 201103L
void f() {
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index 5659594577111e..6ff04b6c8c7223 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -1558,6 +1558,238 @@ void is_standard_layout()
int t71[F(__is_standard_layout(HasEmptyIndirectBaseAsSecondUnionMember))];
}
+struct CStruct2 {
+ int one;
+ int two;
+};
+
+struct CEmptyStruct2 {};
+
+struct CppEmptyStruct2 : CStruct2 {};
+struct CppStructStandard2 : CEmptyStruct2 {
+ int three;
+ int four;
+};
+struct CppStructNonStandardByBase2 : CStruct2 {
+ int three;
+ int four;
+};
+struct CppStructNonStandardByVirt2 : CStruct2 {
+ virtual void method() {}
+};
+struct CppStructNonStandardByMemb2 : CStruct2 {
+ CppStructNonStandardByVirt member;
+};
+struct CppStructNonStandardByProt2 : CStruct2 {
+ int five;
+protected:
+ int six;
+};
+struct CppStructNonStandardByVirtBase2 : virtual CStruct2 {
+};
+struct CppStructNonStandardBySameBase2 : CEmptyStruct2 {
+ CEmptyStruct member;
+};
+struct CppStructNonStandardBy2ndVirtBase2 : CEmptyStruct2 {
+ CEmptyStruct member;
+};
+
+struct CStructWithQualifiers {
+ const int one;
+ volatile int two;
+};
+
+struct CStructNoUniqueAddress {
+ int one;
+ [[no_unique_address]] int two;
+};
+
+struct CStructNoUniqueAddress2 {
+ int one;
+ [[no_unique_address]] int two;
+};
+
+struct CStructAlignment {
+ int one;
+ alignas(16) int two;
+};
+
+enum EnumLayout : int {};
+enum class EnumClassLayout {};
+enum EnumForward : int;
+enum class EnumClassForward;
+
+struct CStructIncomplete;
+
+struct CStructNested {
+ int a;
+ CStruct s;
+ int b;
+};
+
+struct CStructNested2 {
+ int a2;
+ CStruct s2;
+ int b2;
+};
+
+struct CStructWithBitfelds {
+ int a : 5;
+ int : 0;
+};
+
+struct CStructWithBitfelds2 {
+ int a : 5;
+ int : 0;
+};
+
+struct CStructWithBitfelds3 {
+ int : 0;
+ int b : 5;
+};
+
+struct CStructWithBitfelds4 {
+ EnumLayout a : 5;
+ int : 0;
+};
+
+union UnionLayout {
+ int a;
+ double b;
+ CStruct c;
+ [[no_unique_address]] CEmptyStruct d;
+ [[no_unique_address]] CEmptyStruct2 e;
+};
+
+union UnionLayout2 {
+ CStruct c;
+ int a;
+ CEmptyStruct2 e;
+ double b;
+ [[no_unique_address]] CEmptyStruct d;
+};
+
+union UnionLayout3 {
+ CStruct c;
+ int a;
+ double b;
+ [[no_unique_address]] CEmptyStruct d;
+};
+
+struct StructWithAnonUnion {
+ union {
+ int a;
+ double b;
+ CStruct c;
+ [[no_unique_address]] CEmptyStruct d;
+ [[no_unique_address]] CEmptyStruct2 e;
+ };
+};
+
+struct StructWithAnonUnion2 {
+ union {
+ CStruct c;
+ int a;
+ CEmptyStruct2 e;
+ double b;
+ [[no_unique_address]] CEmptyStruct d;
+ };
+};
+
+struct StructWithAnonUnion3 {
+ union {
+ CStruct c;
+ int a;
+ CEmptyStruct2 e;
+ double b;
+ [[no_unique_address]] CEmptyStruct d;
+ } u;
+};
+
+
+void is_layout_compatible(int n)
+{
+ static_assert(__is_layout_compatible(void, void), "");
+ static_assert(!__is_layout_compatible(void, int), "");
+ static_assert(!__is_layout_compatible(void, const void), ""); // FIXME: this is CWG1719
+ static_assert(!__is_layout_compatible(void, volatile void), ""); // FIXME: this is CWG1719
+ static_assert(!__is_layout_compatible(const int, volatile int), ""); // FIXME: this is CWG1719
+ static_assert(__is_layout_compatible(int, int), "");
+ static_assert(!__is_layout_compatible(int, const int), ""); // FIXME: this is CWG1719
+ static_assert(!__is_layout_compatible(int, volatile int), ""); // FIXME: this is CWG1719
+ static_assert(!__is_layout_compatible(const int, volatile int), ""); // FIXME: this is CWG1719
+ static_assert(!__is_layout_compatible(int, unsigned int), "");
+ static_assert(!__is_layout_compatible(char, unsigned char), "");
+ static_assert(!__is_layout_compatible(char, signed char), "");
+ static_assert(!__is_layout_compatible(unsigned char, signed char), "");
+ static_assert(__is_layout_compatible(int[], int[]), "");
+ static_assert(__is_layout_compatible(int[2], int[2]), "");
+ static_assert(!__is_layout_compatible(int[n], int[2]), ""); // FIXME: VLAs should be rejected
+ static_assert(!__is_layout_compatible(int[n], int[n]), ""); // FIXME: VLAs should be rejected
+ static_assert(__is_layout_compatible(int&, int&), "");
+ static_assert(!__is_layout_compatible(int&, char&), "");
+ static_assert(__is_layout_compatible(void(int), void(int)), "");
+ static_assert(!__is_layout_compatible(void(int), void(char)), "");
+ static_assert(__is_layout_compatible(void(&)(int), void(&)(int)), "");
+ static_assert(!__is_layout_compatible(void(&)(int), void(&)(char)), "");
+ static_assert(__is_layout_compatible(void(*)(int), void(*)(int)), "");
+ static_assert(!__is_layout_compatible(void(*)(int), void(*)(char)), "");
+ using function_type = void();
+ using function_type2 = void(char);
+ static_assert(__is_layout_compatible(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_layout_compatible(function_type, const function_type), "");
+ // expected-warning at -1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
+ static_assert(!__is_layout_compatible(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_layout_compatible(CStruct, CStruct2), "");
+ static_assert(__is_layout_compatible(CStruct, const CStruct2), "");
+ static_assert(__is_layout_compatible(CStruct, volatile CStruct2), "");
+ static_assert(__is_layout_compatible(const CStruct, volatile CStruct2), "");
+ static_assert(__is_layout_compatible(CEmptyStruct, CEmptyStruct2), "");
+ static_assert(__is_layout_compatible(CppEmptyStruct, CppEmptyStruct2), "");
+ static_assert(__is_layout_compatible(CppStructStandard, CppStructStandard2), "");
+ static_assert(!__is_layout_compatible(CppStructNonStandardByBase, CppStructNonStandardByBase2), "");
+ static_assert(!__is_layout_compatible(CppStructNonStandardByVirt, CppStructNonStandardByVirt2), "");
+ static_assert(!__is_layout_compatible(CppStructNonStandardByMemb, CppStructNonStandardByMemb2), "");
+ static_assert(!__is_layout_compatible(CppStructNonStandardByProt, CppStructNonStandardByProt2), "");
+ static_assert(!__is_layout_compatible(CppStructNonStandardByVirtBase, CppStructNonStandardByVirtBase2), "");
+ static_assert(!__is_layout_compatible(CppStructNonStandardBySameBase, CppStructNonStandardBySameBase2), "");
+ static_assert(!__is_layout_compatible(CppStructNonStandardBy2ndVirtBase, CppStructNonStandardBy2ndVirtBase2), "");
+ static_assert(!__is_layout_compatible(CStruct, CStructWithQualifiers), ""); // FIXME: this is CWG1719
+ static_assert(__is_layout_compatible(CStruct, CStructNoUniqueAddress) == bool(__has_cpp_attribute(no_unique_address)), ""); // FIXME: this is CWG2759
+ static_assert(__is_layout_compatible(CStructNoUniqueAddress, CStructNoUniqueAddress2) == bool(__has_cpp_attribute(no_unique_address)), ""); // FIXME: this is CWG2759
+ static_assert(__is_layout_compatible(CStruct, CStructAlignment), "");
+ static_assert(__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds), "");
+ static_assert(__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds2), "");
+ static_assert(!__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds3), "");
+ static_assert(!__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds4), "");
+ static_assert(__is_layout_compatible(int CStruct2::*, int CStruct2::*), "");
+ static_assert(!__is_layout_compatible(int CStruct2::*, char CStruct2::*), "");
+ static_assert(__is_layout_compatible(void(CStruct2::*)(int), void(CStruct2::*)(int)), "");
+ static_assert(!__is_layout_compatible(void(CStruct2::*)(int), void(CStruct2::*)(char)), "");
+ static_assert(__is_layout_compatible(CStructNested, CStructNested2), "");
+ static_assert(__is_layout_compatible(UnionLayout, UnionLayout), "");
+ static_assert(__is_layout_compatible(UnionLayout, UnionLayout2), "");
+ static_assert(!__is_layout_compatible(UnionLayout, UnionLayout3), "");
+ static_assert(__is_layout_compatible(StructWithAnonUnion, StructWithAnonUnion2), "");
+ static_assert(__is_layout_compatible(StructWithAnonUnion, StructWithAnonUnion3), "");
+ static_assert(__is_layout_compatible(EnumLayout, EnumClassLayout), "");
+ static_assert(__is_layout_compatible(EnumForward, EnumForward), "");
+ static_assert(__is_layout_compatible(EnumForward, EnumClassForward), "");
+ // Layout compatibility for enums might be relaxed in the future. See https://github.com/cplusplus/CWG/issues/39#issuecomment-1184791364
+ static_assert(!__is_layout_compatible(EnumLayout, int), "");
+ static_assert(!__is_layout_compatible(EnumClassLayout, int), "");
+ static_assert(!__is_layout_compatible(EnumForward, int), "");
+ static_assert(!__is_layout_compatible(EnumClassForward, int), "");
+ // FIXME: the following should be rejected (array of unknown bound and void are the only allowed incomplete types)
+ static_assert(__is_layout_compatible(CStructIncomplete, CStructIncomplete), "");
+ static_assert(!__is_layout_compatible(CStruct, CStructIncomplete), "");
+ static_assert(__is_layout_compatible(CStructIncomplete[2], CStructIncomplete[2]), "");
+}
+
void is_signed()
{
//int t01[T(__is_signed(char))];
More information about the cfe-commits
mailing list