[clang] [clang][ExprConst] Move shared `EvalInfo` state into `interp::State` (PR #177738)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 31 07:54:02 PST 2026
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/177738 at github.com>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/177738
>From b5a702ece74dd1f53ebbb3d74497b5bb788fc081 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 1/2] State
---
clang/lib/AST/ByteCode/InterpState.cpp | 13 ++-
clang/lib/AST/ByteCode/InterpState.h | 37 +------
clang/lib/AST/ByteCode/State.cpp | 74 ++++++++++---
clang/lib/AST/ByteCode/State.h | 84 +++++++++++---
clang/lib/AST/ExprConstant.cpp | 145 +++----------------------
5 files changed, 156 insertions(+), 197 deletions(-)
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp
index a95916cd63981..837d5fef91b8e 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -17,10 +17,10 @@
using namespace clang;
using namespace clang::interp;
-InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
+InterpState::InterpState(const 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;
@@ -28,9 +28,10 @@ InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
EvalMode = Parent.EvalMode;
}
-InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
+InterpState::InterpState(const 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;
@@ -75,7 +76,7 @@ void InterpState::cleanup() {
Alloc->cleanup();
}
-Frame *InterpState::getCurrentFrame() { return Current; }
+const Frame *InterpState::getCurrentFrame() { return Current; }
void InterpState::deallocate(Block *B) {
assert(B);
diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h
index 231b51124c70d..98dc5cfd3b3c4 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -35,9 +35,9 @@ struct StdAllocatorCaller {
/// Interpreter context.
class InterpState final : public State, public SourceMapper {
public:
- InterpState(State &Parent, Program &P, InterpStack &Stk, Context &Ctx,
+ InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx,
SourceMapper *M = nullptr);
- InterpState(State &Parent, Program &P, InterpStack &Stk, Context &Ctx,
+ InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx,
const Function *Func);
~InterpState();
@@ -50,41 +50,14 @@ class InterpState final : public State, public SourceMapper {
bool diagnosing() const { return getEvalStatus().Diag != nullptr; }
// Stack frame accessors.
- Frame *getCurrentFrame() override;
+ const Frame *getCurrentFrame() override;
unsigned getCallStackDepth() override {
return Current ? (Current->getDepth() + 1) : 1;
}
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();
- }
-
- // 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);
@@ -149,8 +122,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..f1192f1d07ebd 100644
--- a/clang/lib/AST/ByteCode/State.cpp
+++ b/clang/lib/AST/ByteCode/State.cpp
@@ -25,15 +25,15 @@ OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId,
OptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId,
unsigned ExtraNotes) {
- if (getEvalStatus().Diag)
+ if (EvalStatus.Diag)
return diag(E->getExprLoc(), DiagId, ExtraNotes, false);
setActiveDiagnostic(false);
return OptionalDiagnostic();
}
-OptionalDiagnostic State::FFDiag(const SourceInfo &SI, diag::kind DiagId,
+OptionalDiagnostic State::FFDiag(SourceInfo SI, diag::kind DiagId,
unsigned ExtraNotes) {
- if (getEvalStatus().Diag)
+ if (EvalStatus.Diag)
return diag(SI.getLoc(), DiagId, ExtraNotes, false);
setActiveDiagnostic(false);
return OptionalDiagnostic();
@@ -43,7 +43,7 @@ OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,
unsigned ExtraNotes) {
// Don't override a previous diagnostic. Don't bother collecting
// diagnostics if we're evaluating for overflow.
- if (!getEvalStatus().Diag || !getEvalStatus().Diag->empty()) {
+ if (!EvalStatus.Diag || !EvalStatus.Diag->empty()) {
setActiveDiagnostic(false);
return OptionalDiagnostic();
}
@@ -55,7 +55,7 @@ OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId,
return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes);
}
-OptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId,
+OptionalDiagnostic State::CCEDiag(SourceInfo SI, diag::kind DiagId,
unsigned ExtraNotes) {
return CCEDiag(SI.getLoc(), DiagId, ExtraNotes);
}
@@ -68,31 +68,29 @@ OptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) {
void State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
if (hasActiveDiagnostic())
- llvm::append_range(*getEvalStatus().Diag, Diags);
+ llvm::append_range(*EvalStatus.Diag, 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());
- getEvalStatus().Diag->push_back(std::make_pair(Loc, PD));
- return getEvalStatus().Diag->back().second;
+ PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator());
+ EvalStatus.Diag->push_back(std::make_pair(Loc, PD));
+ return EvalStatus.Diag->back().second;
}
OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,
unsigned ExtraNotes, bool IsCCEDiag) {
- Expr::EvalStatus &EvalStatus = getEvalStatus();
if (EvalStatus.Diag) {
if (hasPriorDiagnostic()) {
return OptionalDiagnostic();
}
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 +156,53 @@ void State::addCallStack(unsigned Limit) {
<< Out.str() << CallRange;
}
}
+
+bool State::hasPriorDiagnostic() {
+ 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;
+}
+
+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");
+}
+
+bool State::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");
+}
diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h
index 2d30ff4596cc7..c0d32fba7fd9b 100644
--- a/clang/lib/AST/ByteCode/State.h
+++ b/clang/lib/AST/ByteCode/State.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_AST_INTERP_STATE_H
#define LLVM_CLANG_AST_INTERP_STATE_H
+#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/Expr.h"
#include "clang/AST/OptionalDiagnostic.h"
@@ -79,21 +80,40 @@ 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 *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; }
+ ASTContext &getASTContext() const { return Ctx; }
+ const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
+
+ /// Note that we have had a side-effect, and determine whether we should
+ /// keep evaluating.
+ bool noteSideEffect() const {
+ EvalStatus.HasSideEffects = true;
+ return keepEvaluatingAfterSideEffect();
+ }
+
+ /// 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;
+
+ /// 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() const {
+ EvalStatus.HasUndefinedBehavior = true;
+ return keepEvaluatingAfterUndefinedBehavior();
+ }
/// Are we checking whether the expression is a potential constant
/// expression?
@@ -105,8 +125,6 @@ class State {
return CheckingForUndefinedBehavior;
}
-public:
- State() = default;
/// Diagnose that the evaluation could not be folded (FF => FoldFailure)
OptionalDiagnostic
FFDiag(SourceLocation Loc,
@@ -119,7 +137,7 @@ class State {
unsigned ExtraNotes = 0);
OptionalDiagnostic
- FFDiag(const SourceInfo &SI,
+ FFDiag(SourceInfo SI,
diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
unsigned ExtraNotes = 0);
@@ -139,7 +157,7 @@ class State {
unsigned ExtraNotes = 0);
OptionalDiagnostic
- CCEDiag(const SourceInfo &SI,
+ CCEDiag(SourceInfo SI,
diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
unsigned ExtraNotes = 0);
@@ -170,14 +188,50 @@ class State {
bool CheckingForUndefinedBehavior = false;
EvaluationMode EvalMode;
+ ASTContext &Ctx;
+ Expr::EvalStatus &EvalStatus;
private:
+ /// 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;
+
void addCallStack(unsigned Limit);
PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId);
OptionalDiagnostic diag(SourceLocation Loc, diag::kind DiagId,
unsigned ExtraNotes, bool IsCCEDiag);
+
+ /// Should we continue evaluation after encountering undefined behavior?
+ bool keepEvaluatingAfterUndefinedBehavior() const {
+ switch (EvalMode) {
+ case EvaluationMode::IgnoreSideEffects:
+ case EvaluationMode::ConstantFold:
+ return true;
+
+ case EvaluationMode::ConstantExpression:
+ case EvaluationMode::ConstantExpressionUnevaluated:
+ return checkingForUndefinedBehavior();
+ }
+ llvm_unreachable("Missed EvalMode case");
+ }
+
+ // 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();
+
+ void setFoldFailureDiagnostic(bool Flag) { HasFoldFailureDiagnostic = Flag; };
+ void setActiveDiagnostic(bool Flag) { HasActiveDiagnostic = Flag; };
+ bool hasActiveDiagnostic() const { return HasActiveDiagnostic; }
};
} // namespace interp
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 58748fa5ba49b..91d0c655abfc6 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,9 +931,6 @@ namespace {
discardCleanups();
}
- ASTContext &getASTContext() const override { return Ctx; }
- const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
-
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,
EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) {
EvaluatingDecl = Base;
@@ -990,7 +973,7 @@ namespace {
// We use the number of constexpr steps as a proxy for the maximum size
// of arrays to avoid exhausting the system resources, as initialization
// of each element is likely to take some number of steps anyway.
- uint64_t Limit = Ctx.getLangOpts().ConstexprStepLimit;
+ uint64_t Limit = getLangOpts().ConstexprStepLimit;
if (Limit != 0 && ElemCount > Limit) {
if (Diag)
FFDiag(Loc, diag::note_constexpr_new_exceeds_limits)
@@ -1017,7 +1000,7 @@ namespace {
}
bool nextStep(const Stmt *S) {
- if (Ctx.getLangOpts().ConstexprStepLimit == 0)
+ if (getLangOpts().ConstexprStepLimit == 0)
return true;
if (!StepsLeft) {
@@ -1101,110 +1084,13 @@ namespace {
}
private:
- interp::Frame *getCurrentFrame() override { return CurrentCall; }
+ const 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.
@@ -2412,9 +2298,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.getLangOpts().CUDA && Info.getLangOpts().CUDAIsDevice &&
+ Info.Ctx.CUDAConstantEvalCtx.NoWrongSidedVars) {
if ((!Var->hasAttr<CUDADeviceAttr>() &&
!Var->hasAttr<CUDAConstantAttr>() &&
!Var->getType()->isCUDADeviceBuiltinSurfaceType() &&
@@ -2767,7 +2652,7 @@ static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E,
/// So return "tonearest" mode instead of "dynamic".
static llvm::RoundingMode getActiveRoundingMode(EvalInfo &Info, const Expr *E) {
llvm::RoundingMode RM =
- E->getFPFeaturesInEffect(Info.Ctx.getLangOpts()).getRoundingMode();
+ E->getFPFeaturesInEffect(Info.getLangOpts()).getRoundingMode();
if (RM == llvm::RoundingMode::Dynamic)
RM = llvm::RoundingMode::NearestTiesToEven;
return RM;
@@ -2781,7 +2666,7 @@ static bool checkFloatingPointResult(EvalInfo &Info, const Expr *E,
if (Info.InConstantContext)
return true;
- FPOptions FPO = E->getFPFeaturesInEffect(Info.Ctx.getLangOpts());
+ FPOptions FPO = E->getFPFeaturesInEffect(Info.getLangOpts());
if ((St & APFloat::opInexact) &&
FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) {
// Inexact result means that it depends on rounding mode. If the requested
@@ -6326,7 +6211,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);
@@ -6337,7 +6222,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;
@@ -9325,8 +9210,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);
>From fe5c66c22e68d28eaf8b6f2eb58d8ff3b731b8ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sat, 31 Jan 2026 16:53:34 +0100
Subject: [PATCH 2/2] Move keepEvaluatingAfterUndefinedBehavior to source file
---
clang/lib/AST/ByteCode/State.cpp | 13 +++++++++++++
clang/lib/AST/ByteCode/State.h | 13 +------------
2 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/clang/lib/AST/ByteCode/State.cpp b/clang/lib/AST/ByteCode/State.cpp
index f1192f1d07ebd..00e3b1a331172 100644
--- a/clang/lib/AST/ByteCode/State.cpp
+++ b/clang/lib/AST/ByteCode/State.cpp
@@ -206,3 +206,16 @@ bool State::keepEvaluatingAfterSideEffect() const {
}
llvm_unreachable("Missed EvalMode case");
}
+
+bool State::keepEvaluatingAfterUndefinedBehavior() const {
+ switch (EvalMode) {
+ case EvaluationMode::IgnoreSideEffects:
+ case EvaluationMode::ConstantFold:
+ return true;
+
+ case EvaluationMode::ConstantExpression:
+ case EvaluationMode::ConstantExpressionUnevaluated:
+ return checkingForUndefinedBehavior();
+ }
+ llvm_unreachable("Missed EvalMode case");
+}
diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h
index c0d32fba7fd9b..a720033c7914b 100644
--- a/clang/lib/AST/ByteCode/State.h
+++ b/clang/lib/AST/ByteCode/State.h
@@ -208,18 +208,7 @@ class State {
unsigned ExtraNotes, bool IsCCEDiag);
/// Should we continue evaluation after encountering undefined behavior?
- bool keepEvaluatingAfterUndefinedBehavior() const {
- switch (EvalMode) {
- case EvaluationMode::IgnoreSideEffects:
- case EvaluationMode::ConstantFold:
- return true;
-
- case EvaluationMode::ConstantExpression:
- case EvaluationMode::ConstantExpressionUnevaluated:
- return checkingForUndefinedBehavior();
- }
- llvm_unreachable("Missed EvalMode case");
- }
+ bool keepEvaluatingAfterUndefinedBehavior() const;
// If we have a prior diagnostic, it will be noting that the expression
// isn't a constant expression. This diagnostic is more important,
More information about the cfe-commits
mailing list