Skip to content

Commit b6a3400

Browse files
authored
[clang] Respect field alignment in layout compatibility of structs (#84313)
This patch implements [CWG2586](https://cplusplus.github.io/CWG/issues/2583.html) "Common initial sequence should consider over-alignment". Note that alignment of union members doesn't have to match, as layout compatibility of unions is not defined in terms of common initial sequence (http://eel.is/c++draft/class.mem.general#25).
1 parent f6b825f commit b6a3400

File tree

5 files changed

+64
-4
lines changed

5 files changed

+64
-4
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ Resolutions to C++ Defect Reports
115115
of two types.
116116
(`CWG1719: Layout compatibility and cv-qualification revisited <https://cplusplus.github.io/CWG/issues/1719.html>`_).
117117

118+
- Alignment of members is now respected when evaluating layout compatibility
119+
of structs.
120+
(`CWG2583: Common initial sequence should consider over-alignment <https://cplusplus.github.io/CWG/issues/2583.html>`_).
121+
118122
- ``[[no_unique_address]]`` is now respected when evaluating layout
119123
compatibility of two types.
120124
(`CWG2759: [[no_unique_address] and common initial sequence <https://cplusplus.github.io/CWG/issues/2759.html>`_).

clang/lib/Sema/SemaChecking.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19184,8 +19184,22 @@ static bool isLayoutCompatible(ASTContext &C, EnumDecl *ED1, EnumDecl *ED2) {
1918419184
}
1918519185

1918619186
/// Check if two fields are layout-compatible.
19187+
/// Can be used on union members, which are exempt from alignment requirement
19188+
/// of common initial sequence.
1918719189
static bool isLayoutCompatible(ASTContext &C, FieldDecl *Field1,
19188-
FieldDecl *Field2) {
19190+
FieldDecl *Field2,
19191+
bool AreUnionMembers = false) {
19192+
const Type *Field1Parent = Field1->getParent()->getTypeForDecl();
19193+
const Type *Field2Parent = Field2->getParent()->getTypeForDecl();
19194+
assert(((Field1Parent->isStructureOrClassType() &&
19195+
Field2Parent->isStructureOrClassType()) ||
19196+
(Field1Parent->isUnionType() && Field2Parent->isUnionType())) &&
19197+
"Can't evaluate layout compatibility between a struct field and a "
19198+
"union field.");
19199+
assert(((!AreUnionMembers && Field1Parent->isStructureOrClassType()) ||
19200+
(AreUnionMembers && Field1Parent->isUnionType())) &&
19201+
"AreUnionMembers should be 'true' for union fields (only).");
19202+
1918919203
if (!isLayoutCompatible(C, Field1->getType(), Field2->getType()))
1919019204
return false;
1919119205

@@ -19204,6 +19218,11 @@ static bool isLayoutCompatible(ASTContext &C, FieldDecl *Field1,
1920419218
if (Field1->hasAttr<clang::NoUniqueAddressAttr>() ||
1920519219
Field2->hasAttr<clang::NoUniqueAddressAttr>())
1920619220
return false;
19221+
19222+
if (!AreUnionMembers &&
19223+
Field1->getMaxAlignment() != Field2->getMaxAlignment())
19224+
return false;
19225+
1920719226
return true;
1920819227
}
1920919228

@@ -19265,7 +19284,7 @@ static bool isLayoutCompatibleUnion(ASTContext &C, RecordDecl *RD1,
1926519284
E = UnmatchedFields.end();
1926619285

1926719286
for ( ; I != E; ++I) {
19268-
if (isLayoutCompatible(C, Field1, *I)) {
19287+
if (isLayoutCompatible(C, Field1, *I, /*IsUnionMember=*/true)) {
1926919288
bool Result = UnmatchedFields.erase(*I);
1927019289
(void) Result;
1927119290
assert(Result);

clang/test/CXX/drs/dr25xx.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,32 @@ namespace dr2565 { // dr2565: 16 open 2023-06-07
211211
#endif
212212
}
213213

214+
namespace dr2583 { // dr2583: 19
215+
#if __cplusplus >= 201103L
216+
struct A {
217+
int i;
218+
char c;
219+
};
220+
221+
struct B {
222+
int i;
223+
alignas(8) char c;
224+
};
225+
226+
union U {
227+
A a;
228+
B b;
229+
};
230+
231+
union V {
232+
A a;
233+
alignas(64) B b;
234+
};
235+
236+
static_assert(!__is_layout_compatible(A, B), "");
237+
static_assert(__is_layout_compatible(U, V), "");
238+
#endif
239+
} // namespace dr2583
214240

215241
namespace dr2598 { // dr2598: 18
216242
#if __cplusplus >= 201103L

clang/test/SemaCXX/type-traits.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,16 @@ union UnionLayout3 {
16811681
[[no_unique_address]] CEmptyStruct d;
16821682
};
16831683

1684+
union UnionNoOveralignedMembers {
1685+
int a;
1686+
double b;
1687+
};
1688+
1689+
union UnionWithOveralignedMembers {
1690+
int a;
1691+
alignas(16) double b;
1692+
};
1693+
16841694
struct StructWithAnonUnion {
16851695
union {
16861696
int a;
@@ -1771,7 +1781,8 @@ void is_layout_compatible(int n)
17711781
static_assert(__is_layout_compatible(CStruct, CStructNoUniqueAddress) != bool(__has_cpp_attribute(no_unique_address)), "");
17721782
static_assert(__is_layout_compatible(CStructNoUniqueAddress, CStructNoUniqueAddress2) != bool(__has_cpp_attribute(no_unique_address)), "");
17731783
static_assert(__is_layout_compatible(CStruct, CStructAlignment), "");
1774-
static_assert(__is_layout_compatible(CStruct, CStructAlignedMembers), ""); // FIXME: alignment of members impact common initial sequence
1784+
static_assert(!__is_layout_compatible(CStruct, CStructAlignedMembers), "");
1785+
static_assert(__is_layout_compatible(UnionNoOveralignedMembers, UnionWithOveralignedMembers), "");
17751786
static_assert(__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds), "");
17761787
static_assert(__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds2), "");
17771788
static_assert(!__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds3), "");

clang/www/cxx_dr_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15306,7 +15306,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1530615306
<td><a href="https://cplusplus.github.io/CWG/issues/2583.html">2583</a></td>
1530715307
<td>C++23</td>
1530815308
<td>Common initial sequence should consider over-alignment</td>
15309-
<td class="unknown" align="center">Unknown</td>
15309+
<td class="unreleased" align="center">Clang 19</td>
1531015310
</tr>
1531115311
<tr class="open" id="2584">
1531215312
<td><a href="https://cplusplus.github.io/CWG/issues/2584.html">2584</a></td>

0 commit comments

Comments
 (0)