Skip to content

Commit 457226e

Browse files
committed
For P0784R7: add support for constexpr destructors, and call them as
appropriate during constant evaluation. Note that the evaluator is sometimes invoked on incomplete expressions. In such cases, if an object is constructed but we never reach the point where it would be destroyed (and it has non-trivial destruction), we treat the expression as having an unmodeled side-effect. llvm-svn: 372538
1 parent da4a470 commit 457226e

File tree

17 files changed

+741
-159
lines changed

17 files changed

+741
-159
lines changed

clang/include/clang/AST/DeclCXX.h

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,9 @@ class CXXRecordDecl : public RecordDecl {
445445
/// or an implicitly declared constexpr default constructor.
446446
unsigned HasConstexprDefaultConstructor : 1;
447447

448+
/// True if a defaulted destructor for this class would be constexpr.
449+
unsigned DefaultedDestructorIsConstexpr : 1;
450+
448451
/// True when this class contains at least one non-static data
449452
/// member or base class of non-literal or volatile type.
450453
unsigned HasNonLiteralTypeFieldsOrBases : 1;
@@ -1441,6 +1444,16 @@ class CXXRecordDecl : public RecordDecl {
14411444
!(data().HasTrivialSpecialMembers & SMF_MoveAssignment));
14421445
}
14431446

1447+
/// Determine whether a defaulted default constructor for this class
1448+
/// would be constexpr.
1449+
bool defaultedDestructorIsConstexpr() const {
1450+
return data().DefaultedDestructorIsConstexpr &&
1451+
getASTContext().getLangOpts().CPlusPlus2a;
1452+
}
1453+
1454+
/// Determine whether this class has a constexpr destructor.
1455+
bool hasConstexprDestructor() const;
1456+
14441457
/// Determine whether this class has a trivial destructor
14451458
/// (C++ [class.dtor]p3)
14461459
bool hasTrivialDestructor() const {
@@ -1532,8 +1545,10 @@ class CXXRecordDecl : public RecordDecl {
15321545
///
15331546
/// Only in C++17 and beyond, are lambdas literal types.
15341547
bool isLiteral() const {
1535-
return hasTrivialDestructor() &&
1536-
(!isLambda() || getASTContext().getLangOpts().CPlusPlus17) &&
1548+
ASTContext &Ctx = getASTContext();
1549+
return (Ctx.getLangOpts().CPlusPlus2a ? hasConstexprDestructor()
1550+
: hasTrivialDestructor()) &&
1551+
(!isLambda() || Ctx.getLangOpts().CPlusPlus17) &&
15371552
!hasNonLiteralTypeFieldsOrBases() &&
15381553
(isAggregate() || isLambda() ||
15391554
hasConstexprNonCopyMoveConstructor() ||
@@ -2802,9 +2817,9 @@ class CXXDestructorDecl : public CXXMethodDecl {
28022817
CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
28032818
const DeclarationNameInfo &NameInfo, QualType T,
28042819
TypeSourceInfo *TInfo, bool isInline,
2805-
bool isImplicitlyDeclared)
2820+
bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind)
28062821
: CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo,
2807-
SC_None, isInline, CSK_unspecified, SourceLocation()) {
2822+
SC_None, isInline, ConstexprKind, SourceLocation()) {
28082823
setImplicit(isImplicitlyDeclared);
28092824
}
28102825

@@ -2814,9 +2829,9 @@ class CXXDestructorDecl : public CXXMethodDecl {
28142829
static CXXDestructorDecl *Create(ASTContext &C, CXXRecordDecl *RD,
28152830
SourceLocation StartLoc,
28162831
const DeclarationNameInfo &NameInfo,
2817-
QualType T, TypeSourceInfo* TInfo,
2818-
bool isInline,
2819-
bool isImplicitlyDeclared);
2832+
QualType T, TypeSourceInfo *TInfo,
2833+
bool isInline, bool isImplicitlyDeclared,
2834+
ConstexprSpecKind ConstexprKind);
28202835
static CXXDestructorDecl *CreateDeserialized(ASTContext & C, unsigned ID);
28212836

28222837
void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg);

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ def note_constexpr_baa_insufficient_alignment : Note<
193193
def note_constexpr_baa_value_insufficient_alignment : Note<
194194
"value of the aligned pointer (%0) is not a multiple of the asserted %1 "
195195
"%plural{1:byte|:bytes}1">;
196+
def note_constexpr_unsupported_destruction : Note<
197+
"non-trivial destruction of type %0 in a constant expression is not supported">;
196198
def note_constexpr_unsupported_unsized_array : Note<
197199
"array-to-pointer decay of array member without known bound is not supported">;
198200
def note_constexpr_unsized_array_indexed : Note<

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2366,7 +2366,13 @@ def err_constexpr_tag : Error<
23662366
"%select{class|struct|interface|union|enum}0 "
23672367
"cannot be marked %sub{select_constexpr_spec_kind}1">;
23682368
def err_constexpr_dtor : Error<
2369-
"destructor cannot be marked %sub{select_constexpr_spec_kind}0">;
2369+
"destructor cannot be declared %sub{select_constexpr_spec_kind}0">;
2370+
def err_constexpr_dtor_subobject : Error<
2371+
"destructor cannot be declared %sub{select_constexpr_spec_kind}0 because "
2372+
"%select{data member %2|base class %3}1 does not have a "
2373+
"constexpr destructor">;
2374+
def note_constexpr_dtor_subobject : Note<
2375+
"%select{data member %1|base class %2}0 declared here">;
23702376
def err_constexpr_wrong_decl_kind : Error<
23712377
"%sub{select_constexpr_spec_kind}0 can only be used "
23722378
"in %select{|variable and function|function|variable}0 declarations">;
@@ -2507,6 +2513,8 @@ def note_non_literal_user_provided_dtor : Note<
25072513
"%0 is not literal because it has a user-provided destructor">;
25082514
def note_non_literal_nontrivial_dtor : Note<
25092515
"%0 is not literal because it has a non-trivial destructor">;
2516+
def note_non_literal_non_constexpr_dtor : Note<
2517+
"%0 is not literal because its destructor is not constexpr">;
25102518
def note_non_literal_lambda : Note<
25112519
"lambda closure types are non-literal types before C++17">;
25122520
def warn_private_extern : Warning<

clang/lib/AST/ASTImporter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3244,7 +3244,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
32443244
if (GetImportedOrCreateDecl<CXXDestructorDecl>(
32453245
ToFunction, D, Importer.getToContext(), cast<CXXRecordDecl>(DC),
32463246
ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(),
3247-
D->isImplicit()))
3247+
D->isImplicit(), D->getConstexprKind()))
32483248
return ToFunction;
32493249

32503250
CXXDestructorDecl *ToDtor = cast<CXXDestructorDecl>(ToFunction);

clang/lib/AST/DeclCXX.cpp

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
9595
HasDefaultedDefaultConstructor(false),
9696
DefaultedDefaultConstructorIsConstexpr(true),
9797
HasConstexprDefaultConstructor(false),
98+
DefaultedDestructorIsConstexpr(true),
9899
HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false),
99100
UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0),
100101
ImplicitCopyConstructorCanHaveConstParamForVBase(true),
@@ -325,10 +326,12 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
325326
data().IsStandardLayout = false;
326327
data().IsCXX11StandardLayout = false;
327328

328-
// C++11 [dcl.constexpr]p4:
329-
// In the definition of a constexpr constructor [...]
330-
// -- the class shall not have any virtual base classes
329+
// C++20 [dcl.constexpr]p3:
330+
// In the definition of a constexpr function [...]
331+
// -- if the function is a constructor or destructor,
332+
// its class shall not have any virtual base classes
331333
data().DefaultedDefaultConstructorIsConstexpr = false;
334+
data().DefaultedDestructorIsConstexpr = false;
332335

333336
// C++1z [class.copy]p8:
334337
// The implicitly-declared copy constructor for a class X will have
@@ -520,6 +523,19 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
520523
data().NeedOverloadResolutionForMoveConstructor = true;
521524
data().NeedOverloadResolutionForDestructor = true;
522525
}
526+
527+
// C++2a [dcl.constexpr]p4:
528+
// The definition of a constexpr destructor [shall] satisfy the
529+
// following requirement:
530+
// -- for every subobject of class type or (possibly multi-dimensional)
531+
// array thereof, that class type shall have a constexpr destructor
532+
if (!Subobj->hasConstexprDestructor())
533+
data().DefaultedDestructorIsConstexpr = false;
534+
}
535+
536+
bool CXXRecordDecl::hasConstexprDestructor() const {
537+
auto *Dtor = getDestructor();
538+
return Dtor ? Dtor->isConstexpr() : defaultedDestructorIsConstexpr();
523539
}
524540

525541
bool CXXRecordDecl::hasAnyDependentBases() const {
@@ -2571,20 +2587,19 @@ CXXDestructorDecl *
25712587
CXXDestructorDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
25722588
return new (C, ID)
25732589
CXXDestructorDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(),
2574-
QualType(), nullptr, false, false);
2590+
QualType(), nullptr, false, false, CSK_unspecified);
25752591
}
25762592

2577-
CXXDestructorDecl *
2578-
CXXDestructorDecl::Create(ASTContext &C, CXXRecordDecl *RD,
2579-
SourceLocation StartLoc,
2580-
const DeclarationNameInfo &NameInfo,
2581-
QualType T, TypeSourceInfo *TInfo,
2582-
bool isInline, bool isImplicitlyDeclared) {
2593+
CXXDestructorDecl *CXXDestructorDecl::Create(
2594+
ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
2595+
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
2596+
bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind) {
25832597
assert(NameInfo.getName().getNameKind()
25842598
== DeclarationName::CXXDestructorName &&
25852599
"Name must refer to a destructor");
2586-
return new (C, RD) CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo,
2587-
isInline, isImplicitlyDeclared);
2600+
return new (C, RD)
2601+
CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, isInline,
2602+
isImplicitlyDeclared, ConstexprKind);
25882603
}
25892604

25902605
void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) {

0 commit comments

Comments
 (0)