[clang] 63c5a42 - [Clang] Fix constexpr-ness on implicitly deleted destructors (#116359)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 27 17:19:05 PST 2024
Author: A. Jiang
Date: 2024-11-28T09:19:02+08:00
New Revision: 63c5a422f07da9925a4700f54016ab8623567718
URL: https://github.com/llvm/llvm-project/commit/63c5a422f07da9925a4700f54016ab8623567718
DIFF: https://github.com/llvm/llvm-project/commit/63c5a422f07da9925a4700f54016ab8623567718.diff
LOG: [Clang] Fix constexpr-ness on implicitly deleted destructors (#116359)
In C++20, a defaulted but implicitly deleted destructor is constexpr if
and only if the class has no virtual base class. This hasn't been
changed in C++23 by P2448R2.
Constexpr-ness on a deleted destructor affects almost nothing. The
`__is_literal` intrinsic is related, while the corresponding
`std::is_literal_type(_v)` utility has been removed in C++20. A recently
added example in `test/AST/ByteCode/cxx23.cpp` will become valid, and
the example is already accepted by GCC.
Clang currently behaves correctly in C++23 mode, because the
constexpr-ness on defaulted destructor is relaxed by P2448R2. But we
should make similar relaxation for an implicitly deleted destructor.
Fixes #85550.
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/DeclCXX.h
clang/lib/AST/DeclCXX.cpp
clang/test/AST/ByteCode/cxx23.cpp
clang/test/SemaCXX/literal-type.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b1b8f3bfa33b19..70227a6248afad 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -158,6 +158,35 @@ C++ Specific Potentially Breaking Changes
Previously, this code was erroneously accepted.
+- Clang will now consider the implicitly deleted destructor of a union or
+ a non-union class without virtual base class to be ``constexpr`` in C++20
+ mode (Clang 19 only did so in C++23 mode but the standard specification for
+ this changed in C++20). (#GH85550)
+
+ .. code-block:: c++
+
+ struct NonLiteral {
+ NonLiteral() {}
+ ~NonLiteral() {}
+ };
+
+ template <class T>
+ struct Opt {
+ union {
+ char c;
+ T data;
+ };
+ bool engaged = false;
+
+ constexpr Opt() {}
+ constexpr ~Opt() {
+ if (engaged)
+ data.~T();
+ }
+ };
+
+ // Previously only accepted in C++23 and later, now also accepted in C++20.
+ consteval void foo() { Opt<NonLiteral>{}; }
ABI Changes in This Version
---------------------------
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 2693cc0e95b4b2..e389b5cd6df5b9 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -890,6 +890,13 @@ class CXXRecordDecl : public RecordDecl {
needsOverloadResolutionForDestructor()) &&
"destructor should not be deleted");
data().DefaultedDestructorIsDeleted = true;
+ // C++23 [dcl.constexpr]p3.2:
+ // if the function is a constructor or destructor, its class does not have
+ // any virtual base classes.
+ // C++20 [dcl.constexpr]p5:
+ // The definition of a constexpr destructor whose function-body is
+ // not = delete shall additionally satisfy...
+ data().DefaultedDestructorIsConstexpr = data().NumVBases == 0;
}
/// Determine whether this class should get an implicit move
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 25560faae8672b..f2f2835641245e 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -546,9 +546,9 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
data().NeedOverloadResolutionForDestructor = true;
}
- // C++2a [dcl.constexpr]p4:
- // The definition of a constexpr destructor [shall] satisfy the
- // following requirement:
+ // C++20 [dcl.constexpr]p5:
+ // The definition of a constexpr destructor whose function-body is not
+ // = delete shall additionally satisfy the following requirement:
// -- for every subobject of class type or (possibly multi-dimensional)
// array thereof, that class type shall have a constexpr destructor
if (!Subobj->hasConstexprDestructor())
@@ -1213,8 +1213,13 @@ void CXXRecordDecl::addedMember(Decl *D) {
data().DefaultedCopyAssignmentIsDeleted = true;
if (FieldRec->hasNonTrivialMoveAssignment())
data().DefaultedMoveAssignmentIsDeleted = true;
- if (FieldRec->hasNonTrivialDestructor())
+ if (FieldRec->hasNonTrivialDestructor()) {
data().DefaultedDestructorIsDeleted = true;
+ // C++20 [dcl.constexpr]p5:
+ // The definition of a constexpr destructor whose function-body is
+ // not = delete shall additionally satisfy...
+ data().DefaultedDestructorIsConstexpr = true;
+ }
}
// For an anonymous union member, our overload resolution will perform
diff --git a/clang/test/AST/ByteCode/cxx23.cpp b/clang/test/AST/ByteCode/cxx23.cpp
index 1803fb8ab2e9a4..6a62ac11cde792 100644
--- a/clang/test/AST/ByteCode/cxx23.cpp
+++ b/clang/test/AST/ByteCode/cxx23.cpp
@@ -263,7 +263,7 @@ namespace AnonUnionDtor {
template <class T>
struct opt
{
- union { // all20-note {{is not literal}}
+ union {
char c;
T data;
};
@@ -279,7 +279,7 @@ namespace AnonUnionDtor {
};
consteval void foo() {
- opt<A> a; // all20-error {{variable of non-literal type}}
+ opt<A> a;
}
void bar() { foo(); }
diff --git a/clang/test/SemaCXX/literal-type.cpp b/clang/test/SemaCXX/literal-type.cpp
index 88535c169fe01c..44b6ba62262fd0 100644
--- a/clang/test/SemaCXX/literal-type.cpp
+++ b/clang/test/SemaCXX/literal-type.cpp
@@ -20,6 +20,7 @@ static_assert(__is_literal(VectorExt), "fail");
// [...]
// -- a class type that has all of the following properties:
// -- it has a trivial destructor
+// [P0784R7 changed the condition to "constexpr destructor" in C++20]
// -- every constructor call and full-expression in the
// brace-or-equal-initializers for non-static data members (if an) is
// a constant expression,
@@ -108,3 +109,70 @@ void test() {
}
#endif
+
+#if __cplusplus >= 201103L
+namespace GH85550 {
+struct HasDefaultCtorAndNonConstexprDtor {
+ constexpr HasDefaultCtorAndNonConstexprDtor() = default;
+ ~HasDefaultCtorAndNonConstexprDtor() {}
+};
+
+union UnionWithNonLiteralMember {
+ HasDefaultCtorAndNonConstexprDtor x;
+ int y;
+
+ constexpr UnionWithNonLiteralMember() : x{} {}
+};
+#if __cplusplus >= 202002L
+static_assert(__is_literal(UnionWithNonLiteralMember), "fail");
+#else
+static_assert(!__is_literal(UnionWithNonLiteralMember), "fail");
+#endif
+
+union UnionWithNonLiteralMemberExplicitDtor1 {
+ HasDefaultCtorAndNonConstexprDtor x;
+ int y;
+ // expected-note at -2 {{destructor of 'UnionWithNonLiteralMemberExplicitDtor1' is implicitly deleted because variant field 'x' has a non-trivial destructor}}
+
+ constexpr UnionWithNonLiteralMemberExplicitDtor1() : x{} {}
+ ~UnionWithNonLiteralMemberExplicitDtor1() = default; // expected-warning {{explicitly defaulted destructor is implicitly deleted}}
+ // expected-note at -1 {{replace 'default' with 'delete'}}
+};
+#if __cplusplus >= 202002L
+static_assert(__is_literal(UnionWithNonLiteralMemberExplicitDtor1), "fail");
+#else
+static_assert(!__is_literal(UnionWithNonLiteralMemberExplicitDtor1), "fail");
+#endif
+
+union UnionWithNonLiteralMemberExplicitDtor2 {
+ HasDefaultCtorAndNonConstexprDtor x;
+ int y;
+
+ constexpr UnionWithNonLiteralMemberExplicitDtor2() : x{} {}
+ ~UnionWithNonLiteralMemberExplicitDtor2() = delete;
+};
+static_assert(!__is_literal(UnionWithNonLiteralMemberExplicitDtor2), "fail");
+
+#if __cplusplus >= 202002L
+union UnionWithNonLiteralMemberConstexprDtor1 {
+ HasDefaultCtorAndNonConstexprDtor x;
+ int y;
+ // expected-note at -2 {{destructor of 'UnionWithNonLiteralMemberConstexprDtor1' is implicitly deleted because variant field 'x' has a non-trivial destructor}}
+
+ constexpr UnionWithNonLiteralMemberConstexprDtor1() : x{} {}
+ constexpr ~UnionWithNonLiteralMemberConstexprDtor1() = default; // expected-warning {{explicitly defaulted destructor is implicitly deleted}}
+ // expected-note at -1 {{replace 'default' with 'delete'}}
+};
+static_assert(__is_literal(UnionWithNonLiteralMemberConstexprDtor1), "fail");
+
+union UnionWithNonLiteralMemberConstexprDtor2 {
+ HasDefaultCtorAndNonConstexprDtor x;
+ int y;
+
+ constexpr UnionWithNonLiteralMemberConstexprDtor2() : x{} {}
+ constexpr ~UnionWithNonLiteralMemberConstexprDtor2() = delete;
+};
+static_assert(__is_literal(UnionWithNonLiteralMemberConstexprDtor2), "fail");
+#endif
+}
+#endif
More information about the cfe-commits
mailing list