Skip to content
36 changes: 24 additions & 12 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1806,6 +1806,7 @@ static unsigned getRecordDiagFromTagKind(TagTypeKind Tag) {
static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
Stmt *Body,
Sema::CheckConstexprKind Kind);
static bool CheckConstexprMissingReturn(Sema &SemaRef, const FunctionDecl *Dcl);

// Check whether a function declaration satisfies the requirements of a
// constexpr function definition or a constexpr constructor definition. If so,
Expand Down Expand Up @@ -2411,20 +2412,9 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
}
} else {
if (ReturnStmts.empty()) {
// C++1y doesn't require constexpr functions to contain a 'return'
// statement. We still do, unless the return type might be void, because
// otherwise if there's no return statement, the function cannot
// be used in a core constant expression.
bool OK = SemaRef.getLangOpts().CPlusPlus14 &&
(Dcl->getReturnType()->isVoidType() ||
Dcl->getReturnType()->isDependentType());
switch (Kind) {
case Sema::CheckConstexprKind::Diagnose:
SemaRef.Diag(Dcl->getLocation(),
OK ? diag::warn_cxx11_compat_constexpr_body_no_return
: diag::err_constexpr_body_no_return)
<< Dcl->isConsteval();
if (!OK)
if (!CheckConstexprMissingReturn(SemaRef, Dcl))
return false;
break;

Expand Down Expand Up @@ -2494,6 +2484,28 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
return true;
}

static bool CheckConstexprMissingReturn(Sema &SemaRef,
const FunctionDecl *Dcl) {
bool IsVoidOrDependentType = Dcl->getReturnType()->isVoidType() ||
Dcl->getReturnType()->isDependentType();
// Skip emitting a missing return error diagnostic for non-void functions
// since C++23 no longer mandates constexpr functions to yield constant
// expressions.
if (SemaRef.getLangOpts().CPlusPlus23 && !IsVoidOrDependentType)
return true;

// C++14 doesn't require constexpr functions to contain a 'return'
// statement. We still do, unless the return type might be void, because
// otherwise if there's no return statement, the function cannot
// be used in a core constant expression.
bool OK = SemaRef.getLangOpts().CPlusPlus14 && IsVoidOrDependentType;
SemaRef.Diag(Dcl->getLocation(),
OK ? diag::warn_cxx11_compat_constexpr_body_no_return
: diag::err_constexpr_body_no_return)
<< Dcl->isConsteval();
return OK;
}

bool Sema::CheckImmediateEscalatingFunctionDefinition(
FunctionDecl *FD, const sema::FunctionScopeInfo *FSI) {
if (!getLangOpts().CPlusPlus20 || !FD->isImmediateEscalating())
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ constexpr int ClassDecl3() {
return 0;
}

constexpr int NoReturn() {} // expected-error {{no return statement in constexpr function}}
constexpr int NoReturn() {} // beforecxx23-error {{no return statement in constexpr function}}
constexpr int MultiReturn() {
return 0; // beforecxx14-note {{return statement}}
return 0; // beforecxx14-warning {{multiple return statements in constexpr function}}
Expand Down
2 changes: 1 addition & 1 deletion clang/test/SemaCXX/constant-expression-cxx14.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ constexpr void k() {

// If the return type is not 'void', no return statements => never a constant
// expression, so still diagnose that case.
[[noreturn]] constexpr int fn() { // expected-error {{no return statement in constexpr function}}
[[noreturn]] constexpr int fn() { // cxx14_20-error {{no return statement in constexpr function}}
fn();
}

Expand Down
7 changes: 7 additions & 0 deletions clang/test/SemaCXX/constexpr-return-non-void-cxx2b.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s

constexpr int f() { } // expected-warning {{non-void function does not return a value}}
static_assert(__is_same(decltype([] constexpr -> int { }( )), int)); // expected-warning {{non-void lambda does not return a value}}

consteval int g() { } // expected-warning {{non-void function does not return a value}}
static_assert(__is_same(decltype([] consteval -> int { }( )), int)); // expected-warning {{non-void lambda does not return a value}}