[clang] [clang][bytecode] Implement constexpr step limit (PR #176150)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 15 04:51:06 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Timm Baeder (tbaederr)
<details>
<summary>Changes</summary>
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
---
Full diff: https://github.com/llvm/llvm-project/pull/176150.diff
4 Files Affected:
- (modified) clang/lib/AST/ByteCode/Interp.cpp (+3-3)
- (modified) clang/lib/AST/ByteCode/InterpState.cpp (+11)
- (modified) clang/lib/AST/ByteCode/InterpState.h (+5)
- (added) clang/test/AST/ByteCode/constexpr-steps.cpp (+10)
``````````diff
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}}
+
``````````
</details>
https://github.com/llvm/llvm-project/pull/176150
More information about the cfe-commits
mailing list