summaryrefslogtreecommitdiff
path: root/lib/Sema
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2019-08-26 22:51:28 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2019-08-26 22:51:28 +0000
commitd814f930835df25d66f9f4b22fd7762f89ad89a4 (patch)
tree21701f0e8d485fdfbe9ec7bdedd463b69c589035 /lib/Sema
parent4ea481ed968c192a0df8a58dc02b9ec6eb591f07 (diff)
downloadclang-d814f930835df25d66f9f4b22fd7762f89ad89a4.tar.gz
PR42587: diagnose unexpanded uses of a pack parameter of a generic
lambda from within the lambda-declarator. Instead of trying to reconstruct whether a parameter pack was declared inside a lambda (which we can't do correctly in general because we might not have attached parameters to their declaration contexts yet), track the set of parameter packs introduced in each live lambda scope, and require only those parameters to be immediately expanded when they appear inside that lambda. In passing, fix incorrect disambiguation of a lambda-expression starting with an init-capture pack in a braced-init-list. We previously incorrectly parsed that as a designated initializer. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@369985 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema')
-rw-r--r--lib/Sema/Sema.cpp17
-rw-r--r--lib/Sema/SemaDecl.cpp7
-rw-r--r--lib/Sema/SemaLambda.cpp15
-rw-r--r--lib/Sema/SemaTemplate.cpp12
-rw-r--r--lib/Sema/SemaTemplateVariadic.cpp72
5 files changed, 89 insertions, 34 deletions
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;
}
}