[clang] Enable AST mutation in the constant evaluator (PR #115168)

Vlad Serebrennikov via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 25 10:48:04 PST 2024


https://github.com/Endilll updated https://github.com/llvm/llvm-project/pull/115168

>From 5ca48e03412b1b8e9253f13356b9cc957f6fd9e5 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Wed, 6 Nov 2024 17:58:43 +0300
Subject: [PATCH 01/13] Add EvalASTMutator interface with
 `InstantiateFunctionDefinition` function

---
 clang/include/clang/AST/ASTContext.h          |  2 +-
 clang/include/clang/AST/Decl.h                | 22 ++++++++++---
 clang/include/clang/AST/Expr.h                |  3 +-
 clang/include/clang/Sema/Sema.h               | 17 ++++++++++
 clang/lib/AST/ASTContext.cpp                  |  4 +--
 clang/lib/AST/Decl.cpp                        | 16 +++++-----
 clang/lib/AST/ExprConstant.cpp                | 24 +++++++++++---
 clang/lib/Sema/Sema.cpp                       | 11 ++++++-
 clang/lib/Sema/SemaDecl.cpp                   |  5 +--
 .../constexpr-function-instantiation.cpp      | 31 +++++++++++++++++++
 10 files changed, 113 insertions(+), 22 deletions(-)
 create mode 100644 clang/test/SemaCXX/constexpr-function-instantiation.cpp

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index a4d36f2eacd5d1..d143591de9f2cd 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3258,7 +3258,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   ///
   /// \returns true if the function/var must be CodeGen'ed/deserialized even if
   /// it is not used.
-  bool DeclMustBeEmitted(const Decl *D);
+  bool DeclMustBeEmitted(const Decl *D, EvalASTMutator *ASTMutator = nullptr);
 
   /// Visits all versions of a multiversioned function with the passed
   /// predicate.
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 7ff35d73df5997..89a2833c82194d 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -78,6 +78,18 @@ class UnresolvedSetImpl;
 class VarTemplateDecl;
 enum class ImplicitParamKind;
 
+/// Interface that allows constant evaluator to mutate AST.
+/// When constant evaluation is triggered by Sema, it can supply a proper
+/// implementation of this interface.
+struct EvalASTMutator {
+  virtual ~EvalASTMutator() = default;
+
+  virtual void
+  InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
+                                FunctionDecl *Function, bool Recursive,
+                                bool DefinitionRequired, bool AtEndOfTU) = 0;
+};
+
 /// The top declaration context.
 class TranslationUnitDecl : public Decl,
                             public DeclContext,
@@ -1355,11 +1367,12 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
   /// Attempt to evaluate the value of the initializer attached to this
   /// declaration, and produce notes explaining why it cannot be evaluated.
   /// Returns a pointer to the value if evaluation succeeded, 0 otherwise.
-  APValue *evaluateValue() const;
+  APValue *evaluateValue(EvalASTMutator *ASTMutator = nullptr) const;
 
 private:
   APValue *evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
-                             bool IsConstantInitialization) const;
+                             bool IsConstantInitialization,
+                             EvalASTMutator *ASTMutator = nullptr) const;
 
 public:
   /// Return the already-evaluated value of this variable's
@@ -1391,8 +1404,9 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
   /// Evaluate the initializer of this variable to determine whether it's a
   /// constant initializer. Should only be called once, after completing the
   /// definition of the variable.
-  bool checkForConstantInitialization(
-      SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
+  bool
+  checkForConstantInitialization(SmallVectorImpl<PartialDiagnosticAt> &Notes,
+                                 EvalASTMutator *ASTMutator = nullptr) 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 466c65a9685ad3..ccf7fd226b5a8f 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -734,7 +734,8 @@ class Expr : public ValueStmt {
   bool EvaluateAsInitializer(APValue &Result, const ASTContext &Ctx,
                              const VarDecl *VD,
                              SmallVectorImpl<PartialDiagnosticAt> &Notes,
-                             bool IsConstantInitializer) const;
+                             bool IsConstantInitializer,
+                             EvalASTMutator *ASTMutator = nullptr) const;
 
   /// EvaluateWithSubstitution - Evaluate an expression as if from the context
   /// of a call to the given function with the given arguments, inside an
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 93d98e1cbb9c81..1c0c6949573335 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -153,6 +153,7 @@ enum class OverloadCandidateParamOrder : char;
 enum OverloadCandidateRewriteKind : unsigned;
 class OverloadCandidateSet;
 class Preprocessor;
+class Sema;
 class SemaAMDGPU;
 class SemaARM;
 class SemaAVR;
@@ -352,6 +353,15 @@ struct SkipBodyInfo {
   NamedDecl *New = nullptr;
 };
 
+/// Implementation of EvalASTMutator interface that enables constant evaluator
+/// to modify AST, e.g. to instantiate templates.
+struct SemaASTMutator : EvalASTMutator {
+  Sema &SemaRef;
+  SemaASTMutator(Sema &SemaRef) void InstantiateFunctionDefinition(
+      SourceLocation PointOfInstantiation, FunctionDecl *Function,
+      bool Recursive, bool DefinitionRequired, bool AtEndOfTU) override;
+};
+
 /// Describes the result of template argument deduction.
 ///
 /// The TemplateDeductionResult enumeration describes the result of
@@ -1042,6 +1052,9 @@ class Sema final : public SemaBase {
   /// CurContext - This is the current declaration context of parsing.
   DeclContext *CurContext;
 
+  /// Get a Sema implementation of EvalASTMutator interface.
+  SemaASTMutator *getASTMutator() { return &ASTMutator; }
+
   SemaAMDGPU &AMDGPU() {
     assert(AMDGPUPtr);
     return *AMDGPUPtr;
@@ -1199,6 +1212,10 @@ class Sema final : public SemaBase {
 
   mutable IdentifierInfo *Ident_super;
 
+  /// EvalASTMutator implementation that can be passed to constant evaluator
+  /// to enable it to do AST mutations, e.g. template instantiation.
+  SemaASTMutator ASTMutator;
+
   std::unique_ptr<SemaAMDGPU> AMDGPUPtr;
   std::unique_ptr<SemaARM> ARMPtr;
   std::unique_ptr<SemaAVR> AVRPtr;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 69892bda42b256..0aaf23af28d3d6 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -12522,7 +12522,7 @@ GVALinkage ASTContext::GetGVALinkageForVariable(const VarDecl *VD) const {
              basicGVALinkageForVariable(*this, VD)));
 }
 
-bool ASTContext::DeclMustBeEmitted(const Decl *D) {
+bool ASTContext::DeclMustBeEmitted(const Decl *D, EvalASTMutator *ASTMutator) {
   if (const auto *VD = dyn_cast<VarDecl>(D)) {
     if (!VD->isFileVarDecl())
       return false;
@@ -12627,7 +12627,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
   // Variables that have initialization with side-effects are required.
   if (VD->getInit() && VD->getInit()->HasSideEffects(*this) &&
       // We can get a value-dependent initializer during error recovery.
-      (VD->getInit()->isValueDependent() || !VD->evaluateValue()))
+      (VD->getInit()->isValueDependent() || !VD->evaluateValue(ASTMutator)))
     return true;
 
   // Likewise, variables with tuple-like bindings are required if their
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 86913763ef9ff5..f49ef66cb43aea 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2546,13 +2546,14 @@ EvaluatedStmt *VarDecl::getEvaluatedStmt() const {
   return Init.dyn_cast<EvaluatedStmt *>();
 }
 
-APValue *VarDecl::evaluateValue() const {
+APValue *VarDecl::evaluateValue(EvalASTMutator *ASTMutator) const {
   SmallVector<PartialDiagnosticAt, 8> Notes;
-  return evaluateValueImpl(Notes, hasConstantInitialization());
+  return evaluateValueImpl(Notes, hasConstantInitialization(), ASTMutator);
 }
 
 APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
-                                    bool IsConstantInitialization) const {
+                                    bool IsConstantInitialization,
+                                    EvalASTMutator *ASTMutator) const {
   EvaluatedStmt *Eval = ensureEvaluatedStmt();
 
   const auto *Init = getInit();
@@ -2572,8 +2573,8 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
   Eval->IsEvaluating = true;
 
   ASTContext &Ctx = getASTContext();
-  bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes,
-                                            IsConstantInitialization);
+  bool Result = Init->EvaluateAsInitializer(
+      Eval->Evaluated, Ctx, this, Notes, IsConstantInitialization, ASTMutator);
 
   // In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't
   // a constant initializer if we produced notes. In that case, we can't keep
@@ -2636,7 +2637,8 @@ bool VarDecl::hasConstantInitialization() const {
 }
 
 bool VarDecl::checkForConstantInitialization(
-    SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
+    SmallVectorImpl<PartialDiagnosticAt> &Notes,
+    EvalASTMutator *ASTMutator) 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
@@ -2651,7 +2653,7 @@ bool VarDecl::checkForConstantInitialization(
 
   // Evaluate the initializer to check whether it's a constant expression.
   Eval->HasConstantInitialization =
-      evaluateValueImpl(Notes, true) && Notes.empty();
+      evaluateValueImpl(Notes, true, ASTMutator) && 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/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index d664c503655ba6..b9a075154b6d6f 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1004,6 +1004,11 @@ namespace {
       EM_IgnoreSideEffects,
     } EvalMode;
 
+    /// Implementation of an interface for AST mutation.
+    /// Basically something backed by Sema,
+    /// or nullptr if Sema is not available.
+    EvalASTMutator *ASTMutator;
+
     /// Are we checking whether the expression is a potential constant
     /// expression?
     bool checkingPotentialConstantExpression() const override  {
@@ -1017,7 +1022,8 @@ namespace {
       return CheckingForUndefinedBehavior;
     }
 
-    EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode)
+    EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode,
+             EvalASTMutator *ASTMutator = nullptr)
         : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr),
           CallStackDepth(0), NextCallIndex(1),
           StepsLeft(C.getLangOpts().ConstexprStepLimit),
@@ -1027,13 +1033,15 @@ namespace {
                       /*CallExpr=*/nullptr, CallRef()),
           EvaluatingDecl((const ValueDecl *)nullptr),
           EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
-          HasFoldFailureDiagnostic(false), EvalMode(Mode) {}
+          HasFoldFailureDiagnostic(false), EvalMode(Mode),
+          ASTMutator(ASTMutator) {}
 
     ~EvalInfo() {
       discardCleanups();
     }
 
     ASTContext &getASTContext() const override { return Ctx; }
+    EvalASTMutator *getASTMutator() const { return ASTMutator; }
 
     void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,
                            EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) {
@@ -8328,6 +8336,12 @@ class ExprEvaluatorBase
 
     const FunctionDecl *Definition = nullptr;
     Stmt *Body = FD->getBody(Definition);
+    if (!Definition && FD->getTemplateInstantiationPattern()) {
+      Info.getASTMutator()->InstantiateFunctionDefinition(
+          E->getExprLoc(), const_cast<FunctionDecl *>(FD),
+          /*Recursive=*/true, /*DefinitionRequired=*/true, /*AtEndOfTU=*/false);
+      Body = FD->getBody(Definition);
+    }
 
     if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition, Body) ||
         !HandleFunctionCall(E->getExprLoc(), Definition, This, E, Args, Call,
@@ -16669,7 +16683,8 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx,
 bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
                                  const VarDecl *VD,
                                  SmallVectorImpl<PartialDiagnosticAt> &Notes,
-                                 bool IsConstantInitialization) const {
+                                 bool IsConstantInitialization,
+                                 EvalASTMutator *ASTMutator) const {
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
 
@@ -16687,7 +16702,8 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
                 (IsConstantInitialization &&
                  (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
                     ? EvalInfo::EM_ConstantExpression
-                    : EvalInfo::EM_ConstantFold);
+                    : EvalInfo::EM_ConstantFold,
+                ASTMutator);
   Info.setEvaluatingDecl(VD, Value);
   Info.InConstantContext = IsConstantInitialization;
 
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 2b51765e80864a..328a8f7912a8ea 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -208,6 +208,8 @@ class SemaPPCallbacks : public PPCallbacks {
 } // end namespace sema
 } // end namespace clang
 
+SemaASTMutator::SemaASTMutator(Sema &SemaRef) : SemaRef(SemaRef) {}
+
 const unsigned Sema::MaxAlignmentExponent;
 const uint64_t Sema::MaximumAlignment;
 
@@ -221,7 +223,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
       LateTemplateParser(nullptr), LateTemplateParserCleanup(nullptr),
       OpaqueParser(nullptr), CurContext(nullptr), ExternalSource(nullptr),
       StackHandler(Diags), CurScope(nullptr), Ident_super(nullptr),
-      AMDGPUPtr(std::make_unique<SemaAMDGPU>(*this)),
+      ASTMutator(*this), AMDGPUPtr(std::make_unique<SemaAMDGPU>(*this)),
       ARMPtr(std::make_unique<SemaARM>(*this)),
       AVRPtr(std::make_unique<SemaAVR>(*this)),
       BPFPtr(std::make_unique<SemaBPF>(*this)),
@@ -2798,3 +2800,10 @@ Attr *Sema::CreateAnnotationAttr(const ParsedAttr &AL) {
 
   return CreateAnnotationAttr(AL, Str, Args);
 }
+
+void SemaASTMutator::InstantiateFunctionDefinition(
+    SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive,
+    bool DefinitionRequired, bool AtEndOfTU) {
+  SemaRef.InstantiateFunctionDefinition(
+      PointOfInstantiation, Function, Recursive, DefinitionRequired, AtEndOfTU);
+}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index f8e5f3c6d309d6..ea6ca66f8c69e9 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14441,7 +14441,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, getASTMutator());
     }
 
     if (HasConstInit) {
@@ -14549,7 +14550,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
 
   // If this variable must be emitted, add it as an initializer for the current
   // module.
-  if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty())
+  if (Context.DeclMustBeEmitted(var, getASTMutator()) && !ModuleScopes.empty())
     Context.addModuleInitializer(ModuleScopes.back().Module, var);
 
   // Build the bindings if this is a structured binding declaration.
diff --git a/clang/test/SemaCXX/constexpr-function-instantiation.cpp b/clang/test/SemaCXX/constexpr-function-instantiation.cpp
new file mode 100644
index 00000000000000..43c2ced137474d
--- /dev/null
+++ b/clang/test/SemaCXX/constexpr-function-instantiation.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s
+
+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 ex1
+
+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>{};
+} // #1
+template <> struct QMetaTypeId<int> {}; // #20428 
+} // namespace ex2
+} // namespace GH73232
\ No newline at end of file

>From 57de67a735c40557ba203ad1c3e46c6791dff706 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Wed, 6 Nov 2024 18:00:39 +0300
Subject: [PATCH 02/13] Add missing newline

---
 clang/test/SemaCXX/constexpr-function-instantiation.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/SemaCXX/constexpr-function-instantiation.cpp b/clang/test/SemaCXX/constexpr-function-instantiation.cpp
index 43c2ced137474d..6c7e43f85cf6f9 100644
--- a/clang/test/SemaCXX/constexpr-function-instantiation.cpp
+++ b/clang/test/SemaCXX/constexpr-function-instantiation.cpp
@@ -28,4 +28,4 @@ template <typename T> constexpr void fromType() {
 } // #1
 template <> struct QMetaTypeId<int> {}; // #20428 
 } // namespace ex2
-} // namespace GH73232
\ No newline at end of file
+} // namespace GH73232

>From 548298c8e2d1030b5d4edb8b548609b55aeba7f0 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Wed, 6 Nov 2024 18:14:45 +0300
Subject: [PATCH 03/13] Add missing semicolon

---
 clang/include/clang/Sema/Sema.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1c0c6949573335..61114b7107c650 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -357,7 +357,8 @@ struct SkipBodyInfo {
 /// to modify AST, e.g. to instantiate templates.
 struct SemaASTMutator : EvalASTMutator {
   Sema &SemaRef;
-  SemaASTMutator(Sema &SemaRef) void InstantiateFunctionDefinition(
+  SemaASTMutator(Sema &SemaRef);
+  void InstantiateFunctionDefinition(
       SourceLocation PointOfInstantiation, FunctionDecl *Function,
       bool Recursive, bool DefinitionRequired, bool AtEndOfTU) override;
 };

>From 33092200cb15b3e095ee82d9001dd0e50eda668f Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Thu, 7 Nov 2024 19:35:41 +0300
Subject: [PATCH 04/13] Put ASTMutator inside ASTContext

---
 clang/include/clang/AST/ASTContext.h          | 21 ++++++++++++++++++-
 clang/include/clang/AST/Decl.h                | 20 +++---------------
 clang/include/clang/AST/Expr.h                |  3 +--
 clang/lib/AST/ASTContext.cpp                  |  4 ++--
 clang/lib/AST/Decl.cpp                        | 14 ++++++-------
 clang/lib/AST/ExprConstant.cpp                |  7 +++----
 clang/lib/Sema/Sema.cpp                       |  6 ++++++
 clang/lib/Sema/SemaDecl.cpp                   |  4 ++--
 .../constexpr-function-instantiation.cpp      | 20 +++++++++++-------
 9 files changed, 56 insertions(+), 43 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index d143591de9f2cd..487b1647012ca8 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -98,6 +98,7 @@ class ParentMapContext;
 struct ParsedTargetAttr;
 class Preprocessor;
 class ProfileList;
+class Sema;
 class StoredDeclsMap;
 class TargetAttr;
 class TargetInfo;
@@ -182,6 +183,18 @@ struct TypeInfoChars {
   }
 };
 
+// Interface that allows constant evaluator to mutate AST.
+// When constant evaluation is triggered by Sema, it can supply a proper
+// implementation of this interface.
+struct EvalASTMutator {
+  virtual ~EvalASTMutator() = default;
+
+  virtual void
+  InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
+                                FunctionDecl *Function, bool Recursive,
+                                bool DefinitionRequired, bool AtEndOfTU) = 0;
+};
+
 /// Holds long-lived AST nodes (such as types and decls) that can be
 /// referred to throughout the semantic analysis of a file.
 class ASTContext : public RefCountedBase<ASTContext> {
@@ -671,7 +684,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// Keeps track of the deallocated DeclListNodes for future reuse.
   DeclListNode *ListNodeFreeList = nullptr;
 
+  EvalASTMutator *ASTMutator = nullptr;
+
 public:
+  EvalASTMutator *getASTMutator() const { return ASTMutator; }
+
   IdentifierTable &Idents;
   SelectorTable &Selectors;
   Builtin::Context &BuiltinInfo;
@@ -3258,7 +3275,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   ///
   /// \returns true if the function/var must be CodeGen'ed/deserialized even if
   /// it is not used.
-  bool DeclMustBeEmitted(const Decl *D, EvalASTMutator *ASTMutator = nullptr);
+  bool DeclMustBeEmitted(const Decl *D);
 
   /// Visits all versions of a multiversioned function with the passed
   /// predicate.
@@ -3509,6 +3526,8 @@ OPT_LIST(V)
 
   void ReleaseDeclContextMaps();
 
+  friend void injectASTMutatorIntoASTContext(Sema &, ASTContext &);
+
 public:
   enum PragmaSectionFlag : unsigned {
     PSF_None = 0,
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 89a2833c82194d..83a4a2f4863fab 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -78,18 +78,6 @@ class UnresolvedSetImpl;
 class VarTemplateDecl;
 enum class ImplicitParamKind;
 
-/// Interface that allows constant evaluator to mutate AST.
-/// When constant evaluation is triggered by Sema, it can supply a proper
-/// implementation of this interface.
-struct EvalASTMutator {
-  virtual ~EvalASTMutator() = default;
-
-  virtual void
-  InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
-                                FunctionDecl *Function, bool Recursive,
-                                bool DefinitionRequired, bool AtEndOfTU) = 0;
-};
-
 /// The top declaration context.
 class TranslationUnitDecl : public Decl,
                             public DeclContext,
@@ -1367,12 +1355,11 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
   /// Attempt to evaluate the value of the initializer attached to this
   /// declaration, and produce notes explaining why it cannot be evaluated.
   /// Returns a pointer to the value if evaluation succeeded, 0 otherwise.
-  APValue *evaluateValue(EvalASTMutator *ASTMutator = nullptr) const;
+  APValue *evaluateValue() const;
 
 private:
   APValue *evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
-                             bool IsConstantInitialization,
-                             EvalASTMutator *ASTMutator = nullptr) const;
+                             bool IsConstantInitialization) const;
 
 public:
   /// Return the already-evaluated value of this variable's
@@ -1405,8 +1392,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,
-                                 EvalASTMutator *ASTMutator = nullptr) const;
+  checkForConstantInitialization(SmallVectorImpl<PartialDiagnosticAt> &Notes) 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 ccf7fd226b5a8f..466c65a9685ad3 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -734,8 +734,7 @@ class Expr : public ValueStmt {
   bool EvaluateAsInitializer(APValue &Result, const ASTContext &Ctx,
                              const VarDecl *VD,
                              SmallVectorImpl<PartialDiagnosticAt> &Notes,
-                             bool IsConstantInitializer,
-                             EvalASTMutator *ASTMutator = nullptr) const;
+                             bool IsConstantInitializer) const;
 
   /// EvaluateWithSubstitution - Evaluate an expression as if from the context
   /// of a call to the given function with the given arguments, inside an
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 0aaf23af28d3d6..69892bda42b256 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -12522,7 +12522,7 @@ GVALinkage ASTContext::GetGVALinkageForVariable(const VarDecl *VD) const {
              basicGVALinkageForVariable(*this, VD)));
 }
 
-bool ASTContext::DeclMustBeEmitted(const Decl *D, EvalASTMutator *ASTMutator) {
+bool ASTContext::DeclMustBeEmitted(const Decl *D) {
   if (const auto *VD = dyn_cast<VarDecl>(D)) {
     if (!VD->isFileVarDecl())
       return false;
@@ -12627,7 +12627,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D, EvalASTMutator *ASTMutator) {
   // Variables that have initialization with side-effects are required.
   if (VD->getInit() && VD->getInit()->HasSideEffects(*this) &&
       // We can get a value-dependent initializer during error recovery.
-      (VD->getInit()->isValueDependent() || !VD->evaluateValue(ASTMutator)))
+      (VD->getInit()->isValueDependent() || !VD->evaluateValue()))
     return true;
 
   // Likewise, variables with tuple-like bindings are required if their
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index f49ef66cb43aea..d264441fe74e7e 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2546,14 +2546,13 @@ EvaluatedStmt *VarDecl::getEvaluatedStmt() const {
   return Init.dyn_cast<EvaluatedStmt *>();
 }
 
-APValue *VarDecl::evaluateValue(EvalASTMutator *ASTMutator) const {
+APValue *VarDecl::evaluateValue() const {
   SmallVector<PartialDiagnosticAt, 8> Notes;
-  return evaluateValueImpl(Notes, hasConstantInitialization(), ASTMutator);
+  return evaluateValueImpl(Notes, hasConstantInitialization());
 }
 
 APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
-                                    bool IsConstantInitialization,
-                                    EvalASTMutator *ASTMutator) const {
+                                    bool IsConstantInitialization) const {
   EvaluatedStmt *Eval = ensureEvaluatedStmt();
 
   const auto *Init = getInit();
@@ -2574,7 +2573,7 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
 
   ASTContext &Ctx = getASTContext();
   bool Result = Init->EvaluateAsInitializer(
-      Eval->Evaluated, Ctx, this, Notes, IsConstantInitialization, ASTMutator);
+      Eval->Evaluated, Ctx, this, Notes, IsConstantInitialization);
 
   // In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't
   // a constant initializer if we produced notes. In that case, we can't keep
@@ -2637,8 +2636,7 @@ bool VarDecl::hasConstantInitialization() const {
 }
 
 bool VarDecl::checkForConstantInitialization(
-    SmallVectorImpl<PartialDiagnosticAt> &Notes,
-    EvalASTMutator *ASTMutator) const {
+    SmallVectorImpl<PartialDiagnosticAt> &Notes) 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
@@ -2653,7 +2651,7 @@ bool VarDecl::checkForConstantInitialization(
 
   // Evaluate the initializer to check whether it's a constant expression.
   Eval->HasConstantInitialization =
-      evaluateValueImpl(Notes, true, ASTMutator) && Notes.empty();
+      evaluateValueImpl(Notes, 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/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index b9a075154b6d6f..c167e6fa8e92a4 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8336,7 +8336,7 @@ class ExprEvaluatorBase
 
     const FunctionDecl *Definition = nullptr;
     Stmt *Body = FD->getBody(Definition);
-    if (!Definition && FD->getTemplateInstantiationPattern()) {
+    if (Info.Ctx.getLangOpts().CPlusPlus26 && Info.getASTMutator() && !Definition && FD->getTemplateInstantiationPattern()) {
       Info.getASTMutator()->InstantiateFunctionDefinition(
           E->getExprLoc(), const_cast<FunctionDecl *>(FD),
           /*Recursive=*/true, /*DefinitionRequired=*/true, /*AtEndOfTU=*/false);
@@ -16683,8 +16683,7 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx,
 bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
                                  const VarDecl *VD,
                                  SmallVectorImpl<PartialDiagnosticAt> &Notes,
-                                 bool IsConstantInitialization,
-                                 EvalASTMutator *ASTMutator) const {
+                                 bool IsConstantInitialization) const {
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
 
@@ -16703,7 +16702,7 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
                  (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
                     ? EvalInfo::EM_ConstantExpression
                     : EvalInfo::EM_ConstantFold,
-                ASTMutator);
+                Ctx.getASTMutator());
   Info.setEvaluatingDecl(VD, Value);
   Info.InConstantContext = IsConstantInitialization;
 
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 328a8f7912a8ea..ab58a54541c628 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -208,6 +208,10 @@ class SemaPPCallbacks : public PPCallbacks {
 } // end namespace sema
 } // end namespace clang
 
+void clang::injectASTMutatorIntoASTContext(Sema &S, ASTContext &Context) {
+  Context.ASTMutator = S.getASTMutator();
+}
+
 SemaASTMutator::SemaASTMutator(Sema &SemaRef) : SemaRef(SemaRef) {}
 
 const unsigned Sema::MaxAlignmentExponent;
@@ -300,6 +304,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
   SemaPPCallbackHandler->set(*this);
 
   CurFPFeatures.setFPEvalMethod(PP.getCurrentFPEvalMethod());
+
+  injectASTMutatorIntoASTContext(*this, Context);
 }
 
 // Anchor Sema's type info to this TU.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index ea6ca66f8c69e9..3d95fd7013fbdd 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14442,7 +14442,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
     } else {
       // Evaluate the initializer to see if it's a constant initializer.
       HasConstInit =
-          var->checkForConstantInitialization(Notes, getASTMutator());
+          var->checkForConstantInitialization(Notes);
     }
 
     if (HasConstInit) {
@@ -14550,7 +14550,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
 
   // If this variable must be emitted, add it as an initializer for the current
   // module.
-  if (Context.DeclMustBeEmitted(var, getASTMutator()) && !ModuleScopes.empty())
+  if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty())
     Context.addModuleInitializer(ModuleScopes.back().Module, var);
 
   // Build the bindings if this is a structured binding declaration.
diff --git a/clang/test/SemaCXX/constexpr-function-instantiation.cpp b/clang/test/SemaCXX/constexpr-function-instantiation.cpp
index 6c7e43f85cf6f9..aec14e938be9f5 100644
--- a/clang/test/SemaCXX/constexpr-function-instantiation.cpp
+++ b/clang/test/SemaCXX/constexpr-function-instantiation.cpp
@@ -1,21 +1,27 @@
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=cxx20-23 %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=cxx20-23 %s
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify=cxx26 %s
+
+// cxx26-no-diagnostics
 
 namespace GH73232 {
 namespace ex1 {
 template <typename T>
-constexpr void g(T);
+constexpr void g(T); // #ex1-g-decl
 
 constexpr int f() {
-    g(0);
-    return 0;
+  g(0); // #ex1-g-call
+  return 0;
 }
 
 template <typename T> 
 constexpr void g(T) {}
 
-constexpr auto z = f();
+constexpr auto z = f(); // #ex1-z-defn
+// cxx20-23-error at -1 {{constexpr variable 'z' must be initialized by a constant expression}}
+//   cxx20-23-note@#ex1-g-call {{undefined function 'g<int>' cannot be used in a constant expression}}
+//   cxx20-23-note@#ex1-z-defn {{in call to 'f()'}}
+//   cxx20-23-note@#ex1-g-decl {{declared here}}
 } // namespace ex1
 
 namespace ex2 {

>From 54da0e85a0095e6b1c40a6bb51446c4feb20e67b Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Thu, 7 Nov 2024 20:08:04 +0300
Subject: [PATCH 05/13] Don't store `ASTMutator` inside `EvalInfo`

---
 clang/include/clang/AST/ASTContext.h | 11 +++++++++--
 clang/include/clang/AST/Decl.h       |  4 ++--
 clang/lib/AST/Decl.cpp               |  4 ++--
 clang/lib/AST/ExprConstant.cpp       | 19 ++++++-------------
 clang/lib/Sema/Sema.cpp              |  3 +++
 clang/lib/Sema/SemaDecl.cpp          |  3 +--
 6 files changed, 23 insertions(+), 21 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 487b1647012ca8..b5895627213d10 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -184,8 +184,7 @@ struct TypeInfoChars {
 };
 
 // Interface that allows constant evaluator to mutate AST.
-// When constant evaluation is triggered by Sema, it can supply a proper
-// implementation of this interface.
+// Sema is the only entity that can implement this.
 struct EvalASTMutator {
   virtual ~EvalASTMutator() = default;
 
@@ -684,9 +683,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// Keeps track of the deallocated DeclListNodes for future reuse.
   DeclListNode *ListNodeFreeList = nullptr;
 
+  /// Implementation of the interface that Sema provides during its
+  /// construction.
   EvalASTMutator *ASTMutator = nullptr;
 
 public:
+  /// Returns an object that is capable of modifying AST,
+  /// or nullptr if it's not available. The latter happens when
+  /// Sema is not available.
   EvalASTMutator *getASTMutator() const { return ASTMutator; }
 
   IdentifierTable &Idents;
@@ -3526,6 +3530,9 @@ OPT_LIST(V)
 
   void ReleaseDeclContextMaps();
 
+  /// This is a function that is implemented in the Sema layer,
+  /// that needs friendship to initialize ASTMutator without this capability
+  /// being available in the public interface of ASTContext.
   friend void injectASTMutatorIntoASTContext(Sema &, ASTContext &);
 
 public:
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 83a4a2f4863fab..7ff35d73df5997 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1391,8 +1391,8 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
   /// Evaluate the initializer of this variable to determine whether it's a
   /// constant initializer. Should only be called once, after completing the
   /// definition of the variable.
-  bool
-  checkForConstantInitialization(SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
+  bool checkForConstantInitialization(
+      SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
 
   void setInitStyle(InitializationStyle Style) {
     VarDeclBits.InitStyle = Style;
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index d264441fe74e7e..86913763ef9ff5 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2572,8 +2572,8 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
   Eval->IsEvaluating = true;
 
   ASTContext &Ctx = getASTContext();
-  bool Result = Init->EvaluateAsInitializer(
-      Eval->Evaluated, Ctx, this, Notes, IsConstantInitialization);
+  bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes,
+                                            IsConstantInitialization);
 
   // In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't
   // a constant initializer if we produced notes. In that case, we can't keep
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index c167e6fa8e92a4..701dca6bf373ee 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1004,11 +1004,6 @@ namespace {
       EM_IgnoreSideEffects,
     } EvalMode;
 
-    /// Implementation of an interface for AST mutation.
-    /// Basically something backed by Sema,
-    /// or nullptr if Sema is not available.
-    EvalASTMutator *ASTMutator;
-
     /// Are we checking whether the expression is a potential constant
     /// expression?
     bool checkingPotentialConstantExpression() const override  {
@@ -1022,8 +1017,7 @@ namespace {
       return CheckingForUndefinedBehavior;
     }
 
-    EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode,
-             EvalASTMutator *ASTMutator = nullptr)
+    EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode)
         : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr),
           CallStackDepth(0), NextCallIndex(1),
           StepsLeft(C.getLangOpts().ConstexprStepLimit),
@@ -1033,15 +1027,14 @@ namespace {
                       /*CallExpr=*/nullptr, CallRef()),
           EvaluatingDecl((const ValueDecl *)nullptr),
           EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
-          HasFoldFailureDiagnostic(false), EvalMode(Mode),
-          ASTMutator(ASTMutator) {}
+          HasFoldFailureDiagnostic(false), EvalMode(Mode) {}
 
     ~EvalInfo() {
       discardCleanups();
     }
 
     ASTContext &getASTContext() const override { return Ctx; }
-    EvalASTMutator *getASTMutator() const { return ASTMutator; }
+    EvalASTMutator *getASTMutator() const { return Ctx.getASTMutator(); }
 
     void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,
                            EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) {
@@ -8336,7 +8329,8 @@ class ExprEvaluatorBase
 
     const FunctionDecl *Definition = nullptr;
     Stmt *Body = FD->getBody(Definition);
-    if (Info.Ctx.getLangOpts().CPlusPlus26 && Info.getASTMutator() && !Definition && FD->getTemplateInstantiationPattern()) {
+    if (Info.Ctx.getLangOpts().CPlusPlus26 && Info.getASTMutator() &&
+        !Definition && FD->getTemplateInstantiationPattern()) {
       Info.getASTMutator()->InstantiateFunctionDefinition(
           E->getExprLoc(), const_cast<FunctionDecl *>(FD),
           /*Recursive=*/true, /*DefinitionRequired=*/true, /*AtEndOfTU=*/false);
@@ -16701,8 +16695,7 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
                 (IsConstantInitialization &&
                  (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
                     ? EvalInfo::EM_ConstantExpression
-                    : EvalInfo::EM_ConstantFold,
-                Ctx.getASTMutator());
+                    : EvalInfo::EM_ConstantFold);
   Info.setEvaluatingDecl(VD, Value);
   Info.InConstantContext = IsConstantInitialization;
 
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index ab58a54541c628..41eff868a3fafb 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -305,6 +305,9 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
 
   CurFPFeatures.setFPEvalMethod(PP.getCurrentFPEvalMethod());
 
+  /// Initialize ASTMutator within ASTContext.
+  /// This is very intentionally not a part of public interface
+  /// of ASTContext.
   injectASTMutatorIntoASTContext(*this, Context);
 }
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 3d95fd7013fbdd..f8e5f3c6d309d6 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14441,8 +14441,7 @@ 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);
     }
 
     if (HasConstInit) {

>From 4d91ede45c51acc0dee61695ef19e1b8f0fcfb72 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 8 Nov 2024 18:20:22 +0300
Subject: [PATCH 06/13] Remove `Sema` from `ASTContext`

---
 clang/include/clang/AST/ASTContext.h | 3 +--
 clang/lib/Sema/Sema.cpp              | 7 ++++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index b5895627213d10..d2d4e13ee9b7e3 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -98,7 +98,6 @@ class ParentMapContext;
 struct ParsedTargetAttr;
 class Preprocessor;
 class ProfileList;
-class Sema;
 class StoredDeclsMap;
 class TargetAttr;
 class TargetInfo;
@@ -3533,7 +3532,7 @@ OPT_LIST(V)
   /// This is a function that is implemented in the Sema layer,
   /// that needs friendship to initialize ASTMutator without this capability
   /// being available in the public interface of ASTContext.
-  friend void injectASTMutatorIntoASTContext(Sema &, ASTContext &);
+  friend void injectASTMutatorIntoASTContext(ASTContext &, EvalASTMutator *);
 
 public:
   enum PragmaSectionFlag : unsigned {
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 41eff868a3fafb..9469be0ba1e8cc 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -208,8 +208,9 @@ class SemaPPCallbacks : public PPCallbacks {
 } // end namespace sema
 } // end namespace clang
 
-void clang::injectASTMutatorIntoASTContext(Sema &S, ASTContext &Context) {
-  Context.ASTMutator = S.getASTMutator();
+void clang::injectASTMutatorIntoASTContext(ASTContext &Context,
+                                           EvalASTMutator *ASTMutator) {
+  Context.ASTMutator = ASTMutator;
 }
 
 SemaASTMutator::SemaASTMutator(Sema &SemaRef) : SemaRef(SemaRef) {}
@@ -308,7 +309,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
   /// Initialize ASTMutator within ASTContext.
   /// This is very intentionally not a part of public interface
   /// of ASTContext.
-  injectASTMutatorIntoASTContext(*this, Context);
+  injectASTMutatorIntoASTContext(Context, getASTMutator());
 }
 
 // Anchor Sema's type info to this TU.

>From 98945b41b480b2f51cf95b2705e9750be997d5bc Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Wed, 13 Nov 2024 18:46:58 +0300
Subject: [PATCH 07/13] Rename ASTMutator to SemaProxy

---
 clang/include/clang/AST/ASTContext.h | 18 +++++++++---------
 clang/include/clang/Sema/Sema.h      | 16 ++++++++--------
 clang/lib/AST/ExprConstant.cpp       |  6 +++---
 clang/lib/Sema/Sema.cpp              | 12 ++++++------
 4 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index d2d4e13ee9b7e3..944140542b11b4 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -182,10 +182,10 @@ struct TypeInfoChars {
   }
 };
 
-// Interface that allows constant evaluator to mutate AST.
-// Sema is the only entity that can implement this.
-struct EvalASTMutator {
-  virtual ~EvalASTMutator() = default;
+// Interface that allows constant evaluator to call Sema
+// and mutate the AST.
+struct SemaProxy {
+  virtual ~SemaProxy() = default;
 
   virtual void
   InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
@@ -684,13 +684,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
 
   /// Implementation of the interface that Sema provides during its
   /// construction.
-  EvalASTMutator *ASTMutator = nullptr;
+  SemaProxy *ASTMutator = nullptr;
 
 public:
   /// Returns an object that is capable of modifying AST,
   /// or nullptr if it's not available. The latter happens when
   /// Sema is not available.
-  EvalASTMutator *getASTMutator() const { return ASTMutator; }
+  SemaProxy *getSemaProxy() const { return ASTMutator; }
 
   IdentifierTable &Idents;
   SelectorTable &Selectors;
@@ -3530,9 +3530,9 @@ OPT_LIST(V)
   void ReleaseDeclContextMaps();
 
   /// This is a function that is implemented in the Sema layer,
-  /// that needs friendship to initialize ASTMutator without this capability
-  /// being available in the public interface of ASTContext.
-  friend void injectASTMutatorIntoASTContext(ASTContext &, EvalASTMutator *);
+  /// that needs friendship to initialize SemaProxy without this capability
+  /// being exposed in the public interface of ASTContext.
+  friend void injectSemaProxyIntoASTContext(ASTContext &, SemaProxy *);
 
 public:
   enum PragmaSectionFlag : unsigned {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 61114b7107c650..7fe05a8991acfa 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -353,11 +353,11 @@ struct SkipBodyInfo {
   NamedDecl *New = nullptr;
 };
 
-/// Implementation of EvalASTMutator interface that enables constant evaluator
-/// to modify AST, e.g. to instantiate templates.
-struct SemaASTMutator : EvalASTMutator {
+/// Implementation of SemaProxy interface that enables constant evaluator
+/// to call Sema and modify AST, e.g. to instantiate templates.
+struct SemaProxyImpl : SemaProxy {
   Sema &SemaRef;
-  SemaASTMutator(Sema &SemaRef);
+  SemaProxyImpl(Sema &SemaRef);
   void InstantiateFunctionDefinition(
       SourceLocation PointOfInstantiation, FunctionDecl *Function,
       bool Recursive, bool DefinitionRequired, bool AtEndOfTU) override;
@@ -1053,8 +1053,8 @@ class Sema final : public SemaBase {
   /// CurContext - This is the current declaration context of parsing.
   DeclContext *CurContext;
 
-  /// Get a Sema implementation of EvalASTMutator interface.
-  SemaASTMutator *getASTMutator() { return &ASTMutator; }
+  /// Get a Sema implementation of SemaProxy interface.
+  SemaProxyImpl *getSemaProxy() { return &SemaProxy; }
 
   SemaAMDGPU &AMDGPU() {
     assert(AMDGPUPtr);
@@ -1213,9 +1213,9 @@ class Sema final : public SemaBase {
 
   mutable IdentifierInfo *Ident_super;
 
-  /// EvalASTMutator implementation that can be passed to constant evaluator
+  /// SemaProxy implementation that can be passed to constant evaluator
   /// to enable it to do AST mutations, e.g. template instantiation.
-  SemaASTMutator ASTMutator;
+  SemaProxyImpl SemaProxy;
 
   std::unique_ptr<SemaAMDGPU> AMDGPUPtr;
   std::unique_ptr<SemaARM> ARMPtr;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 701dca6bf373ee..c118fba87e615f 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1034,7 +1034,7 @@ namespace {
     }
 
     ASTContext &getASTContext() const override { return Ctx; }
-    EvalASTMutator *getASTMutator() const { return Ctx.getASTMutator(); }
+    SemaProxy *getSemaProxy() const { return Ctx.getSemaProxy(); }
 
     void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,
                            EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) {
@@ -8329,9 +8329,9 @@ class ExprEvaluatorBase
 
     const FunctionDecl *Definition = nullptr;
     Stmt *Body = FD->getBody(Definition);
-    if (Info.Ctx.getLangOpts().CPlusPlus26 && Info.getASTMutator() &&
+    if (Info.Ctx.getLangOpts().CPlusPlus26 && Info.getSemaProxy() &&
         !Definition && FD->getTemplateInstantiationPattern()) {
-      Info.getASTMutator()->InstantiateFunctionDefinition(
+      Info.getSemaProxy()->InstantiateFunctionDefinition(
           E->getExprLoc(), const_cast<FunctionDecl *>(FD),
           /*Recursive=*/true, /*DefinitionRequired=*/true, /*AtEndOfTU=*/false);
       Body = FD->getBody(Definition);
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 9469be0ba1e8cc..d5f65470a02486 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -208,12 +208,12 @@ class SemaPPCallbacks : public PPCallbacks {
 } // end namespace sema
 } // end namespace clang
 
-void clang::injectASTMutatorIntoASTContext(ASTContext &Context,
-                                           EvalASTMutator *ASTMutator) {
+void clang::injectSemaProxyIntoASTContext(ASTContext &Context,
+                                           SemaProxy *ASTMutator) {
   Context.ASTMutator = ASTMutator;
 }
 
-SemaASTMutator::SemaASTMutator(Sema &SemaRef) : SemaRef(SemaRef) {}
+SemaProxyImpl::SemaProxyImpl(Sema &SemaRef) : SemaRef(SemaRef) {}
 
 const unsigned Sema::MaxAlignmentExponent;
 const uint64_t Sema::MaximumAlignment;
@@ -228,7 +228,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
       LateTemplateParser(nullptr), LateTemplateParserCleanup(nullptr),
       OpaqueParser(nullptr), CurContext(nullptr), ExternalSource(nullptr),
       StackHandler(Diags), CurScope(nullptr), Ident_super(nullptr),
-      ASTMutator(*this), AMDGPUPtr(std::make_unique<SemaAMDGPU>(*this)),
+      SemaProxy(*this), AMDGPUPtr(std::make_unique<SemaAMDGPU>(*this)),
       ARMPtr(std::make_unique<SemaARM>(*this)),
       AVRPtr(std::make_unique<SemaAVR>(*this)),
       BPFPtr(std::make_unique<SemaBPF>(*this)),
@@ -309,7 +309,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
   /// Initialize ASTMutator within ASTContext.
   /// This is very intentionally not a part of public interface
   /// of ASTContext.
-  injectASTMutatorIntoASTContext(Context, getASTMutator());
+  injectSemaProxyIntoASTContext(Context, getSemaProxy());
 }
 
 // Anchor Sema's type info to this TU.
@@ -2811,7 +2811,7 @@ Attr *Sema::CreateAnnotationAttr(const ParsedAttr &AL) {
   return CreateAnnotationAttr(AL, Str, Args);
 }
 
-void SemaASTMutator::InstantiateFunctionDefinition(
+void SemaProxyImpl::InstantiateFunctionDefinition(
     SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive,
     bool DefinitionRequired, bool AtEndOfTU) {
   SemaRef.InstantiateFunctionDefinition(

>From b4e605ac1afe57c39d9b2b145873fbe306fd0ddb Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 15 Nov 2024 17:58:02 +0300
Subject: [PATCH 08/13] Address feedback

---
 clang/include/clang/AST/ASTContext.h          | 27 ++++++++++++++-----
 .../include/clang/Basic/DiagnosticASTKinds.td |  5 ++++
 clang/include/clang/Basic/DiagnosticGroups.td |  1 +
 clang/include/clang/Sema/Sema.h               | 14 +++-------
 clang/lib/AST/ASTContext.cpp                  | 18 ++++++++++---
 clang/lib/AST/ExprConstant.cpp                | 10 +++----
 clang/lib/Sema/Sema.cpp                       | 17 ++++++------
 7 files changed, 57 insertions(+), 35 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 944140542b11b4..d05d55206c55ee 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -182,15 +182,14 @@ struct TypeInfoChars {
   }
 };
 
-// Interface that allows constant evaluator to call Sema
-// and mutate the AST.
+/// Interface that allows constant evaluator to call Sema
+/// and mutate the AST.
 struct SemaProxy {
   virtual ~SemaProxy() = default;
 
   virtual void
   InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
-                                FunctionDecl *Function, bool Recursive,
-                                bool DefinitionRequired, bool AtEndOfTU) = 0;
+                                FunctionDecl *Function) = 0;
 };
 
 /// Holds long-lived AST nodes (such as types and decls) that can be
@@ -684,13 +683,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
 
   /// Implementation of the interface that Sema provides during its
   /// construction.
-  SemaProxy *ASTMutator = nullptr;
+  std::unique_ptr<SemaProxy> SemaProxyPtr;
 
 public:
   /// Returns an object that is capable of modifying AST,
   /// or nullptr if it's not available. The latter happens when
   /// Sema is not available.
-  SemaProxy *getSemaProxy() const { return ASTMutator; }
+  SemaProxy &getSemaProxy() const {
+    assert(SemaProxyPtr);
+    return *SemaProxyPtr;
+  }
 
   IdentifierTable &Idents;
   SelectorTable &Selectors;
@@ -3532,7 +3534,8 @@ OPT_LIST(V)
   /// This is a function that is implemented in the Sema layer,
   /// that needs friendship to initialize SemaProxy without this capability
   /// being exposed in the public interface of ASTContext.
-  friend void injectSemaProxyIntoASTContext(ASTContext &, SemaProxy *);
+  friend void injectSemaProxyIntoASTContext(ASTContext &,
+                                            std::unique_ptr<SemaProxy>);
 
 public:
   enum PragmaSectionFlag : unsigned {
@@ -3601,6 +3604,16 @@ inline Selector GetUnarySelector(StringRef name, ASTContext &Ctx) {
   return Ctx.Selectors.getSelector(1, &II);
 }
 
+/// Placeholder implementation that issues a diagnostic on any usage.
+struct UnimplementedSemaProxy : SemaProxy {
+  ASTContext &Ctx;
+  explicit UnimplementedSemaProxy(ASTContext &);
+  virtual ~UnimplementedSemaProxy() = default;
+
+  void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
+                                     FunctionDecl *Function) override;
+};
+
 } // namespace clang
 
 // operator new and delete aren't allowed inside namespaces.
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 6a658cf14356f5..58ded8d0b2a7fc 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -1025,4 +1025,9 @@ def warn_unpacked_field
 def warn_unaligned_access : Warning<
   "field %1 within %0 is less aligned than %2 and is usually due to %0 being "
   "packed, which can lead to unaligned accesses">, InGroup<UnalignedAccess>, DefaultIgnore;
+
+def warn_side_effects_on_ast_unavailable : Warning<
+  "%select{instantiation of a function template}0"
+  " did not happen, because internal program representation is immutable">,
+  InGroup<SideEffectsUnavailable>;
 }
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 72eada50a56cc9..4119c90352137f 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -629,6 +629,7 @@ def Packed : DiagGroup<"packed", [PackedNonPod]>;
 def PaddedBitField : DiagGroup<"padded-bitfield">;
 def Padded : DiagGroup<"padded", [PaddedBitField]>;
 def UnalignedAccess : DiagGroup<"unaligned-access">;
+def SideEffectsUnavailable : DiagGroup<"side-effects-unavailable">;
 
 def PessimizingMove : DiagGroup<"pessimizing-move">;
 def ReturnStdMove : DiagGroup<"return-std-move">;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 7fe05a8991acfa..dfde0e69323a65 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -357,10 +357,9 @@ struct SkipBodyInfo {
 /// to call Sema and modify AST, e.g. to instantiate templates.
 struct SemaProxyImpl : SemaProxy {
   Sema &SemaRef;
-  SemaProxyImpl(Sema &SemaRef);
-  void InstantiateFunctionDefinition(
-      SourceLocation PointOfInstantiation, FunctionDecl *Function,
-      bool Recursive, bool DefinitionRequired, bool AtEndOfTU) override;
+  explicit SemaProxyImpl(Sema &SemaRef);
+  void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
+                                     FunctionDecl *Function) override;
 };
 
 /// Describes the result of template argument deduction.
@@ -1053,9 +1052,6 @@ class Sema final : public SemaBase {
   /// CurContext - This is the current declaration context of parsing.
   DeclContext *CurContext;
 
-  /// Get a Sema implementation of SemaProxy interface.
-  SemaProxyImpl *getSemaProxy() { return &SemaProxy; }
-
   SemaAMDGPU &AMDGPU() {
     assert(AMDGPUPtr);
     return *AMDGPUPtr;
@@ -1213,10 +1209,6 @@ class Sema final : public SemaBase {
 
   mutable IdentifierInfo *Ident_super;
 
-  /// SemaProxy implementation that can be passed to constant evaluator
-  /// to enable it to do AST mutations, e.g. template instantiation.
-  SemaProxyImpl SemaProxy;
-
   std::unique_ptr<SemaAMDGPU> AMDGPUPtr;
   std::unique_ptr<SemaARM> ARMPtr;
   std::unique_ptr<SemaAVR> AVRPtr;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 69892bda42b256..b61e064bc255bd 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -51,6 +51,7 @@
 #include "clang/Basic/AddressSpaces.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/CommentOptions.h"
+#include "clang/Basic/DiagnosticAST.h"
 #include "clang/Basic/ExceptionSpecificationType.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/LLVM.h"
@@ -902,9 +903,11 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
                                         LangOpts.XRayNeverInstrumentFiles,
                                         LangOpts.XRayAttrListFiles, SM)),
       ProfList(new ProfileList(LangOpts.ProfileListFiles, SM)),
-      PrintingPolicy(LOpts), Idents(idents), Selectors(sels),
-      BuiltinInfo(builtins), TUKind(TUKind), DeclarationNames(*this),
-      Comments(SM), CommentCommandTraits(BumpAlloc, LOpts.CommentOpts),
+      PrintingPolicy(LOpts),
+      SemaProxyPtr(std::make_unique<UnimplementedSemaProxy>(*this)),
+      Idents(idents), Selectors(sels), BuiltinInfo(builtins), TUKind(TUKind),
+      DeclarationNames(*this), Comments(SM),
+      CommentCommandTraits(BumpAlloc, LOpts.CommentOpts),
       CompCategories(this_()), LastSDM(nullptr, 0) {
   addTranslationUnitDecl();
 }
@@ -14492,3 +14495,12 @@ bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
   ThunksToBeAbbreviated[VirtualMethodDecl] = std::move(SimplifiedThunkNames);
   return Result;
 }
+
+UnimplementedSemaProxy::UnimplementedSemaProxy(ASTContext &Ctx) : Ctx(Ctx) {}
+
+void UnimplementedSemaProxy::InstantiateFunctionDefinition(
+    SourceLocation PointOfInstantiation, FunctionDecl *Function) {
+  Ctx.getDiagnostics().Report(PointOfInstantiation,
+                              diag::warn_side_effects_on_ast_unavailable)
+      << 0;
+}
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index c118fba87e615f..4d523d9f15e9c4 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1034,7 +1034,7 @@ namespace {
     }
 
     ASTContext &getASTContext() const override { return Ctx; }
-    SemaProxy *getSemaProxy() const { return Ctx.getSemaProxy(); }
+    SemaProxy &getSemaProxy() const { return Ctx.getSemaProxy(); }
 
     void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,
                            EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) {
@@ -8329,11 +8329,9 @@ class ExprEvaluatorBase
 
     const FunctionDecl *Definition = nullptr;
     Stmt *Body = FD->getBody(Definition);
-    if (Info.Ctx.getLangOpts().CPlusPlus26 && Info.getSemaProxy() &&
-        !Definition && FD->getTemplateInstantiationPattern()) {
-      Info.getSemaProxy()->InstantiateFunctionDefinition(
-          E->getExprLoc(), const_cast<FunctionDecl *>(FD),
-          /*Recursive=*/true, /*DefinitionRequired=*/true, /*AtEndOfTU=*/false);
+    if (!Definition && FD->getTemplateInstantiationPattern()) {
+      Info.getSemaProxy().InstantiateFunctionDefinition(
+          E->getExprLoc(), const_cast<FunctionDecl *>(FD));
       Body = FD->getBody(Definition);
     }
 
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index d5f65470a02486..793e6ed3d059bd 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -209,8 +209,8 @@ class SemaPPCallbacks : public PPCallbacks {
 } // end namespace clang
 
 void clang::injectSemaProxyIntoASTContext(ASTContext &Context,
-                                           SemaProxy *ASTMutator) {
-  Context.ASTMutator = ASTMutator;
+                                          std::unique_ptr<SemaProxy> ProxyPtr) {
+  Context.SemaProxyPtr = std::move(ProxyPtr);
 }
 
 SemaProxyImpl::SemaProxyImpl(Sema &SemaRef) : SemaRef(SemaRef) {}
@@ -228,7 +228,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
       LateTemplateParser(nullptr), LateTemplateParserCleanup(nullptr),
       OpaqueParser(nullptr), CurContext(nullptr), ExternalSource(nullptr),
       StackHandler(Diags), CurScope(nullptr), Ident_super(nullptr),
-      SemaProxy(*this), AMDGPUPtr(std::make_unique<SemaAMDGPU>(*this)),
+      AMDGPUPtr(std::make_unique<SemaAMDGPU>(*this)),
       ARMPtr(std::make_unique<SemaARM>(*this)),
       AVRPtr(std::make_unique<SemaAVR>(*this)),
       BPFPtr(std::make_unique<SemaBPF>(*this)),
@@ -306,10 +306,11 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
 
   CurFPFeatures.setFPEvalMethod(PP.getCurrentFPEvalMethod());
 
-  /// Initialize ASTMutator within ASTContext.
+  /// Initialize SemaProxyPtr within ASTContext.
   /// This is very intentionally not a part of public interface
   /// of ASTContext.
-  injectSemaProxyIntoASTContext(Context, getSemaProxy());
+  injectSemaProxyIntoASTContext(Context,
+                                std::make_unique<SemaProxyImpl>(*this));
 }
 
 // Anchor Sema's type info to this TU.
@@ -2812,8 +2813,8 @@ Attr *Sema::CreateAnnotationAttr(const ParsedAttr &AL) {
 }
 
 void SemaProxyImpl::InstantiateFunctionDefinition(
-    SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive,
-    bool DefinitionRequired, bool AtEndOfTU) {
+    SourceLocation PointOfInstantiation, FunctionDecl *Function) {
   SemaRef.InstantiateFunctionDefinition(
-      PointOfInstantiation, Function, Recursive, DefinitionRequired, AtEndOfTU);
+      PointOfInstantiation, Function, /*Recursive=*/true,
+      /*DefinitionRequired=*/true, /*AtEndOfTU=*/false);
 }

>From 57bed5d3f61decf192edc4686c35202bd69b59f8 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 15 Nov 2024 21:56:43 +0300
Subject: [PATCH 09/13] Replace the diagnostic with llvm_unreachable that can
 be disabled

---
 clang/include/clang/AST/ASTContext.h            |  6 ++++++
 clang/include/clang/Basic/DiagnosticASTKinds.td |  5 -----
 clang/include/clang/Basic/DiagnosticGroups.td   |  1 -
 clang/lib/AST/ASTContext.cpp                    | 15 ++++++++++++---
 clang/lib/Sema/Sema.cpp                         |  2 ++
 5 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index d05d55206c55ee..d7a9197500b30e 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -187,9 +187,15 @@ struct TypeInfoChars {
 struct SemaProxy {
   virtual ~SemaProxy() = default;
 
+  bool getIgnoreSideEffectsOnAST();
+  void setIgnoreSideEffectsOnAST(bool ignore = true);
+
   virtual void
   InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
                                 FunctionDecl *Function) = 0;
+
+private:
+  bool IgnoreSideEffectsOnAST = false;
 };
 
 /// Holds long-lived AST nodes (such as types and decls) that can be
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 58ded8d0b2a7fc..6a658cf14356f5 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -1025,9 +1025,4 @@ def warn_unpacked_field
 def warn_unaligned_access : Warning<
   "field %1 within %0 is less aligned than %2 and is usually due to %0 being "
   "packed, which can lead to unaligned accesses">, InGroup<UnalignedAccess>, DefaultIgnore;
-
-def warn_side_effects_on_ast_unavailable : Warning<
-  "%select{instantiation of a function template}0"
-  " did not happen, because internal program representation is immutable">,
-  InGroup<SideEffectsUnavailable>;
 }
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 4119c90352137f..72eada50a56cc9 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -629,7 +629,6 @@ def Packed : DiagGroup<"packed", [PackedNonPod]>;
 def PaddedBitField : DiagGroup<"padded-bitfield">;
 def Padded : DiagGroup<"padded", [PaddedBitField]>;
 def UnalignedAccess : DiagGroup<"unaligned-access">;
-def SideEffectsUnavailable : DiagGroup<"side-effects-unavailable">;
 
 def PessimizingMove : DiagGroup<"pessimizing-move">;
 def ReturnStdMove : DiagGroup<"return-std-move">;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index b61e064bc255bd..916fecefd01fd7 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -14496,11 +14496,20 @@ bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
   return Result;
 }
 
+bool SemaProxy::getIgnoreSideEffectsOnAST() { return IgnoreSideEffectsOnAST; }
+
+void SemaProxy::setIgnoreSideEffectsOnAST(bool Ignore) {
+  IgnoreSideEffectsOnAST = Ignore;
+}
+
 UnimplementedSemaProxy::UnimplementedSemaProxy(ASTContext &Ctx) : Ctx(Ctx) {}
 
 void UnimplementedSemaProxy::InstantiateFunctionDefinition(
     SourceLocation PointOfInstantiation, FunctionDecl *Function) {
-  Ctx.getDiagnostics().Report(PointOfInstantiation,
-                              diag::warn_side_effects_on_ast_unavailable)
-      << 0;
+  if (getIgnoreSideEffectsOnAST())
+    return;
+  llvm_unreachable(
+      "AST mutation was requested without clang::Sema available. "
+      "Consider providing it, or disabling side effects on AST via "
+      "ASTContext.getSemaProxy().setIgnoreSideEffectsOnAST(true).");
 }
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 793e6ed3d059bd..f3568faba0b935 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -2814,6 +2814,8 @@ Attr *Sema::CreateAnnotationAttr(const ParsedAttr &AL) {
 
 void SemaProxyImpl::InstantiateFunctionDefinition(
     SourceLocation PointOfInstantiation, FunctionDecl *Function) {
+  if (getIgnoreSideEffectsOnAST())
+    return;
   SemaRef.InstantiateFunctionDefinition(
       PointOfInstantiation, Function, /*Recursive=*/true,
       /*DefinitionRequired=*/true, /*AtEndOfTU=*/false);

>From 369aac84ae58a84e7feeacf5c4e982fbdfbd2571 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 15 Nov 2024 22:01:45 +0300
Subject: [PATCH 10/13] Mark both implementation of `SemaProxy` as `final`

---
 clang/include/clang/AST/ASTContext.h | 2 +-
 clang/include/clang/Sema/Sema.h      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index d7a9197500b30e..4b3d571b68489e 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3611,7 +3611,7 @@ inline Selector GetUnarySelector(StringRef name, ASTContext &Ctx) {
 }
 
 /// Placeholder implementation that issues a diagnostic on any usage.
-struct UnimplementedSemaProxy : SemaProxy {
+struct UnimplementedSemaProxy final : SemaProxy {
   ASTContext &Ctx;
   explicit UnimplementedSemaProxy(ASTContext &);
   virtual ~UnimplementedSemaProxy() = default;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index dfde0e69323a65..b64ede7cf28196 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -355,7 +355,7 @@ struct SkipBodyInfo {
 
 /// Implementation of SemaProxy interface that enables constant evaluator
 /// to call Sema and modify AST, e.g. to instantiate templates.
-struct SemaProxyImpl : SemaProxy {
+struct SemaProxyImpl final : SemaProxy {
   Sema &SemaRef;
   explicit SemaProxyImpl(Sema &SemaRef);
   void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,

>From 0ffb6eea02f03521c753a41d83069472084a95c2 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 15 Nov 2024 22:10:42 +0300
Subject: [PATCH 11/13] Clean up `UnimplementedSemaProxy`

---
 clang/include/clang/AST/ASTContext.h | 2 --
 clang/lib/AST/ASTContext.cpp         | 4 +---
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 4b3d571b68489e..d3cfb34b9c4ed9 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3612,8 +3612,6 @@ inline Selector GetUnarySelector(StringRef name, ASTContext &Ctx) {
 
 /// Placeholder implementation that issues a diagnostic on any usage.
 struct UnimplementedSemaProxy final : SemaProxy {
-  ASTContext &Ctx;
-  explicit UnimplementedSemaProxy(ASTContext &);
   virtual ~UnimplementedSemaProxy() = default;
 
   void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 916fecefd01fd7..c02f3b5389dee5 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -904,7 +904,7 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
                                         LangOpts.XRayAttrListFiles, SM)),
       ProfList(new ProfileList(LangOpts.ProfileListFiles, SM)),
       PrintingPolicy(LOpts),
-      SemaProxyPtr(std::make_unique<UnimplementedSemaProxy>(*this)),
+      SemaProxyPtr(std::make_unique<UnimplementedSemaProxy>()),
       Idents(idents), Selectors(sels), BuiltinInfo(builtins), TUKind(TUKind),
       DeclarationNames(*this), Comments(SM),
       CommentCommandTraits(BumpAlloc, LOpts.CommentOpts),
@@ -14502,8 +14502,6 @@ void SemaProxy::setIgnoreSideEffectsOnAST(bool Ignore) {
   IgnoreSideEffectsOnAST = Ignore;
 }
 
-UnimplementedSemaProxy::UnimplementedSemaProxy(ASTContext &Ctx) : Ctx(Ctx) {}
-
 void UnimplementedSemaProxy::InstantiateFunctionDefinition(
     SourceLocation PointOfInstantiation, FunctionDecl *Function) {
   if (getIgnoreSideEffectsOnAST())

>From 857dc2f63c7aa2d6a1eb959fac3ca226566bfe3d Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Mon, 25 Nov 2024 21:06:09 +0300
Subject: [PATCH 12/13] Adjust the test case to acknowledge that it fails in
 C++20

---
 clang/lib/AST/ExprConstant.cpp                   |  3 ++-
 .../SemaCXX/constexpr-function-instantiation.cpp | 16 ++++++++--------
 2 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4d523d9f15e9c4..4c6953f5933034 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8329,7 +8329,8 @@ class ExprEvaluatorBase
 
     const FunctionDecl *Definition = nullptr;
     Stmt *Body = FD->getBody(Definition);
-    if (!Definition && FD->getTemplateInstantiationPattern()) {
+    if (Info.getLangOpts().CPlusPlus23 && !Definition &&
+        FD->getTemplateInstantiationPattern()) {
       Info.getSemaProxy().InstantiateFunctionDefinition(
           E->getExprLoc(), const_cast<FunctionDecl *>(FD));
       Body = FD->getBody(Definition);
diff --git a/clang/test/SemaCXX/constexpr-function-instantiation.cpp b/clang/test/SemaCXX/constexpr-function-instantiation.cpp
index aec14e938be9f5..2480f14fdb7387 100644
--- a/clang/test/SemaCXX/constexpr-function-instantiation.cpp
+++ b/clang/test/SemaCXX/constexpr-function-instantiation.cpp
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=cxx20-23 %s
-// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=cxx20-23 %s
-// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify=cxx26 %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=cxx20 %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=cxx23-26 %s
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify=cxx23-26 %s
 
-// cxx26-no-diagnostics
+// cxx23-26-no-diagnostics
 
 namespace GH73232 {
 namespace ex1 {
@@ -18,10 +18,10 @@ template <typename T>
 constexpr void g(T) {}
 
 constexpr auto z = f(); // #ex1-z-defn
-// cxx20-23-error at -1 {{constexpr variable 'z' must be initialized by a constant expression}}
-//   cxx20-23-note@#ex1-g-call {{undefined function 'g<int>' cannot be used in a constant expression}}
-//   cxx20-23-note@#ex1-z-defn {{in call to 'f()'}}
-//   cxx20-23-note@#ex1-g-decl {{declared here}}
+// cxx20-error at -1 {{constexpr variable 'z' must be initialized by a constant expression}}
+//   cxx20-note@#ex1-g-call {{undefined function 'g<int>' cannot be used in a constant expression}}
+//   cxx20-note@#ex1-z-defn {{in call to 'f()'}}
+//   cxx20-note@#ex1-g-decl {{declared here}}
 } // namespace ex1
 
 namespace ex2 {

>From 0b2d92e0fed41ee2428c3ef8b8369790a1279a21 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Mon, 25 Nov 2024 21:47:41 +0300
Subject: [PATCH 13/13] Update the new constant interpreter to use the Sema
 callback

---
 clang/lib/AST/ByteCode/ByteCodeEmitter.cpp           | 12 +++++++++++-
 clang/lib/AST/ByteCode/ByteCodeEmitter.h             |  2 +-
 clang/lib/AST/ByteCode/Compiler.cpp                  |  9 +++++----
 clang/lib/AST/ByteCode/Compiler.h                    |  2 +-
 clang/lib/AST/ByteCode/Context.cpp                   |  8 +++++---
 clang/lib/AST/ByteCode/Context.h                     |  3 ++-
 .../SemaCXX/constexpr-function-instantiation.cpp     |  4 ++++
 7 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
index 4fd697ebe4938d..5b9fffdfc74dea 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
@@ -22,7 +22,8 @@
 using namespace clang;
 using namespace clang::interp;
 
-Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
+Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl,
+                                       SourceLocation Loc) {
 
   // Manually created functions that haven't been assigned proper
   // parameters yet.
@@ -145,6 +146,15 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
   }
 
   assert(Func);
+
+  if (Ctx.getLangOpts().CPlusPlus23 && !FuncDecl->isDefined() &&
+      FuncDecl->isImplicitlyInstantiable()) {
+    assert(Loc.isValid() && "Implicitly instantiating from the new constant "
+                            "interpreter without a valid source location!");
+    Ctx.getASTContext().getSemaProxy().InstantiateFunctionDefinition(
+        Loc, const_cast<FunctionDecl *>(FuncDecl));
+  }
+
   // For not-yet-defined functions, we only create a Function instance and
   // compile their body later.
   if (!FuncDecl->isDefined() ||
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
index ac728830527a26..ac03a86aff41bd 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
@@ -31,7 +31,7 @@ class ByteCodeEmitter {
 
 public:
   /// Compiles the function into the module.
-  Function *compileFunc(const FunctionDecl *FuncDecl);
+  Function *compileFunc(const FunctionDecl *FuncDecl, SourceLocation Loc);
   Function *compileObjCBlock(const BlockExpr *BE);
 
 protected:
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 59e09a44d747b9..734f122f3d10d4 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -2983,7 +2983,7 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
         return true;
     }
 
-    const Function *Func = getFunction(Ctor);
+    const Function *Func = getFunction(Ctor, E->getExprLoc());
 
     if (!Func)
       return false;
@@ -4121,8 +4121,9 @@ Record *Compiler<Emitter>::getRecord(const RecordDecl *RD) {
 }
 
 template <class Emitter>
-const Function *Compiler<Emitter>::getFunction(const FunctionDecl *FD) {
-  return Ctx.getOrCreateFunction(FD);
+const Function *Compiler<Emitter>::getFunction(const FunctionDecl *FD,
+                                               SourceLocation Loc) {
+  return Ctx.getOrCreateFunction(FD, Loc);
 }
 
 template <class Emitter>
@@ -4648,7 +4649,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
   }
 
   if (FuncDecl) {
-    const Function *Func = getFunction(FuncDecl);
+    const Function *Func = getFunction(FuncDecl, E->getExprLoc());
     if (!Func)
       return false;
     assert(HasRVO == Func->hasRVO());
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 5627d5071e810a..c6f4b3b638765d 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -243,7 +243,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
 
   /// Returns a function for the given FunctionDecl.
   /// If the function does not exist yet, it is compiled.
-  const Function *getFunction(const FunctionDecl *FD);
+  const Function *getFunction(const FunctionDecl *FD, SourceLocation Loc = {});
 
   std::optional<PrimType> classify(const Expr *E) const {
     return Ctx.classify(E);
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index 7088cf02901c63..df34594242f4c3 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -29,7 +29,8 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
   assert(Stk.empty());
   Function *Func = P->getFunction(FD);
   if (!Func || !Func->hasBody())
-    Func = Compiler<ByteCodeEmitter>(*this, *P).compileFunc(FD);
+    Func =
+        Compiler<ByteCodeEmitter>(*this, *P).compileFunc(FD, SourceLocation{});
 
   if (!Func)
     return false;
@@ -270,7 +271,8 @@ Context::getOverridingFunction(const CXXRecordDecl *DynamicDecl,
   return nullptr;
 }
 
-const Function *Context::getOrCreateFunction(const FunctionDecl *FD) {
+const Function *Context::getOrCreateFunction(const FunctionDecl *FD,
+                                             SourceLocation Loc) {
   assert(FD);
   const Function *Func = P->getFunction(FD);
   bool IsBeingCompiled = Func && Func->isDefined() && !Func->isFullyCompiled();
@@ -280,7 +282,7 @@ const Function *Context::getOrCreateFunction(const FunctionDecl *FD) {
     return Func;
 
   if (!Func || WasNotDefined) {
-    if (auto F = Compiler<ByteCodeEmitter>(*this, *P).compileFunc(FD))
+    if (auto F = Compiler<ByteCodeEmitter>(*this, *P).compileFunc(FD, Loc))
       Func = F;
   }
 
diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h
index e0d4bafdebaf2b..fd630dcaed708e 100644
--- a/clang/lib/AST/ByteCode/Context.h
+++ b/clang/lib/AST/ByteCode/Context.h
@@ -91,7 +91,8 @@ class Context final {
                         const CXXRecordDecl *StaticDecl,
                         const CXXMethodDecl *InitialFunction) const;
 
-  const Function *getOrCreateFunction(const FunctionDecl *FD);
+  const Function *getOrCreateFunction(const FunctionDecl *FD,
+                                      SourceLocation Loc = {});
 
   /// Returns whether we should create a global variable for the
   /// given ValueDecl.
diff --git a/clang/test/SemaCXX/constexpr-function-instantiation.cpp b/clang/test/SemaCXX/constexpr-function-instantiation.cpp
index 2480f14fdb7387..edc3e6fbd52639 100644
--- a/clang/test/SemaCXX/constexpr-function-instantiation.cpp
+++ b/clang/test/SemaCXX/constexpr-function-instantiation.cpp
@@ -2,6 +2,10 @@
 // RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=cxx23-26 %s
 // RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify=cxx23-26 %s
 
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=cxx20    -fexperimental-new-constant-interpreter %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=cxx23-26 -fexperimental-new-constant-interpreter %s
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify=cxx23-26 -fexperimental-new-constant-interpreter %s
+
 // cxx23-26-no-diagnostics
 
 namespace GH73232 {



More information about the cfe-commits mailing list