Skip to content

Commit b93d63f

Browse files
[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.
1 parent 7c8e05a commit b93d63f

File tree

5 files changed

+87
-10
lines changed

5 files changed

+87
-10
lines changed

clang/include/clang/AST/CXXRecordDeclDefinitionBits.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ FIELD(IsStandardLayout, 1, NO_MERGE)
8181
/// member.
8282
FIELD(IsCXX11StandardLayout, 1, NO_MERGE)
8383

84+
/// True when the class has a virtual base class.
85+
FIELD(HasVBases, 1, NO_MERGE)
86+
8487
/// True when any base class has any declared non-static data
8588
/// members or bit-fields.
8689
/// This is a helper bit of state used to implement IsStandardLayout more

clang/include/clang/AST/DeclCXX.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,13 @@ class CXXRecordDecl : public RecordDecl {
890890
needsOverloadResolutionForDestructor()) &&
891891
"destructor should not be deleted");
892892
data().DefaultedDestructorIsDeleted = true;
893+
// C++23 [dcl.constexpr]p3.2:
894+
// if the function is a constructor or destructor, its class does not have
895+
// any virtual base classes.
896+
// C++20 [dcl.constexpr]p5:
897+
// The definition of a constexpr destructor whose function-body is
898+
// [not = delete] shall additionally satisfy...
899+
data().DefaultedDestructorIsConstexpr = !data().HasVBases;
893900
}
894901

895902
/// Determine whether this class should get an implicit move

clang/lib/AST/DeclCXX.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,11 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
7777
: UserDeclaredConstructor(false), UserDeclaredSpecialMembers(0),
7878
Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false),
7979
Abstract(false), IsStandardLayout(true), IsCXX11StandardLayout(true),
80-
HasBasesWithFields(false), HasBasesWithNonStaticDataMembers(false),
81-
HasPrivateFields(false), HasProtectedFields(false),
82-
HasPublicFields(false), HasMutableFields(false), HasVariantMembers(false),
83-
HasOnlyCMembers(true), HasInitMethod(false), HasInClassInitializer(false),
80+
HasVBases(false), HasBasesWithFields(false),
81+
HasBasesWithNonStaticDataMembers(false), HasPrivateFields(false),
82+
HasProtectedFields(false), HasPublicFields(false),
83+
HasMutableFields(false), HasVariantMembers(false), HasOnlyCMembers(true),
84+
HasInitMethod(false), HasInClassInitializer(false),
8485
HasUninitializedReferenceMember(false), HasUninitializedFields(false),
8586
HasInheritedConstructor(false), HasInheritedDefaultConstructor(false),
8687
HasInheritedAssignment(false),
@@ -316,6 +317,8 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
316317
}
317318

318319
if (Base->isVirtual()) {
320+
data().HasVBases = true;
321+
319322
// Add this base if it's not already in the list.
320323
if (SeenVBaseTypes.insert(C.getCanonicalType(BaseType)).second)
321324
VBases.push_back(Base);
@@ -547,9 +550,9 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
547550
data().NeedOverloadResolutionForDestructor = true;
548551
}
549552

550-
// C++2a [dcl.constexpr]p4:
551-
// The definition of a constexpr destructor [shall] satisfy the
552-
// following requirement:
553+
// C++20 [dcl.constexpr]p5:
554+
// The definition of a constexpr destructor whose function-body is not
555+
// = delete [shall] additionally satisfy the following requirement:
553556
// -- for every subobject of class type or (possibly multi-dimensional)
554557
// array thereof, that class type shall have a constexpr destructor
555558
if (!Subobj->hasConstexprDestructor())
@@ -1214,8 +1217,13 @@ void CXXRecordDecl::addedMember(Decl *D) {
12141217
data().DefaultedCopyAssignmentIsDeleted = true;
12151218
if (FieldRec->hasNonTrivialMoveAssignment())
12161219
data().DefaultedMoveAssignmentIsDeleted = true;
1217-
if (FieldRec->hasNonTrivialDestructor())
1220+
if (FieldRec->hasNonTrivialDestructor()) {
12181221
data().DefaultedDestructorIsDeleted = true;
1222+
// C++20 [dcl.constexpr]p5:
1223+
// The definition of a constexpr destructor whose function-body is
1224+
// [not = delete] shall additionally satisfy...
1225+
data().DefaultedDestructorIsConstexpr = true;
1226+
}
12191227
}
12201228

12211229
// For an anonymous union member, our overload resolution will perform

clang/test/AST/ByteCode/cxx23.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ namespace AnonUnionDtor {
263263
template <class T>
264264
struct opt
265265
{
266-
union { // all20-note {{is not literal}}
266+
union {
267267
char c;
268268
T data;
269269
};
@@ -279,7 +279,7 @@ namespace AnonUnionDtor {
279279
};
280280

281281
consteval void foo() {
282-
opt<A> a; // all20-error {{variable of non-literal type}}
282+
opt<A> a;
283283
}
284284

285285
void bar() { foo(); }

clang/test/SemaCXX/literal-type.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ static_assert(__is_literal(VectorExt), "fail");
2020
// [...]
2121
// -- a class type that has all of the following properties:
2222
// -- it has a trivial destructor
23+
// [P0784R7 changed the condition to "constexpr destructor" in C++20]
2324
// -- every constructor call and full-expression in the
2425
// brace-or-equal-initializers for non-static data members (if an) is
2526
// a constant expression,
@@ -108,3 +109,61 @@ void test() {
108109

109110
}
110111
#endif
112+
113+
#if __cplusplus >= 201103L
114+
namespace GH85550 {
115+
struct HasDefaultCtorAndNonConstexprDtor {
116+
constexpr HasDefaultCtorAndNonConstexprDtor() = default;
117+
~HasDefaultCtorAndNonConstexprDtor() {}
118+
};
119+
120+
union UnionWithNonLiteralMember {
121+
HasDefaultCtorAndNonConstexprDtor x;
122+
123+
constexpr UnionWithNonLiteralMember() : x{} {}
124+
};
125+
#if __cplusplus >= 202002L
126+
static_assert(__is_literal(UnionWithNonLiteralMember), "fail");
127+
#else
128+
static_assert(!__is_literal(UnionWithNonLiteralMember), "fail");
129+
#endif
130+
131+
union UnionWithNonLiteralMemberExplicitDtor1 {
132+
HasDefaultCtorAndNonConstexprDtor x;
133+
134+
constexpr UnionWithNonLiteralMemberExplicitDtor1() : x{} {}
135+
~UnionWithNonLiteralMemberExplicitDtor1() = default; // expected-warning {{explicitly defaulted destructor is implicitly deleted}}
136+
};
137+
#if __cplusplus >= 202002L
138+
static_assert(__is_literal(UnionWithNonLiteralMemberExplicitDtor1), "fail");
139+
#else
140+
static_assert(!__is_literal(UnionWithNonLiteralMemberExplicitDtor1), "fail");
141+
#endif
142+
143+
union UnionWithNonLiteralMemberExplicitDtor2 {
144+
HasDefaultCtorAndNonConstexprDtor x;
145+
146+
constexpr UnionWithNonLiteralMemberExplicitDtor2() : x{} {}
147+
~UnionWithNonLiteralMemberExplicitDtor2() = delete;
148+
};
149+
static_assert(!__is_literal(UnionWithNonLiteralMemberExplicitDtor2), "fail");
150+
151+
#if __cplusplus >= 202002L
152+
union UnionWithNonLiteralMemberConstexprDtor1 {
153+
HasDefaultCtorAndNonConstexprDtor x;
154+
155+
constexpr UnionWithNonLiteralMemberConstexprDtor1() : x{} {}
156+
constexpr ~UnionWithNonLiteralMemberConstexprDtor1() = default; // expected-warning {{explicitly defaulted destructor is implicitly deleted}}
157+
};
158+
static_assert(__is_literal(UnionWithNonLiteralMemberConstexprDtor1), "fail");
159+
160+
union UnionWithNonLiteralMemberConstexprDtor2 {
161+
HasDefaultCtorAndNonConstexprDtor x;
162+
163+
constexpr UnionWithNonLiteralMemberConstexprDtor2() : x{} {}
164+
constexpr ~UnionWithNonLiteralMemberConstexprDtor2() = delete;
165+
};
166+
static_assert(__is_literal(UnionWithNonLiteralMemberConstexprDtor2), "fail");
167+
#endif
168+
}
169+
#endif

0 commit comments

Comments
 (0)