Author: Younan Zhang Date: 2024-08-27T09:25:53+08:00 New Revision: b412ec5d3924c7570c2c96106f95a92403a4e09b
URL: https://github.com/llvm/llvm-project/commit/b412ec5d3924c7570c2c96106f95a92403a4e09b DIFF: https://github.com/llvm/llvm-project/commit/b412ec5d3924c7570c2c96106f95a92403a4e09b.diff LOG: [Clang][Sema] Revisit the fix for the lambda within a type alias template decl (#89934) In the last patch #82310, we used template depths to tell if such alias decls contain lambdas, which is wrong because the lambda can also appear as a part of the default argument, and that would make `getTemplateInstantiationArgs` provide extra template arguments in undesired contexts. This leads to issue #89853. Moreover, our approach for https://github.com/llvm/llvm-project/issues/82104 was sadly wrong. We tried to teach `DeduceReturnType` to consider alias template arguments; however, giving these arguments in the context where they should have been substituted in a `TransformCallExpr` call is never correct. This patch addresses such problems by using a `RecursiveASTVisitor` to check if the lambda is contained by an alias `Decl`, as well as twiddling the lambda dependencies - we should also build a dependent lambda expression if the surrounding alias template arguments were dependent. Fixes #89853 Fixes #102760 Fixes #105885 Added: Modified: clang/lib/Sema/SemaTemplateInstantiate.cpp clang/test/SemaTemplate/alias-template-with-lambdas.cpp Removed: ################################################################################ diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index fd90f83f3976ca..786daea5bab319 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" @@ -88,12 +89,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; @@ -139,22 +147,28 @@ 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(); + + return !Visitor(getPrimaryTemplateOfGenericLambda(LambdaCallOperator)) + .TraverseType(Underlying); } // Add template arguments from a variable template instantiation. @@ -293,23 +307,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( @@ -421,10 +420,9 @@ 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)) { - const FunctionDecl *PrimaryLambdaCallOperator = - getPrimaryTemplateOfGenericLambda(Rec->getLambdaCallOperator()); - if (isLambdaEnclosedByTypeAliasDecl(PrimaryLambdaCallOperator, + if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef); + ForConstraintInstantiation && TypeAlias) { + if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(), TypeAlias.PrimaryTypeAliasDecl)) { Result.addOuterTemplateArguments(TypeAlias.Template, TypeAlias.AssociatedTemplateArguments, @@ -1647,12 +1645,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..5ec93163e4d188 100644 --- a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp +++ b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp @@ -91,15 +91,84 @@ void bar() { namespace GH82104 { -template <typename, typename...> int Zero = 0; +template <typename, typename... D> constexpr int Value = sizeof...(D); -template <typename T, typename...U> -using T14 = decltype([]<int V = 0>() { return Zero<T, U...>; }()); +template <typename T, typename... U> +using T14 = decltype([]<int V = 0>(auto 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; }; + +template <auto Pred = innocuous<>> +using broken = decltype(Pred.template operator()<42>()); + +broken<> *boom; + +template <auto Pred = + []<char c> { + (void)static_cast<char>(c); + }> +using broken2 = decltype(Pred.template operator()<42>()); + +broken2<> *boom2; + +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 GH89853 + +namespace GH105885 { + +template<int> +using test = decltype([](auto...) { +}()); + +static_assert(__is_same(test<0>, void)); + +} // namespace GH105885 + +namespace GH102760 { + +auto make_tuple = []< class Tag, class... Captures>(Tag, Captures...) { + return []< class _Fun >( _Fun) -> void requires requires { 0; } + {}; +}; + +template < class, class... _As > +using Result = decltype(make_tuple(0)(_As{}...)); + +using T = Result<int, int>; + +} // namespace GH102760 + } // namespace lambda_calls _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits