[clang] [Clang] Fix constexpr-ness on implicitly deleted destructors (PR #116359)
A. Jiang via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 15 01:11:53 PST 2024
https://github.com/frederick-vs-ja created https://github.com/llvm/llvm-project/pull/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. It seems that only the `__is_literal` intrinsic is related, while the corresponding `std::is_literal_type(_v)` has been removed in C++20.
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.
>From 8a3cb70e3bc4bbb3a8862a1dbf22322b4b7decc2 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 15 Nov 2024 17:06:08 +0800
Subject: [PATCH] [Clang] Fix constexpr-ness on implicitly deleted destructors
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. It seems
that only the `__is_literal` intrinsic is related, while the
corresponding `std::is_literal_type(_v)` has been removed in C++20.
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.
---
.../clang/AST/CXXRecordDeclDefinitionBits.def | 3 ++
clang/include/clang/AST/DeclCXX.h | 7 +++
clang/lib/AST/DeclCXX.cpp | 24 ++++++---
clang/test/SemaCXX/literal-type.cpp | 53 +++++++++++++++++++
4 files changed, 79 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
index 6620840df0ced2..7f47fb0890f50e 100644
--- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
+++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
@@ -81,6 +81,9 @@ FIELD(IsStandardLayout, 1, NO_MERGE)
/// member.
FIELD(IsCXX11StandardLayout, 1, NO_MERGE)
+/// True when the class has a virtual base class.
+FIELD(HasVBases, 1, NO_MERGE)
+
/// True when any base class has any declared non-static data
/// members or bit-fields.
/// This is a helper bit of state used to implement IsStandardLayout more
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 2693cc0e95b4b2..6aadb9794328ae 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().HasVBases;
}
/// Determine whether this class should get an implicit move
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 4394a0724b3c17..f094482eec6165 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -77,10 +77,11 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
: UserDeclaredConstructor(false), UserDeclaredSpecialMembers(0),
Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false),
Abstract(false), IsStandardLayout(true), IsCXX11StandardLayout(true),
- HasBasesWithFields(false), HasBasesWithNonStaticDataMembers(false),
- HasPrivateFields(false), HasProtectedFields(false),
- HasPublicFields(false), HasMutableFields(false), HasVariantMembers(false),
- HasOnlyCMembers(true), HasInitMethod(false), HasInClassInitializer(false),
+ HasVBases(false), HasBasesWithFields(false),
+ HasBasesWithNonStaticDataMembers(false), HasPrivateFields(false),
+ HasProtectedFields(false), HasPublicFields(false),
+ HasMutableFields(false), HasVariantMembers(false), HasOnlyCMembers(true),
+ HasInitMethod(false), HasInClassInitializer(false),
HasUninitializedReferenceMember(false), HasUninitializedFields(false),
HasInheritedConstructor(false), HasInheritedDefaultConstructor(false),
HasInheritedAssignment(false),
@@ -316,6 +317,8 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
}
if (Base->isVirtual()) {
+ data().HasVBases = true;
+
// Add this base if it's not already in the list.
if (SeenVBaseTypes.insert(C.getCanonicalType(BaseType)).second)
VBases.push_back(Base);
@@ -547,9 +550,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())
@@ -1214,8 +1217,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/SemaCXX/literal-type.cpp b/clang/test/SemaCXX/literal-type.cpp
index 88535c169fe01c..7d5b9c286f0439 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,55 @@ void test() {
}
#endif
+
+#if __cplusplus >= 201103L
+namespace GH85550 {
+struct HasDefaultCtorAndNonConstexprDtor {
+ constexpr HasDefaultCtorAndNonConstexprDtor() = default;
+ ~HasDefaultCtorAndNonConstexprDtor() {}
+};
+
+union UnionWithNonLiteralMember { HasDefaultCtorAndNonConstexprDtor x; };
+#if __cplusplus >= 202002L
+static_assert(__is_literal(UnionWithNonLiteralMember), "fail");
+#else
+static_assert(!__is_literal(UnionWithNonLiteralMember), "fail");
+#endif
+
+union UnionWithNonLiteralMemberExplicitDtor1 {
+ HasDefaultCtorAndNonConstexprDtor x;
+
+ UnionWithNonLiteralMemberExplicitDtor1() = default;
+ ~UnionWithNonLiteralMemberExplicitDtor1() = default;
+};
+#if __cplusplus >= 202002L
+static_assert(__is_literal(UnionWithNonLiteralMemberExplicitDtor1), "fail");
+#else
+static_assert(!__is_literal(UnionWithNonLiteralMemberExplicitDtor1), "fail");
+#endif
+
+union UnionWithNonLiteralMemberExplicitDtor2 {
+ HasDefaultCtorAndNonConstexprDtor x;
+
+ UnionWithNonLiteralMemberExplicitDtor2() = default;
+ ~UnionWithNonLiteralMemberExplicitDtor2() = delete;
+};
+static_assert(!__is_literal(UnionWithNonLiteralMemberExplicitDtor2), "fail");
+
+#if __cplusplus >= 202002L
+union UnionWithNonLiteralMemberConstexprDtor1 {
+ HasDefaultCtorAndNonConstexprDtor x;
+
+ UnionWithNonLiteralMemberConstexprDtor1() = default;
+ constexpr ~UnionWithNonLiteralMemberConstexprDtor1() = default;
+};
+static_assert(__is_literal(UnionWithNonLiteralMemberConstexprDtor1), "fail");
+
+union UnionWithNonLiteralMemberConstexprDtor2 {
+ HasDefaultCtorAndNonConstexprDtor x;
+ constexpr ~UnionWithNonLiteralMemberConstexprDtor2() = delete;
+};
+static_assert(__is_literal(UnionWithNonLiteralMemberConstexprDtor2), "fail");
+#endif
+}
+#endif
More information about the cfe-commits
mailing list