[clang] f71e321 - [clang][bytecode] Implement constexpr step limit (#176150)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 9 03:22:53 PST 2026
Author: Timm Baeder
Date: 2026-02-09T12:22:48+01:00
New Revision: f71e32196667264607974e22d28d3badb2d73b5e
URL: https://github.com/llvm/llvm-project/commit/f71e32196667264607974e22d28d3badb2d73b5e
DIFF: https://github.com/llvm/llvm-project/commit/f71e32196667264607974e22d28d3badb2d73b5e.diff
LOG: [clang][bytecode] Implement constexpr step limit (#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?).
Fixes https://github.com/llvm/llvm-project/issues/165951
Added:
clang/test/AST/ByteCode/constexpr-steps.cpp
Modified:
clang/lib/AST/ByteCode/Interp.cpp
clang/lib/AST/ByteCode/InterpState.cpp
clang/lib/AST/ByteCode/InterpState.h
Removed:
################################################################################
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 8eaff4bb07f7d..f6e2f149165ff 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..df507bd5507c3 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -20,7 +20,9 @@ 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;
@@ -33,7 +35,8 @@ 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;
@@ -154,3 +157,15 @@ StdAllocatorCaller InterpState::getStdAllocatorCaller(StringRef Name) const {
return {};
}
+
+bool InterpState::noteStep(CodePtr OpPC) {
+ if (InfiniteSteps)
+ return true;
+
+ --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..83ef56e7f8452 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;
@@ -146,6 +150,12 @@ 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 = 1;
+ /// 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;
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