diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -270,6 +270,11 @@ - Fix crash when parsing the requires clause of some generic lambdas. (`#64689 `_) +- Fix crash when the trailing return type of a generic and dependent + lambda refers to an init-capture. + (`#65067 `_` and + `#63675 `_`) + Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed an import failure of recursive friend class template. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7365,6 +7365,14 @@ sema::LambdaScopeInfo *RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator); + class LambdaScopeForCallOperatorInstantiationRAII + : private FunctionScopeRAII { + public: + LambdaScopeForCallOperatorInstantiationRAII( + Sema &SemasRef, FunctionDecl *FD, MultiLevelTemplateArgumentList MLTAL, + LocalInstantiationScope &Scope); + }; + /// 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 diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp --- a/clang/lib/Sema/SemaConcept.cpp +++ b/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(cast(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(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(Decl)); - LSI->AfterParameterList = false; - } else { - FuncScope.disable(); - } + CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); + LambdaScopeForCallOperatorInstantiationRAII LambdaScope( + *this, const_cast(Decl), *MLTAL, Scope); llvm::SmallVector Converted; return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15382,6 +15382,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) diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -20,6 +20,7 @@ #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 using namespace clang; @@ -2254,3 +2255,34 @@ return BuildBlock; } + +Sema::LambdaScopeForCallOperatorInstantiationRAII:: + LambdaScopeForCallOperatorInstantiationRAII( + Sema &SemasRef, FunctionDecl *FD, MultiLevelTemplateArgumentList MLTAL, + LocalInstantiationScope &Scope) + : FunctionScopeRAII(SemasRef) { + if (!isLambdaCallOperator(FD)) { + FunctionScopeRAII::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(FD)); +} diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2426,6 +2426,9 @@ cast(Owner)->isDefinedOutsideFunctionOrMethod()); LocalInstantiationScope Scope(SemaRef, MergeWithParentScope); + Sema::LambdaScopeForCallOperatorInstantiationRAII LambdaScope( + SemaRef, const_cast(D), TemplateArgs, Scope); + // Instantiate enclosing template arguments for friends. SmallVector TempParamLists; unsigned NumTempParamLists = 0; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -12325,7 +12325,16 @@ template ExprResult TreeTransform::TransformCXXThisExpr(CXXThisExpr *E) { - QualType T = getSema().getCurrentThisType(); + + // In lambdas, the qualifiers of the type depends of where in + // the call operator `this` appear, and we do not have a good way to + // rebuild this information, so we transform the type. + // + // In other contexts, the type of `this` may be overrided + // for type deduction, so we need to recompute it. + QualType T = getSema().getCurLambda() ? + getDerived().TransformType(E->getType()) + : getSema().getCurrentThisType(); if (!getDerived().AlwaysRebuild() && T == E->getType()) { // Mark it referenced in the new context regardless. diff --git a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp --- a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp +++ b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp @@ -260,3 +260,40 @@ void test() { f(0); } } + +namespace GH65067 { + +template class a { +public: + template void c(b f) { d(f)(0); } + template auto d(b f) { + return [f = f](auto arg) -> a { return {}; }; + } +}; +a e; +auto fn1() { + e.c([](int) {}); +} + +} + +namespace GH63675 { + +template _Tp __declval(); +struct __get_tag { + template void operator()(_Tag); +}; +template struct __basic_sender { + using __tag_t = decltype(__declval<_ImplFn>()(__declval<__get_tag>())); + _ImplFn __impl_; +}; +auto __make_basic_sender = []( + _Children... __children) { + return __basic_sender{[... __children = __children]( + _Fun __fun) -> decltype(__fun(__children...)) {}}; +}; +void __trans_tmp_1() { + __make_basic_sender(__trans_tmp_1); +} + +} diff --git a/clang/test/SemaCXX/this-type-deduction-concept.cpp b/clang/test/SemaCXX/this-type-deduction-concept.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/this-type-deduction-concept.cpp @@ -0,0 +1,54 @@ + +// This test case came up in the review of +// https://reviews.llvm.org/D159126 +// when transforming `this` within a +// requires expression, we need to make sure +// the type of this (and its qualifiers) is respected. +namespace D159126 { + +template +concept __member_begin = requires(_Tp __t) { + __t.begin(); +}; + +struct { + template + requires __member_begin<_Tp> + auto operator()(_Tp &&) {} +} inline begin; + +template +concept range = requires { + begin; +}; + +template +concept __can_compare_begin = requires(_Tp __t) { + begin(__t); +}; + +struct { + template <__can_compare_begin _Tp> void operator()(_Tp &&); +} empty; + +template struct owning_view { + _Rp __r_; +public: + void empty() const requires requires { empty(__r_); }; +}; + +template +concept HasEmpty = requires(T t) { + t.empty(); +}; + +struct ComparableIters { + void begin(); +}; + +static_assert(HasEmpty>); +static_assert(HasEmpty>); +static_assert(!HasEmpty>); +static_assert(!HasEmpty>); + +}