[clang] [clang][ExprConst] Move shared `EvalInfo` state into `interp::State` (PR #177738)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 23 22:05:44 PST 2026
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/177738
Instead of having `InterpState` call into its parent `EvalInfo`, just save the state in `interp::State`, where both subclasses can access it.
>From 5b1a9965b6bf8ca5698e3fdb7a4b2863e5aa8879 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Fri, 23 Jan 2026 14:29:38 +0100
Subject: [PATCH] State
---
clang/lib/AST/ByteCode/InterpState.cpp | 7 +-
clang/lib/AST/ByteCode/InterpState.h | 28 +-----
clang/lib/AST/ByteCode/State.cpp | 23 ++++-
clang/lib/AST/ByteCode/State.h | 106 ++++++++++++++++++--
clang/lib/AST/ExprConstant.cpp | 133 ++-----------------------
5 files changed, 130 insertions(+), 167 deletions(-)
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp
index a95916cd63981..510606c84af13 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -19,8 +19,8 @@ using namespace clang::interp;
InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
Context &Ctx, SourceMapper *M)
- : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this),
- Current(&BottomFrame) {
+ : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(M), P(P), Stk(Stk),
+ Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame) {
InConstantContext = Parent.InConstantContext;
CheckingPotentialConstantExpression =
Parent.CheckingPotentialConstantExpression;
@@ -30,7 +30,8 @@ InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
Context &Ctx, const Function *Func)
- : Parent(Parent), M(nullptr), P(P), Stk(Stk), Ctx(Ctx),
+ : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(nullptr), P(P),
+ Stk(Stk), Ctx(Ctx),
BottomFrame(*this, Func, nullptr, CodePtr(), Func->getArgSize()),
Current(&BottomFrame) {
InConstantContext = Parent.InConstantContext;
diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h
index e2e4d5c985f93..5c620c0b8a775 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -63,35 +63,13 @@ class InterpState final : public State, public SourceMapper {
}
const Frame *getBottomFrame() const override { return &BottomFrame; }
- // Access objects from the walker context.
- Expr::EvalStatus &getEvalStatus() const override {
- return Parent.getEvalStatus();
- }
- ASTContext &getASTContext() const override { return Ctx.getASTContext(); }
const LangOptions &getLangOpts() const {
return Ctx.getASTContext().getLangOpts();
}
+ ASTContext &getASTContext() { return Ctx.getASTContext(); }
- // Forward status checks and updates to the walker.
- bool keepEvaluatingAfterFailure() const override {
- return Parent.keepEvaluatingAfterFailure();
- }
- bool keepEvaluatingAfterSideEffect() const override {
- return Parent.keepEvaluatingAfterSideEffect();
- }
- bool noteUndefinedBehavior() override {
- return Parent.noteUndefinedBehavior();
- }
+ bool stepsLeft() const override { return true; }
bool inConstantContext() const;
- bool hasActiveDiagnostic() override { return Parent.hasActiveDiagnostic(); }
- void setActiveDiagnostic(bool Flag) override {
- Parent.setActiveDiagnostic(Flag);
- }
- void setFoldFailureDiagnostic(bool Flag) override {
- Parent.setFoldFailureDiagnostic(Flag);
- }
- bool hasPriorDiagnostic() override { return Parent.hasPriorDiagnostic(); }
- bool noteSideEffect() override { return Parent.noteSideEffect(); }
/// Deallocates a pointer.
void deallocate(Block *B);
@@ -156,8 +134,6 @@ class InterpState final : public State, public SourceMapper {
private:
friend class EvaluationResult;
friend class InterpStateCCOverride;
- /// AST Walker state.
- State &Parent;
/// Dead block chain.
DeadBlock *DeadBlocks = nullptr;
/// Reference to the offset-source mapping.
diff --git a/clang/lib/AST/ByteCode/State.cpp b/clang/lib/AST/ByteCode/State.cpp
index 323231fbf8236..2a32f9f74d137 100644
--- a/clang/lib/AST/ByteCode/State.cpp
+++ b/clang/lib/AST/ByteCode/State.cpp
@@ -72,12 +72,12 @@ void State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
}
DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) {
- return getASTContext().getDiagnostics().Report(Loc, DiagId);
+ return Ctx.getDiagnostics().Report(Loc, DiagId);
}
/// Add a diagnostic to the diagnostics list.
PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) {
- PartialDiagnostic PD(DiagId, getASTContext().getDiagAllocator());
+ PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator());
getEvalStatus().Diag->push_back(std::make_pair(Loc, PD));
return getEvalStatus().Diag->back().second;
}
@@ -91,8 +91,7 @@ OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,
}
unsigned CallStackNotes = getCallStackDepth() - 1;
- unsigned Limit =
- getASTContext().getDiagnostics().getConstexprBacktraceLimit();
+ unsigned Limit = Ctx.getDiagnostics().getConstexprBacktraceLimit();
if (Limit)
CallStackNotes = std::min(CallStackNotes, Limit + 1);
if (checkingPotentialConstantExpression())
@@ -158,3 +157,19 @@ void State::addCallStack(unsigned Limit) {
<< Out.str() << CallRange;
}
}
+
+bool State::keepEvaluatingAfterFailure() const {
+ uint64_t Limit = Ctx.getLangOpts().ConstexprStepLimit;
+ if (Limit != 0 && !stepsLeft())
+ return false;
+
+ switch (EvalMode) {
+ case EvaluationMode::ConstantExpression:
+ case EvaluationMode::ConstantExpressionUnevaluated:
+ case EvaluationMode::ConstantFold:
+ case EvaluationMode::IgnoreSideEffects:
+ return checkingPotentialConstantExpression() ||
+ checkingForUndefinedBehavior();
+ }
+ llvm_unreachable("Missed EvalMode case");
+}
diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h
index 0695c61c07a05..99474284816fc 100644
--- a/clang/lib/AST/ByteCode/State.h
+++ b/clang/lib/AST/ByteCode/State.h
@@ -78,21 +78,82 @@ class SourceInfo;
/// Interface for the VM to interact with the AST walker's context.
class State {
public:
+ State(ASTContext &ASTCtx, Expr::EvalStatus &EvalStatus)
+ : Ctx(ASTCtx), EvalStatus(EvalStatus) {}
virtual ~State();
- virtual bool noteUndefinedBehavior() = 0;
- virtual bool keepEvaluatingAfterFailure() const = 0;
- virtual bool keepEvaluatingAfterSideEffect() const = 0;
virtual Frame *getCurrentFrame() = 0;
virtual const Frame *getBottomFrame() const = 0;
- virtual bool hasActiveDiagnostic() = 0;
- virtual void setActiveDiagnostic(bool Flag) = 0;
- virtual void setFoldFailureDiagnostic(bool Flag) = 0;
- virtual Expr::EvalStatus &getEvalStatus() const = 0;
- virtual ASTContext &getASTContext() const = 0;
- virtual bool hasPriorDiagnostic() = 0;
virtual unsigned getCallStackDepth() = 0;
- virtual bool noteSideEffect() = 0;
+ virtual bool stepsLeft() const = 0;
+
+ Expr::EvalStatus &getEvalStatus() const { return EvalStatus; }
+
+ /// Note that we have had a side-effect, and determine whether we should
+ /// keep evaluating.
+ bool noteSideEffect() {
+ getEvalStatus().HasSideEffects = true;
+ return keepEvaluatingAfterSideEffect();
+ }
+
+ // If we have a prior diagnostic, it will be noting that the expression
+ // isn't a constant expression. This diagnostic is more important,
+ // unless we require this evaluation to produce a constant expression.
+ //
+ // FIXME: We might want to show both diagnostics to the user in
+ // EvaluationMode::ConstantFold mode.
+
+ bool hasPriorDiagnostic() {
+ if (!getEvalStatus().Diag->empty()) {
+ switch (EvalMode) {
+ case EvaluationMode::ConstantFold:
+ case EvaluationMode::IgnoreSideEffects:
+ if (!HasFoldFailureDiagnostic)
+ break;
+ // We've already failed to fold something. Keep that diagnostic.
+ [[fallthrough]];
+ case EvaluationMode::ConstantExpression:
+ case EvaluationMode::ConstantExpressionUnevaluated:
+ setActiveDiagnostic(false);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Should we continue evaluation as much as possible after encountering a
+ /// construct which can't be reduced to a value?
+ bool keepEvaluatingAfterFailure() const;
+ /// Should we continue evaluation after encountering a side-effect that we
+ /// couldn't model?
+ bool keepEvaluatingAfterSideEffect() const {
+ switch (EvalMode) {
+ case EvaluationMode::IgnoreSideEffects:
+ return true;
+
+ case EvaluationMode::ConstantExpression:
+ case EvaluationMode::ConstantExpressionUnevaluated:
+ case EvaluationMode::ConstantFold:
+ // By default, assume any side effect might be valid in some other
+ // evaluation of this expression from a different context.
+ return checkingPotentialConstantExpression() ||
+ checkingForUndefinedBehavior();
+ }
+ llvm_unreachable("Missed EvalMode case");
+ }
+
+ /// Note that we hit something that was technically undefined behavior, but
+ /// that we can evaluate past it (such as signed overflow or floating-point
+ /// division by zero.)
+ bool noteUndefinedBehavior() {
+ getEvalStatus().HasUndefinedBehavior = true;
+ return keepEvaluatingAfterUndefinedBehavior();
+ }
+
+ bool hasActiveDiagnostic() const { return HasActiveDiagnostic; }
+ void setActiveDiagnostic(bool Flag) { HasActiveDiagnostic = Flag; };
+
+ void setFoldFailureDiagnostic(bool Flag) { HasFoldFailureDiagnostic = Flag; };
/// Are we checking whether the expression is a potential constant
/// expression?
@@ -105,7 +166,6 @@ class State {
}
public:
- State() = default;
/// Diagnose that the evaluation could not be folded (FF => FoldFailure)
OptionalDiagnostic
FFDiag(SourceLocation Loc,
@@ -168,7 +228,17 @@ class State {
/// is set; this is used when evaluating ICEs in C.
bool CheckingForUndefinedBehavior = false;
+ /// HasActiveDiagnostic - Was the previous diagnostic stored? If so, further
+ /// notes attached to it will also be stored, otherwise they will not be.
+ bool HasActiveDiagnostic = false;
+
+ /// Have we emitted a diagnostic explaining why we couldn't constant
+ /// fold (not just why it's not strictly a constant expression)?
+ bool HasFoldFailureDiagnostic = false;
+
EvaluationMode EvalMode;
+ ASTContext &Ctx;
+ Expr::EvalStatus &EvalStatus;
private:
void addCallStack(unsigned Limit);
@@ -177,6 +247,20 @@ class State {
OptionalDiagnostic diag(SourceLocation Loc, diag::kind DiagId,
unsigned ExtraNotes, bool IsCCEDiag);
+
+ /// Should we continue evaluation after encountering undefined behavior?
+ bool keepEvaluatingAfterUndefinedBehavior() {
+ switch (EvalMode) {
+ case EvaluationMode::IgnoreSideEffects:
+ case EvaluationMode::ConstantFold:
+ return true;
+
+ case EvaluationMode::ConstantExpression:
+ case EvaluationMode::ConstantExpressionUnevaluated:
+ return checkingForUndefinedBehavior();
+ }
+ llvm_unreachable("Missed EvalMode case");
+ }
};
} // namespace interp
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 857688ed8039d..86f728fd9f4a0 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -794,13 +794,8 @@ namespace {
/// rules. For example, the RHS of (0 && foo()) is not evaluated. We can
/// evaluate the expression regardless of what the RHS is, but C only allows
/// certain things in certain situations.
- class EvalInfo : public interp::State {
+ class EvalInfo final : public interp::State {
public:
- ASTContext &Ctx;
-
- /// EvalStatus - Contains information about the evaluation.
- Expr::EvalStatus &EvalStatus;
-
/// CurrentCall - The top of the constexpr call stack.
CallStackFrame *CurrentCall;
@@ -919,16 +914,8 @@ namespace {
/// initialization.
uint64_t ArrayInitIndex = -1;
- /// HasActiveDiagnostic - Was the previous diagnostic stored? If so, further
- /// notes attached to it will also be stored, otherwise they will not be.
- bool HasActiveDiagnostic;
-
- /// Have we emitted a diagnostic explaining why we couldn't constant
- /// fold (not just why it's not strictly a constant expression)?
- bool HasFoldFailureDiagnostic;
-
EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode)
- : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr),
+ : State(const_cast<ASTContext &>(C), S), CurrentCall(nullptr),
CallStackDepth(0), NextCallIndex(1),
StepsLeft(C.getLangOpts().ConstexprStepLimit),
EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp),
@@ -936,8 +923,7 @@ namespace {
/*This=*/nullptr,
/*CallExpr=*/nullptr, CallRef()),
EvaluatingDecl((const ValueDecl *)nullptr),
- EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
- HasFoldFailureDiagnostic(false) {
+ EvaluatingDeclValue(nullptr) {
EvalMode = Mode;
}
@@ -945,7 +931,6 @@ namespace {
discardCleanups();
}
- ASTContext &getASTContext() const override { return Ctx; }
const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,
@@ -1104,107 +1089,10 @@ namespace {
interp::Frame *getCurrentFrame() override { return CurrentCall; }
const interp::Frame *getBottomFrame() const override { return &BottomFrame; }
- bool hasActiveDiagnostic() override { return HasActiveDiagnostic; }
- void setActiveDiagnostic(bool Flag) override { HasActiveDiagnostic = Flag; }
-
- void setFoldFailureDiagnostic(bool Flag) override {
- HasFoldFailureDiagnostic = Flag;
- }
-
- Expr::EvalStatus &getEvalStatus() const override { return EvalStatus; }
-
- // If we have a prior diagnostic, it will be noting that the expression
- // isn't a constant expression. This diagnostic is more important,
- // unless we require this evaluation to produce a constant expression.
- //
- // FIXME: We might want to show both diagnostics to the user in
- // EvaluationMode::ConstantFold mode.
- bool hasPriorDiagnostic() override {
- if (!EvalStatus.Diag->empty()) {
- switch (EvalMode) {
- case EvaluationMode::ConstantFold:
- case EvaluationMode::IgnoreSideEffects:
- if (!HasFoldFailureDiagnostic)
- break;
- // We've already failed to fold something. Keep that diagnostic.
- [[fallthrough]];
- case EvaluationMode::ConstantExpression:
- case EvaluationMode::ConstantExpressionUnevaluated:
- setActiveDiagnostic(false);
- return true;
- }
- }
- return false;
- }
-
unsigned getCallStackDepth() override { return CallStackDepth; }
+ bool stepsLeft() const override { return StepsLeft > 0; }
public:
- /// Should we continue evaluation after encountering a side-effect that we
- /// couldn't model?
- bool keepEvaluatingAfterSideEffect() const override {
- switch (EvalMode) {
- case EvaluationMode::IgnoreSideEffects:
- return true;
-
- case EvaluationMode::ConstantExpression:
- case EvaluationMode::ConstantExpressionUnevaluated:
- case EvaluationMode::ConstantFold:
- // By default, assume any side effect might be valid in some other
- // evaluation of this expression from a different context.
- return checkingPotentialConstantExpression() ||
- checkingForUndefinedBehavior();
- }
- llvm_unreachable("Missed EvalMode case");
- }
-
- /// Note that we have had a side-effect, and determine whether we should
- /// keep evaluating.
- bool noteSideEffect() override {
- EvalStatus.HasSideEffects = true;
- return keepEvaluatingAfterSideEffect();
- }
-
- /// Should we continue evaluation after encountering undefined behavior?
- bool keepEvaluatingAfterUndefinedBehavior() {
- switch (EvalMode) {
- case EvaluationMode::IgnoreSideEffects:
- case EvaluationMode::ConstantFold:
- return true;
-
- case EvaluationMode::ConstantExpression:
- case EvaluationMode::ConstantExpressionUnevaluated:
- return checkingForUndefinedBehavior();
- }
- llvm_unreachable("Missed EvalMode case");
- }
-
- /// Note that we hit something that was technically undefined behavior, but
- /// that we can evaluate past it (such as signed overflow or floating-point
- /// division by zero.)
- bool noteUndefinedBehavior() override {
- EvalStatus.HasUndefinedBehavior = true;
- return keepEvaluatingAfterUndefinedBehavior();
- }
-
- /// Should we continue evaluation as much as possible after encountering a
- /// construct which can't be reduced to a value?
- bool keepEvaluatingAfterFailure() const override {
- uint64_t Limit = Ctx.getLangOpts().ConstexprStepLimit;
- if (Limit != 0 && !StepsLeft)
- return false;
-
- switch (EvalMode) {
- case EvaluationMode::ConstantExpression:
- case EvaluationMode::ConstantExpressionUnevaluated:
- case EvaluationMode::ConstantFold:
- case EvaluationMode::IgnoreSideEffects:
- return checkingPotentialConstantExpression() ||
- checkingForUndefinedBehavior();
- }
- llvm_unreachable("Missed EvalMode case");
- }
-
/// Notes that we failed to evaluate an expression that other expressions
/// directly depend on, and determine if we should keep evaluating. This
/// should only be called if we actually intend to keep evaluating.
@@ -2416,9 +2304,8 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
// In CUDA/HIP device compilation, only device side variables have
// constant addresses.
- if (Info.getASTContext().getLangOpts().CUDA &&
- Info.getASTContext().getLangOpts().CUDAIsDevice &&
- Info.getASTContext().CUDAConstantEvalCtx.NoWrongSidedVars) {
+ if (Info.Ctx.getLangOpts().CUDA && Info.Ctx.getLangOpts().CUDAIsDevice &&
+ Info.Ctx.CUDAConstantEvalCtx.NoWrongSidedVars) {
if ((!Var->hasAttr<CUDADeviceAttr>() &&
!Var->hasAttr<CUDAConstantAttr>() &&
!Var->getType()->isCUDADeviceBuiltinSurfaceType() &&
@@ -6330,7 +6217,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
*Info.CurrentCall, hasSpecificAttr<MSConstexprAttr>(AS->getAttrs()) &&
isa<ReturnStmt>(SS));
- auto LO = Info.getASTContext().getLangOpts();
+ auto LO = Info.Ctx.getLangOpts();
if (LO.CXXAssumptions && !LO.MSVCCompat) {
for (auto *Attr : AS->getAttrs()) {
auto *AA = dyn_cast<CXXAssumeAttr>(Attr);
@@ -6341,7 +6228,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
if (Assumption->isValueDependent())
return ESR_Failed;
- if (Assumption->HasSideEffects(Info.getASTContext()))
+ if (Assumption->HasSideEffects(Info.Ctx))
continue;
bool Value;
@@ -9329,8 +9216,8 @@ class LValueExprEvaluator
bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
bool VisitMemberExpr(const MemberExpr *E);
bool VisitStringLiteral(const StringLiteral *E) {
- return Success(APValue::LValueBase(
- E, 0, Info.getASTContext().getNextStringLiteralVersion()));
+ return Success(
+ APValue::LValueBase(E, 0, Info.Ctx.getNextStringLiteralVersion()));
}
bool VisitObjCEncodeExpr(const ObjCEncodeExpr *E) { return Success(E); }
bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
More information about the cfe-commits
mailing list