Skip to content

Commit

Permalink
Reapply "[Clang][C++23] Implement P2448R2 ..." (#85136) (#85145)
Browse files Browse the repository at this point in the history
This reverts commit 003e292 because
there were dependent changes in the codebase that now fail.
  • Loading branch information
amykhuang committed Mar 13, 2024
1 parent 7009c98 commit f9a1478
Show file tree
Hide file tree
Showing 27 changed files with 408 additions and 269 deletions.
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ C++23 Feature Support
materialize temporary object which is a prvalue in discarded-value expression.
- Implemented `P1774R8: Portable assumptions <https://wg21.link/P1774R8>`_.

- Implemented `P2448R2: Relaxing some constexpr restrictions <https://wg21.link/P2448R2>`_.

C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^

Expand Down
26 changes: 7 additions & 19 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -9617,13 +9617,10 @@ def err_defaulted_copy_assign_not_ref : Error<
"the parameter for an explicitly-defaulted copy assignment operator must be an "
"lvalue reference type">;
def err_incorrect_defaulted_constexpr : Error<
"defaulted definition of %sub{select_special_member_kind}0 "
"is not constexpr">;
"defaulted definition of %sub{select_special_member_kind}0 cannot be marked %select{constexpr|consteval}1 "
"before C++23">;
def err_incorrect_defaulted_constexpr_with_vb: Error<
"%sub{select_special_member_kind}0 cannot be 'constexpr' in a class with virtual base class">;
def err_incorrect_defaulted_consteval : Error<
"defaulted declaration of %sub{select_special_member_kind}0 "
"cannot be consteval because implicit definition is not constexpr">;
def warn_defaulted_method_deleted : Warning<
"explicitly defaulted %sub{select_special_member_kind}0 is implicitly "
"deleted">, InGroup<DefaultedFunctionDeleted>;
Expand Down Expand Up @@ -9734,21 +9731,12 @@ def note_defaulted_comparison_cannot_deduce_undeduced_auto : Note<
"%select{|member|base class}0 %1 declared here">;
def note_defaulted_comparison_cannot_deduce_callee : Note<
"selected 'operator<=>' for %select{|member|base class}0 %1 declared here">;
def ext_defaulted_comparison_constexpr_mismatch : Extension<
def err_defaulted_comparison_constexpr_mismatch : Error<
"defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|"
"three-way comparison operator}0 that is "
"declared %select{constexpr|consteval}2 but"
"%select{|for which the corresponding implicit 'operator==' }0 "
"invokes a non-constexpr comparison function is a C++23 extension">,
InGroup<DiagGroup<"c++23-default-comp-relaxed-constexpr">>;
def warn_cxx23_compat_defaulted_comparison_constexpr_mismatch : Warning<
"defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|"
"three-way comparison operator}0 that is "
"declared %select{constexpr|consteval}2 but"
"%select{|for which the corresponding implicit 'operator==' }0 "
"invokes a non-constexpr comparison function is incompatible with C++ "
"standards before C++23">,
InGroup<CXXPre23Compat>, DefaultIgnore;
"three-way comparison operator}0 cannot be "
"declared %select{constexpr|consteval}2 because "
"%select{it|for which the corresponding implicit 'operator==' }0 "
"invokes a non-constexpr comparison function ">;
def note_defaulted_comparison_not_constexpr : Note<
"non-constexpr comparison function would be used to compare "
"%select{|member %1|base class %1}0">;
Expand Down
13 changes: 8 additions & 5 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,10 +400,11 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,

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

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

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

// C++11 [class.copy]p8:
// The implicitly-declared copy constructor for a class X will have
Expand Down
93 changes: 52 additions & 41 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1715,6 +1715,8 @@ static bool CheckLiteralType(Sema &SemaRef, Sema::CheckConstexprKind Kind,
static bool CheckConstexprDestructorSubobjects(Sema &SemaRef,
const CXXDestructorDecl *DD,
Sema::CheckConstexprKind Kind) {
assert(!SemaRef.getLangOpts().CPlusPlus23 &&
"this check is obsolete for C++23");
auto Check = [&](SourceLocation Loc, QualType T, const FieldDecl *FD) {
const CXXRecordDecl *RD =
T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
Expand Down Expand Up @@ -1746,6 +1748,8 @@ static bool CheckConstexprDestructorSubobjects(Sema &SemaRef,
static bool CheckConstexprParameterTypes(Sema &SemaRef,
const FunctionDecl *FD,
Sema::CheckConstexprKind Kind) {
assert(!SemaRef.getLangOpts().CPlusPlus23 &&
"this check is obsolete for C++23");
unsigned ArgIndex = 0;
const auto *FT = FD->getType()->castAs<FunctionProtoType>();
for (FunctionProtoType::param_type_iterator i = FT->param_type_begin(),
Expand All @@ -1767,6 +1771,8 @@ static bool CheckConstexprParameterTypes(Sema &SemaRef,
/// true. If not, produce a suitable diagnostic and return false.
static bool CheckConstexprReturnType(Sema &SemaRef, const FunctionDecl *FD,
Sema::CheckConstexprKind Kind) {
assert(!SemaRef.getLangOpts().CPlusPlus23 &&
"this check is obsolete for C++23");
if (CheckLiteralType(SemaRef, Kind, FD->getLocation(), FD->getReturnType(),
diag::err_constexpr_non_literal_return,
FD->isConsteval()))
Expand Down Expand Up @@ -1856,25 +1862,28 @@ bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD,
}
}

// - its return type shall be a literal type;
if (!CheckConstexprReturnType(*this, NewFD, Kind))
// - its return type shall be a literal type; (removed in C++23)
if (!getLangOpts().CPlusPlus23 &&
!CheckConstexprReturnType(*this, NewFD, Kind))
return false;
}

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

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

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

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

// -- every constructor involved in initializing [...] base class
// sub-objects shall be a constexpr constructor;
// -- the assignment operator selected to copy/move each direct base
// class is a constexpr function, and
for (const auto &B : ClassDecl->bases()) {
const RecordType *BaseType = B.getType()->getAs<RecordType>();
if (!BaseType)
continue;
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, 0, ConstArg,
InheritedCtor, Inherited))
return false;
if (!S.getLangOpts().CPlusPlus23) {
for (const auto &B : ClassDecl->bases()) {
const RecordType *BaseType = B.getType()->getAs<RecordType>();
if (!BaseType)
continue;
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, 0, ConstArg,
InheritedCtor, Inherited))
return false;
}
}

// -- every constructor involved in initializing non-static data members
Expand All @@ -7559,20 +7571,22 @@ static bool defaultedSpecialMemberIsConstexpr(
// -- for each non-static data member of X that is of class type (or array
// thereof), the assignment operator selected to copy/move that member is
// a constexpr function
for (const auto *F : ClassDecl->fields()) {
if (F->isInvalidDecl())
continue;
if (CSM == Sema::CXXDefaultConstructor && F->hasInClassInitializer())
continue;
QualType BaseType = S.Context.getBaseElementType(F->getType());
if (const RecordType *RecordTy = BaseType->getAs<RecordType>()) {
CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM,
BaseType.getCVRQualifiers(),
ConstArg && !F->isMutable()))
if (!S.getLangOpts().CPlusPlus23) {
for (const auto *F : ClassDecl->fields()) {
if (F->isInvalidDecl())
continue;
if (CSM == Sema::CXXDefaultConstructor && F->hasInClassInitializer())
continue;
QualType BaseType = S.Context.getBaseElementType(F->getType());
if (const RecordType *RecordTy = BaseType->getAs<RecordType>()) {
CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM,
BaseType.getCVRQualifiers(),
ConstArg && !F->isMutable()))
return false;
} else if (CSM == Sema::CXXDefaultConstructor) {
return false;
} else if (CSM == Sema::CXXDefaultConstructor) {
return false;
}
}
}

Expand Down Expand Up @@ -7858,18 +7872,17 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
MD->isConstexpr() && !Constexpr &&
MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
if (!MD->isConsteval() && RD->getNumVBases()) {
Diag(MD->getBeginLoc(), diag::err_incorrect_defaulted_constexpr_with_vb)
Diag(MD->getBeginLoc(),
diag::err_incorrect_defaulted_constexpr_with_vb)
<< CSM;
for (const auto &I : RD->vbases())
Diag(I.getBeginLoc(), diag::note_constexpr_virtual_base_here);
} else {
Diag(MD->getBeginLoc(), MD->isConsteval()
? diag::err_incorrect_defaulted_consteval
: diag::err_incorrect_defaulted_constexpr)
<< CSM;
Diag(MD->getBeginLoc(), diag::err_incorrect_defaulted_constexpr)
<< CSM << MD->isConsteval();
}
// FIXME: Explain why the special member can't be constexpr.
HadError = true;
HadError = true;
// FIXME: Explain why the special member can't be constexpr.
}

if (First) {
Expand Down Expand Up @@ -9101,13 +9114,11 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
// - if the function is a constructor or destructor, its class does not
// have any virtual base classes.
if (FD->isConstexpr()) {
if (CheckConstexprReturnType(*this, FD, CheckConstexprKind::Diagnose) &&
if (!getLangOpts().CPlusPlus23 &&
CheckConstexprReturnType(*this, FD, CheckConstexprKind::Diagnose) &&
CheckConstexprParameterTypes(*this, FD, CheckConstexprKind::Diagnose) &&
!Info.Constexpr) {
Diag(FD->getBeginLoc(),
getLangOpts().CPlusPlus23
? diag::warn_cxx23_compat_defaulted_comparison_constexpr_mismatch
: diag::ext_defaulted_comparison_constexpr_mismatch)
Diag(FD->getBeginLoc(), diag::err_defaulted_comparison_constexpr_mismatch)
<< FD->isImplicit() << (int)DCK << FD->isConsteval();
DefaultedComparisonAnalyzer(*this, RD, FD, DCK,
DefaultedComparisonAnalyzer::ExplainConstexpr)
Expand Down

0 comments on commit f9a1478

Please sign in to comment.