From f354ca458c1fa3cebb375f756f1a87fcf0586c3c Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Mon, 15 Jan 2024 18:09:48 +0100 Subject: [PATCH 1/5] [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 7e130304c5c08..d967d904ba3fc 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 `_ and `CWG2096 `_, + making unions (that have either no members or at least one literal member) literal types. + (`#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 648f5f9464087..75b73700c44d6 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 98b0a6dc28ea2..0d49d54fd6419 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 60ee7684440f5..f7f37379e61ad 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 32bbfc63d0df4..6f51e9c249040 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 14a4094c45a1b..88535c169fe01 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 class HasConstExprCtorT { static_assert(__is_literal(HasConstExprCtor), "fail"); static_assert(__is_literal(HasConstExprCtorTemplate), "fail"); static_assert(__is_literal(HasConstExprCtorT), "fail"); + + +#if __cplusplus >= 202003L +namespace GH77924 { + +struct A { A(); }; +template +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; +} + +void test() { + foo(); +} + +} +#endif diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 397bf1357d3cb..5e7c1a0fa2f24 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -12384,7 +12384,7 @@

C++ defect report implementation status

2096 CD4 Constraints on literal unions - Unknown + Duplicate of 2598 2097 @@ -15396,7 +15396,7 @@

C++ defect report implementation status

2598 C++23 Unions should not require a non-static data member of literal type - Unknown + Clang 18 2599 From ec1e9ccee0b780dff4288b7f219037657ba7af28 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Mon, 15 Jan 2024 19:07:57 +0100 Subject: [PATCH 2/5] 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 0d49d54fd6419..8cb8500be8b28 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 6f51e9c249040..9b712b14088fa 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 Date: Tue, 16 Jan 2024 00:27:44 +0100 Subject: [PATCH 3/5] 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 8cb8500be8b28..8600b8a4faf7f 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 9b712b14088fa..502f03271d9af 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 Date: Tue, 16 Jan 2024 19:03:32 +0100 Subject: [PATCH 4/5] 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 8600b8a4faf7f..62b18dbc2d3b9 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) { From 1ae7695dfddc50378ee600a97fe342571f15af37 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Tue, 16 Jan 2024 19:48:54 +0100 Subject: [PATCH 5/5] Tweak isLiteral again --- clang/lib/AST/DeclCXX.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 62b18dbc2d3b9..c11f6458c07dd 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1389,13 +1389,6 @@ bool CXXRecordDecl::isLiteral() const { : hasTrivialDestructor())) return false; - // 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()) { // CWG2598 // is an aggregate union type that has either no variant @@ -1411,7 +1404,8 @@ bool CXXRecordDecl::isLiteral() const { return false; } - return true; + return isAggregate() || (isLambda() && LangOpts.CPlusPlus17) || + hasConstexprNonCopyMoveConstructor() || hasTrivialDefaultConstructor(); } void CXXRecordDecl::addedSelectedDestructor(CXXDestructorDecl *DD) {