diff options
-rw-r--r-- | include/clang/Sema/ScopeInfo.h | 3 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 5 | ||||
-rw-r--r-- | lib/Parse/ParseInit.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/Sema.cpp | 17 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 7 | ||||
-rw-r--r-- | lib/Sema/SemaLambda.cpp | 15 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 12 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateVariadic.cpp | 72 | ||||
-rw-r--r-- | test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp | 13 |
9 files changed, 111 insertions, 34 deletions
diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index 344ef78230..4f7534f9ef 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -820,6 +820,9 @@ public: /// Whether the lambda contains an unexpanded parameter pack. bool ContainsUnexpandedParameterPack = false; + /// Packs introduced by this lambda, if any. + SmallVector<NamedDecl*, 4> LocalPacks; + /// If this is a generic lambda, use this as the depth of /// each 'auto' parameter, during initial AST construction. unsigned AutoTemplateParameterDepth = 0; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 19cf8041dd..624238c00e 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1474,6 +1474,11 @@ public: /// Retrieve the current block, if any. sema::BlockScopeInfo *getCurBlock(); + /// Get the innermost lambda enclosing the current location, if any. This + /// looks through intervening non-lambda scopes such as local functions and + /// blocks. + sema::LambdaScopeInfo *getEnclosingLambda() const; + /// Retrieve the current lambda scope info, if any. /// \param IgnoreNonLambdaCapturingScope true if should find the top-most /// lambda scope info ignoring all inner capturing scopes that are not diff --git a/lib/Parse/ParseInit.cpp b/lib/Parse/ParseInit.cpp index 769140f2d3..eaf31e81b9 100644 --- a/lib/Parse/ParseInit.cpp +++ b/lib/Parse/ParseInit.cpp @@ -39,6 +39,7 @@ bool Parser::MayBeDesignationStart() { // cases here, and fall back to tentative parsing if those fail. switch (PP.LookAhead(0).getKind()) { case tok::equal: + case tok::ellipsis: case tok::r_square: // Definitely starts a lambda expression. return false; diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index d4be3e9242..152cc7d9a2 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -1804,6 +1804,22 @@ FunctionScopeInfo *Sema::getEnclosingFunction() const { return nullptr; } +LambdaScopeInfo *Sema::getEnclosingLambda() const { + for (auto *Scope : llvm::reverse(FunctionScopes)) { + if (auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Scope)) { + if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext)) { + // We have switched contexts due to template instantiation. + // FIXME: We should swap out the FunctionScopes during code synthesis + // so that we don't need to check for this. + assert(!CodeSynthesisContexts.empty()); + return nullptr; + } + return LSI; + } + } + return nullptr; +} + LambdaScopeInfo *Sema::getCurLambda(bool IgnoreNonLambdaCapturingScope) { if (FunctionScopes.empty()) return nullptr; @@ -1826,6 +1842,7 @@ LambdaScopeInfo *Sema::getCurLambda(bool IgnoreNonLambdaCapturingScope) { return CurLSI; } + // We have a generic lambda if we parsed auto parameters, or we have // an associated template parameter list. LambdaScopeInfo *Sema::getCurGenericLambda() { diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index b0d610401a..a96f0d28e8 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -12706,6 +12706,13 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, Context.getAdjustedParameterType(T), TSInfo, SC, nullptr); + // Make a note if we created a new pack in the scope of a lambda, so that + // we know that references to that pack must also be expanded within the + // lambda scope. + if (New->isParameterPack()) + if (auto *LSI = getEnclosingLambda()) + LSI->LocalPacks.push_back(New); + // Parameters can not be abstract class types. // For record types, this is done by the AbstractClassUsageDiagnoser once // the class has been completely parsed. diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index e7c51a3e3d..a808854ed9 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -824,8 +824,11 @@ VarDecl *Sema::createLambdaInitCaptureVarDecl(SourceLocation Loc, // FIXME: Retain the TypeSourceInfo from buildLambdaInitCaptureInitialization // rather than reconstructing it here. TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(InitCaptureType, Loc); - if (auto PETL = TSI->getTypeLoc().getAs<PackExpansionTypeLoc>()) + bool IsInitCapturePack = false; + if (auto PETL = TSI->getTypeLoc().getAs<PackExpansionTypeLoc>()) { PETL.setEllipsisLoc(EllipsisLoc); + IsInitCapturePack = true; + } // Create a dummy variable representing the init-capture. This is not actually // used as a variable, and only exists as a way to name and refer to the @@ -839,6 +842,8 @@ VarDecl *Sema::createLambdaInitCaptureVarDecl(SourceLocation Loc, NewVD->setInitStyle(static_cast<VarDecl::InitializationStyle>(InitStyle)); NewVD->markUsed(Context); NewVD->setInit(Init); + if (NewVD->isParameterPack()) + getCurLambda()->LocalPacks.push_back(NewVD); return NewVD; } @@ -928,12 +933,12 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, // Check for unexpanded parameter packs in the method type. if (MethodTyInfo->getType()->containsUnexpandedParameterPack()) - ContainsUnexpandedParameterPack = true; + DiagnoseUnexpandedParameterPack(Intro.Range.getBegin(), MethodTyInfo, + UPPC_DeclarationType); } CXXRecordDecl *Class = createLambdaClosureType(Intro.Range, MethodTyInfo, KnownDependent, Intro.Default); - CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params, ParamInfo.getDeclSpec().getConstexprSpecifier()); @@ -1053,7 +1058,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, if (C->Init.get()->containsUnexpandedParameterPack() && !C->InitCaptureType.get()->getAs<PackExpansionType>()) - ContainsUnexpandedParameterPack = true; + DiagnoseUnexpandedParameterPack(C->Init.get(), UPPC_Initializer); unsigned InitStyle; switch (C->InitKind) { @@ -1184,7 +1189,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, } finishLambdaExplicitCaptures(LSI); - LSI->ContainsUnexpandedParameterPack = ContainsUnexpandedParameterPack; + LSI->ContainsUnexpandedParameterPack |= ContainsUnexpandedParameterPack; // Add lambda parameters into scope. addLambdaParameters(Intro.Captures, Method, CurScope); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 00771e2246..d7a1d9ea1b 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1013,6 +1013,10 @@ NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename, Typename, IsParameterPack); Param->setAccess(AS_public); + if (Param->isParameterPack()) + if (auto *LSI = getEnclosingLambda()) + LSI->LocalPacks.push_back(Param); + if (ParamName) { maybeDiagnoseTemplateParameterShadow(*this, S, ParamNameLoc, ParamName); @@ -1211,6 +1215,10 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, if (Invalid) Param->setInvalidDecl(); + if (Param->isParameterPack()) + if (auto *LSI = getEnclosingLambda()) + LSI->LocalPacks.push_back(Param); + if (ParamName) { maybeDiagnoseTemplateParameterShadow(*this, S, D.getIdentifierLoc(), ParamName); @@ -1274,6 +1282,10 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(Scope* S, Name, Params); Param->setAccess(AS_public); + if (Param->isParameterPack()) + if (auto *LSI = getEnclosingLambda()) + LSI->LocalPacks.push_back(Param); + // If the template template parameter has a name, then link the identifier // into the scope and lookup mechanisms. if (Name) { diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index f90bff6e7a..975d6620c0 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -294,44 +294,58 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc, return false; // If we are within a lambda expression and referencing a pack that is not - // a parameter of the lambda itself, that lambda contains an unexpanded + // declared within the lambda itself, that lambda contains an unexpanded // parameter pack, and we are done. // FIXME: Store 'Unexpanded' on the lambda so we don't need to recompute it // later. SmallVector<UnexpandedParameterPack, 4> LambdaParamPackReferences; - for (unsigned N = FunctionScopes.size(); N; --N) { - sema::FunctionScopeInfo *Func = FunctionScopes[N-1]; - // We do not permit pack expansion that would duplicate a statement - // expression, not even within a lambda. - // FIXME: We could probably support this for statement expressions that do - // not contain labels, and for pack expansions that expand both the stmt - // expr and the enclosing lambda. - if (std::any_of( - Func->CompoundScopes.begin(), Func->CompoundScopes.end(), - [](sema::CompoundScopeInfo &CSI) { return CSI.IsStmtExpr; })) - break; + if (auto *LSI = getEnclosingLambda()) { + for (auto &Pack : Unexpanded) { + auto DeclaresThisPack = [&](NamedDecl *LocalPack) { + if (auto *TTPT = Pack.first.dyn_cast<const TemplateTypeParmType *>()) { + auto *TTPD = dyn_cast<TemplateTypeParmDecl>(LocalPack); + return TTPD && TTPD->getTypeForDecl() == TTPT; + } + return declaresSameEntity(Pack.first.get<NamedDecl *>(), LocalPack); + }; + if (std::find_if(LSI->LocalPacks.begin(), LSI->LocalPacks.end(), + DeclaresThisPack) != LSI->LocalPacks.end()) + LambdaParamPackReferences.push_back(Pack); + } - if (auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Func)) { - if (N == FunctionScopes.size()) { - for (auto &Pack : Unexpanded) { - auto *VD = dyn_cast_or_null<VarDecl>( - Pack.first.dyn_cast<NamedDecl *>()); - if (VD && VD->getDeclContext() == LSI->CallOperator) - LambdaParamPackReferences.push_back(Pack); + if (LambdaParamPackReferences.empty()) { + // Construct in lambda only references packs declared outside the lambda. + // That's OK for now, but the lambda itself is considered to contain an + // unexpanded pack in this case, which will require expansion outside the + // lambda. + + // We do not permit pack expansion that would duplicate a statement + // expression, not even within a lambda. + // FIXME: We could probably support this for statement expressions that + // do not contain labels. + // FIXME: This is insufficient to detect this problem; consider + // f( ({ bad: 0; }) + pack ... ); + bool EnclosingStmtExpr = false; + for (unsigned N = FunctionScopes.size(); N; --N) { + sema::FunctionScopeInfo *Func = FunctionScopes[N-1]; + if (std::any_of( + Func->CompoundScopes.begin(), Func->CompoundScopes.end(), + [](sema::CompoundScopeInfo &CSI) { return CSI.IsStmtExpr; })) { + EnclosingStmtExpr = true; + break; } + // Coumpound-statements outside the lambda are OK for now; we'll check + // for those when we finish handling the lambda. + if (Func == LSI) + break; } - // If we have references to a parameter pack of the innermost enclosing - // lambda, only diagnose those ones. We don't know whether any other - // unexpanded parameters referenced herein are actually unexpanded; - // they might be expanded at an outer level. - if (!LambdaParamPackReferences.empty()) { - Unexpanded = LambdaParamPackReferences; - break; + if (!EnclosingStmtExpr) { + LSI->ContainsUnexpandedParameterPack = true; + return false; } - - LSI->ContainsUnexpandedParameterPack = true; - return false; + } else { + Unexpanded = LambdaParamPackReferences; } } diff --git a/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp b/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp index b8022d2091..5c1eb32ad2 100644 --- a/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp +++ b/test/SemaCXX/cxx1y-generic-lambdas-variadics.cpp @@ -122,3 +122,16 @@ namespace PR33082 { b(Pack<int*, float*>(), 1, 2, 3); // expected-note {{instantiation of}} } } + +void pr42587() { + (void)[](auto... args) -> decltype(args) {}; // expected-error {{type contains unexpanded parameter pack}} + (void)[](auto... args, int = args) {}; // expected-error {{default argument contains unexpanded parameter pack}} + (void)[](auto... args, decltype(args)) {}; // expected-error {{type contains unexpanded parameter pack}} + (void)[](auto... args, decltype(args)...) {}; // (ok) + (void)[](auto... args, int = [=] { return args; }()) {}; // expected-error {{default argument contains unexpanded parameter pack}} + (void)([]<typename ...T> (T t) {} + ...); // expected-error {{contains unexpanded parameter pack 'T'}} expected-error {{does not contain any unexpanded}} expected-warning 0-2{{extension}} + (void)([]<int ...N> (int k = N) {} + ...); // expected-error {{contains unexpanded parameter pack 'N'}} expected-error {{does not contain any unexpanded}} expected-warning 0-2{{extension}} + (void)([]<template<typename> typename ...T> (T<int>) {} + ...); // expected-error {{contains unexpanded parameter pack 'T'}} expected-error {{does not contain any unexpanded}} expected-warning 0-3{{extension}} +} + +template<typename ...T> int v = {[...x = T()] { int k = x; } ...}; // expected-error {{contains unexpanded parameter pack 'x'}} expected-error {{does not contain any unexpanded}} expected-warning 0-1{{extension}} |