Skip to content
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

[Clang][C++23] Implement P2448R2: Relaxing some constexpr restrictions #77753

Merged
merged 20 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,7 @@ Conditional ``explicit`` __cpp_conditional_explicit C++20
``if consteval`` __cpp_if_consteval C++23 C++20
``static operator()`` __cpp_static_call_operator C++23 C++03
Attributes on Lambda-Expressions C++23 C++11
Relaxing some constexpr restrictions __cpp_constexpr C++23 C++11
-------------------------------------- -------------------------------- ------------- -------------
Designated initializers (N494) C99 C89
Array & element qualification (N2607) C23 C89
Expand Down
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ C++23 Feature Support
- Added a separate warning to warn the use of attributes on lambdas as a C++23 extension
in previous language versions: ``-Wc++23-lambda-attributes``.

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

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

Expand Down
42 changes: 26 additions & 16 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2765,10 +2765,14 @@ def err_constexpr_tag : Error<
"cannot be marked %sub{select_constexpr_spec_kind}1">;
def err_constexpr_dtor : Error<
"destructor cannot be declared %sub{select_constexpr_spec_kind}0">;
def err_constexpr_dtor_subobject : Error<
"destructor cannot be declared %sub{select_constexpr_spec_kind}0 because "
def ext_constexpr_dtor_subobject : ExtWarn<
"destructor cannot be declared %sub{select_constexpr_spec_kind}0 before C++23 because "
"%select{data member %2|base class %3}1 does not have a "
"constexpr destructor">;
"constexpr destructor">, InGroup<CXX23>, DefaultError;
def warn_cxx23_compat_constexpr_dtor_subobject : ExtWarn<
Fznamznon marked this conversation as resolved.
Show resolved Hide resolved
"%sub{select_constexpr_spec_kind}0 destructor is incompatible with C++ standards before C++23 because "
"%select{data member %2|base class %3}1 does not have a "
"constexpr destructor">, InGroup<CXXPre23Compat>, DefaultIgnore;
def note_constexpr_dtor_subobject : Note<
"%select{data member %1|base class %2}0 declared here">;
def err_constexpr_wrong_decl_kind : Error<
Expand Down Expand Up @@ -2800,11 +2804,14 @@ def note_non_literal_incomplete : Note<
def note_non_literal_virtual_base : Note<"%select{struct|interface|class}0 "
"with virtual base %plural{1:class|:classes}1 is not a literal type">;
def note_constexpr_virtual_base_here : Note<"virtual base class declared here">;
def err_constexpr_non_literal_return : Error<
"%select{constexpr|consteval}0 function's return type %1 is not a literal type">;
def err_constexpr_non_literal_param : Error<
"%select{constexpr|consteval}2 %select{function|constructor}1's %ordinal0 parameter type %3 is "
"not a literal type">;
def ext_constexpr_non_literal_return : ExtWarn<
"%select{constexpr|consteval}0 function with non-literal return type %1 is a C++23 extension">, InGroup<CXX23>, DefaultError;
def warn_cxx23_compat_constexpr_non_literal_return : Warning<
"%select{constexpr|consteval}0 function with non-literal return type %1 is incompatible with C++ standards before C++23">, InGroup<CXXPre23Compat>, DefaultIgnore;
def ext_constexpr_non_literal_param : ExtWarn<
"%select{constexpr|consteval}2 %select{function|constructor}1 with %ordinal0 non-literal parameter type %3 is a C++23 extension">, InGroup<CXX23>, DefaultError;
def warn_cxx23_compat_constexpr_non_literal_param : Warning<
"%select{constexpr|consteval}2 %select{function|constructor}1 with %ordinal0 non-literal parameter type %3 is not compatible with C++ standards before C++23">, InGroup<CXXPre23Compat>, DefaultIgnore;
def err_constexpr_body_invalid_stmt : Error<
"statement not allowed in %select{constexpr|consteval}1 %select{function|constructor}0">;
def ext_constexpr_body_invalid_stmt : ExtWarn<
Expand Down Expand Up @@ -2865,8 +2872,11 @@ def warn_cxx17_compat_constexpr_local_var_no_init : Warning<
"is incompatible with C++ standards before C++20">,
InGroup<CXXPre20Compat>, DefaultIgnore;
def ext_constexpr_function_never_constant_expr : ExtWarn<
"%select{constexpr|consteval}1 %select{function|constructor}0 never produces a "
"constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
"%select{constexpr|consteval}1 %select{function|constructor}0 that never produces a "
"constant expression is a C++23 extension">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
def warn_cxx23_compat_constexpr_function_never_constant_expr : Warning<
"%select{constexpr|consteval}1 %select{function|constructor}0 that never produces a "
"constant expression is incompatible with C++ standards before C++23">, InGroup<CXXPre23Compat>, DefaultIgnore;
def err_attr_cond_never_constant_expr : Error<
"%0 attribute expression never produces a constant expression">;
def err_diagnose_if_invalid_diagnostic_type : Error<
Expand Down Expand Up @@ -9539,14 +9549,14 @@ def err_defaulted_special_member_copy_const_param : Error<
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">;
def ext_incorrect_defaulted_constexpr : ExtWarn<
"defaulted definition of %sub{select_special_member_kind}0 that marked %select{constexpr|consteval}1 "
"but never produces a constant expression is a C++23 extension">, InGroup<CXX23>, DefaultError;
def warn_cxx23_compat_incorrect_defaulted_constexpr : Warning<
"defaulted definition of %sub{select_special_member_kind}0 that marked %select{constexpr|consteval}1 "
"but never produces a constant expression is incompatible with C++ standards before C++23">, InGroup<CXXPre23Compat>, DefaultIgnore;
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
49 changes: 31 additions & 18 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1722,13 +1722,16 @@ static bool CheckConstexprDestructorSubobjects(Sema &SemaRef,
return true;

if (Kind == Sema::CheckConstexprKind::Diagnose) {
SemaRef.Diag(DD->getLocation(), diag::err_constexpr_dtor_subobject)
SemaRef.Diag(DD->getLocation(),
SemaRef.getLangOpts().CPlusPlus23
? diag::warn_cxx23_compat_constexpr_dtor_subobject
: diag::ext_constexpr_dtor_subobject)
<< static_cast<int>(DD->getConstexprKind()) << !FD
<< (FD ? FD->getDeclName() : DeclarationName()) << T;
SemaRef.Diag(Loc, diag::note_constexpr_dtor_subobject)
<< !FD << (FD ? FD->getDeclName() : DeclarationName()) << T;
}
return false;
return !!SemaRef.getLangOpts().CPlusPlus23;
Fznamznon marked this conversation as resolved.
Show resolved Hide resolved
};

const CXXRecordDecl *RD = DD->getParent();
Expand All @@ -1754,11 +1757,14 @@ static bool CheckConstexprParameterTypes(Sema &SemaRef,
const ParmVarDecl *PD = FD->getParamDecl(ArgIndex);
assert(PD && "null in a parameter list");
SourceLocation ParamLoc = PD->getLocation();
if (CheckLiteralType(SemaRef, Kind, ParamLoc, *i,
diag::err_constexpr_non_literal_param, ArgIndex + 1,
PD->getSourceRange(), isa<CXXConstructorDecl>(FD),
FD->isConsteval()))
return false;
if (CheckLiteralType(
SemaRef, Kind, ParamLoc, *i,
SemaRef.getLangOpts().CPlusPlus23
? diag::warn_cxx23_compat_constexpr_non_literal_param
: diag::ext_constexpr_non_literal_param,
ArgIndex + 1, PD->getSourceRange(), isa<CXXConstructorDecl>(FD),
FD->isConsteval()))
return SemaRef.getLangOpts().CPlusPlus23;
}
return true;
}
Expand All @@ -1767,10 +1773,13 @@ 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) {
if (CheckLiteralType(SemaRef, Kind, FD->getLocation(), FD->getReturnType(),
diag::err_constexpr_non_literal_return,
FD->isConsteval()))
return false;
if (CheckLiteralType(
SemaRef, Kind, FD->getLocation(), FD->getReturnType(),
SemaRef.getLangOpts().CPlusPlus23
? diag::warn_cxx23_compat_constexpr_non_literal_return
: diag::ext_constexpr_non_literal_return,
FD->isConsteval()))
return SemaRef.getLangOpts().CPlusPlus23;
return true;
}

Expand Down Expand Up @@ -2458,8 +2467,11 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
SmallVector<PartialDiagnosticAt, 8> Diags;
if (Kind == Sema::CheckConstexprKind::Diagnose &&
!Expr::isPotentialConstantExpr(Dcl, Diags)) {
SemaRef.Diag(Dcl->getLocation(),
diag::ext_constexpr_function_never_constant_expr)
SemaRef.Diag(
Dcl->getLocation(),
SemaRef.getLangOpts().CPlusPlus23
? diag::warn_cxx23_compat_constexpr_function_never_constant_expr
: diag::ext_constexpr_function_never_constant_expr)
<< isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval()
<< Dcl->getNameInfo().getSourceRange();
for (size_t I = 0, N = Diags.size(); I != N; ++I)
Expand Down Expand Up @@ -7851,14 +7863,15 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
<< CSM;
for (const auto &I : RD->vbases())
Diag(I.getBeginLoc(), diag::note_constexpr_virtual_base_here);
HadError = true;
} else {
Diag(MD->getBeginLoc(), MD->isConsteval()
? diag::err_incorrect_defaulted_consteval
: diag::err_incorrect_defaulted_constexpr)
<< CSM;
Diag(MD->getBeginLoc(),
getLangOpts().CPlusPlus23
? diag::warn_cxx23_compat_incorrect_defaulted_constexpr
: diag::ext_incorrect_defaulted_constexpr)
<< CSM << MD->isConsteval();
}
// FIXME: Explain why the special member can't be constexpr.
HadError = true;
}

if (First) {
Expand Down
24 changes: 6 additions & 18 deletions clang/test/AST/Interp/cxx23.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,45 @@

/// 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}} \
// ref23-error {{constexpr function never produces a constant expression}}
constexpr int f(int n) { // ref20-error {{constexpr function that 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}}

return m;
}
constexpr int g(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}}
constexpr int g(int n) { // ref20-error {{constexpr function that 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}}
return m;
}

constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}}
constexpr int c_thread_local(int n) { // ref20-error {{constexpr function that 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}}
return m;
}


constexpr int gnu_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}}
constexpr int gnu_thread_local(int n) { // ref20-error {{constexpr function that never produces a constant expression}}
static __thread 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}}
return m;
}

constexpr int h(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}}
constexpr int h(int n) { // ref20-error {{constexpr function that 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}}
return &m - &m;
}

constexpr int i(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
// ref23-error {{constexpr function never produces a constant expression}}
constexpr int i(int n) { // ref20-error {{constexpr function that 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}}
return &m - &m;
}
Expand Down
2 changes: 1 addition & 1 deletion clang/test/AST/Interp/literals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ namespace SizeOf {

#if __cplusplus >= 202002L
/// FIXME: The following code should be accepted.
consteval int foo(int n) { // ref-error {{consteval function never produces a constant expression}}
consteval int foo(int n) { // ref-error {{consteval function that never produces a constant expression}}
return sizeof(int[n]); // ref-note 3{{not valid in a constant expression}}
}
constinit int var = foo(5); // ref-error {{not a constant expression}} \
Expand Down
8 changes: 4 additions & 4 deletions clang/test/AST/Interp/shifts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@


namespace shifts {
constexpr void test() { // ref-error {{constexpr function never produces a constant expression}} \
// ref-cxx17-error {{constexpr function never produces a constant expression}} \
// expected-error {{constexpr function never produces a constant expression}} \
// cxx17-error {{constexpr function never produces a constant expression}} \
constexpr void test() { // ref-error {{constexpr function that never produces a constant expression}} \
// ref-cxx17-error {{constexpr function that never produces a constant expression}} \
// expected-error {{constexpr function that never produces a constant expression}} \
// cxx17-error {{constexpr function that never produces a constant expression}} \

char c; // cxx17-warning {{uninitialized variable}} \
// ref-cxx17-warning {{uninitialized variable}}
Expand Down
Loading
Loading