Skip to content

Commit 9420327

Browse files
zyn0217tstellar
authored andcommitted
[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 (cherry picked from commit dcc2182)
1 parent 4da7285 commit 9420327

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
@@ -702,75 +702,6 @@ bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
702702
.isInvalid();
703703
}
704704

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

clang/lib/Sema/SemaLambda.cpp

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

2392+
bool Sema::addInstantiatedCapturesToScope(
2393+
FunctionDecl *Function, const FunctionDecl *PatternDecl,
2394+
LocalInstantiationScope &Scope,
2395+
const MultiLevelTemplateArgumentList &TemplateArgs) {
2396+
const auto *LambdaClass = cast<CXXMethodDecl>(Function)->getParent();
2397+
const auto *LambdaPattern = cast<CXXMethodDecl>(PatternDecl)->getParent();
2398+
2399+
unsigned Instantiated = 0;
2400+
2401+
// FIXME: This is a workaround for not having deferred lambda body
2402+
// instantiation.
2403+
// When transforming a lambda's body, if we encounter another call to a
2404+
// nested lambda that contains a constraint expression, we add all of the
2405+
// outer lambda's instantiated captures to the current instantiation scope to
2406+
// facilitate constraint evaluation. However, these captures don't appear in
2407+
// the CXXRecordDecl until after the lambda expression is rebuilt, so we
2408+
// pull them out from the corresponding LSI.
2409+
LambdaScopeInfo *InstantiatingScope = nullptr;
2410+
if (LambdaPattern->capture_size() && !LambdaClass->capture_size()) {
2411+
for (FunctionScopeInfo *Scope : llvm::reverse(FunctionScopes)) {
2412+
auto *LSI = dyn_cast<LambdaScopeInfo>(Scope);
2413+
if (!LSI || getPatternFunctionDecl(LSI->CallOperator) != PatternDecl)
2414+
continue;
2415+
InstantiatingScope = LSI;
2416+
break;
2417+
}
2418+
assert(InstantiatingScope);
2419+
}
2420+
2421+
auto AddSingleCapture = [&](const ValueDecl *CapturedPattern,
2422+
unsigned Index) {
2423+
ValueDecl *CapturedVar =
2424+
InstantiatingScope ? InstantiatingScope->Captures[Index].getVariable()
2425+
: LambdaClass->getCapture(Index)->getCapturedVar();
2426+
assert(CapturedVar->isInitCapture());
2427+
Scope.InstantiatedLocal(CapturedPattern, CapturedVar);
2428+
};
2429+
2430+
for (const LambdaCapture &CapturePattern : LambdaPattern->captures()) {
2431+
if (!CapturePattern.capturesVariable()) {
2432+
Instantiated++;
2433+
continue;
2434+
}
2435+
ValueDecl *CapturedPattern = CapturePattern.getCapturedVar();
2436+
2437+
if (!CapturedPattern->isInitCapture()) {
2438+
Instantiated++;
2439+
continue;
2440+
}
2441+
2442+
if (!CapturedPattern->isParameterPack()) {
2443+
AddSingleCapture(CapturedPattern, Instantiated++);
2444+
} else {
2445+
Scope.MakeInstantiatedLocalArgPack(CapturedPattern);
2446+
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
2447+
SemaRef.collectUnexpandedParameterPacks(
2448+
dyn_cast<VarDecl>(CapturedPattern)->getInit(), Unexpanded);
2449+
auto NumArgumentsInExpansion =
2450+
getNumArgumentsInExpansionFromUnexpanded(Unexpanded, TemplateArgs);
2451+
if (!NumArgumentsInExpansion)
2452+
continue;
2453+
for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg)
2454+
AddSingleCapture(CapturedPattern, Instantiated++);
2455+
}
2456+
}
2457+
return false;
2458+
}
2459+
23922460
Sema::LambdaScopeForCallOperatorInstantiationRAII::
23932461
LambdaScopeForCallOperatorInstantiationRAII(
23942462
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)