-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[clang] Implement __is_layout_compatible
#81506
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@llvm/pr-subscribers-clang Author: Vlad Serebrennikov (Endilll) ChangesThis patch implements 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 "Layout compatibility and cv-qualification revisited" and CWG2759 " Partially addresses #48204 Full diff: https://github.com/llvm/llvm-project/pull/81506.diff 7 Files Affected:
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 23817cde7a9354..112bfe8b23c2c0 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 851560f759f0e4..ddeba328749653 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14040,6 +14040,8 @@ class Sema final {
bool SemaValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum);
public:
+ bool SemaIsLayoutCompatible(QualType T1, QualType T2);
+
// Used by C++ template instantiation.
ExprResult SemaBuiltinShuffleVector(CallExpr *TheCall);
ExprResult SemaConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 79928ddb5af599..c9360981c1c1e4 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1717,6 +1717,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
tok::kw___is_fundamental,
tok::kw___is_integral,
tok::kw___is_interface_class,
+ tok::kw___is_layout_compatible,
tok::kw___is_literal,
tok::kw___is_lvalue_expr,
tok::kw___is_lvalue_reference,
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 52cebdb6f64bac..db21a7f1e9e368 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 71e6e7230fc455..8aa744873f3704 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -19150,6 +19150,10 @@ static bool isLayoutCompatible(ASTContext &C, QualType T1, QualType T2) {
return false;
}
+bool Sema::SemaIsLayoutCompatible(QualType T1, QualType T2) {
+ 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 246d2313e089f3..b279c367342fce 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5922,6 +5922,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
llvm_unreachable("unhandled type trait");
return false;
+ }
+ case BTT_IsLayoutCompatible: {
+ return Self.SemaIsLayoutCompatible(LhsT, RhsT);
}
default: llvm_unreachable("not a BTT");
}
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index 5659594577111e..51592849e97edd 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -1558,6 +1558,89 @@ 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;
+};
+
+struct CStructIncomplete;
+
+void is_layout_compatible()
+{
+ static_assert(__is_layout_compatible(void, void), "");
+ static_assert(__is_layout_compatible(int, int), "");
+ static_assert(__is_layout_compatible(int[], int[]), "");
+ static_assert(__is_layout_compatible(int[2], int[2]), "");
+ static_assert(__is_layout_compatible(CStruct, 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(CStructIncomplete, CStructIncomplete), "");
+ static_assert(!__is_layout_compatible(CStruct, CStructIncomplete), "");
+}
+
void is_signed()
{
//int t01[T(__is_signed(char))];
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
|
Formatting suggestions to |
cor3ntin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good generally.
Can you add a changelog entry?
Can you add some doc in LanguageExtension.rst?
Thanks
AaronBallman
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this! Please be sure to also add a release note and documentation (https://clang.llvm.org/docs/LanguageExtensions.html#type-trait-primitives).
| static_assert(__is_layout_compatible(CStruct, CStructAlignment), ""); | ||
| static_assert(__is_layout_compatible(CStructIncomplete, CStructIncomplete), ""); | ||
| static_assert(!__is_layout_compatible(CStruct, CStructIncomplete), ""); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some additional test cases to consider:
- A type is layout compatible with its qualified version. e.g.,
SomeStructandconst SomeStruct - Layout compatibility of function types (not function pointer types), e.g.,
void(int)andvoid(int) - Diagnostics when given an incomplete type for either operand
- That
intandunsigned intare not layout compatible - That
charandsigned char/unsigned charare not layout compatible - That an enumeration type and its underlying type are not layout compatible
If this catches bugs in the implementation of isLayoutCompatible(), that' fine, we can document the current behavior in the test with a FIXME and come back to addressing the issues in a follow-up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- A type is layout compatible with its qualified version. e.g.,
SomeStructandconst SomeStruct
We do not yet implement https://cplusplus.github.io/CWG/issues/1719.html, so some tests are better deferred to the implementation of that DR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should add those tests somewhere (either here or in dr1xxx.cpp) so 1) we know we don't crash on them, 2) we're alerted to behavioral changes in this area, and 3) better documentation of existing behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A type is layout compatible with its qualified version. e.g., SomeStruct and const SomeStruct
Done. It even works correctly for class types.
Layout compatibility of function types (not function pointer types), e.g., void(int) and void(int)
Done. I added tests for function types, reference to functions, pointers to function, pointers to data members, pointers to member functions.
Diagnostics when given an incomplete type for either operand
We don't issue any, as far as I can see. And I don't think we can when the types are the same, ignoring cv qualifiers:
Two types cv1 T1 and cv2 T2 are layout-compatible types if T1 and T2 are the same type, layout-compatible enumerations, or layout-compatible standard-layout class types.
That int and unsigned int are not layout compatible
That char and signed char/unsigned char are not layout compatible
That an enumeration type and its underlying type are not layout compatible
Done.
I think we should add those tests somewhere (either here or in dr1xxx.cpp) so 1) we know we don't crash on them, 2) we're alerted to behavioral changes in this area, and 3) better documentation of existing behavior.
Agreed. I marked 1334 as superseded by 1719, and added a test for 1719.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't issue any, as far as I can see. And I don't think we can when the types are the same, ignoring cv qualifiers:
That's a bug, see https://eel.is/c++draft/tab:meta.rel#row-6-column-3-sentence-1: "T and U shall be complete types, cv void, or arrays of unknown bound."
but also: https://eel.is/c++draft/tab:meta.rel#row-6-column-3-sentence-1and http://eel.is/c++draft/class.mem#general-23 -- you can't test for layout compatibility if the class type is not complete because you don't know the members. So we should accept void but not struct NeverDefined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- That an enumeration type and its underlying type are not layout compatible
This is possibly a defect (cplusplus/CWG#39 (comment), cplusplus/CWG#95 (comment)), but no CWG issue is filed yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for mentioning that! I left a comment around enum tests.
cor3ntin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
AaronBallman
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
This is a follow-up to llvm#81506. Since `__is_layout_compatible()` is a C++ intrinsic (https://github.com/llvm/llvm-project/blob/ff1e72d68d1224271801ff5192a8c14fbd3be83b/clang/include/clang/Basic/TokenKinds.def#L523), I don't think we should define how it interacts with VLA extension unless we have a compelling reason to. Since llvm#81506 was merged after 18 cut-off, we don't have to follow any kind of deprecation process.
This is a follow-up to #81506. Since `__is_layout_compatible()` is a C++ intrinsic (https://github.com/llvm/llvm-project/blob/ff1e72d68d1224271801ff5192a8c14fbd3be83b/clang/include/clang/Basic/TokenKinds.def#L523), I don't think we should define how it interacts with VLA extension unless we have a compelling reason to. Since #81506 was merged after 18 cut-off, we don't have to follow any kind of deprecation process.
This is a follow-up to llvm#81506. As discussed in llvm#87737, we're rejecting incomplete types, save for exceptions listed in the C++ standard (`void` and arrays of unknown bound). Note that arrays of unknown bound of incomplete types are accepted. Since we're happy with the current behavior of this intrinsic for flexible array members (llvm#87737 (comment)), I added a couple of tests for that as well.
This is a follow-up to #81506. As discussed in #87737, we're rejecting incomplete types, save for exceptions listed in the C++ standard (`void` and arrays of unknown bound). Note that arrays of unknown bound of incomplete types are accepted. Since we're happy with the current behavior of this intrinsic for flexible array members (#87737 (comment)), I added a couple of tests for that as well.
…ist (#100572) `__is_layout_compatible` was added in Clang 19 (#81506), and at that time it wasn't entirely clear whether it should be a revertible type trait or not. We decided to follow the example of other type traits. Since then #95969 happened, and now we know that we don't want new revertible type traits. This patch removes `__is_layout_compatible` from revertible type traits list, and leaves a comment what revertible type traits are, and that new type traits should not be added there. The intention is to also cherry-pick this to 19 branch.
…ist (llvm#100572) `__is_layout_compatible` was added in Clang 19 (llvm#81506), and at that time it wasn't entirely clear whether it should be a revertible type trait or not. We decided to follow the example of other type traits. Since then llvm#95969 happened, and now we know that we don't want new revertible type traits. This patch removes `__is_layout_compatible` from revertible type traits list, and leaves a comment what revertible type traits are, and that new type traits should not be added there. The intention is to also cherry-pick this to 19 branch. (cherry picked from commit 3295d37)
…ist (#100572) Summary: `__is_layout_compatible` was added in Clang 19 (#81506), and at that time it wasn't entirely clear whether it should be a revertible type trait or not. We decided to follow the example of other type traits. Since then #95969 happened, and now we know that we don't want new revertible type traits. This patch removes `__is_layout_compatible` from revertible type traits list, and leaves a comment what revertible type traits are, and that new type traits should not be added there. The intention is to also cherry-pick this to 19 branch. Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60250602
…ist (llvm#100572) `__is_layout_compatible` was added in Clang 19 (llvm#81506), and at that time it wasn't entirely clear whether it should be a revertible type trait or not. We decided to follow the example of other type traits. Since then llvm#95969 happened, and now we know that we don't want new revertible type traits. This patch removes `__is_layout_compatible` from revertible type traits list, and leaves a comment what revertible type traits are, and that new type traits should not be added there. The intention is to also cherry-pick this to 19 branch. (cherry picked from commit 3295d37)
This patch implements
__is_layout_compatibleintrinsic, which backs upstd::is_layout_compatibletype trait introduced in C++20 (P0466R5 "Layout-compatibility and Pointer-interconvertibility Traits"). Name matched GCC and MSVC intrinsics.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 "Layout compatibility and cv-qualification revisited" and CWG2759 "
[[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