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?= <[email protected]> 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); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
