[clang] [clang][diagnostics] Refactor constexpr diagnostics to use enum_select (PR #179233)
Luka Aladashvili via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 2 06:58:23 PST 2026
https://github.com/llukito updated https://github.com/llvm/llvm-project/pull/179233
>From 6ab49ef74a8de1b59aadad09681cbe62c159e1f7 Mon Sep 17 00:00:00 2001
From: Luka Aladashvili <115102487+llukito at users.noreply.github.com>
Date: Mon, 2 Feb 2026 17:38:34 +0400
Subject: [PATCH 1/4] [clang][diagnostics] Refactor constexpr diagnostics to
use enum_select
Replaces %select{function|constructor} with %enum_select to improve clarity and type safety.
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 807440c107897..4d55ef460c928 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -23,9 +23,9 @@ defm typename_outside_of_template : CXX11Compat<"'typename' outside of a templat
// C++14 compatibility with C++11 and earlier.
defm constexpr_type_definition : CXX14Compat<
- "type definition in a constexpr %select{function|constructor}0 is">;
+ "type definition in a constexpr %enum_select<Function, Constructor>0 is">;
defm constexpr_local_var : CXX14Compat<
- "variable declaration in a constexpr %select{function|constructor}0 is">;
+ "variable declaration in a constexpr %enum_select<Function, Constructor>0 is">;
defm constexpr_body_multiple_return : CXX14Compat<
"multiple return statements in constexpr function is">;
defm variable_template : CXX14Compat<"variable templates are">;
@@ -38,9 +38,9 @@ defm inline_variable : CXX17Compat<"inline variables are">;
defm decomp_decl_spec
: CXX20Compat<"structured binding declaration declared '%0' is">;
defm constexpr_local_var_no_init : CXX20Compat<
- "uninitialized variable in a constexpr %select{function|constructor}0 is">;
+ "uninitialized variable in a constexpr %enum_select<Function, Constructor>0 is">;
defm constexpr_function_try_block : CXX20Compat<
- "function try block in constexpr %select{function|constructor}0 is">;
+ "function try block in constexpr %enum_select<Function, Constructor>0 is">;
defm constexpr_union_ctor_no_init : CXX20Compat<
"constexpr union constructor that does not initialize any member is">;
defm constexpr_ctor_missing_init : CXX20Compat<
@@ -56,7 +56,7 @@ defm implicit_typename
// C++23 compatibility with C++20 and earlier.
defm constexpr_static_var : CXX23Compat<
"definition of a %select{static|thread_local}1 variable "
- "in a constexpr %select{function|constructor}0 "
+ "in a constexpr %enum_select<Function, Constructor>0 "
"is">;
// C++26 compatibility with C++23 and earlier.
@@ -65,7 +65,7 @@ defm decomp_decl_cond : CXX26Compat<"structured binding declaration in a conditi
// Compatibility warnings duplicated across multiple language versions.
foreach std = [14, 20, 23] in {
defm cxx#std#_constexpr_body_invalid_stmt : CXXCompat<
- "use of this statement in a constexpr %select{function|constructor}0 is", std>;
+ "use of this statement in a constexpr %enum_select<Function, Constructor>0 is", std>;
}
def note_previous_decl : Note<"%0 declared here">;
>From 71e1f6b3df0179dcebec2fc5fa1771fb7ae4be9c Mon Sep 17 00:00:00 2001
From: Luka Aladashvili <115102487+llukito at users.noreply.github.com>
Date: Mon, 2 Feb 2026 18:33:57 +0400
Subject: [PATCH 2/4] Fix constexpr indentation and logic
---
clang/lib/Sema/SemaDeclCXX.cpp | 35 ++++++++++++++++++++++++----------
1 file changed, 25 insertions(+), 10 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 5837ecd6b9163..3b20a254c4a60 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -2073,33 +2073,39 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
// thread storage duration or [before C++2a] for which no
// initialization is performed.
const auto *VD = cast<VarDecl>(DclIt);
+
+ // Define the enum UP HERE so it is visible to all the checks below
+ enum { Function, Constructor };
+
if (VD->isThisDeclarationADefinition()) {
if (VD->isStaticLocal()) {
if (Kind == Sema::CheckConstexprKind::Diagnose) {
SemaRef.DiagCompat(VD->getLocation(),
diag_compat::constexpr_static_var)
- << isa<CXXConstructorDecl>(Dcl)
+ << (isa<CXXConstructorDecl>(Dcl) ? Constructor : Function)
<< (VD->getTLSKind() == VarDecl::TLS_Dynamic);
} else if (!SemaRef.getLangOpts().CPlusPlus23) {
return false;
}
}
+
if (SemaRef.LangOpts.CPlusPlus23) {
CheckLiteralType(SemaRef, Kind, VD->getLocation(), VD->getType(),
diag::warn_cxx20_compat_constexpr_var,
- isa<CXXConstructorDecl>(Dcl));
+ isa<CXXConstructorDecl>(Dcl) ? Constructor
+ : Function);
} else if (CheckLiteralType(
SemaRef, Kind, VD->getLocation(), VD->getType(),
diag::err_constexpr_local_var_non_literal_type,
- isa<CXXConstructorDecl>(Dcl))) {
+ isa<CXXConstructorDecl>(Dcl) ? Constructor : Function)) {
return false;
}
- if (!VD->getType()->isDependentType() &&
- !VD->hasInit() && !VD->isCXXForRangeDecl()) {
+ if (!VD->getType()->isDependentType() && !VD->hasInit() &&
+ !VD->isCXXForRangeDecl()) {
if (Kind == Sema::CheckConstexprKind::Diagnose) {
SemaRef.DiagCompat(VD->getLocation(),
diag_compat::constexpr_local_var_no_init)
- << isa<CXXConstructorDecl>(Dcl);
+ << (isa<CXXConstructorDecl>(Dcl) ? Constructor : Function);
} else if (!SemaRef.getLangOpts().CPlusPlus20) {
return false;
}
@@ -2108,7 +2114,7 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
}
if (Kind == Sema::CheckConstexprKind::Diagnose) {
SemaRef.DiagCompat(VD->getLocation(), diag_compat::constexpr_local_var)
- << isa<CXXConstructorDecl>(Dcl);
+ << (isa<CXXConstructorDecl>(Dcl) ? Constructor : Function);
} else if (!SemaRef.getLangOpts().CPlusPlus14) {
return false;
}
@@ -2382,6 +2388,10 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
//
// This restriction is lifted in C++2a, as long as inner statements also
// apply the general constexpr rules.
+
+ // <--- START FIX
+ enum { Function, Constructor }; // Defined locally for this block
+
switch (Kind) {
case Sema::CheckConstexprKind::CheckValid:
if (!SemaRef.getLangOpts().CPlusPlus20)
@@ -2391,11 +2401,12 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
case Sema::CheckConstexprKind::Diagnose:
SemaRef.DiagCompat(Body->getBeginLoc(),
diag_compat::constexpr_function_try_block)
- << isa<CXXConstructorDecl>(Dcl);
+ << (isa<CXXConstructorDecl>(Dcl) ? Constructor : Function);
break;
}
+ // <--- END FIX
}
-
+
// - its function-body shall be [...] a compound-statement that contains only
// [... list of cases ...]
//
@@ -2502,10 +2513,14 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
break;
}
} else if (ReturnStmts.size() > 1) {
+ // Define the enum locally again because the previous one is out of scope
+ enum { Function, Constructor };
+
switch (Kind) {
case Sema::CheckConstexprKind::Diagnose:
SemaRef.DiagCompat(ReturnStmts.back(),
- diag_compat::constexpr_body_multiple_return);
+ diag_compat::constexpr_body_multiple_return)
+ << (isa<CXXConstructorDecl>(Dcl) ? Constructor : Function);
for (unsigned I = 0; I < ReturnStmts.size() - 1; ++I)
SemaRef.Diag(ReturnStmts[I],
diag::note_constexpr_body_previous_return);
>From 374a0977cb76ef4c5828d6edb8b1c09bbe181aa8 Mon Sep 17 00:00:00 2001
From: Luka Aladashvili <115102487+llukito at users.noreply.github.com>
Date: Mon, 2 Feb 2026 18:42:06 +0400
Subject: [PATCH 3/4] Fix remaining magic numbers
---
clang/lib/Sema/SemaDeclCXX.cpp | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3b20a254c4a60..e8ef89e16878e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -2049,10 +2049,11 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
case Decl::CXXRecord:
// C++1y allows types to be defined, not just declared.
if (cast<TagDecl>(DclIt)->isThisDeclarationADefinition()) {
+ enum { Function, Constructor }; // Define enum locally
if (Kind == Sema::CheckConstexprKind::Diagnose) {
SemaRef.DiagCompat(DS->getBeginLoc(),
diag_compat::constexpr_type_definition)
- << isa<CXXConstructorDecl>(Dcl);
+ << (isa<CXXConstructorDecl>(Dcl) ? Constructor : Function);
} else if (!SemaRef.getLangOpts().CPlusPlus14) {
return false;
}
@@ -2427,15 +2428,18 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
(Cxx2aLoc.isValid() && !SemaRef.getLangOpts().CPlusPlus20) ||
(Cxx1yLoc.isValid() && !SemaRef.getLangOpts().CPlusPlus17))
return false;
- } else if (Cxx2bLoc.isValid()) {
- SemaRef.DiagCompat(Cxx2bLoc, diag_compat::cxx23_constexpr_body_invalid_stmt)
- << isa<CXXConstructorDecl>(Dcl);
- } else if (Cxx2aLoc.isValid()) {
- SemaRef.DiagCompat(Cxx2aLoc, diag_compat::cxx20_constexpr_body_invalid_stmt)
- << isa<CXXConstructorDecl>(Dcl);
- } else if (Cxx1yLoc.isValid()) {
- SemaRef.DiagCompat(Cxx1yLoc, diag_compat::cxx14_constexpr_body_invalid_stmt)
- << isa<CXXConstructorDecl>(Dcl);
+ } else {
+ enum { Function, Constructor };
+ if (Cxx2bLoc.isValid()) {
+ SemaRef.DiagCompat(Cxx2bLoc, diag_compat::cxx23_constexpr_body_invalid_stmt)
+ << (isa<CXXConstructorDecl>(Dcl) ? Constructor : Function);
+ } else if (Cxx2aLoc.isValid()) {
+ SemaRef.DiagCompat(Cxx2aLoc, diag_compat::cxx20_constexpr_body_invalid_stmt)
+ << (isa<CXXConstructorDecl>(Dcl) ? Constructor : Function);
+ } else if (Cxx1yLoc.isValid()) {
+ SemaRef.DiagCompat(Cxx1yLoc, diag_compat::cxx14_constexpr_body_invalid_stmt)
+ << (isa<CXXConstructorDecl>(Dcl) ? Constructor : Function);
+ }
}
if (const CXXConstructorDecl *Constructor
>From a40e1d16471f0fe6e283bf6cc847d9951aba452a Mon Sep 17 00:00:00 2001
From: Luka Aladashvili <115102487+llukito at users.noreply.github.com>
Date: Mon, 2 Feb 2026 18:58:11 +0400
Subject: [PATCH 4/4] [clang][diagnostics] Hoist enum definitions to function
scope to fix magic numbers
---
clang/lib/Sema/SemaDeclCXX.cpp | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index e8ef89e16878e..2afe13d818dd2 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -2012,6 +2012,7 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
// C++11 [dcl.constexpr]p3 and p4:
// The definition of a constexpr function(p3) or constructor(p4) [...] shall
// contain only
+ enum { Function = 0, Constructor = 1 };
for (const auto *DclIt : DS->decls()) {
switch (DclIt->getKind()) {
case Decl::StaticAssert:
@@ -2049,7 +2050,6 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
case Decl::CXXRecord:
// C++1y allows types to be defined, not just declared.
if (cast<TagDecl>(DclIt)->isThisDeclarationADefinition()) {
- enum { Function, Constructor }; // Define enum locally
if (Kind == Sema::CheckConstexprKind::Diagnose) {
SemaRef.DiagCompat(DS->getBeginLoc(),
diag_compat::constexpr_type_definition)
@@ -2075,8 +2075,6 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
// initialization is performed.
const auto *VD = cast<VarDecl>(DclIt);
- // Define the enum UP HERE so it is visible to all the checks below
- enum { Function, Constructor };
if (VD->isThisDeclarationADefinition()) {
if (VD->isStaticLocal()) {
@@ -2215,6 +2213,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
SourceLocation &Cxx2bLoc,
Sema::CheckConstexprKind Kind) {
// - its function-body shall be [...] a compound-statement that contains only
+ enum { Function = 0, Constructor = 1 };
switch (S->getStmtClass()) {
case Stmt::NullStmtClass:
// - null statements,
@@ -2374,6 +2373,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
Stmt *Body,
Sema::CheckConstexprKind Kind) {
+ enum { Function = 0, Constructor = 1 };
SmallVector<SourceLocation, 4> ReturnStmts;
if (isa<CXXTryStmt>(Body)) {
@@ -2389,9 +2389,6 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
//
// This restriction is lifted in C++2a, as long as inner statements also
// apply the general constexpr rules.
-
- // <--- START FIX
- enum { Function, Constructor }; // Defined locally for this block
switch (Kind) {
case Sema::CheckConstexprKind::CheckValid:
@@ -2405,7 +2402,6 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
<< (isa<CXXConstructorDecl>(Dcl) ? Constructor : Function);
break;
}
- // <--- END FIX
}
// - its function-body shall be [...] a compound-statement that contains only
@@ -2429,7 +2425,6 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
(Cxx1yLoc.isValid() && !SemaRef.getLangOpts().CPlusPlus17))
return false;
} else {
- enum { Function, Constructor };
if (Cxx2bLoc.isValid()) {
SemaRef.DiagCompat(Cxx2bLoc, diag_compat::cxx23_constexpr_body_invalid_stmt)
<< (isa<CXXConstructorDecl>(Dcl) ? Constructor : Function);
@@ -2516,10 +2511,7 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
return false;
break;
}
- } else if (ReturnStmts.size() > 1) {
- // Define the enum locally again because the previous one is out of scope
- enum { Function, Constructor };
-
+ } else if (ReturnStmts.size() > 1) {
switch (Kind) {
case Sema::CheckConstexprKind::Diagnose:
SemaRef.DiagCompat(ReturnStmts.back(),
More information about the cfe-commits
mailing list