[clang] [clang][bytecode] Implement constexpr step limit (PR #176150)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 15 04:50:35 PST 2026
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/176150
This only calls `noteStep()` on jump opcodes, so this works for loops. It does not prevent "hangs" when a function is just _very_ long (could be interesting how this interfaces with expand statements?).
>From f5c9591995a7146205ef3bcd33ca3b2901d57d8d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 15 Jan 2026 13:46:23 +0100
Subject: [PATCH] [clang][bytecode] Implement constexpr step limit
---
clang/lib/AST/ByteCode/Interp.cpp | 6 +++---
clang/lib/AST/ByteCode/InterpState.cpp | 11 +++++++++++
clang/lib/AST/ByteCode/InterpState.h | 5 +++++
clang/test/AST/ByteCode/constexpr-steps.cpp | 10 ++++++++++
4 files changed, 29 insertions(+), 3 deletions(-)
create mode 100644 clang/test/AST/ByteCode/constexpr-steps.cpp
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 0205d840fd71e..cff8f6c83cd68 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -38,21 +38,21 @@ static bool RetValue(InterpState &S, CodePtr &Pt) {
static bool Jmp(InterpState &S, CodePtr &PC, int32_t Offset) {
PC += Offset;
- return true;
+ return S.noteStep(PC);
}
static bool Jt(InterpState &S, CodePtr &PC, int32_t Offset) {
if (S.Stk.pop<bool>()) {
PC += Offset;
}
- return true;
+ return S.noteStep(PC);
}
static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
if (!S.Stk.pop<bool>()) {
PC += Offset;
}
- return true;
+ return S.noteStep(PC);
}
// https://github.com/llvm/llvm-project/issues/102513
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp
index a95916cd63981..20207b9337e02 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -26,6 +26,7 @@ InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
Parent.CheckingPotentialConstantExpression;
CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior;
EvalMode = Parent.EvalMode;
+ StepsLeft = Ctx.getLangOpts().ConstexprStepLimit;
}
InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
@@ -38,6 +39,7 @@ InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
Parent.CheckingPotentialConstantExpression;
CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior;
EvalMode = Parent.EvalMode;
+ StepsLeft = Ctx.getLangOpts().ConstexprStepLimit;
}
bool InterpState::inConstantContext() const {
@@ -153,3 +155,12 @@ StdAllocatorCaller InterpState::getStdAllocatorCaller(StringRef Name) const {
return {};
}
+
+bool InterpState::noteStep(CodePtr OpPC) {
+ --StepsLeft;
+ if (StepsLeft != 0)
+ return true;
+
+ FFDiag(Current->getSource(OpPC), diag::note_constexpr_step_limit_exceeded);
+ return false;
+}
diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h
index e2e4d5c985f93..197ea2b138e05 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -153,6 +153,10 @@ class InterpState final : public State, public SourceMapper {
return Floating(Mem, llvm::APFloatBase::SemanticsToEnum(Sem));
}
+ /// Note that a step has been executed. If there are no more steps remaining,
+ /// diagnoses and returns \c false.
+ bool noteStep(CodePtr OpPC);
+
private:
friend class EvaluationResult;
friend class InterpStateCCOverride;
@@ -184,6 +188,7 @@ class InterpState final : public State, public SourceMapper {
SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr;
unsigned SpeculationDepth = 0;
std::optional<bool> ConstantContextOverride;
+ unsigned StepsLeft;
llvm::SmallVector<
std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>>
diff --git a/clang/test/AST/ByteCode/constexpr-steps.cpp b/clang/test/AST/ByteCode/constexpr-steps.cpp
new file mode 100644
index 0000000000000..490425107a140
--- /dev/null
+++ b/clang/test/AST/ByteCode/constexpr-steps.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s -fconstexpr-steps=100
+
+
+constexpr int foo() { // expected-error {{never produces a constant expression}}
+ while (1) {} // expected-note 2{{constexpr evaluation hit maximum step limit}}
+ return 0;
+}
+static_assert (foo() == 0, ""); // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to}}
+
More information about the cfe-commits
mailing list