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 <corentinja...@gmail.com> Date: Mon, 15 Jan 2024 18:09:48 +0100 Subject: [PATCH 1/3] [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 <corentinja...@gmail.com> Date: Mon, 15 Jan 2024 19:07:57 +0100 Subject: [PATCH 2/3] 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 <corentinja...@gmail.com> Date: Tue, 16 Jan 2024 00:27:44 +0100 Subject: [PATCH 3/3] 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 } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits