[clang] 445028c - [Clang] Refactor instantiation of declarations within concepts (#221707)

via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 11 09:52:41 PDT 2026


Author: Younan Zhang
Date: 2026-09-12T00:52:36+08:00
New Revision: 445028c28190b9c883c08cb2ef69e002b65e5af9

URL: https://github.com/llvm/llvm-project/commit/445028c28190b9c883c08cb2ef69e002b65e5af9
DIFF: https://github.com/llvm/llvm-project/commit/445028c28190b9c883c08cb2ef69e002b65e5af9.diff

LOG: [Clang] Refactor instantiation of declarations within concepts (#221707)

After implementation of CWG2369, declarations are instantiated on demand
when evaluating a concept.

This was hinged on TemplateInstantiator, where we intercepted most calls
that need an instantiated declaration. This is almost identity to
SetupConstraintScope, which tries to re-instantiate any declarations to
the current scope. This patch removes SetupConstraintScope because we
can instantiate anything on demand.

The concept normalization patch brings us more troubles when we have to
deal with sugars: declarations instantiated outside of the scope (e.g. a
typedef declaration) are never added/instantiated during instantiation
of concepts: this patch makes that function also look up outer scopes
for those declarations. Note that we couldn't simply make the scope of
concepts 'transparent', since we don't want clashes of instantiated
function parameters.

Also also this added a call to instantiateMissingDeclsToScopeForConcepts
for C++26 fold expressions, before checking any pack sizes.

Fixes https://github.com/llvm/llvm-project/issues/198052
Fixes https://github.com/llvm/llvm-project/issues/209632

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.md
    clang/include/clang/Sema/Sema.h
    clang/include/clang/Sema/Template.h
    clang/lib/Sema/SemaConcept.cpp
    clang/lib/Sema/SemaTemplateInstantiate.cpp
    clang/test/SemaCXX/cxx2c-fold-exprs.cpp
    clang/test/SemaTemplate/concepts-lambda.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 6a2201012693e..ca29a33a2abba 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -588,6 +588,9 @@ features cannot lower the translation-unit ABI level;
 - Fixed a crash when module directive export module foo not following a
   semicolon and there are no rest pp-tokens in current module file. (#GH187771)
 
+- Fixed concept evaluation bugs where some declarations were not added to
+  the current instantiation scope. (#GH198052), (#GH209632)
+
 - Fixed a crash when a lambda parameter pack was given a default argument that
   is a pack expansion referencing an enclosing function's parameter pack (e.g.
   `[](Types... = args...) {}`). Clang now diagnoses the illegal default

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4ff4c669a6b70..fc8da0ed56005 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -15129,11 +15129,17 @@ class Sema final : public SemaBase {
       const NamedDecl *D1, ArrayRef<AssociatedConstraint> AC1,
       const NamedDecl *D2, ArrayRef<AssociatedConstraint> AC2);
 
+private:
+  friend class ConstraintSatisfactionChecker;
+  friend class SubstituteParameterMappings;
+
+  UnsignedOrNone EvaluateFoldExpandedConstraintSize(
+      const Expr *Pattern, const MultiLevelTemplateArgumentList &MLTAL);
+
   /// Cache the satisfaction of an atomic constraint.
   /// The key is based on the unsubstituted expression and the parameter
   /// mapping. This lets us not substituting the mapping more than once,
   /// which is (very!) expensive.
-  /// FIXME: this should be private.
   llvm::DenseMap<llvm::FoldingSetNodeID,
                  UnsubstitutedConstraintSatisfactionCacheResult>
       UnsubstitutedConstraintSatisfactionCache;
@@ -15145,7 +15151,6 @@ class Sema final : public SemaBase {
   llvm::DenseMap<llvm::FoldingSetNodeID, TemplateArgumentLoc>
       *CurrentCachedTemplateArgs = nullptr;
 
-private:
   /// Caches pairs of template-like decls whose associated constraints were
   /// checked for subsumption and whether or not the first's constraints did in
   /// fact subsume the second's.
@@ -15166,23 +15171,6 @@ class Sema final : public SemaBase {
   // The current stack of constraint satisfactions, so we can exit-early.
   llvm::SmallVector<SatisfactionStackEntryTy, 10> SatisfactionStack;
 
-  /// Used by SetupConstraintCheckingTemplateArgumentsAndScope to set up the
-  /// LocalInstantiationScope of the current non-lambda function. For lambdas,
-  /// use LambdaScopeForCallOperatorInstantiationRAII.
-  bool
-  SetupConstraintScope(FunctionDecl *FD,
-                       std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
-                       const MultiLevelTemplateArgumentList &MLTAL,
-                       LocalInstantiationScope &Scope);
-
-  /// Used during constraint checking, sets up the constraint template argument
-  /// lists, and calls SetupConstraintScope to set up the
-  /// LocalInstantiationScope to have the proper set of ParVarDecls configured.
-  std::optional<MultiLevelTemplateArgumentList>
-  SetupConstraintCheckingTemplateArgumentsAndScope(
-      FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
-      LocalInstantiationScope &Scope);
-
   ///@}
 
   //

diff  --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 50e950e56c6ca..818528744511d 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -540,6 +540,8 @@ enum class TemplateSubstitutionKind : char {
     llvm::PointerUnion<Decl *, DeclArgumentPack *> *
     getInstantiationOfIfExists(const Decl *D);
 
+    LocalInstantiationScope *getOuterScope() const { return Outer; }
+
     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 ee17f826dc32c..0122e920e5d89 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -549,7 +549,9 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
     }
   }
 };
+} // namespace
 
+namespace clang {
 class ConstraintSatisfactionChecker {
   Sema &S;
   const NamedDecl *Template;
@@ -652,10 +654,6 @@ class ConstraintSatisfactionChecker {
   EvaluateAtomicConstraint(const Expr *AtomicExpr,
                            const MultiLevelTemplateArgumentList &MLTAL);
 
-  UnsignedOrNone EvaluateFoldExpandedConstraintSize(
-      const FoldExpandedConstraint &FE,
-      const MultiLevelTemplateArgumentList &MLTAL);
-
   // XXX: It is SLOW! Use it very carefully.
   std::optional<MultiLevelTemplateArgumentList> SubstitutionInTemplateArguments(
       const NormalizedConstraintWithParamMapping &Constraint,
@@ -700,7 +698,7 @@ class ConstraintSatisfactionChecker {
                       const MultiLevelTemplateArgumentList &MLTAL);
 };
 
-} // namespace
+} // namespace clang
 
 ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint(
     const Expr *AtomicExpr, const MultiLevelTemplateArgumentList &MLTAL) {
@@ -948,31 +946,6 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
   return PMCache.cache(EvaluateSlow(Constraint, MLTAL));
 }
 
-UnsignedOrNone
-ConstraintSatisfactionChecker::EvaluateFoldExpandedConstraintSize(
-    const FoldExpandedConstraint &FE,
-    const MultiLevelTemplateArgumentList &MLTAL) {
-
-  Expr *Pattern = const_cast<Expr *>(FE.getPattern());
-
-  SmallVector<UnexpandedParameterPack, 2> Unexpanded;
-  S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
-  assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
-  bool Expand = true;
-  bool RetainExpansion = false;
-  UnsignedOrNone NumExpansions(std::nullopt);
-  if (S.CheckParameterPacksForExpansion(
-          Pattern->getExprLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL,
-          /*FailOnPackProducingTemplates=*/false, Expand, RetainExpansion,
-          NumExpansions, /*Diagnose=*/false) ||
-      !Expand || RetainExpansion)
-    return std::nullopt;
-
-  if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions)
-    return std::nullopt;
-  return NumExpansions;
-}
-
 ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
     const FoldExpandedConstraint &Constraint,
     const MultiLevelTemplateArgumentList &MLTAL) {
@@ -993,9 +966,15 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
     return ExprError();
   }
 
-  ExprResult Out;
-  UnsignedOrNone NumExpansions =
-      EvaluateFoldExpandedConstraintSize(Constraint, *SubstitutedArgs);
+  UnsignedOrNone NumExpansions(std::nullopt);
+  {
+    Sema::InstantiatingTemplate InstTemplate(
+        S, TemplateNameLoc,
+        Sema::InstantiatingTemplate::ConstraintSubstitution{},
+        const_cast<NamedDecl *>(Template), Constraint.getSourceRange());
+    NumExpansions = S.EvaluateFoldExpandedConstraintSize(
+        Constraint.getPattern(), *SubstitutedArgs);
+  }
   if (!NumExpansions)
     return ExprEmpty();
 
@@ -1004,6 +983,7 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
     return ExprEmpty();
   }
 
+  ExprResult Out;
   for (unsigned I = 0; I < *NumExpansions; I++) {
     Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
     Satisfaction.IsSatisfied = false;
@@ -1412,98 +1392,6 @@ SubstituteConceptsInConstraintExpression(Sema &S, const NamedDecl *D,
                                          MLTAL);
 }
 
-bool Sema::SetupConstraintScope(
-    FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
-    const MultiLevelTemplateArgumentList &MLTAL,
-    LocalInstantiationScope &Scope) {
-  assert(!isLambdaCallOperator(FD) &&
-         "Use LambdaScopeForCallOperatorInstantiationRAII to handle lambda "
-         "instantiations");
-  if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) {
-    FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
-    InstantiatingTemplate Inst(
-        *this, FD->getPointOfInstantiation(),
-        Sema::InstantiatingTemplate::ConstraintsCheck{}, PrimaryTemplate,
-        TemplateArgs ? *TemplateArgs : ArrayRef<TemplateArgument>{},
-        SourceRange());
-    if (Inst.isInvalid())
-      return true;
-
-    // addInstantiatedParametersToScope creates a map of 'uninstantiated' to
-    // 'instantiated' parameters and adds it to the context. For the case where
-    // this function is a template being instantiated NOW, we also need to add
-    // the list of current template arguments to the list so that they also can
-    // be picked out of the map.
-    if (auto *SpecArgs = FD->getTemplateSpecializationArgs()) {
-      MultiLevelTemplateArgumentList JustTemplArgs(FD, SpecArgs->asArray(),
-                                                   /*Final=*/false);
-      if (addInstantiatedParametersToScope(
-              FD, PrimaryTemplate->getTemplatedDecl(), Scope, JustTemplArgs))
-        return true;
-    }
-
-    // If this is a member function, make sure we get the parameters that
-    // reference the original primary template.
-    if (FunctionTemplateDecl *FromMemTempl =
-            PrimaryTemplate->getInstantiatedFromMemberTemplate()) {
-      if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
-                                           Scope, MLTAL))
-        return true;
-    }
-
-    return false;
-  }
-
-  if (FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization ||
-      FD->getTemplatedKind() == FunctionDecl::TK_DependentNonTemplate) {
-    FunctionDecl *InstantiatedFrom =
-        FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization
-            ? FD->getInstantiatedFromMemberFunction()
-            : FD->getInstantiatedFromDecl();
-
-    InstantiatingTemplate Inst(
-        *this, FD->getPointOfInstantiation(),
-        Sema::InstantiatingTemplate::ConstraintsCheck{}, InstantiatedFrom,
-        TemplateArgs ? *TemplateArgs : ArrayRef<TemplateArgument>{},
-        SourceRange());
-    if (Inst.isInvalid())
-      return true;
-
-    // Case where this was not a template, but instantiated as a
-    // child-function.
-    if (addInstantiatedParametersToScope(FD, InstantiatedFrom, Scope, MLTAL))
-      return true;
-  }
-
-  return false;
-}
-
-// This function collects all of the template arguments for the purposes of
-// constraint-instantiation and checking.
-std::optional<MultiLevelTemplateArgumentList>
-Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
-    FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
-    LocalInstantiationScope &Scope) {
-  MultiLevelTemplateArgumentList MLTAL;
-
-  // 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.
-  MLTAL =
-      getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(),
-                                   /*Final=*/false, /*Innermost=*/std::nullopt,
-                                   /*RelativeToPrimary=*/true,
-                                   /*Pattern=*/nullptr,
-                                   /*ForConstraintInstantiation=*/true);
-  // Lambdas are handled by LambdaScopeForCallOperatorInstantiationRAII.
-  if (isLambdaCallOperator(FD))
-    return MLTAL;
-  if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope))
-    return std::nullopt;
-
-  return MLTAL;
-}
-
 bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
                                     ConstraintSatisfaction &Satisfaction,
                                     SourceLocation UsageLoc,
@@ -1543,12 +1431,12 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
 
   ContextRAII SavedContext{*this, CtxToSave};
   LocalInstantiationScope Scope(*this, !ForOverloadResolution);
-  std::optional<MultiLevelTemplateArgumentList> MLTAL =
-      SetupConstraintCheckingTemplateArgumentsAndScope(
-          const_cast<FunctionDecl *>(FD), {}, Scope);
-
-  if (!MLTAL)
-    return true;
+  MultiLevelTemplateArgumentList MLTAL =
+      getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(),
+                                   /*Final=*/false, /*Innermost=*/std::nullopt,
+                                   /*RelativeToPrimary=*/true,
+                                   /*Pattern=*/nullptr,
+                                   /*ForConstraintInstantiation=*/true);
 
   Qualifiers ThisQuals;
   CXXRecordDecl *Record = nullptr;
@@ -1559,11 +1447,11 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
   CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
 
   LambdaScopeForCallOperatorInstantiationRAII LambdaScope(
-      *this, const_cast<FunctionDecl *>(FD), *MLTAL, Scope,
+      *this, const_cast<FunctionDecl *>(FD), MLTAL, Scope,
       ForOverloadResolution);
 
   return CheckConstraintSatisfaction(
-      FD, FD->getTrailingRequiresClause(), *MLTAL,
+      FD, FD->getTrailingRequiresClause(), MLTAL,
       SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
       Satisfaction);
 }
@@ -1796,12 +1684,12 @@ bool Sema::CheckFunctionTemplateConstraints(
   Sema::ContextRAII savedContext(*this, Decl);
   LocalInstantiationScope Scope(*this);
 
-  std::optional<MultiLevelTemplateArgumentList> MLTAL =
-      SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs,
-                                                       Scope);
-
-  if (!MLTAL)
-    return true;
+  MultiLevelTemplateArgumentList MLTAL =
+      getTemplateInstantiationArgs(Decl, Decl->getLexicalDeclContext(),
+                                   /*Final=*/false, /*Innermost=*/std::nullopt,
+                                   /*RelativeToPrimary=*/true,
+                                   /*Pattern=*/nullptr,
+                                   /*ForConstraintInstantiation=*/true);
 
   Qualifiers ThisQuals;
   CXXRecordDecl *Record = nullptr;
@@ -1811,10 +1699,10 @@ bool Sema::CheckFunctionTemplateConstraints(
   }
 
   CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
-  LambdaScopeForCallOperatorInstantiationRAII LambdaScope(*this, Decl, *MLTAL,
+  LambdaScopeForCallOperatorInstantiationRAII LambdaScope(*this, Decl, MLTAL,
                                                           Scope);
 
-  return CheckConstraintSatisfaction(Template, TemplateAC, *MLTAL,
+  return CheckConstraintSatisfaction(Template, TemplateAC, MLTAL,
                                      PointOfInstantiation, Satisfaction);
 }
 
@@ -2087,7 +1975,7 @@ void Sema::DiagnoseUnsatisfiedConstraint(
                                   ConstraintExpr->getBeginLoc(), First);
 }
 
-namespace {
+namespace clang {
 
 class SubstituteParameterMappings {
   Sema &SemaRef;
@@ -2125,6 +2013,8 @@ class SubstituteParameterMappings {
   bool substitute(NormalizedConstraint &N);
 };
 
+} // namespace clang
+
 void SubstituteParameterMappings::buildParameterMapping(
     NormalizedConstraintWithParamMapping &N) {
   TemplateParameterList *TemplateParams =
@@ -2418,8 +2308,6 @@ bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) {
   llvm_unreachable("Unknown ConstraintKind enum");
 }
 
-} // namespace
-
 NormalizedConstraint *NormalizedConstraint::fromAssociatedConstraints(
     Sema &S, const NamedDecl *D, ArrayRef<AssociatedConstraint> ACs) {
   assert(ACs.size() != 0);

diff  --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index ffbe8bb0506bc..cadc5689cebc2 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1324,11 +1324,10 @@ namespace {
     bool BailOutOnIncomplete;
 
     std::optional<llvm::FoldingSetNodeID> TemplateArgsHashValue;
+    llvm::DenseMap<llvm::FoldingSetNodeID, TemplateArgumentLoc>
+        *CurrentCachedTemplateArgs = nullptr;
 
-    // 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);
+    bool instantiateMissingDeclsToScopeForConcepts(Decl *D);
 
   public:
     typedef TreeTransform<TemplateInstantiator> inherited;
@@ -1358,12 +1357,14 @@ namespace {
     inline static struct ForConstraintSubstitution_t {
     } ForConstraintSubstitution;
 
-    TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef,
-                         SourceLocation Loc,
-                         const MultiLevelTemplateArgumentList &TemplateArgs)
+    TemplateInstantiator(
+        ForParameterMappingSubstitution_t, Sema &SemaRef, SourceLocation Loc,
+        const MultiLevelTemplateArgumentList &TemplateArgs,
+        llvm::DenseMap<llvm::FoldingSetNodeID, TemplateArgumentLoc> *Cache)
         : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
-          EvaluateLambdaConstraint(true), BailOutOnIncomplete(false) {
-      if (!SemaRef.CurrentCachedTemplateArgs)
+          EvaluateLambdaConstraint(true), BailOutOnIncomplete(false),
+          CurrentCachedTemplateArgs(Cache) {
+      if (!Cache)
         return;
       auto &V = TemplateArgsHashValue.emplace();
       for (auto &Level : TemplateArgs)
@@ -1410,22 +1411,18 @@ namespace {
                                  ArrayRef<UnexpandedParameterPack> Unexpanded,
                                  bool FailOnPackProducingTemplates,
                                  bool &ShouldExpand, bool &RetainExpansion,
-                                 UnsignedOrNone &NumExpansions) {
-      if (SemaRef.CurrentInstantiationScope &&
-          (SemaRef.inConstraintSubstitution() ||
-           SemaRef.inParameterMappingSubstitution())) {
-        for (UnexpandedParameterPack ParmPack : Unexpanded) {
-          NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
-          if (auto *PVD = dyn_cast_if_present<ParmVarDecl>(VD);
-              PVD && maybeInstantiateFunctionParameterToScope(PVD))
-            return true;
-        }
+                                 UnsignedOrNone &NumExpansions,
+                                 bool Diagnose = true) {
+      for (UnexpandedParameterPack ParmPack : Unexpanded) {
+        if (instantiateMissingDeclsToScopeForConcepts(
+                dyn_cast<NamedDecl *>(ParmPack.first)))
+          return true;
       }
 
       return getSema().CheckParameterPacksForExpansion(
           EllipsisLoc, PatternRange, Unexpanded, TemplateArgs,
           FailOnPackProducingTemplates, ShouldExpand, RetainExpansion,
-          NumExpansions);
+          NumExpansions, Diagnose);
     }
 
     void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
@@ -1637,7 +1634,7 @@ namespace {
                                    TemplateArgumentLoc &Output,
                                    bool Uneval = false) {
       const TemplateArgument &Arg = Input.getArgument();
-      if (auto *Cache = SemaRef.CurrentCachedTemplateArgs;
+      if (auto *Cache = CurrentCachedTemplateArgs;
           Cache && TemplateArgsHashValue) {
         llvm::FoldingSetNodeID ID = *TemplateArgsHashValue;
         ID.AddInteger(SemaRef.ArgPackSubstIndex.toInternalRepresentation());
@@ -1980,11 +1977,7 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
     // template parameter.
   }
 
-  if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(D);
-      PVD && SemaRef.CurrentInstantiationScope &&
-      (SemaRef.inConstraintSubstitution() ||
-       SemaRef.inParameterMappingSubstitution()) &&
-      maybeInstantiateFunctionParameterToScope(PVD))
+  if (instantiateMissingDeclsToScopeForConcepts(D))
     return nullptr;
 
   if (isa<CXXExpansionStmtDecl>(D)) {
@@ -1996,9 +1989,39 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
   return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
 }
 
-bool TemplateInstantiator::maybeInstantiateFunctionParameterToScope(
-    ParmVarDecl *OldParm) {
-  if (SemaRef.CurrentInstantiationScope->getInstantiationOfIfExists(OldParm))
+bool TemplateInstantiator::instantiateMissingDeclsToScopeForConcepts(Decl *D) {
+  if (!(D && (SemaRef.inConstraintSubstitution() ||
+              SemaRef.inParameterMappingSubstitution())))
+    return false;
+
+  auto *Current = SemaRef.CurrentInstantiationScope;
+  if (!Current)
+    return false;
+  if (Current->getInstantiationOfIfExists(D))
+    return false;
+
+  for (auto *Outer = Current->getOuterScope(); Outer;
+       Outer = Outer->getOuterScope()) {
+    auto *Pair = Outer->getInstantiationOfIfExists(D);
+    if (!Pair)
+      continue;
+
+    if (auto *InstD = dyn_cast<Decl *>(*Pair)) {
+      Current->InstantiatedLocal(D, InstD);
+    } else {
+      Current->MakeInstantiatedLocalArgPack(D);
+      auto *Pack = cast<LocalInstantiationScope::DeclArgumentPack *>(*Pair);
+      for (auto *VD : *Pack)
+        Current->InstantiatedLocal(D, VD);
+    }
+    return false;
+  }
+
+  // CWG2770: Function parameters should be instantiated when they are
+  // needed by a satisfaction check of an atomic constraint or
+  // (recursively) by another function parameter.
+  auto *OldParm = dyn_cast<ParmVarDecl>(D);
+  if (!OldParm)
     return false;
 
   if (!OldParm->isParameterPack())
@@ -2459,11 +2482,7 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) {
   // Handle references to function parameter packs.
   if (VarDecl *PD = dyn_cast<VarDecl>(D))
     if (PD->isParameterPack()) {
-      if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(PD);
-          PVD && SemaRef.CurrentInstantiationScope &&
-          (SemaRef.inConstraintSubstitution() ||
-           SemaRef.inParameterMappingSubstitution()) &&
-          maybeInstantiateFunctionParameterToScope(PVD))
+      if (instantiateMissingDeclsToScopeForConcepts(PD))
         return ExprError();
 
       return TransformFunctionParmPackRefExpr(E, PD);
@@ -4486,10 +4505,35 @@ bool Sema::SubstTemplateArgumentsInParameterMapping(
     TemplateArgumentListInfo &Out) {
   TemplateInstantiator Instantiator(
       TemplateInstantiator::ForParameterMappingSubstitution, *this, BaseLoc,
-      TemplateArgs);
+      TemplateArgs, CurrentCachedTemplateArgs);
   return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
 }
 
+UnsignedOrNone Sema::EvaluateFoldExpandedConstraintSize(
+    const Expr *Pattern, const MultiLevelTemplateArgumentList &TemplateArgs) {
+  TemplateInstantiator Instantiator(
+      TemplateInstantiator::ForConstraintSubstitution, *this, TemplateArgs,
+      SourceLocation(), DeclarationName());
+
+  SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+  collectUnexpandedParameterPacks(const_cast<Expr *>(Pattern), Unexpanded);
+  assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+
+  bool Expand = true;
+  bool RetainExpansion = false;
+  UnsignedOrNone NumExpansions(std::nullopt);
+  if (Instantiator.TryExpandParameterPacks(
+          Pattern->getExprLoc(), Pattern->getSourceRange(), Unexpanded,
+          /*FailOnPackProducingTemplates=*/false, Expand, RetainExpansion,
+          NumExpansions, /*Diagnose=*/false) ||
+      !Expand || RetainExpansion)
+    return std::nullopt;
+
+  if (NumExpansions && getLangOpts().BracketDepth < *NumExpansions)
+    return std::nullopt;
+  return NumExpansions;
+}
+
 ExprResult
 Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
   if (!E)

diff  --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index d3e681f22ab26..b4f763f7fdb7d 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -692,3 +692,29 @@ void g() {
 }
 
 }
+
+namespace GH198052 {
+
+template <class T, class U>
+concept is_same = __is_same(T, U);
+
+constexpr int NumberOfTrueInstances(auto... booleans)
+  requires (is_same<bool, decltype(booleans)> && ...)
+{
+  bool the_booleans[] = {booleans...};
+  int nrvo = 0;
+  for (bool a_boolean : the_booleans) {
+    if (a_boolean) nrvo += 1;
+  }
+  return nrvo;
+}
+
+constexpr bool a = true;
+constexpr bool b = false;
+constexpr bool c = true;
+constexpr bool d = false;
+constexpr int count = NumberOfTrueInstances(a, b, c, d);
+static_assert(count == 2);
+
+}
+

diff  --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp
index 2010a028fce4a..26deec7ad7777 100644
--- a/clang/test/SemaTemplate/concepts-lambda.cpp
+++ b/clang/test/SemaTemplate/concepts-lambda.cpp
@@ -493,3 +493,19 @@ static_assert(count_if_v_bad_2<L, double> == 111);
 static_assert(count_if_v_bad_2<L, char> == 111);
 
 }
+
+namespace GH209632 {
+
+template <class A, class B> concept same_as = __is_same(A, B);
+
+template <class NR> void f(NR) {
+  using N = NR;
+  auto inner = [](same_as<N> auto) {};
+  inner(N{});
+}
+
+void main() {
+  f(0);
+}
+
+}


        


More information about the cfe-commits mailing list