[clang] [Clang] Implement CWG2598: Union of non-literal types (PR #78195)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 16 10:03:47 PST 2024
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/78195
>From f354ca458c1fa3cebb375f756f1a87fcf0586c3c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 15 Jan 2024 18:09:48 +0100
Subject: [PATCH 1/4] [Clang] Implement CWG2598: Union of non-literal types
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
---
clang/docs/ReleaseNotes.rst | 5 +++
clang/include/clang/AST/DeclCXX.h | 37 ++++++++---------------
clang/lib/AST/DeclCXX.cpp | 28 +++++++++++++++++
clang/test/CXX/drs/dr20xx.cpp | 2 ++
clang/test/CXX/drs/dr25xx.cpp | 47 +++++++++++++++++++++++++++++
clang/test/SemaCXX/literal-type.cpp | 33 ++++++++++++++++++++
clang/www/cxx_dr_status.html | 4 +--
7 files changed, 130 insertions(+), 26 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7e130304c5c08f8..d967d904ba3fce4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -227,6 +227,11 @@ C++2c Feature Support
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Implemented `CWG2598 <https://wg21.link/CWG2598>`_ and `CWG2096 <https://wg21.link/CWG2096>`_,
+ making unions (that have either no members or at least one literal member) literal types.
+ (`#77924: <https://github.com/llvm/llvm-project/issues/77924>`_).
+
+
C Language Changes
------------------
- ``structs``, ``unions``, and ``arrays`` that are const may now be used as
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 648f5f946408700..75b73700c44d671 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1439,31 +1439,20 @@ class CXXRecordDecl : public RecordDecl {
/// Determine whether this class is a literal type.
///
- /// C++11 [basic.types]p10:
+ /// C++20 [basic.types]p10:
/// A class type that has all the following properties:
- /// - it has a trivial destructor
- /// - every constructor call and full-expression in the
- /// brace-or-equal-intializers for non-static data members (if any) is
- /// a constant expression.
- /// - it is an aggregate type or has at least one constexpr constructor
- /// or constructor template that is not a copy or move constructor, and
- /// - all of its non-static data members and base classes are of literal
- /// types
- ///
- /// We resolve DR1361 by ignoring the second bullet. We resolve DR1452 by
- /// treating types with trivial default constructors as literal types.
- ///
- /// Only in C++17 and beyond, are lambdas literal types.
- bool isLiteral() const {
- const LangOptions &LangOpts = getLangOpts();
- return (LangOpts.CPlusPlus20 ? hasConstexprDestructor()
- : hasTrivialDestructor()) &&
- (!isLambda() || LangOpts.CPlusPlus17) &&
- !hasNonLiteralTypeFieldsOrBases() &&
- (isAggregate() || isLambda() ||
- hasConstexprNonCopyMoveConstructor() ||
- hasTrivialDefaultConstructor());
- }
+ /// - it has a constexpr destructor
+ /// - all of its non-static non-variant data members and base classes
+ /// are of non-volatile literal types, and it:
+ /// - is a closure type
+ /// - is an aggregate union type that has either no variant members
+ /// or at least one variant member of non-volatile literal type
+ /// - is a non-union aggregate type for which each of its anonymous
+ /// union members satisfies the above requirements for an aggregate
+ /// union type, or
+ /// - has at least one constexpr constructor or constructor template
+ /// that is not a copy or move constructor.
+ bool isLiteral() const;
/// Determine whether this is a structural type.
bool isStructural() const {
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 98b0a6dc28ea2f0..0d49d54fd641977 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1383,6 +1383,34 @@ void CXXRecordDecl::addedMember(Decl *D) {
}
}
+bool CXXRecordDecl::isLiteral() const {
+ const LangOptions &LangOpts = getLangOpts();
+ if (!(LangOpts.CPlusPlus20 ? hasConstexprDestructor()
+ : hasTrivialDestructor()))
+ return false;
+
+ if (isLambda() && !LangOpts.CPlusPlus17)
+ return false;
+
+ if (hasNonLiteralTypeFieldsOrBases()) {
+ // CWG2598
+ // is an aggregate union type that has either no variant
+ // members or at least one variant member of non-volatile literal type,
+ if (!isUnion())
+ return false;
+ bool HasAtLeastOneTrivialMember =
+ fields().empty() || any_of(fields(), [this](const FieldDecl *D) {
+ return !D->getType().isVolatileQualified() &&
+ D->getType().isTrivialType(getASTContext());
+ });
+ if (!HasAtLeastOneTrivialMember)
+ return false;
+ }
+
+ return isAggregate() || isLambda() || hasConstexprNonCopyMoveConstructor() ||
+ hasTrivialDefaultConstructor();
+}
+
void CXXRecordDecl::addedSelectedDestructor(CXXDestructorDecl *DD) {
DD->setIneligibleOrNotSelected(false);
addedEligibleSpecialMemberFunction(DD, SMF_Destructor);
diff --git a/clang/test/CXX/drs/dr20xx.cpp b/clang/test/CXX/drs/dr20xx.cpp
index 60ee7684440f548..f7f37379e61ad14 100644
--- a/clang/test/CXX/drs/dr20xx.cpp
+++ b/clang/test/CXX/drs/dr20xx.cpp
@@ -418,3 +418,5 @@ namespace dr2094 { // dr2094: 5
static_assert(__is_trivially_assignable(A, const A&), "");
static_assert(__is_trivially_assignable(B, const B&), "");
}
+
+// dr2096: dup 2598
diff --git a/clang/test/CXX/drs/dr25xx.cpp b/clang/test/CXX/drs/dr25xx.cpp
index 32bbfc63d0df4f3..6f51e9c249040ac 100644
--- a/clang/test/CXX/drs/dr25xx.cpp
+++ b/clang/test/CXX/drs/dr25xx.cpp
@@ -208,3 +208,50 @@ namespace dr2565 { // dr2565: 16 open
#endif
}
+
+
+namespace dr2598 { // dr2598: 18
+#if __cplusplus >= 201103L
+struct NonLiteral {
+ NonLiteral();
+};
+
+struct anonymous1 {
+ union {} a;
+};
+static_assert(__is_literal(anonymous1), "");
+
+struct anonymous2 {
+ union { char c; };
+};
+static_assert(__is_literal(anonymous2), "");
+
+struct anonymous3 {
+ union { char c; NonLiteral NL; };
+};
+static_assert(__is_literal(anonymous3), "");
+
+struct anonymous4 {
+ union { NonLiteral NL; };
+};
+static_assert(!__is_literal(anonymous4), "");
+
+union empty {};
+static_assert(__is_literal(empty), "");
+
+union union1 { char c; };
+static_assert(__is_literal(union1), "");
+
+union union2 { char c; NonLiteral NL;};
+static_assert(__is_literal(union2), "");
+
+union union3 { NonLiteral NL;};
+static_assert(!__is_literal(union3), "");
+
+union union4 { union4(); };
+static_assert(!__is_literal(union4), "");
+
+union union5 { static NonLiteral NL; };
+static_assert(__is_literal(union5), "");
+#endif
+}
diff --git a/clang/test/SemaCXX/literal-type.cpp b/clang/test/SemaCXX/literal-type.cpp
index 14a4094c45a1b81..88535c169fe01c3 100644
--- a/clang/test/SemaCXX/literal-type.cpp
+++ b/clang/test/SemaCXX/literal-type.cpp
@@ -1,4 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
+
static_assert(__is_literal(int), "fail");
static_assert(__is_literal_type(int), "fail"); // alternate spelling for GCC
@@ -75,3 +77,34 @@ template <typename T> class HasConstExprCtorT {
static_assert(__is_literal(HasConstExprCtor), "fail");
static_assert(__is_literal(HasConstExprCtorTemplate<int>), "fail");
static_assert(__is_literal(HasConstExprCtorT<NonLiteral>), "fail");
+
+
+#if __cplusplus >= 202003L
+namespace GH77924 {
+
+struct A { A(); };
+template <class T>
+struct opt {
+ union Data {
+ constexpr Data() : x{} {}
+ constexpr ~Data() {}
+ char x;
+ T data;
+ };
+
+ constexpr opt() : data{} {}
+ constexpr ~opt() { if (engaged) data.data.~T(); }
+ Data data;
+ bool engaged = false;
+};
+
+consteval void foo() {
+ opt<A> a;
+}
+
+void test() {
+ foo();
+}
+
+}
+#endif
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 397bf1357d3cb36..5e7c1a0fa2f2462 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -12384,7 +12384,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2096.html">2096</a></td>
<td>CD4</td>
<td>Constraints on literal unions</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="unreleased" align="center">Duplicate of <a href="#2598">2598</a></td>
</tr>
<tr class="open" id="2097">
<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>
<td><a href="https://cplusplus.github.io/CWG/issues/2598.html">2598</a></td>
<td>C++23</td>
<td>Unions should not require a non-static data member of literal type</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 18</td>
</tr>
<tr id="2599">
<td><a href="https://cplusplus.github.io/CWG/issues/2599.html">2599</a></td>
>From ec1e9ccee0b780dff4288b7f219037657ba7af28 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 15 Jan 2024 19:07:57 +0100
Subject: [PATCH 2/4] s/isTrivialType/isLiteralType
---
clang/lib/AST/DeclCXX.cpp | 2 +-
clang/test/CXX/drs/dr25xx.cpp | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 0d49d54fd641977..8cb8500be8b28e5 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1401,7 +1401,7 @@ bool CXXRecordDecl::isLiteral() const {
bool HasAtLeastOneTrivialMember =
fields().empty() || any_of(fields(), [this](const FieldDecl *D) {
return !D->getType().isVolatileQualified() &&
- D->getType().isTrivialType(getASTContext());
+ D->getType()->isLiteralType(getASTContext());
});
if (!HasAtLeastOneTrivialMember)
return false;
diff --git a/clang/test/CXX/drs/dr25xx.cpp b/clang/test/CXX/drs/dr25xx.cpp
index 6f51e9c249040ac..9b712b14088faca 100644
--- a/clang/test/CXX/drs/dr25xx.cpp
+++ b/clang/test/CXX/drs/dr25xx.cpp
@@ -253,5 +253,9 @@ static_assert(!__is_literal(union4), "");
union union5 { static NonLiteral NL; };
static_assert(__is_literal(union5), "");
+
+struct Literal { constexpr Literal() {} };
+union union6 { NonLiteral NL; Literal L; };
+
#endif
}
>From b3fd98f7e24704c092cb09c967b22899a05ac993 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 16 Jan 2024 00:27:44 +0100
Subject: [PATCH 3/4] Address Shafik's comments
---
clang/lib/AST/DeclCXX.cpp | 4 ++--
clang/test/CXX/drs/dr25xx.cpp | 13 +++++++++++++
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 8cb8500be8b28e5..8600b8a4faf7f4e 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1398,12 +1398,12 @@ bool CXXRecordDecl::isLiteral() const {
// members or at least one variant member of non-volatile literal type,
if (!isUnion())
return false;
- bool HasAtLeastOneTrivialMember =
+ bool HasAtLeastOneLiteralMember =
fields().empty() || any_of(fields(), [this](const FieldDecl *D) {
return !D->getType().isVolatileQualified() &&
D->getType()->isLiteralType(getASTContext());
});
- if (!HasAtLeastOneTrivialMember)
+ if (!HasAtLeastOneLiteralMember)
return false;
}
diff --git a/clang/test/CXX/drs/dr25xx.cpp b/clang/test/CXX/drs/dr25xx.cpp
index 9b712b14088faca..502f03271d9afe2 100644
--- a/clang/test/CXX/drs/dr25xx.cpp
+++ b/clang/test/CXX/drs/dr25xx.cpp
@@ -256,6 +256,19 @@ static_assert(__is_literal(union5), "");
struct Literal { constexpr Literal() {} };
union union6 { NonLiteral NL; Literal L; };
+static_assert(__is_literal(union6), "");
+
+#if __cplusplus >= 202003L
+struct A { A(); };
+union U {
+ A a;
+ constexpr U() {}
+ constexpr ~U() {}
+};
+static_assert(!__is_literal(U), "");
+#endif
+
+
#endif
}
>From 5fa791ade8b22b3504a5023f562ce585f8c0c72f Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 16 Jan 2024 19:03:32 +0100
Subject: [PATCH 4/4] Address Erich's comments
---
clang/lib/AST/DeclCXX.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 8600b8a4faf7f4e..62b18dbc2d3b982 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1389,7 +1389,11 @@ bool CXXRecordDecl::isLiteral() const {
: hasTrivialDestructor()))
return false;
- if (isLambda() && !LangOpts.CPlusPlus17)
+ // Lambdas are literal types since C++17.
+ if (!isAggregate()
+ // Lambdas are literal types since C++17.
+ && !(LangOpts.CPlusPlus17 && isLambda()) &&
+ !hasConstexprNonCopyMoveConstructor() && !hasTrivialDefaultConstructor())
return false;
if (hasNonLiteralTypeFieldsOrBases()) {
@@ -1407,8 +1411,7 @@ bool CXXRecordDecl::isLiteral() const {
return false;
}
- return isAggregate() || isLambda() || hasConstexprNonCopyMoveConstructor() ||
- hasTrivialDefaultConstructor();
+ return true;
}
void CXXRecordDecl::addedSelectedDestructor(CXXDestructorDecl *DD) {
More information about the cfe-commits
mailing list