[clang] [clang][bytecode] Implement constexpr step limit (PR #176150)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 2 20:49:45 PST 2026
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/176150 at github.com>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/176150
>From 32e04aa0207901d3e32144ee9098f87dfa49e236 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 1/3] [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 d095e6f862fc5..b1791556502a8 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 837d5fef91b8e..1c39b030446a9 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -26,6 +26,7 @@ InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
Parent.CheckingPotentialConstantExpression;
CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior;
EvalMode = Parent.EvalMode;
+ StepsLeft = Ctx.getLangOpts().ConstexprStepLimit;
}
InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
@@ -39,6 +40,7 @@ InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
Parent.CheckingPotentialConstantExpression;
CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior;
EvalMode = Parent.EvalMode;
+ StepsLeft = Ctx.getLangOpts().ConstexprStepLimit;
}
bool InterpState::inConstantContext() const {
@@ -154,3 +156,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 98dc5cfd3b3c4..ddf7daf3a24b0 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -119,6 +119,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;
@@ -150,6 +154,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}}
+
>From 527af74024f999f3ce32eb14a34982e4ad7f84a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 27 Jan 2026 06:59:15 +0100
Subject: [PATCH 2/3] Add InfiniteSteps flag
---
clang/lib/AST/ByteCode/InterpState.cpp | 12 ++++++++----
clang/lib/AST/ByteCode/InterpState.h | 7 ++++++-
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp
index 1c39b030446a9..df507bd5507c3 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -20,13 +20,14 @@ using namespace clang::interp;
InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
Context &Ctx, SourceMapper *M)
: State(Ctx.getASTContext(), Parent.getEvalStatus()), M(M), P(P), Stk(Stk),
- Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame) {
+ Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame),
+ StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
+ InfiniteSteps(StepsLeft == 0) {
InConstantContext = Parent.InConstantContext;
CheckingPotentialConstantExpression =
Parent.CheckingPotentialConstantExpression;
CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior;
EvalMode = Parent.EvalMode;
- StepsLeft = Ctx.getLangOpts().ConstexprStepLimit;
}
InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
@@ -34,13 +35,13 @@ InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
: State(Ctx.getASTContext(), Parent.getEvalStatus()), M(nullptr), P(P),
Stk(Stk), Ctx(Ctx),
BottomFrame(*this, Func, nullptr, CodePtr(), Func->getArgSize()),
- Current(&BottomFrame) {
+ Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
+ InfiniteSteps(StepsLeft == 0) {
InConstantContext = Parent.InConstantContext;
CheckingPotentialConstantExpression =
Parent.CheckingPotentialConstantExpression;
CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior;
EvalMode = Parent.EvalMode;
- StepsLeft = Ctx.getLangOpts().ConstexprStepLimit;
}
bool InterpState::inConstantContext() const {
@@ -158,6 +159,9 @@ StdAllocatorCaller InterpState::getStdAllocatorCaller(StringRef Name) const {
}
bool InterpState::noteStep(CodePtr OpPC) {
+ if (InfiniteSteps)
+ return true;
+
--StepsLeft;
if (StepsLeft != 0)
return true;
diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h
index ddf7daf3a24b0..3e5197c6f5b1d 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -150,11 +150,16 @@ class InterpState final : public State, public SourceMapper {
SourceLocation EvalLocation;
/// Declaration we're initializing/evaluting, if any.
const VarDecl *EvaluatingDecl = nullptr;
+ /// Steps left during evaluation.
+ unsigned StepsLeft = 0;
+ /// Whether infinite evaluation steps have been requested. If this is false,
+ /// we use the StepsLeft value above.
+ const bool InfiniteSteps = false;
+
/// Things needed to do speculative execution.
SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr;
unsigned SpeculationDepth = 0;
std::optional<bool> ConstantContextOverride;
- unsigned StepsLeft;
llvm::SmallVector<
std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>>
>From 079e61dda5e6c4e767d62173f8c72f4080e1d0fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 3 Feb 2026 05:49:26 +0100
Subject: [PATCH 3/3] Initialize StepsLeft to 1
---
clang/lib/AST/ByteCode/InterpState.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h
index 3e5197c6f5b1d..83ef56e7f8452 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -151,7 +151,7 @@ class InterpState final : public State, public SourceMapper {
/// Declaration we're initializing/evaluting, if any.
const VarDecl *EvaluatingDecl = nullptr;
/// Steps left during evaluation.
- unsigned StepsLeft = 0;
+ unsigned StepsLeft = 1;
/// Whether infinite evaluation steps have been requested. If this is false,
/// we use the StepsLeft value above.
const bool InfiniteSteps = false;
More information about the cfe-commits
mailing list