[clang] [clang][bytecode] Use bytecode interpreter in toplevel `Expr::Evaluate*` functions (PR #226217)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 25 04:04:24 PDT 2026
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/226217 at github.com>
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?= <tbaeder at redhat.com>
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 6c8601cbfdbb24b1a5c7b7eae25f10b826a083d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 24 Sep 2026 16:22:09 +0200
Subject: [PATCH 2/2] Direct eval in accessor
---
clang/lib/AST/ByteCode/Context.cpp | 29 -----
clang/lib/AST/ByteCode/Context.h | 2 -
clang/lib/AST/ByteCode/EvalEmitter.cpp | 6 --
clang/lib/AST/ByteCode/EvalEmitter.h | 3 -
clang/lib/AST/ByteCode/InterpState.cpp | 13 ---
clang/lib/AST/ByteCode/InterpState.h | 3 -
clang/lib/AST/ExprConstant.cpp | 143 +++++++++++++++++++++----
7 files changed, 123 insertions(+), 76 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index a08f93335957ef..0456ec08d27f7d 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -73,35 +73,6 @@ void Context::isPotentialConstantExprUnevaluated(const EvalSettings &Settings,
}
}
-bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
- ++EvalID;
- bool Recursing = !Stk.empty();
- size_t StackSizeBefore = Stk.size();
- Compiler<EvalEmitter> C(*this, *P, Parent, 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::evaluateAsRValue(const EvalSettings &Settings, const Expr *E,
APValue &Result) {
++EvalID;
diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h
index 0565322d3f4e33..8cba610a725bc3 100644
--- a/clang/lib/AST/ByteCode/Context.h
+++ b/clang/lib/AST/ByteCode/Context.h
@@ -62,8 +62,6 @@ class Context final {
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);
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index caa7f4b28ddd81..10f8d58ad31aaa 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -17,12 +17,6 @@
using namespace clang;
using namespace clang::interp;
-EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
- InterpStack &Stk, FrameAllocator &FA,
- ConstantExprKind ConstexprKind)
- : 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)
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h
index 8a4312bad5c589..653f5384c91892 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.h
+++ b/clang/lib/AST/ByteCode/EvalEmitter.h
@@ -71,9 +71,6 @@ class EvalEmitter : public SourceMapper {
}
protected:
- 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);
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp
index e69880d9e1a87e..8592998cc721ad 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -18,19 +18,6 @@
using namespace clang;
using namespace clang::interp;
-InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
- FrameAllocator &FrameAlloc, Context &Ctx,
- SourceMapper *M)
- : State(Ctx.getASTContext(), Parent.getEvalStatus()), 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 = Parent.InConstantContext;
- CheckingPotentialConstantExpression =
- Parent.CheckingPotentialConstantExpression;
- CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior;
- EvalMode = Parent.EvalMode;
-}
InterpState::InterpState(const EvalSettings &Settings, Program &P,
InterpStack &Stk, FrameAllocator &FrameAlloc,
Context &Ctx, SourceMapper *M)
diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h
index eeb010ed216496..eb290584285db6 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -46,9 +46,6 @@ enum class EvaluationKind : uint8_t {
/// Interpreter context.
class InterpState final : public State {
public:
- 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);
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;
}
More information about the cfe-commits
mailing list