[clang] [Clang] speed up -Wassign-enum via enumerator caching (PR #176560)
Oleksandr Tarasiuk via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 17 03:53:48 PST 2026
https://github.com/a-tarasyuk created https://github.com/llvm/llvm-project/pull/176560
Fixes #176454
---
This patch addresses a performance issue in `-Wassign-enum` where enumerator values were repeatedly rebuilt and sorted for each assignment check, leading to excessive compile-time overhead for large enums.
>From 0651b94ef5e9708f16bf5eed37379abc88417c0b Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Sat, 17 Jan 2026 13:14:41 +0200
Subject: [PATCH] [Clang] speed up -Wassign-enum via enumerator caching
---
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/Sema/Sema.h | 4 ++
clang/lib/Sema/SemaStmt.cpp | 47 ++++++++++---------
.../test/Sema/warn-outof-range-assign-enum.c | 13 ++++-
4 files changed, 43 insertions(+), 22 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4139d1d80ed4a..c9e19128af74e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -105,6 +105,7 @@ Attribute Changes in Clang
Improvements to Clang's diagnostics
-----------------------------------
+- Improved `-Wassign-enum` performance by caching enum enumerator values. (#GH176454)
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 23eb954ce774c..7929f26fd92d0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3522,6 +3522,10 @@ class Sema final : public SemaBase {
/// attribute.
mutable llvm::DenseMap<const EnumDecl *, llvm::APInt> FlagBitsCache;
+ /// A cache of enumerator values for enums checked by -Wassign-enum.
+ mutable llvm::DenseMap<const EnumDecl *, llvm::SmallVector<llvm::APSInt, 64>>
+ AssignEnumCache;
+
/// WeakUndeclaredIdentifiers - Identifiers contained in \#pragma weak before
/// declared. Rare. May alias another identifier, declared or undeclared.
///
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 1b1643250d05e..18788ed4b428f 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -1761,30 +1761,35 @@ Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType,
return;
}
- typedef SmallVector<std::pair<llvm::APSInt, EnumConstantDecl *>, 64>
- EnumValsTy;
- EnumValsTy EnumVals;
-
- // Gather all enum values, set their type and sort them,
- // allowing easier comparison with rhs constant.
- for (auto *EDI : ED->enumerators()) {
- llvm::APSInt Val = EDI->getInitVal();
- AdjustAPSInt(Val, DstWidth, DstIsSigned);
- EnumVals.emplace_back(Val, EDI);
+ auto EnumValuesCmp = [](const llvm::APSInt &A, const llvm::APSInt &B) {
+ return A < B;
+ };
+
+ const EnumDecl *Key = ED->getCanonicalDecl();
+ auto [It, Inserted] = AssignEnumCache.try_emplace(Key);
+ auto &Values = It->second;
+
+ if (Inserted) {
+ Values.reserve(std::distance(ED->enumerator_begin(), ED->enumerator_end()));
+
+ for (auto *EC : ED->enumerators()) {
+ llvm::APSInt V = EC->getInitVal();
+ AdjustAPSInt(V, DstWidth, DstIsSigned);
+ Values.push_back(V);
+ }
+
+ if (Values.empty())
+ return;
+
+ llvm::sort(Values, EnumValuesCmp);
+ Values.erase(llvm::unique(Values), Values.end());
}
- if (EnumVals.empty())
+
+ if (llvm::binary_search(Values, *RHSVal, EnumValuesCmp))
return;
- llvm::stable_sort(EnumVals, CmpEnumVals);
- EnumValsTy::iterator EIend = llvm::unique(EnumVals, EqEnumVals);
- // See which values aren't in the enum.
- EnumValsTy::const_iterator EI = EnumVals.begin();
- while (EI != EIend && EI->first < *RHSVal)
- EI++;
- if (EI == EIend || EI->first != *RHSVal) {
- Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignment)
- << DstType.getUnqualifiedType();
- }
+ Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignment)
+ << DstType.getUnqualifiedType();
}
StmtResult Sema::ActOnWhileStmt(SourceLocation WhileLoc,
diff --git a/clang/test/Sema/warn-outof-range-assign-enum.c b/clang/test/Sema/warn-outof-range-assign-enum.c
index 23c78497b37e4..0d8a4dab114e2 100644
--- a/clang/test/Sema/warn-outof-range-assign-enum.c
+++ b/clang/test/Sema/warn-outof-range-assign-enum.c
@@ -50,6 +50,18 @@ void f(void)
x += 1; // expected-warning {{integer constant not in range of enumerated type}}
}
+typedef enum OutOfOrderTestEnum {
+ OO1 = 100,
+ OO2 = 50,
+ OO3 = 75,
+ OO4 = 9,
+ OO5 = 99
+} OutOfOrderTestEnum;
+
+OutOfOrderTestEnum t1 = 75;
+OutOfOrderTestEnum t2 = 9;
+OutOfOrderTestEnum t3 = 76; // expected-warning {{integer constant not in range of enumerated type 'OutOfOrderTestEnum'}}
+
int main(void) {
CCTestEnum test = 1; // expected-warning {{integer constant not in range of enumerated type 'CCTestEnum'}}
test = 600; // expected-warning {{integer constant not in range of enumerated type 'CCTestEnum'}}
@@ -58,4 +70,3 @@ int main(void) {
foo(4);
foo(Two+1);
}
-
More information about the cfe-commits
mailing list