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/226217 >From 6a8aa7591252662a5a79a9a34ea256630c9f9053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Thu, 24 Sep 2026 16:08:13 +0200 Subject: [PATCH 1/2] [clang][bytecode] Use EvalSettings struct --- clang/lib/AST/ByteCode/Context.cpp | 114 +++++++++++++------- clang/lib/AST/ByteCode/Context.h | 41 ++++--- clang/lib/AST/ByteCode/EvalEmitter.cpp | 6 ++ clang/lib/AST/ByteCode/EvalEmitter.h | 3 + clang/lib/AST/ByteCode/EvalSettings.h | 35 ++++++ clang/lib/AST/ByteCode/InterpState.cpp | 30 ++++-- clang/lib/AST/ByteCode/InterpState.h | 6 +- clang/lib/AST/ExprConstant.cpp | 143 +++++++++++++++---------- 8 files changed, 260 insertions(+), 118 deletions(-) create mode 100644 clang/lib/AST/ByteCode/EvalSettings.h diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp index 9cdb2725a92040..a08f93335957ef 100644 --- a/clang/lib/AST/ByteCode/Context.cpp +++ b/clang/lib/AST/ByteCode/Context.cpp @@ -12,6 +12,7 @@ #include "Char.h" #include "Compiler.h" #include "EvalEmitter.h" +#include "EvalSettings.h" #include "Integral.h" #include "InterpFrame.h" #include "InterpHelpers.h" @@ -37,7 +38,8 @@ Context::Context(ASTContext &Ctx) : Ctx(Ctx), P(new Program(*this)) { Context::~Context() = default; -bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) { +bool Context::isPotentialConstantExpr(const EvalSettings &Settings, + const FunctionDecl *FD) { assert(Stk.empty()); // Get a function handle. @@ -54,15 +56,16 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) { ++EvalID; // And run it. - return Run(Parent, Func); + return Run(Settings, Func); } -void Context::isPotentialConstantExprUnevaluated(State &Parent, const Expr *E, +void Context::isPotentialConstantExprUnevaluated(const EvalSettings &Settings, + const Expr *E, const FunctionDecl *FD) { assert(Stk.empty()); ++EvalID; size_t StackSizeBefore = Stk.size(); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); if (!C.interpretCall(FD, E)) { C.cleanup(); @@ -99,12 +102,42 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { return true; } -bool Context::evaluate(State &Parent, const Expr *E, APValue &Result, - ConstantExprKind Kind) { +bool Context::evaluateAsRValue(const EvalSettings &Settings, const Expr *E, + APValue &Result) { ++EvalID; bool Recursing = !Stk.empty(); size_t StackSizeBefore = Stk.size(); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc, Kind); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); + + auto Res = C.interpretExpr(E); + + if (Res.isInvalid()) { + C.cleanup(); + Stk.clearTo(StackSizeBefore); + return false; + } + + if (!Recursing) { + // We *can* actually get here with a non-empty stack, since + // things like InterpState::noteSideEffect() exist. + C.cleanup(); +#ifndef NDEBUG + // Make sure we don't rely on some value being still alive in + // InterpStack memory. + Stk.clearTo(StackSizeBefore); +#endif + } + + Result = Res.stealAPValue(); + return true; +} + +bool Context::evaluate(const EvalSettings &Settings, const Expr *E, + APValue &Result, ConstantExprKind Kind) { + ++EvalID; + bool Recursing = !Stk.empty(); + size_t StackSizeBefore = Stk.size(); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc, Kind); auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/false, /*DestroyToplevelScope=*/true); @@ -128,12 +161,13 @@ bool Context::evaluate(State &Parent, const Expr *E, APValue &Result, return true; } -bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, - const Expr *Init, APValue &Result) { +bool Context::evaluateAsInitializer(const EvalSettings &Settings, + const VarDecl *VD, const Expr *Init, + APValue &Result) { ++EvalID; bool Recursing = !Stk.empty(); size_t StackSizeBefore = Stk.size(); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); bool CheckGlobalInitialized = (VD->getType()->isRecordType() || VD->getType()->isArrayType()); @@ -160,10 +194,10 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, return true; } -bool Context::evaluateDestruction(State &Parent, const VarDecl *VD, - APValue Value) { +bool Context::evaluateDestruction(const EvalSettings &Settings, + const VarDecl *VD, APValue Value) { assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); auto Res = C.interpretDestructor(VD, Value); @@ -186,14 +220,15 @@ void Context::registerRedecl(const VarDecl *VD, const APValue &V) { } template <typename ResultT> -bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr, - const Expr *PtrExpr, ResultT &Result) { +bool Context::evaluateStringRepr(const EvalSettings &Settings, + const Expr *SizeExpr, const Expr *PtrExpr, + ResultT &Result) { assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); // Evaluate size value. APValue SizeValue; - if (!evaluateAsRValue(Parent, SizeExpr, SizeValue)) + if (!evaluateAsRValue(Settings, SizeExpr, SizeValue)) return false; if (!SizeValue.isInt()) @@ -269,26 +304,28 @@ bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr, return true; } -bool Context::evaluateCharRange(State &Parent, const Expr *SizeExpr, - const Expr *PtrExpr, APValue &Result) { +bool Context::evaluateCharRange(const EvalSettings &Settings, + const Expr *SizeExpr, const Expr *PtrExpr, + APValue &Result) { assert(SizeExpr); assert(PtrExpr); - return evaluateStringRepr(Parent, SizeExpr, PtrExpr, Result); + return evaluateStringRepr(Settings, SizeExpr, PtrExpr, Result); } -bool Context::evaluateCharRange(State &Parent, const Expr *SizeExpr, - const Expr *PtrExpr, std::string &Result) { +bool Context::evaluateCharRange(const EvalSettings &Settings, + const Expr *SizeExpr, const Expr *PtrExpr, + std::string &Result) { assert(SizeExpr); assert(PtrExpr); - return evaluateStringRepr(Parent, SizeExpr, PtrExpr, Result); + return evaluateStringRepr(Settings, SizeExpr, PtrExpr, Result); } -bool Context::evaluateString(State &Parent, const Expr *E, +bool Context::evaluateString(const EvalSettings &Settings, const Expr *E, std::string &Result) { assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); auto PtrRes = C.interpretAsPointer(E, [&](InterpState &S, CodePtr OpPC, const Pointer &Ptr) { @@ -350,9 +387,10 @@ bool Context::evaluateString(State &Parent, const Expr *E, return true; } -std::optional<uint64_t> Context::evaluateStrlen(State &Parent, const Expr *E) { +std::optional<uint64_t> Context::evaluateStrlen(const EvalSettings &Settings, + const Expr *E) { assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); std::optional<uint64_t> Result; auto PtrRes = C.interpretAsPointer(E, [&](InterpState &S, CodePtr OpPC, @@ -416,12 +454,11 @@ std::optional<uint64_t> Context::evaluateStrlen(State &Parent, const Expr *E) { return Result; } -std::optional<uint64_t> Context::tryEvaluateObjectSize(State &Parent, - const Expr *E, - unsigned Kind, - bool IsDynamic) { +std::optional<uint64_t> +Context::tryEvaluateObjectSize(const EvalSettings &Settings, const Expr *E, + unsigned Kind, bool IsDynamic) { assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); std::optional<uint64_t> Result; auto PtrRes = C.interpretAsLValuePointer(E, [&](InterpState &S, CodePtr OpPC, @@ -448,17 +485,16 @@ std::optional<uint64_t> Context::tryEvaluateObjectSize(State &Parent, return Result; } -std::optional<bool> -Context::evaluateWithSubstitution(State &Parent, const FunctionDecl *Callee, - ArrayRef<const Expr *> Args, const Expr *This, - const Expr *Condition) { +std::optional<bool> Context::evaluateWithSubstitution( + const EvalSettings &Settings, const FunctionDecl *Callee, + ArrayRef<const Expr *> Args, const Expr *This, const Expr *Condition) { if (OptPrimType ConditionT = classify(Condition); !ConditionT || ConditionT != PT_Bool) { return std::nullopt; } assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); std::optional<bool> Result = C.interpretWithSubstitutions(Callee, Args, This, Condition); @@ -599,9 +635,9 @@ const llvm::fltSemantics &Context::getFloatSemantics(QualType T) const { return Ctx.getFloatTypeSemantics(T); } -bool Context::Run(State &Parent, const Function *Func) { +bool Context::Run(const EvalSettings &Settings, const Function *Func) { auto Memory = std::make_unique<char[]>(InterpFrame::allocSize(Func)); - InterpState State(Parent, *P, Stk, FrameAlloc, *this, Func); + InterpState State(Settings, *P, Stk, FrameAlloc, *this, Func); InterpFrame *Frame = new (Memory.get()) InterpFrame( State, Func, /*Caller=*/nullptr, CodePtr(), Func->getArgSize()); State.Current = Frame; diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h index 1390bc280602e4..0565322d3f4e33 100644 --- a/clang/lib/AST/ByteCode/Context.h +++ b/clang/lib/AST/ByteCode/Context.h @@ -32,6 +32,7 @@ class Function; class Program; class State; enum PrimType : uint8_t; +struct EvalSettings; struct ParamOffset { unsigned Offset; @@ -54,37 +55,46 @@ class Context final { ~Context(); /// Checks if a function is a potential constant expression. - bool isPotentialConstantExpr(State &Parent, const FunctionDecl *FD); - void isPotentialConstantExprUnevaluated(State &Parent, const Expr *E, + bool isPotentialConstantExpr(const EvalSettings &Settings, + const FunctionDecl *FD); + void isPotentialConstantExprUnevaluated(const EvalSettings &Settings, + const Expr *E, const FunctionDecl *FD); /// Evaluates a toplevel expression as an rvalue. + // FIXME: Get rid of the version using a parent State. bool evaluateAsRValue(State &Parent, const Expr *E, APValue &Result); + bool evaluateAsRValue(const EvalSettings &Settings, const Expr *E, + APValue &Result); /// Like evaluateAsRvalue(), but does no implicit lvalue-to-rvalue conversion. - bool evaluate(State &Parent, const Expr *E, APValue &Result, + // FIXME: Remove the Kind parameter, it's already in Settings. + bool evaluate(const EvalSettings &Settings, const Expr *E, APValue &Result, ConstantExprKind Kind); /// Evaluates a toplevel initializer. - bool evaluateAsInitializer(State &Parent, const VarDecl *VD, const Expr *Init, - APValue &Result); + bool evaluateAsInitializer(const EvalSettings &Settings, const VarDecl *VD, + const Expr *Init, APValue &Result); void registerRedecl(const VarDecl *VD, const APValue &V); /// Evaluates the destruction of a variable. - bool evaluateDestruction(State &Parent, const VarDecl *VD, APValue Value); + bool evaluateDestruction(const EvalSettings &Settings, const VarDecl *VD, + APValue Value); - bool evaluateCharRange(State &Parent, const Expr *SizeExpr, + bool evaluateCharRange(const EvalSettings &Settings, const Expr *SizeExpr, const Expr *PtrExpr, APValue &Result); - bool evaluateCharRange(State &Parent, const Expr *SizeExpr, + bool evaluateCharRange(const EvalSettings &Settings, const Expr *SizeExpr, const Expr *PtrExpr, std::string &Result); /// Evaluate \param E and if it can be evaluated to a null-terminated string, /// copy the result into \param Result. - bool evaluateString(State &Parent, const Expr *E, std::string &Result); + bool evaluateString(const EvalSettings &Settings, const Expr *E, + std::string &Result); /// Evalute \param E and if it can be evaluated to a string literal, /// run strlen() on it. - std::optional<uint64_t> evaluateStrlen(State &Parent, const Expr *E); + std::optional<uint64_t> evaluateStrlen(const EvalSettings &Settings, + const Expr *E); /// If \param E evaluates to a pointer the number of accessible bytes /// past the pointer is estimated in \param Result as if evaluated by @@ -96,10 +106,11 @@ class Context final { /// as the one referred to by E are considered, when Kind & 1 == 0 /// bytes belonging to the same storage (stack, heap allocation, /// global variable) are considered. - std::optional<uint64_t> tryEvaluateObjectSize(State &Parent, const Expr *E, - unsigned Kind, bool IsDynamic); + std::optional<uint64_t> tryEvaluateObjectSize(const EvalSettings &Settings, + const Expr *E, unsigned Kind, + bool IsDynamic); - std::optional<bool> evaluateWithSubstitution(State &Parent, + std::optional<bool> evaluateWithSubstitution(const EvalSettings &Settings, const FunctionDecl *Callee, ArrayRef<const Expr *> Args, const Expr *This, @@ -192,10 +203,10 @@ class Context final { private: friend class EvalIDScope; /// Runs a function. - bool Run(State &Parent, const Function *Func); + bool Run(const EvalSettings &Settings, const Function *Func); template <typename ResultT> - bool evaluateStringRepr(State &Parent, const Expr *SizeExpr, + bool evaluateStringRepr(const EvalSettings &Settings, const Expr *SizeExpr, const Expr *PtrExpr, ResultT &Result); /// Current compilation context. diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp index ec7550eb4cc17a..caa7f4b28ddd81 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.cpp +++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp @@ -23,6 +23,12 @@ EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent, : Ctx(Ctx), P(P), S(Parent, P, Stk, FA, Ctx, this), EvalResult(Ctx), ConstexprKind(ConstexprKind) {} +EvalEmitter::EvalEmitter(Context &Ctx, Program &P, const EvalSettings &Settings, + InterpStack &Stk, FrameAllocator &FA, + ConstantExprKind ConstexprKind) + : Ctx(Ctx), P(P), S(Settings, P, Stk, FA, Ctx, this), EvalResult(Ctx), + ConstexprKind(ConstexprKind) {} + EvalEmitter::EvalEmitter(Context &Ctx, Program &P, Expr::EvalStatus &Status, InterpStack &Stk, FrameAllocator &FA) : Ctx(Ctx), P(P), S(Status, P, Stk, FA, Ctx, this), EvalResult(Ctx) {} diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h index 70ad775e84f81c..8a4312bad5c589 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.h +++ b/clang/lib/AST/ByteCode/EvalEmitter.h @@ -74,6 +74,9 @@ class EvalEmitter : public SourceMapper { EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk, FrameAllocator &FrameAlloc, ConstantExprKind ConstexprKind = ConstantExprKind::Normal); + EvalEmitter(Context &Ctx, Program &P, const EvalSettings &Settings, + InterpStack &Stk, FrameAllocator &FrameAlloc, + ConstantExprKind ConstexprKind = ConstantExprKind::Normal); EvalEmitter(Context &Ctx, Program &P, Expr::EvalStatus &Status, InterpStack &Stk, FrameAllocator &FrameAlloc); diff --git a/clang/lib/AST/ByteCode/EvalSettings.h b/clang/lib/AST/ByteCode/EvalSettings.h new file mode 100644 index 00000000000000..df4dcda515ded2 --- /dev/null +++ b/clang/lib/AST/ByteCode/EvalSettings.h @@ -0,0 +1,35 @@ +//===--------------------------- EvalSettings.h -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_EVALSETTINGS_H +#define LLVM_CLANG_AST_INTERP_EVALSETTINGS_H + +#include "State.h" + +namespace clang { +namespace interp { + +struct EvalSettings { + Expr::EvalStatus &EvalStatus; + const EvaluationMode EvalMode; + const ConstantExprKind ConstexprKind; + + bool InConstantContext = false; + bool CheckingPotentialConstantExpression = false; + bool CheckingForUndefinedBehavior = false; + + EvalSettings(EvaluationMode EvalMode, Expr::EvalStatus &EvalStatus, + ConstantExprKind ConstexprKind = ConstantExprKind::Normal) + : EvalStatus(EvalStatus), EvalMode(EvalMode), + ConstexprKind(ConstexprKind) {} +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp index 7d0364fa7d10d8..e69880d9e1a87e 100644 --- a/clang/lib/AST/ByteCode/InterpState.cpp +++ b/clang/lib/AST/ByteCode/InterpState.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "InterpState.h" +#include "EvalSettings.h" #include "InterpFrame.h" #include "InterpStack.h" #include "Program.h" @@ -30,19 +31,32 @@ InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; EvalMode = Parent.EvalMode; } +InterpState::InterpState(const EvalSettings &Settings, Program &P, + InterpStack &Stk, FrameAllocator &FrameAlloc, + Context &Ctx, SourceMapper *M) + : State(Ctx.getASTContext(), Settings.EvalStatus), M(M), + FrameAlloc(FrameAlloc), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this), + Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), + InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) { + InConstantContext = Settings.InConstantContext; + CheckingPotentialConstantExpression = + Settings.CheckingPotentialConstantExpression; + CheckingForUndefinedBehavior = Settings.CheckingForUndefinedBehavior; + EvalMode = Settings.EvalMode; +} -InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, - FrameAllocator &FrameAlloc, Context &Ctx, - const Function *Func) - : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(nullptr), +InterpState::InterpState(const EvalSettings &Settings, Program &P, + InterpStack &Stk, FrameAllocator &FrameAlloc, + Context &Ctx, const Function *Func) + : State(Ctx.getASTContext(), Settings.EvalStatus), M(nullptr), FrameAlloc(FrameAlloc), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) { - InConstantContext = Parent.InConstantContext; + InConstantContext = Settings.InConstantContext; CheckingPotentialConstantExpression = - Parent.CheckingPotentialConstantExpression; - CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; - EvalMode = Parent.EvalMode; + Settings.CheckingPotentialConstantExpression; + CheckingForUndefinedBehavior = Settings.CheckingForUndefinedBehavior; + EvalMode = Settings.EvalMode; } InterpState::InterpState(Expr::EvalStatus &Status, Program &P, InterpStack &Stk, diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h index b4f89e9583d972..eeb010ed216496 100644 --- a/clang/lib/AST/ByteCode/InterpState.h +++ b/clang/lib/AST/ByteCode/InterpState.h @@ -27,6 +27,7 @@ namespace clang { namespace interp { class Context; class SourceMapper; +struct EvalSettings; struct StdAllocatorCaller { @@ -48,8 +49,11 @@ class InterpState final : public State { InterpState(const State &Parent, Program &P, InterpStack &Stk, FrameAllocator &FrameAlloc, Context &Ctx, SourceMapper *M = nullptr); + InterpState(const EvalSettings &Settings, Program &P, InterpStack &Stk, + FrameAllocator &FrameAlloc, Context &Ctx, + SourceMapper *M = nullptr); - InterpState(const State &Parent, Program &P, InterpStack &Stk, + InterpState(const EvalSettings &Settings, Program &P, InterpStack &Stk, FrameAllocator &FA, Context &Ctx, const Function *Func); InterpState(Expr::EvalStatus &Status, Program &P, InterpStack &Stk, diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index dea859382918b0..e90f86e6279856 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -33,6 +33,7 @@ //===----------------------------------------------------------------------===// #include "ByteCode/Context.h" +#include "ByteCode/EvalSettings.h" #include "ByteCode/Frame.h" #include "ByteCode/State.h" #include "ExprConstShared.h" @@ -22001,16 +22002,19 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx, "Expression evaluator can't be called on a dependent expression."); ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsLValue"); + + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantFold, Result); + Settings.InConstantContext = InConstantContext; + return Ctx.getInterpContext().evaluate(Settings, this, Result.Val, + ConstantExprKind::Normal); + } + EvalInfo Info(Ctx, Result, EvaluationMode::ConstantFold); Info.InConstantContext = InConstantContext; LValue LV; CheckedTemporaries CheckedTemps; - if (Info.EnableNewConstInterp) { - return Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, - ConstantExprKind::Normal); - } - if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() || Result.HasSideEffects || !CheckLValueConstantExpression(Info, getExprLoc(), @@ -22056,13 +22060,16 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, return true; ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsConstantExpr"); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantExpression, Result); + Settings.InConstantContext = true; + return Ctx.getInterpContext().evaluate(Settings, this, Result.Val, Kind); + } + EvaluationMode EM = EvaluationMode::ConstantExpression; EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; - if (Info.EnableNewConstInterp) - return Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, Kind); - // The type of the object we're initializing is 'const T' for a class NTTP. QualType T = getType(); if (Kind == ConstantExprKind::ClassTemplateArgument) @@ -22122,6 +22129,21 @@ bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD, return Name; }); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings( + (IsConstantInitialization && + (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23)) + ? EvaluationMode::ConstantExpression + : EvaluationMode::ConstantFold, + EStatus); + Settings.InConstantContext = IsConstantInitialization; + return Ctx.getInterpContext().evaluateAsInitializer(Settings, VD, this, + EStatus.Val); + } + + SourceLocation DeclLoc = VD->getLocation(); + QualType DeclTy = VD->getType(); + EvalInfo Info(Ctx, EStatus, (IsConstantInitialization && (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23)) @@ -22130,14 +22152,6 @@ bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD, Info.setEvaluatingDecl(VD, EStatus.Val); Info.InConstantContext = IsConstantInitialization; - SourceLocation DeclLoc = VD->getLocation(); - QualType DeclTy = VD->getType(); - - if (Info.EnableNewConstInterp) { - auto &InterpCtx = Ctx.getInterpContext(); - return InterpCtx.evaluateAsInitializer(Info, VD, this, EStatus.Val); - } - LValue LVal; LVal.set(VD); @@ -22202,11 +22216,12 @@ bool VarDecl::evaluateDestruction( return false; if (Ctx.getLangOpts().EnableNewConstInterp) { - EvalInfo Info(Ctx, EStatus, - IsConstantDestruction ? EvaluationMode::ConstantExpression - : EvaluationMode::ConstantFold); - Info.InConstantContext = IsConstantDestruction; - if (!Ctx.getInterpContext().evaluateDestruction(Info, this, + interp::EvalSettings Settings(IsConstantDestruction + ? EvaluationMode::ConstantExpression + : EvaluationMode::ConstantFold, + EStatus); + Settings.InConstantContext = IsConstantDestruction; + if (!Ctx.getInterpContext().evaluateDestruction(Settings, this, std::move(DestroyedValue))) return false; ensureEvaluatedStmt()->HasConstantDestruction = true; @@ -22905,19 +22920,23 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, }); Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpressionUnevaluated); - Info.InConstantContext = true; - if (Info.EnableNewConstInterp) { + if (Ctx.getLangOpts().EnableNewConstInterp) { + auto Settings = interp::EvalSettings( + EvaluationMode::ConstantExpressionUnevaluated, Status); + Settings.InConstantContext = true; if (std::optional<bool> BoolResult = - Info.Ctx.getInterpContext().evaluateWithSubstitution( - Info, Callee, Args, This, this)) { + Ctx.getInterpContext().evaluateWithSubstitution(Settings, Callee, + Args, This, this)) { Value = APValue(APSInt(APInt(1, static_cast<uint64_t>(*BoolResult)))); return true; } return false; } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpressionUnevaluated); + Info.InConstantContext = true; + LValue ThisVal; const LValue *ThisPtr = nullptr; if (This) { @@ -22988,20 +23007,23 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, return Name; }); + const ASTContext &Ctx = FD->getASTContext(); Expr::EvalStatus Status; Status.Diag = &Diags; - EvalInfo Info(FD->getASTContext(), Status, - EvaluationMode::ConstantExpression); - Info.InConstantContext = true; - Info.CheckingPotentialConstantExpression = true; - // The constexpr VM attempts to compile all methods to bytecode here. - if (Info.EnableNewConstInterp) { - Info.Ctx.getInterpContext().isPotentialConstantExpr(Info, FD); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantExpression, Status); + Settings.InConstantContext = true; + Settings.CheckingPotentialConstantExpression = true; + Ctx.getInterpContext().isPotentialConstantExpr(Settings, FD); return Diags.empty(); } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); + Info.InConstantContext = true; + Info.CheckingPotentialConstantExpression = true; + const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD); const CXXRecordDecl *RD = MD ? MD->getParent()->getCanonicalDecl() : nullptr; @@ -23038,19 +23060,23 @@ bool Expr::isPotentialConstantExprUnevaluated(Expr *E, assert(!E->isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + const ASTContext &Ctx = FD->getASTContext(); Expr::EvalStatus Status; Status.Diag = &Diags; - EvalInfo Info(FD->getASTContext(), Status, - EvaluationMode::ConstantExpressionUnevaluated); - Info.InConstantContext = true; - Info.CheckingPotentialConstantExpression = true; - - if (Info.EnableNewConstInterp) { - Info.Ctx.getInterpContext().isPotentialConstantExprUnevaluated(Info, E, FD); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantExpressionUnevaluated, + Status); + Settings.InConstantContext = true; + Settings.CheckingPotentialConstantExpression = true; + Ctx.getInterpContext().isPotentialConstantExprUnevaluated(Settings, E, FD); return Diags.empty(); } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpressionUnevaluated); + Info.InConstantContext = true; + Info.CheckingPotentialConstantExpression = true; + // Fabricate a call stack frame to give the arguments a plausible cover story. CallStackFrame Frame(Info, SourceLocation(), FD, /*This=*/nullptr, /*CallExpr=*/nullptr, CallRef()); @@ -23066,12 +23092,13 @@ std::optional<uint64_t> Expr::tryEvaluateObjectSize(const ASTContext &Ctx, return std::nullopt; Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); - if (Info.EnableNewConstInterp) - return Info.Ctx.getInterpContext().tryEvaluateObjectSize( - Info, this, Type, - /*IsDynamic=*/false); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantFold, Status); + return Ctx.getInterpContext().tryEvaluateObjectSize(Settings, this, Type, + /*IsDynamic=*/false); + } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); return tryEvaluateBuiltinObjectSize(this, Type, Info); } @@ -23122,15 +23149,16 @@ EvaluateBuiltinStrLen(const Expr *E, EvalInfo &Info, std::optional<std::string> Expr::tryEvaluateString(ASTContext &Ctx) const { Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); std::string StringResult; - if (Info.EnableNewConstInterp) { - if (!Info.Ctx.getInterpContext().evaluateString(Info, this, StringResult)) + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantFold, Status); + if (!Ctx.getInterpContext().evaluateString(Settings, this, StringResult)) return std::nullopt; return StringResult; } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); if (EvaluateBuiltinStrLen(this, Info, &StringResult)) return StringResult; return std::nullopt; @@ -23142,13 +23170,16 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result, const Expr *PtrExpression, ASTContext &Ctx, Expr::EvalResult &Status) { + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantExpression, Status); + Settings.InConstantContext = true; + return Ctx.getInterpContext().evaluateCharRange(Settings, SizeExpression, + PtrExpression, Result); + } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); Info.InConstantContext = true; - if (Info.EnableNewConstInterp) - return Info.Ctx.getInterpContext().evaluateCharRange(Info, SizeExpression, - PtrExpression, Result); - LValue String; FullExpressionRAII Scope(Info); APSInt SizeValue; @@ -23210,10 +23241,12 @@ bool Expr::EvaluateCharRangeAsString(APValue &Result, std::optional<uint64_t> Expr::tryEvaluateStrLen(const ASTContext &Ctx) const { Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); - if (Info.EnableNewConstInterp) - return Info.Ctx.getInterpContext().evaluateStrlen(Info, this); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantFold, Status); + return Ctx.getInterpContext().evaluateStrlen(Settings, this); + } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); return EvaluateBuiltinStrLen(this, Info); } >From a42fa447c80f1b55ed2bc50900165d297f3454e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Thu, 24 Sep 2026 16:22:09 +0200 Subject: [PATCH 2/2] Direct eval in accessor --- clang/lib/AST/ExprConstant.cpp | 143 ++++++++++++++++++++++++++++----- 1 file changed, 123 insertions(+), 20 deletions(-) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index e90f86e6279856..726e9edf73639f 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -780,10 +780,6 @@ namespace { /// we will evaluate. unsigned StepsLeft; - /// Enable the experimental new constant interpreter. If an expression is - /// not supported by the interpreter, an error is triggered. - bool EnableNewConstInterp; - /// BottomFrame - The frame in which evaluation started. This must be /// initialized after CurrentCall and CallStackDepth. CallStackFrame BottomFrame; @@ -888,7 +884,6 @@ namespace { : State(const_cast<ASTContext &>(C), S), CurrentCall(nullptr), CallStackDepth(0), NextCallIndex(1), StepsLeft(C.getLangOpts().ConstexprStepLimit), - EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp), BottomFrame(*this, SourceLocation(), /*Callee=*/nullptr, /*This=*/nullptr, /*CallExpr=*/nullptr, CallRef()), @@ -21799,6 +21794,7 @@ static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This, /// lvalue-to-rvalue cast if it is an lvalue. static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { assert(!E->isValueDependent()); + assert(!Info.Ctx.getLangOpts().EnableNewConstInterp); if (E->getType().isNull()) return false; @@ -21806,9 +21802,6 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { if (!CheckLiteralType(Info, E)) return false; - if (Info.EnableNewConstInterp) - return Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result); - if (!::Evaluate(Result, Info, E)) return false; @@ -21939,6 +21932,17 @@ bool Expr::EvaluateAsRValue(EvalResult &Result, const ASTContext &Ctx, assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsRValue"); + + bool IsConst; + if (FastEvaluateAsRValue(this, Result.Val, Ctx, IsConst)) + return IsConst; + + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::IgnoreSideEffects, Result); + Settings.InConstantContext = InConstantContext; + return Ctx.getInterpContext().evaluateAsRValue(Settings, this, Result.Val); + } + EvalInfo Info(Ctx, Result, EvaluationMode::IgnoreSideEffects); Info.InConstantContext = InConstantContext; return ::EvaluateAsRValue(this, Result, Ctx, Info); @@ -21959,7 +21963,28 @@ bool Expr::EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, bool InConstantContext) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsInt"); + + if (!getType()->isIntegralOrEnumerationType()) + return false; + + bool IsConst; + if (FastEvaluateAsRValue(this, Result.Val, Ctx, IsConst)) + return IsConst; + + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::IgnoreSideEffects, Result); + Settings.InConstantContext = InConstantContext; + if (!Ctx.getInterpContext().evaluateAsRValue(Settings, this, Result.Val)) + return false; + + if (!Result.Val.isInt() || + hasUnacceptableSideEffect(Result, AllowSideEffects)) + return false; + return true; + } + EvalInfo Info(Ctx, Result, EvaluationMode::IgnoreSideEffects); Info.InConstantContext = InConstantContext; return ::EvaluateAsInt(this, Result, Ctx, AllowSideEffects, Info); @@ -21971,6 +21996,24 @@ bool Expr::EvaluateAsFixedPoint(EvalResult &Result, const ASTContext &Ctx, assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsFixedPoint"); + + if (!getType()->isFixedPointType()) + return false; + + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::IgnoreSideEffects, Result); + Settings.InConstantContext = InConstantContext; + + if (!Ctx.getInterpContext().evaluateAsRValue(Settings, this, Result.Val)) + return false; + + if (!Result.Val.isFixedPoint() || + hasUnacceptableSideEffect(Result, AllowSideEffects)) + return false; + + return true; + } + EvalInfo Info(Ctx, Result, EvaluationMode::IgnoreSideEffects); Info.InConstantContext = InConstantContext; return ::EvaluateAsFixedPoint(this, Result, Ctx, AllowSideEffects, Info); @@ -22254,6 +22297,18 @@ APSInt Expr::EvaluateKnownConstInt(const ASTContext &Ctx) const { ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateKnownConstInt"); EvalResult EVResult; + + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::IgnoreSideEffects, EVResult); + Settings.InConstantContext = true; + [[maybe_unused]] bool Result = + Ctx.getInterpContext().evaluateAsRValue(Settings, this, EVResult.Val); + assert(Result && "Could not evaluate expression"); + assert(EVResult.Val.isInt() && "Expression did not evaluate to integer"); + + return EVResult.Val.getInt(); + } + EvalInfo Info(Ctx, EVResult, EvaluationMode::IgnoreSideEffects); Info.InConstantContext = true; @@ -22273,6 +22328,19 @@ APSInt Expr::EvaluateKnownConstIntCheckOverflow( ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateKnownConstIntCheckOverflow"); EvalResult EVResult; EVResult.Diag = Diag; + + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::IgnoreSideEffects, EVResult); + Settings.InConstantContext = true; + Settings.CheckingForUndefinedBehavior = true; + [[maybe_unused]] bool Result = + Ctx.getInterpContext().evaluateAsRValue(Settings, this, EVResult.Val); + assert(Result && "Could not evaluate expression"); + assert(EVResult.Val.isInt() && "Expression did not evaluate to integer"); + + return EVResult.Val.getInt(); + } + EvalInfo Info(Ctx, EVResult, EvaluationMode::IgnoreSideEffects); Info.InConstantContext = true; Info.CheckingForUndefinedBehavior = true; @@ -22292,11 +22360,19 @@ void Expr::EvaluateForOverflow(const ASTContext &Ctx) const { ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateForOverflow"); bool IsConst; EvalResult EVResult; - if (!FastEvaluateAsRValue(this, EVResult.Val, Ctx, IsConst)) { - EvalInfo Info(Ctx, EVResult, EvaluationMode::IgnoreSideEffects); - Info.CheckingForUndefinedBehavior = true; - (void)::EvaluateAsRValue(Info, this, EVResult.Val); + if (FastEvaluateAsRValue(this, EVResult.Val, Ctx, IsConst)) + return; + + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::IgnoreSideEffects, EVResult); + Settings.CheckingForUndefinedBehavior = true; + (void)Ctx.getInterpContext().evaluateAsRValue(Settings, this, EVResult.Val); + return; } + + EvalInfo Info(Ctx, EVResult, EvaluationMode::IgnoreSideEffects); + Info.CheckingForUndefinedBehavior = true; + (void)::EvaluateAsRValue(Info, this, EVResult.Val); } bool Expr::EvalResult::isGlobalLValue() const { @@ -22346,6 +22422,16 @@ static ICEDiag Worst(ICEDiag A, ICEDiag B) { return A.Kind >= B.Kind ? A : B; } static ICEDiag CheckEvalInICE(const Expr* E, const ASTContext &Ctx) { Expr::EvalResult EVResult; + + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantExpression, EVResult); + Settings.InConstantContext = true; + if (!Ctx.getInterpContext().evaluateAsRValue(Settings, E, EVResult.Val) || + EVResult.HasSideEffects || !EVResult.Val.isInt()) + return ICEDiag(IK_NotICE, E->getBeginLoc()); + return NoDiag(); + } + Expr::EvalStatus Status; EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); @@ -22855,6 +22941,17 @@ Expr::getIntegerConstantExpr(const ASTContext &Ctx, // required to treat the expression as an ICE, so we produce the folded // value. EvalResult ExprResult; + + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::IgnoreSideEffects, + ExprResult); + Settings.InConstantContext = true; + if (!Ctx.getInterpContext().evaluateAsRValue(Settings, this, + ExprResult.Val)) + llvm_unreachable("ICE cannot be evaluated!"); + return ExprResult.Val.getInt(); + } + Expr::EvalStatus Status; EvalInfo Info(Ctx, Status, EvaluationMode::IgnoreSideEffects); Info.InConstantContext = true; @@ -22889,18 +22986,24 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result, return true; } - // Build evaluation settings. + bool IsConstExpr; Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); SmallVector<PartialDiagnosticAt> MSRelaxedDiag; Status.ExtendedDiag = AllowRelaxedEval ? &MSRelaxedDiag : nullptr; - bool IsConstExpr = - ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch) && - // NOTE: We don't produce a diagnostic for this, but the callers that - // call us on arbitrary full-expressions should generally not care. - Info.discardCleanups() && !Status.HasSideEffects; - + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantExpression, Status); + IsConstExpr = Ctx.getInterpContext().evaluateAsRValue( + Settings, this, Result ? *Result : Scratch); + } else { + // Build evaluation settings. + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); + IsConstExpr = + ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch) && + // NOTE: We don't produce a diagnostic for this, but the callers that + // call us on arbitrary full-expressions should generally not care. + Info.discardCleanups() && !Status.HasSideEffects; + } return IsConstExpr && !Status.DiagEmitted; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
