Skip to content

Commit 4e64159

Browse files
authored
[Clang] Implement CWG2598: Union of non-literal types (#78195)
A union is considered a literal type unless it has no non-literal member. This resolves CWG2096 (which makes unions with literal members literal) and CWG2598 (empty unions are literal types). Fixes #77924
1 parent 933c25e commit 4e64159

File tree

7 files changed

+144
-26
lines changed

7 files changed

+144
-26
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,11 @@ C++2c Feature Support
227227
Resolutions to C++ Defect Reports
228228
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
229229

230+
- Implemented `CWG2598 <https://wg21.link/CWG2598>`_ and `CWG2096 <https://wg21.link/CWG2096>`_,
231+
making unions (that have either no members or at least one literal member) literal types.
232+
(`#77924: <https://github.com/llvm/llvm-project/issues/77924>`_).
233+
234+
230235
C Language Changes
231236
------------------
232237
- ``structs``, ``unions``, and ``arrays`` that are const may now be used as

clang/include/clang/AST/DeclCXX.h

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,31 +1439,20 @@ class CXXRecordDecl : public RecordDecl {
14391439

14401440
/// Determine whether this class is a literal type.
14411441
///
1442-
/// C++11 [basic.types]p10:
1442+
/// C++20 [basic.types]p10:
14431443
/// A class type that has all the following properties:
1444-
/// - it has a trivial destructor
1445-
/// - every constructor call and full-expression in the
1446-
/// brace-or-equal-intializers for non-static data members (if any) is
1447-
/// a constant expression.
1448-
/// - it is an aggregate type or has at least one constexpr constructor
1449-
/// or constructor template that is not a copy or move constructor, and
1450-
/// - all of its non-static data members and base classes are of literal
1451-
/// types
1452-
///
1453-
/// We resolve DR1361 by ignoring the second bullet. We resolve DR1452 by
1454-
/// treating types with trivial default constructors as literal types.
1455-
///
1456-
/// Only in C++17 and beyond, are lambdas literal types.
1457-
bool isLiteral() const {
1458-
const LangOptions &LangOpts = getLangOpts();
1459-
return (LangOpts.CPlusPlus20 ? hasConstexprDestructor()
1460-
: hasTrivialDestructor()) &&
1461-
(!isLambda() || LangOpts.CPlusPlus17) &&
1462-
!hasNonLiteralTypeFieldsOrBases() &&
1463-
(isAggregate() || isLambda() ||
1464-
hasConstexprNonCopyMoveConstructor() ||
1465-
hasTrivialDefaultConstructor());
1466-
}
1444+
/// - it has a constexpr destructor
1445+
/// - all of its non-static non-variant data members and base classes
1446+
/// are of non-volatile literal types, and it:
1447+
/// - is a closure type
1448+
/// - is an aggregate union type that has either no variant members
1449+
/// or at least one variant member of non-volatile literal type
1450+
/// - is a non-union aggregate type for which each of its anonymous
1451+
/// union members satisfies the above requirements for an aggregate
1452+
/// union type, or
1453+
/// - has at least one constexpr constructor or constructor template
1454+
/// that is not a copy or move constructor.
1455+
bool isLiteral() const;
14671456

14681457
/// Determine whether this is a structural type.
14691458
bool isStructural() const {

clang/lib/AST/DeclCXX.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,6 +1383,31 @@ void CXXRecordDecl::addedMember(Decl *D) {
13831383
}
13841384
}
13851385

1386+
bool CXXRecordDecl::isLiteral() const {
1387+
const LangOptions &LangOpts = getLangOpts();
1388+
if (!(LangOpts.CPlusPlus20 ? hasConstexprDestructor()
1389+
: hasTrivialDestructor()))
1390+
return false;
1391+
1392+
if (hasNonLiteralTypeFieldsOrBases()) {
1393+
// CWG2598
1394+
// is an aggregate union type that has either no variant
1395+
// members or at least one variant member of non-volatile literal type,
1396+
if (!isUnion())
1397+
return false;
1398+
bool HasAtLeastOneLiteralMember =
1399+
fields().empty() || any_of(fields(), [this](const FieldDecl *D) {
1400+
return !D->getType().isVolatileQualified() &&
1401+
D->getType()->isLiteralType(getASTContext());
1402+
});
1403+
if (!HasAtLeastOneLiteralMember)
1404+
return false;
1405+
}
1406+
1407+
return isAggregate() || (isLambda() && LangOpts.CPlusPlus17) ||
1408+
hasConstexprNonCopyMoveConstructor() || hasTrivialDefaultConstructor();
1409+
}
1410+
13861411
void CXXRecordDecl::addedSelectedDestructor(CXXDestructorDecl *DD) {
13871412
DD->setIneligibleOrNotSelected(false);
13881413
addedEligibleSpecialMemberFunction(DD, SMF_Destructor);

clang/test/CXX/drs/dr20xx.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,3 +418,5 @@ namespace dr2094 { // dr2094: 5
418418
static_assert(__is_trivially_assignable(A, const A&), "");
419419
static_assert(__is_trivially_assignable(B, const B&), "");
420420
}
421+
422+
// dr2096: dup 2598

clang/test/CXX/drs/dr25xx.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,67 @@ namespace dr2565 { // dr2565: 16 open
208208

209209
#endif
210210
}
211+
212+
213+
namespace dr2598 { // dr2598: 18
214+
#if __cplusplus >= 201103L
215+
struct NonLiteral {
216+
NonLiteral();
217+
};
218+
219+
struct anonymous1 {
220+
union {} a;
221+
};
222+
static_assert(__is_literal(anonymous1), "");
223+
224+
struct anonymous2 {
225+
union { char c; };
226+
};
227+
static_assert(__is_literal(anonymous2), "");
228+
229+
struct anonymous3 {
230+
union { char c; NonLiteral NL; };
231+
};
232+
static_assert(__is_literal(anonymous3), "");
233+
234+
struct anonymous4 {
235+
union { NonLiteral NL; };
236+
};
237+
static_assert(!__is_literal(anonymous4), "");
238+
239+
union empty {};
240+
static_assert(__is_literal(empty), "");
241+
242+
union union1 { char c; };
243+
static_assert(__is_literal(union1), "");
244+
245+
union union2 { char c; NonLiteral NL;};
246+
static_assert(__is_literal(union2), "");
247+
248+
union union3 { NonLiteral NL;};
249+
static_assert(!__is_literal(union3), "");
250+
251+
union union4 { union4(); };
252+
static_assert(!__is_literal(union4), "");
253+
254+
union union5 { static NonLiteral NL; };
255+
static_assert(__is_literal(union5), "");
256+
257+
struct Literal { constexpr Literal() {} };
258+
union union6 { NonLiteral NL; Literal L; };
259+
static_assert(__is_literal(union6), "");
260+
261+
#if __cplusplus >= 202003L
262+
struct A { A(); };
263+
union U {
264+
A a;
265+
constexpr U() {}
266+
constexpr ~U() {}
267+
};
268+
static_assert(!__is_literal(U), "");
269+
#endif
270+
271+
272+
273+
#endif
274+
}

clang/test/SemaCXX/literal-type.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
3+
24

35
static_assert(__is_literal(int), "fail");
46
static_assert(__is_literal_type(int), "fail"); // alternate spelling for GCC
@@ -75,3 +77,34 @@ template <typename T> class HasConstExprCtorT {
7577
static_assert(__is_literal(HasConstExprCtor), "fail");
7678
static_assert(__is_literal(HasConstExprCtorTemplate<int>), "fail");
7779
static_assert(__is_literal(HasConstExprCtorT<NonLiteral>), "fail");
80+
81+
82+
#if __cplusplus >= 202003L
83+
namespace GH77924 {
84+
85+
struct A { A(); };
86+
template <class T>
87+
struct opt {
88+
union Data {
89+
constexpr Data() : x{} {}
90+
constexpr ~Data() {}
91+
char x;
92+
T data;
93+
};
94+
95+
constexpr opt() : data{} {}
96+
constexpr ~opt() { if (engaged) data.data.~T(); }
97+
Data data;
98+
bool engaged = false;
99+
};
100+
101+
consteval void foo() {
102+
opt<A> a;
103+
}
104+
105+
void test() {
106+
foo();
107+
}
108+
109+
}
110+
#endif

clang/www/cxx_dr_status.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12384,7 +12384,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1238412384
<td><a href="https://cplusplus.github.io/CWG/issues/2096.html">2096</a></td>
1238512385
<td>CD4</td>
1238612386
<td>Constraints on literal unions</td>
12387-
<td class="unknown" align="center">Unknown</td>
12387+
<td class="unreleased" align="center">Duplicate of <a href="#2598">2598</a></td>
1238812388
</tr>
1238912389
<tr class="open" id="2097">
1239012390
<td><a href="https://cplusplus.github.io/CWG/issues/2097.html">2097</a></td>
@@ -15396,7 +15396,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1539615396
<td><a href="https://cplusplus.github.io/CWG/issues/2598.html">2598</a></td>
1539715397
<td>C++23</td>
1539815398
<td>Unions should not require a non-static data member of literal type</td>
15399-
<td class="unknown" align="center">Unknown</td>
15399+
<td class="unreleased" align="center">Clang 18</td>
1540015400
</tr>
1540115401
<tr id="2599">
1540215402
<td><a href="https://cplusplus.github.io/CWG/issues/2599.html">2599</a></td>

0 commit comments

Comments
 (0)