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