Skip to content

Commit d15559d

Browse files
authored
[Clang] Don't assert on substituted-but-yet-expanded packs for nested lambdas (#112896)
Nested lambdas could refer to outer packs that would be expanded by a larger CXXFoldExpr, in which case that reference could happen to be a full expression containing intermediate types/expressions, e.g. SubstTemplateTypeParmPackType/FunctionParmPackExpr. They are designated as "UnexpandedPack" dependencies but don't introduce new packs anyway. This also handles a missed case for VarDecls, where the flag of ContainsUnexpandedPack was not propagated up to the surrounding lambda. Fixes #112352
1 parent 2f58ac4 commit d15559d

File tree

4 files changed

+92
-24
lines changed

4 files changed

+92
-24
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ Bug Fixes to C++ Support
507507
- Clang no longer tries to capture non-odr used default arguments of template parameters of generic lambdas (#GH107048)
508508
- Fixed a bug where defaulted comparison operators would remove ``const`` from base classes. (#GH102588)
509509
- Fix a crash when using ``source_location`` in the trailing return type of a lambda expression. (#GH67134)
510-
- A follow-up fix was added for (#GH61460), as the previous fix was not entirely correct. (#GH86361)
510+
- A follow-up fix was added for (#GH61460), as the previous fix was not entirely correct. (#GH86361), (#GH112352)
511511
- Fixed a crash in the typo correction of an invalid CTAD guide. (#GH107887)
512512
- Fixed a crash when clang tries to subtitute parameter pack while retaining the parameter
513513
pack. (#GH63819), (#GH107560)

clang/lib/Sema/SemaTemplateVariadic.cpp

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace {
4141
unsigned DepthLimit = (unsigned)-1;
4242

4343
#ifndef NDEBUG
44-
bool ContainsFunctionParmPackExpr = false;
44+
bool ContainsIntermediatePacks = false;
4545
#endif
4646

4747
void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
@@ -114,6 +114,11 @@ namespace {
114114
addUnexpanded(TTP);
115115
}
116116

117+
#ifndef NDEBUG
118+
ContainsIntermediatePacks |=
119+
(bool)Template.getAsSubstTemplateTemplateParmPack();
120+
#endif
121+
117122
return inherited::TraverseTemplateName(Template);
118123
}
119124

@@ -297,13 +302,28 @@ namespace {
297302

298303
#ifndef NDEBUG
299304
bool TraverseFunctionParmPackExpr(FunctionParmPackExpr *) {
300-
ContainsFunctionParmPackExpr = true;
305+
ContainsIntermediatePacks = true;
306+
return true;
307+
}
308+
309+
bool TraverseSubstNonTypeTemplateParmPackExpr(
310+
SubstNonTypeTemplateParmPackExpr *) {
311+
ContainsIntermediatePacks = true;
312+
return true;
313+
}
314+
315+
bool VisitSubstTemplateTypeParmPackType(SubstTemplateTypeParmPackType *) {
316+
ContainsIntermediatePacks = true;
301317
return true;
302318
}
303319

304-
bool containsFunctionParmPackExpr() const {
305-
return ContainsFunctionParmPackExpr;
320+
bool
321+
VisitSubstTemplateTypeParmPackTypeLoc(SubstTemplateTypeParmPackTypeLoc) {
322+
ContainsIntermediatePacks = true;
323+
return true;
306324
}
325+
326+
bool containsIntermediatePacks() const { return ContainsIntermediatePacks; }
307327
#endif
308328
};
309329
}
@@ -439,21 +459,20 @@ bool Sema::DiagnoseUnexpandedParameterPack(Expr *E,
439459
if (!E->containsUnexpandedParameterPack())
440460
return false;
441461

442-
// FunctionParmPackExprs are special:
443-
//
444-
// 1) they're used to model DeclRefExprs to packs that have been expanded but
445-
// had that expansion held off in the process of transformation.
446-
//
447-
// 2) they always have the unexpanded dependencies but don't introduce new
448-
// unexpanded packs.
449-
//
450-
// We might encounter a FunctionParmPackExpr being a full expression, which a
451-
// larger CXXFoldExpr would expand.
452462
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
453463
CollectUnexpandedParameterPacksVisitor Visitor(Unexpanded);
454464
Visitor.TraverseStmt(E);
455-
assert((!Unexpanded.empty() || Visitor.containsFunctionParmPackExpr()) &&
465+
#ifndef NDEBUG
466+
// The expression might contain a type/subexpression that has been substituted
467+
// but has the expansion held off, e.g. a FunctionParmPackExpr which a larger
468+
// CXXFoldExpr would expand. It's only possible when expanding a lambda as a
469+
// pattern of a fold expression, so don't fire on an empty result in that
470+
// case.
471+
bool LambdaReferencingOuterPacks =
472+
getEnclosingLambdaOrBlock() && Visitor.containsIntermediatePacks();
473+
assert((!Unexpanded.empty() || LambdaReferencingOuterPacks) &&
456474
"Unable to find unexpanded parameter packs");
475+
#endif
457476
return DiagnoseUnexpandedParameterPacks(E->getBeginLoc(), UPPC, Unexpanded);
458477
}
459478

clang/lib/Sema/TreeTransform.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8385,14 +8385,19 @@ TreeTransform<Derived>::TransformDeclStmt(DeclStmt *S) {
83858385
if (Transformed != D)
83868386
DeclChanged = true;
83878387

8388-
if (LSI && isa<TypeDecl>(Transformed))
8389-
LSI->ContainsUnexpandedParameterPack |=
8390-
getSema()
8391-
.getASTContext()
8392-
.getTypeDeclType(cast<TypeDecl>(Transformed))
8393-
.getCanonicalType()
8394-
.getTypePtr()
8395-
->containsUnexpandedParameterPack();
8388+
if (LSI) {
8389+
if (auto *TD = dyn_cast<TypeDecl>(Transformed))
8390+
LSI->ContainsUnexpandedParameterPack |=
8391+
getSema()
8392+
.getASTContext()
8393+
.getTypeDeclType(TD)
8394+
.getCanonicalType()
8395+
->containsUnexpandedParameterPack();
8396+
8397+
if (auto *VD = dyn_cast<VarDecl>(Transformed))
8398+
LSI->ContainsUnexpandedParameterPack |=
8399+
VD->getType()->containsUnexpandedParameterPack();
8400+
}
83968401

83978402
Decls.push_back(Transformed);
83988403
}

clang/test/SemaCXX/lambda-pack-expansion.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,47 @@ template <typename... Ts> void g2(Ts... p1s) {
9494
void f1() { g(); }
9595

9696
} // namespace GH61460
97+
98+
namespace GH112352 {
99+
100+
template <class>
101+
constexpr bool foo = false;
102+
103+
template <int>
104+
constexpr bool bar = false;
105+
106+
template <template<class> class>
107+
constexpr bool baz = false;
108+
109+
struct S {
110+
template <typename... Types, int... Values> void foldExpr1() {
111+
(void)[]<int... Is> {
112+
([] {
113+
Is;
114+
// Propagate up the flag ContainsUnexpandedParameterPack from VarDecl.
115+
S var(foo<Types>);
116+
foo<Types>;
117+
bar<Values>;
118+
int a = Values;
119+
} &&
120+
...);
121+
};
122+
}
123+
124+
template <template<class> class... TTPs> void foldExpr2() {
125+
(void)[]<int... Is> {
126+
([] {
127+
Is;
128+
baz<TTPs>;
129+
TTPs<int> D;
130+
} && ...);
131+
};
132+
}
133+
};
134+
135+
void use() {
136+
S().foldExpr1();
137+
S().foldExpr2();
138+
}
139+
140+
} // namespace GH112352

0 commit comments

Comments
 (0)