https://github.com/zyn0217 updated 
https://github.com/llvm/llvm-project/pull/122423

>From 56bacf47c53aca276ae4fa6aa2972b7eda152ddd Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Fri, 10 Jan 2025 09:46:24 +0800
Subject: [PATCH 01/10] Reapply "[Clang] Implement CWG2369 "Ordering between
 constraints and substitution"" (#122130)

This reverts commit 3972ed57088f6515b787d7d38dec03dc74e51827.
---
 clang/include/clang/Sema/Sema.h               |  22 +++-
 clang/include/clang/Sema/Template.h           |   6 +
 clang/lib/Sema/SemaConcept.cpp                |  47 ++++++-
 clang/lib/Sema/SemaTemplateDeduction.cpp      |  49 +++++---
 clang/lib/Sema/SemaTemplateDeductionGuide.cpp |   8 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 115 ++++++++++++++++--
 clang/lib/Sema/TreeTransform.h                |   2 +-
 clang/test/CXX/drs/cwg23xx.cpp                |  29 +++++
 clang/test/CXX/drs/cwg26xx.cpp                |   2 +-
 clang/test/CXX/drs/cwg27xx.cpp                |  20 +++
 .../expr.prim.req/nested-requirement.cpp      |   2 +-
 .../constrant-satisfaction-conversions.cpp    |   6 +-
 .../SemaCXX/concept-crash-on-diagnostic.cpp   |   2 +-
 clang/test/SemaCXX/cxx20-ctad-type-alias.cpp  |   2 +-
 clang/test/SemaCXX/cxx23-assume.cpp           |   6 +-
 clang/test/SemaCXX/cxx2c-fold-exprs.cpp       |   2 +-
 clang/test/SemaCXX/lambda-unevaluated.cpp     |   4 +-
 .../SemaTemplate/concepts-recursive-inst.cpp  |   4 +-
 .../SemaTemplate/cxx2a-constraint-exprs.cpp   |   2 +-
 .../nested-implicit-deduction-guides.cpp      |   8 +-
 clang/www/cxx_dr_status.html                  |   8 +-
 21 files changed, 289 insertions(+), 57 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 96d81e618494a..3b1f4d3234ea9 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13303,6 +13303,7 @@ class Sema final : public SemaBase {
   ///
   /// \param SkipForSpecialization when specified, any template specializations
   /// in a traversal would be ignored.
+  ///
   /// \param ForDefaultArgumentSubstitution indicates we should continue 
looking
   /// when encountering a specialized member function template, rather than
   /// returning immediately.
@@ -13314,6 +13315,17 @@ class Sema final : public SemaBase {
       bool SkipForSpecialization = false,
       bool ForDefaultArgumentSubstitution = false);
 
+  /// Apart from storing the result to \p Result, this behaves the same as
+  /// another overload.
+  void getTemplateInstantiationArgs(
+      MultiLevelTemplateArgumentList &Result, const NamedDecl *D,
+      const DeclContext *DC = nullptr, bool Final = false,
+      std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
+      bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
+      bool ForConstraintInstantiation = false,
+      bool SkipForSpecialization = false,
+      bool ForDefaultArgumentSubstitution = false);
+
   /// RAII object to handle the state changes required to synthesize
   /// a function body.
   class SynthesizedFunctionScope {
@@ -13590,7 +13602,7 @@ class Sema final : public SemaBase {
   ExprResult
   SubstConstraintExpr(Expr *E,
                       const MultiLevelTemplateArgumentList &TemplateArgs);
-  // Unlike the above, this does not evaluates constraints.
+  // Unlike the above, this does not evaluate constraints.
   ExprResult SubstConstraintExprWithoutSatisfaction(
       Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs);
 
@@ -14732,10 +14744,10 @@ class Sema final : public SemaBase {
       const MultiLevelTemplateArgumentList &TemplateArgs,
       SourceRange TemplateIDRange);
 
-  bool CheckInstantiatedFunctionTemplateConstraints(
-      SourceLocation PointOfInstantiation, FunctionDecl *Decl,
-      ArrayRef<TemplateArgument> TemplateArgs,
-      ConstraintSatisfaction &Satisfaction);
+  bool CheckFunctionTemplateConstraints(SourceLocation PointOfInstantiation,
+                                        FunctionDecl *Decl,
+                                        ArrayRef<TemplateArgument> 
TemplateArgs,
+                                        ConstraintSatisfaction &Satisfaction);
 
   /// \brief Emit diagnostics explaining why a constraint expression was deemed
   /// unsatisfied.
diff --git a/clang/include/clang/Sema/Template.h 
b/clang/include/clang/Sema/Template.h
index f9a10cfafb1f7..39f0cf225e673 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -522,6 +522,12 @@ enum class TemplateSubstitutionKind : char {
     llvm::PointerUnion<Decl *, DeclArgumentPack *> *
     findInstantiationOf(const Decl *D);
 
+    /// Similar to \p findInstantiationOf(), but it wouldn't assert if the
+    /// instantiation was not found within the current instantiation scope. 
This
+    /// is helpful for on-demand declaration instantiation.
+    llvm::PointerUnion<Decl *, DeclArgumentPack *> *
+    findInstantiationUnsafe(const Decl *D);
+
     void InstantiatedLocal(const Decl *D, Decl *Inst);
     void InstantiatedLocalPackArg(const Decl *D, VarDecl *Inst);
     void MakeInstantiatedLocalArgPack(const Decl *D);
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 15b9c97489e7f..520fe4aac97be 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -792,7 +792,7 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
                                     bool ForOverloadResolution) {
   // Don't check constraints if the function is dependent. Also don't check if
   // this is a function template specialization, as the call to
-  // CheckinstantiatedFunctionTemplateConstraints after this will check it
+  // CheckFunctionTemplateConstraints after this will check it
   // better.
   if (FD->isDependentContext() ||
       FD->getTemplatedKind() ==
@@ -1060,12 +1060,55 @@ bool Sema::EnsureTemplateArgumentListConstraints(
   return false;
 }
 
-bool Sema::CheckInstantiatedFunctionTemplateConstraints(
+static bool CheckFunctionConstraintsWithoutInstantiation(
+    Sema &SemaRef, SourceLocation PointOfInstantiation,
+    FunctionTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
+    ConstraintSatisfaction &Satisfaction) {
+  SmallVector<const Expr *, 3> TemplateAC;
+  Template->getAssociatedConstraints(TemplateAC);
+  if (TemplateAC.empty()) {
+    Satisfaction.IsSatisfied = true;
+    return false;
+  }
+
+  LocalInstantiationScope Scope(SemaRef);
+
+  FunctionDecl *FD = Template->getTemplatedDecl();
+  // Collect the list of template arguments relative to the 'primary'
+  // template. We need the entire list, since the constraint is completely
+  // uninstantiated at this point.
+
+  // FIXME: Add TemplateArgs through the 'Innermost' parameter once
+  // the refactoring of getTemplateInstantiationArgs() relands.
+  MultiLevelTemplateArgumentList MLTAL;
+  MLTAL.addOuterTemplateArguments(Template, std::nullopt, /*Final=*/false);
+  SemaRef.getTemplateInstantiationArgs(
+      MLTAL, /*D=*/FD, FD,
+      /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true,
+      /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true);
+  MLTAL.replaceInnermostTemplateArguments(Template, TemplateArgs);
+
+  Sema::ContextRAII SavedContext(SemaRef, FD);
+  std::optional<Sema::CXXThisScopeRAII> ThisScope;
+  if (auto *Method = dyn_cast<CXXMethodDecl>(FD))
+    ThisScope.emplace(SemaRef, /*Record=*/Method->getParent(),
+                      /*ThisQuals=*/Method->getMethodQualifiers());
+  return SemaRef.CheckConstraintSatisfaction(
+      Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction);
+}
+
+bool Sema::CheckFunctionTemplateConstraints(
     SourceLocation PointOfInstantiation, FunctionDecl *Decl,
     ArrayRef<TemplateArgument> TemplateArgs,
     ConstraintSatisfaction &Satisfaction) {
   // In most cases we're not going to have constraints, so check for that 
first.
   FunctionTemplateDecl *Template = Decl->getPrimaryTemplate();
+
+  if (!Template)
+    return ::CheckFunctionConstraintsWithoutInstantiation(
+        *this, PointOfInstantiation, Decl->getDescribedFunctionTemplate(),
+        TemplateArgs, Satisfaction);
+
   // Note - code synthesis context for the constraints check is created
   // inside CheckConstraintsSatisfaction.
   SmallVector<AssociatedConstraint, 3> TemplateAC;
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp 
b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 0ecdbb3ffb89f..4a58fdf014d43 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3991,18 +3991,6 @@ TemplateDeductionResult 
Sema::FinishTemplateArgumentDeduction(
       Result != TemplateDeductionResult::Success)
     return Result;
 
-  // C++ [temp.deduct.call]p10: [DR1391]
-  //   If deduction succeeds for all parameters that contain
-  //   template-parameters that participate in template argument deduction,
-  //   and all template arguments are explicitly specified, deduced, or
-  //   obtained from default template arguments, remaining parameters are then
-  //   compared with the corresponding arguments. For each remaining parameter
-  //   P with a type that was non-dependent before substitution of any
-  //   explicitly-specified template arguments, if the corresponding argument
-  //   A cannot be implicitly converted to P, deduction fails.
-  if (CheckNonDependent())
-    return TemplateDeductionResult::NonDependentConversionFailure;
-
   // Form the template argument list from the deduced template arguments.
   TemplateArgumentList *SugaredDeducedArgumentList =
       TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted);
@@ -4017,6 +4005,39 @@ TemplateDeductionResult 
Sema::FinishTemplateArgumentDeduction(
     Owner = FunctionTemplate->getLexicalDeclContext();
   FunctionDecl *FD = FunctionTemplate->getTemplatedDecl();
 
+  // C++20 [temp.deduct.general]p5: [CWG2369]
+  //   If the function template has associated constraints, those constraints
+  //   are checked for satisfaction. If the constraints are not satisfied, type
+  //   deduction fails.
+  //
+  // FIXME: We haven't implemented CWG2369 for lambdas yet, because we need
+  // to figure out how to instantiate lambda captures to the scope without
+  // first instantiating the lambda.
+  bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD);
+  if (!IsLambda && !IsIncomplete) {
+    if (CheckFunctionTemplateConstraints(
+            Info.getLocation(),
+            FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
+            CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction))
+      return TemplateDeductionResult::MiscellaneousDeductionFailure;
+    if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
+      Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy(
+                                         Context, CTAI.CanonicalConverted));
+      return TemplateDeductionResult::ConstraintsNotSatisfied;
+    }
+  }
+  // C++ [temp.deduct.call]p10: [CWG1391]
+  //   If deduction succeeds for all parameters that contain
+  //   template-parameters that participate in template argument deduction,
+  //   and all template arguments are explicitly specified, deduced, or
+  //   obtained from default template arguments, remaining parameters are then
+  //   compared with the corresponding arguments. For each remaining parameter
+  //   P with a type that was non-dependent before substitution of any
+  //   explicitly-specified template arguments, if the corresponding argument
+  //   A cannot be implicitly converted to P, deduction fails.
+  if (CheckNonDependent())
+    return TemplateDeductionResult::NonDependentConversionFailure;
+
   MultiLevelTemplateArgumentList SubstArgs(
       FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
       /*Final=*/false);
@@ -4051,8 +4072,8 @@ TemplateDeductionResult 
Sema::FinishTemplateArgumentDeduction(
   //   ([temp.constr.decl]), those constraints are checked for satisfaction
   //   ([temp.constr.constr]). If the constraints are not satisfied, type
   //   deduction fails.
-  if (!IsIncomplete) {
-    if (CheckInstantiatedFunctionTemplateConstraints(
+  if (IsLambda && !IsIncomplete) {
+    if (CheckFunctionTemplateConstraints(
             Info.getLocation(), Specialization, CTAI.CanonicalConverted,
             Info.AssociatedConstraintsSatisfaction))
       return TemplateDeductionResult::MiscellaneousDeductionFailure;
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp 
b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index 29c5736a9bf9e..0f3dd1ee9b071 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -955,10 +955,12 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef,
       Context.getTrivialTypeSourceInfo(
           Context.getDeducedTemplateSpecializationType(
               TemplateName(AliasTemplate), /*DeducedType=*/QualType(),
-              /*IsDependent=*/true)), // template specialization type whose
-                                      // arguments will be deduced.
+              /*IsDependent=*/true),
+          AliasTemplate->getLocation()), // template specialization type whose
+                                         // arguments will be deduced.
       Context.getTrivialTypeSourceInfo(
-          ReturnType), // type from which template arguments are deduced.
+          ReturnType, AliasTemplate->getLocation()), // type from which 
template
+                                                     // arguments are deduced.
   };
   return TypeTraitExpr::Create(
       Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(),
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 0e81804f8c1e7..49b1ac32692ef 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -475,6 +475,21 @@ MultiLevelTemplateArgumentList 
Sema::getTemplateInstantiationArgs(
   assert((ND || DC) && "Can't find arguments for a decl if one isn't 
provided");
   // Accumulate the set of template argument lists in this structure.
   MultiLevelTemplateArgumentList Result;
+  getTemplateInstantiationArgs(
+      Result, ND, DC, Final, Innermost, RelativeToPrimary, Pattern,
+      ForConstraintInstantiation, SkipForSpecialization,
+      ForDefaultArgumentSubstitution);
+  return Result;
+}
+
+void Sema::getTemplateInstantiationArgs(
+    MultiLevelTemplateArgumentList &Result, const NamedDecl *ND,
+    const DeclContext *DC, bool Final,
+    std::optional<ArrayRef<TemplateArgument>> Innermost, bool 
RelativeToPrimary,
+    const FunctionDecl *Pattern, bool ForConstraintInstantiation,
+    bool SkipForSpecialization, bool ForDefaultArgumentSubstitution) {
+  assert((ND || DC) && "Can't find arguments for a decl if one isn't 
provided");
+  // Accumulate the set of template argument lists in this structure.
 
   using namespace TemplateInstArgsHelpers;
   const Decl *CurDecl = ND;
@@ -534,14 +549,12 @@ MultiLevelTemplateArgumentList 
Sema::getTemplateInstantiationArgs(
     }
 
     if (R.IsDone)
-      return Result;
+      return;
     if (R.ClearRelativeToPrimary)
       RelativeToPrimary = false;
     assert(R.NextDecl);
     CurDecl = R.NextDecl;
   }
-
-  return Result;
 }
 
 bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
@@ -1374,6 +1387,19 @@ namespace {
     // Whether an incomplete substituion should be treated as an error.
     bool BailOutOnIncomplete;
 
+  private:
+    bool isSubstitutingConstraints() const {
+      return llvm::any_of(SemaRef.CodeSynthesisContexts, [](auto &Context) {
+        return Context.Kind ==
+               Sema::CodeSynthesisContext::ConstraintSubstitution;
+      });
+    }
+
+    // CWG2770: Function parameters should be instantiated when they are
+    // needed by a satisfaction check of an atomic constraint or
+    // (recursively) by another function parameter.
+    bool maybeInstantiateFunctionParameterToScope(ParmVarDecl *OldParm);
+
   public:
     typedef TreeTransform<TemplateInstantiator> inherited;
 
@@ -1430,12 +1456,19 @@ namespace {
                                  ArrayRef<UnexpandedParameterPack> Unexpanded,
                                  bool &ShouldExpand, bool &RetainExpansion,
                                  UnsignedOrNone &NumExpansions) {
-      return getSema().CheckParameterPacksForExpansion(EllipsisLoc,
-                                                       PatternRange, 
Unexpanded,
-                                                       TemplateArgs,
-                                                       ShouldExpand,
-                                                       RetainExpansion,
-                                                       NumExpansions);
+      if (SemaRef.CurrentInstantiationScope && isSubstitutingConstraints()) {
+        for (UnexpandedParameterPack ParmPack : Unexpanded) {
+          NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
+          if (!isa_and_present<ParmVarDecl>(VD))
+            continue;
+          if (maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(VD)))
+            return true;
+        }
+      }
+
+      return getSema().CheckParameterPacksForExpansion(
+          EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand,
+          RetainExpansion, NumExpansions);
     }
 
     void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
@@ -1919,9 +1952,62 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation 
Loc, Decl *D) {
     // template parameter.
   }
 
+  if (SemaRef.CurrentInstantiationScope) {
+    if (isSubstitutingConstraints() && isa<ParmVarDecl>(D) &&
+        maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(D)))
+      return nullptr;
+  }
+
   return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
 }
 
+bool TemplateInstantiator::maybeInstantiateFunctionParameterToScope(
+    ParmVarDecl *OldParm) {
+  if (SemaRef.CurrentInstantiationScope->findInstantiationUnsafe(OldParm))
+    return false;
+  // We're instantiating a function parameter whose associated function 
template
+  // has not been instantiated at this point for constraint evaluation, so make
+  // sure the instantiated parameters are owned by a function declaration such
+  // that they can be correctly 'captured' in tryCaptureVariable().
+  Sema::ContextRAII Context(SemaRef, OldParm->getDeclContext());
+
+  if (!OldParm->isParameterPack())
+    return !TransformFunctionTypeParam(OldParm, /*indexAdjustment=*/0,
+                                       /*NumExpansions=*/std::nullopt,
+                                       /*ExpectParameterPack=*/false);
+
+  SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+
+  // Find the parameter packs that could be expanded.
+  TypeLoc TL = OldParm->getTypeSourceInfo()->getTypeLoc();
+  PackExpansionTypeLoc ExpansionTL = TL.castAs<PackExpansionTypeLoc>();
+  TypeLoc Pattern = ExpansionTL.getPatternLoc();
+  SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded);
+  assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+
+  bool ShouldExpand = false;
+  bool RetainExpansion = false;
+  std::optional<unsigned> OrigNumExpansions =
+      ExpansionTL.getTypePtr()->getNumExpansions();
+  std::optional<unsigned> NumExpansions = OrigNumExpansions;
+  if (TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(),
+                              Pattern.getSourceRange(), Unexpanded,
+                              ShouldExpand, RetainExpansion, NumExpansions))
+    return true;
+
+  assert(ShouldExpand && !RetainExpansion &&
+         "Shouldn't preserve pack expansion when evaluating constraints");
+  ExpandingFunctionParameterPack(OldParm);
+  for (unsigned I = 0; I != *NumExpansions; ++I) {
+    Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
+    if (!TransformFunctionTypeParam(OldParm, /*indexAdjustment=*/0,
+                                    /*NumExpansions=*/OrigNumExpansions,
+                                    /*ExpectParameterPack=*/false))
+      return true;
+  }
+  return false;
+}
+
 Decl *TemplateInstantiator::TransformDefinition(SourceLocation Loc, Decl *D) {
   Decl *Inst = getSema().SubstDecl(D, getSema().CurContext, TemplateArgs);
   if (!Inst)
@@ -4485,9 +4571,8 @@ static const Decl *getCanonicalParmVarDecl(const Decl *D) 
{
   return D;
 }
 
-
 llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
-LocalInstantiationScope::findInstantiationOf(const Decl *D) {
+LocalInstantiationScope::findInstantiationUnsafe(const Decl *D) {
   D = getCanonicalParmVarDecl(D);
   for (LocalInstantiationScope *Current = this; Current;
        Current = Current->Outer) {
@@ -4512,6 +4597,14 @@ LocalInstantiationScope::findInstantiationOf(const Decl 
*D) {
       break;
   }
 
+  return nullptr;
+}
+
+llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
+LocalInstantiationScope::findInstantiationOf(const Decl *D) {
+  auto *Result = findInstantiationUnsafe(D);
+  if (Result)
+    return Result;
   // If we're performing a partial substitution during template argument
   // deduction, we may not have values for template parameters yet.
   if (isa<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) ||
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 071389543ab68..4b1752105bafc 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -714,7 +714,7 @@ class TreeTransform {
   /// variables vector are acceptable.
   ///
   /// LastParamTransformed, if non-null, will be set to the index of the last
-  /// parameter on which transfromation was started. In the event of an error,
+  /// parameter on which transformation was started. In the event of an error,
   /// this will contain the parameter which failed to instantiate.
   ///
   /// Return true on error.
diff --git a/clang/test/CXX/drs/cwg23xx.cpp b/clang/test/CXX/drs/cwg23xx.cpp
index 74e72f2371e2a..128566537b577 100644
--- a/clang/test/CXX/drs/cwg23xx.cpp
+++ b/clang/test/CXX/drs/cwg23xx.cpp
@@ -365,6 +365,35 @@ struct A {
 #endif
 } // namespace cwg2363
 
+namespace cwg2369 { // cwg2369: partial
+#if __cplusplus >= 202002L
+template <class T> struct Z {
+  typedef typename T::x xx;
+};
+
+template <class T>
+concept C = requires { typename T::A; };
+template <C T> typename Z<T>::xx f(void *, T); // #1
+template <class T> void f(int, T);             // #2
+
+struct A {
+} a;
+
+struct ZZ {
+  template <class T, class = typename Z<T>::xx> operator T *();
+  operator int();
+};
+
+void foo() {
+  ZZ zz;
+  f(1, a); // OK, deduction fails for #1 because there is no conversion from 
int
+           // to void*
+  f(zz, 42); // OK, deduction fails for #1 because C<int> is not satisfied
+}
+
+#endif
+} // namespace cwg2369
+
 namespace cwg2370 { // cwg2370: no
 namespace N {
 typedef int type;
diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp
index 3eb70583b6026..ab4d3695b6e22 100644
--- a/clang/test/CXX/drs/cwg26xx.cpp
+++ b/clang/test/CXX/drs/cwg26xx.cpp
@@ -310,7 +310,7 @@ void f(T) requires requires { []() { T::invalid; } (); };
 //   since-cxx20-note@-3 {{in instantiation of requirement here}}
 //   since-cxx20-note@-4 {{while substituting template arguments into 
constraint expression here}}
 //   since-cxx20-note@#cwg2672-f-0 {{while checking constraint satisfaction 
for template 'f<int>' required here}}
-//   since-cxx20-note@#cwg2672-f-0 {{in instantiation of function template 
specialization 'cwg2672::f<int>' requested here}}
+//   since-cxx20-note@#cwg2672-f-0 {{while substituting deduced template 
arguments into function template 'f' [with T = int]}}
 void f(...);
 
 template <class T>
diff --git a/clang/test/CXX/drs/cwg27xx.cpp b/clang/test/CXX/drs/cwg27xx.cpp
index a87d26dfc9acf..7caf36a9f23b2 100644
--- a/clang/test/CXX/drs/cwg27xx.cpp
+++ b/clang/test/CXX/drs/cwg27xx.cpp
@@ -174,6 +174,26 @@ static_assert(!__is_layout_compatible(StructWithAnonUnion, 
StructWithAnonUnion3)
 #endif
 } // namespace cwg2759
 
+namespace cwg2770 { // cwg2770: 20 open 2023-07-14
+#if __cplusplus >= 202002L
+template<typename T>
+struct B {
+  static_assert(sizeof(T) == 1);
+  using type = int;
+};
+
+template<typename T>
+int f(T t, typename B<T>::type u) requires (sizeof(t) == 1);
+
+template<typename T>
+int f(T t, long);
+
+int i = f(1, 2);
+int j = f('a', 2);
+
+#endif
+} // namespace cwg2770
+
 namespace cwg2789 { // cwg2789: 18
 #if __cplusplus >= 202302L
 template <typename T = int>
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp 
b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
index 651cca927d513..19145f68b2f75 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
@@ -154,7 +154,7 @@ void func() {
 
   bar<int>();
   // expected-note@-1 {{while checking constraint satisfaction for template 
'bar<int>' required here}} \
-  // expected-note@-1 {{in instantiation of function template specialization}}
+  // expected-note@-1 {{while substituting deduced template arguments into 
function template 'bar' [with T = int]}}
   // expected-note@#bar {{in instantiation of static data member}}
   // expected-note@#bar {{in instantiation of requirement here}}
   // expected-note@#bar {{while checking the satisfaction of nested 
requirement requested here}}
diff --git 
a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
 
b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
index 083e743818121..59e6a48e48878 100644
--- 
a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
+++ 
b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
@@ -11,7 +11,7 @@ template<typename T> struct S {
 
 // expected-error@+3{{atomic constraint must be of type 'bool' (found 
'S<int>')}}
 // expected-note@#FINST{{while checking constraint satisfaction}}
-// expected-note@#FINST{{in instantiation of function template specialization}}
+// expected-note@#FINST{{while substituting deduced template arguments into 
function template 'f' [with T = int]}}
 template<typename T> requires (S<T>{})
 void f(T);
 void f(long);
@@ -19,7 +19,7 @@ void f(long);
 // Ensure this applies to operator && as well.
 // expected-error@+3{{atomic constraint must be of type 'bool' (found 
'S<int>')}}
 // expected-note@#F2INST{{while checking constraint satisfaction}}
-// expected-note@#F2INST{{in instantiation of function template 
specialization}}
+// expected-note@#F2INST{{while substituting deduced template arguments into 
function template 'f2' [with T = int]}}
 template<typename T> requires (S<T>{} && true)
 void f2(T);
 void f2(long);
@@ -32,7 +32,7 @@ template<typename T> requires requires {
   // expected-note@-4{{while checking the satisfaction}}
   // expected-note@-6{{while substituting template arguments}}
   // expected-note@#F3INST{{while checking constraint satisfaction}}
-  // expected-note@#F3INST{{in instantiation of function template 
specialization}}
+  // expected-note@#F3INST{{while substituting deduced template arguments into 
function template 'f3' [with T = int]}}
   //
 }
 void f3(T);
diff --git a/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp 
b/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
index c38f8888075de..00d6f8b62a458 100644
--- a/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
+++ b/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -31,7 +31,7 @@ void function() {
 // expected-note@#3 {{checking the satisfaction of concept 
'convertible_to<bool, bool>'}}
 // expected-note@#2 {{substituting template arguments into constraint 
expression here}}
 // expected-note@#5 {{checking constraint satisfaction for template 
'compare<Object *, Object *>'}}
-// expected-note@#5 {{in instantiation of function template specialization 
'compare<Object *, Object *>' requested here}}
+// expected-note@#5 {{while substituting deduced template arguments into 
function template 'compare' [with IteratorL = Object *, IteratorR = Object *]}}
 
 // expected-note@#4 {{candidate template ignored: constraints not satisfied 
[with IteratorL = Object *, IteratorR = Object *]}}
 // We don't know exactly the substituted type for `lhs == rhs`, thus a 
placeholder 'expr-type' is emitted.
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp 
b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index c863cc841af42..aeb02c9e4898e 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -196,7 +196,7 @@ struct Foo {
 
 template <int K>
 using Bar = Foo<double, K>; // expected-note {{constraints not satisfied for 
class template 'Foo'}}
-// expected-note@-1 {{candidate template ignored: could not match}}
+// expected-note@-1 {{candidate template ignored: could not match}} 
expected-note@-1 {{candidate template ignored: constraints not satisfied}}
 // expected-note@-2 {{implicit deduction guide declared as 'template <int K> 
requires __is_deducible(test14::Bar, Foo<double, K>) Bar(Foo<double, K>) -> 
Foo<double, K>'}}
 // expected-note@-3 {{implicit deduction guide declared as 'template <int K> 
requires __is_deducible(test14::Bar, Foo<double, K>) Bar(const double (&)[K]) 
-> Foo<double, K>'}}
 double abc[3];
diff --git a/clang/test/SemaCXX/cxx23-assume.cpp 
b/clang/test/SemaCXX/cxx23-assume.cpp
index 7f80cdfe7d452..726cb3bff652e 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -129,12 +129,12 @@ constexpr int f5() requires (!C<T>) { return 2; } // 
expected-note 4 {{while che
 
 static_assert(f5<int>() == 1);
 static_assert(f5<D>() == 1); // expected-note 3 {{while checking constraint 
satisfaction}}
-                             // expected-note@-1 3 {{in instantiation of}}
+                             // expected-note@-1 3 {{while substituting 
deduced template arguments}}
                              // expected-error@-2 {{no matching function for 
call}}
 
 static_assert(f5<double>() == 2);
-static_assert(f5<E>() == 1); // expected-note {{while checking constraint 
satisfaction}} expected-note {{in instantiation of}}
-static_assert(f5<F>() == 2); // expected-note {{while checking constraint 
satisfaction}} expected-note {{in instantiation of}}
+static_assert(f5<E>() == 1); // expected-note {{while checking constraint 
satisfaction}} expected-note {{while substituting deduced template arguments}}
+static_assert(f5<F>() == 2); // expected-note {{while checking constraint 
satisfaction}} expected-note {{while substituting deduced template arguments}}
 
 // Do not validate assumptions whose evaluation would have side-effects.
 constexpr int foo() {
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp 
b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 48061439941f2..4220486d3aed3 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -233,7 +233,7 @@ void g() {
     A<Thingy, Thingy> *ap;
     f(ap, ap); // expected-error{{no matching function for call to 'f'}} \
                // expected-note {{while checking constraint satisfaction}} \
-               // expected-note {{in instantiation of function template 
specialization}}
+               // expected-note {{while substituting deduced template 
arguments}}
 }
 
 }
diff --git a/clang/test/SemaCXX/lambda-unevaluated.cpp 
b/clang/test/SemaCXX/lambda-unevaluated.cpp
index a9bcab58464e2..d3f937281f201 100644
--- a/clang/test/SemaCXX/lambda-unevaluated.cpp
+++ b/clang/test/SemaCXX/lambda-unevaluated.cpp
@@ -174,7 +174,7 @@ int* func(T) requires requires { []() { T::foo(); }; }; // 
expected-error{{type
 double* func(...);
 
 static_assert(__is_same(decltype(func(0)), double*)); // expected-note {{while 
checking constraint satisfaction for template 'func<int>' required here}}
-                                                      // expected-note@-1 {{in 
instantiation of function template specialization 
'lambda_in_constraints::func<int>'}}
+                                                      // expected-note@-1 
{{while substituting deduced template arguments into function template 'func' 
[with T = int]}}
 static_assert(__is_same(decltype(func(WithFoo())), int*));
 
 template <class T>
@@ -252,7 +252,7 @@ S s("a"); // #use
 // expected-note@#S-requires {{substituting template arguments into constraint 
expression here}}
 // expected-note@#S-requires {{in instantiation of requirement here}}
 // expected-note@#use {{checking constraint satisfaction for template 'S<const 
char *>' required here}}
-// expected-note@#use {{requested here}}
+// expected-note@#use {{while substituting deduced template arguments into 
function template 'S' [with value:auto = const char *]}}
 // expected-note-re@#S 2{{candidate constructor {{.*}} not viable}}
 // expected-note@#S-ctor {{constraints not satisfied}}
 // expected-note-re@#S-requires {{because {{.*}} would be invalid}}
diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp 
b/clang/test/SemaTemplate/concepts-recursive-inst.cpp
index 9330df8cdd039..30a410cef91ee 100644
--- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp
+++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp
@@ -76,7 +76,7 @@ auto it = begin(rng); // #BEGIN_CALL
 // expected-note@#INF_BEGIN {{while checking the satisfaction of concept 
'Inf<DirectRecursiveCheck::my_range>' requested here}}
 // expected-note@#INF_BEGIN {{while substituting template arguments into 
constraint expression here}}
 // expected-note@#BEGIN_CALL {{while checking constraint satisfaction for 
template 'begin<DirectRecursiveCheck::my_range>' required here}}
-// expected-note@#BEGIN_CALL {{in instantiation of function template 
specialization}}
+// expected-note@#BEGIN_CALL {{while substituting deduced template arguments 
into function template}}
 
 // Fallout of the failure is failed lookup, which is necessary to stop odd
 // cascading errors.
@@ -103,7 +103,7 @@ namespace GH50891 {
   // expected-note@#OP_TO {{while checking the satisfaction of concept 
'Numeric<GH50891::Deferred>' requested here}}
   // expected-note@#OP_TO {{while substituting template arguments into 
constraint expression here}}
   // expected-note@#FOO_CALL {{while checking constraint satisfaction for 
template}}
-  // expected-note@#FOO_CALL {{in instantiation of function template 
specialization}}
+  // expected-note@#FOO_CALL {{while substituting deduced template arguments 
into function template}}
   // expected-note@#FOO_CALL {{in instantiation of requirement here}}
   // expected-note@#NUMERIC {{while substituting template arguments into 
constraint expression here}}
 
diff --git a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp 
b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
index f4403587a6259..5809ef684bbf3 100644
--- a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
+++ b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
@@ -34,7 +34,7 @@ namespace constant_evaluated {
      expected-note@-1{{candidate template ignored}}
   int a = (foo<int>(), 0);
   // expected-note@-1 {{while checking}} expected-error@-1{{no matching 
function}} \
-     expected-note@-1 {{in instantiation}}
+     expected-note@-1 {{while substituting}}
   template<typename T> void bar() requires requires { requires f<int[2]>; } { 
};
   // expected-note@-1{{in instantiation}} \
      expected-note@-1{{while substituting}} \
diff --git a/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp 
b/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp
index af3e3358f6138..5c7a90273d0e0 100644
--- a/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp
+++ b/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp
@@ -38,7 +38,7 @@ template<typename A, typename T>
 concept True = true;
 
 template<typename T>
-concept False = false;
+concept False = false; // #False
 
 template<class X> struct concepts {
     template<class Y> struct B {
@@ -68,7 +68,7 @@ template<typename X> struct nested_init_list {
         Y y;
     };
 
-    template<False F>
+    template<False F>  // #INIT_LIST_INNER_INVALID_HEADER
     struct concept_fail { // #INIT_LIST_INNER_INVALID
         X x;
         F f;
@@ -81,7 +81,9 @@ using NIL = nested_init_list<int>::B<int>;
 
 // expected-error@+1 {{no viable constructor or deduction guide for deduction 
of template arguments of 'nested_init_list<int>::concept_fail'}}
 nested_init_list<int>::concept_fail nil_invalid{1, ""};
-// expected-note@#INIT_LIST_INNER_INVALID {{candidate template ignored: 
substitution failure [with F = const char *]: constraints not satisfied for 
class template 'concept_fail' [with F = const char *]}}
+// expected-note@#INIT_LIST_INNER_INVALID {{candidate template ignored: 
constraints not satisfied [with F = const char *]}}
+// expected-note@#INIT_LIST_INNER_INVALID_HEADER {{because 'const char *' does 
not satisfy 'False'}}
+// expected-note@#False {{because 'false' evaluated to false}}
 // expected-note@#INIT_LIST_INNER_INVALID {{implicit deduction guide declared 
as 'template <False F> concept_fail(int, F) -> concept_fail<F>'}}
 // expected-note@#INIT_LIST_INNER_INVALID {{candidate function template not 
viable: requires 1 argument, but 2 were provided}}
 // expected-note@#INIT_LIST_INNER_INVALID {{implicit deduction guide declared 
as 'template <False F> concept_fail(concept_fail<F>) -> concept_fail<F>'}}
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 8fe53ad46aca9..969356df960b7 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -14049,7 +14049,7 @@ <h2 id="cxxdr">C++ defect report implementation 
status</h2>
     <td><a 
href="https://cplusplus.github.io/CWG/issues/2369.html";>2369</a></td>
     <td>CD6</td>
     <td>Ordering between constraints and substitution</td>
-    <td class="unknown" align="center">Unknown</td>
+    <td class="partial" align="center">Partial</td>
   </tr>
   <tr id="2370">
     <td><a 
href="https://cplusplus.github.io/CWG/issues/2370.html";>2370</a></td>
@@ -16468,7 +16468,11 @@ <h2 id="cxxdr">C++ defect report implementation 
status</h2>
     <td><a 
href="https://cplusplus.github.io/CWG/issues/2770.html";>2770</a></td>
     <td>open</td>
     <td>Trailing <I>requires-clause</I> can refer to function parameters 
before they are substituted into</td>
-    <td align="center">Not resolved</td>
+    <td align="center">
+      <details>
+        <summary>Not resolved</summary>
+        Clang 20 implements 2023-07-14 resolution
+      </details></td>
   </tr>
   <tr id="2771">
     <td><a 
href="https://cplusplus.github.io/CWG/issues/2771.html";>2771</a></td>

>From df7500578957061d4f05d9d71fec07ff5e75e354 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Fri, 10 Jan 2025 13:32:11 +0800
Subject: [PATCH 02/10] Prevent dependent code generation when substituting
 constraints

---
 clang/include/clang/Sema/Sema.h               |  9 ++++++
 clang/lib/Sema/Sema.cpp                       |  3 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 20 +++++++------
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 10 +++++--
 clang/test/CodeGenCXX/ms-mangle-requires.cpp  | 29 +++++++++++++++++++
 5 files changed, 59 insertions(+), 12 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/ms-mangle-requires.cpp

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3b1f4d3234ea9..6419410ec181e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13424,6 +13424,10 @@ class Sema final : public SemaBase {
   // FIXME: Should we have a similar limit for other forms of synthesis?
   unsigned NonInstantiationEntries;
 
+  /// The number of \p CodeSynthesisContexts that are not constraint
+  /// substitution.
+  unsigned NonConstraintSubstitutionEntries;
+
   /// The depth of the context stack at the point when the most recent
   /// error or warning was produced.
   ///
@@ -13752,6 +13756,11 @@ class Sema final : public SemaBase {
     return CodeSynthesisContexts.size() > NonInstantiationEntries;
   }
 
+  /// Determine whether we are currently performing constraint substitution.
+  bool inConstraintSubstitution() const {
+    return CodeSynthesisContexts.size() > NonConstraintSubstitutionEntries;
+  }
+
   using EntityPrinter = llvm::function_ref<void(llvm::raw_ostream &)>;
 
   /// \brief create a Requirement::SubstitutionDiagnostic with only a
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index d2da9cd1201c2..7ee839676c9d9 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -266,7 +266,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer 
&consumer,
       TyposCorrected(0), IsBuildingRecoveryCallExpr(false), NumSFINAEErrors(0),
       AccessCheckingSFINAE(false), CurrentInstantiationScope(nullptr),
       InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
-      ArgPackSubstIndex(std::nullopt), SatisfactionCache(Context) {
+      NonConstraintSubstitutionEntries(0), ArgPackSubstIndex(std::nullopt),
+      SatisfactionCache(Context) {
   assert(pp.TUKind == TUKind);
   TUScope = nullptr;
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 49b1ac32692ef..df2b07b402702 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -833,6 +833,9 @@ void Sema::pushCodeSynthesisContext(CodeSynthesisContext 
Ctx) {
   if (!Ctx.isInstantiationRecord())
     ++NonInstantiationEntries;
 
+  if (Ctx.Kind != CodeSynthesisContext::ConstraintSubstitution)
+    ++NonConstraintSubstitutionEntries;
+
   // Check to see if we're low on stack space. We can't do anything about this
   // from here, but we can at least warn the user.
   StackHandler.warnOnStackNearlyExhausted(Ctx.PointOfInstantiation);
@@ -845,6 +848,11 @@ void Sema::popCodeSynthesisContext() {
     --NonInstantiationEntries;
   }
 
+  if (Active.Kind != CodeSynthesisContext::ConstraintSubstitution) {
+    assert(NonConstraintSubstitutionEntries > 0);
+    --NonConstraintSubstitutionEntries;
+  }
+
   InNonInstantiationSFINAEContext = 
Active.SavedInNonInstantiationSFINAEContext;
 
   // Name lookup no longer looks in this template's defining module.
@@ -1388,13 +1396,6 @@ namespace {
     bool BailOutOnIncomplete;
 
   private:
-    bool isSubstitutingConstraints() const {
-      return llvm::any_of(SemaRef.CodeSynthesisContexts, [](auto &Context) {
-        return Context.Kind ==
-               Sema::CodeSynthesisContext::ConstraintSubstitution;
-      });
-    }
-
     // CWG2770: Function parameters should be instantiated when they are
     // needed by a satisfaction check of an atomic constraint or
     // (recursively) by another function parameter.
@@ -1456,7 +1457,8 @@ namespace {
                                  ArrayRef<UnexpandedParameterPack> Unexpanded,
                                  bool &ShouldExpand, bool &RetainExpansion,
                                  UnsignedOrNone &NumExpansions) {
-      if (SemaRef.CurrentInstantiationScope && isSubstitutingConstraints()) {
+      if (SemaRef.CurrentInstantiationScope &&
+          SemaRef.inConstraintSubstitution()) {
         for (UnexpandedParameterPack ParmPack : Unexpanded) {
           NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
           if (!isa_and_present<ParmVarDecl>(VD))
@@ -1953,7 +1955,7 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation 
Loc, Decl *D) {
   }
 
   if (SemaRef.CurrentInstantiationScope) {
-    if (isSubstitutingConstraints() && isa<ParmVarDecl>(D) &&
+    if (SemaRef.inConstraintSubstitution() && isa<ParmVarDecl>(D) &&
         maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(D)))
       return nullptr;
   }
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 76c055d28f091..9d6cfb5860049 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5833,8 +5833,14 @@ void Sema::InstantiateFunctionDefinition(SourceLocation 
PointOfInstantiation,
     savedContext.pop();
   }
 
-  DeclGroupRef DG(Function);
-  Consumer.HandleTopLevelDecl(DG);
+  // With CWG2369, we substitute constraints before instantiating the 
associated
+  // function template. This helps prevent potential code generation for
+  // dependent types, particularly under the MS ABI.
+  if (!inConstraintSubstitution() ||
+      !getLambdaAwareParentOfDeclContext(Function)->isDependentContext()) {
+    DeclGroupRef DG(Function);
+    Consumer.HandleTopLevelDecl(DG);
+  }
 
   // This class may have local implicit instantiations that need to be
   // instantiation within this scope.
diff --git a/clang/test/CodeGenCXX/ms-mangle-requires.cpp 
b/clang/test/CodeGenCXX/ms-mangle-requires.cpp
new file mode 100644
index 0000000000000..1d873a3914fad
--- /dev/null
+++ b/clang/test/CodeGenCXX/ms-mangle-requires.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-windows-msvc 
-Wno-defaulted-function-deleted -fms-compatibility -fms-extensions -emit-llvm 
%s -o - | FileCheck %s
+
+namespace CWG2369_Regression {
+
+template <class, class Up>
+using compare_three_way_result_t = Up::type;
+
+struct sfinae_assign_base {};
+
+template <class Tp>
+concept is_derived_from_optional =
+    requires(Tp param) { []<class Up>(Up) {}(param); };
+
+template <class Tp, class Up>
+  requires(is_derived_from_optional<Up>)
+compare_three_way_result_t<Tp, Up> operator<=>(Tp, Up);
+
+struct RuntimeModeArgs {
+  auto operator<=>(const RuntimeModeArgs &) const = default;
+  sfinae_assign_base needs_admin;
+};
+
+RuntimeModeArgs foo() {
+  return {};
+}
+
+// CHECK: ?foo@CWG2369_Regression@@YA?AURuntimeModeArgs@1@XZ
+
+}

>From ea9dd82737e47d78d751d1ff75891c42db9ed615 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Sat, 11 Jan 2025 16:14:24 +0800
Subject: [PATCH 03/10] Add a regression test

---
 clang/test/CodeGenCXX/ms-mangle-requires.cpp | 34 ++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

diff --git a/clang/test/CodeGenCXX/ms-mangle-requires.cpp 
b/clang/test/CodeGenCXX/ms-mangle-requires.cpp
index 1d873a3914fad..1160d81d726d1 100644
--- a/clang/test/CodeGenCXX/ms-mangle-requires.cpp
+++ b/clang/test/CodeGenCXX/ms-mangle-requires.cpp
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -std=c++20 -triple=x86_64-windows-msvc 
-Wno-defaulted-function-deleted -fms-compatibility -fms-extensions -emit-llvm 
%s -o - | FileCheck %s
 
-namespace CWG2369_Regression {
+namespace CWG2369 {
+
+namespace Regression1 {
 
 template <class, class Up>
 using compare_three_way_result_t = Up::type;
@@ -24,6 +26,34 @@ RuntimeModeArgs foo() {
   return {};
 }
 
-// CHECK: ?foo@CWG2369_Regression@@YA?AURuntimeModeArgs@1@XZ
+// CHECK: ?foo@Regression1@CWG2369@@YA?AURuntimeModeArgs@12@XZ
+
+} // namespace Regression1
+
+namespace Regression2 {
+
+template <class _Tp>
+constexpr _Tp * __to_address(_Tp *) {
+  return nullptr;
+}
+
+template <class _Ip>
+concept contiguous_iterator = requires(_Ip __i) { __to_address(__i); };
+
+struct basic_string_view {
+  template <contiguous_iterator _It>
+  basic_string_view(_It, _It);
+};
+
+const char *str;
+void sv() { basic_string_view(str, str); }
+
+void m_fn2() {
+  const char __trans_tmp_1 = *__to_address(&__trans_tmp_1);
+}
+
+// CHECK: define {{.*}} 
@"??$__to_address@$$CBD@Regression2@CWG2369@@YAPEBDPEBD@Z"
+
+} // namespace Regression2
 
 }

>From 4943c262df8620eb3a36ba4e8848259b83916620 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Sat, 11 Jan 2025 20:59:25 +0800
Subject: [PATCH 04/10] Switch to checking EvaluationContext

---
 clang/lib/Sema/SemaTemplateDeduction.cpp       | 7 ++++---
 clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 7 +++++--
 clang/test/CodeGenCXX/ms-mangle-requires.cpp   | 2 +-
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp 
b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 4a58fdf014d43..42b9e3e9d7e80 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4829,8 +4829,8 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
                                           /*AdjustExceptionSpec*/false);
 
   // Unevaluated SFINAE context.
-  EnterExpressionEvaluationContext Unevaluated(
-      *this, Sema::ExpressionEvaluationContext::Unevaluated);
+  std::optional<EnterExpressionEvaluationContext> Unevaluated(
+      std::in_place, *this, Sema::ExpressionEvaluationContext::Unevaluated);
   SFINAETrap Trap(*this);
 
   Deduced.resize(TemplateParams->size());
@@ -4873,13 +4873,14 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
       DeduceReturnType(Specialization, Info.getLocation(), false))
     return TemplateDeductionResult::MiscellaneousDeductionFailure;
 
+  Unevaluated = std::nullopt;
   // [C++26][expr.const]/p17
   // An expression or conversion is immediate-escalating if it is not initially
   // in an immediate function context and it is [...]
   // a potentially-evaluated id-expression that denotes an immediate function.
   if (IsAddressOfFunction && getLangOpts().CPlusPlus20 &&
       Specialization->isImmediateEscalating() &&
-      parentEvaluationContext().isPotentiallyEvaluated() &&
+      currentEvaluationContext().isPotentiallyEvaluated() &&
       CheckIfFunctionSpecializationIsImmediate(Specialization,
                                                Info.getLocation()))
     return TemplateDeductionResult::MiscellaneousDeductionFailure;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 9d6cfb5860049..c090d0e6f6798 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5836,8 +5836,11 @@ void Sema::InstantiateFunctionDefinition(SourceLocation 
PointOfInstantiation,
   // With CWG2369, we substitute constraints before instantiating the 
associated
   // function template. This helps prevent potential code generation for
   // dependent types, particularly under the MS ABI.
-  if (!inConstraintSubstitution() ||
-      !getLambdaAwareParentOfDeclContext(Function)->isDependentContext()) {
+  bool InUnevaluatedOrImmediateContext =
+      llvm::any_of(ExprEvalContexts, [](auto &Context) {
+        return Context.isUnevaluated() || Context.isImmediateFunctionContext();
+      });
+  if (!InUnevaluatedOrImmediateContext) {
     DeclGroupRef DG(Function);
     Consumer.HandleTopLevelDecl(DG);
   }
diff --git a/clang/test/CodeGenCXX/ms-mangle-requires.cpp 
b/clang/test/CodeGenCXX/ms-mangle-requires.cpp
index 1160d81d726d1..15318ecffaf01 100644
--- a/clang/test/CodeGenCXX/ms-mangle-requires.cpp
+++ b/clang/test/CodeGenCXX/ms-mangle-requires.cpp
@@ -14,7 +14,7 @@ concept is_derived_from_optional =
     requires(Tp param) { []<class Up>(Up) {}(param); };
 
 template <class Tp, class Up>
-  requires(is_derived_from_optional<Up>)
+  requires(is_derived_from_optional<Up> && []<class W>(W) { return true; 
}(Up()))
 compare_three_way_result_t<Tp, Up> operator<=>(Tp, Up);
 
 struct RuntimeModeArgs {

>From ec143c18731e1bc885c452443adae19fd81facc4 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Sat, 11 Jan 2025 22:14:38 +0800
Subject: [PATCH 05/10] fixup! Switch to checking EvaluationContext

---
 clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c090d0e6f6798..4a71bc3ac85bb 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5836,11 +5836,16 @@ void Sema::InstantiateFunctionDefinition(SourceLocation 
PointOfInstantiation,
   // With CWG2369, we substitute constraints before instantiating the 
associated
   // function template. This helps prevent potential code generation for
   // dependent types, particularly under the MS ABI.
-  bool InUnevaluatedOrImmediateContext =
-      llvm::any_of(ExprEvalContexts, [](auto &Context) {
-        return Context.isUnevaluated() || Context.isImmediateFunctionContext();
-      });
-  if (!InUnevaluatedOrImmediateContext) {
+  bool ShouldSkipCG = [&] {
+    auto *RD = dyn_cast<CXXRecordDecl>(Function->getParent());
+    if (!RD || !RD->isLambda())
+      return false;
+
+    return llvm::any_of(ExprEvalContexts, [](auto &Context) {
+      return Context.isUnevaluated() || Context.isImmediateFunctionContext();
+    });
+  }();
+  if (ShouldSkipCG) {
     DeclGroupRef DG(Function);
     Consumer.HandleTopLevelDecl(DG);
   }

>From 5134b1b5b73989d4db31936cbef077b274c16c8d Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Sat, 11 Jan 2025 22:27:45 +0800
Subject: [PATCH 06/10] negative, sorry

---
 clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 4a71bc3ac85bb..5b6a9156f88a8 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5845,7 +5845,7 @@ void Sema::InstantiateFunctionDefinition(SourceLocation 
PointOfInstantiation,
       return Context.isUnevaluated() || Context.isImmediateFunctionContext();
     });
   }();
-  if (ShouldSkipCG) {
+  if (!ShouldSkipCG) {
     DeclGroupRef DG(Function);
     Consumer.HandleTopLevelDecl(DG);
   }

>From 7270bacd47529dcde3f23db27b3fd2acdcee5f12 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Sat, 11 Jan 2025 23:10:38 +0800
Subject: [PATCH 07/10] Remove unused header

---
 clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 5b6a9156f88a8..732b350f2a16c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -12,7 +12,6 @@
 #include "TreeTransform.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
-#include "clang/AST/ASTLambda.h"
 #include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DependentDiagnostic.h"

>From 671976d2423fec7b852d2188ae30699a5360e9b5 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Wed, 23 Apr 2025 16:40:21 +0800
Subject: [PATCH 08/10] Use UnsignedOrNone

---
 clang/lib/Sema/SemaConcept.cpp                              | 2 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp                  | 6 +++---
 .../test/SemaCXX/overload-resolution-deferred-templates.cpp | 2 +-
 clang/test/SemaTemplate/deduction-guide.cpp                 | 4 ++--
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 520fe4aac97be..4791d324ad30d 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1064,7 +1064,7 @@ static bool CheckFunctionConstraintsWithoutInstantiation(
     Sema &SemaRef, SourceLocation PointOfInstantiation,
     FunctionTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
     ConstraintSatisfaction &Satisfaction) {
-  SmallVector<const Expr *, 3> TemplateAC;
+  SmallVector<AssociatedConstraint, 3> TemplateAC;
   Template->getAssociatedConstraints(TemplateAC);
   if (TemplateAC.empty()) {
     Satisfaction.IsSatisfied = true;
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index df2b07b402702..c6e6c9e01b8b5 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1989,9 +1989,9 @@ bool 
TemplateInstantiator::maybeInstantiateFunctionParameterToScope(
 
   bool ShouldExpand = false;
   bool RetainExpansion = false;
-  std::optional<unsigned> OrigNumExpansions =
+  UnsignedOrNone OrigNumExpansions =
       ExpansionTL.getTypePtr()->getNumExpansions();
-  std::optional<unsigned> NumExpansions = OrigNumExpansions;
+  UnsignedOrNone NumExpansions = OrigNumExpansions;
   if (TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(),
                               Pattern.getSourceRange(), Unexpanded,
                               ShouldExpand, RetainExpansion, NumExpansions))
@@ -2001,7 +2001,7 @@ bool 
TemplateInstantiator::maybeInstantiateFunctionParameterToScope(
          "Shouldn't preserve pack expansion when evaluating constraints");
   ExpandingFunctionParameterPack(OldParm);
   for (unsigned I = 0; I != *NumExpansions; ++I) {
-    Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
+    Sema::ArgPackSubstIndexRAII SubstIndex(getSema(), I);
     if (!TransformFunctionTypeParam(OldParm, /*indexAdjustment=*/0,
                                     /*NumExpansions=*/OrigNumExpansions,
                                     /*ExpectParameterPack=*/false))
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp 
b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
index d68a942f64050..9736550f38e30 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -173,7 +173,7 @@ void f(int);
 void g(int n) { f(n); } // OK
 void h(short n) { f(n); }
 // expected-error@#GH62096-err {{static assertion failed due to requirement 
'sizeof(short) == 0'}} \
-// expected-note@-1{{in instantiation of function template specialization}} \
+// expected-note@-1{{while substituting deduced template arguments}} \
 // expected-note@-1{{while checking constraint satisfaction for template}}
 // expected-note@#GH62096-note1{{in instantiation}}
 // expected-note@#GH62096-note1{{while substituting template arguments into 
constraint expression here}}
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp 
b/clang/test/SemaTemplate/deduction-guide.cpp
index dabd0cf12f77e..fac6d0496f15f 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++2a -verify -ast-dump -ast-dump-decl-types 
-ast-dump-filter "deduction guide" %s | FileCheck %s --strict-whitespace 
-dump-input=always
+// RUN: %clang_cc1 -std=c++2a -verify -ast-dump -ast-dump-decl-types 
-ast-dump-filter "deduction guide" %s | FileCheck %s --strict-whitespace
 
 template<auto ...> struct X {};
 template<template<typename X, X> typename> struct Y {};
@@ -234,7 +234,7 @@ F s(0);
 // CHECK: | `-CXXBoolLiteralExpr {{.*}} 'bool' false
 // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for F> 
'auto (U) -> F<value-parameter-0-0>'
 // CHECK: | `-ParmVarDecl {{.*}} 'U'
-// CHECK: `-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for F> 
'auto (int) -> F<>'
+// CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for F> 
'auto (int) -> F<>'
 // CHECK:   |-TemplateArgument integral ''x''
 // CHECK:   |-TemplateArgument type 'int'
 // CHECK:   | `-BuiltinType {{.*}} 'int'

>From 0a517fd8743dcdb46f1d9f147268e7c881aeeafa Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Fri, 24 Jan 2025 13:52:37 +0800
Subject: [PATCH 09/10] Implement GCC's CWG 2369 heuristic

---
 clang/include/clang/Sema/Sema.h               |   9 +-
 clang/lib/Sema/SemaOverload.cpp               |  62 ++++++-
 clang/lib/Sema/SemaTemplateDeduction.cpp      |  13 +-
 .../SemaTemplate/concepts-recursive-inst.cpp  | 169 ++++++++++++++++++
 4 files changed, 240 insertions(+), 13 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 6419410ec181e..5611bad6fb2d6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10452,7 +10452,8 @@ class Sema final : public SemaBase {
       FunctionTemplateDecl *FunctionTemplate, ArrayRef<QualType> ParamTypes,
       ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
       ConversionSequenceList &Conversions, bool SuppressUserConversions,
-      CXXRecordDecl *ActingContext = nullptr, QualType ObjectType = QualType(),
+      bool NonInstOnly, CXXRecordDecl *ActingContext = nullptr,
+      QualType ObjectType = QualType(),
       Expr::Classification ObjectClassification = {},
       OverloadCandidateParamOrder PO = {});
 
@@ -12497,7 +12498,9 @@ class Sema final : public SemaBase {
       sema::TemplateDeductionInfo &Info,
       SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs,
       bool PartialOverloading, bool PartialOrdering,
-      llvm::function_ref<bool()> CheckNonDependent = [] { return false; });
+      llvm::function_ref<bool(bool)> CheckNonDependent = [](bool) {
+        return false;
+      });
 
   /// Perform template argument deduction from a function call
   /// (C++ [temp.deduct.call]).
@@ -12532,7 +12535,7 @@ class Sema final : public SemaBase {
       bool PartialOverloading, bool AggregateDeductionCandidate,
       bool PartialOrdering, QualType ObjectType,
       Expr::Classification ObjectClassification,
-      llvm::function_ref<bool(ArrayRef<QualType>)> CheckNonDependent);
+      llvm::function_ref<bool(ArrayRef<QualType>, bool)> CheckNonDependent);
 
   /// Deduce template arguments when taking the address of a function
   /// template (C++ [temp.deduct.funcaddr]) or matching a specialization to
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 5b224b6c08fef..eaaff56ca336c 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -7829,10 +7829,10 @@ static void AddMethodTemplateCandidateImmediately(
           MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info,
           PartialOverloading, /*AggregateDeductionCandidate=*/false,
           /*PartialOrdering=*/false, ObjectType, ObjectClassification,
-          [&](ArrayRef<QualType> ParamTypes) {
+          [&](ArrayRef<QualType> ParamTypes, bool NonInstOnly) {
             return S.CheckNonDependentConversions(
                 MethodTmpl, ParamTypes, Args, CandidateSet, Conversions,
-                SuppressUserConversions, ActingContext, ObjectType,
+                SuppressUserConversions, NonInstOnly, ActingContext, 
ObjectType,
                 ObjectClassification, PO);
           });
       Result != TemplateDeductionResult::Success) {
@@ -7943,10 +7943,11 @@ static void AddTemplateOverloadCandidateImmediately(
           /*PartialOrdering=*/false,
           /*ObjectType=*/QualType(),
           /*ObjectClassification=*/Expr::Classification(),
-          [&](ArrayRef<QualType> ParamTypes) {
+          [&](ArrayRef<QualType> ParamTypes, bool NonInstOnly) {
             return S.CheckNonDependentConversions(
                 FunctionTemplate, ParamTypes, Args, CandidateSet, Conversions,
-                SuppressUserConversions, nullptr, QualType(), {}, PO);
+                SuppressUserConversions, NonInstOnly, nullptr, QualType(), {},
+                PO);
           });
       Result != TemplateDeductionResult::Success) {
     OverloadCandidate &Candidate =
@@ -8021,7 +8022,7 @@ bool Sema::CheckNonDependentConversions(
     FunctionTemplateDecl *FunctionTemplate, ArrayRef<QualType> ParamTypes,
     ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
     ConversionSequenceList &Conversions, bool SuppressUserConversions,
-    CXXRecordDecl *ActingContext, QualType ObjectType,
+    bool NonInstOnly, CXXRecordDecl *ActingContext, QualType ObjectType,
     Expr::Classification ObjectClassification, OverloadCandidateParamOrder PO) 
{
   // FIXME: The cases in which we allow explicit conversions for constructor
   // arguments never consider calling a constructor template. It's not clear
@@ -8058,6 +8059,54 @@ bool Sema::CheckNonDependentConversions(
     }
   }
 
+  // A heuristic & speculative workaround for bug
+  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99599 that manifests after
+  // CWG2369.
+  auto ConversionMightInduceInstantiation = [&](QualType ParmType,
+                                                QualType ArgType) {
+    ParmType = ParmType.getNonReferenceType();
+    ArgType = ArgType.getNonReferenceType();
+    bool PointerConv = ParmType->isPointerType() && ArgType->isPointerType();
+    if (PointerConv) {
+      ParmType = ParmType->getPointeeType();
+      ArgType = ArgType->getPointeeType();
+    }
+
+    auto IsInstantiation = [&](QualType T) {
+      if (auto *RT = T->getAs<RecordType>()) {
+        if (auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl())) {
+          if (auto *ClassTemplateSpec =
+                  dyn_cast<ClassTemplateSpecializationDecl>(RD))
+            return ClassTemplateSpec->getSpecializationKind() == 
TSK_Undeclared;
+          if (RD->getInstantiatedFromMemberClass())
+            return RD->getMemberSpecializationInfo()
+                       ->getTemplateSpecializationKind() !=
+                   TemplateSpecializationKind::TSK_ExplicitSpecialization;
+        }
+      }
+      return false;
+    };
+    if (IsInstantiation(ParmType) || IsInstantiation(ArgType))
+      return true;
+
+    if (PointerConv || CompareReferenceRelationship(SourceLocation(), ParmType,
+                                                    ArgType) == Ref_Related)
+      return false;
+
+    if (auto *RT = ParmType->getAs<RecordType>())
+      if (auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
+          RD && RD->hasDefinition() && !RD->isAggregate())
+        return false;
+
+    if (auto *RT = ArgType->getAs<RecordType>())
+      if (auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
+          RD && RD->hasDefinition() &&
+          !RD->getVisibleConversionFunctions().empty())
+        return true;
+
+    return false;
+  };
+
   unsigned Offset =
       Method && Method->hasCXXExplicitFunctionObjectParameter() ? 1 : 0;
 
@@ -8078,6 +8127,9 @@ bool Sema::CheckNonDependentConversions(
         // For members, 'this' got ConvIdx = 0 previously.
         ConvIdx = ThisConversions + I;
       }
+      if (NonInstOnly &&
+          ConversionMightInduceInstantiation(ParamType, Args[I]->getType()))
+        continue;
       Conversions[ConvIdx]
         = TryCopyInitialization(*this, Args[I], ParamType,
                                 SuppressUserConversions,
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp 
b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 42b9e3e9d7e80..d3b3ef8cbef5f 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3962,7 +3962,7 @@ TemplateDeductionResult 
Sema::FinishTemplateArgumentDeduction(
     TemplateDeductionInfo &Info,
     SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs,
     bool PartialOverloading, bool PartialOrdering,
-    llvm::function_ref<bool()> CheckNonDependent) {
+    llvm::function_ref<bool(bool)> CheckNonDependent) {
   // Unevaluated SFINAE context.
   EnterExpressionEvaluationContext Unevaluated(
       *this, Sema::ExpressionEvaluationContext::Unevaluated);
@@ -4005,6 +4005,9 @@ TemplateDeductionResult 
Sema::FinishTemplateArgumentDeduction(
     Owner = FunctionTemplate->getLexicalDeclContext();
   FunctionDecl *FD = FunctionTemplate->getTemplatedDecl();
 
+  if (CheckNonDependent(/*NonInstOnly=*/true))
+    return TemplateDeductionResult::NonDependentConversionFailure;
+
   // C++20 [temp.deduct.general]p5: [CWG2369]
   //   If the function template has associated constraints, those constraints
   //   are checked for satisfaction. If the constraints are not satisfied, type
@@ -4035,7 +4038,7 @@ TemplateDeductionResult 
Sema::FinishTemplateArgumentDeduction(
   //   P with a type that was non-dependent before substitution of any
   //   explicitly-specified template arguments, if the corresponding argument
   //   A cannot be implicitly converted to P, deduction fails.
-  if (CheckNonDependent())
+  if (CheckNonDependent(/*NonInstOnly=*/false))
     return TemplateDeductionResult::NonDependentConversionFailure;
 
   MultiLevelTemplateArgumentList SubstArgs(
@@ -4532,7 +4535,7 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
     bool PartialOverloading, bool AggregateDeductionCandidate,
     bool PartialOrdering, QualType ObjectType,
     Expr::Classification ObjectClassification,
-    llvm::function_ref<bool(ArrayRef<QualType>)> CheckNonDependent) {
+    llvm::function_ref<bool(ArrayRef<QualType>, bool)> CheckNonDependent) {
   if (FunctionTemplate->isInvalidDecl())
     return TemplateDeductionResult::Invalid;
 
@@ -4746,9 +4749,9 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
     Result = FinishTemplateArgumentDeduction(
         FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, 
Info,
         &OriginalCallArgs, PartialOverloading, PartialOrdering,
-        [&, CallingCtx]() {
+        [&, CallingCtx](bool NonInstOnly) {
           ContextRAII SavedContext(*this, CallingCtx);
-          return CheckNonDependent(ParamTypesForArgChecking);
+          return CheckNonDependent(ParamTypesForArgChecking, NonInstOnly);
         });
   });
   return Result;
diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp 
b/clang/test/SemaTemplate/concepts-recursive-inst.cpp
index 30a410cef91ee..f251ba6dc5b09 100644
--- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp
+++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp
@@ -143,3 +143,172 @@ namespace GH60323 {
             Size().sizeparens(i);
   }
 }
+
+namespace CWG2369_Regressions {
+
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109397
+namespace GCC_103997 {
+
+template<typename _type, typename _stream>
+concept streamable = requires(_stream &s, _type &&v) {
+  s << static_cast<_type &&>(v);
+};
+
+struct type_a {
+  template<typename _arg>
+  type_a &operator<<(_arg &&) {
+    // std::clog << "type_a" << std::endl;
+    return *this;
+  }
+};
+
+struct type_b {
+  type_b &operator<<(type_a const &) {
+    // std::clog << "type_b" << std::endl;
+    return *this;
+  }
+};
+
+struct type_c {
+  type_b b;
+  template<typename _arg>
+  requires streamable<_arg, type_b>
+  friend type_c &operator<<(type_c &c, _arg &&a) {
+    // std::clog << "type_c" << std::endl;
+    c.b << static_cast<_arg &&>(a);
+    return c;
+  }
+};
+
+void foo() {
+  type_a a;
+  type_c c;
+  a << c; // "type_a\n" (gcc gives error here)
+  c << a; // "type_c\ntype_b\n"
+}
+
+}
+
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108393
+namespace GCC_108393 {
+
+template<class>
+struct iterator_traits
+{};
+
+template<class T>
+  requires requires(T __t, T __u) { __t == __u; }
+struct iterator_traits<T>
+{};
+
+template<class T>
+concept C = requires { typename iterator_traits<T>::A; };
+
+struct unreachable_sentinel_t
+{
+  template<C _Iter>
+  friend constexpr bool operator==(unreachable_sentinel_t, const _Iter&) 
noexcept;
+};
+
+template<class T>
+struct S
+{};
+
+static_assert(!C<S<unreachable_sentinel_t>>);
+
+}
+
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107429
+namespace GCC_107429 {
+
+struct tag_foo { } inline constexpr foo;
+struct tag_bar { } inline constexpr bar;
+
+template<typename... T>
+auto f(tag_foo, T... x)
+{
+  return (x + ...);
+}
+
+template<typename... T>
+concept fooable = requires (T... x) { f(foo, x...); };
+
+template<typename... T> requires (fooable<T...>)
+auto f(tag_bar, T... x)
+{
+  return f(foo, x...);
+}
+
+auto test()
+{
+  return f(bar, 1, 2, 3);
+}
+
+}
+
+namespace GCC_99599 {
+
+struct foo_tag {};
+struct bar_tag {};
+
+template <class T>
+concept fooable = requires(T it) {
+  invoke_tag(foo_tag{}, it); // <-- here
+};
+
+template <class T> auto invoke_tag(foo_tag, T in) { return in; }
+
+template <fooable T> auto invoke_tag(bar_tag, T it) { return it; }
+
+int main() {
+  // Neither line below compiles in GCC 11, independently of the other
+  return invoke_tag(foo_tag{}, 2) + invoke_tag(bar_tag{}, 2);
+}
+
+}
+
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99599#c22
+namespace GCC_99599_2 {
+
+template<typename T> class indirect {
+public:
+  template<typename U> requires
+    requires (const T& t, const U& u) { t == u; }
+  friend constexpr bool operator==(const indirect&, const U&) { return false; }
+
+private:
+  T* _M_ptr{};
+};
+
+indirect<int> i;
+bool b = i == 1;
+
+}
+
+namespace FAILED_GCC_110160 {
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110160
+// Current heuristic FAILED; GCC trunk also failed
+// https://godbolt.org/z/r3Pz9Tehz
+#if 0
+#include <sstream>
+#include <string>
+
+template <class T>
+concept StreamCanReceiveString = requires(T& t, std::string s) {
+    { t << s };
+};
+
+struct NotAStream {};
+struct UnrelatedType {};
+
+template <StreamCanReceiveString S>
+S& operator<<(S& s, UnrelatedType) {
+    return s;
+}
+
+static_assert(!StreamCanReceiveString<NotAStream>);
+
+static_assert(StreamCanReceiveString<std::stringstream>);
+#endif
+}
+}

>From b32afd2383dccd4cb501f4dc51e17e8bdb9e7ebe Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Fri, 25 Apr 2025 19:59:01 +0800
Subject: [PATCH 10/10] Check the non-user-defined-non-dependent conversions
 earlier

---
 clang/include/clang/Sema/Sema.h |  2 +-
 clang/lib/Sema/SemaOverload.cpp | 66 ++++++++++++++++-----------------
 2 files changed, 32 insertions(+), 36 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5611bad6fb2d6..baccc6949680c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10452,7 +10452,7 @@ class Sema final : public SemaBase {
       FunctionTemplateDecl *FunctionTemplate, ArrayRef<QualType> ParamTypes,
       ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
       ConversionSequenceList &Conversions, bool SuppressUserConversions,
-      bool NonInstOnly, CXXRecordDecl *ActingContext = nullptr,
+      bool SkipUserDefinedConversions, CXXRecordDecl *ActingContext = nullptr,
       QualType ObjectType = QualType(),
       Expr::Classification ObjectClassification = {},
       OverloadCandidateParamOrder PO = {});
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index eaaff56ca336c..405aefb7e9105 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -8022,8 +8022,9 @@ bool Sema::CheckNonDependentConversions(
     FunctionTemplateDecl *FunctionTemplate, ArrayRef<QualType> ParamTypes,
     ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
     ConversionSequenceList &Conversions, bool SuppressUserConversions,
-    bool NonInstOnly, CXXRecordDecl *ActingContext, QualType ObjectType,
-    Expr::Classification ObjectClassification, OverloadCandidateParamOrder PO) 
{
+    bool SkipUserDefinedConversions, CXXRecordDecl *ActingContext,
+    QualType ObjectType, Expr::Classification ObjectClassification,
+    OverloadCandidateParamOrder PO) {
   // FIXME: The cases in which we allow explicit conversions for constructor
   // arguments never consider calling a constructor template. It's not clear
   // that is correct.
@@ -8034,8 +8035,9 @@ bool Sema::CheckNonDependentConversions(
   bool HasThisConversion = Method && !isa<CXXConstructorDecl>(Method);
   unsigned ThisConversions = HasThisConversion ? 1 : 0;
 
-  Conversions =
-      CandidateSet.allocateConversionSequences(ThisConversions + Args.size());
+  Conversions = Conversions.empty() ? CandidateSet.allocateConversionSequences(
+                                          ThisConversions + Args.size())
+                                    : Conversions;
 
   // Overload resolution is always an unevaluated context.
   EnterExpressionEvaluationContext Unevaluated(
@@ -8059,11 +8061,11 @@ bool Sema::CheckNonDependentConversions(
     }
   }
 
-  // A heuristic & speculative workaround for bug
-  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99599 that manifests after
-  // CWG2369.
-  auto ConversionMightInduceInstantiation = [&](QualType ParmType,
-                                                QualType ArgType) {
+  // A speculative workaround for self-dependent constraint bugs that manifest
+  // after CWG2369.
+  // FIXME: Add references to the standard once P3606 is adopted.
+  auto MaybeInvolveUserDefinedConversion = [&](QualType ParmType,
+                                               QualType ArgType) {
     ParmType = ParmType.getNonReferenceType();
     ArgType = ArgType.getNonReferenceType();
     bool PointerConv = ParmType->isPointerType() && ArgType->isPointerType();
@@ -8072,37 +8074,29 @@ bool Sema::CheckNonDependentConversions(
       ArgType = ArgType->getPointeeType();
     }
 
-    auto IsInstantiation = [&](QualType T) {
-      if (auto *RT = T->getAs<RecordType>()) {
-        if (auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl())) {
-          if (auto *ClassTemplateSpec =
-                  dyn_cast<ClassTemplateSpecializationDecl>(RD))
-            return ClassTemplateSpec->getSpecializationKind() == 
TSK_Undeclared;
-          if (RD->getInstantiatedFromMemberClass())
-            return RD->getMemberSpecializationInfo()
-                       ->getTemplateSpecializationKind() !=
-                   TemplateSpecializationKind::TSK_ExplicitSpecialization;
-        }
-      }
-      return false;
-    };
-    if (IsInstantiation(ParmType) || IsInstantiation(ArgType))
-      return true;
-
-    if (PointerConv || CompareReferenceRelationship(SourceLocation(), ParmType,
-                                                    ArgType) == Ref_Related)
-      return false;
-
     if (auto *RT = ParmType->getAs<RecordType>())
       if (auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
-          RD && RD->hasDefinition() && !RD->isAggregate())
-        return false;
+          RD && RD->hasDefinition()) {
+        if (llvm::any_of(LookupConstructors(RD), [](NamedDecl *ND) {
+              auto Info = getConstructorInfo(ND);
+              if (!Info)
+                return false;
+              CXXConstructorDecl *Ctor = Info.Constructor;
+              /// isConvertingConstructor takes copy/move constructors into
+              /// account!
+              return !Ctor->isCopyOrMoveConstructor() &&
+                     Ctor->isConvertingConstructor(
+                         /*AllowExplicit=*/true);
+            }))
+          return true;
+      }
 
     if (auto *RT = ArgType->getAs<RecordType>())
       if (auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
           RD && RD->hasDefinition() &&
-          !RD->getVisibleConversionFunctions().empty())
+          !RD->getVisibleConversionFunctions().empty()) {
         return true;
+      }
 
     return false;
   };
@@ -8127,8 +8121,10 @@ bool Sema::CheckNonDependentConversions(
         // For members, 'this' got ConvIdx = 0 previously.
         ConvIdx = ThisConversions + I;
       }
-      if (NonInstOnly &&
-          ConversionMightInduceInstantiation(ParamType, Args[I]->getType()))
+      if (Conversions[ConvIdx].isInitialized())
+        continue;
+      if (SkipUserDefinedConversions &&
+          MaybeInvolveUserDefinedConversion(ParamType, Args[I]->getType()))
         continue;
       Conversions[ConvIdx]
         = TryCopyInitialization(*this, Args[I], ParamType,

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to