[clang] [Clang][Sema] Fix the lambda call expression inside of a type alias declaration (PR #82310)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 12 21:30:18 PDT 2024


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

>From 25f493da55e5cd7d46dda6fca6062aa56b6a3fd0 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 20 Feb 2024 14:54:14 +0800
Subject: [PATCH 1/7] The lambda call inside of a type alias

---
 clang/docs/ReleaseNotes.rst                   |  2 +
 clang/include/clang/AST/DeclCXX.h             |  4 +
 clang/include/clang/Sema/Sema.h               |  8 ++
 clang/lib/Frontend/FrontendActions.cpp        |  2 +
 clang/lib/Sema/SemaConcept.cpp                | 15 ++--
 clang/lib/Sema/SemaTemplate.cpp               |  9 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 64 ++++++++++++++-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  5 ++
 clang/lib/Sema/TreeTransform.h                |  9 ++
 .../alias-template-with-lambdas.cpp           | 82 +++++++++++++++++++
 10 files changed, 186 insertions(+), 14 deletions(-)
 create mode 100644 clang/test/SemaTemplate/alias-template-with-lambdas.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 690fc7ed271a3db..f5dc956cf5fc4aa 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -344,6 +344,8 @@ Bug Fixes to C++ Support
   when one of the function had more specialized templates.
   Fixes (`#82509 <https://github.com/llvm/llvm-project/issues/82509>`_)
   and (`#74494 <https://github.com/llvm/llvm-project/issues/74494>`_)
+- Clang now supports direct lambda calls inside of a type alias template declarations.
+  This addresses (#GH70601), (#GH76674), (#GH79555), (#GH81145) and so on.
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 9cebaff63bb0dbe..7aed4d5cbc002e2 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1869,6 +1869,10 @@ class CXXRecordDecl : public RecordDecl {
     DL.MethodTyInfo = TS;
   }
 
+  void setLambdaDependencyKind(unsigned Kind) {
+    getLambdaData().DependencyKind = Kind;
+  }
+
   void setLambdaIsGeneric(bool IsGeneric) {
     assert(DefinitionData && DefinitionData->IsLambda &&
            "setting lambda property of non-lambda class");
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index cfc1c3b34947880..a5520e19a14327d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10145,6 +10145,8 @@ class Sema final {
 
       /// We are building deduction guides for a class.
       BuildingDeductionGuides,
+
+      TypeAliasTemplateInstantiation,
     } Kind;
 
     /// Was the enclosing context a non-instantiation SFINAE context?
@@ -10234,6 +10236,12 @@ class Sema final {
                           FunctionDecl *Entity, ExceptionSpecification,
                           SourceRange InstantiationRange = SourceRange());
 
+    /// Note that we are instantiating a type alias template declaration.
+    InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+                          TypeAliasTemplateDecl *Template,
+                          ArrayRef<TemplateArgument> TemplateArgs,
+                          SourceRange InstantiationRange = SourceRange());
+
     /// Note that we are instantiating a default argument in a
     /// template-id.
     InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index 50338bfa670f830..5f0299dfa32e24b 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -452,6 +452,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
       return "BuildingBuiltinDumpStructCall";
     case CodeSynthesisContext::BuildingDeductionGuides:
       return "BuildingDeductionGuides";
+    case Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation:
+      return "TypeAliasTemplateInstantiation";
     }
     return "";
   }
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index a8e387e35fb4c90..0b84d83b4fb4b81 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -615,14 +615,13 @@ bool Sema::SetupConstraintScope(
     // reference the original primary template.
     // We walk up the instantiated template chain so that nested lambdas get
     // handled properly.
-    for (FunctionTemplateDecl *FromMemTempl =
-             PrimaryTemplate->getInstantiatedFromMemberTemplate();
-         FromMemTempl;
-         FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate()) {
-      if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
-                                           Scope, MLTAL))
-        return true;
-    }
+    FunctionTemplateDecl *FromMemTempl =
+        PrimaryTemplate->getInstantiatedFromMemberTemplate();
+    while (FromMemTempl && FromMemTempl->getInstantiatedFromMemberTemplate())
+      FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate();
+    if (FromMemTempl && addInstantiatedParametersToScope(
+                            FD, FromMemTempl->getTemplatedDecl(), Scope, MLTAL))
+      return true;
 
     return false;
   }
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index d62095558d0ffbd..30499df1797fcfa 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4343,9 +4343,12 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
     if (Inst.isInvalid())
       return QualType();
 
-    CanonType = SubstType(Pattern->getUnderlyingType(),
-                          TemplateArgLists, AliasTemplate->getLocation(),
-                          AliasTemplate->getDeclName());
+    InstantiatingTemplate InstTemplate(
+        *this, Pattern->getTypeSourceInfo()->getTypeLoc().getBeginLoc(),
+        AliasTemplate, TemplateArgLists.getInnermost());
+    CanonType =
+        SubstType(Pattern->getUnderlyingType(), TemplateArgLists,
+                  AliasTemplate->getLocation(), AliasTemplate->getDeclName());
     if (CanonType.isNull()) {
       // If this was enable_if and we failed to find the nested type
       // within enable_if in a SFINAE context, dig out the specific
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index d9994d7fd37adbb..50f92cc8836b1e1 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -283,7 +283,8 @@ Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD,
   return Response::ChangeDecl(FTD->getLexicalDeclContext());
 }
 
-Response HandleRecordDecl(const CXXRecordDecl *Rec,
+Response HandleRecordDecl(Sema &SemaRef,
+                          const CXXRecordDecl *Rec,
                           MultiLevelTemplateArgumentList &Result,
                           ASTContext &Context,
                           bool ForConstraintInstantiation) {
@@ -314,9 +315,42 @@ Response HandleRecordDecl(const CXXRecordDecl *Rec,
 
   // This is to make sure we pick up the VarTemplateSpecializationDecl that this
   // lambda is defined inside of.
-  if (Rec->isLambda())
+  if (Rec->isLambda()) {
     if (const Decl *LCD = Rec->getLambdaContextDecl())
       return Response::ChangeDecl(LCD);
+    if (ForConstraintInstantiation && !SemaRef.CodeSynthesisContexts.empty()) {
+      for (auto &CSC : llvm::reverse(SemaRef.CodeSynthesisContexts)) {
+        if (CSC.Kind == Sema::CodeSynthesisContext::SynthesisKind::TypeAliasTemplateInstantiation) {
+          auto *TATD = cast<TypeAliasTemplateDecl>(CSC.Entity), *CurrentTATD = TATD;
+          FunctionDecl *LambdaCallOperator = Rec->getLambdaCallOperator();
+          while (true) {
+            auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
+                LambdaCallOperator->getDescribedTemplate());
+            if (FTD && FTD->getInstantiatedFromMemberTemplate()) {
+              LambdaCallOperator =
+                  FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
+            } else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
+                                        ->getInstantiatedFromMemberFunction())
+              LambdaCallOperator = Prev;
+            else
+              break;
+          }
+          while (TATD->getInstantiatedFromMemberTemplate())
+            TATD = TATD->getInstantiatedFromMemberTemplate();
+          // Constraint template parameters have a deeper depth.
+          if (cast<CXXRecordDecl>(LambdaCallOperator->getDeclContext())
+                      ->getTemplateDepth() == TATD->getTemplateDepth() &&
+              getLambdaAwareParentOfDeclContext(LambdaCallOperator) ==
+                  TATD->getDeclContext()) {
+            Result.addOuterTemplateArguments(CurrentTATD,
+                                             CSC.template_arguments(),
+                                             /*Final=*/false);
+            return Response::ChangeDecl(CurrentTATD->getDeclContext());
+          }
+        }
+      }
+    }
+  }
 
   return Response::UseNextDecl(Rec);
 }
@@ -413,7 +447,7 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
       R = HandleFunction(Function, Result, Pattern, RelativeToPrimary,
                          ForConstraintInstantiation);
     } else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) {
-      R = HandleRecordDecl(Rec, Result, Context, ForConstraintInstantiation);
+      R = HandleRecordDecl(*this, Rec, Result, Context, ForConstraintInstantiation);
     } else if (const auto *CSD =
                    dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) {
       R = HandleImplicitConceptSpecializationDecl(CSD, Result);
@@ -470,6 +504,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
   case BuildingBuiltinDumpStructCall:
   case LambdaExpressionSubstitution:
   case BuildingDeductionGuides:
+  case TypeAliasTemplateInstantiation:
     return false;
 
   // This function should never be called when Kind's value is Memoization.
@@ -615,6 +650,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
           PointOfInstantiation, InstantiationRange, Param, Template,
           TemplateArgs) {}
 
+Sema::InstantiatingTemplate::InstantiatingTemplate(
+    Sema &SemaRef, SourceLocation PointOfInstantiation,
+    TypeAliasTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
+    SourceRange InstantiationRange)
+    : InstantiatingTemplate(
+          SemaRef, Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation,
+          PointOfInstantiation, InstantiationRange, /*Entity=*/Template,
+          nullptr, TemplateArgs) {}
+
 Sema::InstantiatingTemplate::InstantiatingTemplate(
     Sema &SemaRef, SourceLocation PointOfInstantiation, TemplateDecl *Template,
     NamedDecl *Param, ArrayRef<TemplateArgument> TemplateArgs,
@@ -1132,6 +1176,8 @@ void Sema::PrintInstantiationStack() {
       Diags.Report(Active->PointOfInstantiation,
                    diag::note_building_deduction_guide_here);
       break;
+    case CodeSynthesisContext::TypeAliasTemplateInstantiation:
+      break;
     }
   }
 }
@@ -1209,6 +1255,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
       break;
 
     case CodeSynthesisContext::Memoization:
+    case CodeSynthesisContext::TypeAliasTemplateInstantiation:
       break;
     }
 
@@ -1533,6 +1580,17 @@ namespace {
                                            SubstTemplateTypeParmPackTypeLoc TL,
                                            bool SuppressObjCLifetime);
 
+    CXXRecordDecl::LambdaDependencyKind
+    ComputeLambdaDependency(LambdaScopeInfo *LSI) {
+      auto &CCS = SemaRef.CodeSynthesisContexts.back();
+      if (CCS.Kind == Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
+        unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth();
+        if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
+          return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
+      }
+      return inherited::ComputeLambdaDependency(LSI);
+    }
+
     ExprResult TransformLambdaExpr(LambdaExpr *E) {
       LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
       Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 20c2c93ac9c7b41..a4277d6d9efed5a 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1083,6 +1083,11 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
     return nullptr;
 
   TypeAliasDecl *Pattern = D->getTemplatedDecl();
+  Sema::InstantiatingTemplate InstTemplate(
+      SemaRef, Pattern->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), D,
+      D->getTemplateDepth() >= TemplateArgs.getNumSubstitutedLevels()
+          ? ArrayRef<TemplateArgument>()
+          : TemplateArgs.getInnermost());
 
   TypeAliasTemplateDecl *PrevAliasTemplate = nullptr;
   if (getPreviousDeclForInstantiation<TypedefNameDecl>(Pattern)) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 2d22692f3ab7502..8c9a80729f44c89 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -767,6 +767,10 @@ class TreeTransform {
   /// the body.
   StmtResult SkipLambdaBody(LambdaExpr *E, Stmt *Body);
 
+  CXXRecordDecl::LambdaDependencyKind ComputeLambdaDependency(LambdaScopeInfo *LSI) {
+    return static_cast<CXXRecordDecl::LambdaDependencyKind>(LSI->Lambda->getLambdaDependencyKind());
+  }
+
   QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL);
 
   StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr);
@@ -13936,6 +13940,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
                                     /*IsInstantiation*/ true);
   SavedContext.pop();
 
+  DependencyKind = getDerived().ComputeLambdaDependency(&LSICopy);
+  Class->setLambdaDependencyKind(DependencyKind);
+  Class->setTypeForDecl(nullptr);
+  getSema().Context.getTypeDeclType(Class);
+
   return getSema().BuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(),
                                    &LSICopy);
 }
diff --git a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
new file mode 100644
index 000000000000000..c3931287cb6404d
--- /dev/null
+++ b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
@@ -0,0 +1,82 @@
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s
+namespace lambda_calls {
+
+template <class>
+concept True = true;
+
+template <class>
+concept False = false; // #False
+
+template <class T> struct S {
+  template <class... U> using type = decltype([](U...) {}(U()...));
+  template <class U> using type2 = decltype([](auto) {}(1));
+  template <class U> using type3 = decltype([](True auto) {}(1));
+  template <class>
+  using type4 = decltype([](auto... pack) { return sizeof...(pack); }(1, 2));
+
+  template <class U> using type5 = decltype([](False auto...) {}(1)); // #Type5
+
+  template <class U>
+  using type6 = decltype([]<True> {}.template operator()<char>());
+  template <class U>
+  using type7 = decltype([]<False> {}.template operator()<char>()); // #Type7
+
+  template <class U>
+  using type8 = decltype([]() // #Type8
+                           requires(sizeof(U) == 32) // #Type8-requirement
+                         {}());
+
+  template <class... U>
+  using type9 = decltype([]<True>(U...) {}.template operator()<char>(U()...));
+  // https://github.com/llvm/llvm-project/issues/76674
+  template <class U>
+  using type10 = decltype([]<class V> { return V(); }.template operator()<U>());
+
+  template <class U> using type11 = decltype([] { return U{}; });
+};
+
+template <class> using Meow = decltype([]<True> {}.template operator()<int>());
+
+template <class... U>
+using MeowMeow = decltype([]<True>(U...) {}.template operator()<char>(U()...));
+
+// https://github.com/llvm/llvm-project/issues/70601
+template <class> using U = decltype([]<True> {}.template operator()<int>());
+
+U<int> foo();
+
+void bar() {
+  using T = S<int>::type<int, int, int>;
+  using T2 = S<int>::type2<int>;
+  using T3 = S<int>::type3<char>;
+  using T4 = S<int>::type4<void>;
+  using T5 = S<int>::type5<void>; // #T5
+  // expected-error@#Type5 {{no matching function for call}}
+  // expected-note@#T5 {{type alias 'type5' requested here}}
+  // expected-note@#Type5 {{constraints not satisfied [with auto:1 = <int>]}}
+  // expected-note@#Type5 {{because 'int' does not satisfy 'False'}}
+  // expected-note@#False {{because 'false' evaluated to false}}
+
+  using T6 = S<int>::type6<void>;
+  using T7 = S<int>::type7<void>; // #T7
+  // expected-error@#Type7 {{no matching member function for call}}
+  // expected-note@#T7 {{type alias 'type7' requested here}}
+  // expected-note@#Type7 {{constraints not satisfied [with $0 = char]}}
+  // expected-note@#Type7 {{because 'char' does not satisfy 'False'}}
+  // expected-note@#False {{because 'false' evaluated to false}}
+
+  using T8 = S<int>::type8<char>; // #T8
+  // expected-error@#Type8 {{no matching function for call}}
+  // expected-note@#T8 {{type alias 'type8' requested here}}
+  // expected-note@#Type8 {{constraints not satisfied}}
+  // expected-note@#Type8-requirement {{because 'sizeof(char) == 32' (1 == 32) evaluated to false}}
+
+  using T9 = S<int>::type9<long, long, char>;
+  using T10 = S<int>::type10<int>;
+  using T11 = S<int>::type11<int>;
+  int x = T11()();
+  using T12 = Meow<int>;
+  using T13 = MeowMeow<char, int, long, unsigned>;
+}
+
+} // namespace lambda_calls

>From 303fd1bc662f2dbb4a63cc28744a1e3c92b7a34a Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 20 Feb 2024 16:16:09 +0800
Subject: [PATCH 2/7] Format & Comments

---
 clang/include/clang/Sema/Sema.h            |  1 +
 clang/lib/Sema/SemaConcept.cpp             | 19 +++--
 clang/lib/Sema/SemaTemplate.cpp            |  5 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp | 96 +++++++++++++++-------
 clang/lib/Sema/TreeTransform.h             | 26 +++++-
 5 files changed, 105 insertions(+), 42 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index a5520e19a14327d..eefb7db3b7ffe85 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10146,6 +10146,7 @@ class Sema final {
       /// We are building deduction guides for a class.
       BuildingDeductionGuides,
 
+      /// We are instantiating a type alias template declaration.
       TypeAliasTemplateInstantiation,
     } Kind;
 
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 0b84d83b4fb4b81..0149766584bf251 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -615,13 +615,18 @@ bool Sema::SetupConstraintScope(
     // reference the original primary template.
     // We walk up the instantiated template chain so that nested lambdas get
     // handled properly.
-    FunctionTemplateDecl *FromMemTempl =
-        PrimaryTemplate->getInstantiatedFromMemberTemplate();
-    while (FromMemTempl && FromMemTempl->getInstantiatedFromMemberTemplate())
-      FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate();
-    if (FromMemTempl && addInstantiatedParametersToScope(
-                            FD, FromMemTempl->getTemplatedDecl(), Scope, MLTAL))
-      return true;
+    // Note that we shall not collect instantiated parameters from
+    // 'intermediate' transformed function templates but the primary template
+    // for which we have built up the template arguments relative to. Otherwise,
+    // we may have mismatched template parameter depth!
+    if (FunctionTemplateDecl *FromMemTempl =
+            PrimaryTemplate->getInstantiatedFromMemberTemplate()) {
+      while (FromMemTempl->getInstantiatedFromMemberTemplate())
+        FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate();
+      if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
+                                           Scope, MLTAL))
+        return true;
+    }
 
     return false;
   }
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 30499df1797fcfa..8856356e5214289 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4344,8 +4344,9 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
       return QualType();
 
     InstantiatingTemplate InstTemplate(
-        *this, Pattern->getTypeSourceInfo()->getTypeLoc().getBeginLoc(),
-        AliasTemplate, TemplateArgLists.getInnermost());
+        *this, /*PointOfInstantiation=*/AliasTemplate->getBeginLoc(),
+        /*Template=*/AliasTemplate,
+        /*TemplateArgs=*/TemplateArgLists.getInnermost());
     CanonType =
         SubstType(Pattern->getUnderlyingType(), TemplateArgLists,
                   AliasTemplate->getLocation(), AliasTemplate->getDeclName());
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 50f92cc8836b1e1..ad09cc8252d4fab 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -283,8 +283,7 @@ Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD,
   return Response::ChangeDecl(FTD->getLexicalDeclContext());
 }
 
-Response HandleRecordDecl(Sema &SemaRef,
-                          const CXXRecordDecl *Rec,
+Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
                           MultiLevelTemplateArgumentList &Result,
                           ASTContext &Context,
                           bool ForConstraintInstantiation) {
@@ -318,35 +317,68 @@ Response HandleRecordDecl(Sema &SemaRef,
   if (Rec->isLambda()) {
     if (const Decl *LCD = Rec->getLambdaContextDecl())
       return Response::ChangeDecl(LCD);
+    // Attempt to retrieve the template arguments for a using alias declaration.
+    // This is necessary for constraint checking, since we always keep
+    // constraints relative to the primary template.
     if (ForConstraintInstantiation && !SemaRef.CodeSynthesisContexts.empty()) {
       for (auto &CSC : llvm::reverse(SemaRef.CodeSynthesisContexts)) {
-        if (CSC.Kind == Sema::CodeSynthesisContext::SynthesisKind::TypeAliasTemplateInstantiation) {
-          auto *TATD = cast<TypeAliasTemplateDecl>(CSC.Entity), *CurrentTATD = TATD;
-          FunctionDecl *LambdaCallOperator = Rec->getLambdaCallOperator();
-          while (true) {
-            auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
-                LambdaCallOperator->getDescribedTemplate());
-            if (FTD && FTD->getInstantiatedFromMemberTemplate()) {
-              LambdaCallOperator =
-                  FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
-            } else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
-                                        ->getInstantiatedFromMemberFunction())
-              LambdaCallOperator = Prev;
-            else
-              break;
-          }
-          while (TATD->getInstantiatedFromMemberTemplate())
-            TATD = TATD->getInstantiatedFromMemberTemplate();
-          // Constraint template parameters have a deeper depth.
-          if (cast<CXXRecordDecl>(LambdaCallOperator->getDeclContext())
-                      ->getTemplateDepth() == TATD->getTemplateDepth() &&
-              getLambdaAwareParentOfDeclContext(LambdaCallOperator) ==
-                  TATD->getDeclContext()) {
-            Result.addOuterTemplateArguments(CurrentTATD,
-                                             CSC.template_arguments(),
-                                             /*Final=*/false);
-            return Response::ChangeDecl(CurrentTATD->getDeclContext());
-          }
+        if (CSC.Kind != Sema::CodeSynthesisContext::SynthesisKind::
+                            TypeAliasTemplateInstantiation)
+          continue;
+        auto *TATD = cast<TypeAliasTemplateDecl>(CSC.Entity),
+             *CurrentTATD = TATD;
+        FunctionDecl *LambdaCallOperator = Rec->getLambdaCallOperator();
+        // Retrieve the 'primary' template for a lambda call operator. It's
+        // unfortunate that we only have the mappings of call operators rather
+        // than lambda classes.
+        while (true) {
+          auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
+              LambdaCallOperator->getDescribedTemplate());
+          if (FTD && FTD->getInstantiatedFromMemberTemplate()) {
+            LambdaCallOperator =
+                FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
+          } else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
+                                      ->getInstantiatedFromMemberFunction())
+            LambdaCallOperator = Prev;
+          else
+            break;
+        }
+        // Same applies for type alias Decl. We perform this to obtain the
+        // "canonical" template parameter depths.
+        while (TATD->getInstantiatedFromMemberTemplate())
+          TATD = TATD->getInstantiatedFromMemberTemplate();
+        // Tell if we're currently inside of a lambda expression that is
+        // surrounded by a using alias declaration. e.g.
+        //   template <class> using type = decltype([](auto) { ^ }());
+        // By checking if:
+        //  1. The lambda expression and the using alias declaration share the
+        //  same declaration context.
+        //  2. They have the same template depth.
+        // Then we assume the template arguments from the using alias
+        // declaration are essential for constraint instantiation. We have to do
+        // so since a TypeAliasTemplateDecl (or a TypeAliasDecl) is never a
+        // DeclContext, nor does it have an associated specialization Decl from
+        // which we could collect these template arguments.
+        if (cast<CXXRecordDecl>(LambdaCallOperator->getDeclContext())
+                    ->getTemplateDepth() == TATD->getTemplateDepth() &&
+            getLambdaAwareParentOfDeclContext(LambdaCallOperator) ==
+                TATD->getDeclContext()) {
+          Result.addOuterTemplateArguments(CurrentTATD,
+                                           CSC.template_arguments(),
+                                           /*Final=*/false);
+          // Visit the parent of the current type alias declaration rather than
+          // the lambda thereof. We have the following case:
+          // struct S {
+          //  template <class> using T = decltype([]<Concept> {} ());
+          // };
+          // void foo() {
+          //   S::T var;
+          // }
+          // The instantiated lambda expression (which we're visiting at 'var')
+          // has a function DeclContext 'foo' rather than the Record DeclContext
+          // S. This seems to be an oversight that we may want to set a Sema
+          // Context from the CXXScopeSpec before substituting into T to me.
+          return Response::ChangeDecl(CurrentTATD->getDeclContext());
         }
       }
     }
@@ -447,7 +479,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
       R = HandleFunction(Function, Result, Pattern, RelativeToPrimary,
                          ForConstraintInstantiation);
     } else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) {
-      R = HandleRecordDecl(*this, Rec, Result, Context, ForConstraintInstantiation);
+      R = HandleRecordDecl(*this, Rec, Result, Context,
+                           ForConstraintInstantiation);
     } else if (const auto *CSD =
                    dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) {
       R = HandleImplicitConceptSpecializationDecl(CSD, Result);
@@ -1583,7 +1616,8 @@ namespace {
     CXXRecordDecl::LambdaDependencyKind
     ComputeLambdaDependency(LambdaScopeInfo *LSI) {
       auto &CCS = SemaRef.CodeSynthesisContexts.back();
-      if (CCS.Kind == Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
+      if (CCS.Kind ==
+          Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
         unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth();
         if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
           return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 8c9a80729f44c89..5cacd5b368b2fb2 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -767,8 +767,10 @@ class TreeTransform {
   /// the body.
   StmtResult SkipLambdaBody(LambdaExpr *E, Stmt *Body);
 
-  CXXRecordDecl::LambdaDependencyKind ComputeLambdaDependency(LambdaScopeInfo *LSI) {
-    return static_cast<CXXRecordDecl::LambdaDependencyKind>(LSI->Lambda->getLambdaDependencyKind());
+  CXXRecordDecl::LambdaDependencyKind
+  ComputeLambdaDependency(LambdaScopeInfo *LSI) {
+    return static_cast<CXXRecordDecl::LambdaDependencyKind>(
+        LSI->Lambda->getLambdaDependencyKind());
   }
 
   QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL);
@@ -13940,8 +13942,28 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
                                     /*IsInstantiation*/ true);
   SavedContext.pop();
 
+  // Recompute the dependency of the lambda so that we can defer the lambda call
+  // construction until after we have sufficient template arguments. For
+  // example, template <class> struct S {
+  //   template <class U>
+  //   using Type = decltype([](U){}(42.0));
+  // };
+  // void foo() {
+  //   using T = S<int>::Type<float>;
+  //             ^~~~~~
+  // }
+  // We would end up here from instantiating the S<int> as we're ensuring the
+  // completeness. That would make us transform the lambda call expression
+  // despite the fact that we don't see the argument for U yet. We have a
+  // mechanism that circumvents the semantic checking if the CallExpr is
+  // dependent. We can harness that by recomputing the lambda dependency from
+  // the instantiation arguments. I'm putting it here rather than the above
+  // since we can see transformed lambda parameters in case that they're
+  // useful for calculation.
   DependencyKind = getDerived().ComputeLambdaDependency(&LSICopy);
   Class->setLambdaDependencyKind(DependencyKind);
+  // Clean up the type cache created previously. Then, we re-create a type for
+  // such Decl with the new DependencyKind.
   Class->setTypeForDecl(nullptr);
   getSema().Context.getTypeDeclType(Class);
 

>From 62a00af61f3c56778fd1a6679ffba9a842aad834 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 20 Feb 2024 21:41:19 +0800
Subject: [PATCH 3/7] fixup

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

diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index a4277d6d9efed5a..1de64ef11381fc4 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1084,10 +1084,12 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
 
   TypeAliasDecl *Pattern = D->getTemplatedDecl();
   Sema::InstantiatingTemplate InstTemplate(
-      SemaRef, Pattern->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), D,
-      D->getTemplateDepth() >= TemplateArgs.getNumSubstitutedLevels()
+      SemaRef, D->getBeginLoc(), D,
+      D->getTemplateDepth() >= TemplateArgs.getNumLevels()
           ? ArrayRef<TemplateArgument>()
-          : TemplateArgs.getInnermost());
+          : (TemplateArgs.begin() + TemplateArgs.getNumLevels() - 1 -
+             D->getTemplateDepth())
+                ->Args);
 
   TypeAliasTemplateDecl *PrevAliasTemplate = nullptr;
   if (getPreviousDeclForInstantiation<TypedefNameDecl>(Pattern)) {

>From d3a76a1d09a4b7f86bdcab9fb4725d42eb883e79 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 8 Mar 2024 15:52:08 +0800
Subject: [PATCH 4/7] Slightly refactor & Fix GH82104

---
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 186 +++++++++++-------
 clang/lib/Sema/TreeTransform.h                |  35 +++-
 .../alias-template-with-lambdas.cpp           |  11 ++
 3 files changed, 155 insertions(+), 77 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index ad09cc8252d4fab..ab295de350ceb93 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -80,6 +80,81 @@ struct Response {
     return R;
   }
 };
+
+// Retrieve the primary template for a lambda call operator. It's
+// unfortunate that we only have the mappings of call operators rather
+// than lambda classes.
+const FunctionDecl *
+getPrimaryTemplateOfGenericLambda(const FunctionDecl *LambdaCallOperator) {
+  while (true) {
+    if (auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
+            LambdaCallOperator->getDescribedTemplate());
+        FTD && FTD->getInstantiatedFromMemberTemplate()) {
+      LambdaCallOperator =
+          FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
+    } else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
+                                ->getInstantiatedFromMemberFunction())
+      LambdaCallOperator = Prev;
+    else
+      break;
+  }
+  return LambdaCallOperator;
+}
+
+struct EnclosingTypeAliasTemplateDetails {
+  TypeAliasTemplateDecl *Template = nullptr;
+  TypeAliasTemplateDecl *PrimaryTypeAliasDecl = nullptr;
+  ArrayRef<TemplateArgument> AssociatedTemplateArguments;
+
+  explicit operator bool() noexcept { return Template; }
+};
+
+// Find the enclosing type alias template Decl from CodeSynthesisContexts, as
+// well as its primary template and instantiating template arguments.
+EnclosingTypeAliasTemplateDetails
+getEnclosingTypeAliasTemplateDecl(Sema &SemaRef) {
+  for (auto &CSC : llvm::reverse(SemaRef.CodeSynthesisContexts)) {
+    if (CSC.Kind != Sema::CodeSynthesisContext::SynthesisKind::
+                        TypeAliasTemplateInstantiation)
+      continue;
+    EnclosingTypeAliasTemplateDetails Result;
+    auto *TATD = cast<TypeAliasTemplateDecl>(CSC.Entity),
+         *Next = TATD->getInstantiatedFromMemberTemplate();
+    Result = {
+        /*Template=*/TATD,
+        /*PrimaryTypeAliasDecl=*/TATD,
+        /*AssociatedTemplateArguments=*/CSC.template_arguments(),
+    };
+    while (Next) {
+      Result.PrimaryTypeAliasDecl = Next;
+      Next = Next->getInstantiatedFromMemberTemplate();
+    }
+    return Result;
+  }
+  return {};
+}
+
+// Check if we are currently inside of a lambda expression that is
+// surrounded by a using alias declaration. e.g.
+//   template <class> using type = decltype([](auto) { ^ }());
+// By checking if:
+//  1. The lambda expression and the using alias declaration share the
+//  same declaration context.
+//  2. They have the same template depth.
+// We have to do so since a TypeAliasTemplateDecl (or a TypeAliasDecl) is never
+// a DeclContext, nor does it have an associated specialization Decl from which
+// we could collect these template arguments.
+bool isLambdaEnclosedByTypeAliasDecl(
+    const FunctionDecl *PrimaryLambdaCallOperator,
+    const TypeAliasTemplateDecl *PrimaryTypeAliasDecl) {
+  return cast<CXXRecordDecl>(PrimaryLambdaCallOperator->getDeclContext())
+                 ->getTemplateDepth() ==
+             PrimaryTypeAliasDecl->getTemplateDepth() &&
+         getLambdaAwareParentOfDeclContext(
+             const_cast<FunctionDecl *>(PrimaryLambdaCallOperator)) ==
+             PrimaryTypeAliasDecl->getDeclContext();
+}
+
 // Add template arguments from a variable template instantiation.
 Response
 HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec,
@@ -176,7 +251,7 @@ HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec,
   return Response::UseNextDecl(ClassTemplSpec);
 }
 
-Response HandleFunction(const FunctionDecl *Function,
+Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
                         MultiLevelTemplateArgumentList &Result,
                         const FunctionDecl *Pattern, bool RelativeToPrimary,
                         bool ForConstraintInstantiation) {
@@ -207,8 +282,23 @@ Response HandleFunction(const FunctionDecl *Function,
 
     // If this function is a generic lambda specialization, we are done.
     if (!ForConstraintInstantiation &&
-        isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
+        isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) {
+      // TypeAliasTemplateDecls should be taken into account, e.g.
+      // when we're deducing the return type of a lambda.
+      //
+      // template <class> int Value = 0;
+      // template <class T>
+      // using T = decltype([]<int U = 0>() { return Value<T>; }());
+      //
+      if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
+        if (isLambdaEnclosedByTypeAliasDecl(
+                /*PrimaryLambdaCallOperator=*/getPrimaryTemplateOfGenericLambda(
+                    Function),
+                /*PrimaryTypeAliasDecl=*/TypeAlias.PrimaryTypeAliasDecl))
+          return Response::UseNextDecl(Function);
+      }
       return Response::Done();
+    }
 
   } else if (Function->getDescribedFunctionTemplate()) {
     assert(
@@ -312,74 +402,36 @@ Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
     return Response::ChangeDecl(Rec->getLexicalDeclContext());
   }
 
-  // This is to make sure we pick up the VarTemplateSpecializationDecl that this
-  // lambda is defined inside of.
+  // This is to make sure we pick up the VarTemplateSpecializationDecl or the
+  // TypeAliasTemplateDecl that this lambda is defined inside of.
   if (Rec->isLambda()) {
     if (const Decl *LCD = Rec->getLambdaContextDecl())
       return Response::ChangeDecl(LCD);
-    // Attempt to retrieve the template arguments for a using alias declaration.
+    // Retrieve the template arguments for a using alias declaration.
     // This is necessary for constraint checking, since we always keep
     // constraints relative to the primary template.
-    if (ForConstraintInstantiation && !SemaRef.CodeSynthesisContexts.empty()) {
-      for (auto &CSC : llvm::reverse(SemaRef.CodeSynthesisContexts)) {
-        if (CSC.Kind != Sema::CodeSynthesisContext::SynthesisKind::
-                            TypeAliasTemplateInstantiation)
-          continue;
-        auto *TATD = cast<TypeAliasTemplateDecl>(CSC.Entity),
-             *CurrentTATD = TATD;
-        FunctionDecl *LambdaCallOperator = Rec->getLambdaCallOperator();
-        // Retrieve the 'primary' template for a lambda call operator. It's
-        // unfortunate that we only have the mappings of call operators rather
-        // than lambda classes.
-        while (true) {
-          auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
-              LambdaCallOperator->getDescribedTemplate());
-          if (FTD && FTD->getInstantiatedFromMemberTemplate()) {
-            LambdaCallOperator =
-                FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
-          } else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
-                                      ->getInstantiatedFromMemberFunction())
-            LambdaCallOperator = Prev;
-          else
-            break;
-        }
-        // Same applies for type alias Decl. We perform this to obtain the
-        // "canonical" template parameter depths.
-        while (TATD->getInstantiatedFromMemberTemplate())
-          TATD = TATD->getInstantiatedFromMemberTemplate();
-        // Tell if we're currently inside of a lambda expression that is
-        // surrounded by a using alias declaration. e.g.
-        //   template <class> using type = decltype([](auto) { ^ }());
-        // By checking if:
-        //  1. The lambda expression and the using alias declaration share the
-        //  same declaration context.
-        //  2. They have the same template depth.
-        // Then we assume the template arguments from the using alias
-        // declaration are essential for constraint instantiation. We have to do
-        // so since a TypeAliasTemplateDecl (or a TypeAliasDecl) is never a
-        // DeclContext, nor does it have an associated specialization Decl from
-        // which we could collect these template arguments.
-        if (cast<CXXRecordDecl>(LambdaCallOperator->getDeclContext())
-                    ->getTemplateDepth() == TATD->getTemplateDepth() &&
-            getLambdaAwareParentOfDeclContext(LambdaCallOperator) ==
-                TATD->getDeclContext()) {
-          Result.addOuterTemplateArguments(CurrentTATD,
-                                           CSC.template_arguments(),
-                                           /*Final=*/false);
-          // Visit the parent of the current type alias declaration rather than
-          // the lambda thereof. We have the following case:
-          // struct S {
-          //  template <class> using T = decltype([]<Concept> {} ());
-          // };
-          // void foo() {
-          //   S::T var;
-          // }
-          // The instantiated lambda expression (which we're visiting at 'var')
-          // has a function DeclContext 'foo' rather than the Record DeclContext
-          // S. This seems to be an oversight that we may want to set a Sema
-          // Context from the CXXScopeSpec before substituting into T to me.
-          return Response::ChangeDecl(CurrentTATD->getDeclContext());
-        }
+    if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
+      const FunctionDecl *PrimaryLambdaCallOperator =
+          getPrimaryTemplateOfGenericLambda(Rec->getLambdaCallOperator());
+      if (isLambdaEnclosedByTypeAliasDecl(PrimaryLambdaCallOperator,
+                                          TypeAlias.PrimaryTypeAliasDecl)) {
+        Result.addOuterTemplateArguments(TypeAlias.Template,
+                                         TypeAlias.AssociatedTemplateArguments,
+                                         /*Final=*/false);
+        // Visit the parent of the current type alias declaration rather than
+        // the lambda thereof.
+        // E.g., in the following example:
+        // struct S {
+        //  template <class> using T = decltype([]<Concept> {} ());
+        // };
+        // void foo() {
+        //   S::T var;
+        // }
+        // The instantiated lambda expression (which we're visiting at 'var')
+        // has a function DeclContext 'foo' rather than the Record DeclContext
+        // S. This seems to be an oversight to me that we may want to set a
+        // Sema Context from the CXXScopeSpec before substituting into T.
+        return Response::ChangeDecl(TypeAlias.Template->getDeclContext());
       }
     }
   }
@@ -476,7 +528,7 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
       R = HandleClassTemplateSpec(ClassTemplSpec, Result,
                                   SkipForSpecialization);
     } else if (const auto *Function = dyn_cast<FunctionDecl>(CurDecl)) {
-      R = HandleFunction(Function, Result, Pattern, RelativeToPrimary,
+      R = HandleFunction(*this, Function, Result, Pattern, RelativeToPrimary,
                          ForConstraintInstantiation);
     } else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) {
       R = HandleRecordDecl(*this, Rec, Result, Context,
@@ -690,7 +742,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
     : InstantiatingTemplate(
           SemaRef, Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation,
           PointOfInstantiation, InstantiationRange, /*Entity=*/Template,
-          nullptr, TemplateArgs) {}
+          /*Template=*/nullptr, TemplateArgs) {}
 
 Sema::InstantiatingTemplate::InstantiatingTemplate(
     Sema &SemaRef, SourceLocation PointOfInstantiation, TemplateDecl *Template,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 5cacd5b368b2fb2..c9c70c48fd76c60 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -13943,8 +13943,10 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
   SavedContext.pop();
 
   // Recompute the dependency of the lambda so that we can defer the lambda call
-  // construction until after we have sufficient template arguments. For
-  // example, template <class> struct S {
+  // construction until after we have all the necessary template arguments. For
+  // example, given
+  //
+  // template <class> struct S {
   //   template <class U>
   //   using Type = decltype([](U){}(42.0));
   // };
@@ -13952,14 +13954,27 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
   //   using T = S<int>::Type<float>;
   //             ^~~~~~
   // }
-  // We would end up here from instantiating the S<int> as we're ensuring the
-  // completeness. That would make us transform the lambda call expression
-  // despite the fact that we don't see the argument for U yet. We have a
-  // mechanism that circumvents the semantic checking if the CallExpr is
-  // dependent. We can harness that by recomputing the lambda dependency from
-  // the instantiation arguments. I'm putting it here rather than the above
-  // since we can see transformed lambda parameters in case that they're
-  // useful for calculation.
+  //
+  // We would end up here from instantiating S<int> when ensuring its
+  // completeness. That would transform the lambda call expression regardless of
+  // the absence of the corresponding argument for U.
+  //
+  // Going ahead with unsubstituted type U makes things worse: we would soon
+  // compare the argument type (which is float) against the parameter U
+  // somewhere in Sema::BuildCallExpr. Then we would quickly run into a bogus
+  // error suggesting unmatched types 'U' and 'float'!
+  //
+  // That said, everything will be fine if we defer that semantic checking.
+  // Fortunately, we have such a mechanism that bypasses it if the CallExpr is
+  // dependent. Since the CallExpr's dependency boils down to the lambda's
+  // dependency in this case, we can harness that by recomputing the dependency
+  // from the instantiation arguments.
+  //
+  // FIXME: Creating the type of a lambda requires us to have a dependency
+  // value, which happens before its substitution. We update its dependency
+  // *after* the substitution in case we can't decide the dependency
+  // so early, e.g. because we want to see if any of the *substituted*
+  // parameters are dependent.
   DependencyKind = getDerived().ComputeLambdaDependency(&LSICopy);
   Class->setLambdaDependencyKind(DependencyKind);
   // Clean up the type cache created previously. Then, we re-create a type for
diff --git a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
index c3931287cb6404d..128fe2f8f739035 100644
--- a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
+++ b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
@@ -79,4 +79,15 @@ void bar() {
   using T13 = MeowMeow<char, int, long, unsigned>;
 }
 
+namespace GH82104 {
+
+template <typename, typename...> int Zero = 0;
+
+template <typename T, typename...U>
+using T14 = decltype([]<int V = 0>() { return Zero<T, U...>; }());
+
+template <typename T> using T15 = T14<T, T>;
+
+} // namespace GH82104
+
 } // namespace lambda_calls

>From be4164175966f92242bea8cfb0e56b8822a532be Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 8 Mar 2024 18:08:33 +0800
Subject: [PATCH 5/7] Mention GH82104 in the release note

---
 clang/docs/ReleaseNotes.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f5dc956cf5fc4aa..98ec1c2f258e214 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -345,7 +345,7 @@ Bug Fixes to C++ Support
   Fixes (`#82509 <https://github.com/llvm/llvm-project/issues/82509>`_)
   and (`#74494 <https://github.com/llvm/llvm-project/issues/74494>`_)
 - Clang now supports direct lambda calls inside of a type alias template declarations.
-  This addresses (#GH70601), (#GH76674), (#GH79555), (#GH81145) and so on.
+  This addresses (#GH70601), (#GH76674), (#GH79555), (#GH81145), (#GH82104) and so on.
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^

>From 2e95a41b60b310c67dd7eafa987b5b63ae461ee6 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 8 Mar 2024 18:11:03 +0800
Subject: [PATCH 6/7] Rephrase & Format

---
 clang/lib/Sema/SemaConcept.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 0149766584bf251..c5594a5f047f6c1 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -615,10 +615,8 @@ bool Sema::SetupConstraintScope(
     // reference the original primary template.
     // We walk up the instantiated template chain so that nested lambdas get
     // handled properly.
-    // Note that we shall not collect instantiated parameters from
-    // 'intermediate' transformed function templates but the primary template
-    // for which we have built up the template arguments relative to. Otherwise,
-    // we may have mismatched template parameter depth!
+    // We should only collect instantiated parameters from the primary template.
+    // Otherwise, we may have mismatched template parameter depth!
     if (FunctionTemplateDecl *FromMemTempl =
             PrimaryTemplate->getInstantiatedFromMemberTemplate()) {
       while (FromMemTempl->getInstantiatedFromMemberTemplate())

>From 4fcafbea82933c010ddcfbe474fd26087476206b Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 13 Mar 2024 12:27:08 +0800
Subject: [PATCH 7/7] Validate deduced types

---
 .../SemaTemplate/alias-template-with-lambdas.cpp     | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
index 128fe2f8f739035..ff94031e4d86f11 100644
--- a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
+++ b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp
@@ -77,6 +77,16 @@ void bar() {
   int x = T11()();
   using T12 = Meow<int>;
   using T13 = MeowMeow<char, int, long, unsigned>;
+
+  static_assert(__is_same(T, void));
+  static_assert(__is_same(T2, void));
+  static_assert(__is_same(T3, void));
+  static_assert(__is_same(T4, decltype(sizeof(0))));
+  static_assert(__is_same(T6, void));
+  static_assert(__is_same(T9, void));
+  static_assert(__is_same(T10, int));
+  static_assert(__is_same(T12, void));
+  static_assert(__is_same(T13, void));
 }
 
 namespace GH82104 {
@@ -88,6 +98,8 @@ using T14 = decltype([]<int V = 0>() { return Zero<T, U...>; }());
 
 template <typename T> using T15 = T14<T, T>;
 
+static_assert(__is_same(T15<char>, int));
+
 } // namespace GH82104
 
 } // namespace lambda_calls



More information about the cfe-commits mailing list