https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/89934
>From 92f8cfb255a549769c39327239c69edd6b2e947e Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Wed, 24 Apr 2024 20:54:58 +0800 Subject: [PATCH 1/3] [Clang][Sema] Revisit the lambda within a type alias template decl --- clang/lib/Sema/SemaTemplateInstantiate.cpp | 72 ++++++++++--------- .../alias-template-with-lambdas.cpp | 27 ++++++- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 98d5c7cb3a8a80..572d8e4c820ee9 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -20,6 +20,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprConcepts.h" #include "clang/AST/PrettyDeclStackTrace.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeVisitor.h" @@ -87,12 +88,19 @@ struct Response { // than lambda classes. const FunctionDecl * getPrimaryTemplateOfGenericLambda(const FunctionDecl *LambdaCallOperator) { + if (!isLambdaCallOperator(LambdaCallOperator)) + return LambdaCallOperator; while (true) { if (auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>( LambdaCallOperator->getDescribedTemplate()); FTD && FTD->getInstantiatedFromMemberTemplate()) { LambdaCallOperator = FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl(); + } else if (LambdaCallOperator->getPrimaryTemplate()) { + // Cases where the lambda operator is instantiated in + // TemplateDeclInstantiator::VisitCXXMethodDecl. + LambdaCallOperator = + LambdaCallOperator->getPrimaryTemplate()->getTemplatedDecl(); } else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator) ->getInstantiatedFromMemberFunction()) LambdaCallOperator = Prev; @@ -138,22 +146,30 @@ getEnclosingTypeAliasTemplateDecl(Sema &SemaRef) { // 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 FunctionDecl *LambdaCallOperator, const TypeAliasTemplateDecl *PrimaryTypeAliasDecl) { - return cast<CXXRecordDecl>(PrimaryLambdaCallOperator->getDeclContext()) - ->getTemplateDepth() == - PrimaryTypeAliasDecl->getTemplateDepth() && - getLambdaAwareParentOfDeclContext( - const_cast<FunctionDecl *>(PrimaryLambdaCallOperator)) == - PrimaryTypeAliasDecl->getDeclContext(); + struct Visitor : RecursiveASTVisitor<Visitor> { + Visitor(const FunctionDecl *CallOperator) : CallOperator(CallOperator) {} + bool VisitLambdaExpr(const LambdaExpr *LE) { + // Return true to bail out of the traversal, implying the Decl contains + // the lambda. + return getPrimaryTemplateOfGenericLambda(LE->getCallOperator()) != + CallOperator; + } + const FunctionDecl *CallOperator; + }; + + QualType Underlying = + PrimaryTypeAliasDecl->getTemplatedDecl()->getUnderlyingType(); + + if (auto *DT = dyn_cast<DecltypeType>(Underlying.getTypePtr())) + return !Visitor(getPrimaryTemplateOfGenericLambda(LambdaCallOperator)) + .TraverseStmt(DT->getUnderlyingExpr()); + return false; } // Add template arguments from a variable template instantiation. @@ -283,23 +299,8 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function, // If this function is a generic lambda specialization, we are done. if (!ForConstraintInstantiation && - 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); - } + isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) return Response::Done(); - } } else if (Function->getDescribedFunctionTemplate()) { assert( @@ -412,9 +413,7 @@ Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec, // This is necessary for constraint checking, since we always keep // constraints relative to the primary template. if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) { - const FunctionDecl *PrimaryLambdaCallOperator = - getPrimaryTemplateOfGenericLambda(Rec->getLambdaCallOperator()); - if (isLambdaEnclosedByTypeAliasDecl(PrimaryLambdaCallOperator, + if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(), TypeAlias.PrimaryTypeAliasDecl)) { Result.addOuterTemplateArguments(TypeAlias.Template, TypeAlias.AssociatedTemplateArguments, @@ -1670,12 +1669,17 @@ namespace { CXXRecordDecl::LambdaDependencyKind ComputeLambdaDependency(LambdaScopeInfo *LSI) { - auto &CCS = SemaRef.CodeSynthesisContexts.back(); - if (CCS.Kind == - Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) { - unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth(); + if (auto TypeAlias = + TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl( + getSema()); + TypeAlias && TemplateInstArgsHelpers::isLambdaEnclosedByTypeAliasDecl( + LSI->CallOperator, TypeAlias.PrimaryTypeAliasDecl)) { + unsigned TypeAliasDeclDepth = TypeAlias.Template->getTemplateDepth(); if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels()) return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent; + for (const TemplateArgument &TA : TypeAlias.AssociatedTemplateArguments) + if (TA.isDependent()) + return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent; } return inherited::ComputeLambdaDependency(LSI); } diff --git a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp index ff94031e4d86f1..bedbd4a6c07b4f 100644 --- a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp +++ b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp @@ -94,7 +94,9 @@ namespace GH82104 { template <typename, typename...> int Zero = 0; template <typename T, typename...U> -using T14 = decltype([]<int V = 0>() { return Zero<T, U...>; }()); +using T14 = decltype([]<int V = 0>(auto Param) { + return Zero<T, U...> + V + (int)sizeof(Param); +}("hello")); template <typename T> using T15 = T14<T, T>; @@ -102,4 +104,27 @@ static_assert(__is_same(T15<char>, int)); } // namespace GH82104 +namespace GH89853 { +template <typename = void> +static constexpr auto innocuous = []<int m> { return m; }; + +template <auto Pred = innocuous<>> +using broken = decltype(Pred.template operator()<42>()); + +broken<> *boom; + +template <auto Pred = + []<const char c> { + (void)static_cast<char>(c); + }> +using broken2 = decltype(Pred.template operator()<42>()); + +broken2<> *boom2; + +template <auto Pred = []<const char m> { return m; }> +using broken3 = decltype(Pred.template operator()<42>()); + +broken3<> *boom3; +} + } // namespace lambda_calls >From 0598618aa06b5531a5f92ea07104ef1453fb07b5 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Wed, 24 Apr 2024 23:21:37 +0800 Subject: [PATCH 2/3] Just use TraverseType --- clang/lib/Sema/SemaTemplateInstantiate.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 572d8e4c820ee9..ec7f5af1dc01c3 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -166,10 +166,8 @@ bool isLambdaEnclosedByTypeAliasDecl( QualType Underlying = PrimaryTypeAliasDecl->getTemplatedDecl()->getUnderlyingType(); - if (auto *DT = dyn_cast<DecltypeType>(Underlying.getTypePtr())) - return !Visitor(getPrimaryTemplateOfGenericLambda(LambdaCallOperator)) - .TraverseStmt(DT->getUnderlyingExpr()); - return false; + return !Visitor(getPrimaryTemplateOfGenericLambda(LambdaCallOperator)) + .TraverseType(Underlying); } // Add template arguments from a variable template instantiation. >From c2342fedca28d21ebbc4b0884d7d6042c1e4ee88 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Thu, 25 Apr 2024 10:40:51 +0800 Subject: [PATCH 3/3] Add more tests --- clang/lib/Sema/SemaTemplateInstantiate.cpp | 3 +- .../alias-template-with-lambdas.cpp | 30 +++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index ec7f5af1dc01c3..8b4d841fab46cf 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -410,7 +410,8 @@ Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec, // 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 (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) { + if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef); + ForConstraintInstantiation && TypeAlias) { if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(), TypeAlias.PrimaryTypeAliasDecl)) { Result.addOuterTemplateArguments(TypeAlias.Template, diff --git a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp index bedbd4a6c07b4f..032ad2cf6a5ad2 100644 --- a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp +++ b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp @@ -91,20 +91,30 @@ void bar() { namespace GH82104 { -template <typename, typename...> int Zero = 0; +template <typename, typename... D> int Value = sizeof...(D); -template <typename T, typename...U> +template <typename T, typename... U> using T14 = decltype([]<int V = 0>(auto Param) { - return Zero<T, U...> + V + (int)sizeof(Param); + return Value<T, U...> + V + (int)sizeof(Param); }("hello")); template <typename T> using T15 = T14<T, T>; static_assert(__is_same(T15<char>, int)); +// FIXME: This still crashes because we can't extract template arguments T and U +// outside of the instantiation context of T16. +#if 0 +template <typename T, typename... U> +using T16 = decltype([](auto Param) requires (sizeof(Param) != 1 && sizeof...(U) > 0) { + return Value<T, U...> + sizeof(Param); +}); +static_assert(T16<int, char, float>()(42) == 2 + sizeof(42)); +#endif } // namespace GH82104 namespace GH89853 { + template <typename = void> static constexpr auto innocuous = []<int m> { return m; }; @@ -114,17 +124,27 @@ using broken = decltype(Pred.template operator()<42>()); broken<> *boom; template <auto Pred = - []<const char c> { + []<char c> { (void)static_cast<char>(c); }> using broken2 = decltype(Pred.template operator()<42>()); broken2<> *boom2; -template <auto Pred = []<const char m> { return m; }> +template <auto Pred = []<char m> { return m; }> using broken3 = decltype(Pred.template operator()<42>()); broken3<> *boom3; + +static constexpr auto non_default = []<char c>(True auto) { + (void) static_cast<char>(c); +}; + +template<True auto Pred> +using broken4 = decltype(Pred.template operator()<42>(Pred)); + +broken4<non_default>* boom4; + } } // namespace lambda_calls _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits