[clang] [Clang] Keep lambdas in default template arguments dependent until used (PR #222541)

Akash Manna via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 11 05:15:58 PDT 2026


https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/222541

>From 0ab79d6ecdddaeb0a13591c5f0c73f54632634bb Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Thu, 10 Sep 2026 13:40:05 +0530
Subject: [PATCH 1/3] [Clang] Keep lambdas in default template arguments
 dependent until used

Fixes #176405

A lambda in the default argument of a template parameter is parsed as dependent, with its own template parameters one depth below the parameter list (#62611): it can only be finished once the default argument is substituted for a use of the template. That got lost when the parameter list belongs to a member template of a class template. Instantiating the enclosing class substitutes the default argument with just the outer levels, and TransformLambdaExpr then recomputes the closure's dependence from the DeclContext, which is the concrete specialization by now. So the generic lambda was rebuilt as non-dependent while its template parameters still sat at depth 1, and `[]<typename... U>(U...) {}()` was resolved on the spot: deduction never saw U, the pack was never expanded, and BuildCXXDefaultArgExpr asserted on a parameter that has no default. The non-pack variant shows the same problem as a bogus "couldn't infer template argument 'U'" on valid code.

Default arguments of a template parameter list being instantiated now go through Sema::SubstTemplateParameterDefaultArgument, which flags the TemplateInstantiator so that ComputeLambdaDependency keeps any lambda it transforms LDK_AlwaysDependent, the same way the alias-template case is handled. The instantiated member template's default argument then looks exactly like one parsed in a non-template class: the lambda is built and called only when the default argument is used, with the parameter list's own level present and the depths lined up. Deduction, overload resolution and default-argument building are unchanged.
---
 clang/docs/ReleaseNotes.md                    |  5 ++
 clang/include/clang/Sema/Sema.h               |  8 +++
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 23 ++++++++
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  8 +--
 clang/test/SemaCXX/cxx2a-template-lambdas.cpp | 52 +++++++++++++++++++
 5 files changed, 92 insertions(+), 4 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3cca316a91d4d..97944a8870206 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -676,6 +676,11 @@ features cannot lower the translation-unit ABI level;
   class with an invalid non-static data member, such as one qualified with an
   address space. (#GH194605)
 
+- Fixed an assertion when instantiating a class template whose member template
+  has a default template argument that calls a generic lambda, e.g.
+  ``template <auto = []<typename... U>(U...) {}()> struct X;``. The lambda now
+  remains dependent until the default argument is used. (#GH176405)
+
 #### Bug Fixes to AST Handling
 
 - Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4ff4c669a6b70..5d9f9cf683018 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13576,6 +13576,14 @@ class Sema final : public SemaBase {
                              TemplateArgumentLoc &Output,
                              SourceLocation Loc = {},
                              const DeclarationName &Entity = {});
+
+  /// Substitute into the default argument of a template parameter as part of
+  /// instantiating its template parameter list. Lambdas within the default
+  /// argument stay dependent, as they were when parsed.
+  bool SubstTemplateParameterDefaultArgument(
+      const TemplateArgumentLoc &Input,
+      const MultiLevelTemplateArgumentList &TemplateArgs,
+      TemplateArgumentLoc &Output);
   bool
   SubstTemplateArguments(ArrayRef<TemplateArgumentLoc> Args,
                          const MultiLevelTemplateArgumentList &TemplateArgs,
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index ffbe8bb0506bc..db13b05c81d1c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1317,6 +1317,9 @@ namespace {
     // Whether to evaluate the C++20 constraints or simply substitute into them.
     bool EvaluateConstraints = true;
     bool EvaluateLambdaConstraint = false;
+    // Whether we are substituting into the default argument of a template
+    // parameter whose template parameter list is being instantiated.
+    bool InTemplateParameterDefaultArgument = false;
     // Whether Substitution was Incomplete, that is, we tried to substitute in
     // any user provided template arguments which were null.
     bool IsIncomplete = false;
@@ -1352,6 +1355,10 @@ namespace {
       return EvaluateConstraints;
     }
 
+    void setInTemplateParameterDefaultArgument(bool B) {
+      InTemplateParameterDefaultArgument = B;
+    }
+
     inline static struct ForParameterMappingSubstitution_t {
     } ForParameterMappingSubstitution;
 
@@ -1760,6 +1767,12 @@ namespace {
 
     CXXRecordDecl::LambdaDependencyKind
     ComputeLambdaDependency(LambdaScopeInfo *LSI) {
+      // A lambda in the default argument of a template parameter is dependent
+      // when parsed (it is within a template parameter list) and remains so
+      // while that parameter list is instantiated without being substituted
+      // itself, e.g. for a member template of a class being instantiated.
+      if (InTemplateParameterDefaultArgument)
+        return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
       if (auto TypeAlias =
               TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl(
                   getSema());
@@ -4471,6 +4484,16 @@ bool Sema::SubstTemplateArgument(
   return Instantiator.TransformTemplateArgument(Input, Output);
 }
 
+bool Sema::SubstTemplateParameterDefaultArgument(
+    const TemplateArgumentLoc &Input,
+    const MultiLevelTemplateArgumentList &TemplateArgs,
+    TemplateArgumentLoc &Output) {
+  TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
+                                    DeclarationName());
+  Instantiator.setInTemplateParameterDefaultArgument(true);
+  return Instantiator.TransformTemplateArgument(Input, Output);
+}
+
 bool Sema::SubstTemplateArguments(
     ArrayRef<TemplateArgumentLoc> Args,
     const MultiLevelTemplateArgumentList &TemplateArgs,
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 7668b75e836e4..e9336fd823129 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3862,8 +3862,8 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl(
   }
   if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) {
     TemplateArgumentLoc Output;
-    if (!SemaRef.SubstTemplateArgument(D->getDefaultArgument(), TemplateArgs,
-                                       Output))
+    if (!SemaRef.SubstTemplateParameterDefaultArgument(D->getDefaultArgument(),
+                                                       TemplateArgs, Output))
       Inst->setDefaultArgument(SemaRef.getASTContext(), Output);
   }
 
@@ -4026,8 +4026,8 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl(
     EnterExpressionEvaluationContext ConstantEvaluated(
         SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
     TemplateArgumentLoc Result;
-    if (!SemaRef.SubstTemplateArgument(D->getDefaultArgument(), TemplateArgs,
-                                       Result))
+    if (!SemaRef.SubstTemplateParameterDefaultArgument(D->getDefaultArgument(),
+                                                       TemplateArgs, Result))
       Param->setDefaultArgument(SemaRef.Context, Result);
   }
 
diff --git a/clang/test/SemaCXX/cxx2a-template-lambdas.cpp b/clang/test/SemaCXX/cxx2a-template-lambdas.cpp
index 45d265e2cdc2b..2f7eb9ddcca48 100644
--- a/clang/test/SemaCXX/cxx2a-template-lambdas.cpp
+++ b/clang/test/SemaCXX/cxx2a-template-lambdas.cpp
@@ -96,6 +96,58 @@ void foo() {
   {return {};}(1);
 }
 
+}
+
+namespace GH176405 {
+template <int> struct bad {
+  template <auto = []<typename... U>(U...) {}()> struct X;
+  static int f() { return 0; }
+};
+int y = bad<0>::f();
+
+template <int N> struct S {
+  template <auto V = []<typename... U>(U...) { return sizeof...(U) + N; }()>
+  struct A { static constexpr auto value = V; };
+  template <auto V = []<typename U>(U u) { return u + N; }(41)>
+  struct B { static constexpr auto value = V; };
+  template <auto V = [](auto... x) { return sizeof...(x) + N; }(1, 2)>
+  struct C { static constexpr auto value = V; };
+  template <typename T, auto V = [] { return sizeof(T) + N; }()>
+  struct D { static constexpr auto value = V; };
+  template <typename T = decltype([]<typename... U>(U...) { return N; }())>
+  struct E { using type = T; };
+  template <auto V = []<typename... U>(U...) { return sizeof...(U) + N; }()>
+  static constexpr auto f() { return V; }
+  template <auto V = []<typename... U>(U...) { return sizeof...(U) + N; }()>
+  static constexpr auto var = V;
+  template <auto V = []<typename... U>(U...) { return sizeof...(U) + N; }()>
+  using alias = A<V>;
+};
+static_assert(S<1>::A<>::value == 1);
+static_assert(S<1>::A<5>::value == 5);
+static_assert(S<1>::B<>::value == 42);
+static_assert(S<1>::C<>::value == 3);
+static_assert(S<1>::D<int>::value == sizeof(int) + 1);
+static_assert(__is_same(S<1>::E<>::type, int));
+static_assert(S<1>::f() == 1);
+static_assert(S<1>::var<> == 1);
+static_assert(S<1>::alias<>::value == 1);
+
+template <int N> struct Outer {
+  template <int M> struct Inner {
+    template <auto V = []<typename... U>(U...) { return N + M; }()>
+    struct X { static constexpr auto value = V; };
+  };
+};
+static_assert(Outer<1>::Inner<2>::X<>::value == 3);
+
+template <int N> constexpr auto g() {
+  auto l = []<typename T = decltype([]<typename V>(V v) { return v; }(N))>() {
+    return T{};
+  };
+  return l();
+}
+static_assert(g<1>() == 0);
 }
 #endif
 

>From 3a00e68cd06a95aa0b8074c5ba39502e1021af54 Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Fri, 11 Sep 2026 08:52:26 +0530
Subject: [PATCH 2/3] [Clang] Decide lambda dependence in default template
 arguments before building the closure

Fixes the SemaTemplate/GH176155.cpp failure in CI.
---
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 14 ++++++++------
 clang/lib/Sema/TreeTransform.h                | 16 +++++++++++-----
 clang/test/SemaCXX/cxx2a-template-lambdas.cpp |  6 ++++++
 3 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index db13b05c81d1c..98c7ebb974344 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1765,14 +1765,16 @@ namespace {
     TransformSubstBuiltinTemplatePackType(TypeLocBuilder &TLB,
                                           SubstBuiltinTemplatePackTypeLoc TL);
 
+    // A lambda in the default argument of a template parameter is dependent
+    // when parsed (it is within a template parameter list) and remains so
+    // while that parameter list is instantiated without being substituted
+    // itself, e.g. for a member template of a class being instantiated.
+    bool IsLambdaAlwaysDependent() {
+      return InTemplateParameterDefaultArgument;
+    }
+
     CXXRecordDecl::LambdaDependencyKind
     ComputeLambdaDependency(LambdaScopeInfo *LSI) {
-      // A lambda in the default argument of a template parameter is dependent
-      // when parsed (it is within a template parameter list) and remains so
-      // while that parameter list is instantiated without being substituted
-      // itself, e.g. for a member template of a class being instantiated.
-      if (InTemplateParameterDefaultArgument)
-        return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
       if (auto TypeAlias =
               TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl(
                   getSema());
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index c8458fda58a88..93a3673a88ed5 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -834,6 +834,10 @@ class TreeTransform {
   /// the body.
   StmtResult SkipLambdaBody(LambdaExpr *E, Stmt *Body);
 
+  /// Whether a lambda-expression is dependent regardless of the context it is
+  /// rebuilt in.
+  bool IsLambdaAlwaysDependent() { return false; }
+
   CXXRecordDecl::LambdaDependencyKind
   ComputeLambdaDependency(LambdaScopeInfo *LSI) {
     return static_cast<CXXRecordDecl::LambdaDependencyKind>(
@@ -16304,11 +16308,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
   // (A ClassTemplateSpecializationDecl is always a dependent context.)
   while (DC->isRequiresExprBody() || isa<CXXExpansionStmtDecl>(DC))
     DC = DC->getParent();
-  if ((getSema().isUnevaluatedContext() ||
-       getSema().isConstantEvaluatedContext()) &&
-      !(dyn_cast_or_null<CXXRecordDecl>(DC->getParent()) &&
-        cast<CXXRecordDecl>(DC->getParent())->isGenericLambda()) &&
-      (DC->isFileContext() || !DC->getParent()->isDependentContext()))
+  if (getDerived().IsLambdaAlwaysDependent())
+    DependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
+  else if ((getSema().isUnevaluatedContext() ||
+            getSema().isConstantEvaluatedContext()) &&
+           !(dyn_cast_or_null<CXXRecordDecl>(DC->getParent()) &&
+             cast<CXXRecordDecl>(DC->getParent())->isGenericLambda()) &&
+           (DC->isFileContext() || !DC->getParent()->isDependentContext()))
     DependencyKind = CXXRecordDecl::LDK_NeverDependent;
 
   CXXRecordDecl *OldClass = E->getLambdaClass();
diff --git a/clang/test/SemaCXX/cxx2a-template-lambdas.cpp b/clang/test/SemaCXX/cxx2a-template-lambdas.cpp
index 2f7eb9ddcca48..20efddcc0263b 100644
--- a/clang/test/SemaCXX/cxx2a-template-lambdas.cpp
+++ b/clang/test/SemaCXX/cxx2a-template-lambdas.cpp
@@ -122,6 +122,11 @@ template <int N> struct S {
   static constexpr auto var = V;
   template <auto V = []<typename... U>(U...) { return sizeof...(U) + N; }()>
   using alias = A<V>;
+  template <auto V = [] {
+    struct Local { static constexpr int get() { return N; } };
+    return Local::get();
+  }()>
+  struct F { static constexpr auto value = V; };
 };
 static_assert(S<1>::A<>::value == 1);
 static_assert(S<1>::A<5>::value == 5);
@@ -132,6 +137,7 @@ static_assert(__is_same(S<1>::E<>::type, int));
 static_assert(S<1>::f() == 1);
 static_assert(S<1>::var<> == 1);
 static_assert(S<1>::alias<>::value == 1);
+static_assert(S<1>::F<>::value == 1);
 
 template <int N> struct Outer {
   template <int M> struct Inner {

>From 038f22152102255a5c121c49009de99e016d2342 Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Fri, 11 Sep 2026 17:44:52 +0530
Subject: [PATCH 3/3] [Clang] Account for the template depth of generic lambdas
 in default template arguments

Fixes #176405

A lambda in a default template argument is parsed one depth below the
parameter list that declares it. When the enclosing class of a member
template is instantiated, only the outer levels are substituted, so the
rebuilt lambda's call operator template sits at depth 1. Overload
resolution deduced at depth 0 and substituted the specialization with a
single-level argument list, so U... never expanded and
BuildCXXDefaultArgExpr asserted on a parameter without a default.

Deduce method and conversion template candidates at their parameter
list's depth, retain the outer levels when substituting the
specialization and its explicit arguments, and have
getTemplateInstantiationArgs represent those levels for generic lambda
specializations, for both the body and the constraints.
---
 clang/docs/ReleaseNotes.md                    |  5 ++-
 clang/include/clang/Sema/Sema.h               |  8 ----
 clang/lib/Sema/SemaOverload.cpp               |  7 +++-
 clang/lib/Sema/SemaTemplateDeduction.cpp      |  3 ++
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 42 ++++++-------------
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  8 ++--
 clang/lib/Sema/TreeTransform.h                | 16 +++----
 clang/test/SemaCXX/cxx2a-template-lambdas.cpp | 28 +++++++++++++
 8 files changed, 61 insertions(+), 56 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 97944a8870206..edfbba8ab3135 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -678,8 +678,9 @@ features cannot lower the translation-unit ABI level;
 
 - Fixed an assertion when instantiating a class template whose member template
   has a default template argument that calls a generic lambda, e.g.
-  ``template <auto = []<typename... U>(U...) {}()> struct X;``. The lambda now
-  remains dependent until the default argument is used. (#GH176405)
+  ``template <auto = []<typename... U>(U...) {}()> struct X;``. Template
+  argument deduction for the lambda's call operator now accounts for the depth
+  of its template parameters. (#GH176405)
 
 #### Bug Fixes to AST Handling
 
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5d9f9cf683018..4ff4c669a6b70 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13576,14 +13576,6 @@ class Sema final : public SemaBase {
                              TemplateArgumentLoc &Output,
                              SourceLocation Loc = {},
                              const DeclarationName &Entity = {});
-
-  /// Substitute into the default argument of a template parameter as part of
-  /// instantiating its template parameter list. Lambdas within the default
-  /// argument stay dependent, as they were when parsed.
-  bool SubstTemplateParameterDefaultArgument(
-      const TemplateArgumentLoc &Input,
-      const MultiLevelTemplateArgumentList &TemplateArgs,
-      TemplateArgumentLoc &Output);
   bool
   SubstTemplateArguments(ArrayRef<TemplateArgumentLoc> Args,
                          const MultiLevelTemplateArgumentList &TemplateArgs,
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 5d008cb2fd780..7299518000b6f 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -8163,7 +8163,8 @@ static void AddMethodTemplateCandidateImmediately(
   //   functions. In such a case, the candidate functions generated from each
   //   function template are combined with the set of non-template candidate
   //   functions.
-  TemplateDeductionInfo Info(CandidateSet.getLocation());
+  TemplateDeductionInfo Info(CandidateSet.getLocation(),
+                             MethodTmpl->getTemplateParameters()->getDepth());
   auto *Method = cast<CXXMethodDecl>(MethodTmpl->getTemplatedDecl());
   FunctionDecl *Specialization = nullptr;
   ConversionSequenceList Conversions;
@@ -8762,7 +8763,9 @@ static void AddTemplateConversionCandidateImmediately(
   QualType ObjectType = From->getType();
   Expr::Classification ObjectClassification = From->Classify(S.Context);
 
-  TemplateDeductionInfo Info(CandidateSet.getLocation());
+  TemplateDeductionInfo Info(
+      CandidateSet.getLocation(),
+      FunctionTemplate->getTemplateParameters()->getDepth());
   CXXConversionDecl *Specialization = nullptr;
   if (TemplateDeductionResult Result = S.DeduceTemplateArguments(
           FunctionTemplate, ObjectType, ObjectClassification, ToType,
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index b66152f2d971d..9d3b3c54ced3d 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3648,6 +3648,7 @@ TemplateDeductionResult Sema::SubstituteExplicitTemplateArguments(
   MultiLevelTemplateArgumentList MLTAL(FunctionTemplate,
                                        SugaredExplicitArgumentList->asArray(),
                                        /*Final=*/true);
+  MLTAL.addOuterRetainedLevels(TemplateParams->getDepth());
 
   // Instantiate the types of each of the function parameters given the
   // explicitly-specified template arguments. If the function has a trailing
@@ -4011,6 +4012,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
   MultiLevelTemplateArgumentList SubstArgs(
       FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
       /*Final=*/false);
+  SubstArgs.addOuterRetainedLevels(
+      FunctionTemplate->getTemplateParameters()->getDepth());
   Specialization = cast_or_null<FunctionDecl>(
       SubstDecl(FD, Owner, SubstArgs));
   if (!Specialization || Specialization->isInvalidDecl())
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 98c7ebb974344..ad9316583d9bf 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -303,10 +303,19 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
         Function->getPrimaryTemplate()->isMemberSpecialization())
       return Response::Done();
 
-    // If this function is a generic lambda specialization, we are done.
-    if (!ForConstraintInstantiation &&
-        isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
-      return Response::Done();
+    if (isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) {
+      // The lambda may be nested in template parameter lists that are not
+      // substituted, e.g. a default template argument of a member template.
+      unsigned Depth =
+          Function->getPrimaryTemplate()->getTemplateParameters()->getDepth();
+      // If this function is a generic lambda specialization, we are done.
+      if (!ForConstraintInstantiation) {
+        Result.addOuterRetainedLevels(Depth);
+        return Response::Done();
+      }
+      for (unsigned I = 0; I != Depth; ++I)
+        Result.addOuterTemplateArguments(std::nullopt);
+    }
 
   } else if (auto *Template = Function->getDescribedFunctionTemplate()) {
     assert(
@@ -1317,9 +1326,6 @@ namespace {
     // Whether to evaluate the C++20 constraints or simply substitute into them.
     bool EvaluateConstraints = true;
     bool EvaluateLambdaConstraint = false;
-    // Whether we are substituting into the default argument of a template
-    // parameter whose template parameter list is being instantiated.
-    bool InTemplateParameterDefaultArgument = false;
     // Whether Substitution was Incomplete, that is, we tried to substitute in
     // any user provided template arguments which were null.
     bool IsIncomplete = false;
@@ -1355,10 +1361,6 @@ namespace {
       return EvaluateConstraints;
     }
 
-    void setInTemplateParameterDefaultArgument(bool B) {
-      InTemplateParameterDefaultArgument = B;
-    }
-
     inline static struct ForParameterMappingSubstitution_t {
     } ForParameterMappingSubstitution;
 
@@ -1765,14 +1767,6 @@ namespace {
     TransformSubstBuiltinTemplatePackType(TypeLocBuilder &TLB,
                                           SubstBuiltinTemplatePackTypeLoc TL);
 
-    // A lambda in the default argument of a template parameter is dependent
-    // when parsed (it is within a template parameter list) and remains so
-    // while that parameter list is instantiated without being substituted
-    // itself, e.g. for a member template of a class being instantiated.
-    bool IsLambdaAlwaysDependent() {
-      return InTemplateParameterDefaultArgument;
-    }
-
     CXXRecordDecl::LambdaDependencyKind
     ComputeLambdaDependency(LambdaScopeInfo *LSI) {
       if (auto TypeAlias =
@@ -4486,16 +4480,6 @@ bool Sema::SubstTemplateArgument(
   return Instantiator.TransformTemplateArgument(Input, Output);
 }
 
-bool Sema::SubstTemplateParameterDefaultArgument(
-    const TemplateArgumentLoc &Input,
-    const MultiLevelTemplateArgumentList &TemplateArgs,
-    TemplateArgumentLoc &Output) {
-  TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
-                                    DeclarationName());
-  Instantiator.setInTemplateParameterDefaultArgument(true);
-  return Instantiator.TransformTemplateArgument(Input, Output);
-}
-
 bool Sema::SubstTemplateArguments(
     ArrayRef<TemplateArgumentLoc> Args,
     const MultiLevelTemplateArgumentList &TemplateArgs,
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e9336fd823129..7668b75e836e4 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3862,8 +3862,8 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl(
   }
   if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) {
     TemplateArgumentLoc Output;
-    if (!SemaRef.SubstTemplateParameterDefaultArgument(D->getDefaultArgument(),
-                                                       TemplateArgs, Output))
+    if (!SemaRef.SubstTemplateArgument(D->getDefaultArgument(), TemplateArgs,
+                                       Output))
       Inst->setDefaultArgument(SemaRef.getASTContext(), Output);
   }
 
@@ -4026,8 +4026,8 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl(
     EnterExpressionEvaluationContext ConstantEvaluated(
         SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
     TemplateArgumentLoc Result;
-    if (!SemaRef.SubstTemplateParameterDefaultArgument(D->getDefaultArgument(),
-                                                       TemplateArgs, Result))
+    if (!SemaRef.SubstTemplateArgument(D->getDefaultArgument(), TemplateArgs,
+                                       Result))
       Param->setDefaultArgument(SemaRef.Context, Result);
   }
 
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 93a3673a88ed5..c8458fda58a88 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -834,10 +834,6 @@ class TreeTransform {
   /// the body.
   StmtResult SkipLambdaBody(LambdaExpr *E, Stmt *Body);
 
-  /// Whether a lambda-expression is dependent regardless of the context it is
-  /// rebuilt in.
-  bool IsLambdaAlwaysDependent() { return false; }
-
   CXXRecordDecl::LambdaDependencyKind
   ComputeLambdaDependency(LambdaScopeInfo *LSI) {
     return static_cast<CXXRecordDecl::LambdaDependencyKind>(
@@ -16308,13 +16304,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
   // (A ClassTemplateSpecializationDecl is always a dependent context.)
   while (DC->isRequiresExprBody() || isa<CXXExpansionStmtDecl>(DC))
     DC = DC->getParent();
-  if (getDerived().IsLambdaAlwaysDependent())
-    DependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
-  else if ((getSema().isUnevaluatedContext() ||
-            getSema().isConstantEvaluatedContext()) &&
-           !(dyn_cast_or_null<CXXRecordDecl>(DC->getParent()) &&
-             cast<CXXRecordDecl>(DC->getParent())->isGenericLambda()) &&
-           (DC->isFileContext() || !DC->getParent()->isDependentContext()))
+  if ((getSema().isUnevaluatedContext() ||
+       getSema().isConstantEvaluatedContext()) &&
+      !(dyn_cast_or_null<CXXRecordDecl>(DC->getParent()) &&
+        cast<CXXRecordDecl>(DC->getParent())->isGenericLambda()) &&
+      (DC->isFileContext() || !DC->getParent()->isDependentContext()))
     DependencyKind = CXXRecordDecl::LDK_NeverDependent;
 
   CXXRecordDecl *OldClass = E->getLambdaClass();
diff --git a/clang/test/SemaCXX/cxx2a-template-lambdas.cpp b/clang/test/SemaCXX/cxx2a-template-lambdas.cpp
index 20efddcc0263b..079576231d88b 100644
--- a/clang/test/SemaCXX/cxx2a-template-lambdas.cpp
+++ b/clang/test/SemaCXX/cxx2a-template-lambdas.cpp
@@ -154,6 +154,34 @@ template <int N> constexpr auto g() {
   return l();
 }
 static_assert(g<1>() == 0);
+
+namespace valid {
+template <int> struct bad {
+  template <auto = []<typename... U>(U...) { return 42; }()>
+  struct X {};
+};
+
+bad<1> b;
+bad<1>::X x;
+static_assert(__is_same(decltype(x), bad<1>::X<42>));
+}
+
+namespace invalid {
+template <class>
+concept C = false; // expected-note 2{{because 'false' evaluated to false}}
+
+template <int> struct bad {
+  template <auto = []<C... U>(U...) { return 42; }(1, 2)> // expected-error {{no matching function for call to object of type}} \
+                                                        // expected-note {{candidate template ignored: constraints not satisfied [with U = <int, int>]}} \
+                                                        // expected-note 2{{'int' does not satisfy 'C'}}
+  struct X {}; // expected-note {{couldn't infer template argument ''}} \
+               // expected-note 2{{implicit deduction guide declared as}} \
+               // expected-note {{candidate function template not viable: requires 1 argument, but 0 were provided}}
+};
+
+bad<1>::X x; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of}} \
+             // expected-note {{in instantiation of template class 'GH176405::invalid::bad<1>' requested here}}
+}
 }
 #endif
 



More information about the cfe-commits mailing list