Skip to content

[Clang] Implement CWG2598: Union of non-literal types #78195

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 13 additions & 24 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
25 changes: 25 additions & 0 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions clang/test/CXX/drs/dr20xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
64 changes: 64 additions & 0 deletions clang/test/CXX/drs/dr25xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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; };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also include the exact example from CWG2598 as well, just to be complete.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also should we have a static_assert after this line?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also noticed that gcc does not seem to support __is_literal only __is_literal_type

Copy link
Contributor Author

@cor3ntin cor3ntin Jan 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, both are synonym in clang

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
}
33 changes: 33 additions & 0 deletions clang/test/SemaCXX/literal-type.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
4 changes: 2 additions & 2 deletions clang/www/cxx_dr_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down