Skip to content

Commit 52126dc

Browse files
authored
[Clang] Fix Handling of Init Capture with Parameter Packs in LambdaScopeForCallOperatorInstantiationRAII (#100766)
This PR addresses issues related to the handling of `init capture` with parameter packs in Clang's `LambdaScopeForCallOperatorInstantiationRAII`. Previously, `addInstantiatedCapturesToScope` would add `init capture` containing packs to the scope using the type of the `init capture` to determine the expanded pack size. However, this approach resulted in a pack size of 0 because `getType()->containsUnexpandedParameterPack()` returns `false`. After extensive testing, it appears that the correct pack size can only be inferred from `getInit`. But `getInit` may reference parameters and `init capture` from an outer lambda, as shown in the following example: ```cpp auto L = [](auto... z) { return [... w = z](auto... y) { // ... }; }; ``` To address this, `addInstantiatedCapturesToScope` in `LambdaScopeForCallOperatorInstantiationRAII` should be called last. Additionally, `addInstantiatedCapturesToScope` has been modified to only add `init capture` to the scope. The previous implementation incorrectly called `MakeInstantiatedLocalArgPack` for other non-init captures containing packs, resulting in a pack size of 0. ### Impact This patch affects scenarios where `LambdaScopeForCallOperatorInstantiationRAII` is passed with `ShouldAddDeclsFromParentScope = false`, preventing the correct addition of the current lambda's `init capture` to the scope. There are two main scenarios for `ShouldAddDeclsFromParentScope = false`: 1. **Constraints**: Sometimes constraints are instantiated in place rather than delayed. In this case, `LambdaScopeForCallOperatorInstantiationRAII` does not need to add `init capture` to the scope. 2. **`noexcept` Expressions**: The expressions inside `noexcept` have already been transformed, and the packs referenced within have been expanded. Only `RebuildLambdaInfo` needs to add the expanded captures to the scope, without requiring `addInstantiatedCapturesToScope` from `LambdaScopeForCallOperatorInstantiationRAII`. ### Considerations An alternative approach could involve adding a data structure within the lambda to record the expanded size of the `init capture` pack. However, this would increase the lambda's size and require extensive modifications. This PR is a prerequisite for implmenting #61426
1 parent 669d844 commit 52126dc

File tree

6 files changed

+64
-13
lines changed

6 files changed

+64
-13
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ Bug Fixes to C++ Support
202202
^^^^^^^^^^^^^^^^^^^^^^^^
203203

204204
- Fixed a crash when an expression with a dependent ``__typeof__`` type is used as the operand of a unary operator. (#GH97646)
205+
- Fixed incorrect pack expansion of init-capture references in requires expresssions.
205206
- Fixed a failed assertion when checking invalid delete operator declaration. (#GH96191)
206207
- Fix a crash when checking destructor reference with an invalid initializer. (#GH97230)
207208
- Clang now correctly parses potentially declarative nested-name-specifiers in pointer-to-member declarators.

clang/include/clang/Sema/Sema.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14186,6 +14186,10 @@ class Sema final : public SemaBase {
1418614186
std::optional<unsigned> getNumArgumentsInExpansion(
1418714187
QualType T, const MultiLevelTemplateArgumentList &TemplateArgs);
1418814188

14189+
std::optional<unsigned> getNumArgumentsInExpansionFromUnexpanded(
14190+
llvm::ArrayRef<UnexpandedParameterPack> Unexpanded,
14191+
const MultiLevelTemplateArgumentList &TemplateArgs);
14192+
1418914193
/// Determine whether the given declarator contains any unexpanded
1419014194
/// parameter packs.
1419114195
///

clang/lib/Sema/SemaConcept.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -716,22 +716,30 @@ bool Sema::addInstantiatedCapturesToScope(
716716
auto AddSingleCapture = [&](const ValueDecl *CapturedPattern,
717717
unsigned Index) {
718718
ValueDecl *CapturedVar = LambdaClass->getCapture(Index)->getCapturedVar();
719-
if (CapturedVar->isInitCapture())
720-
Scope.InstantiatedLocal(CapturedPattern, CapturedVar);
719+
assert(CapturedVar->isInitCapture());
720+
Scope.InstantiatedLocal(CapturedPattern, CapturedVar);
721721
};
722722

723723
for (const LambdaCapture &CapturePattern : LambdaPattern->captures()) {
724724
if (!CapturePattern.capturesVariable()) {
725725
Instantiated++;
726726
continue;
727727
}
728-
const ValueDecl *CapturedPattern = CapturePattern.getCapturedVar();
728+
ValueDecl *CapturedPattern = CapturePattern.getCapturedVar();
729+
730+
if (!CapturedPattern->isInitCapture()) {
731+
continue;
732+
}
733+
729734
if (!CapturedPattern->isParameterPack()) {
730735
AddSingleCapture(CapturedPattern, Instantiated++);
731736
} else {
732737
Scope.MakeInstantiatedLocalArgPack(CapturedPattern);
733-
std::optional<unsigned> NumArgumentsInExpansion =
734-
getNumArgumentsInExpansion(CapturedPattern->getType(), TemplateArgs);
738+
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
739+
SemaRef.collectUnexpandedParameterPacks(
740+
dyn_cast<VarDecl>(CapturedPattern)->getInit(), Unexpanded);
741+
auto NumArgumentsInExpansion =
742+
getNumArgumentsInExpansionFromUnexpanded(Unexpanded, TemplateArgs);
735743
if (!NumArgumentsInExpansion)
736744
continue;
737745
for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg)

clang/lib/Sema/SemaLambda.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2393,11 +2393,10 @@ Sema::LambdaScopeForCallOperatorInstantiationRAII::
23932393
if (!FDPattern)
23942394
return;
23952395

2396-
SemaRef.addInstantiatedCapturesToScope(FD, FDPattern, Scope, MLTAL);
2397-
23982396
if (!ShouldAddDeclsFromParentScope)
23992397
return;
24002398

2399+
FunctionDecl *InnermostFD = FD, *InnermostFDPattern = FDPattern;
24012400
llvm::SmallVector<std::pair<FunctionDecl *, FunctionDecl *>, 4>
24022401
ParentInstantiations;
24032402
while (true) {
@@ -2421,5 +2420,11 @@ Sema::LambdaScopeForCallOperatorInstantiationRAII::
24212420
for (const auto &[FDPattern, FD] : llvm::reverse(ParentInstantiations)) {
24222421
SemaRef.addInstantiatedParametersToScope(FD, FDPattern, Scope, MLTAL);
24232422
SemaRef.addInstantiatedLocalVarsToScope(FD, FDPattern, Scope);
2423+
2424+
if (isLambdaCallOperator(FD))
2425+
SemaRef.addInstantiatedCapturesToScope(FD, FDPattern, Scope, MLTAL);
24242426
}
2427+
2428+
SemaRef.addInstantiatedCapturesToScope(InnermostFD, InnermostFDPattern, Scope,
2429+
MLTAL);
24252430
}

clang/lib/Sema/SemaTemplateVariadic.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -825,12 +825,9 @@ bool Sema::CheckParameterPacksForExpansion(
825825
return false;
826826
}
827827

828-
std::optional<unsigned> Sema::getNumArgumentsInExpansion(
829-
QualType T, const MultiLevelTemplateArgumentList &TemplateArgs) {
830-
QualType Pattern = cast<PackExpansionType>(T)->getPattern();
831-
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
832-
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseType(Pattern);
833-
828+
std::optional<unsigned> Sema::getNumArgumentsInExpansionFromUnexpanded(
829+
llvm::ArrayRef<UnexpandedParameterPack> Unexpanded,
830+
const MultiLevelTemplateArgumentList &TemplateArgs) {
834831
std::optional<unsigned> Result;
835832
for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
836833
// Compute the depth and index for this parameter pack.
@@ -878,6 +875,14 @@ std::optional<unsigned> Sema::getNumArgumentsInExpansion(
878875
return Result;
879876
}
880877

878+
std::optional<unsigned> Sema::getNumArgumentsInExpansion(
879+
QualType T, const MultiLevelTemplateArgumentList &TemplateArgs) {
880+
QualType Pattern = cast<PackExpansionType>(T)->getPattern();
881+
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
882+
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseType(Pattern);
883+
return getNumArgumentsInExpansionFromUnexpanded(Unexpanded, TemplateArgs);
884+
}
885+
881886
bool Sema::containsUnexpandedParameterPacks(Declarator &D) {
882887
const DeclSpec &DS = D.getDeclSpec();
883888
switch (DS.getTypeSpecType()) {

clang/test/SemaTemplate/concepts-lambda.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,31 @@ void dependent_param() {
251251
L(0, 1)(1, 2)(1);
252252
}
253253
} // namespace dependent_param_concept
254+
255+
namespace init_captures {
256+
template <int N> struct V {};
257+
258+
void sink(V<0>, V<1>, V<2>, V<3>, V<4>) {}
259+
260+
void init_capture_pack() {
261+
auto L = [](auto... z) {
262+
return [=](auto... y) {
263+
return [... w = z, y...](auto)
264+
requires requires { sink(w..., y...); }
265+
{};
266+
};
267+
};
268+
L(V<0>{}, V<1>{}, V<2>{})(V<3>{}, V<4>{})(1);
269+
}
270+
271+
void dependent_capture_packs() {
272+
auto L = [](auto... z) {
273+
return [... w = z](auto... y) {
274+
return [... c = w](auto)
275+
requires requires { sink(c..., y...); }
276+
{};
277+
};
278+
};
279+
L(V<0>{}, V<1>{}, V<2>{})(V<3>{}, V<4>{})(1);
280+
}
281+
} // namespace init_captures

0 commit comments

Comments
 (0)