[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 Feb 20 05:41:47 PST 2024


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

>From 0f97fae5d1ba4debe04824e5d2d98598504d003d 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/3] The lambda call inside of a type alias

---
 clang/docs/ReleaseNotes.rst                   |  5 ++
 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, 189 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 649ad655905af2..7988912faa2075 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -267,6 +267,11 @@ Bug Fixes to C++ Support
   was only accepted at namespace scope but not at local function scope.
 - Clang no longer tries to call consteval constructors at runtime when they appear in a member initializer.
   (`#782154 <https://github.com/llvm/llvm-project/issues/82154>`_`)
+- Clang now supports direct lambda calls inside of a type alias template declarations.
+  This addresses (`#70601 <https://github.com/llvm/llvm-project/issues/70601>`_),
+  (`#76674 <https://github.com/llvm/llvm-project/issues/76674>`_),
+  (`#79555 <https://github.com/llvm/llvm-project/issues/79555>`_),
+  (`#81145 <https://github.com/llvm/llvm-project/issues/81145>`_), 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 9cebaff63bb0db..7aed4d5cbc002e 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 e9cd42ae777df5..488d2e07a2732e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9622,6 +9622,8 @@ class Sema final {
 
       /// We are building deduction guides for a class.
       BuildingDeductionGuides,
+
+      TypeAliasTemplateInstantiation,
     } Kind;
 
     /// Was the enclosing context a non-instantiation SFINAE context?
@@ -9812,6 +9814,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 b9ed5dedfa4223..43d6e2230fb129 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -426,6 +426,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 2878e4d31ee8fe..5cc6236c3991b6 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -614,14 +614,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 9e516da2aa27a1..9e246b552fdd70 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4016,9 +4016,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 371378485626c2..7d401336741638 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -282,7 +282,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) {
@@ -313,9 +314,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);
 }
@@ -412,7 +446,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);
@@ -469,6 +503,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.
@@ -614,6 +649,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,
@@ -1131,6 +1175,8 @@ void Sema::PrintInstantiationStack() {
       Diags.Report(Active->PointOfInstantiation,
                    diag::note_building_deduction_guide_here);
       break;
+    case CodeSynthesisContext::TypeAliasTemplateInstantiation:
+      break;
     }
   }
 }
@@ -1208,6 +1254,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
       break;
 
     case CodeSynthesisContext::Memoization:
+    case CodeSynthesisContext::TypeAliasTemplateInstantiation:
       break;
     }
 
@@ -1479,6 +1526,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 9c696e072ba4a7..2d8675690972ff 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 a32a585531873a..3b49d636c5e5ac 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);
@@ -13905,6 +13909,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 00000000000000..c3931287cb6404
--- /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 b63364ef8d75a0c0df9efeac287d57e346681c01 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/3] 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 488d2e07a2732e..5847b0301a4ca4 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9623,6 +9623,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 5cc6236c3991b6..5908b941f21a7d 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -614,13 +614,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 9e246b552fdd70..85962afb8899e9 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4017,8 +4017,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 7d401336741638..a94b48ecd13ffb 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -282,8 +282,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) {
@@ -317,35 +316,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());
         }
       }
     }
@@ -446,7 +478,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);
@@ -1529,7 +1562,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 3b49d636c5e5ac..41633646863536 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);
@@ -13909,8 +13911,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 48836063ad9b9e11f5d53daf97803d233cfbb848 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/3] 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 2d8675690972ff..b03ff1cf511d7d 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)) {



More information about the cfe-commits mailing list