[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