Timm =?utf-8?q?Bäder?= <[email protected]>, Timm =?utf-8?q?Bäder?= <[email protected]> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/[email protected]>
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?= <[email protected]> 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?= <[email protected]> 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?= <[email protected]> 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; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
