[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