Skip to content

Commit 05ca9a5

Browse files
SirraideDanielCChen
authored andcommitted
[Clang] [Sema] Don't crash on unexpanded pack in invalid block literal (llvm#110762)
Consider llvm#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 llvm#109148.
1 parent 23375e5 commit 05ca9a5

File tree

16 files changed

+151
-69
lines changed

16 files changed

+151
-69
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,8 @@ Bug Fixes to C++ Support
505505
- Fix a crash when parsing a pseudo destructor involving an invalid type. (#GH111460)
506506
- Fixed an assertion failure when invoking recovery call expressions with explicit attributes
507507
and undeclared templates. (#GH107047, #GH49093)
508+
- Clang no longer crashes when a lambda contains an invalid block declaration that contains an unexpanded
509+
parameter pack. (#GH109148)
508510

509511
Bug Fixes to AST Handling
510512
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/ComputeDependence.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ ExprDependence computeDependence(ArrayInitLoopExpr *E);
133133
ExprDependence computeDependence(ImplicitValueInitExpr *E);
134134
ExprDependence computeDependence(InitListExpr *E);
135135
ExprDependence computeDependence(ExtVectorElementExpr *E);
136-
ExprDependence computeDependence(BlockExpr *E);
136+
ExprDependence computeDependence(BlockExpr *E,
137+
bool ContainsUnexpandedParameterPack);
137138
ExprDependence computeDependence(AsTypeExpr *E);
138139
ExprDependence computeDependence(DeclRefExpr *E, const ASTContext &Ctx);
139140
ExprDependence computeDependence(RecoveryExpr *E);

clang/include/clang/AST/Expr.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6413,9 +6413,9 @@ class BlockExpr : public Expr {
64136413
protected:
64146414
BlockDecl *TheBlock;
64156415
public:
6416-
BlockExpr(BlockDecl *BD, QualType ty)
6416+
BlockExpr(BlockDecl *BD, QualType ty, bool ContainsUnexpandedParameterPack)
64176417
: Expr(BlockExprClass, ty, VK_PRValue, OK_Ordinary), TheBlock(BD) {
6418-
setDependence(computeDependence(this));
6418+
setDependence(computeDependence(this, ContainsUnexpandedParameterPack));
64196419
}
64206420

64216421
/// Build an empty block expression.

clang/include/clang/Sema/ScopeInfo.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -724,10 +724,16 @@ class CapturingScopeInfo : public FunctionScopeInfo {
724724
/// is deduced (e.g. a lambda or block with omitted return type).
725725
bool HasImplicitReturnType = false;
726726

727+
/// Whether this contains an unexpanded parameter pack.
728+
bool ContainsUnexpandedParameterPack = false;
729+
727730
/// ReturnType - The target type of return statements in this context,
728731
/// or null if unknown.
729732
QualType ReturnType;
730733

734+
/// Packs introduced by this, if any.
735+
SmallVector<NamedDecl *, 4> LocalPacks;
736+
731737
void addCapture(ValueDecl *Var, bool isBlock, bool isByref, bool isNested,
732738
SourceLocation Loc, SourceLocation EllipsisLoc,
733739
QualType CaptureType, bool Invalid) {
@@ -895,12 +901,6 @@ class LambdaScopeInfo final :
895901
/// Whether any of the capture expressions requires cleanups.
896902
CleanupInfo Cleanup;
897903

898-
/// Whether the lambda contains an unexpanded parameter pack.
899-
bool ContainsUnexpandedParameterPack = false;
900-
901-
/// Packs introduced by this lambda, if any.
902-
SmallVector<NamedDecl*, 4> LocalPacks;
903-
904904
/// Source range covering the explicit template parameter list (if it exists).
905905
SourceRange ExplicitTemplateParamsRange;
906906

clang/include/clang/Sema/Sema.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -702,10 +702,10 @@ class Sema final : public SemaBase {
702702
/// Retrieve the current block, if any.
703703
sema::BlockScopeInfo *getCurBlock();
704704

705-
/// Get the innermost lambda enclosing the current location, if any. This
706-
/// looks through intervening non-lambda scopes such as local functions and
707-
/// blocks.
708-
sema::LambdaScopeInfo *getEnclosingLambda() const;
705+
/// Get the innermost lambda or block enclosing the current location, if any.
706+
/// This looks through intervening non-lambda, non-block scopes such as local
707+
/// functions.
708+
sema::CapturingScopeInfo *getEnclosingLambdaOrBlock() const;
709709

710710
/// Retrieve the current lambda scope info, if any.
711711
/// \param IgnoreNonLambdaCapturingScope true if should find the top-most

clang/include/clang/Sema/Template.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,10 @@ enum class TemplateSubstitutionKind : char {
411411
/// lookup will search our outer scope.
412412
bool CombineWithOuterScope;
413413

414-
/// Whether this scope is being used to instantiate a lambda expression,
415-
/// in which case it should be reused for instantiating the lambda's
416-
/// FunctionProtoType.
417-
bool InstantiatingLambda = false;
414+
/// Whether this scope is being used to instantiate a lambda or block
415+
/// expression, in which case it should be reused for instantiating the
416+
/// lambda's FunctionProtoType.
417+
bool InstantiatingLambdaOrBlock = false;
418418

419419
/// If non-NULL, the template parameter pack that has been
420420
/// partially substituted per C++0x [temp.arg.explicit]p9.
@@ -431,10 +431,10 @@ enum class TemplateSubstitutionKind : char {
431431

432432
public:
433433
LocalInstantiationScope(Sema &SemaRef, bool CombineWithOuterScope = false,
434-
bool InstantiatingLambda = false)
434+
bool InstantiatingLambdaOrBlock = false)
435435
: SemaRef(SemaRef), Outer(SemaRef.CurrentInstantiationScope),
436436
CombineWithOuterScope(CombineWithOuterScope),
437-
InstantiatingLambda(InstantiatingLambda) {
437+
InstantiatingLambdaOrBlock(InstantiatingLambdaOrBlock) {
438438
SemaRef.CurrentInstantiationScope = this;
439439
}
440440

@@ -561,8 +561,8 @@ enum class TemplateSubstitutionKind : char {
561561
/// Determine whether D is a pack expansion created in this scope.
562562
bool isLocalPackExpansion(const Decl *D);
563563

564-
/// Determine whether this scope is for instantiating a lambda.
565-
bool isLambda() const { return InstantiatingLambda; }
564+
/// Determine whether this scope is for instantiating a lambda or block.
565+
bool isLambdaOrBlock() const { return InstantiatingLambdaOrBlock; }
566566
};
567567

568568
class TemplateDeclInstantiator

clang/lib/AST/ComputeDependence.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,13 @@ ExprDependence clang::computeDependence(ExtVectorElementExpr *E) {
252252
return E->getBase()->getDependence();
253253
}
254254

255-
ExprDependence clang::computeDependence(BlockExpr *E) {
255+
ExprDependence clang::computeDependence(BlockExpr *E,
256+
bool ContainsUnexpandedParameterPack) {
256257
auto D = toExprDependenceForImpliedType(E->getType()->getDependence());
257258
if (E->getBlockDecl()->isDependentContext())
258259
D |= ExprDependence::Instantiation;
260+
if (ContainsUnexpandedParameterPack)
261+
D |= ExprDependence::UnexpandedPack;
259262
return D;
260263
}
261264

clang/lib/Sema/Sema.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,18 +2385,19 @@ FunctionScopeInfo *Sema::getEnclosingFunction() const {
23852385
return nullptr;
23862386
}
23872387

2388-
LambdaScopeInfo *Sema::getEnclosingLambda() const {
2388+
CapturingScopeInfo *Sema::getEnclosingLambdaOrBlock() const {
23892389
for (auto *Scope : llvm::reverse(FunctionScopes)) {
2390-
if (auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Scope)) {
2391-
if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext) &&
2390+
if (auto *CSI = dyn_cast<CapturingScopeInfo>(Scope)) {
2391+
auto *LSI = dyn_cast<LambdaScopeInfo>(CSI);
2392+
if (LSI && LSI->Lambda && !LSI->Lambda->Encloses(CurContext) &&
23922393
LSI->AfterParameterList) {
23932394
// We have switched contexts due to template instantiation.
23942395
// FIXME: We should swap out the FunctionScopes during code synthesis
23952396
// so that we don't need to check for this.
23962397
assert(!CodeSynthesisContexts.empty());
23972398
return nullptr;
23982399
}
2399-
return LSI;
2400+
return CSI;
24002401
}
24012402
}
24022403
return nullptr;

clang/lib/Sema/SemaDecl.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15165,8 +15165,8 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
1516515165
// we know that references to that pack must also be expanded within the
1516615166
// lambda scope.
1516715167
if (New->isParameterPack())
15168-
if (auto *LSI = getEnclosingLambda())
15169-
LSI->LocalPacks.push_back(New);
15168+
if (auto *CSI = getEnclosingLambdaOrBlock())
15169+
CSI->LocalPacks.push_back(New);
1517015170

1517115171
if (New->getType().hasNonTrivialToPrimitiveDestructCUnion() ||
1517215172
New->getType().hasNonTrivialToPrimitiveCopyCUnion())

clang/lib/Sema/SemaExpr.cpp

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16087,17 +16087,7 @@ void Sema::ActOnBlockArguments(SourceLocation CaretLoc, Declarator &ParamInfo,
1608716087

1608816088
TypeSourceInfo *Sig = GetTypeForDeclarator(ParamInfo);
1608916089
QualType T = Sig->getType();
16090-
16091-
// FIXME: We should allow unexpanded parameter packs here, but that would,
16092-
// in turn, make the block expression contain unexpanded parameter packs.
16093-
if (DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block)) {
16094-
// Drop the parameters.
16095-
FunctionProtoType::ExtProtoInfo EPI;
16096-
EPI.HasTrailingReturn = false;
16097-
EPI.TypeQuals.addConst();
16098-
T = Context.getFunctionType(Context.DependentTy, std::nullopt, EPI);
16099-
Sig = Context.getTrivialTypeSourceInfo(T);
16100-
}
16090+
DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block);
1610116091

1610216092
// GetTypeForDeclarator always produces a function type for a block
1610316093
// literal signature. Furthermore, it is always a FunctionProtoType
@@ -16369,7 +16359,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
1636916359
AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
1637016360
PoppedFunctionScopePtr ScopeRAII = PopFunctionScopeInfo(&WP, BD, BlockTy);
1637116361

16372-
BlockExpr *Result = new (Context) BlockExpr(BD, BlockTy);
16362+
BlockExpr *Result = new (Context)
16363+
BlockExpr(BD, BlockTy, BSI->ContainsUnexpandedParameterPack);
1637316364

1637416365
// If the block isn't obviously global, i.e. it captures anything at
1637516366
// all, then we need to do a few things in the surrounding context:
@@ -16392,6 +16383,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
1639216383
if (getCurFunction())
1639316384
getCurFunction()->addBlock(BD);
1639416385

16386+
// This can happen if the block's return type is deduced, but
16387+
// the return expression is invalid.
1639516388
if (BD->isInvalidDecl())
1639616389
return CreateRecoveryExpr(Result->getBeginLoc(), Result->getEndLoc(),
1639716390
{Result}, Result->getType());

clang/lib/Sema/SemaLambda.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2353,7 +2353,10 @@ ExprResult Sema::BuildBlockForLambdaConversion(SourceLocation CurrentLocation,
23532353
Block->setBody(new (Context) CompoundStmt(ConvLocation));
23542354

23552355
// Create the block literal expression.
2356-
Expr *BuildBlock = new (Context) BlockExpr(Block, Conv->getConversionType());
2356+
// TODO: Do we ever get here if we have unexpanded packs in the lambda???
2357+
Expr *BuildBlock =
2358+
new (Context) BlockExpr(Block, Conv->getConversionType(),
2359+
/*ContainsUnexpandedParameterPack=*/false);
23572360
ExprCleanupObjects.push_back(Block);
23582361
Cleanup.setExprNeedsCleanups(true);
23592362

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,8 +1010,8 @@ NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename,
10101010
Param->setAccess(AS_public);
10111011

10121012
if (Param->isParameterPack())
1013-
if (auto *LSI = getEnclosingLambda())
1014-
LSI->LocalPacks.push_back(Param);
1013+
if (auto *CSI = getEnclosingLambdaOrBlock())
1014+
CSI->LocalPacks.push_back(Param);
10151015

10161016
if (ParamName) {
10171017
maybeDiagnoseTemplateParameterShadow(*this, S, ParamNameLoc, ParamName);
@@ -1542,8 +1542,8 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
15421542
Param->setInvalidDecl();
15431543

15441544
if (Param->isParameterPack())
1545-
if (auto *LSI = getEnclosingLambda())
1546-
LSI->LocalPacks.push_back(Param);
1545+
if (auto *CSI = getEnclosingLambdaOrBlock())
1546+
CSI->LocalPacks.push_back(Param);
15471547

15481548
if (ParamName) {
15491549
maybeDiagnoseTemplateParameterShadow(*this, S, D.getIdentifierLoc(),
@@ -1593,7 +1593,7 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
15931593
Param->setAccess(AS_public);
15941594

15951595
if (Param->isParameterPack())
1596-
if (auto *LSI = getEnclosingLambda())
1596+
if (auto *LSI = getEnclosingLambdaOrBlock())
15971597
LSI->LocalPacks.push_back(Param);
15981598

15991599
// If the template template parameter has a name, then link the identifier

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,12 +1694,18 @@ namespace {
16941694
if (SemaRef.RebuildingImmediateInvocation)
16951695
return E;
16961696
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
1697-
/*InstantiatingLambda=*/true);
1697+
/*InstantiatingLambdaOrBlock=*/true);
16981698
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
16991699

17001700
return inherited::TransformLambdaExpr(E);
17011701
}
17021702

1703+
ExprResult TransformBlockExpr(BlockExpr *E) {
1704+
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
1705+
/*InstantiatingLambdaOrBlock=*/true);
1706+
return inherited::TransformBlockExpr(E);
1707+
}
1708+
17031709
ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
17041710
LambdaScopeInfo *LSI) {
17051711
CXXMethodDecl *MD = LSI->CallOperator;
@@ -2488,7 +2494,7 @@ QualType TemplateInstantiator::TransformFunctionProtoType(TypeLocBuilder &TLB,
24882494
CXXRecordDecl *ThisContext,
24892495
Qualifiers ThisTypeQuals,
24902496
Fn TransformExceptionSpec) {
2491-
// If this is a lambda, the transformation MUST be done in the
2497+
// If this is a lambda or block, the transformation MUST be done in the
24922498
// CurrentInstantiationScope since it introduces a mapping of
24932499
// the original to the newly created transformed parameters.
24942500
//
@@ -2497,7 +2503,7 @@ QualType TemplateInstantiator::TransformFunctionProtoType(TypeLocBuilder &TLB,
24972503
// a second one.
24982504
LocalInstantiationScope *Current = getSema().CurrentInstantiationScope;
24992505
std::optional<LocalInstantiationScope> Scope;
2500-
if (!Current || !Current->isLambda())
2506+
if (!Current || !Current->isLambdaOrBlock())
25012507
Scope.emplace(SemaRef, /*CombineWithOuterScope=*/true);
25022508

25032509
return inherited::TransformFunctionProtoType(

0 commit comments

Comments
 (0)