[clang] 48bda00 - [Clang] [Sema] Don't crash on unexpanded pack in invalid block literal (#110762)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 11 11:03:47 PDT 2024
Author: Sirraide
Date: 2024-10-11T20:03:43+02:00
New Revision: 48bda00b281a432d6de5e5e5dde6c5d66b992ac8
URL: https://github.com/llvm/llvm-project/commit/48bda00b281a432d6de5e5e5dde6c5d66b992ac8
DIFF: https://github.com/llvm/llvm-project/commit/48bda00b281a432d6de5e5e5dde6c5d66b992ac8.diff
LOG: [Clang] [Sema] Don't crash on unexpanded pack in invalid block literal (#110762)
Consider #109148:
```c++
template <typename ...Ts>
void f() {
[] {
(^Ts);
};
}
```
When we encounter `^Ts`, we try to parse a block and subsequently call
`DiagnoseUnexpandedParameterPack()` (in `ActOnBlockArguments()`), which
sees `Ts` and sets `ContainsUnexpandedParameterPack` to `true` in the
`LambdaScopeInfo` of the enclosing lambda. However, the entire block is
subsequently discarded entirely because it isn’t even syntactically
well-formed. As a result, `ContainsUnexpandedParameterPack` is `true`
despite the lambda’s body no longer containing any unexpanded packs,
which causes an assertion the next time
`DiagnoseUnexpandedParameterPack()` is called.
This pr moves handling of unexpanded parameter packs into
`CapturingScopeInfo` instead so that the same logic is used for both
blocks and lambdas. This fixes this issue since the
`ContainsUnexpandedParameterPack` flag is now part of the block (and
before that, its `CapturingScopeInfo`) and no longer affects the
surrounding lambda directly when the block is parsed. Moreover, this
change makes blocks actually usable with pack expansion.
This fixes #109148.
Added:
clang/test/SemaCXX/block-packs.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/ComputeDependence.h
clang/include/clang/AST/Expr.h
clang/include/clang/Sema/ScopeInfo.h
clang/include/clang/Sema/Sema.h
clang/include/clang/Sema/Template.h
clang/lib/AST/ComputeDependence.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaLambda.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/SemaTemplateVariadic.cpp
clang/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 763bc3ac159322..b8fdef8d463a79 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -505,6 +505,8 @@ Bug Fixes to C++ Support
- Fix a crash when parsing a pseudo destructor involving an invalid type. (#GH111460)
- Fixed an assertion failure when invoking recovery call expressions with explicit attributes
and undeclared templates. (#GH107047, #GH49093)
+- Clang no longer crashes when a lambda contains an invalid block declaration that contains an unexpanded
+ parameter pack. (#GH109148)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h
index 1a8507cfbf9872..e96275e5f2e073 100644
--- a/clang/include/clang/AST/ComputeDependence.h
+++ b/clang/include/clang/AST/ComputeDependence.h
@@ -133,7 +133,8 @@ ExprDependence computeDependence(ArrayInitLoopExpr *E);
ExprDependence computeDependence(ImplicitValueInitExpr *E);
ExprDependence computeDependence(InitListExpr *E);
ExprDependence computeDependence(ExtVectorElementExpr *E);
-ExprDependence computeDependence(BlockExpr *E);
+ExprDependence computeDependence(BlockExpr *E,
+ bool ContainsUnexpandedParameterPack);
ExprDependence computeDependence(AsTypeExpr *E);
ExprDependence computeDependence(DeclRefExpr *E, const ASTContext &Ctx);
ExprDependence computeDependence(RecoveryExpr *E);
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 607bf313c4d95e..cbe62411d11bff 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -6413,9 +6413,9 @@ class BlockExpr : public Expr {
protected:
BlockDecl *TheBlock;
public:
- BlockExpr(BlockDecl *BD, QualType ty)
+ BlockExpr(BlockDecl *BD, QualType ty, bool ContainsUnexpandedParameterPack)
: Expr(BlockExprClass, ty, VK_PRValue, OK_Ordinary), TheBlock(BD) {
- setDependence(computeDependence(this));
+ setDependence(computeDependence(this, ContainsUnexpandedParameterPack));
}
/// Build an empty block expression.
diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h
index 700e361ef83f13..958d65055fa9b2 100644
--- a/clang/include/clang/Sema/ScopeInfo.h
+++ b/clang/include/clang/Sema/ScopeInfo.h
@@ -724,10 +724,16 @@ class CapturingScopeInfo : public FunctionScopeInfo {
/// is deduced (e.g. a lambda or block with omitted return type).
bool HasImplicitReturnType = false;
+ /// Whether this contains an unexpanded parameter pack.
+ bool ContainsUnexpandedParameterPack = false;
+
/// ReturnType - The target type of return statements in this context,
/// or null if unknown.
QualType ReturnType;
+ /// Packs introduced by this, if any.
+ SmallVector<NamedDecl *, 4> LocalPacks;
+
void addCapture(ValueDecl *Var, bool isBlock, bool isByref, bool isNested,
SourceLocation Loc, SourceLocation EllipsisLoc,
QualType CaptureType, bool Invalid) {
@@ -895,12 +901,6 @@ class LambdaScopeInfo final :
/// Whether any of the capture expressions requires cleanups.
CleanupInfo Cleanup;
- /// Whether the lambda contains an unexpanded parameter pack.
- bool ContainsUnexpandedParameterPack = false;
-
- /// Packs introduced by this lambda, if any.
- SmallVector<NamedDecl*, 4> LocalPacks;
-
/// Source range covering the explicit template parameter list (if it exists).
SourceRange ExplicitTemplateParamsRange;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 24191fd688dc5a..2d0e31e90895a0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -702,10 +702,10 @@ class Sema final : public SemaBase {
/// 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;
+ /// Get the innermost lambda or block enclosing the current location, if any.
+ /// This looks through intervening non-lambda, non-block scopes such as local
+ /// functions.
+ sema::CapturingScopeInfo *getEnclosingLambdaOrBlock() const;
/// Retrieve the current lambda scope info, if any.
/// \param IgnoreNonLambdaCapturingScope true if should find the top-most
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index fe27290efdbfc5..6872d04cc4dfb9 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -411,10 +411,10 @@ enum class TemplateSubstitutionKind : char {
/// lookup will search our outer scope.
bool CombineWithOuterScope;
- /// Whether this scope is being used to instantiate a lambda expression,
- /// in which case it should be reused for instantiating the lambda's
- /// FunctionProtoType.
- bool InstantiatingLambda = false;
+ /// Whether this scope is being used to instantiate a lambda or block
+ /// expression, in which case it should be reused for instantiating the
+ /// lambda's FunctionProtoType.
+ bool InstantiatingLambdaOrBlock = false;
/// If non-NULL, the template parameter pack that has been
/// partially substituted per C++0x [temp.arg.explicit]p9.
@@ -431,10 +431,10 @@ enum class TemplateSubstitutionKind : char {
public:
LocalInstantiationScope(Sema &SemaRef, bool CombineWithOuterScope = false,
- bool InstantiatingLambda = false)
+ bool InstantiatingLambdaOrBlock = false)
: SemaRef(SemaRef), Outer(SemaRef.CurrentInstantiationScope),
CombineWithOuterScope(CombineWithOuterScope),
- InstantiatingLambda(InstantiatingLambda) {
+ InstantiatingLambdaOrBlock(InstantiatingLambdaOrBlock) {
SemaRef.CurrentInstantiationScope = this;
}
@@ -561,8 +561,8 @@ enum class TemplateSubstitutionKind : char {
/// Determine whether D is a pack expansion created in this scope.
bool isLocalPackExpansion(const Decl *D);
- /// Determine whether this scope is for instantiating a lambda.
- bool isLambda() const { return InstantiatingLambda; }
+ /// Determine whether this scope is for instantiating a lambda or block.
+ bool isLambdaOrBlock() const { return InstantiatingLambdaOrBlock; }
};
class TemplateDeclInstantiator
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 8c79df8317a2ec..e37ebec0851951 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -252,10 +252,13 @@ ExprDependence clang::computeDependence(ExtVectorElementExpr *E) {
return E->getBase()->getDependence();
}
-ExprDependence clang::computeDependence(BlockExpr *E) {
+ExprDependence clang::computeDependence(BlockExpr *E,
+ bool ContainsUnexpandedParameterPack) {
auto D = toExprDependenceForImpliedType(E->getType()->getDependence());
if (E->getBlockDecl()->isDependentContext())
D |= ExprDependence::Instantiation;
+ if (ContainsUnexpandedParameterPack)
+ D |= ExprDependence::UnexpandedPack;
return D;
}
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 9f91ee9a39f2f9..f0d1634af529f0 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -2385,10 +2385,11 @@ FunctionScopeInfo *Sema::getEnclosingFunction() const {
return nullptr;
}
-LambdaScopeInfo *Sema::getEnclosingLambda() const {
+CapturingScopeInfo *Sema::getEnclosingLambdaOrBlock() const {
for (auto *Scope : llvm::reverse(FunctionScopes)) {
- if (auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Scope)) {
- if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext) &&
+ if (auto *CSI = dyn_cast<CapturingScopeInfo>(Scope)) {
+ auto *LSI = dyn_cast<LambdaScopeInfo>(CSI);
+ if (LSI && LSI->Lambda && !LSI->Lambda->Encloses(CurContext) &&
LSI->AfterParameterList) {
// We have switched contexts due to template instantiation.
// FIXME: We should swap out the FunctionScopes during code synthesis
@@ -2396,7 +2397,7 @@ LambdaScopeInfo *Sema::getEnclosingLambda() const {
assert(!CodeSynthesisContexts.empty());
return nullptr;
}
- return LSI;
+ return CSI;
}
}
return nullptr;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 118873bc93ad4b..f100f1c6e73966 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15165,8 +15165,8 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
// 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);
+ if (auto *CSI = getEnclosingLambdaOrBlock())
+ CSI->LocalPacks.push_back(New);
if (New->getType().hasNonTrivialToPrimitiveDestructCUnion() ||
New->getType().hasNonTrivialToPrimitiveCopyCUnion())
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 4e37385710af5e..531e7f6933d6d3 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -16087,17 +16087,7 @@ void Sema::ActOnBlockArguments(SourceLocation CaretLoc, Declarator &ParamInfo,
TypeSourceInfo *Sig = GetTypeForDeclarator(ParamInfo);
QualType T = Sig->getType();
-
- // FIXME: We should allow unexpanded parameter packs here, but that would,
- // in turn, make the block expression contain unexpanded parameter packs.
- if (DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block)) {
- // Drop the parameters.
- FunctionProtoType::ExtProtoInfo EPI;
- EPI.HasTrailingReturn = false;
- EPI.TypeQuals.addConst();
- T = Context.getFunctionType(Context.DependentTy, std::nullopt, EPI);
- Sig = Context.getTrivialTypeSourceInfo(T);
- }
+ DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block);
// GetTypeForDeclarator always produces a function type for a block
// literal signature. Furthermore, it is always a FunctionProtoType
@@ -16369,7 +16359,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
PoppedFunctionScopePtr ScopeRAII = PopFunctionScopeInfo(&WP, BD, BlockTy);
- BlockExpr *Result = new (Context) BlockExpr(BD, BlockTy);
+ BlockExpr *Result = new (Context)
+ BlockExpr(BD, BlockTy, BSI->ContainsUnexpandedParameterPack);
// If the block isn't obviously global, i.e. it captures anything at
// all, then we need to do a few things in the surrounding context:
@@ -16392,6 +16383,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
if (getCurFunction())
getCurFunction()->addBlock(BD);
+ // This can happen if the block's return type is deduced, but
+ // the return expression is invalid.
if (BD->isInvalidDecl())
return CreateRecoveryExpr(Result->getBeginLoc(), Result->getEndLoc(),
{Result}, Result->getType());
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index aeb20299b714a3..7b9d5f4ff7eb34 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -2353,7 +2353,10 @@ ExprResult Sema::BuildBlockForLambdaConversion(SourceLocation CurrentLocation,
Block->setBody(new (Context) CompoundStmt(ConvLocation));
// Create the block literal expression.
- Expr *BuildBlock = new (Context) BlockExpr(Block, Conv->getConversionType());
+ // TODO: Do we ever get here if we have unexpanded packs in the lambda???
+ Expr *BuildBlock =
+ new (Context) BlockExpr(Block, Conv->getConversionType(),
+ /*ContainsUnexpandedParameterPack=*/false);
ExprCleanupObjects.push_back(Block);
Cleanup.setExprNeedsCleanups(true);
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c7d48b81bc0347..490689321a9b55 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1010,8 +1010,8 @@ NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename,
Param->setAccess(AS_public);
if (Param->isParameterPack())
- if (auto *LSI = getEnclosingLambda())
- LSI->LocalPacks.push_back(Param);
+ if (auto *CSI = getEnclosingLambdaOrBlock())
+ CSI->LocalPacks.push_back(Param);
if (ParamName) {
maybeDiagnoseTemplateParameterShadow(*this, S, ParamNameLoc, ParamName);
@@ -1542,8 +1542,8 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
Param->setInvalidDecl();
if (Param->isParameterPack())
- if (auto *LSI = getEnclosingLambda())
- LSI->LocalPacks.push_back(Param);
+ if (auto *CSI = getEnclosingLambdaOrBlock())
+ CSI->LocalPacks.push_back(Param);
if (ParamName) {
maybeDiagnoseTemplateParameterShadow(*this, S, D.getIdentifierLoc(),
@@ -1593,7 +1593,7 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
Param->setAccess(AS_public);
if (Param->isParameterPack())
- if (auto *LSI = getEnclosingLambda())
+ if (auto *LSI = getEnclosingLambdaOrBlock())
LSI->LocalPacks.push_back(Param);
// If the template template parameter has a name, then link the identifier
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 2263b76520ca25..f5c33f42d7e3af 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1694,12 +1694,18 @@ namespace {
if (SemaRef.RebuildingImmediateInvocation)
return E;
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
- /*InstantiatingLambda=*/true);
+ /*InstantiatingLambdaOrBlock=*/true);
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
return inherited::TransformLambdaExpr(E);
}
+ ExprResult TransformBlockExpr(BlockExpr *E) {
+ LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
+ /*InstantiatingLambdaOrBlock=*/true);
+ return inherited::TransformBlockExpr(E);
+ }
+
ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
LambdaScopeInfo *LSI) {
CXXMethodDecl *MD = LSI->CallOperator;
@@ -2488,7 +2494,7 @@ QualType TemplateInstantiator::TransformFunctionProtoType(TypeLocBuilder &TLB,
CXXRecordDecl *ThisContext,
Qualifiers ThisTypeQuals,
Fn TransformExceptionSpec) {
- // If this is a lambda, the transformation MUST be done in the
+ // If this is a lambda or block, the transformation MUST be done in the
// CurrentInstantiationScope since it introduces a mapping of
// the original to the newly created transformed parameters.
//
@@ -2497,7 +2503,7 @@ QualType TemplateInstantiator::TransformFunctionProtoType(TypeLocBuilder &TLB,
// a second one.
LocalInstantiationScope *Current = getSema().CurrentInstantiationScope;
std::optional<LocalInstantiationScope> Scope;
- if (!Current || !Current->isLambda())
+ if (!Current || !Current->isLambdaOrBlock())
Scope.emplace(SemaRef, /*CombineWithOuterScope=*/true);
return inherited::TransformFunctionProtoType(
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 40522a07f6339c..19bd4547665835 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -18,6 +18,7 @@
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/Template.h"
+#include "llvm/Support/SaveAndRestore.h"
#include <optional>
using namespace clang;
@@ -36,7 +37,7 @@ namespace {
SmallVectorImpl<UnexpandedParameterPack> &Unexpanded;
- bool InLambda = false;
+ bool InLambdaOrBlock = false;
unsigned DepthLimit = (unsigned)-1;
#ifndef NDEBUG
@@ -140,7 +141,7 @@ namespace {
/// do not contain unexpanded parameter packs.
bool TraverseStmt(Stmt *S) {
Expr *E = dyn_cast_or_null<Expr>(S);
- if ((E && E->containsUnexpandedParameterPack()) || InLambda)
+ if ((E && E->containsUnexpandedParameterPack()) || InLambdaOrBlock)
return inherited::TraverseStmt(S);
return true;
@@ -149,7 +150,8 @@ namespace {
/// Suppress traversal into types that do not contain
/// unexpanded parameter packs.
bool TraverseType(QualType T) {
- if ((!T.isNull() && T->containsUnexpandedParameterPack()) || InLambda)
+ if ((!T.isNull() && T->containsUnexpandedParameterPack()) ||
+ InLambdaOrBlock)
return inherited::TraverseType(T);
return true;
@@ -160,7 +162,7 @@ namespace {
bool TraverseTypeLoc(TypeLoc TL) {
if ((!TL.getType().isNull() &&
TL.getType()->containsUnexpandedParameterPack()) ||
- InLambda)
+ InLambdaOrBlock)
return inherited::TraverseTypeLoc(TL);
return true;
@@ -262,20 +264,28 @@ namespace {
if (!Lambda->containsUnexpandedParameterPack())
return true;
- bool WasInLambda = InLambda;
+ SaveAndRestore _(InLambdaOrBlock, true);
unsigned OldDepthLimit = DepthLimit;
- InLambda = true;
if (auto *TPL = Lambda->getTemplateParameterList())
DepthLimit = TPL->getDepth();
inherited::TraverseLambdaExpr(Lambda);
- InLambda = WasInLambda;
DepthLimit = OldDepthLimit;
return true;
}
+ /// Analogously for blocks.
+ bool TraverseBlockExpr(BlockExpr *Block) {
+ if (!Block->containsUnexpandedParameterPack())
+ return true;
+
+ SaveAndRestore _(InLambdaOrBlock, true);
+ inherited::TraverseBlockExpr(Block);
+ return true;
+ }
+
/// Suppress traversal within pack expansions in lambda captures.
bool TraverseLambdaCapture(LambdaExpr *Lambda, const LambdaCapture *C,
Expr *Init) {
@@ -323,11 +333,11 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
// If we are within a lambda expression and referencing a pack that is not
// declared within the lambda itself, that lambda contains an unexpanded
- // parameter pack, and we are done.
+ // parameter pack, and we are done. Analogously for blocks.
// FIXME: Store 'Unexpanded' on the lambda so we don't need to recompute it
// later.
- SmallVector<UnexpandedParameterPack, 4> LambdaParamPackReferences;
- if (auto *LSI = getEnclosingLambda()) {
+ SmallVector<UnexpandedParameterPack, 4> ParamPackReferences;
+ if (sema::CapturingScopeInfo *CSI = getEnclosingLambdaOrBlock()) {
for (auto &Pack : Unexpanded) {
auto DeclaresThisPack = [&](NamedDecl *LocalPack) {
if (auto *TTPT = Pack.first.dyn_cast<const TemplateTypeParmType *>()) {
@@ -336,11 +346,11 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
}
return declaresSameEntity(Pack.first.get<NamedDecl *>(), LocalPack);
};
- if (llvm::any_of(LSI->LocalPacks, DeclaresThisPack))
- LambdaParamPackReferences.push_back(Pack);
+ if (llvm::any_of(CSI->LocalPacks, DeclaresThisPack))
+ ParamPackReferences.push_back(Pack);
}
- if (LambdaParamPackReferences.empty()) {
+ if (ParamPackReferences.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
@@ -363,16 +373,16 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
}
// Coumpound-statements outside the lambda are OK for now; we'll check
// for those when we finish handling the lambda.
- if (Func == LSI)
+ if (Func == CSI)
break;
}
if (!EnclosingStmtExpr) {
- LSI->ContainsUnexpandedParameterPack = true;
+ CSI->ContainsUnexpandedParameterPack = true;
return false;
}
} else {
- Unexpanded = LambdaParamPackReferences;
+ Unexpanded = ParamPackReferences;
}
}
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm
index e93c37f3b9ae12..b644e0fdfe3c0f 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.mm
@@ -119,17 +119,16 @@ void call_with_lambda() {
template<typename ... Args> static void f1()
{
- (void)^(Args args) { // expected-error{{block contains unexpanded parameter pack 'Args'}}
+ (void)^(Args args) { // expected-error{{expression contains unexpanded parameter pack 'Args'}}
};
}
template<typename ... Args> static void f2()
{
- // FIXME: Allow this.
f(
- ^(Args args) // expected-error{{block contains unexpanded parameter pack 'Args'}}
+ ^(Args args)
{ }
- ... // expected-error{{pack expansion does not contain any unexpanded parameter packs}}
+ ...
);
}
diff --git a/clang/test/SemaCXX/block-packs.cpp b/clang/test/SemaCXX/block-packs.cpp
new file mode 100644
index 00000000000000..4f3d68a727b7c9
--- /dev/null
+++ b/clang/test/SemaCXX/block-packs.cpp
@@ -0,0 +1,64 @@
+// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin -fsyntax-only -verify -Wno-unused %s
+// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin -fsyntax-only -verify -Wno-unused %s -frecovery-ast -frecovery-ast-type
+
+template <typename ...Ts>
+void f() {
+ ((^ { Ts t; }), ...);
+ ((^ (Ts t) {}), ...);
+ ((^ Ts () {}), ...);
+
+ ^ { Ts t; }; // expected-error {{unexpanded parameter pack 'Ts'}}
+ ^ (Ts t) {}; // expected-error {{unexpanded parameter pack 'Ts'}}
+ ^ Ts () {}; // expected-error {{unexpanded parameter pack 'Ts'}}
+}
+
+template <typename ...Ts>
+void gh109148() {
+ (^Ts); // expected-error {{expected expression}}
+
+ [] {
+ (^Ts); // expected-error {{expected expression}}
+ ^Ts; // expected-error {{expected expression}}
+ ^(Ts); // expected-error {{expected expression}}
+ ^ Ts); // expected-error {{expected expression}}
+ };
+
+ ([] {
+ (^Ts); // expected-error {{expected expression}}
+ ^Ts; // expected-error {{expected expression}}
+ ^(Ts); // expected-error {{expected expression}}
+ ^ Ts); // expected-error {{expected expression}}
+ }, ...); // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
+
+ [] { // expected-error {{unexpanded parameter pack 'Ts'}}
+ ^ (Ts) {};
+ };
+
+ [] { // expected-error {{unexpanded parameter pack 'Ts'}}
+ ^ { Ts x; };
+ };
+
+ [] { // expected-error {{unexpanded parameter pack 'Ts'}}
+ Ts s;
+ (^Ts); // expected-error {{expected expression}}
+ };
+
+ ([] {
+ Ts s;
+ (^Ts); // expected-error {{expected expression}}
+ }, ...);
+
+ [] { // expected-error {{unexpanded parameter pack 'Ts'}}
+ ^ { Ts s; return not_defined; }; // expected-error {{use of undeclared identifier 'not_defined'}}
+ };
+}
+
+void g() {
+ f<>();
+ f<int>();
+ f<long, float>();
+
+ gh109148<>();
+ gh109148<int>();
+ gh109148<long, float>();
+}
More information about the cfe-commits
mailing list