cor3ntin created this revision. Herald added subscribers: ChuanqiXu, Anastasia. Herald added a project: All. cor3ntin requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
Like concepts checking, a trailing return type of a lambda in a dependent context may refer to captures in which case they may need to be rebuilt, so the map of local decl should include captures. This patch reveal a pre-existing issue. `this` is always recomputed by TreeTransform. `*this` (like all captures) only become `const` after the parameter list. However, if try to recompute the value of `this` (in a parameter) during template instantiation while determining the type of the call operator, we will determine it to be const (unless the lambda is mutable). There is no good way to know at that point that we are in a parameter or not, the easiest/best solution is to transform the type of this. Note that doing so break a handful of HLSL tests. So this is a prototype at this point. Fixes #65067 Fixes #63675 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D159126 Files: clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaConcept.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaLambda.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/lib/Sema/TreeTransform.h clang/test/SemaCXX/lambda-capture-type-deduction.cpp
Index: clang/test/SemaCXX/lambda-capture-type-deduction.cpp =================================================================== --- clang/test/SemaCXX/lambda-capture-type-deduction.cpp +++ clang/test/SemaCXX/lambda-capture-type-deduction.cpp @@ -260,3 +260,40 @@ void test() { f<int>(0); } } + +namespace GH65067 { + +template <typename> class a { +public: + template <typename b> void c(b f) { d<int>(f)(0); } + template <typename, typename b> auto d(b f) { + return [f = f](auto arg) -> a<decltype(f(arg))> { return {}; }; + } +}; +a<void> e; +auto fn1() { + e.c([] {}); +} + +} + +namespace GH63675 { + +template <class _Tp> _Tp __declval(); +struct __get_tag { + template <class _Tag> void operator()(_Tag); +}; +template <class _ImplFn> struct __basic_sender { + using __tag_t = decltype(__declval<_ImplFn>()(__declval<__get_tag>())); + _ImplFn __impl_; +}; +auto __make_basic_sender = []<class... _Children>( + _Children... __children) { + return __basic_sender{[... __children = __children]<class _Fun>( + _Fun __fun) -> decltype(__fun(__children...)) {}}; +}; +void __trans_tmp_1() { + __make_basic_sender(__trans_tmp_1); +} + +} Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -28,6 +28,7 @@ #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" #include "clang/AST/StmtOpenMP.h" +#include "clang/AST/Type.h" #include "clang/Basic/DiagnosticParse.h" #include "clang/Basic/OpenMPKinds.h" #include "clang/Sema/Designator.h" @@ -12320,7 +12321,8 @@ template<typename Derived> ExprResult TreeTransform<Derived>::TransformCXXThisExpr(CXXThisExpr *E) { - QualType T = getSema().getCurrentThisType(); + + QualType T = getDerived().TransformType(E->getType()); if (!getDerived().AlwaysRebuild() && T == E->getType()) { // Mark it referenced in the new context regardless. Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -12,7 +12,9 @@ #include "TreeTransform.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/ASTLambda.h" #include "clang/AST/ASTMutationListener.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/DependentDiagnostic.h" @@ -2426,6 +2428,9 @@ cast<Decl>(Owner)->isDefinedOutsideFunctionOrMethod()); LocalInstantiationScope Scope(SemaRef, MergeWithParentScope); + Sema::LambdaScopeForCallOperatorInstantiationRAII LambdaScope( + SemaRef, const_cast<CXXMethodDecl *>(D), TemplateArgs, Scope); + // Instantiate enclosing template arguments for friends. SmallVector<TemplateParameterList *, 4> TempParamLists; unsigned NumTempParamLists = 0; Index: clang/lib/Sema/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -9,17 +9,19 @@ // This file implements semantic analysis for C++ lambda expressions. // //===----------------------------------------------------------------------===// -#include "clang/Sema/DeclSpec.h" +#include "clang/Sema/SemaLambda.h" #include "TypeLocBuilder.h" #include "clang/AST/ASTLambda.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Sema/DeclSpec.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" -#include "clang/Sema/SemaLambda.h" +#include "clang/Sema/Template.h" #include "llvm/ADT/STLExtras.h" #include <optional> using namespace clang; @@ -2232,3 +2234,34 @@ return BuildBlock; } + +Sema::LambdaScopeForCallOperatorInstantiationRAII:: + LambdaScopeForCallOperatorInstantiationRAII( + Sema &SemasRef, FunctionDecl *FD, MultiLevelTemplateArgumentList MLTAL, + LocalInstantiationScope &Scope) + : FunctionScope(SemasRef) { + if (!isLambdaCallOperator(FD)) { + FunctionScope.disable(); + return; + } + + if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) { + FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate(); + if (const auto *FromMemTempl = + PrimaryTemplate->getInstantiatedFromMemberTemplate()) { + SemasRef.addInstantiatedCapturesToScope( + FD, FromMemTempl->getTemplatedDecl(), Scope, MLTAL); + } + } + + else if (FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization || + FD->getTemplatedKind() == FunctionDecl::TK_DependentNonTemplate) { + FunctionDecl *InstantiatedFrom = + FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization + ? FD->getInstantiatedFromMemberFunction() + : FD->getInstantiatedFromDecl(); + SemasRef.addInstantiatedCapturesToScope(FD, InstantiatedFrom, Scope, MLTAL); + } + + SemasRef.RebuildLambdaScopeInfo(cast<CXXMethodDecl>(FD)); +} Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -15304,6 +15304,10 @@ LSI->CallOperator = CallOperator; LSI->Lambda = LambdaClass; LSI->ReturnType = CallOperator->getReturnType(); + // This function in calls in situation where the context of the call operator + // is not entered, so we set AfterParameterList to false, so that + // `tryCaptureVariable` finds explicit captures in the appropriate context. + LSI->AfterParameterList = false; const LambdaCaptureDefault LCD = LambdaClass->getLambdaCaptureDefault(); if (LCD == LCD_None) Index: clang/lib/Sema/SemaConcept.cpp =================================================================== --- clang/lib/Sema/SemaConcept.cpp +++ clang/lib/Sema/SemaConcept.cpp @@ -600,11 +600,6 @@ if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(), Scope, MLTAL)) return true; - // Make sure the captures are also added to the instantiation scope. - if (isLambdaCallOperator(FD) && - addInstantiatedCapturesToScope(FD, FromMemTempl->getTemplatedDecl(), - Scope, MLTAL)) - return true; } return false; @@ -629,11 +624,6 @@ // child-function. if (addInstantiatedParametersToScope(FD, InstantiatedFrom, Scope, MLTAL)) return true; - - // Make sure the captures are also added to the instantiation scope. - if (isLambdaCallOperator(FD) && - addInstantiatedCapturesToScope(FD, InstantiatedFrom, Scope, MLTAL)) - return true; } return false; @@ -712,20 +702,8 @@ } CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); - // When checking the constraints of a lambda, we need to restore a - // LambdaScopeInfo populated with correct capture information so that the type - // of a variable referring to a capture is correctly const-adjusted. - FunctionScopeRAII FuncScope(*this); - if (isLambdaCallOperator(FD)) { - LambdaScopeInfo *LSI = RebuildLambdaScopeInfo( - const_cast<CXXMethodDecl *>(cast<CXXMethodDecl>(FD))); - // Constraints are checked from the parent context of the lambda, so we set - // AfterParameterList to false, so that `tryCaptureVariable` finds - // explicit captures in the appropriate context. - LSI->AfterParameterList = false; - } else { - FuncScope.disable(); - } + LambdaScopeForCallOperatorInstantiationRAII LambdaScope( + *this, const_cast<FunctionDecl *>(FD), *MLTAL, Scope); return CheckConstraintSatisfaction( FD, {FD->getTrailingRequiresClause()}, *MLTAL, @@ -913,15 +891,10 @@ ThisQuals = Method->getMethodQualifiers(); Record = Method->getParent(); } - CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); - FunctionScopeRAII FuncScope(*this); - if (isLambdaCallOperator(Decl)) { - LambdaScopeInfo *LSI = RebuildLambdaScopeInfo(cast<CXXMethodDecl>(Decl)); - LSI->AfterParameterList = false; - } else { - FuncScope.disable(); - } + CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); + LambdaScopeForCallOperatorInstantiationRAII LambdaScope( + *this, const_cast<FunctionDecl *>(Decl), *MLTAL, Scope); llvm::SmallVector<Expr *, 1> Converted; return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL, Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -7345,6 +7345,16 @@ sema::LambdaScopeInfo *RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator); + class LambdaScopeForCallOperatorInstantiationRAII { + public: + LambdaScopeForCallOperatorInstantiationRAII( + Sema &SemasRef, FunctionDecl *FD, MultiLevelTemplateArgumentList MLTAL, + LocalInstantiationScope &Scope); + + private: + FunctionScopeRAII FunctionScope; + }; + /// Check whether the given expression is a valid constraint expression. /// A diagnostic is emitted if it is not, false is returned, and /// PossibleNonPrimary will be set to true if the failure might be due to a
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits