-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Revert "[Clang][C++23] Implement P2448R2 ..." #85136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…trictions (llvm#77753)" This reverts commit 99500e8 because it causes a behavior change for std=c++20. See llvm#77753.
@llvm/pr-subscribers-clang Author: Amy Huang (amykhuang) ChangesRevert "[Clang][C++23] Implement P2448R2: Relaxing some constexpr restrictions (#77753)" This reverts commit 99500e8 because it causes a behavior change for std=c++20. See Patch is 83.76 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/85136.diff 27 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5fe3fd066df235..e018d38355945f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -102,8 +102,6 @@ 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
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d7ab1635cf12bc..605fbc52701df0 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9617,10 +9617,13 @@ 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 cannot be marked %select{constexpr|consteval}1 "
- "before C++23">;
+ "defaulted definition of %sub{select_special_member_kind}0 "
+ "is not constexpr">;
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>;
@@ -9731,12 +9734,21 @@ 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 err_defaulted_comparison_constexpr_mismatch : Error<
+def ext_defaulted_comparison_constexpr_mismatch : Extension<
"defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|"
- "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 ">;
+ "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;
def note_defaulted_comparison_not_constexpr : Note<
"non-constexpr comparison function would be used to compare "
"%select{|member %1|base class %1}0">;
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 1c3dcf63465c68..b4f2327d9c560a 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -400,11 +400,10 @@ 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/function(C++23), the
- // implicitly-defined default constructor is constexpr.
+ // requirements of a constexpr constructor, the implicitly-defined
+ // default constructor is constexpr.
if (!BaseClassDecl->hasConstexprDefaultConstructor())
- data().DefaultedDefaultConstructorIsConstexpr =
- C.getLangOpts().CPlusPlus23;
+ data().DefaultedDefaultConstructorIsConstexpr = false;
// C++1z [class.copy]p8:
// The implicitly-declared copy constructor for a class X will have
@@ -549,8 +548,7 @@ 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 =
- getASTContext().getLangOpts().CPlusPlus23;
+ data().DefaultedDestructorIsConstexpr = false;
// C++20 [temp.param]p7:
// A structural type is [...] a literal class type [for which] the types
@@ -1299,8 +1297,7 @@ 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 =
- Context.getLangOpts().CPlusPlus23;
+ data().DefaultedDefaultConstructorIsConstexpr = false;
// C++11 [class.copy]p8:
// The implicitly-declared copy constructor for a class X will have
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index e258a4f7c89415..199f2523cfb5d2 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1715,8 +1715,6 @@ 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();
@@ -1748,8 +1746,6 @@ 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(),
@@ -1771,8 +1767,6 @@ 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()))
@@ -1862,18 +1856,16 @@ bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD,
}
}
- // - its return type shall be a literal type; (removed in C++23)
- if (!getLangOpts().CPlusPlus23 &&
- !CheckConstexprReturnType(*this, NewFD, Kind))
+ // - its return type shall be a literal type;
+ if (!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. (removed in C++23)
- if (!getLangOpts().CPlusPlus23 &&
- !Dtor->getParent()->defaultedDestructorIsConstexpr()) {
+ // have constexpr destructors.
+ if (!Dtor->getParent()->defaultedDestructorIsConstexpr()) {
if (Kind == CheckConstexprKind::CheckValid)
return false;
if (!CheckConstexprDestructorSubobjects(*this, Dtor, Kind))
@@ -1881,9 +1873,8 @@ bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD,
}
}
- // - each of its parameter types shall be a literal type; (removed in C++23)
- if (!getLangOpts().CPlusPlus23 &&
- !CheckConstexprParameterTypes(*this, NewFD, Kind))
+ // - each of its parameter types shall be a literal type;
+ if (!CheckConstexprParameterTypes(*this, NewFD, Kind))
return false;
Stmt *Body = NewFD->getBody();
@@ -2466,8 +2457,7 @@ 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) &&
- !SemaRef.getLangOpts().CPlusPlus23) {
+ !Expr::isPotentialConstantExpr(Dcl, Diags)) {
SemaRef.Diag(Dcl->getLocation(),
diag::ext_constexpr_function_never_constant_expr)
<< isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval()
@@ -7545,23 +7535,21 @@ static bool defaultedSpecialMemberIsConstexpr(
// C++1y [class.copy]p26:
// -- [the class] is a literal type, and
- if (!Ctor && !ClassDecl->isLiteral() && !S.getLangOpts().CPlusPlus23)
+ if (!Ctor && !ClassDecl->isLiteral())
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
- 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;
- }
+ 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
@@ -7571,22 +7559,20 @@ 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
- 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) {
+ 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;
}
}
@@ -7872,17 +7858,18 @@ 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(), diag::err_incorrect_defaulted_constexpr)
- << CSM << MD->isConsteval();
+ Diag(MD->getBeginLoc(), MD->isConsteval()
+ ? diag::err_incorrect_defaulted_consteval
+ : diag::err_incorrect_defaulted_constexpr)
+ << CSM;
}
- HadError = true;
- // FIXME: Explain why the special member can't be constexpr.
+ // FIXME: Explain why the special member can't be constexpr.
+ HadError = true;
}
if (First) {
@@ -9114,11 +9101,13 @@ 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 (!getLangOpts().CPlusPlus23 &&
- CheckConstexprReturnType(*this, FD, CheckConstexprKind::Diagnose) &&
+ if (CheckConstexprReturnType(*this, FD, CheckConstexprKind::Diagnose) &&
CheckConstexprParameterTypes(*this, FD, CheckConstexprKind::Diagnose) &&
!Info.Constexpr) {
- Diag(FD->getBeginLoc(), diag::err_defaulted_comparison_constexpr_mismatch)
+ Diag(FD->getBeginLoc(),
+ getLangOpts().CPlusPlus23
+ ? diag::warn_cxx23_compat_defaulted_comparison_constexpr_mismatch
+ : diag::ext_defaulted_comparison_constexpr_mismatch)
<< FD->isImplicit() << (int)DCK << FD->isConsteval();
DefaultedComparisonAnalyzer(*this, RD, FD, DCK,
DefaultedComparisonAnalyzer::ExplainConstexpr)
diff --git a/clang/test/AST/Interp/cxx23.cpp b/clang/test/AST/Interp/cxx23.cpp
index 127b58915127cf..f1df936a5abe74 100644
--- a/clang/test/AST/Interp/cxx23.cpp
+++ b/clang/test/AST/Interp/cxx23.cpp
@@ -1,58 +1,82 @@
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=ref20,all,all-20 %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=ref20,all %s
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=ref23,all %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20,all,all-20 %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20,all %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23,all %s -fexperimental-new-constant-interpreter
/// FIXME: The new interpreter is missing all the 'control flows through...' diagnostics.
constexpr int f(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // expected20-error {{constexpr function never produces a constant expression}}
+ // ref23-error {{constexpr function never produces a constant expression}} \
+ // expected20-error {{constexpr function never produces a constant expression}} \
+ // expected23-error {{constexpr function never produces a constant expression}}
static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \
// ref20-warning {{is a C++23 extension}} \
+ // ref23-note {{control flows through the definition of a static variable}} \
// expected20-warning {{is a C++23 extension}} \
// expected20-note {{declared here}} \
+ // expected23-note {{declared here}}
- return m; // expected20-note {{initializer of 'm' is not a constant expression}}
+ return m; // expected20-note {{initializer of 'm' is not a constant expression}} \
+ // expected23-note {{initializer of 'm' is not a constant expression}}
}
constexpr int g(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // expected20-error {{constexpr function never produces a constant expression}}
+ // ref23-error {{constexpr function never produces a constant expression}} \
+ // expected20-error {{constexpr function never produces a constant expression}} \
+ // expected23-error {{constexpr function never produces a constant expression}}
thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
+ // ref23-note {{control flows through the definition of a thread_local variable}} \
// expected20-warning {{is a C++23 extension}} \
- // expected20-note {{declared here}}
- return m; // expected20-note {{initializer of 'm' is not a constant expression}}
+ // expected20-note {{declared here}} \
+ // expected23-note {{declared here}}
+ return m; // expected20-note {{initializer of 'm' is not a constant expression}} \
+ // expected23-note {{initializer of 'm' is not a constant expression}}
}
constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // expected20-error {{constexpr function never produces a constant expression}}
+ // ref23-error {{constexpr function never produces a constant expression}} \
+ // expected20-error {{constexpr function never produces a constant expression}} \
+ // expected23-error {{constexpr function never produces a constant expression}}
static _Thread_local int m = 0; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
+ // ref23-note {{control flows through the definition of a thread_local variable}} \
// expected20-warning {{is a C++23 extension}} \
- // expected20-note {{declared here}}
- return m; // expected20-note {{read of non-const variable}}
+ // expected20-note {{declared here}} \
+ // expected23-note {{declared here}}
+ return m; // expected20-note {{read of non-const variable}} \
+ // expected23-note {{read of non-const variable}}
}
constexpr int gnu_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // expected20-error {{constexpr function never produces a constant expression}}
+ // ref23-error {{constexpr function never produces a constant expression}} \
+ // expected20-error {{constexpr function never produces a constant expression}} \
+ // expected23-error {{constexpr function never produces a cons...
[truncated]
|
You can test this locally with the following command:git-clang-format --diff ccfb9e6eb7429885e6d09e99cf89bce41f1ca3cc 2f67dfb012038678dd0b873394a55bd5c937f843 -- clang/lib/AST/DeclCXX.cpp clang/lib/Sema/SemaDeclCXX.cpp clang/test/AST/Interp/cxx23.cpp clang/test/CXX/class/class.compare/class.compare.default/p3.cpp clang/test/CXX/class/class.compare/class.compare.default/p4.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3-2b.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp clang/test/CXX/drs/dr13xx.cpp clang/test/CXX/drs/dr14xx.cpp clang/test/CXX/drs/dr15xx.cpp clang/test/CXX/drs/dr16xx.cpp clang/test/CXX/drs/dr6xx.cpp clang/test/CXX/expr/expr.const/p5-26.cpp clang/test/CXX/special/class.copy/p13-0x.cpp clang/test/SemaCXX/constant-expression-cxx11.cpp clang/test/SemaCXX/constant-expression-cxx14.cpp clang/test/SemaCXX/constant-expression-cxx2b.cpp clang/test/SemaCXX/cxx2a-consteval.cpp clang/test/SemaCXX/deduced-return-type-cxx14.cpp View the diff from clang-format here.diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 199f2523cf..b14699894b 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1743,8 +1743,7 @@ static bool CheckConstexprDestructorSubobjects(Sema &SemaRef,
/// Check whether a function's parameter types are all literal types. If so,
/// return true. If not, produce a suitable diagnostic and return false.
-static bool CheckConstexprParameterTypes(Sema &SemaRef,
- const FunctionDecl *FD,
+static bool CheckConstexprParameterTypes(Sema &SemaRef, const FunctionDecl *FD,
Sema::CheckConstexprKind Kind) {
unsigned ArgIndex = 0;
const auto *FT = FD->getType()->castAs<FunctionProtoType>();
@@ -7858,7 +7857,8 @@ 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);
@@ -7868,8 +7868,8 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
: diag::err_incorrect_defaulted_constexpr)
<< CSM;
}
- // FIXME: Explain why the special member can't be constexpr.
- HadError = true;
+ // FIXME: Explain why the special member can't be constexpr.
+ HadError = true;
}
if (First) {
|
This reverts commit 003e292.
Revert "[Clang][C++23] Implement P2448R2: Relaxing some constexpr restrictions (#77753)"
This reverts commit 99500e8 because it causes a behavior change for std=c++20. See
#77753.