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

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.


>From 0ab79d6ecdddaeb0a13591c5f0c73f54632634bb Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Thu, 10 Sep 2026 13:40:05 +0530
Subject: [PATCH] [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
 

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

Reply via email to