Skip to content

Commit 99500e8

Browse files
authored
[Clang][C++23] Implement P2448R2: Relaxing some constexpr restrictions (#77753)
Per https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2448r2.html function/constructor/destructor can be marked `constexpr` even though it never produces a constant expression. Non-literal types as return types and parameter types of functions marked `constexpr` are also allowed. Since this is not a DR, the diagnostic messages are still preserved for C++ standards older than C++23.
1 parent 91808c8 commit 99500e8

File tree

27 files changed

+408
-269
lines changed

27 files changed

+408
-269
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ C++23 Feature Support
9797
- Implemented `P2718R0: Lifetime extension in range-based for loops <https://wg21.link/P2718R0>`_. Also
9898
materialize temporary object which is a prvalue in discarded-value expression.
9999

100+
- Implemented `P2448R2: Relaxing some constexpr restrictions <https://wg21.link/P2448R2>`_.
101+
100102
C++2c Feature Support
101103
^^^^^^^^^^^^^^^^^^^^^
102104

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9607,13 +9607,10 @@ def err_defaulted_copy_assign_not_ref : Error<
96079607
"the parameter for an explicitly-defaulted copy assignment operator must be an "
96089608
"lvalue reference type">;
96099609
def err_incorrect_defaulted_constexpr : Error<
9610-
"defaulted definition of %sub{select_special_member_kind}0 "
9611-
"is not constexpr">;
9610+
"defaulted definition of %sub{select_special_member_kind}0 cannot be marked %select{constexpr|consteval}1 "
9611+
"before C++23">;
96129612
def err_incorrect_defaulted_constexpr_with_vb: Error<
96139613
"%sub{select_special_member_kind}0 cannot be 'constexpr' in a class with virtual base class">;
9614-
def err_incorrect_defaulted_consteval : Error<
9615-
"defaulted declaration of %sub{select_special_member_kind}0 "
9616-
"cannot be consteval because implicit definition is not constexpr">;
96179614
def warn_defaulted_method_deleted : Warning<
96189615
"explicitly defaulted %sub{select_special_member_kind}0 is implicitly "
96199616
"deleted">, InGroup<DefaultedFunctionDeleted>;
@@ -9724,21 +9721,12 @@ def note_defaulted_comparison_cannot_deduce_undeduced_auto : Note<
97249721
"%select{|member|base class}0 %1 declared here">;
97259722
def note_defaulted_comparison_cannot_deduce_callee : Note<
97269723
"selected 'operator<=>' for %select{|member|base class}0 %1 declared here">;
9727-
def ext_defaulted_comparison_constexpr_mismatch : Extension<
9724+
def err_defaulted_comparison_constexpr_mismatch : Error<
97289725
"defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|"
9729-
"three-way comparison operator}0 that is "
9730-
"declared %select{constexpr|consteval}2 but"
9731-
"%select{|for which the corresponding implicit 'operator==' }0 "
9732-
"invokes a non-constexpr comparison function is a C++23 extension">,
9733-
InGroup<DiagGroup<"c++23-default-comp-relaxed-constexpr">>;
9734-
def warn_cxx23_compat_defaulted_comparison_constexpr_mismatch : Warning<
9735-
"defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|"
9736-
"three-way comparison operator}0 that is "
9737-
"declared %select{constexpr|consteval}2 but"
9738-
"%select{|for which the corresponding implicit 'operator==' }0 "
9739-
"invokes a non-constexpr comparison function is incompatible with C++ "
9740-
"standards before C++23">,
9741-
InGroup<CXXPre23Compat>, DefaultIgnore;
9726+
"three-way comparison operator}0 cannot be "
9727+
"declared %select{constexpr|consteval}2 because "
9728+
"%select{it|for which the corresponding implicit 'operator==' }0 "
9729+
"invokes a non-constexpr comparison function ">;
97429730
def note_defaulted_comparison_not_constexpr : Note<
97439731
"non-constexpr comparison function would be used to compare "
97449732
"%select{|member %1|base class %1}0">;

clang/lib/AST/DeclCXX.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -400,10 +400,11 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
400400

401401
// C++11 [class.ctor]p6:
402402
// If that user-written default constructor would satisfy the
403-
// requirements of a constexpr constructor, the implicitly-defined
404-
// default constructor is constexpr.
403+
// requirements of a constexpr constructor/function(C++23), the
404+
// implicitly-defined default constructor is constexpr.
405405
if (!BaseClassDecl->hasConstexprDefaultConstructor())
406-
data().DefaultedDefaultConstructorIsConstexpr = false;
406+
data().DefaultedDefaultConstructorIsConstexpr =
407+
C.getLangOpts().CPlusPlus23;
407408

408409
// C++1z [class.copy]p8:
409410
// The implicitly-declared copy constructor for a class X will have
@@ -548,7 +549,8 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
548549
// -- for every subobject of class type or (possibly multi-dimensional)
549550
// array thereof, that class type shall have a constexpr destructor
550551
if (!Subobj->hasConstexprDestructor())
551-
data().DefaultedDestructorIsConstexpr = false;
552+
data().DefaultedDestructorIsConstexpr =
553+
getASTContext().getLangOpts().CPlusPlus23;
552554

553555
// C++20 [temp.param]p7:
554556
// A structural type is [...] a literal class type [for which] the types
@@ -1297,7 +1299,8 @@ void CXXRecordDecl::addedMember(Decl *D) {
12971299
!FieldRec->hasConstexprDefaultConstructor() && !isUnion())
12981300
// The standard requires any in-class initializer to be a constant
12991301
// expression. We consider this to be a defect.
1300-
data().DefaultedDefaultConstructorIsConstexpr = false;
1302+
data().DefaultedDefaultConstructorIsConstexpr =
1303+
Context.getLangOpts().CPlusPlus23;
13011304

13021305
// C++11 [class.copy]p8:
13031306
// The implicitly-declared copy constructor for a class X will have

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,6 +1715,8 @@ static bool CheckLiteralType(Sema &SemaRef, Sema::CheckConstexprKind Kind,
17151715
static bool CheckConstexprDestructorSubobjects(Sema &SemaRef,
17161716
const CXXDestructorDecl *DD,
17171717
Sema::CheckConstexprKind Kind) {
1718+
assert(!SemaRef.getLangOpts().CPlusPlus23 &&
1719+
"this check is obsolete for C++23");
17181720
auto Check = [&](SourceLocation Loc, QualType T, const FieldDecl *FD) {
17191721
const CXXRecordDecl *RD =
17201722
T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
@@ -1746,6 +1748,8 @@ static bool CheckConstexprDestructorSubobjects(Sema &SemaRef,
17461748
static bool CheckConstexprParameterTypes(Sema &SemaRef,
17471749
const FunctionDecl *FD,
17481750
Sema::CheckConstexprKind Kind) {
1751+
assert(!SemaRef.getLangOpts().CPlusPlus23 &&
1752+
"this check is obsolete for C++23");
17491753
unsigned ArgIndex = 0;
17501754
const auto *FT = FD->getType()->castAs<FunctionProtoType>();
17511755
for (FunctionProtoType::param_type_iterator i = FT->param_type_begin(),
@@ -1767,6 +1771,8 @@ static bool CheckConstexprParameterTypes(Sema &SemaRef,
17671771
/// true. If not, produce a suitable diagnostic and return false.
17681772
static bool CheckConstexprReturnType(Sema &SemaRef, const FunctionDecl *FD,
17691773
Sema::CheckConstexprKind Kind) {
1774+
assert(!SemaRef.getLangOpts().CPlusPlus23 &&
1775+
"this check is obsolete for C++23");
17701776
if (CheckLiteralType(SemaRef, Kind, FD->getLocation(), FD->getReturnType(),
17711777
diag::err_constexpr_non_literal_return,
17721778
FD->isConsteval()))
@@ -1856,25 +1862,28 @@ bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD,
18561862
}
18571863
}
18581864

1859-
// - its return type shall be a literal type;
1860-
if (!CheckConstexprReturnType(*this, NewFD, Kind))
1865+
// - its return type shall be a literal type; (removed in C++23)
1866+
if (!getLangOpts().CPlusPlus23 &&
1867+
!CheckConstexprReturnType(*this, NewFD, Kind))
18611868
return false;
18621869
}
18631870

18641871
if (auto *Dtor = dyn_cast<CXXDestructorDecl>(NewFD)) {
18651872
// A destructor can be constexpr only if the defaulted destructor could be;
18661873
// we don't need to check the members and bases if we already know they all
1867-
// have constexpr destructors.
1868-
if (!Dtor->getParent()->defaultedDestructorIsConstexpr()) {
1874+
// have constexpr destructors. (removed in C++23)
1875+
if (!getLangOpts().CPlusPlus23 &&
1876+
!Dtor->getParent()->defaultedDestructorIsConstexpr()) {
18691877
if (Kind == CheckConstexprKind::CheckValid)
18701878
return false;
18711879
if (!CheckConstexprDestructorSubobjects(*this, Dtor, Kind))
18721880
return false;
18731881
}
18741882
}
18751883

1876-
// - each of its parameter types shall be a literal type;
1877-
if (!CheckConstexprParameterTypes(*this, NewFD, Kind))
1884+
// - each of its parameter types shall be a literal type; (removed in C++23)
1885+
if (!getLangOpts().CPlusPlus23 &&
1886+
!CheckConstexprParameterTypes(*this, NewFD, Kind))
18781887
return false;
18791888

18801889
Stmt *Body = NewFD->getBody();
@@ -2457,7 +2466,8 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
24572466
// function", so is not checked in CheckValid mode.
24582467
SmallVector<PartialDiagnosticAt, 8> Diags;
24592468
if (Kind == Sema::CheckConstexprKind::Diagnose &&
2460-
!Expr::isPotentialConstantExpr(Dcl, Diags)) {
2469+
!Expr::isPotentialConstantExpr(Dcl, Diags) &&
2470+
!SemaRef.getLangOpts().CPlusPlus23) {
24612471
SemaRef.Diag(Dcl->getLocation(),
24622472
diag::ext_constexpr_function_never_constant_expr)
24632473
<< isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval()
@@ -7535,21 +7545,23 @@ static bool defaultedSpecialMemberIsConstexpr(
75357545

75367546
// C++1y [class.copy]p26:
75377547
// -- [the class] is a literal type, and
7538-
if (!Ctor && !ClassDecl->isLiteral())
7548+
if (!Ctor && !ClassDecl->isLiteral() && !S.getLangOpts().CPlusPlus23)
75397549
return false;
75407550

75417551
// -- every constructor involved in initializing [...] base class
75427552
// sub-objects shall be a constexpr constructor;
75437553
// -- the assignment operator selected to copy/move each direct base
75447554
// class is a constexpr function, and
7545-
for (const auto &B : ClassDecl->bases()) {
7546-
const RecordType *BaseType = B.getType()->getAs<RecordType>();
7547-
if (!BaseType)
7548-
continue;
7549-
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
7550-
if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, 0, ConstArg,
7551-
InheritedCtor, Inherited))
7552-
return false;
7555+
if (!S.getLangOpts().CPlusPlus23) {
7556+
for (const auto &B : ClassDecl->bases()) {
7557+
const RecordType *BaseType = B.getType()->getAs<RecordType>();
7558+
if (!BaseType)
7559+
continue;
7560+
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
7561+
if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, 0, ConstArg,
7562+
InheritedCtor, Inherited))
7563+
return false;
7564+
}
75537565
}
75547566

75557567
// -- every constructor involved in initializing non-static data members
@@ -7559,20 +7571,22 @@ static bool defaultedSpecialMemberIsConstexpr(
75597571
// -- for each non-static data member of X that is of class type (or array
75607572
// thereof), the assignment operator selected to copy/move that member is
75617573
// a constexpr function
7562-
for (const auto *F : ClassDecl->fields()) {
7563-
if (F->isInvalidDecl())
7564-
continue;
7565-
if (CSM == Sema::CXXDefaultConstructor && F->hasInClassInitializer())
7566-
continue;
7567-
QualType BaseType = S.Context.getBaseElementType(F->getType());
7568-
if (const RecordType *RecordTy = BaseType->getAs<RecordType>()) {
7569-
CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
7570-
if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM,
7571-
BaseType.getCVRQualifiers(),
7572-
ConstArg && !F->isMutable()))
7574+
if (!S.getLangOpts().CPlusPlus23) {
7575+
for (const auto *F : ClassDecl->fields()) {
7576+
if (F->isInvalidDecl())
7577+
continue;
7578+
if (CSM == Sema::CXXDefaultConstructor && F->hasInClassInitializer())
7579+
continue;
7580+
QualType BaseType = S.Context.getBaseElementType(F->getType());
7581+
if (const RecordType *RecordTy = BaseType->getAs<RecordType>()) {
7582+
CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
7583+
if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM,
7584+
BaseType.getCVRQualifiers(),
7585+
ConstArg && !F->isMutable()))
7586+
return false;
7587+
} else if (CSM == Sema::CXXDefaultConstructor) {
75737588
return false;
7574-
} else if (CSM == Sema::CXXDefaultConstructor) {
7575-
return false;
7589+
}
75767590
}
75777591
}
75787592

@@ -7858,18 +7872,17 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
78587872
MD->isConstexpr() && !Constexpr &&
78597873
MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
78607874
if (!MD->isConsteval() && RD->getNumVBases()) {
7861-
Diag(MD->getBeginLoc(), diag::err_incorrect_defaulted_constexpr_with_vb)
7875+
Diag(MD->getBeginLoc(),
7876+
diag::err_incorrect_defaulted_constexpr_with_vb)
78627877
<< CSM;
78637878
for (const auto &I : RD->vbases())
78647879
Diag(I.getBeginLoc(), diag::note_constexpr_virtual_base_here);
78657880
} else {
7866-
Diag(MD->getBeginLoc(), MD->isConsteval()
7867-
? diag::err_incorrect_defaulted_consteval
7868-
: diag::err_incorrect_defaulted_constexpr)
7869-
<< CSM;
7881+
Diag(MD->getBeginLoc(), diag::err_incorrect_defaulted_constexpr)
7882+
<< CSM << MD->isConsteval();
78707883
}
7871-
// FIXME: Explain why the special member can't be constexpr.
7872-
HadError = true;
7884+
HadError = true;
7885+
// FIXME: Explain why the special member can't be constexpr.
78737886
}
78747887

78757888
if (First) {
@@ -9101,13 +9114,11 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
91019114
// - if the function is a constructor or destructor, its class does not
91029115
// have any virtual base classes.
91039116
if (FD->isConstexpr()) {
9104-
if (CheckConstexprReturnType(*this, FD, CheckConstexprKind::Diagnose) &&
9117+
if (!getLangOpts().CPlusPlus23 &&
9118+
CheckConstexprReturnType(*this, FD, CheckConstexprKind::Diagnose) &&
91059119
CheckConstexprParameterTypes(*this, FD, CheckConstexprKind::Diagnose) &&
91069120
!Info.Constexpr) {
9107-
Diag(FD->getBeginLoc(),
9108-
getLangOpts().CPlusPlus23
9109-
? diag::warn_cxx23_compat_defaulted_comparison_constexpr_mismatch
9110-
: diag::ext_defaulted_comparison_constexpr_mismatch)
9121+
Diag(FD->getBeginLoc(), diag::err_defaulted_comparison_constexpr_mismatch)
91119122
<< FD->isImplicit() << (int)DCK << FD->isConsteval();
91129123
DefaultedComparisonAnalyzer(*this, RD, FD, DCK,
91139124
DefaultedComparisonAnalyzer::ExplainConstexpr)

0 commit comments

Comments
 (0)