diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 1eba8ab5590c5..e45e016b3d66b 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..c11f6458c07dd 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1383,6 +1383,31 @@ void CXXRecordDecl::addedMember(Decl *D) { } } +bool CXXRecordDecl::isLiteral() const { + const LangOptions &LangOpts = getLangOpts(); + if (!(LangOpts.CPlusPlus20 ? hasConstexprDestructor() + : hasTrivialDestructor())) + 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 HasAtLeastOneLiteralMember = + fields().empty() || any_of(fields(), [this](const FieldDecl *D) { + return !D->getType().isVolatileQualified() && + D->getType()->isLiteralType(getASTContext()); + }); + if (!HasAtLeastOneLiteralMember) + return false; + } + + return isAggregate() || (isLambda() && LangOpts.CPlusPlus17) || + 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..502f03271d9af 100644 --- a/clang/test/CXX/drs/dr25xx.cpp +++ b/clang/test/CXX/drs/dr25xx.cpp @@ -208,3 +208,67 @@ 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), ""); + +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 +} 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