[clang] The lambda call inside of a type alias (PR #82310)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 19 22:58:37 PST 2024
https://github.com/zyn0217 created https://github.com/llvm/llvm-project/pull/82310
None
>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] 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
More information about the cfe-commits
mailing list