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 <zyn7...@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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits