[clang] [clang][bytecode] Add `EvalSettings` struct and replace parent `State` (PR #226175)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 24 07:10:28 PDT 2026


Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID: <llvm.org/llvm/llvm-project/pull/226175 at github.com>
In-Reply-To:


https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/226175

Once https://github.com/llvm/llvm-project/pull/186045 is merged.

>From 70b3efc6edd08272ff6fd8afb93f1ff5bea18f44 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 12 Mar 2026 06:05:56 +0100
Subject: [PATCH 1/2] 2

---
 clang/lib/AST/ByteCode/ByteCodeEmitter.h      |   1 +
 clang/lib/AST/ByteCode/Compiler.cpp           |   6 +
 clang/lib/AST/ByteCode/Context.cpp            |   7 +-
 clang/lib/AST/ByteCode/Disasm.cpp             |   5 +-
 clang/lib/AST/ByteCode/EvalEmitter.cpp        |  70 ++--
 clang/lib/AST/ByteCode/EvalEmitter.h          |  17 +-
 clang/lib/AST/ByteCode/EvaluationResult.cpp   | 382 +++++++++++++++++-
 clang/lib/AST/ByteCode/EvaluationResult.h     |  32 +-
 clang/lib/AST/ByteCode/Interp.cpp             |   4 +-
 .../AST/ByteCode/InterpBuiltinObjectSize.cpp  |   2 +-
 clang/lib/AST/ByteCode/Pointer.cpp            |   6 +-
 clang/lib/AST/ByteCode/Pointer.h              |  11 +-
 clang/lib/AST/ExprConstShared.h               |  42 ++
 clang/lib/AST/ExprConstant.cpp                | 145 +++----
 clang/test/AST/ByteCode/builtin-functions.cpp |   6 +-
 .../ByteCode/codegen-constexpr-unknown.cpp    |   1 +
 clang/test/AST/ByteCode/references.cpp        |   5 +-
 clang/test/CodeGenCXX/global-init.cpp         |   8 +
 clang/test/SemaCXX/PR19955.cpp                |   3 +
 clang/test/SemaCXX/cxx2c-expansion-stmts.cpp  |   4 +-
 20 files changed, 599 insertions(+), 158 deletions(-)

diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
index 57d967c2b11cd4..4174a26b804640 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
@@ -68,6 +68,7 @@ class ByteCodeEmitter {
   /// We're always emitting bytecode.
   bool isActive() const { return true; }
   bool checkingForUndefinedBehavior() const { return false; }
+  bool constantFolding() const { return false; }
 
   /// Callback for local registration.
   Local createLocal(const Descriptor *D);
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 81c8fb0b9f17d7..631a91519974dc 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3579,6 +3579,10 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
   bool IsStatic = E->getStorageDuration() == SD_Static;
   if (IsStatic ||
       (ExtendingDecl && Context::shouldBeGloballyIndexed(ExtendingDecl))) {
+
+    if (this->constantFolding())
+      return false;
+
     UnsignedOrNone GlobalIndex = P.createGlobal(E, Inner->getType());
     if (!GlobalIndex)
       return false;
@@ -5491,6 +5495,8 @@ const Function *Compiler<Emitter>::getFunction(const FunctionDecl *FD) {
 
 template <class Emitter>
 bool Compiler<Emitter>::visitExpr(const Expr *E, bool DestroyToplevelScope) {
+  assert(E);
+  assert(!E->getType().isNull());
   LocalScope<Emitter> RootScope(this, ScopeKind::FullExpression);
 
   auto maybeDestroyLocals = [&]() -> bool {
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index 61ee2d255e9051..9cdb2725a92040 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -76,7 +76,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
   size_t StackSizeBefore = Stk.size();
   Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
 
-  auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue());
+  auto Res = C.interpretExpr(E);
 
   if (Res.isInvalid()) {
     C.cleanup();
@@ -96,7 +96,6 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
   }
 
   Result = Res.stealAPValue();
-
   return true;
 }
 
@@ -105,7 +104,7 @@ bool Context::evaluate(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);
+  Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc, Kind);
 
   auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/false,
                              /*DestroyToplevelScope=*/true);
@@ -137,8 +136,8 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
   Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
 
   bool CheckGlobalInitialized =
-      shouldBeGloballyIndexed(VD) &&
       (VD->getType()->isRecordType() || VD->getType()->isArrayType());
+
   auto Res = C.interpretDecl(VD, Init, CheckGlobalInitialized);
   if (Res.isInvalid()) {
     C.cleanup();
diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp
index 98fbac4dd0eec6..22d0828df423f2 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -643,9 +643,6 @@ LLVM_DUMP_METHOD void EvaluationResult::dump() const {
     OS << "Invalid\n";
   } else {
     OS << "Value: ";
-#ifndef NDEBUG
-    assert(Ctx);
-    Value.dump(OS, Ctx->getASTContext());
-#endif
+    Value.dump(OS, Ctx.getASTContext());
   }
 }
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index 0e8b6f9dce56ff..ec7550eb4cc17a 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -18,12 +18,14 @@ using namespace clang;
 using namespace clang::interp;
 
 EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
-                         InterpStack &Stk, FrameAllocator &FA)
-    : Ctx(Ctx), P(P), S(Parent, P, Stk, FA, Ctx, this), EvalResult(&Ctx) {}
+                         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, Expr::EvalStatus &Status,
                          InterpStack &Stk, FrameAllocator &FA)
-    : Ctx(Ctx), P(P), S(Status, P, Stk, FA, Ctx, this), EvalResult(&Ctx) {}
+    : Ctx(Ctx), P(P), S(Status, P, Stk, FA, Ctx, this), EvalResult(Ctx) {}
 
 /// Clean up all our resources. This needs to done in failed evaluations before
 /// we call InterpStack::clear(), because there might be a Pointer on the stack
@@ -240,6 +242,18 @@ template <PrimType OpType> bool EvalEmitter::emitRet(SourceInfo Info) {
   return true;
 }
 
+template <> bool EvalEmitter::emitRet<PT_MemberPtr>(SourceInfo Info) {
+  if (!isActive())
+    return true;
+
+  const MemberPointer &MP = S.Stk.pop<MemberPointer>();
+  if (!EvalResult.checkMemberPointer(S, MP, Info, ConstexprKind))
+    return false;
+
+  EvalResult.takeValue(MP.toAPValue(Ctx.getASTContext()));
+  return true;
+}
+
 template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
   if (!isActive())
     return true;
@@ -251,6 +265,7 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
 
   if (!EvalResult.checkDynamicAllocations(S, Ptr, Info))
     return false;
+
   if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
     return false;
 
@@ -258,6 +273,9 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
   if (Ptr.isFunctionPointer()) {
     if (ConvertResultToRValue && Ptr.asFunctionPointer().Func->getDecl())
       return false;
+    if (!EvalResult.checkFunctionPointer(S, Ptr, Info, ConstexprKind))
+      return false;
+
     EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
     return true;
   }
@@ -276,37 +294,39 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
         Ptr.block()->getEvalID() != Ctx.getEvalID())
       return false;
 
+    if (!EvalResult.checkLValueFields(S, Ptr, Info, ConstexprKind))
+      return false;
+
     if (std::optional<APValue> V =
             Ptr.toRValue(Ctx, EvalResult.getSourceType())) {
       EvalResult.takeValue(std::move(*V));
-    } else {
-      return false;
-    }
-  } else {
-    // If this is pointing to a local variable, just return
-    // the result, even if the pointer is dead.
-    // This will later be diagnosed by CheckLValueConstantExpression.
-    if (Ptr.isBlockPointer() && !Ptr.block()->isStatic()) {
-      EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
       return true;
     }
+    return false;
+  }
 
-    if (!Ptr.isLive() && !Ptr.isTemporary())
-      return false;
+  // Return as lvalue.
+  if (!EvalResult.checkLValue(S, Ptr, Info, ConstexprKind))
+    return false;
 
-    // If the variable of this pointer is being evaluated when returning
-    // its value, mark it as constexpr-unknown.
-    APValue V = Ptr.toAPValue(Ctx.getASTContext());
-    if (const Descriptor *DeclDesc = Ptr.getDeclDesc();
-        DeclDesc && S.EvaluatingDecl &&
-        DeclDesc->asVarDecl() == S.EvaluatingDecl &&
+  if (!Ptr.isLive() && !Ptr.isTemporary())
+    return false;
+
+  if (const Descriptor *DeclDesc = Ptr.getDeclDesc();
+      DeclDesc && S.EvaluatingDecl &&
+      ((DeclDesc->asVarDecl() == S.EvaluatingDecl &&
         S.getLangOpts().CPlusPlus23 &&
-        S.EvaluatingDecl->getType()->isReferenceType()) {
-      V.setConstexprUnknown(true);
-    }
-    EvalResult.takeValue(std::move(V));
+        S.EvaluatingDecl->getType()->isReferenceType()) ||
+       DeclDesc->IsConstexprUnknown)) {
+    S.FFDiag(Info, diag::note_constexpr_var_init_non_constant, 1)
+        << DeclDesc->asVarDecl();
+    S.Note(DeclDesc->asVarDecl()->getLocation(), diag::note_declared_at);
+
+    return false;
   }
 
+  APValue V = Ptr.toAPValue(Ctx.getASTContext());
+  EvalResult.takeValue(std::move(V));
   return true;
 }
 
@@ -322,6 +342,8 @@ bool EvalEmitter::emitRetValue(SourceInfo Info) {
     return false;
   if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
     return false;
+  if (!EvalResult.checkLValueFields(S, Ptr, Info, ConstexprKind))
+    return false;
 
   if (std::optional<APValue> APV =
           Ptr.toRValue(Ctx, EvalResult.getSourceType())) {
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h
index 069758615012d7..70ad775e84f81c 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.h
+++ b/clang/lib/AST/ByteCode/EvalEmitter.h
@@ -37,9 +37,14 @@ class EvalEmitter : public SourceMapper {
   using PtrCallback =
       llvm::function_ref<bool(InterpState &S, CodePtr OpPC, const Pointer &)>;
 
-  EvaluationResult interpretExpr(const Expr *E,
-                                 bool ConvertResultToRValue = false,
+  EvaluationResult interpretExpr(const Expr *E) {
+    return interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue(),
+                         /*DestroyToplevelScope=*/false);
+  }
+
+  EvaluationResult interpretExpr(const Expr *E, bool ConvertResultToRValue,
                                  bool DestroyToplevelScope = false);
+
   EvaluationResult interpretDecl(const VarDecl *VD, const Expr *Init,
                                  bool CheckFullyInitialized);
   EvaluationResult interpretDestructor(const VarDecl *VD, const APValue &Value);
@@ -61,9 +66,14 @@ class EvalEmitter : public SourceMapper {
   /// Returns the source location of the current opcode.
   SourceInfo getSource(CodePtr PC) const override { return CurrentSource; }
 
+  bool constantFolding() const {
+    return S.EvalMode == EvaluationMode::ConstantFold;
+  }
+
 protected:
   EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk,
-              FrameAllocator &FrameAlloc);
+              FrameAllocator &FrameAlloc,
+              ConstantExprKind ConstexprKind = ConstantExprKind::Normal);
 
   EvalEmitter(Context &Ctx, Program &P, Expr::EvalStatus &Status,
               InterpStack &Stk, FrameAllocator &FrameAlloc);
@@ -120,6 +130,7 @@ class EvalEmitter : public SourceMapper {
   InterpState S;
   /// Location to write the result to.
   EvaluationResult EvalResult;
+  ConstantExprKind ConstexprKind = ConstantExprKind::Normal;
   /// Whether the result should be converted to an RValue.
   bool ConvertResultToRValue = false;
   /// Whether we should check if the result has been fully
diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp
index 09b1eb822b13e2..30976fcf1d5213 100644
--- a/clang/lib/AST/ByteCode/EvaluationResult.cpp
+++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp
@@ -7,9 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 #include "EvaluationResult.h"
+#include "../ExprConstShared.h"
 #include "InterpState.h"
 #include "Pointer.h"
 #include "Record.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include <iterator>
@@ -17,6 +22,19 @@
 namespace clang {
 namespace interp {
 
+QualType EvaluationResult::getStorageType() const {
+  if (const auto *E = Source.asExpr()) {
+    if (E->isPRValue())
+      return E->getType();
+
+    return Ctx.getASTContext().getLValueReferenceType(E->getType());
+  }
+
+  if (const auto *D = Source.asValueDecl())
+    return D->getType();
+  return QualType();
+}
+
 static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc,
                                            const FieldDecl *SubObjDecl) {
   assert(SubObjDecl && "Subobject declaration does not exist");
@@ -245,9 +263,10 @@ static void collectBlocks(PtrView Ptr,
 
 bool EvaluationResult::checkDynamicAllocations(InterpState &S,
                                                const Pointer &Ptr,
-                                               SourceInfo Info) {
+                                               SourceInfo Info) const {
   if (!Ptr.isBlockPointer())
     return true;
+
   // Collect all blocks that this pointer (transitively) points to and
   // return false if any of them is a dynamic block.
   llvm::SmallPtrSet<const Block *, 4> Blocks;
@@ -271,5 +290,366 @@ bool EvaluationResult::checkDynamicAllocations(InterpState &S,
   return true;
 }
 
+static bool isGlobalLValue(const Pointer &Ptr) {
+  if (Ptr.isDynamic())
+    return true;
+  if (Ptr.isTypeidPointer())
+    return true;
+
+  return ::isGlobalLValue(Ptr.getRootValueDecl(), Ptr.getRootExpr());
+}
+
+/// Check if the given function pointer can be returned from an evaluation.
+static bool checkFunctionPtr(InterpState &S, const Pointer &Ptr,
+                             QualType PtrType, SourceInfo Info,
+                             ConstantExprKind ConstexprKind) {
+  assert(Ptr.isFunctionPointer());
+  const FunctionPointer &FuncPtr = Ptr.asFunctionPointer();
+
+  if (!FuncPtr.Func)
+    return true;
+
+  const FunctionDecl *FD = FuncPtr.Func->getDecl();
+  // E.g. ObjC block pointers.
+  if (!FD)
+    return true;
+  if (FD->isImmediateFunction()) {
+    S.FFDiag(Info, diag::note_consteval_address_accessible)
+        << !PtrType->isAnyPointerType();
+    S.Note(FD->getLocation(), diag::note_declared_at);
+    return false;
+  }
+
+  // __declspec(dllimport) must be handled very carefully:
+  // We must never initialize an expression with the thunk in C++.
+  // Doing otherwise would allow the same id-expression to yield
+  // different addresses for the same function in different translation
+  // units.  However, this means that we must dynamically initialize the
+  // expression with the contents of the import address table at runtime.
+  //
+  // The C language has no notion of ODR; furthermore, it has no notion of
+  // dynamic initialization.  This means that we are permitted to
+  // perform initialization with the address of the thunk.
+  if (S.getLangOpts().CPlusPlus && !isForManglingOnly(ConstexprKind) &&
+      FD->hasAttr<DLLImportAttr>())
+    // FIXME: Diagnostic!
+    return false;
+  return true;
+}
+
+static bool lvalFields(InterpState &S, const ASTContext &Ctx, PtrView Ptr,
+                       QualType PtrType, SourceInfo Info,
+                       ConstantExprKind ConstexprKind,
+                       llvm::SmallPtrSet<const Block *, 4> &CheckedBlocks);
+static bool lval(InterpState &S, const ASTContext &Ctx, const Pointer &Ptr,
+                 QualType PtrType, SourceInfo Info,
+                 ConstantExprKind ConstexprKind,
+                 llvm::SmallPtrSet<const Block *, 4> &CheckedBlocks) {
+  if (Ptr.isFunctionPointer())
+    return checkFunctionPtr(S, Ptr, PtrType, Info, ConstexprKind);
+
+  if (!Ptr.isBlockPointer() && !Ptr.isOpaquePointer())
+    return true;
+
+  const Expr *BaseE = Ptr.getRootExpr();
+  const ValueDecl *BaseVD = Ptr.getRootValueDecl();
+  assert(BaseE || BaseVD);
+  bool IsReferenceType = PtrType->isReferenceType();
+  bool IsSubObj = !Ptr.isRoot() || (Ptr.inArray() && !Ptr.isArrayRoot());
+
+  if (!isGlobalLValue(Ptr)) {
+    if (S.getLangOpts().CPlusPlus11) {
+      S.FFDiag(Info, diag::note_constexpr_non_global, 1)
+          << IsReferenceType << IsSubObj << !!BaseVD << BaseVD;
+      const VarDecl *VarD = dyn_cast_if_present<VarDecl>(BaseVD);
+      if (VarD && VarD->isConstexpr()) {
+        // Non-static local constexpr variables have unintuitive semantics:
+        //   constexpr int a = 1;
+        //   constexpr const int *p = &a;
+        // ... is invalid because the address of 'a' is not constant. Suggest
+        // adding a 'static' in this case.
+        S.Note(VarD->getLocation(), diag::note_constexpr_not_static)
+            << VarD
+            << FixItHint::CreateInsertion(VarD->getBeginLoc(), "static ");
+      } else {
+        if (BaseVD)
+          S.Note(BaseVD->getLocation(), diag::note_declared_at);
+        else if (BaseE)
+          S.Note(BaseE->getExprLoc(), diag::note_constexpr_temporary_here);
+      }
+    } else {
+      S.FFDiag(Info);
+    }
+    return false;
+  }
+
+  if (const auto *VD = dyn_cast_if_present<VarDecl>(BaseVD)) {
+    // Check if this is a thread-local variable.
+    if (VD->getTLSKind()) {
+      // FIXME: Diagnostic!
+      return false;
+    }
+
+    // A dllimport variable never acts like a constant, unless we're
+    // evaluating a value for use only in name mangling, and unless it's a
+    // static local. For the latter case, we'd still need to evaluate the
+    // constant expression in case we're inside a (inlined) function.
+    if (!isForManglingOnly(ConstexprKind) && VD->hasAttr<DLLImportAttr>() &&
+        !VD->isStaticLocal())
+      return false;
+
+    // Address of a managed variable is never a constant expression.
+    if (S.getLangOpts().CUDA && VD->hasAttr<HIPManagedAttr>())
+      return false;
+
+    // In CUDA/HIP device compilation, only device side variables have
+    // constant addresses.
+    if (S.getLangOpts().CUDA && S.getLangOpts().CUDAIsDevice &&
+        Ctx.CUDAConstantEvalCtx.NoWrongSidedVars) {
+      if ((!VD->hasAttr<CUDADeviceAttr>() && !VD->hasAttr<CUDAConstantAttr>() &&
+           !VD->getType()->isCUDADeviceBuiltinSurfaceType() &&
+           !VD->getType()->isCUDADeviceBuiltinTextureType()))
+        return false;
+    }
+
+    return true;
+  }
+
+  if (const auto *MTE = dyn_cast_if_present<MaterializeTemporaryExpr>(BaseE)) {
+    QualType TempType = Ptr.getType();
+
+    if (TempType.isDestructedType()) {
+      S.FFDiag(MTE->getExprLoc(),
+               diag::note_constexpr_unsupported_temporary_nontrivial_dtor)
+          << TempType;
+      return false;
+    }
+
+    if (Ptr.getFieldDesc()->isPrimitive() &&
+        Ptr.getFieldDesc()->getPrimType() == PT_Ptr) {
+      // Recurse!
+      Pointer Pointee = Ptr.deref<Pointer>();
+      if (!Pointee.isBlockPointer() ||
+          CheckedBlocks.insert(Pointee.block()).second) {
+        if (!lval(S, Ctx, Pointee, Pointee.getType(),
+                  Ptr.getDeclDesc()->getLoc(), ConstexprKind, CheckedBlocks))
+          return false;
+      }
+    } else {
+      if (!lvalFields(S, Ctx, Ptr.view(), TempType, Info, ConstexprKind,
+                      CheckedBlocks))
+        return false;
+    }
+  }
+
+  return true;
+}
+
+static bool lvalFields(InterpState &S, const ASTContext &Ctx, PtrView Ptr,
+                       QualType PtrType, SourceInfo Info,
+                       ConstantExprKind ConstexprKind,
+                       llvm::SmallPtrSet<const Block *, 4> &CheckedBlocks) {
+  const Descriptor *FieldDesc = Ptr.getFieldDesc();
+  if (const Record *R = Ptr.getRecord()) {
+    if (!R->hasPtrField())
+      return true;
+
+    for (const Record::Base &B : R->bases()) {
+      if (!B.R->hasPtrField())
+        continue;
+
+      PtrView BasePtr = Ptr.atField(B.Offset);
+      if (!lvalFields(S, Ctx, BasePtr, B.Desc->getType(), Info, ConstexprKind,
+                      CheckedBlocks))
+        return false;
+    }
+
+    for (const Record::Field &F : R->fields()) {
+      PtrView FieldPtr = Ptr.atField(F.Offset);
+      if (!isOrHasPtr(F.Desc))
+        continue;
+
+      if (F.Desc->isPrimitive() && F.Desc->getPrimType() == PT_Ptr) {
+        if (!FieldPtr.isLive())
+          return false;
+
+        Pointer Pointee = FieldPtr.deref<Pointer>();
+        if (!Pointee.isBlockPointer() ||
+            CheckedBlocks.insert(Pointee.block()).second) {
+          QualType FieldType = F.Decl->getType();
+          if (!lval(S, Ctx, Pointee, FieldType, Info, ConstexprKind,
+                    CheckedBlocks))
+            return false;
+        }
+      } else {
+        if (!lvalFields(S, Ctx, FieldPtr, F.Decl->getType(), Info,
+                        ConstexprKind, CheckedBlocks))
+          return false;
+      }
+    }
+
+    for (const Record::Base &B : R->virtual_bases()) {
+      if (!B.R->hasPtrField())
+        continue;
+      PtrView BasePtr = Ptr.atField(B.Offset);
+      if (!lvalFields(S, Ctx, BasePtr, B.Desc->getType(), Info, ConstexprKind,
+                      CheckedBlocks))
+        return false;
+    }
+    return true;
+  }
+
+  if (FieldDesc->isPrimitiveArray() && FieldDesc->getPrimType() == PT_Ptr) {
+    for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
+      if (!Ptr.isLive())
+        return false;
+      Pointer Pointee = Ptr.elem<Pointer>(I);
+
+      if (!Pointee.isBlockPointer() ||
+          CheckedBlocks.insert(Pointee.block()).second) {
+        if (!lval(S, Ctx, Pointee, FieldDesc->getElemQualType(), Info,
+                  ConstexprKind, CheckedBlocks))
+          return false;
+      }
+    }
+    return true;
+  }
+
+  if (FieldDesc->isCompositeArray()) {
+    if (FieldDesc->ElemRecord && !FieldDesc->ElemRecord->hasPtrField())
+      return true;
+
+    for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
+      PtrView Elem = Ptr.atIndex(I).narrow();
+      if (!lvalFields(S, Ctx, Elem, FieldDesc->getElemQualType(), Info,
+                      ConstexprKind, CheckedBlocks))
+        return false;
+    }
+    return true;
+  }
+  if (FieldDesc->isPrimitive() && FieldDesc->getPrimType() == PT_MemberPtr) {
+    MemberPointer MP = Ptr.deref<MemberPointer>();
+    if (!EvaluationResult::checkMemberPointer(S, MP, Info, ConstexprKind))
+      return false;
+  }
+
+  return true;
+}
+
+/// Toplevel accessor to check all lvalue fields.
+bool EvaluationResult::checkLValueFields(InterpState &S, const Pointer &Ptr,
+                                         SourceInfo Info,
+                                         ConstantExprKind ConstexprKind) const {
+  if (!Ptr.isBlockPointer())
+    return true;
+
+  QualType SourceType = getStorageType();
+  llvm::SmallPtrSet<const Block *, 4> CheckedBlocks;
+
+  return lvalFields(S, Ctx.getASTContext(), Ptr.view(), SourceType, Info,
+                    ConstexprKind, CheckedBlocks);
+}
+
+bool EvaluationResult::checkLValue(InterpState &S, const Pointer &Ptr,
+                                   SourceInfo Info,
+                                   ConstantExprKind ConstexprKind) const {
+  QualType SourceType = getStorageType();
+  if (Ptr.isFunctionPointer())
+    return checkFunctionPtr(S, Ptr, SourceType, Info, ConstexprKind);
+
+  if (Ptr.isZero())
+    return true;
+
+  bool IsReferenceType = SourceType->isReferenceType();
+  if (Ptr.isTypeidPointer()) {
+    if (isTemplateArgument(ConstexprKind)) {
+      S.FFDiag(Info, diag::note_constexpr_invalid_template_arg)
+          << IsReferenceType << /*IsSubObj=*/false << /*InvalidBaseKind=*/0;
+      return false;
+    }
+    return true;
+  }
+
+  if (Ptr.isStringPointer()) {
+    // Additional restrictions apply in a template argument. We only enforce the
+    // C++20 restrictions here; additional syntactic and semantic restrictions
+    // are applied elsewhere.
+    if (isTemplateArgument(ConstexprKind)) {
+      bool IsSubObj = Ptr.asStringPointer().Decayed || Ptr.getIndex() != 0;
+      int InvalidBaseKind = -1;
+      StringRef Ident;
+      const Expr *BaseE = Ptr.asStringPointer().Base;
+      if (isa_and_nonnull<StringLiteral>(BaseE))
+        InvalidBaseKind = 1;
+      else if (const auto *PE = dyn_cast_if_present<PredefinedExpr>(BaseE)) {
+        InvalidBaseKind = 3;
+        Ident = PE->getIdentKindName();
+      }
+
+      if (InvalidBaseKind != -1) {
+        S.FFDiag(Info, diag::note_constexpr_invalid_template_arg)
+            << IsReferenceType << IsSubObj << InvalidBaseKind << Ident;
+        return false;
+      }
+    }
+    return true;
+  }
+
+  if (!Ptr.isBlockPointer() && !Ptr.isOpaquePointer())
+    return true;
+
+  // Additional restrictions apply in a template argument. We only enforce the
+  // C++20 restrictions here; additional syntactic and semantic restrictions
+  // are applied elsewhere.
+  if (isTemplateArgument(ConstexprKind)) {
+    const Expr *BaseE = Ptr.getRootExpr();
+    const ValueDecl *BaseVD = Ptr.getRootValueDecl();
+    assert(BaseE || BaseVD);
+    if (isa_and_nonnull<MaterializeTemporaryExpr>(BaseE) ||
+        isa_and_nonnull<LifetimeExtendedTemporaryDecl>(BaseVD)) {
+      bool IsSubObj = !Ptr.isRoot() || (Ptr.inArray() && !Ptr.isArrayRoot());
+      S.FFDiag(Info, diag::note_constexpr_invalid_template_arg)
+          << IsReferenceType << IsSubObj << 2;
+      return false;
+    }
+  }
+
+  llvm::SmallPtrSet<const Block *, 4> CheckedBlocks;
+  if (!lval(S, Ctx.getASTContext(), Ptr, SourceType, Info, ConstexprKind,
+            CheckedBlocks)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool EvaluationResult::checkMemberPointer(InterpState &S,
+                                          const MemberPointer &MemberPtr,
+                                          SourceInfo Info,
+                                          ConstantExprKind ConstexprKind) {
+  const CXXMethodDecl *MD = MemberPtr.getMemberFunction();
+  if (!MD)
+    return true;
+
+  if (MD->isImmediateFunction()) {
+    S.FFDiag(Info, diag::note_consteval_address_accessible)
+        << /*pointer=*/false;
+    S.Note(MD->getLocation(), diag::note_declared_at);
+    return false;
+  }
+
+  if (isForManglingOnly(ConstexprKind) || MD->isVirtual() ||
+      !MD->hasAttr<DLLImportAttr>()) {
+    return true;
+  }
+  return false;
+}
+
+bool EvaluationResult::checkFunctionPointer(
+    InterpState &S, const Pointer &Ptr, SourceInfo Info,
+    ConstantExprKind ConstexprKind) const {
+  return checkFunctionPtr(S, Ptr, getStorageType(), Info, ConstexprKind);
+}
+
 } // namespace interp
 } // namespace clang
diff --git a/clang/lib/AST/ByteCode/EvaluationResult.h b/clang/lib/AST/ByteCode/EvaluationResult.h
index b8d8ddf981f5aa..948f822e69142b 100644
--- a/clang/lib/AST/ByteCode/EvaluationResult.h
+++ b/clang/lib/AST/ByteCode/EvaluationResult.h
@@ -18,6 +18,7 @@ namespace clang {
 namespace interp {
 class EvalEmitter;
 class Context;
+class MemberPointer;
 class Pointer;
 class SourceInfo;
 class InterpState;
@@ -38,9 +39,7 @@ class EvaluationResult final {
   };
 
 private:
-#ifndef NDEBUG
-  const Context *Ctx = nullptr;
-#endif
+  const Context &Ctx;
   APValue Value;
   ResultKind Kind = Empty;
   DeclOrExpr Source = nullptr;
@@ -63,17 +62,14 @@ class EvaluationResult final {
     Kind = Valid;
   }
 
+  QualType getStorageType() const;
+
 public:
-#ifndef NDEBUG
-  EvaluationResult(const Context *Ctx) : Ctx(Ctx) {}
-#else
-  EvaluationResult(const Context *Ctx) {}
-#endif
+  EvaluationResult(const Context &Ctx) : Ctx(Ctx) {}
 
   bool empty() const { return Kind == Empty; }
   bool isInvalid() const { return Kind == Invalid; }
 
-  /// Moves the APValue containing the evaluation result to the caller.
   APValue stealAPValue() { return std::move(Value); }
 
   /// Check that all subobjects of the given pointer have been initialized.
@@ -81,7 +77,23 @@ class EvaluationResult final {
   /// Check that none of the blocks the given pointer (transitively) points
   /// to are dynamically allocated.
   bool checkDynamicAllocations(InterpState &S, const Pointer &Ptr,
-                               SourceInfo Info);
+                               SourceInfo Info) const;
+
+  /// Check the given pointer as an lvalue, i.e. make sure it's a global
+  /// lvalue and diagnose if it's not.
+  bool checkLValue(InterpState &S, const Pointer &Ptr, SourceInfo Info,
+                   ConstantExprKind ConstexprKind) const;
+  /// Check all fields of the given pointer.
+  bool checkLValueFields(InterpState &S, const Pointer &Ptr, SourceInfo Info,
+                         ConstantExprKind ConstexprKind) const;
+
+  /// Check if the given member pointer can be returned from an evaluation.
+  static bool checkMemberPointer(InterpState &S, const MemberPointer &MemberPtr,
+                                 SourceInfo Info,
+                                 ConstantExprKind ConstexprKind);
+  /// Check if the given function pointer can be returned from an evaluation.
+  bool checkFunctionPointer(InterpState &S, const Pointer &Ptr, SourceInfo Info,
+                            ConstantExprKind ConstexprKind) const;
 
   QualType getSourceType() const {
     if (const auto *D = Source.asValueDecl())
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index f885c0dc9cdb2a..aa47b6115eb27f 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -875,6 +875,8 @@ static bool CheckLifetime(InterpState &S, CodePtr OpPC, Lifetime LT,
 }
 static bool CheckLifetime(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                           AccessKinds AK) {
+  if (!Ptr.isBlockPointer())
+    return true;
   return CheckLifetime(S, OpPC, Ptr.getLifetime(), Ptr.block(), AK);
 }
 
@@ -1096,7 +1098,7 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
     if (!CheckMutable(S, OpPC, Ptr.view()))
       return false;
   }
-  if (Ptr.isConstexprUnknown())
+  if (!S.inConstantContext() && isConstexprUnknown(Ptr))
     return false;
   return true;
 }
diff --git a/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp b/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp
index ec3a7af4e1e137..41b347150886f8 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp
@@ -352,7 +352,7 @@ computeOpaqueSize(const ASTContext &ASTCtx, const Pointer &Ptr,
     return TypeSize.getQuantity();
 
   // Check if we need to add the flexible array member size.
-  const VarDecl *Base = OP.getBaseDecl();
+  const VarDecl *Base = OP.getBaseVarDecl();
   if (!Base)
     return TypeSize.getQuantity();
 
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index 552cdf1bf8ec71..c4c3b1be46a327 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -1330,8 +1330,12 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx,
 }
 
 const VarDecl *Pointer::getRootVarDecl() const {
+  return dyn_cast_if_present<VarDecl>(getRootValueDecl());
+}
+
+const ValueDecl *Pointer::getRootValueDecl() const {
   if (isBlockPointer())
-    return getDeclDesc()->asVarDecl();
+    return getDeclDesc()->asValueDecl();
   if (isOpaquePointer())
     return Opaque.getBaseDecl();
   return nullptr;
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 207ec06a32a34d..c7f0da0951121a 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -445,7 +445,8 @@ struct OpaquePointer {
 
   ArrayRef<PointerPathEntry> path() const { return ArrayRef(Path, PathLength); }
   bool hasDeclBase() const { return Base.isDecl(); }
-  const VarDecl *getBaseDecl() const { return Base.asVarDecl(); }
+  const ValueDecl *getBaseDecl() const { return Base.asValueDecl(); }
+  const VarDecl *getBaseVarDecl() const { return Base.asVarDecl(); }
   const Expr *getBaseExpr() const { return Base.asExpr(); }
   bool hasValidBase() const;
 
@@ -742,6 +743,7 @@ class Pointer {
   }
 
   const VarDecl *getRootVarDecl() const;
+  const ValueDecl *getRootValueDecl() const;
   const Expr *getRootExpr() const;
 
   [[nodiscard]] Pointer getDeclPtr() const { return Pointer(BS.Pointee); }
@@ -921,7 +923,7 @@ class Pointer {
     }
 
     if (isOpaquePointer()) {
-      if (const VarDecl *BaseDecl = Opaque.getBaseDecl())
+      if (const VarDecl *BaseDecl = Opaque.getBaseVarDecl())
         return BaseDecl->isWeak();
       return false;
     }
@@ -998,7 +1000,10 @@ class Pointer {
     return view().getNumElems();
   }
 
-  const Block *block() const { return BS.Pointee; }
+  const Block *block() const {
+    assert(isBlockPointer());
+    return BS.Pointee;
+  }
 
   /// If backed by actual data (i.e. a block or string pointer), return
   /// an address to that data.
diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h
index ca3481270605e0..7693ea25592f12 100644
--- a/clang/lib/AST/ExprConstShared.h
+++ b/clang/lib/AST/ExprConstShared.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H
 #define LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H
 
+#include "ByteCode/State.h"
 #include "clang/Basic/BuiltinTraits.h"
 #include <cstdint>
 #include <optional>
@@ -115,5 +116,46 @@ const Expr *ignorePointerCastsAndParens(const Expr *E);
 
 bool isReadByLvalueToRvalueConversion(const CXXRecordDecl *RD);
 bool isReadByLvalueToRvalueConversion(QualType T);
+/// Determines whether the given kind of constant expression is only ever
+/// used for name mangling. If so, it's permitted to reference things that we
+/// can't generate code for (in particular, dllimported functions).
+inline bool isForManglingOnly(ConstantExprKind Kind) {
+  switch (Kind) {
+  case ConstantExprKind::Normal:
+  case ConstantExprKind::ClassTemplateArgument:
+  case ConstantExprKind::ImmediateInvocation:
+    // Note that non-type template arguments of class type are emitted as
+    // template parameter objects.
+    return false;
+
+  case ConstantExprKind::NonClassTemplateArgument:
+    return true;
+  }
+  llvm_unreachable("unknown ConstantExprKind");
+}
+
+inline bool isTemplateArgument(ConstantExprKind Kind) {
+  switch (Kind) {
+  case ConstantExprKind::Normal:
+  case ConstantExprKind::ImmediateInvocation:
+    return false;
+
+  case ConstantExprKind::ClassTemplateArgument:
+  case ConstantExprKind::NonClassTemplateArgument:
+    return true;
+  }
+  llvm_unreachable("unknown ConstantExprKind");
+}
+
+/// Should this call expression be treated as forming an opaque constant?
+inline bool isOpaqueConstantCall(const CallExpr *E) {
+  unsigned Builtin = E->getBuiltinCallee();
+  return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString ||
+          Builtin == Builtin::BI__builtin___NSStringMakeConstantString ||
+          Builtin == Builtin::BI__builtin_ptrauth_sign_constant ||
+          Builtin == Builtin::BI__builtin_function_start);
+}
+
+bool isGlobalLValue(const ValueDecl *D, const Expr *E);
 
 #endif
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 2df754dc9007f2..3efc6ffe7e9079 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -150,37 +150,6 @@ namespace {
     return E && E->getType()->isPointerType() && tryUnwrapAllocSizeCall(E);
   }
 
-  /// Determines whether the given kind of constant expression is only ever
-  /// used for name mangling. If so, it's permitted to reference things that we
-  /// can't generate code for (in particular, dllimported functions).
-  static bool isForManglingOnly(ConstantExprKind Kind) {
-    switch (Kind) {
-    case ConstantExprKind::Normal:
-    case ConstantExprKind::ClassTemplateArgument:
-    case ConstantExprKind::ImmediateInvocation:
-      // Note that non-type template arguments of class type are emitted as
-      // template parameter objects.
-      return false;
-
-    case ConstantExprKind::NonClassTemplateArgument:
-      return true;
-    }
-    llvm_unreachable("unknown ConstantExprKind");
-  }
-
-  static bool isTemplateArgument(ConstantExprKind Kind) {
-    switch (Kind) {
-    case ConstantExprKind::Normal:
-    case ConstantExprKind::ImmediateInvocation:
-      return false;
-
-    case ConstantExprKind::ClassTemplateArgument:
-    case ConstantExprKind::NonClassTemplateArgument:
-      return true;
-    }
-    llvm_unreachable("unknown ConstantExprKind");
-  }
-
   /// The bound to claim that an array of unknown bound has.
   /// The value in MostDerivedArraySize is undefined in this case. So, set it
   /// to an arbitrary value that's likely to loudly break things if it's used.
@@ -1940,31 +1909,30 @@ static bool EvaluateIgnoredValue(EvalInfo &Info, const Expr *E) {
   return true;
 }
 
-/// Should this call expression be treated as forming an opaque constant?
-static bool IsOpaqueConstantCall(const CallExpr *E) {
-  unsigned Builtin = E->getBuiltinCallee();
-  return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString ||
-          Builtin == Builtin::BI__builtin___NSStringMakeConstantString ||
-          Builtin == Builtin::BI__builtin_ptrauth_sign_constant ||
-          Builtin == Builtin::BI__builtin_function_start);
-}
-
 static bool IsOpaqueConstantCall(const LValue &LVal) {
   const auto *BaseExpr =
       llvm::dyn_cast_if_present<CallExpr>(LVal.Base.dyn_cast<const Expr *>());
-  return BaseExpr && IsOpaqueConstantCall(BaseExpr);
+  return BaseExpr && isOpaqueConstantCall(BaseExpr);
 }
 
 static bool IsGlobalLValue(APValue::LValueBase B) {
+  if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>())
+    return true;
+
+  return isGlobalLValue(B.dyn_cast<const ValueDecl *>(),
+                        B.dyn_cast<const Expr *>());
+}
+
+bool isGlobalLValue(const ValueDecl *D, const Expr *E) {
   // C++11 [expr.const]p3 An address constant expression is a prvalue core
   // constant expression of pointer type that evaluates to...
 
   // ... a null pointer value, or a prvalue core constant expression of type
   // std::nullptr_t.
-  if (!B)
+  if (!D && !E)
     return true;
 
-  if (const ValueDecl *D = B.dyn_cast<const ValueDecl*>()) {
+  if (D) {
     // ... the address of an object with static storage duration,
     if (const VarDecl *VD = dyn_cast<VarDecl>(D))
       return VD->hasGlobalStorage();
@@ -1976,10 +1944,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
     return isa<FunctionDecl, MSGuidDecl, UnnamedGlobalConstantDecl>(D);
   }
 
-  if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>())
-    return true;
+  assert(E);
 
-  const Expr *E = B.get<const Expr*>();
   switch (E->getStmtClass()) {
   default:
     return false;
@@ -2002,7 +1968,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
   case Expr::ObjCDictionaryLiteralClass:
     return cast<ObjCObjectLiteral>(E)->isExpressibleAsConstantInitializer();
   case Expr::CallExprClass:
-    return IsOpaqueConstantCall(cast<CallExpr>(E));
+    return isOpaqueConstantCall(cast<CallExpr>(E));
   // For GCC compatibility, &&label has static storage duration.
   case Expr::AddrLabelExprClass:
     return true;
@@ -2023,6 +1989,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
     // an expression might be a global lvalue.
     return true;
   }
+
+  return false;
 }
 
 static const ValueDecl *GetLValueBaseDecl(const LValue &LVal) {
@@ -10636,7 +10604,7 @@ static bool isOneByteCharacterType(QualType T) {
 
 bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
                                                 unsigned BuiltinOp) {
-  if (IsOpaqueConstantCall(E))
+  if (isOpaqueConstantCall(E))
     return Success(E);
 
   switch (BuiltinOp) {
@@ -21837,12 +21805,8 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) {
   if (!CheckLiteralType(Info, E))
     return false;
 
-  if (Info.EnableNewConstInterp) {
-    if (!Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result))
-      return false;
-    return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result,
-                                   ConstantExprKind::Normal);
-  }
+  if (Info.EnableNewConstInterp)
+    return Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result);
 
   if (!::Evaluate(Result, Info, E))
     return false;
@@ -22043,14 +22007,8 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx,
   CheckedTemporaries CheckedTemps;
 
   if (Info.EnableNewConstInterp) {
-    if (!Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val,
-                                              ConstantExprKind::Normal))
-      return false;
-
-    LV.setFrom(Ctx, Result.Val);
-    return CheckLValueConstantExpression(
-        Info, getExprLoc(), Ctx.getLValueReferenceType(getType()), LV,
-        ConstantExprKind::Normal, CheckedTemps);
+    return Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val,
+                                                ConstantExprKind::Normal);
   }
 
   if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() ||
@@ -22102,12 +22060,8 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx,
   EvalInfo Info(Ctx, Result, EM);
   Info.InConstantContext = true;
 
-  if (Info.EnableNewConstInterp) {
-    if (!Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, Kind))
-      return false;
-    return CheckConstantExpression(Info, getExprLoc(),
-                                   getStorageType(Ctx, this), Result.Val, Kind);
-  }
+  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();
@@ -22181,40 +22135,35 @@ bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD,
 
   if (Info.EnableNewConstInterp) {
     auto &InterpCtx = Ctx.getInterpContext();
-    if (!InterpCtx.evaluateAsInitializer(Info, VD, this, EStatus.Val))
-      return false;
-
-    return CheckConstantExpression(Info, DeclLoc, DeclTy, EStatus.Val,
-                                   ConstantExprKind::Normal);
-  } else {
-    LValue LVal;
-    LVal.set(VD);
-
-    {
-      // C++23 [intro.execution]/p5
-      // A full-expression is ... an init-declarator ([dcl.decl]) or a
-      // mem-initializer.
-      // So we need to make sure temporary objects are destroyed after having
-      // evaluated the expression (per C++23 [class.temporary]/p4).
-      //
-      // FIXME: Otherwise this may break test/Modules/pr68702.cpp because the
-      // serialization code calls ParmVarDecl::getDefaultArg() which strips the
-      // outermost FullExpr, such as ExprWithCleanups.
-      FullExpressionRAII Scope(Info);
-      if (!EvaluateInPlace(EStatus.Val, Info, LVal, this,
-                           /*AllowNonLiteralTypes=*/true) ||
-          EStatus.HasSideEffects)
-        return false;
-    }
+    return InterpCtx.evaluateAsInitializer(Info, VD, this, EStatus.Val);
+  }
 
-    // At this point, any lifetime-extended temporaries are completely
-    // initialized.
-    Info.performLifetimeExtension();
+  LValue LVal;
+  LVal.set(VD);
 
-    if (!Info.discardCleanups())
-      llvm_unreachable("Unhandled cleanup; missing full expression marker?");
+  {
+    // C++23 [intro.execution]/p5
+    // A full-expression is ... an init-declarator ([dcl.decl]) or a
+    // mem-initializer.
+    // So we need to make sure temporary objects are destroyed after having
+    // evaluated the expression (per C++23 [class.temporary]/p4).
+    //
+    // FIXME: Otherwise this may break test/Modules/pr68702.cpp because the
+    // serialization code calls ParmVarDecl::getDefaultArg() which strips the
+    // outermost FullExpr, such as ExprWithCleanups.
+    FullExpressionRAII Scope(Info);
+    if (!EvaluateInPlace(EStatus.Val, Info, LVal, this,
+                         /*AllowNonLiteralTypes=*/true) ||
+        EStatus.HasSideEffects)
+      return false;
   }
 
+  // At this point, any lifetime-extended temporaries are completely
+  // initialized.
+  Info.performLifetimeExtension();
+
+  if (!Info.discardCleanups())
+    llvm_unreachable("Unhandled cleanup; missing full expression marker?");
   return CheckConstantExpression(Info, DeclLoc, DeclTy, EStatus.Val,
                                  ConstantExprKind::Normal) &&
          CheckMemoryLeaks(Info);
diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp
index 51e1b6cde47791..5d8c0fa2b7b6b0 100644
--- a/clang/test/AST/ByteCode/builtin-functions.cpp
+++ b/clang/test/AST/ByteCode/builtin-functions.cpp
@@ -2040,9 +2040,9 @@ namespace WithinLifetime {
     constexpr const int &temp = 0; // both-error {{must be initialized by a constant expression}} \
                                    // both-note {{reference to temporary is not a constant expression}} \
                                    // both-note {{temporary created here}} \
-                                   // ref-note {{declared here}}
-    static_assert(__builtin_is_within_lifetime(&temp)); // ref-error {{not an integral constant expression}} \
-                                                        // ref-note {{initializer of 'temp' is not a constant expression}}
+                                   // both-note {{declared here}}
+    static_assert(__builtin_is_within_lifetime(&temp)); // both-error {{not an integral constant expression}} \
+                                                        // both-note {{initializer of 'temp' is not a constant expression}}
   }
 }
 
diff --git a/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp b/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp
index 8b70f5a4251adf..3d1e31d5292ed6 100644
--- a/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp
+++ b/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp
@@ -21,6 +21,7 @@ void rightscope() {
 // CHECK-NEXT: entry:
 // CHECK-NEXT: %p = alloca i32
 // CHECK-NEXT: store i32 0, ptr %p
+// CHECK-NEXT: ret void
 
 
 /// In the if expression below, the read from s.i should fail.
diff --git a/clang/test/AST/ByteCode/references.cpp b/clang/test/AST/ByteCode/references.cpp
index a4a8100f5b31e1..3da3996aaca756 100644
--- a/clang/test/AST/ByteCode/references.cpp
+++ b/clang/test/AST/ByteCode/references.cpp
@@ -188,10 +188,9 @@ namespace ReadFromNullBlockPtr {
     constexpr S s = {&x}; // both-error {{must be initialized by a constant expression}} \
                           // both-note {{reference to temporary}} \
                           // both-note {{created here}} \
-                          // ref-note {{declared here}} \
-                          // expected-note {{created here}}
+                          // ref-note {{declared here}}
     static_assert(s.t == &x, ""); // both-error {{not an integral constant expression}} \
-                                  // expected-note {{read of temporary is not allowed in a constant expression outside the expression that created the temporary}} \
+                                  // expected-note {{read of dereferenced null pointer}} \
                                   // ref-note {{initializer of 's' is not a constant expression}}
   }
 }
diff --git a/clang/test/CodeGenCXX/global-init.cpp b/clang/test/CodeGenCXX/global-init.cpp
index 52039a52082238..f10f1be4ce95db 100644
--- a/clang/test/CodeGenCXX/global-init.cpp
+++ b/clang/test/CodeGenCXX/global-init.cpp
@@ -6,6 +6,14 @@
 // RUN:   | FileCheck -check-prefix CHECK-NOBUILTIN %s
 // RUN: %clang_cc1 %std_cxx17- -triple=x86_64-apple-darwin10 -emit-llvm -fexceptions %s -o - | FileCheck %s
 
+// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm -fexceptions %s -o - -fexperimental-new-constant-interpreter | FileCheck %s --check-prefixes=CHECK,PRE17
+// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm %s -o - -fexperimental-new-constant-interpreter | FileCheck %s --check-prefixes=CHECK-NOEXC,PRE17
+// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm -mframe-pointer=non-leaf %s -o - -fexperimental-new-constant-interpreter \
+// RUN:   | FileCheck -check-prefix CHECK-FP %s
+// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm %s -o - -fno-builtin -fexperimental-new-constant-interpreter \
+// RUN:   | FileCheck -check-prefix CHECK-NOBUILTIN %s
+// RUN: %clang_cc1 %std_cxx17- -triple=x86_64-apple-darwin10 -emit-llvm -fexceptions %s -o - -fexperimental-new-constant-interpreter | FileCheck %s
+
 struct A {
   A();
   ~A();
diff --git a/clang/test/SemaCXX/PR19955.cpp b/clang/test/SemaCXX/PR19955.cpp
index cbbe2fe9af1641..6fa22ab8463749 100644
--- a/clang/test/SemaCXX/PR19955.cpp
+++ b/clang/test/SemaCXX/PR19955.cpp
@@ -1,5 +1,8 @@
 // RUN: %clang_cc1 -triple i686-win32 -verify -std=c++11 %s
 // RUN: %clang_cc1 -triple i686-mingw32 -verify -std=c++11 %s
+// RUN: %clang_cc1 -triple i686-win32 -verify -std=c++11 %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -triple i686-mingw32 -verify -std=c++11 %s -fexperimental-new-constant-interpreter
+
 
 extern int __attribute__((dllimport)) var;
 constexpr int *varp = &var; // expected-error {{must be initialized by a constant expression}}
diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
index 44189ff31dae8e..9045c9b4ae7aef 100644
--- a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
@@ -799,8 +799,8 @@ void not_constant_expression() {
                                              expected-note {{in instantiation of expansion statement requested here}} \
                                              old-interp-note {{read of variable '[__u0]' whose value is not known}} \
                                              old-interp-note {{declared here}} \
-                                             new-interp-note {{temporary created here}} \
-                                             new-interp-note {{read of temporary is not allowed in a constant expression outside the expression that created the temporary}}
+                                             new-interp-note {{declared here}} \
+                                             new-interp-note {{initializer of '[__u0]' is not a constant expression}}
     g(x);
   }
 }

>From dae4d36bd5a083bc8d844fdd13d54088359710d1 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 2/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/InterpState.cpp |  30 ++++--
 clang/lib/AST/ByteCode/InterpState.h   |   6 +-
 clang/lib/AST/ExprConstant.cpp         | 143 +++++++++++++++----------
 7 files changed, 225 insertions(+), 118 deletions(-)

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/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 3efc6ffe7e9079..895bfa0576a720 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);
 }
 



More information about the cfe-commits mailing list