Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 56f4ade

Browse files
MitalAshoktru
authored andcommitted
[Clang] Fix definition of layout-compatible to ignore empty classes (llvm#92103)
Also changes the behaviour of `__builtin_is_layout_compatible` None of the historic nor the current definition of layout-compatible classes mention anything about base classes (other than implicitly through being standard-layout) and are defined in terms of members, not direct members.
1 parent 7bfc4ab commit 56f4ade

File tree

5 files changed

+84
-50
lines changed

5 files changed

+84
-50
lines changed

clang/include/clang/AST/DeclCXX.h

+7
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,13 @@ class CXXRecordDecl : public RecordDecl {
12101210
return D.HasPublicFields || D.HasProtectedFields || D.HasPrivateFields;
12111211
}
12121212

1213+
/// If this is a standard-layout class or union, any and all data members will
1214+
/// be declared in the same type.
1215+
///
1216+
/// This retrieves the type where any fields are declared,
1217+
/// or the current class if there is no class with fields.
1218+
const CXXRecordDecl *getStandardLayoutBaseWithFields() const;
1219+
12131220
/// Whether this class is polymorphic (C++ [class.virtual]),
12141221
/// which means that the class contains or inherits a virtual function.
12151222
bool isPolymorphic() const { return data().Polymorphic; }

clang/lib/AST/DeclCXX.cpp

+36
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,42 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
561561
data().StructuralIfLiteral = false;
562562
}
563563

564+
const CXXRecordDecl *CXXRecordDecl::getStandardLayoutBaseWithFields() const {
565+
assert(
566+
isStandardLayout() &&
567+
"getStandardLayoutBaseWithFields called on a non-standard-layout type");
568+
#ifdef EXPENSIVE_CHECKS
569+
{
570+
unsigned NumberOfBasesWithFields = 0;
571+
if (!field_empty())
572+
++NumberOfBasesWithFields;
573+
llvm::SmallPtrSet<const CXXRecordDecl *, 8> UniqueBases;
574+
forallBases([&](const CXXRecordDecl *Base) -> bool {
575+
if (!Base->field_empty())
576+
++NumberOfBasesWithFields;
577+
assert(
578+
UniqueBases.insert(Base->getCanonicalDecl()).second &&
579+
"Standard layout struct has multiple base classes of the same type");
580+
return true;
581+
});
582+
assert(NumberOfBasesWithFields <= 1 &&
583+
"Standard layout struct has fields declared in more than one class");
584+
}
585+
#endif
586+
if (!field_empty())
587+
return this;
588+
const CXXRecordDecl *Result = this;
589+
forallBases([&](const CXXRecordDecl *Base) -> bool {
590+
if (!Base->field_empty()) {
591+
// This is the base where the fields are declared; return early
592+
Result = Base;
593+
return false;
594+
}
595+
return true;
596+
});
597+
return Result;
598+
}
599+
564600
bool CXXRecordDecl::hasConstexprDestructor() const {
565601
auto *Dtor = getDestructor();
566602
return Dtor ? Dtor->isConstexpr() : defaultedDestructorIsConstexpr();

clang/lib/Sema/SemaChecking.cpp

+24-50
Original file line numberDiff line numberDiff line change
@@ -13664,10 +13664,11 @@ void Sema::DiagnoseSelfMove(const Expr *LHSExpr, const Expr *RHSExpr,
1366413664

1366513665
//===--- Layout compatibility ----------------------------------------------//
1366613666

13667-
static bool isLayoutCompatible(ASTContext &C, QualType T1, QualType T2);
13667+
static bool isLayoutCompatible(const ASTContext &C, QualType T1, QualType T2);
1366813668

1366913669
/// Check if two enumeration types are layout-compatible.
13670-
static bool isLayoutCompatible(ASTContext &C, EnumDecl *ED1, EnumDecl *ED2) {
13670+
static bool isLayoutCompatible(const ASTContext &C, const EnumDecl *ED1,
13671+
const EnumDecl *ED2) {
1367113672
// C++11 [dcl.enum] p8:
1367213673
// Two enumeration types are layout-compatible if they have the same
1367313674
// underlying type.
@@ -13678,8 +13679,8 @@ static bool isLayoutCompatible(ASTContext &C, EnumDecl *ED1, EnumDecl *ED2) {
1367813679
/// Check if two fields are layout-compatible.
1367913680
/// Can be used on union members, which are exempt from alignment requirement
1368013681
/// of common initial sequence.
13681-
static bool isLayoutCompatible(ASTContext &C, FieldDecl *Field1,
13682-
FieldDecl *Field2,
13682+
static bool isLayoutCompatible(const ASTContext &C, const FieldDecl *Field1,
13683+
const FieldDecl *Field2,
1368313684
bool AreUnionMembers = false) {
1368413685
[[maybe_unused]] const Type *Field1Parent =
1368513686
Field1->getParent()->getTypeForDecl();
@@ -13722,60 +13723,33 @@ static bool isLayoutCompatible(ASTContext &C, FieldDecl *Field1,
1372213723

1372313724
/// Check if two standard-layout structs are layout-compatible.
1372413725
/// (C++11 [class.mem] p17)
13725-
static bool isLayoutCompatibleStruct(ASTContext &C, RecordDecl *RD1,
13726-
RecordDecl *RD2) {
13727-
// If both records are C++ classes, check that base classes match.
13728-
if (const CXXRecordDecl *D1CXX = dyn_cast<CXXRecordDecl>(RD1)) {
13729-
// If one of records is a CXXRecordDecl we are in C++ mode,
13730-
// thus the other one is a CXXRecordDecl, too.
13731-
const CXXRecordDecl *D2CXX = cast<CXXRecordDecl>(RD2);
13732-
// Check number of base classes.
13733-
if (D1CXX->getNumBases() != D2CXX->getNumBases())
13734-
return false;
13726+
static bool isLayoutCompatibleStruct(const ASTContext &C, const RecordDecl *RD1,
13727+
const RecordDecl *RD2) {
13728+
// Get to the class where the fields are declared
13729+
if (const CXXRecordDecl *D1CXX = dyn_cast<CXXRecordDecl>(RD1))
13730+
RD1 = D1CXX->getStandardLayoutBaseWithFields();
1373513731

13736-
// Check the base classes.
13737-
for (CXXRecordDecl::base_class_const_iterator
13738-
Base1 = D1CXX->bases_begin(),
13739-
BaseEnd1 = D1CXX->bases_end(),
13740-
Base2 = D2CXX->bases_begin();
13741-
Base1 != BaseEnd1;
13742-
++Base1, ++Base2) {
13743-
if (!isLayoutCompatible(C, Base1->getType(), Base2->getType()))
13744-
return false;
13745-
}
13746-
} else if (const CXXRecordDecl *D2CXX = dyn_cast<CXXRecordDecl>(RD2)) {
13747-
// If only RD2 is a C++ class, it should have zero base classes.
13748-
if (D2CXX->getNumBases() > 0)
13749-
return false;
13750-
}
13732+
if (const CXXRecordDecl *D2CXX = dyn_cast<CXXRecordDecl>(RD2))
13733+
RD2 = D2CXX->getStandardLayoutBaseWithFields();
1375113734

1375213735
// Check the fields.
13753-
RecordDecl::field_iterator Field2 = RD2->field_begin(),
13754-
Field2End = RD2->field_end(),
13755-
Field1 = RD1->field_begin(),
13756-
Field1End = RD1->field_end();
13757-
for ( ; Field1 != Field1End && Field2 != Field2End; ++Field1, ++Field2) {
13758-
if (!isLayoutCompatible(C, *Field1, *Field2))
13759-
return false;
13760-
}
13761-
if (Field1 != Field1End || Field2 != Field2End)
13762-
return false;
13763-
13764-
return true;
13736+
return llvm::equal(RD1->fields(), RD2->fields(),
13737+
[&C](const FieldDecl *F1, const FieldDecl *F2) -> bool {
13738+
return isLayoutCompatible(C, F1, F2);
13739+
});
1376513740
}
1376613741

1376713742
/// Check if two standard-layout unions are layout-compatible.
1376813743
/// (C++11 [class.mem] p18)
13769-
static bool isLayoutCompatibleUnion(ASTContext &C, RecordDecl *RD1,
13770-
RecordDecl *RD2) {
13771-
llvm::SmallPtrSet<FieldDecl *, 8> UnmatchedFields;
13744+
static bool isLayoutCompatibleUnion(const ASTContext &C, const RecordDecl *RD1,
13745+
const RecordDecl *RD2) {
13746+
llvm::SmallPtrSet<const FieldDecl *, 8> UnmatchedFields;
1377213747
for (auto *Field2 : RD2->fields())
1377313748
UnmatchedFields.insert(Field2);
1377413749

1377513750
for (auto *Field1 : RD1->fields()) {
13776-
llvm::SmallPtrSet<FieldDecl *, 8>::iterator
13777-
I = UnmatchedFields.begin(),
13778-
E = UnmatchedFields.end();
13751+
auto I = UnmatchedFields.begin();
13752+
auto E = UnmatchedFields.end();
1377913753

1378013754
for ( ; I != E; ++I) {
1378113755
if (isLayoutCompatible(C, Field1, *I, /*IsUnionMember=*/true)) {
@@ -13792,8 +13766,8 @@ static bool isLayoutCompatibleUnion(ASTContext &C, RecordDecl *RD1,
1379213766
return UnmatchedFields.empty();
1379313767
}
1379413768

13795-
static bool isLayoutCompatible(ASTContext &C, RecordDecl *RD1,
13796-
RecordDecl *RD2) {
13769+
static bool isLayoutCompatible(const ASTContext &C, const RecordDecl *RD1,
13770+
const RecordDecl *RD2) {
1379713771
if (RD1->isUnion() != RD2->isUnion())
1379813772
return false;
1379913773

@@ -13804,7 +13778,7 @@ static bool isLayoutCompatible(ASTContext &C, RecordDecl *RD1,
1380413778
}
1380513779

1380613780
/// Check if two types are layout-compatible in C++11 sense.
13807-
static bool isLayoutCompatible(ASTContext &C, QualType T1, QualType T2) {
13781+
static bool isLayoutCompatible(const ASTContext &C, QualType T1, QualType T2) {
1380813782
if (T1.isNull() || T2.isNull())
1380913783
return false;
1381013784

clang/test/SemaCXX/type-traits.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -1738,6 +1738,11 @@ struct CStructWithFMA2 {
17381738
int f[];
17391739
};
17401740

1741+
template<int N>
1742+
struct UniqueEmpty {};
1743+
template<typename... Bases>
1744+
struct D : Bases... {};
1745+
17411746
void is_layout_compatible(int n)
17421747
{
17431748
static_assert(__is_layout_compatible(void, void));
@@ -1841,6 +1846,12 @@ void is_layout_compatible(int n)
18411846
static_assert(!__is_layout_compatible(EnumClassLayout, int));
18421847
static_assert(!__is_layout_compatible(EnumForward, int));
18431848
static_assert(!__is_layout_compatible(EnumClassForward, int));
1849+
static_assert(__is_layout_compatible(CStruct, D<CStruct>));
1850+
static_assert(__is_layout_compatible(CStruct, D<UniqueEmpty<0>, CStruct>));
1851+
static_assert(__is_layout_compatible(CStruct, D<UniqueEmpty<0>, D<UniqueEmpty<1>, CStruct>, D<UniqueEmpty<2>>>));
1852+
static_assert(__is_layout_compatible(CStruct, D<CStructWithQualifiers>));
1853+
static_assert(__is_layout_compatible(CStruct, D<UniqueEmpty<0>, CStructWithQualifiers>));
1854+
static_assert(__is_layout_compatible(CStructWithQualifiers, D<UniqueEmpty<0>, D<UniqueEmpty<1>, CStruct>, D<UniqueEmpty<2>>>));
18441855
}
18451856

18461857
namespace IPIBO {

llvm/include/llvm/ADT/STLExtras.h

+6
Original file line numberDiff line numberDiff line change
@@ -2027,6 +2027,12 @@ template <typename L, typename R> bool equal(L &&LRange, R &&RRange) {
20272027
adl_end(RRange));
20282028
}
20292029

2030+
template <typename L, typename R, typename BinaryPredicate>
2031+
bool equal(L &&LRange, R &&RRange, BinaryPredicate P) {
2032+
return std::equal(adl_begin(LRange), adl_end(LRange), adl_begin(RRange),
2033+
adl_end(RRange), P);
2034+
}
2035+
20302036
/// Returns true if all elements in Range are equal or when the Range is empty.
20312037
template <typename R> bool all_equal(R &&Range) {
20322038
auto Begin = adl_begin(Range);

0 commit comments

Comments
 (0)