https://github.com/katzdm updated 
https://github.com/llvm/llvm-project/pull/205557

>From 1a0ec89e52ee24e06ad8efcf2747586b6a469400 Mon Sep 17 00:00:00 2001
From: Dan Katz <[email protected]>
Date: Mon, 22 Jun 2026 17:44:45 -0400
Subject: [PATCH 1/3] AST mutation from the constant evaluator.

---
 clang/docs/ReleaseNotes.rst                   |   1 +
 clang/include/clang/AST/Decl.h                |  13 +-
 clang/include/clang/AST/Expr.h                |  20 ++
 clang/include/clang/AST/SemaProxy.h           |  40 +++
 clang/include/clang/Sema/Sema.h               |  26 ++
 clang/lib/AST/ByteCode/Interp.cpp             |  32 +-
 clang/lib/AST/ByteCode/InterpState.cpp        |  12 +-
 clang/lib/AST/ByteCode/State.h                |   7 +-
 clang/lib/AST/Decl.cpp                        |  14 +-
 clang/lib/AST/ExprConstShared.h               |   4 +
 clang/lib/AST/ExprConstant.cpp                | 300 +++++++++++++-----
 clang/lib/Sema/CMakeLists.txt                 |   1 +
 clang/lib/Sema/Sema.cpp                       |   3 +-
 clang/lib/Sema/SemaConcept.cpp                |   4 +-
 clang/lib/Sema/SemaDecl.cpp                   |   5 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |   4 +-
 clang/lib/Sema/SemaEval.cpp                   |  48 +++
 clang/lib/Sema/SemaExpr.cpp                   |  13 +-
 clang/lib/Sema/SemaOverload.cpp               |   3 +-
 .../SemaCXX/constexpr-late-instantiation.cpp  | 227 ++++++++++++-
 20 files changed, 656 insertions(+), 121 deletions(-)
 create mode 100644 clang/include/clang/AST/SemaProxy.h
 create mode 100644 clang/lib/Sema/SemaEval.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8bb17755b28f5..11f2f668b8d40 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -810,6 +810,7 @@ Bug Fixes to C++ Support
 - Fixed a crash in constant evaluation using placement new on an array which 
was later initialized. (#GH196450)
 - Fixed an issue where Clang incorrectly accepted invalid unqualified uses of 
local nested class names outside their declaring scope. (#GH184622)
 - Fixed a crash when parsing invalid friend declaration with storage-class 
specifier. (#GH186569)
+- Instantiate constexpr functions as needed before they are evaluated. 
(#GH73232) (#GH35052) (#GH100897)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 2ea16d0ba6b03..26e4649d99252 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -70,6 +70,7 @@ class Module;
 class NamespaceDecl;
 class ParmVarDecl;
 class RecordDecl;
+class SemaProxy;
 class Stmt;
 class StringLiteral;
 class TagDecl;
@@ -1436,6 +1437,7 @@ class VarDecl : public DeclaratorDecl, public 
Redeclarable<VarDecl> {
 
 private:
   const APValue *evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes,
+                                   SemaProxy *SP,
                                    bool IsConstantInitialization) const;
 
 public:
@@ -1452,6 +1454,15 @@ class VarDecl : public DeclaratorDecl, public 
Redeclarable<VarDecl> {
   ///         not.
   bool evaluateDestruction(SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
 
+  /// Evaluate the destruction of this variable, the destruction of which is
+  /// required by language rules to be constant.
+  ///
+  /// \pre hasConstantInitialization()
+  /// \return \c true if this variable has constant destruction, \c false if
+  ///         not.
+  bool evaluateMandatedConstantDestruction(
+      SmallVectorImpl<PartialDiagnosticAt> &Notes, SemaProxy &SP) const;
+
   /// Determine whether this variable has constant initialization.
   ///
   /// This is only set in two cases: when the language semantics require
@@ -1469,7 +1480,7 @@ class VarDecl : public DeclaratorDecl, public 
Redeclarable<VarDecl> {
   /// constant initializer. Should only be called once, after completing the
   /// definition of the variable.
   bool checkForConstantInitialization(
-      SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
+      SmallVectorImpl<PartialDiagnosticAt> &Notes, SemaProxy *SP) const;
 
   void setInitStyle(InitializationStyle Style) {
     VarDeclBits.InitStyle = Style;
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 7c94c4d35641c..6ca1eb96b651a 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -55,6 +55,7 @@ namespace clang {
   class ObjCPropertyRefExpr;
   class OpaqueValueExpr;
   class ParmVarDecl;
+  class SemaProxy;
   class StringLiteral;
   class TargetInfo;
   class ValueDecl;
@@ -667,6 +668,13 @@ class Expr : public ValueStmt {
   bool EvaluateAsRValue(EvalResult &Result, const ASTContext &Ctx,
                         bool InConstantContext = false) const;
 
+  /// Evaluate an expression that is required by the language to be a constant
+  /// expression, and fold the resulting rvalue constant into Result. If the
+  /// expression is a glvalue, an lvalue-to-rvalue conversion will be applied.
+  bool EvaluateAsMandatedConstantRValue(EvalResult &Result,
+                                        const ASTContext &Ctx,
+                                        SemaProxy &SP) const;
+
   /// EvaluateAsBooleanCondition - Return true if this is a constant
   /// which we can fold and convert to a boolean condition using
   /// any crazy technique that we want to, even if the expression has
@@ -743,6 +751,12 @@ class Expr : public ValueStmt {
                              EvalResult &Result,
                              bool IsConstantInitializer) const;
 
+  /// Evaluate an expression that is required by the language to be a constant
+  /// expression.
+  bool EvaluateAsMandatedConstantInitializer(
+      EvalResult &Result, const ASTContext &Ctx, SemaProxy &SP,
+      const VarDecl *VD) const;
+
   /// EvaluateWithSubstitution - Evaluate an expression as if from the context
   /// of a call to the given function with the given arguments, inside an
   /// unevaluated context. Returns true if the expression could be folded to a
@@ -773,6 +787,12 @@ class Expr : public ValueStmt {
       EvalResult &Result, const ASTContext &Ctx,
       ConstantExprKind Kind = ConstantExprKind::Normal) const;
 
+  /// Evaluate an expression that is required by the language to be a constant
+  /// expression.
+  bool EvaluateAsMandatedConstantExpr(
+      EvalResult &Result, const ASTContext &Ctx, SemaProxy &SP,
+      ConstantExprKind Kind = ConstantExprKind::Normal) const;
+
   /// If the current Expr is a pointer, this will try to statically
   /// determine the number of bytes available where the pointer is pointing.
   /// Returns true if all of the above holds and we were able to figure out the
diff --git a/clang/include/clang/AST/SemaProxy.h 
b/clang/include/clang/AST/SemaProxy.h
new file mode 100644
index 0000000000000..ab6a87f1e32a5
--- /dev/null
+++ b/clang/include/clang/AST/SemaProxy.h
@@ -0,0 +1,40 @@
+//===--- SemaProxy.h - Interface to language semantics ---------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the SemaProxy interface, used during language-mandated
+//  constant evaluation to act on, and query, the representation of the program
+//  according to language-defined semantics.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_SEMA_PROXY_H
+#define LLVM_CLANG_AST_SEMA_PROXY_H
+
+#include "clang/Basic/SourceLocation.h"
+
+namespace clang {
+
+class FunctionDecl;
+
+/// Classes implementing SemaProxy present a restricted view of the (possibly
+/// mutating) actions and queries defined by language semantics against the
+/// representation of the program (i.e., the AST). Such a view is required in
+/// order to evaluate certain expressions (e.g., C++'s manifestly
+/// constant-evaluated expressions) according to language rules.
+class SemaProxy {
+public:
+  virtual ~SemaProxy() = default;
+
+  virtual void
+  instantiateFunctionDefinition(SourceLocation PointOfInstantiation,
+                                FunctionDecl *Function) = 0;
+};
+
+} // end namespace clang
+
+#endif // LLVM_CLANG_AST_SEMA_PROXY_H
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b8d760e7e0975..97b951bf6d672 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -30,6 +30,7 @@
 #include "clang/AST/ExternalASTSource.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/OperationKinds.h"
+#include "clang/AST/SemaProxy.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
@@ -907,6 +908,7 @@ class Sema final : public SemaBase {
   // 33. Types (SemaType.cpp)
   // 34. FixIt Helpers (SemaFixItUtils.cpp)
   // 35. Function Effects (SemaFunctionEffects.cpp)
+  // 36. Language-Mandated Constant Evaluation (SemaEval.cpp)
 
   /// \name Semantic Analysis
   /// Implementations are in Sema.cpp
@@ -15811,6 +15813,30 @@ class Sema final : public SemaBase {
   void performFunctionEffectAnalysis(TranslationUnitDecl *TU);
 
   ///@}
+
+  //
+  //
+  // -------------------------------------------------------------------------
+  //
+  //
+
+  /// \name Language-Mandated Constant Evaluation
+  /// Implementations are in SemaEval.cpp
+  ///@{
+public:
+  SemaProxy &getProxyForEval() const {
+    assert(ProxyForEval);
+    return *ProxyForEval;
+  }
+
+private:
+  std::unique_ptr<SemaProxy> ProxyForEval;
+
+  static SemaProxy *makeProxyForEval(Sema &SemaRef);
+
+  ///@}
+public:
+
 };
 
 DeductionFailureInfo
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 3772def47408f..13de84a26eedc 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/SemaProxy.h"
 #include "clang/Basic/DiagnosticSema.h"
 #include "clang/Basic/TargetInfo.h"
 #include "llvm/ADT/StringExtras.h"
@@ -1776,15 +1777,28 @@ bool CheckBitCast(InterpState &S, CodePtr OpPC, const 
Type *TargetType,
   return true;
 }
 
-static void compileFunction(InterpState &S, const Function *Func) {
-  const FunctionDecl *Definition;
-  if (!Func->getDecl()->getBody(Definition))
-    return;
-  if (!Definition)
+static void compileFunction(InterpState &S, const Function *Func,
+                            CodePtr OpPC) {
+  const FunctionDecl *Fn = Func->getDecl();
+
+  // [C++26] [temp.inst] p5
+  // [...] the function template specialization is implicitly instantiated
+  // when the specialization is referenced in a context that requires a 
function
+  // definition to exist or if the existence of the definition affects the
+  // semantics of the program.
+  if (FunctionDefinitionCanBeLazilyInstantiated(Fn) && S.inConstantContext()) {
+    SemaProxy *SP = S.getSemaProxy();
+    if (!SP)
+      return;
+    SP->instantiateFunctionDefinition(S.Current->getLocation(OpPC),
+                                      const_cast<FunctionDecl *>(Fn));
+  }
+  Fn = Fn->getDefinition();
+  if (!Fn)
     return;
 
   Compiler<ByteCodeEmitter>(S.getContext(), S.P)
-      .compileFunc(Definition, const_cast<Function *>(Func));
+      .compileFunc(Fn, const_cast<Function *>(Func));
 }
 
 bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
@@ -1811,7 +1825,7 @@ bool CallVar(InterpState &S, CodePtr OpPC, const Function 
*Func,
   }
 
   if (!Func->isFullyCompiled())
-    compileFunction(S, Func);
+    compileFunction(S, Func, OpPC);
 
   if (!CheckCallable(S, OpPC, Func))
     return false;
@@ -1898,7 +1912,7 @@ bool Call(InterpState &S, CodePtr OpPC, const Function 
*Func,
   }
 
   if (!Func->isFullyCompiled())
-    compileFunction(S, Func);
+    compileFunction(S, Func, OpPC);
 
   if (!CheckCallable(S, OpPC, Func))
     return cleanup();
@@ -2316,7 +2330,7 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t 
ArgSize,
   // because the Call/CallVirt below might access the instance pointer
   // but the Function's information about them is wrong.
   if (!F->isFullyCompiled())
-    compileFunction(S, F);
+    compileFunction(S, F, OpPC);
 
   if (!CheckCallable(S, OpPC, F))
     return false;
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp 
b/clang/lib/AST/ByteCode/InterpState.cpp
index 2d6ed98e6b52c..ac6d45df2b9e6 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -19,9 +19,9 @@ using namespace clang::interp;
 
 InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
                          Context &Ctx, SourceMapper *M)
-    : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(M), P(P), Stk(Stk),
-      Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame),
-      StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
+    : State(Ctx.getASTContext(), Parent.getSemaProxy(), 
Parent.getEvalStatus()),
+      M(M), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this),
+      Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
       InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) {
   InConstantContext = Parent.InConstantContext;
   CheckingPotentialConstantExpression =
@@ -32,9 +32,9 @@ InterpState::InterpState(const State &Parent, Program &P, 
InterpStack &Stk,
 
 InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
                          Context &Ctx, const Function *Func)
-    : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(nullptr), P(P),
-      Stk(Stk), Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame),
-      StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
+    : State(Ctx.getASTContext(), Parent.getSemaProxy(), 
Parent.getEvalStatus()),
+      M(nullptr), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this),
+      Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
       InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) {
   InConstantContext = Parent.InConstantContext;
   CheckingPotentialConstantExpression =
diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h
index df3afdf8cbc24..e9e94d4ffeb9b 100644
--- a/clang/lib/AST/ByteCode/State.h
+++ b/clang/lib/AST/ByteCode/State.h
@@ -20,6 +20,7 @@
 
 namespace clang {
 class OptionalDiagnostic;
+class SemaProxy;
 
 /// Kinds of access we can perform on an object, for diagnostics. Note that
 /// we consider a member function call to be a kind of access, even though
@@ -80,8 +81,8 @@ class SourceInfo;
 /// Interface for the VM to interact with the AST walker's context.
 class State {
 public:
-  State(ASTContext &ASTCtx, Expr::EvalStatus &EvalStatus)
-      : Ctx(ASTCtx), EvalStatus(EvalStatus) {}
+  State(ASTContext &ASTCtx, SemaProxy *Sema, Expr::EvalStatus &EvalStatus)
+      : Ctx(ASTCtx), Sema(Sema), EvalStatus(EvalStatus) {}
   virtual ~State();
 
   virtual const Frame *getCurrentFrame() = 0;
@@ -90,6 +91,7 @@ class State {
 
   Expr::EvalStatus &getEvalStatus() const { return EvalStatus; }
   ASTContext &getASTContext() const { return Ctx; }
+  SemaProxy *getSemaProxy() const { return Sema; }
   const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
 
   /// Note that we have had a side-effect, and determine whether we should
@@ -188,6 +190,7 @@ class State {
 
   EvaluationMode EvalMode;
   ASTContext &Ctx;
+  SemaProxy *Sema;
   Expr::EvalStatus &EvalStatus;
 
 private:
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index b23bf73ae803c..ed1c29198acfc 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -32,6 +32,7 @@
 #include "clang/AST/Randstruct.h"
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/Redeclarable.h"
+#include "clang/AST/SemaProxy.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/TemplateBase.h"
 #include "clang/AST/Type.h"
@@ -2552,11 +2553,13 @@ EvaluatedStmt *VarDecl::getEvaluatedStmt() const {
 }
 
 const APValue *VarDecl::evaluateValue() const {
-  return evaluateValueImpl(/*Notes=*/nullptr, hasConstantInitialization());
+  return evaluateValueImpl(/*Notes=*/nullptr, /*Sema=*/nullptr,
+                           hasConstantInitialization());
 }
 
 const APValue *
 VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes,
+                           SemaProxy *SP,
                            bool IsConstantInitialization) const {
   EvaluatedStmt *Eval = ensureEvaluatedStmt();
 
@@ -2580,7 +2583,10 @@ 
VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes,
   Expr::EvalResult EStatus;
   EStatus.Diag = Notes;
   bool Result =
-      Init->EvaluateAsInitializer(Ctx, this, EStatus, 
IsConstantInitialization);
+      isConstexpr() ?
+        Init->EvaluateAsMandatedConstantInitializer(EStatus, Ctx, *SP, this)
+      : Init->EvaluateAsInitializer(Ctx, this, EStatus,
+                                    IsConstantInitialization);
   Eval->Evaluated = std::move(EStatus.Val);
 
   // In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't
@@ -2644,7 +2650,7 @@ bool VarDecl::hasConstantInitialization() const {
 }
 
 bool VarDecl::checkForConstantInitialization(
-    SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
+    SmallVectorImpl<PartialDiagnosticAt> &Notes, SemaProxy *SP) const {
   EvaluatedStmt *Eval = ensureEvaluatedStmt();
   // If we ask for the value before we know whether we have a constant
   // initializer, we can compute the wrong value (for example, due to
@@ -2659,7 +2665,7 @@ bool VarDecl::checkForConstantInitialization(
 
   // Evaluate the initializer to check whether it's a constant expression.
   Eval->HasConstantInitialization =
-      evaluateValueImpl(&Notes, true) && Notes.empty();
+      evaluateValueImpl(&Notes, SP, true) && Notes.empty();
 
   // If evaluation as a constant initializer failed, allow re-evaluation as a
   // non-constant initializer if we later find we want the value.
diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h
index 619c79a1408f3..45c7db846fc70 100644
--- a/clang/lib/AST/ExprConstShared.h
+++ b/clang/lib/AST/ExprConstShared.h
@@ -29,6 +29,7 @@ class LangOptions;
 class ASTContext;
 class CharUnits;
 class Expr;
+class FunctionDecl;
 } // namespace clang
 using namespace clang;
 /// Values returned by __builtin_classify_type, chosen to match the values
@@ -89,4 +90,7 @@ std::optional<llvm::APFloat>
 EvalScalarMinMaxFp(const llvm::APFloat &A, const llvm::APFloat &B,
                    std::optional<llvm::APSInt> RoundingMode, bool IsMin);
 
+/// Whether we can instantiate FD during constant evaluation
+bool FunctionDefinitionCanBeLazilyInstantiated(const FunctionDecl *FD);
+
 #endif
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index dde3b8bab43ec..306786746b0e3 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -48,6 +48,7 @@
 #include "clang/AST/OSLog.h"
 #include "clang/AST/OptionalDiagnostic.h"
 #include "clang/AST/RecordLayout.h"
+#include "clang/AST/SemaProxy.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
@@ -909,9 +910,10 @@ namespace {
     /// initialization.
     uint64_t ArrayInitIndex = -1;
 
-    EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode)
-        : State(const_cast<ASTContext &>(C), S), CurrentCall(nullptr),
-          CallStackDepth(0), NextCallIndex(1),
+    EvalInfo(const ASTContext &C, SemaProxy *Sema, Expr::EvalStatus &S,
+             EvaluationMode Mode)
+        : State(const_cast<ASTContext &>(C), Sema, S),
+          CurrentCall(nullptr), CallStackDepth(0), NextCallIndex(1),
           StepsLeft(C.getLangOpts().ConstexprStepLimit),
           EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp),
           BottomFrame(*this, SourceLocation(), /*Callee=*/nullptr,
@@ -6997,6 +6999,30 @@ static bool handleTrivialCopy(EvalInfo &Info, const 
ParmVarDecl *Param,
       CopyObjectRepresentation);
 }
 
+bool FunctionDefinitionCanBeLazilyInstantiated(const FunctionDecl *FD) {
+  if (FD->isDefined() || !FD->isImplicitlyInstantiable() || !FD->isConstexpr())
+    return false;
+
+  FunctionDecl *Pattern = FD->getTemplateInstantiationPattern();
+  return Pattern && Pattern->isDefined();
+}
+
+static void TryInstantiateFunctionBeforeCall(const FunctionDecl *FD,
+                                             EvalInfo &Info,
+                                             SourceLocation Loc) {
+
+  // [C++26] [temp.inst] p5
+  // [...] the function template specialization is implicitly instantiated
+  // when the specialization is referenced in a context that requires a 
function
+  // definition to exist or if the existence of the definition affects the
+  // semantics of the program.
+
+  SemaProxy *SP = Info.getSemaProxy();
+  if (SP && FunctionDefinitionCanBeLazilyInstantiated(FD) &&
+      Info.InConstantContext)
+    SP->instantiateFunctionDefinition(Loc, const_cast<FunctionDecl *>(FD));
+}
+
 /// Evaluate a function call.
 static bool HandleFunctionCall(SourceLocation CallLoc,
                                const FunctionDecl *Callee,
@@ -7390,6 +7416,8 @@ static bool HandleDestructionImpl(EvalInfo &Info, 
SourceRange CallRange,
   if (!Info.CheckCallLimit(CallRange.getBegin()))
     return false;
 
+  TryInstantiateFunctionBeforeCall(DD, Info, CallRange.getBegin());
+
   const FunctionDecl *Definition = nullptr;
   const Stmt *Body = DD->getBody(Definition);
 
@@ -8857,9 +8885,12 @@ class ExprEvaluatorBase
              CallScope.destroy();
     }
 
+    SourceLocation Loc = E->getExprLoc();
+
+    TryInstantiateFunctionBeforeCall(FD, Info, Loc);
+
     const FunctionDecl *Definition = nullptr;
     Stmt *Body = FD->getBody(Definition);
-    SourceLocation Loc = E->getExprLoc();
 
     // Treat the object argument as `this` when evaluating defaulted
     // special menmber functions
@@ -11420,6 +11451,8 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const 
CXXConstructExpr *E,
     return handleDefaultInitValue(T, Result);
   }
 
+  TryInstantiateFunctionBeforeCall(FD, Info, E->getBeginLoc());
+
   const FunctionDecl *Definition = nullptr;
   auto Body = FD->getBody(Definition);
 
@@ -11461,6 +11494,8 @@ bool RecordExprEvaluator::VisitCXXInheritedCtorInitExpr(
   if (FD->isInvalidDecl() || FD->getParent()->isInvalidDecl())
     return false;
 
+  TryInstantiateFunctionBeforeCall(FD, Info, E->getBeginLoc());
+
   const FunctionDecl *Definition = nullptr;
   auto Body = FD->getBody(Definition);
 
@@ -21458,11 +21493,23 @@ 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");
-  EvalInfo Info(Ctx, Result, EvaluationMode::IgnoreSideEffects);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Result,
+                EvaluationMode::IgnoreSideEffects);
   Info.InConstantContext = InConstantContext;
   return ::EvaluateAsRValue(this, Result, Ctx, Info);
 }
 
+bool Expr::EvaluateAsMandatedConstantRValue(EvalResult &Result,
+                                            const ASTContext &Ctx,
+                                            SemaProxy &SP) const {
+  assert(!isValueDependent() &&
+         "Expression evaluator can't be called on a dependent expression.");
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsMandatedConstantRValue");
+  EvalInfo Info(Ctx, &SP, Result, EvaluationMode::IgnoreSideEffects);
+  Info.InConstantContext = true;
+  return ::EvaluateAsRValue(this, Result, Ctx, Info);
+}
+
 bool Expr::EvaluateAsBooleanCondition(bool &Result, const ASTContext &Ctx,
                                       bool InConstantContext) const {
   assert(!isValueDependent() &&
@@ -21479,7 +21526,8 @@ bool Expr::EvaluateAsInt(EvalResult &Result, const 
ASTContext &Ctx,
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
   ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsInt");
-  EvalInfo Info(Ctx, Result, EvaluationMode::IgnoreSideEffects);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Result,
+                EvaluationMode::IgnoreSideEffects);
   Info.InConstantContext = InConstantContext;
   return ::EvaluateAsInt(this, Result, Ctx, AllowSideEffects, Info);
 }
@@ -21490,7 +21538,8 @@ 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");
-  EvalInfo Info(Ctx, Result, EvaluationMode::IgnoreSideEffects);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Result,
+                EvaluationMode::IgnoreSideEffects);
   Info.InConstantContext = InConstantContext;
   return ::EvaluateAsFixedPoint(this, Result, Ctx, AllowSideEffects, Info);
 }
@@ -21521,7 +21570,7 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const 
ASTContext &Ctx,
          "Expression evaluator can't be called on a dependent expression.");
 
   ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsLValue");
-  EvalInfo Info(Ctx, Result, EvaluationMode::ConstantFold);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Result, EvaluationMode::ConstantFold);
   Info.InConstantContext = InConstantContext;
   LValue LV;
   CheckedTemporaries CheckedTemps;
@@ -21548,11 +21597,12 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const 
ASTContext &Ctx,
   return true;
 }
 
-static bool EvaluateDestruction(const ASTContext &Ctx, APValue::LValueBase 
Base,
+static bool EvaluateDestruction(const ASTContext &Ctx, SemaProxy *SP,
+                                APValue::LValueBase Base,
                                 APValue DestroyedValue, QualType Type,
                                 SourceLocation Loc, Expr::EvalStatus &EStatus,
                                 bool IsConstantDestruction) {
-  EvalInfo Info(Ctx, EStatus,
+  EvalInfo Info(Ctx, SP, EStatus,
                 IsConstantDestruction ? EvaluationMode::ConstantExpression
                                       : EvaluationMode::ConstantFold);
   Info.setEvaluatingDecl(Base, DestroyedValue,
@@ -21572,36 +21622,27 @@ static bool EvaluateDestruction(const ASTContext 
&Ctx, APValue::LValueBase Base,
   return true;
 }
 
-bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx,
-                                  ConstantExprKind Kind) const {
-  assert(!isValueDependent() &&
-         "Expression evaluator can't be called on a dependent expression.");
-  bool IsConst;
-  if (FastEvaluateAsRValue(this, Result.Val, Ctx, IsConst) &&
-      Result.Val.hasValue())
-    return true;
-
-  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsConstantExpr");
-  EvaluationMode EM = EvaluationMode::ConstantExpression;
-  EvalInfo Info(Ctx, Result, EM);
+static bool EvaluateConstantExpr(Expr::EvalResult &Result,
+                                 const ASTContext &Ctx, EvalInfo &Info,
+                                 const Expr *E, ConstantExprKind Kind) {
   Info.InConstantContext = true;
 
   if (Info.EnableNewConstInterp) {
-    if (!Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, Kind))
+    if (!Info.Ctx.getInterpContext().evaluate(Info, E, Result.Val, Kind))
       return false;
-    return CheckConstantExpression(Info, getExprLoc(),
-                                   getStorageType(Ctx, this), Result.Val, 
Kind);
+    return CheckConstantExpression(Info, E->getExprLoc(),
+                                   getStorageType(Ctx, E), Result.Val, Kind);
   }
 
   // The type of the object we're initializing is 'const T' for a class NTTP.
-  QualType T = getType();
+  QualType T = E->getType();
   if (Kind == ConstantExprKind::ClassTemplateArgument)
     T.addConst();
 
   // If we're evaluating a prvalue, fake up a MaterializeTemporaryExpr to
   // represent the result of the evaluation. CheckConstantExpression ensures
   // this doesn't escape.
-  MaterializeTemporaryExpr BaseMTE(T, const_cast<Expr*>(this), true);
+  MaterializeTemporaryExpr BaseMTE(T, const_cast<Expr*>(E), true);
   APValue::LValueBase Base(&BaseMTE);
   Info.setEvaluatingDecl(Base, Result.Val);
 
@@ -21612,14 +21653,14 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, 
const ASTContext &Ctx,
   // So we need to make sure temporary objects are destroyed after having
   // evaluating the expression (per C++23 [class.temporary]/p4).
   FullExpressionRAII Scope(Info);
-  if (!::EvaluateInPlace(Result.Val, Info, LVal, this) ||
+  if (!::EvaluateInPlace(Result.Val, Info, LVal, E) ||
       Result.HasSideEffects || !Scope.destroy())
     return false;
 
   if (!Info.discardCleanups())
     llvm_unreachable("Unhandled cleanup; missing full expression marker?");
 
-  if (!CheckConstantExpression(Info, getExprLoc(), getStorageType(Ctx, this),
+  if (!CheckConstantExpression(Info, E->getExprLoc(), getStorageType(Ctx, E),
                                Result.Val, Kind))
     return false;
   if (!CheckMemoryLeaks(Info))
@@ -21628,8 +21669,8 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, 
const ASTContext &Ctx,
   // If this is a class template argument, it's required to have constant
   // destruction too.
   if (Kind == ConstantExprKind::ClassTemplateArgument &&
-      (!EvaluateDestruction(Ctx, Base, Result.Val, T, getBeginLoc(), Result,
-                            true) ||
+      (!EvaluateDestruction(Ctx, Info.getSemaProxy(), Base, Result.Val, T,
+                            E->getBeginLoc(), Result, true) ||
        Result.HasSideEffects)) {
     // FIXME: Prefix a note to indicate that the problem is lack of constant
     // destruction.
@@ -21639,34 +21680,50 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, 
const ASTContext &Ctx,
   return true;
 }
 
-bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD,
-                                 Expr::EvalResult &EStatus,
-                                 bool IsConstantInitialization) const {
+bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx,
+                                  ConstantExprKind Kind) const {
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
-  assert(VD && "Need a valid VarDecl");
+  bool IsConst;
+  if (FastEvaluateAsRValue(this, Result.Val, Ctx, IsConst) &&
+      Result.Val.hasValue())
+    return true;
 
-  llvm::TimeTraceScope TimeScope("EvaluateAsInitializer", [&] {
-    std::string Name;
-    llvm::raw_string_ostream OS(Name);
-    VD->printQualifiedName(OS);
-    return Name;
-  });
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsConstantExpr");
+  EvaluationMode EM = EvaluationMode::ConstantExpression;
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Result, EM);
 
-  EvalInfo Info(Ctx, EStatus,
-                (IsConstantInitialization &&
-                 (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
-                    ? EvaluationMode::ConstantExpression
-                    : EvaluationMode::ConstantFold);
-  Info.setEvaluatingDecl(VD, EStatus.Val);
-  Info.InConstantContext = IsConstantInitialization;
+  return ::EvaluateConstantExpr(Result, Ctx, Info, this, Kind);
+}
+
+bool Expr::EvaluateAsMandatedConstantExpr(EvalResult &Result,
+                                         const ASTContext &Ctx,
+                                         SemaProxy &Sema,
+                                         ConstantExprKind Kind) const {
+  assert(!isValueDependent() &&
+         "Expression evaluator can't be called on a dependent expression.");
+  bool IsConst;
+  if (FastEvaluateAsRValue(this, Result.Val, Ctx, IsConst) &&
+      Result.Val.hasValue())
+    return true;
+
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsMandatedConstantExpr");
+  EvaluationMode EM = EvaluationMode::ConstantExpression;
+  EvalInfo Info(Ctx, &Sema, Result, EM);
+
+  return ::EvaluateConstantExpr(Result, Ctx, Info, this, Kind);
+}
 
+static bool EvaluateInitializer(const ASTContext &Ctx, EvalInfo &Info,
+                                const VarDecl *VD, const Expr *E,
+                                Expr::EvalResult &EStatus,
+                                bool IsConstantInitialization) {
   SourceLocation DeclLoc = VD->getLocation();
   QualType DeclTy = VD->getType();
 
   if (Info.EnableNewConstInterp) {
     auto &InterpCtx = Ctx.getInterpContext();
-    if (!InterpCtx.evaluateAsInitializer(Info, VD, this, EStatus.Val))
+    if (!InterpCtx.evaluateAsInitializer(Info, VD, E, EStatus.Val))
       return false;
 
     return CheckConstantExpression(Info, DeclLoc, DeclTy, EStatus.Val,
@@ -21686,7 +21743,7 @@ bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, 
const VarDecl *VD,
       // serialization code calls ParmVarDecl::getDefaultArg() which strips the
       // outermost FullExpr, such as ExprWithCleanups.
       FullExpressionRAII Scope(Info);
-      if (!EvaluateInPlace(EStatus.Val, Info, LVal, this,
+      if (!EvaluateInPlace(EStatus.Val, Info, LVal, E,
                            /*AllowNonLiteralTypes=*/true) ||
           EStatus.HasSideEffects)
         return false;
@@ -21705,48 +21762,115 @@ bool Expr::EvaluateAsInitializer(const ASTContext 
&Ctx, const VarDecl *VD,
          CheckMemoryLeaks(Info);
 }
 
-bool VarDecl::evaluateDestruction(
-    SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
-  Expr::EvalStatus EStatus;
-  EStatus.Diag = &Notes;
+bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD,
+                                 Expr::EvalResult &EStatus,
+                                 bool IsConstantInitialization) const {
+  assert(!isValueDependent() &&
+         "Expression evaluator can't be called on a dependent expression.");
+  assert(VD && "Need a valid VarDecl");
 
-  // Only treat the destruction as constant destruction if we formally have
-  // constant initialization (or are usable in a constant expression).
-  bool IsConstantDestruction = hasConstantInitialization();
-  ASTContext &Ctx = getASTContext();
+  llvm::TimeTraceScope TimeScope("EvaluateAsInitializer", [&] {
+    std::string Name;
+    llvm::raw_string_ostream OS(Name);
+    VD->printQualifiedName(OS);
+    return Name;
+  });
+
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, EStatus,
+                (IsConstantInitialization &&
+                 (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
+                    ? EvaluationMode::ConstantExpression
+                    : EvaluationMode::ConstantFold);
+  Info.setEvaluatingDecl(VD, EStatus.Val);
+  Info.InConstantContext = IsConstantInitialization;
+
+  return ::EvaluateInitializer(Ctx, Info, VD, this, EStatus,
+                               IsConstantInitialization);
+}
+
+bool Expr::EvaluateAsMandatedConstantInitializer(EvalResult &EStatus,
+                                                 const ASTContext &Ctx,
+                                                 SemaProxy &Sema,
+                                                 const VarDecl *VD) const {
+  assert(!isValueDependent() &&
+         "Expression evaluator can't be called on a dependent expression.");
+  assert(VD && "Need a valid VarDecl");
+
+  llvm::TimeTraceScope TimeScope("EvaluateAsMandatedConstantInitializer", [&] {
+    std::string Name;
+    llvm::raw_string_ostream OS(Name);
+    VD->printQualifiedName(OS);
+    return Name;
+  });
+
+  EvalInfo Info(Ctx, &Sema, EStatus, EvaluationMode::ConstantExpression);
+  Info.setEvaluatingDecl(VD, EStatus.Val);
+  Info.InConstantContext = true;
+
+  return ::EvaluateInitializer(Ctx, Info, VD, this, EStatus,
+                               /*IsConstantInitialization=*/true);
+}
+
+static bool evaluateDestruction(Expr::EvalStatus EStatus, EvalInfo &Info,
+                                const VarDecl *VD, bool IsConstantDestruction) 
{
+  ASTContext &Ctx = VD->getASTContext();
+
+  Info.InConstantContext = IsConstantDestruction;
 
   // Make a copy of the value for the destructor to mutate, if we know it.
   // Otherwise, treat the value as default-initialized; if the destructor works
   // anyway, then the destruction is constant (and must be essentially empty).
   APValue DestroyedValue;
-  if (getEvaluatedValue())
-    DestroyedValue = *getEvaluatedValue();
-  else if (!handleDefaultInitValue(getType(), DestroyedValue))
+  if (VD->getEvaluatedValue())
+    DestroyedValue = *VD->getEvaluatedValue();
+  else if (!handleDefaultInitValue(VD->getType(), DestroyedValue))
     return false;
 
   if (Ctx.getLangOpts().EnableNewConstInterp) {
-    EvalInfo Info(Ctx, EStatus,
-                  IsConstantDestruction ? EvaluationMode::ConstantExpression
-                                        : EvaluationMode::ConstantFold);
-    Info.setEvaluatingDecl(this, DestroyedValue,
+    Info.setEvaluatingDecl(VD, DestroyedValue,
                            EvalInfo::EvaluatingDeclKind::Dtor);
-    Info.InConstantContext = IsConstantDestruction;
-    if (!Ctx.getInterpContext().evaluateDestruction(Info, this,
+    if (!Ctx.getInterpContext().evaluateDestruction(Info, VD,
                                                     std::move(DestroyedValue)))
       return false;
-    ensureEvaluatedStmt()->HasConstantDestruction = true;
+    VD->ensureEvaluatedStmt()->HasConstantDestruction = true;
     return true;
   }
 
-  if (!EvaluateDestruction(Ctx, this, std::move(DestroyedValue), getType(),
-                           getLocation(), EStatus, IsConstantDestruction) ||
+  if (!EvaluateDestruction(Ctx, Info.getSemaProxy(), VD,
+                           std::move(DestroyedValue), VD->getType(),
+                           VD->getLocation(), EStatus, IsConstantDestruction) 
||
       EStatus.HasSideEffects)
     return false;
 
-  ensureEvaluatedStmt()->HasConstantDestruction = true;
+  VD->ensureEvaluatedStmt()->HasConstantDestruction = true;
   return true;
 }
 
+bool VarDecl::evaluateDestruction(
+    SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
+  Expr::EvalStatus EStatus;
+  EStatus.Diag = &Notes;
+
+  bool IsConstantDestruction = hasConstantInitialization();
+
+  EvalInfo Info(getASTContext(), /*Sema=*/nullptr, EStatus,
+                IsConstantDestruction ? EvaluationMode::ConstantExpression
+                                      : EvaluationMode::ConstantFold);
+  return ::evaluateDestruction(EStatus, Info, this, IsConstantDestruction);
+}
+
+bool VarDecl::evaluateMandatedConstantDestruction(
+    SmallVectorImpl<PartialDiagnosticAt> &Notes, SemaProxy &SP) const {
+  Expr::EvalStatus EStatus;
+  EStatus.Diag = &Notes;
+
+  bool IsConstantDestruction = hasConstantInitialization();
+
+  EvalInfo Info(getASTContext(), &SP, EStatus,
+                EvaluationMode::ConstantExpression);
+  return ::evaluateDestruction(EStatus, Info, this, IsConstantDestruction);
+}
+
 /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be
 /// constant folded, but discard the result.
 bool Expr::isEvaluatable(const ASTContext &Ctx, SideEffectsKind SEK) const {
@@ -21764,7 +21888,8 @@ APSInt Expr::EvaluateKnownConstInt(const ASTContext 
&Ctx) const {
 
   ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateKnownConstInt");
   EvalResult EVResult;
-  EvalInfo Info(Ctx, EVResult, EvaluationMode::IgnoreSideEffects);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, EVResult,
+                EvaluationMode::IgnoreSideEffects);
   Info.InConstantContext = true;
 
   bool Result = ::EvaluateAsRValue(this, EVResult, Ctx, Info);
@@ -21783,7 +21908,8 @@ APSInt Expr::EvaluateKnownConstIntCheckOverflow(
   ExprTimeTraceScope TimeScope(this, Ctx, 
"EvaluateKnownConstIntCheckOverflow");
   EvalResult EVResult;
   EVResult.Diag = Diag;
-  EvalInfo Info(Ctx, EVResult, EvaluationMode::IgnoreSideEffects);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, EVResult,
+                EvaluationMode::IgnoreSideEffects);
   Info.InConstantContext = true;
   Info.CheckingForUndefinedBehavior = true;
 
@@ -21803,7 +21929,8 @@ void Expr::EvaluateForOverflow(const ASTContext &Ctx) 
const {
   bool IsConst;
   EvalResult EVResult;
   if (!FastEvaluateAsRValue(this, EVResult.Val, Ctx, IsConst)) {
-    EvalInfo Info(Ctx, EVResult, EvaluationMode::IgnoreSideEffects);
+    EvalInfo Info(Ctx, /*Sema=*/nullptr, EVResult,
+                  EvaluationMode::IgnoreSideEffects);
     Info.CheckingForUndefinedBehavior = true;
     (void)::EvaluateAsRValue(Info, this, EVResult.Val);
   }
@@ -21857,7 +21984,8 @@ 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;
   Expr::EvalStatus Status;
-  EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Status,
+                EvaluationMode::ConstantExpression);
 
   Info.InConstantContext = true;
   if (!::EvaluateAsRValue(E, EVResult, Ctx, Info) || EVResult.HasSideEffects ||
@@ -22361,7 +22489,8 @@ Expr::getIntegerConstantExpr(const ASTContext &Ctx) 
const {
   // value.
   EvalResult ExprResult;
   Expr::EvalStatus Status;
-  EvalInfo Info(Ctx, Status, EvaluationMode::IgnoreSideEffects);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Status,
+                EvaluationMode::IgnoreSideEffects);
   Info.InConstantContext = true;
 
   if (!::EvaluateAsInt(this, ExprResult, Ctx, SE_AllowSideEffects, Info))
@@ -22395,7 +22524,8 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, 
APValue *Result) const {
 
   // Build evaluation settings.
   Expr::EvalStatus Status;
-  EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Status,
+                EvaluationMode::ConstantExpression);
 
   bool IsConstExpr =
       ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch) &&
@@ -22422,7 +22552,8 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, 
ASTContext &Ctx,
   });
 
   Expr::EvalStatus Status;
-  EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpressionUnevaluated);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Status,
+                EvaluationMode::ConstantExpressionUnevaluated);
   Info.InConstantContext = true;
 
   LValue ThisVal;
@@ -22498,7 +22629,7 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl 
*FD,
   Expr::EvalStatus Status;
   Status.Diag = &Diags;
 
-  EvalInfo Info(FD->getASTContext(), Status,
+  EvalInfo Info(FD->getASTContext(), /*Sema=*/nullptr, Status,
                 EvaluationMode::ConstantExpression);
   Info.InConstantContext = true;
   Info.CheckingPotentialConstantExpression = true;
@@ -22548,7 +22679,7 @@ bool Expr::isPotentialConstantExprUnevaluated(Expr *E,
   Expr::EvalStatus Status;
   Status.Diag = &Diags;
 
-  EvalInfo Info(FD->getASTContext(), Status,
+  EvalInfo Info(FD->getASTContext(), /*Sema=*/nullptr, Status,
                 EvaluationMode::ConstantExpressionUnevaluated);
   Info.InConstantContext = true;
   Info.CheckingPotentialConstantExpression = true;
@@ -22573,7 +22704,7 @@ std::optional<uint64_t> 
Expr::tryEvaluateObjectSize(const ASTContext &Ctx,
     return std::nullopt;
 
   Expr::EvalStatus Status;
-  EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, EvaluationMode::ConstantFold);
   if (Info.EnableNewConstInterp)
     return Info.Ctx.getInterpContext().tryEvaluateObjectSize(Info, this, Type);
   return tryEvaluateBuiltinObjectSize(this, Type, Info);
@@ -22632,7 +22763,7 @@ EvaluateBuiltinStrLen(const Expr *E, EvalInfo &Info,
 
 std::optional<std::string> Expr::tryEvaluateString(ASTContext &Ctx) const {
   Expr::EvalStatus Status;
-  EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, EvaluationMode::ConstantFold);
   std::string StringResult;
 
   if (Info.EnableNewConstInterp) {
@@ -22652,7 +22783,8 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, 
T &Result,
                                           const Expr *PtrExpression,
                                           ASTContext &Ctx,
                                           Expr::EvalResult &Status) {
-  EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Status,
+                 EvaluationMode::ConstantExpression);
   Info.InConstantContext = true;
 
   if (Info.EnableNewConstInterp)
@@ -22720,7 +22852,7 @@ bool Expr::EvaluateCharRangeAsString(APValue &Result,
 
 std::optional<uint64_t> Expr::tryEvaluateStrLen(const ASTContext &Ctx) const {
   Expr::EvalStatus Status;
-  EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold);
+  EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, EvaluationMode::ConstantFold);
 
   if (Info.EnableNewConstInterp)
     return Info.Ctx.getInterpContext().evaluateStrlen(Info, this);
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 0ebf56ecffe69..a9d3cf9b02f53 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -52,6 +52,7 @@ add_clang_library(clangSema
   SemaDeclAttr.cpp
   SemaDeclCXX.cpp
   SemaDeclObjC.cpp
+  SemaEval.cpp
   SemaExceptionSpec.cpp
   SemaExpr.cpp
   SemaExprCXX.cpp
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 78fbc9e31842d..d086718bcb235 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -324,7 +324,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer 
&consumer,
       GlobalNewDeleteDeclared(false), DisableTypoCorrection(false),
       TyposCorrected(0), IsBuildingRecoveryCallExpr(false),
       CurrentInstantiationScope(nullptr), NonInstantiationEntries(0),
-      ArgPackSubstIndex(std::nullopt), SatisfactionCache(Context) {
+      ArgPackSubstIndex(std::nullopt), SatisfactionCache(Context),
+      ProxyForEval(makeProxyForEval(*this)) {
   assert(pp.TUKind == TUKind);
   TUScope = nullptr;
 
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index f41f37b8a3d19..acbcd04e808ed 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -795,8 +795,8 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
   SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
   Expr::EvalResult EvalResult;
   EvalResult.Diag = &EvaluationDiags;
-  if (!SubstitutedAtomicExpr.get()->EvaluateAsConstantExpr(EvalResult,
-                                                           S.Context) ||
+  if (!SubstitutedAtomicExpr.get()->EvaluateAsMandatedConstantExpr(
+          EvalResult, S.Context, S.getProxyForEval()) ||
       !EvaluationDiags.empty()) {
     // C++2a [temp.constr.atomic]p1
     //   ...E shall be a constant expression of type bool.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d45c3eb35094f..f6f7e4beb2bd6 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15090,7 +15090,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl 
*var) {
                 << Init->getSourceRange();
           }
         }
-        (void)var->checkForConstantInitialization(Notes);
+        (void)var->checkForConstantInitialization(Notes, &getProxyForEval());
         Notes.clear();
       } else if (CacheCulprit) {
         Notes.emplace_back(CacheCulprit->getExprLoc(),
@@ -15099,7 +15099,8 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl 
*var) {
       }
     } else {
       // Evaluate the initializer to see if it's a constant initializer.
-      HasConstInit = var->checkForConstantInitialization(Notes);
+      HasConstInit = var->checkForConstantInitialization(Notes,
+                                                         &getProxyForEval());
     }
 
     if (HasConstInit) {
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 418ff01f3d98a..984de227220f8 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -16516,8 +16516,8 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, 
CXXRecordDecl *ClassDecl) {
     if (VD->getInit() && !VD->getInit()->isValueDependent())
       HasConstantInit = VD->evaluateValue();
     SmallVector<PartialDiagnosticAt, 8> Notes;
-    if (!VD->evaluateDestruction(Notes) && VD->isConstexpr() &&
-        HasConstantInit) {
+    if (!VD->evaluateMandatedConstantDestruction(Notes, getProxyForEval()) &&
+        VD->isConstexpr() && HasConstantInit) {
       Diag(VD->getLocation(),
            diag::err_constexpr_var_requires_const_destruction) << VD;
       for (const PartialDiagnosticAt &Note : Notes)
diff --git a/clang/lib/Sema/SemaEval.cpp b/clang/lib/Sema/SemaEval.cpp
new file mode 100644
index 0000000000000..b43783bfb9091
--- /dev/null
+++ b/clang/lib/Sema/SemaEval.cpp
@@ -0,0 +1,48 @@
+//=== SemaEval.cpp - Sema handling of effectual constant evaluation 
-------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements Sema handling of language-mandated constant evaluation,
+// which can perform actions and queries against the AST.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/SemaProxy.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/SemaInternal.h"
+
+using namespace clang;
+
+namespace {
+
+class SemaProxyImpl : public clang::SemaProxy {
+public:
+  SemaProxyImpl(clang::Sema &SemaRef) : SemaRef(SemaRef) {}
+
+  void instantiateFunctionDefinition(SourceLocation PointOfInstantiation,
+                                     FunctionDecl *Function) override;
+
+private:
+  Sema &SemaRef;
+};
+
+void SemaProxyImpl::instantiateFunctionDefinition(
+    SourceLocation PointOfInstantiation, FunctionDecl *Function) {
+  SemaRef.InstantiateFunctionDefinition(
+        PointOfInstantiation, Function, /*Recursive=*/true,
+        /*DefinitionRequired=*/true, /*AtEndOfTU=*/false);
+}
+
+} // anonymous namespace
+
+namespace clang {
+
+SemaProxy *Sema::makeProxyForEval(Sema &SemaRef) {
+  return new SemaProxyImpl(SemaRef);
+}
+} // end namespace clang
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 7c868d176e803..d53b01d551e1b 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18050,7 +18050,8 @@ Sema::VerifyIntegerConstantExpression(Expr *E, 
llvm::APSInt *Result,
   // Try to evaluate the expression, and produce diagnostics explaining why 
it's
   // not a constant expression as a side-effect.
   bool Folded =
-      E->EvaluateAsRValue(EvalResult, Context, /*isConstantContext*/ true) &&
+      E->EvaluateAsMandatedConstantRValue(EvalResult, Context,
+                                          getProxyForEval()) &&
       EvalResult.Val.isInt() && !EvalResult.HasSideEffects &&
       (!getLangOpts().CPlusPlus || !EvalResult.HasUndefinedBehavior);
 
@@ -18352,8 +18353,9 @@ ExprResult Sema::CheckForImmediateInvocation(ExprResult 
E, FunctionDecl *Decl) {
   APValue Cached;
   auto CheckConstantExpressionAndKeepResult = [&]() {
     Expr::EvalResult Eval;
-    bool Res = E.get()->EvaluateAsConstantExpr(
-        Eval, getASTContext(), ConstantExprKind::ImmediateInvocation);
+    bool Res = E.get()->EvaluateAsMandatedConstantExpr(
+        Eval, getASTContext(), getProxyForEval(),
+        ConstantExprKind::ImmediateInvocation);
     if (Res && !Eval.DiagEmitted) {
       Cached = std::move(Eval.Val);
       return true;
@@ -18408,8 +18410,9 @@ static void EvaluateAndDiagnoseImmediateInvocation(
   Expr::EvalResult Eval;
   Eval.Diag = &Notes;
   ConstantExpr *CE = Candidate.getPointer();
-  bool Result = CE->EvaluateAsConstantExpr(
-      Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation);
+  bool Result = CE->EvaluateAsMandatedConstantExpr(
+      Eval, SemaRef.getASTContext(), SemaRef.getProxyForEval(),
+      ConstantExprKind::ImmediateInvocation);
   if (!Result || !Notes.empty()) {
     SemaRef.FailedImmediateInvocations.insert(CE);
     Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c663765573612..25ba2dc229be6 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6708,7 +6708,8 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, 
QualType T, APValue &Value,
   else
     Kind = ConstantExprKind::Normal;
 
-  if (!E->EvaluateAsConstantExpr(Eval, Context, Kind) ||
+  if (!E->EvaluateAsMandatedConstantExpr(Eval, Context, getProxyForEval(),
+                                         Kind) ||
       (RequireInt && !Eval.Val.isInt())) {
     // The expression can't be folded, so we can't keep it at this position in
     // the AST.
diff --git a/clang/test/SemaCXX/constexpr-late-instantiation.cpp 
b/clang/test/SemaCXX/constexpr-late-instantiation.cpp
index 9aec0c90e61dc..e893d1f835ddf 100644
--- a/clang/test/SemaCXX/constexpr-late-instantiation.cpp
+++ b/clang/test/SemaCXX/constexpr-late-instantiation.cpp
@@ -1,5 +1,10 @@
-// RUN: %clang_cc1 %s -fsyntax-only -verify
-// RUN: %clang_cc1 %s -fexperimental-new-constant-interpreter -fsyntax-only 
-verify
+// RUN: %clang_cc1 %s -std=c++14 -fsyntax-only -verify
+// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify
+
+// RUN: %clang_cc1 %s -std=c++14 -fsyntax-only 
-fexperimental-new-constant-interpreter -verify
+// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only 
-fexperimental-new-constant-interpreter -verify
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only 
-fexperimental-new-constant-interpreter -verify
 
 template <typename T>
 constexpr T foo(T a);   // expected-note {{declared here}}
@@ -14,3 +19,221 @@ template <typename T>
 constexpr T foo(T a) {
   return a;
 }
+
+namespace GH73232 {
+namespace ex1 {
+template <typename T>
+constexpr void g(T);
+
+constexpr int f() {
+  g(0);
+  return 0;
+}
+
+template <typename T>
+constexpr void g(T) {}
+
+constexpr auto z = f();
+}
+
+namespace ex2 {
+template <typename> constexpr static void fromType();
+
+void registerConverter() { fromType<int>(); }
+template <typename> struct QMetaTypeId  {};
+template <typename T> constexpr void fromType() {
+  (void)QMetaTypeId<T>{};
+}
+template <> struct QMetaTypeId<int> {};
+} // namespace ex2
+
+namespace ex3 {
+
+#if __cplusplus > 202302L
+struct A {
+    consteval A(int i) {
+        chk(i);
+    }
+    constexpr void chk(auto) {}
+};
+A a{1};
+
+#endif
+
+}
+
+} // namespace GH73232
+
+
+namespace GH156255 {
+
+class X
+{
+public:
+    constexpr int f( int x ) const
+    {
+        return g( x );
+    }
+
+private:
+
+    template<class T>
+    constexpr T g( T x ) const
+    {
+        return x;
+    }
+};
+
+// check that g is instantiated here.
+constexpr int x = X().f( 1 );
+}
+
+#if __cplusplus > 202002L
+
+namespace instantiation_context_lookup {
+
+static constexpr int i = 42;
+static constexpr int v = 8;
+
+
+constexpr int f(auto);
+
+constexpr int g(int v = 42) {
+    static constexpr int i = 1;
+    return f(1);
+    return 0;
+}
+
+constexpr int f(auto) {
+    return i + v;
+}
+
+static_assert(g() == 50);
+
+}
+
+namespace GH35052 {
+
+template <typename F>
+constexpr int func(F f) {
+    if constexpr (f(1UL)) {
+        return 1;
+    }
+    return 0;
+}
+
+int test() {
+    auto predicate = [](auto v) constexpr -> bool  { return v == 1; };
+    return func(predicate); // check that "predicate" is instantiated.
+}
+
+
+}  // namespace GH35052
+
+namespace GH115118 {
+
+// Currently fails an assertion due to GH199347.
+/*struct foo {
+    foo(const foo&) = default;
+    foo(auto)
+        requires([]<int = 0>() -> bool { return true; }())
+    {}
+};
+
+struct bar {
+    foo x; // check that the lambda gets instantiated.
+};*/
+
+}  // namespace GH115118
+
+
+namespace GH100897 {
+
+template <typename>
+constexpr auto foo() noexcept {
+    constexpr auto extract_size = []<typename argument_t>() constexpr -> int {
+        return 1;
+    };
+
+    constexpr int result = extract_size.template operator()<int>();
+    return result;
+}
+
+void test() { foo<void>(); } // check that the lambda gets instantiated.
+
+}  // namespace GH100897
+
+namespace from_constexpr_initializer {
+template <typename _CharT>
+struct basic_string {
+  constexpr void _M_construct();
+
+  constexpr basic_string() {
+    _M_construct();
+  }
+
+};
+
+basic_string<char *> a;
+
+template <typename _CharT>
+constexpr void basic_string<_CharT>::_M_construct(){}
+
+constexpr basic_string<char*> z{};
+}  // namespace from_constexpr_initializer
+
+namespace from_imm_invocation_in_immediate_escalating_fn {
+template <int V> constexpr int f();
+consteval int g() { return f<0>(); }
+template <int V> constexpr int f() { return V; }
+
+int h() { return [] { return g(); }(); }
+}  // namespace from_imm_invocation_in_immediate_escalating_fn
+
+namespace from_imm_invocation_in_non_escalating_fn {
+template <int V> constexpr int f();
+consteval int g() { return f<0>(); }
+template <int V> constexpr int f() { return V; }
+
+int h() { return g(); }
+}  // namespace from_imm_invocation_in_non_escalating_fn
+
+namespace from_template_argument {
+template <int V> constexpr int f();
+consteval int g() { return f<0>(); }
+template <int V> constexpr int f() { return V; }
+
+template <int V> consteval int h() { return V; }
+int i() { return h<g()>(); }
+}  // namespace from_template_argument
+
+namespace from_constexpr_if {
+template <int V> constexpr int f();
+consteval int g() { return f<0>(); }
+template <int V> constexpr int f() { return V; }
+
+int h() {
+  if constexpr (g())
+    return 1;
+  else
+    return 2;
+}
+}  // namespace from_constexpr_if
+
+namespace from_static_assertion {
+template <int V> constexpr int f();
+consteval int g() { return f<0>(); }
+template <int V> constexpr int f() { return V; }
+
+static_assert(g() == 0);
+}  // namespace from_static_assertion
+
+namespace from_constexpr_destructor {
+template <int V> constexpr int f() noexcept;
+struct S { constexpr ~S() { (void) f<0>(); } };
+template <int V> constexpr int f() noexcept { return V; }
+
+void h() { constexpr S s; }
+}  // namespace from_constexpr_destructor
+
+#endif

>From 67c2baf172d440213b96580528a1dc1770c9aa5a Mon Sep 17 00:00:00 2001
From: Dan Katz <[email protected]>
Date: Wed, 24 Jun 2026 16:50:34 -0400
Subject: [PATCH 2/3] Move 'SemaEval.cpp' to 'SemaProxy.cpp'.

---
 clang/include/clang/Sema/Sema.h                | 6 +++---
 clang/lib/Sema/CMakeLists.txt                  | 2 +-
 clang/lib/Sema/{SemaEval.cpp => SemaProxy.cpp} | 7 ++++---
 3 files changed, 8 insertions(+), 7 deletions(-)
 rename clang/lib/Sema/{SemaEval.cpp => SemaProxy.cpp} (83%)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 97b951bf6d672..b9e322cab59ea 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -908,7 +908,7 @@ class Sema final : public SemaBase {
   // 33. Types (SemaType.cpp)
   // 34. FixIt Helpers (SemaFixItUtils.cpp)
   // 35. Function Effects (SemaFunctionEffects.cpp)
-  // 36. Language-Mandated Constant Evaluation (SemaEval.cpp)
+  // 36. Proxy to Sema for Constant Evaluation (SemaProxy.cpp)
 
   /// \name Semantic Analysis
   /// Implementations are in Sema.cpp
@@ -15820,8 +15820,8 @@ class Sema final : public SemaBase {
   //
   //
 
-  /// \name Language-Mandated Constant Evaluation
-  /// Implementations are in SemaEval.cpp
+  /// \name Proxy to Sema for Constant Evaluation
+  /// Implementations are in SemaProxy.cpp
   ///@{
 public:
   SemaProxy &getProxyForEval() const {
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index a9d3cf9b02f53..87c273bd52fd9 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -52,7 +52,6 @@ add_clang_library(clangSema
   SemaDeclAttr.cpp
   SemaDeclCXX.cpp
   SemaDeclObjC.cpp
-  SemaEval.cpp
   SemaExceptionSpec.cpp
   SemaExpr.cpp
   SemaExprCXX.cpp
@@ -81,6 +80,7 @@ add_clang_library(clangSema
   SemaOpenMP.cpp
   SemaOverload.cpp
   SemaPPC.cpp
+  SemaProxy.cpp
   SemaPseudoObject.cpp
   SemaRISCV.cpp
   SemaStmt.cpp
diff --git a/clang/lib/Sema/SemaEval.cpp b/clang/lib/Sema/SemaProxy.cpp
similarity index 83%
rename from clang/lib/Sema/SemaEval.cpp
rename to clang/lib/Sema/SemaProxy.cpp
index b43783bfb9091..0dd4c5890a9bf 100644
--- a/clang/lib/Sema/SemaEval.cpp
+++ b/clang/lib/Sema/SemaProxy.cpp
@@ -1,4 +1,4 @@
-//=== SemaEval.cpp - Sema handling of effectual constant evaluation 
-------===//
+//=== SemaProxy.cpp - Sema proxy for effectual constant evaluation 
--------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,9 @@
 //
 
//===----------------------------------------------------------------------===//
 //
-// This file implements Sema handling of language-mandated constant evaluation,
-// which can perform actions and queries against the AST.
+// This file implements a proxy to the Sema class that can be provided to the
+// constant evaluator, thereby facilitating evaluations capable of acting on 
and
+// querying the AST.
 //
 
//===----------------------------------------------------------------------===//
 

>From 01abd04b18425ff2266c358bc4455e90eeac6a36 Mon Sep 17 00:00:00 2001
From: "Daniel M. Katz" <[email protected]>
Date: Wed, 24 Jun 2026 16:52:43 -0400
Subject: [PATCH 3/3] Update clang/include/clang/AST/Decl.h

Co-authored-by: Corentin Jabot <[email protected]>
---
 clang/include/clang/AST/Decl.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 26e4649d99252..9e6f8d470c23b 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1454,8 +1454,8 @@ class VarDecl : public DeclaratorDecl, public 
Redeclarable<VarDecl> {
   ///         not.
   bool evaluateDestruction(SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
 
-  /// Evaluate the destruction of this variable, the destruction of which is
-  /// required by language rules to be constant.
+  /// Evaluate the destruction of a variable that is required by the language 
to have
+  /// constant destruction.
   ///
   /// \pre hasConstantInitialization()
   /// \return \c true if this variable has constant destruction, \c false if

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to