Skip to content

Commit dcc2182

Browse files
authored
[Clang] Fix a lambda pattern comparison mismatch after ecc7e6c (#133863)
In ecc7e6c, we tried to inspect the `LambdaScopeInfo` on stack to recover the instantiating lambda captures. However, there was a mismatch in how we compared the pattern declarations of lambdas: the constraint instantiation used a tailored `getPatternFunctionDecl()` which is localized in SemaLambda that finds the very primal template declaration of a lambda, while `FunctionDecl::getTemplateInstantiationPattern` finds the latest template pattern of a lambda. This difference causes issues when lambdas are nested, as we always want the primary template declaration. This corrects that by moving `Sema::addInstantiatedCapturesToScope` from SemaConcept to SemaLambda, allowing it to use the localized version of `getPatternFunctionDecl`. It is also worth exploring to coalesce the implementation of `getPatternFunctionDecl` with `FunctionDecl::getTemplateInstantiationPattern`. But I’m leaving that for the future, as I’d like to backport this fix (ecc7e6c made the issue more visible in clang 20, sorry!), and changing Sema’s ABI would not be suitable in that regards. Hence, no release note. Fixes #133719
1 parent 4986a79 commit dcc2182

File tree

3 files changed

+83
-69
lines changed

3 files changed

+83
-69
lines changed

clang/lib/Sema/SemaConcept.cpp

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -697,75 +697,6 @@ bool Sema::CheckConstraintSatisfaction(
697697
.isInvalid();
698698
}
699699

700-
bool Sema::addInstantiatedCapturesToScope(
701-
FunctionDecl *Function, const FunctionDecl *PatternDecl,
702-
LocalInstantiationScope &Scope,
703-
const MultiLevelTemplateArgumentList &TemplateArgs) {
704-
const auto *LambdaClass = cast<CXXMethodDecl>(Function)->getParent();
705-
const auto *LambdaPattern = cast<CXXMethodDecl>(PatternDecl)->getParent();
706-
707-
unsigned Instantiated = 0;
708-
709-
// FIXME: This is a workaround for not having deferred lambda body
710-
// instantiation.
711-
// When transforming a lambda's body, if we encounter another call to a
712-
// nested lambda that contains a constraint expression, we add all of the
713-
// outer lambda's instantiated captures to the current instantiation scope to
714-
// facilitate constraint evaluation. However, these captures don't appear in
715-
// the CXXRecordDecl until after the lambda expression is rebuilt, so we
716-
// pull them out from the corresponding LSI.
717-
LambdaScopeInfo *InstantiatingScope = nullptr;
718-
if (LambdaPattern->capture_size() && !LambdaClass->capture_size()) {
719-
for (FunctionScopeInfo *Scope : llvm::reverse(FunctionScopes)) {
720-
auto *LSI = dyn_cast<LambdaScopeInfo>(Scope);
721-
if (!LSI ||
722-
LSI->CallOperator->getTemplateInstantiationPattern() != PatternDecl)
723-
continue;
724-
InstantiatingScope = LSI;
725-
break;
726-
}
727-
assert(InstantiatingScope);
728-
}
729-
730-
auto AddSingleCapture = [&](const ValueDecl *CapturedPattern,
731-
unsigned Index) {
732-
ValueDecl *CapturedVar =
733-
InstantiatingScope ? InstantiatingScope->Captures[Index].getVariable()
734-
: LambdaClass->getCapture(Index)->getCapturedVar();
735-
assert(CapturedVar->isInitCapture());
736-
Scope.InstantiatedLocal(CapturedPattern, CapturedVar);
737-
};
738-
739-
for (const LambdaCapture &CapturePattern : LambdaPattern->captures()) {
740-
if (!CapturePattern.capturesVariable()) {
741-
Instantiated++;
742-
continue;
743-
}
744-
ValueDecl *CapturedPattern = CapturePattern.getCapturedVar();
745-
746-
if (!CapturedPattern->isInitCapture()) {
747-
Instantiated++;
748-
continue;
749-
}
750-
751-
if (!CapturedPattern->isParameterPack()) {
752-
AddSingleCapture(CapturedPattern, Instantiated++);
753-
} else {
754-
Scope.MakeInstantiatedLocalArgPack(CapturedPattern);
755-
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
756-
SemaRef.collectUnexpandedParameterPacks(
757-
dyn_cast<VarDecl>(CapturedPattern)->getInit(), Unexpanded);
758-
auto NumArgumentsInExpansion =
759-
getNumArgumentsInExpansionFromUnexpanded(Unexpanded, TemplateArgs);
760-
if (!NumArgumentsInExpansion)
761-
continue;
762-
for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg)
763-
AddSingleCapture(CapturedPattern, Instantiated++);
764-
}
765-
}
766-
return false;
767-
}
768-
769700
bool Sema::SetupConstraintScope(
770701
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
771702
const MultiLevelTemplateArgumentList &MLTAL,

clang/lib/Sema/SemaLambda.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2412,6 +2412,74 @@ static FunctionDecl *getPatternFunctionDecl(FunctionDecl *FD) {
24122412
return FTD->getTemplatedDecl();
24132413
}
24142414

2415+
bool Sema::addInstantiatedCapturesToScope(
2416+
FunctionDecl *Function, const FunctionDecl *PatternDecl,
2417+
LocalInstantiationScope &Scope,
2418+
const MultiLevelTemplateArgumentList &TemplateArgs) {
2419+
const auto *LambdaClass = cast<CXXMethodDecl>(Function)->getParent();
2420+
const auto *LambdaPattern = cast<CXXMethodDecl>(PatternDecl)->getParent();
2421+
2422+
unsigned Instantiated = 0;
2423+
2424+
// FIXME: This is a workaround for not having deferred lambda body
2425+
// instantiation.
2426+
// When transforming a lambda's body, if we encounter another call to a
2427+
// nested lambda that contains a constraint expression, we add all of the
2428+
// outer lambda's instantiated captures to the current instantiation scope to
2429+
// facilitate constraint evaluation. However, these captures don't appear in
2430+
// the CXXRecordDecl until after the lambda expression is rebuilt, so we
2431+
// pull them out from the corresponding LSI.
2432+
LambdaScopeInfo *InstantiatingScope = nullptr;
2433+
if (LambdaPattern->capture_size() && !LambdaClass->capture_size()) {
2434+
for (FunctionScopeInfo *Scope : llvm::reverse(FunctionScopes)) {
2435+
auto *LSI = dyn_cast<LambdaScopeInfo>(Scope);
2436+
if (!LSI || getPatternFunctionDecl(LSI->CallOperator) != PatternDecl)
2437+
continue;
2438+
InstantiatingScope = LSI;
2439+
break;
2440+
}
2441+
assert(InstantiatingScope);
2442+
}
2443+
2444+
auto AddSingleCapture = [&](const ValueDecl *CapturedPattern,
2445+
unsigned Index) {
2446+
ValueDecl *CapturedVar =
2447+
InstantiatingScope ? InstantiatingScope->Captures[Index].getVariable()
2448+
: LambdaClass->getCapture(Index)->getCapturedVar();
2449+
assert(CapturedVar->isInitCapture());
2450+
Scope.InstantiatedLocal(CapturedPattern, CapturedVar);
2451+
};
2452+
2453+
for (const LambdaCapture &CapturePattern : LambdaPattern->captures()) {
2454+
if (!CapturePattern.capturesVariable()) {
2455+
Instantiated++;
2456+
continue;
2457+
}
2458+
ValueDecl *CapturedPattern = CapturePattern.getCapturedVar();
2459+
2460+
if (!CapturedPattern->isInitCapture()) {
2461+
Instantiated++;
2462+
continue;
2463+
}
2464+
2465+
if (!CapturedPattern->isParameterPack()) {
2466+
AddSingleCapture(CapturedPattern, Instantiated++);
2467+
} else {
2468+
Scope.MakeInstantiatedLocalArgPack(CapturedPattern);
2469+
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
2470+
SemaRef.collectUnexpandedParameterPacks(
2471+
dyn_cast<VarDecl>(CapturedPattern)->getInit(), Unexpanded);
2472+
auto NumArgumentsInExpansion =
2473+
getNumArgumentsInExpansionFromUnexpanded(Unexpanded, TemplateArgs);
2474+
if (!NumArgumentsInExpansion)
2475+
continue;
2476+
for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg)
2477+
AddSingleCapture(CapturedPattern, Instantiated++);
2478+
}
2479+
}
2480+
return false;
2481+
}
2482+
24152483
Sema::LambdaScopeForCallOperatorInstantiationRAII::
24162484
LambdaScopeForCallOperatorInstantiationRAII(
24172485
Sema &SemaRef, FunctionDecl *FD, MultiLevelTemplateArgumentList MLTAL,

clang/test/SemaTemplate/concepts-lambda.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,18 @@ template <class> void f() {
325325
template void f<int>();
326326

327327
}
328+
329+
namespace GH133719 {
330+
331+
template <class T>
332+
constexpr auto f{[] (auto arg) {
333+
return [a{arg}] {
334+
[] () requires true {}();
335+
};
336+
}};
337+
338+
void foo() {
339+
f<int>(0);
340+
}
341+
342+
}

0 commit comments

Comments
 (0)