cor3ntin created this revision. Herald added a project: All. cor3ntin requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
D119136 <https://reviews.llvm.org/D119136> changed how captures are handled in a lambda call operator declaration, but did not properly handled dependant context, which led to crash when refering to init-captures in a trailing return type. We fix that bug by making transformations more symetric with parsing, ie. we first create the call operator, then transform the capture, then compute the type of the lambda call operaror. This ensures captures exist and have the right type when we parse a trailing requires-clause / return type. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D124012 Files: clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaLambda.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 @@ -163,6 +163,35 @@ [&]() requires is_same<decltype(u), T> {}(); } +template <typename T> +void dependent_init_capture(T x = 0) { + [ y = x + 1, x ]() mutable -> decltype(y + x) requires(is_same<decltype((y)), int &> && is_same<decltype((x)), int &>) { + return y; + } + (); + [ y = x + 1, x ]() -> decltype(y + x) requires(is_same<decltype((y)), const int &> && is_same<decltype((x)), const int &>) { + return y; + } + (); +} + +template <typename T, typename...> +struct extract_type { + using type = T; +}; + +template <typename... T> +void dependent_variadic_capture(T... x) { + [... y = x, x... ](auto...) mutable -> typename extract_type<decltype(y)...>::type requires((is_same<decltype((y)), int &> && ...) && (is_same<decltype((x)), int &> && ...)) { + return 0; + } + (x...); + [... y = x, x... ](auto...) -> typename extract_type<decltype(y)...>::type requires((is_same<decltype((y)), const int &> && ...) && (is_same<decltype((x)), const int &> && ...)) { + return 0; + } + (x...); +} + void test_dependent() { int v = 0; int & r = v; @@ -170,6 +199,8 @@ dependent<int&>(v); dependent<int&>(r); dependent<const int&>(cr); + dependent_init_capture(0); + dependent_variadic_capture(1, 2, 3, 4); } void test_CWG2569_tpl(auto a) { Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -12883,9 +12883,8 @@ /*ListInitialization=*/LParenLoc.isInvalid()); } -template<typename Derived> -ExprResult -TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { +template <typename Derived> +ExprResult TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { // Transform any init-capture expressions before entering the scope of the // lambda body, because they are not semantically within that scope. typedef std::pair<ExprResult, QualType> InitCaptureInfoTy; @@ -12943,9 +12942,8 @@ ExpansionTL.getTypePtr()->getNumExpansions(); Optional<unsigned> NumExpansions = OrigNumExpansions; if (getDerived().TryExpandParameterPacks( - ExpansionTL.getEllipsisLoc(), - OldVD->getInit()->getSourceRange(), Unexpanded, Expand, - RetainExpansion, NumExpansions)) + ExpansionTL.getEllipsisLoc(), OldVD->getInit()->getSourceRange(), + Unexpanded, Expand, RetainExpansion, NumExpansions)) return ExprError(); if (Expand) { for (unsigned I = 0; I != *NumExpansions; ++I) { @@ -12966,44 +12964,6 @@ LambdaScopeInfo *LSI = getSema().PushLambdaScope(); Sema::FunctionScopeRAII FuncScopeCleanup(getSema()); - // Transform the template parameters, and add them to the current - // instantiation scope. The null case is handled correctly. - auto TPL = getDerived().TransformTemplateParameterList( - E->getTemplateParameterList()); - LSI->GLTemplateParameterList = TPL; - - // Transform the type of the original lambda's call operator. - // The transformation MUST be done in the CurrentInstantiationScope since - // it introduces a mapping of the original to the newly created - // transformed parameters. - TypeSourceInfo *NewCallOpTSI = nullptr; - { - TypeSourceInfo *OldCallOpTSI = E->getCallOperator()->getTypeSourceInfo(); - FunctionProtoTypeLoc OldCallOpFPTL = - OldCallOpTSI->getTypeLoc().getAs<FunctionProtoTypeLoc>(); - - TypeLocBuilder NewCallOpTLBuilder; - SmallVector<QualType, 4> ExceptionStorage; - TreeTransform *This = this; // Work around gcc.gnu.org/PR56135. - QualType NewCallOpType = TransformFunctionProtoType( - NewCallOpTLBuilder, OldCallOpFPTL, nullptr, Qualifiers(), - [&](FunctionProtoType::ExceptionSpecInfo &ESI, bool &Changed) { - return This->TransformExceptionSpec(OldCallOpFPTL.getBeginLoc(), ESI, - ExceptionStorage, Changed); - }); - if (NewCallOpType.isNull()) - return ExprError(); - NewCallOpTSI = NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, - NewCallOpType); - } - - // Transform the trailing requires clause - ExprResult NewTrailingRequiresClause; - if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause()) - // FIXME: Concepts: Substitution into requires clause should only happen - // when checking satisfaction. - NewTrailingRequiresClause = getDerived().TransformExpr(TRC); - // Create the local class that will describe the lambda. // FIXME: DependencyKind below is wrong when substituting inside a templated @@ -13019,10 +12979,8 @@ DependencyKind = CXXRecordDecl::LDK_NeverDependent; CXXRecordDecl *OldClass = E->getLambdaClass(); - CXXRecordDecl *Class = - getSema().createLambdaClosureType(E->getIntroducerRange(), NewCallOpTSI, - DependencyKind, E->getCaptureDefault()); - + CXXRecordDecl *Class = getSema().createLambdaClosureType( + E->getIntroducerRange(), nullptr, DependencyKind, E->getCaptureDefault()); getDerived().transformedLocalDecl(OldClass, {Class}); Optional<std::tuple<bool, unsigned, unsigned, Decl *>> Mangling; @@ -13032,40 +12990,24 @@ OldClass->getDeviceLambdaManglingNumber(), OldClass->getLambdaContextDecl()); - // Build the call operator. - CXXMethodDecl *NewCallOperator = getSema().startLambdaDefinition( - Class, E->getIntroducerRange(), NewCallOpTSI, - E->getCallOperator()->getEndLoc(), - NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(), - E->getCallOperator()->getConstexprKind(), - NewTrailingRequiresClause.get()); - - LSI->CallOperator = NewCallOperator; - - getDerived().transformAttrs(E->getCallOperator(), NewCallOperator); - getDerived().transformedLocalDecl(E->getCallOperator(), {NewCallOperator}); + CXXMethodDecl *NewCallOperator = + getSema().CreateLambdaCallOperator(E->getIntroducerRange(), Class); + NewCallOperator->setLexicalDeclContext(getSema().CurContext); - // Number the lambda for linkage purposes if necessary. - getSema().handleLambdaNumbering(Class, NewCallOperator, Mangling); + // Enter the scope of the lambda. + getSema().buildLambdaScope(LSI, NewCallOperator, E->getIntroducerRange(), + E->getCaptureDefault(), E->getCaptureDefaultLoc(), + E->hasExplicitParameters(), E->isMutable()); // Introduce the context of the call operator. Sema::ContextRAII SavedContext(getSema(), NewCallOperator, - /*NewThisContext*/false); - - // Enter the scope of the lambda. - getSema().buildLambdaScope(LSI, NewCallOperator, - E->getIntroducerRange(), - E->getCaptureDefault(), - E->getCaptureDefaultLoc(), - E->hasExplicitParameters(), - E->hasExplicitResultType(), - E->isMutable()); + /*NewThisContext*/ false); bool Invalid = false; // Transform captures. for (LambdaExpr::capture_iterator C = E->capture_begin(), - CEnd = E->capture_end(); + CEnd = E->capture_end(); C != CEnd; ++C) { // When we hit the first implicit capture, tell Sema that we've finished // the list of explicit captures. @@ -13089,7 +13031,7 @@ TransformedInitCapture &NewC = InitCaptures[C - E->capture_begin()]; VarDecl *OldVD = C->getCapturedVar(); - llvm::SmallVector<Decl*, 4> NewVDs; + llvm::SmallVector<Decl *, 4> NewVDs; for (InitCaptureInfoTy &Info : NewC.Expansions) { ExprResult Init = Info.first; @@ -13120,22 +13062,19 @@ assert(C->capturesVariable() && "unexpected kind of lambda capture"); // Determine the capture kind for Sema. - Sema::TryCaptureKind Kind - = C->isImplicit()? Sema::TryCapture_Implicit - : C->getCaptureKind() == LCK_ByCopy - ? Sema::TryCapture_ExplicitByVal - : Sema::TryCapture_ExplicitByRef; + Sema::TryCaptureKind Kind = C->isImplicit() ? Sema::TryCapture_Implicit + : C->getCaptureKind() == LCK_ByCopy + ? Sema::TryCapture_ExplicitByVal + : Sema::TryCapture_ExplicitByRef; SourceLocation EllipsisLoc; if (C->isPackExpansion()) { UnexpandedParameterPack Unexpanded(C->getCapturedVar(), C->getLocation()); bool ShouldExpand = false; bool RetainExpansion = false; Optional<unsigned> NumExpansions; - if (getDerived().TryExpandParameterPacks(C->getEllipsisLoc(), - C->getLocation(), - Unexpanded, - ShouldExpand, RetainExpansion, - NumExpansions)) { + if (getDerived().TryExpandParameterPacks( + C->getEllipsisLoc(), C->getLocation(), Unexpanded, ShouldExpand, + RetainExpansion, NumExpansions)) { Invalid = true; continue; } @@ -13147,9 +13086,8 @@ VarDecl *Pack = C->getCapturedVar(); for (unsigned I = 0; I != *NumExpansions; ++I) { Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); - VarDecl *CapturedVar - = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(), - Pack)); + VarDecl *CapturedVar = cast_or_null<VarDecl>( + getDerived().TransformDecl(C->getLocation(), Pack)); if (!CapturedVar) { Invalid = true; continue; @@ -13168,9 +13106,8 @@ } // Transform the captured variable. - VarDecl *CapturedVar - = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(), - C->getCapturedVar())); + VarDecl *CapturedVar = cast_or_null<VarDecl>( + getDerived().TransformDecl(C->getLocation(), C->getCapturedVar())); if (!CapturedVar || CapturedVar->isInvalidDecl()) { Invalid = true; continue; @@ -13182,6 +13119,60 @@ } getSema().finishLambdaExplicitCaptures(LSI); + // Transform the template parameters, and add them to the current + // instantiation scope. The null case is handled correctly. + auto TPL = getDerived().TransformTemplateParameterList( + E->getTemplateParameterList()); + LSI->GLTemplateParameterList = TPL; + + // Transform the type of the original lambda's call operator. + // The transformation MUST be done in the CurrentInstantiationScope since + // it introduces a mapping of the original to the newly created + // transformed parameters. + TypeSourceInfo *NewCallOpTSI = nullptr; + { + TypeSourceInfo *OldCallOpTSI = E->getCallOperator()->getTypeSourceInfo(); + FunctionProtoTypeLoc OldCallOpFPTL = + OldCallOpTSI->getTypeLoc().getAs<FunctionProtoTypeLoc>(); + + TypeLocBuilder NewCallOpTLBuilder; + SmallVector<QualType, 4> ExceptionStorage; + TreeTransform *This = this; // Work around gcc.gnu.org/PR56135. + QualType NewCallOpType = TransformFunctionProtoType( + NewCallOpTLBuilder, OldCallOpFPTL, nullptr, Qualifiers(), + [&](FunctionProtoType::ExceptionSpecInfo &ESI, bool &Changed) { + return This->TransformExceptionSpec(OldCallOpFPTL.getBeginLoc(), ESI, + ExceptionStorage, Changed); + }); + if (NewCallOpType.isNull()) + return ExprError(); + NewCallOpTSI = + NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, NewCallOpType); + } + + // Transform the trailing requires clause + ExprResult NewTrailingRequiresClause; + if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause()) + // FIXME: Concepts: Substitution into requires clause should only happen + // when checking satisfaction. + NewTrailingRequiresClause = getDerived().TransformExpr(TRC); + + getSema().buildCompleteLambdaCallOperator( + NewCallOperator, E->getCallOperator()->getLocation(), + NewTrailingRequiresClause.get(), NewCallOpTSI, + E->getCallOperator()->getConstexprKind(), + NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(), + E->hasExplicitResultType()); + + getDerived().transformAttrs(E->getCallOperator(), NewCallOperator); + getDerived().transformedLocalDecl(E->getCallOperator(), {NewCallOperator}); + + { + // Number the lambda for linkage purposes if necessary. + Sema::ContextRAII ManglingContext(getSema(), Class->getDeclContext()); + getSema().handleLambdaNumbering(Class, NewCallOperator, Mangling); + } + // FIXME: Sema's lambda-building mechanism expects us to push an expression // evaluation context even if we're not transforming the function body. getSema().PushExpressionEvaluationContext( Index: clang/lib/Sema/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -540,11 +540,9 @@ SourceRange IntroducerRange, LambdaCaptureDefault CaptureDefault, SourceLocation CaptureDefaultLoc, - bool ExplicitParams, bool ExplicitResultType, - bool Mutable) { + bool ExplicitParams, bool Mutable) { buildLambdaScopeCaptures(LSI, CallOperator, IntroducerRange, CaptureDefault, CaptureDefaultLoc, ExplicitParams, Mutable); - buildLambdaScopeReturnType(*this, LSI, CallOperator, ExplicitResultType); } void Sema::finishLambdaExplicitCaptures(LambdaScopeInfo *LSI) { @@ -953,28 +951,74 @@ return MethodTyInfo; } -static CXXMethodDecl *CreateMethod(Sema &S, SourceRange IntroducerRange, - CXXRecordDecl *Class) { +CXXMethodDecl *Sema::CreateLambdaCallOperator(SourceRange IntroducerRange, + CXXRecordDecl *Class) { + // C++11 [expr.prim.lambda]p5: // The closure type for a lambda-expression has a public inline function // call operator (13.5.4) whose parameters and return type are described // by the lambda-expression's parameter-declaration-clause and // trailing-return-type respectively. DeclarationName MethodName = - S.Context.DeclarationNames.getCXXOperatorName(OO_Call); + Context.DeclarationNames.getCXXOperatorName(OO_Call); DeclarationNameLoc MethodNameLoc = DeclarationNameLoc::makeCXXOperatorNameLoc(IntroducerRange.getBegin()); CXXMethodDecl *Method = CXXMethodDecl::Create( - S.Context, Class, SourceLocation(), + Context, Class, SourceLocation(), DeclarationNameInfo(MethodName, IntroducerRange.getBegin(), MethodNameLoc), - QualType(), nullptr, SC_None, S.getCurFPFeatures().isFPConstrained(), + QualType(), nullptr, SC_None, getCurFPFeatures().isFPConstrained(), /*isInline=*/true, ConstexprSpecKind::Unspecified, SourceLocation(), nullptr); Method->setAccess(AS_public); return Method; } +void Sema::buildCompleteLambdaCallOperator( + CXXMethodDecl *Method, SourceLocation Loc, Expr *TrailingRequiresClause, + TypeSourceInfo *MethodTyInfo, ConstexprSpecKind ConstexprKind, + ArrayRef<ParmVarDecl *> Params, bool HasExplicitResultType) { + + LambdaScopeInfo *const LSI = getCurrentLambdaScopeUnsafe(*this); + + if (TrailingRequiresClause) + Method->setTrailingRequiresClause(TrailingRequiresClause); + + TemplateParameterList *TemplateParams = + getGenericLambdaTemplateParameterList(LSI, *this); + + auto DC = Method->getLexicalDeclContext(); + Method->setLexicalDeclContext(LSI->Lambda); + if (TemplateParams) { + FunctionTemplateDecl *const TemplateMethod = FunctionTemplateDecl::Create( + Context, LSI->Lambda, Method->getLocation(), Method->getDeclName(), + TemplateParams, Method); + TemplateMethod->setAccess(AS_public); + Method->setDescribedFunctionTemplate(TemplateMethod); + LSI->Lambda->addDecl(TemplateMethod); + TemplateMethod->setLexicalDeclContext(DC); + } else { + LSI->Lambda->addDecl(Method); + } + LSI->Lambda->setLambdaIsGeneric(TemplateParams); + LSI->Lambda->setLambdaTypeInfo(MethodTyInfo); + + Method->setLexicalDeclContext(DC); + Method->setLocation(Loc); + Method->setTypeSourceInfo(MethodTyInfo); + Method->setType(buildTypeForLambdaCallOperator(*this, LSI->Lambda, + TemplateParams, MethodTyInfo)); + Method->setConstexprKind(ConstexprKind); + if (!Params.empty()) { + CheckParmsForFunctionDef(Params, /*CheckParameterNames=*/false); + Method->setParams(Params); + for (auto P : Method->parameters()) + P->setOwningFunction(Method); + } + + buildLambdaScopeReturnType(*this, LSI, Method, HasExplicitResultType); +} + void Sema::ActOnLambdaIntroducer(LambdaIntroducer &Intro, Scope *CurrentScope) { LambdaScopeInfo *const LSI = getCurLambda(); @@ -1016,7 +1060,7 @@ // by the lambda-expression's parameter-declaration-clause and // trailing-return-type respectively. - CXXMethodDecl *Method = CreateMethod(*this, Intro.Range, Class); + CXXMethodDecl *Method = CreateLambdaCallOperator(Intro.Range, Class); LSI->CallOperator = Method; Method->setLexicalDeclContext(CurContext); @@ -1312,40 +1356,9 @@ CXXRecordDecl *Class = LSI->Lambda; CXXMethodDecl *Method = LSI->CallOperator; - if (auto *C = ParamInfo.getTrailingRequiresClause()) - Method->setTrailingRequiresClause(C); - - TemplateParameterList *TemplateParams = - getGenericLambdaTemplateParameterList(LSI, *this); - - auto DC = Method->getLexicalDeclContext(); - Method->setLexicalDeclContext(Class); - if (TemplateParams) { - FunctionTemplateDecl *const TemplateMethod = FunctionTemplateDecl::Create( - Context, Class, Method->getLocation(), Method->getDeclName(), - TemplateParams, Method); - TemplateMethod->setAccess(AS_public); - Method->setDescribedFunctionTemplate(TemplateMethod); - Class->addDecl(TemplateMethod); - TemplateMethod->setLexicalDeclContext(DC); - } else { - Class->addDecl(Method); - } - Method->setLexicalDeclContext(DC); - Class->setLambdaIsGeneric(TemplateParams); - TypeSourceInfo *MethodTyInfo = getLambdaType( *this, Intro, ParamInfo, getCurScope(), TypeLoc, ExplicitResultType); - Class->setLambdaTypeInfo(MethodTyInfo); - Method->setInnerLocStart(LambdaLoc); - Method->setLocation(Intro.Range.getBegin()); - Method->setTypeSourceInfo(MethodTyInfo); - Method->setType(buildTypeForLambdaCallOperator(*this, Class, TemplateParams, - MethodTyInfo)); - Method->setConstexprKind(ParamInfo.getDeclSpec().getConstexprSpecifier()); - buildLambdaScopeReturnType(*this, LSI, Method, ExplicitResultType); - LSI->ExplicitParams = ParamInfo.getNumTypeObjects() != 0; if (ParamInfo.isFunctionDeclarator() != 0 && @@ -1359,14 +1372,15 @@ } } - ContextRAII ManglingContext(*this, Class->getDeclContext()); + Method->setInnerLocStart(LambdaLoc); + buildCompleteLambdaCallOperator( + Method, Intro.Range.getBegin(), ParamInfo.getTrailingRequiresClause(), + MethodTyInfo, ParamInfo.getDeclSpec().getConstexprSpecifier(), Params, + ExplicitResultType); - CheckParmsForFunctionDef(Params, /*CheckParameterNames=*/false); + ContextRAII ManglingContext(*this, Class->getDeclContext()); - if (LSI->ExplicitParams) { - Method->setParams(Params); - CheckCXXDefaultArguments(Method); - } + CheckCXXDefaultArguments(Method); // This represents the function body for the lambda function, check if we // have to apply optnone due to a pragma. Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -6849,9 +6849,15 @@ LambdaCaptureDefault CaptureDefault, SourceLocation CaptureDefaultLoc, bool ExplicitParams, - bool ExplicitResultType, bool Mutable); + CXXMethodDecl *CreateLambdaCallOperator(SourceRange IntroducerRange, + CXXRecordDecl *Class); + void buildCompleteLambdaCallOperator( + CXXMethodDecl *Method, SourceLocation Loc, Expr *TrailingRequiresClause, + TypeSourceInfo *MethodTyInfo, ConstexprSpecKind ConstexprKind, + ArrayRef<ParmVarDecl *> Params, bool HasExplicitResultType); + /// Perform initialization analysis of the init-capture and perform /// any implicit conversions such as an lvalue-to-rvalue conversion if /// not being used to initialize a reference.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits