mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 18:56:43 +00:00
[Clang] Fix a lambda pattern comparison mismatch after ecc7e6ce4 (#133863)
In ecc7e6ce4, 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 (ecc7e6ce4 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 https://github.com/llvm/llvm-project/issues/133719
This commit is contained in:
parent
4986a79648
commit
dcc2182bce
@ -697,75 +697,6 @@ bool Sema::CheckConstraintSatisfaction(
|
||||
.isInvalid();
|
||||
}
|
||||
|
||||
bool Sema::addInstantiatedCapturesToScope(
|
||||
FunctionDecl *Function, const FunctionDecl *PatternDecl,
|
||||
LocalInstantiationScope &Scope,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs) {
|
||||
const auto *LambdaClass = cast<CXXMethodDecl>(Function)->getParent();
|
||||
const auto *LambdaPattern = cast<CXXMethodDecl>(PatternDecl)->getParent();
|
||||
|
||||
unsigned Instantiated = 0;
|
||||
|
||||
// FIXME: This is a workaround for not having deferred lambda body
|
||||
// instantiation.
|
||||
// When transforming a lambda's body, if we encounter another call to a
|
||||
// nested lambda that contains a constraint expression, we add all of the
|
||||
// outer lambda's instantiated captures to the current instantiation scope to
|
||||
// facilitate constraint evaluation. However, these captures don't appear in
|
||||
// the CXXRecordDecl until after the lambda expression is rebuilt, so we
|
||||
// pull them out from the corresponding LSI.
|
||||
LambdaScopeInfo *InstantiatingScope = nullptr;
|
||||
if (LambdaPattern->capture_size() && !LambdaClass->capture_size()) {
|
||||
for (FunctionScopeInfo *Scope : llvm::reverse(FunctionScopes)) {
|
||||
auto *LSI = dyn_cast<LambdaScopeInfo>(Scope);
|
||||
if (!LSI ||
|
||||
LSI->CallOperator->getTemplateInstantiationPattern() != PatternDecl)
|
||||
continue;
|
||||
InstantiatingScope = LSI;
|
||||
break;
|
||||
}
|
||||
assert(InstantiatingScope);
|
||||
}
|
||||
|
||||
auto AddSingleCapture = [&](const ValueDecl *CapturedPattern,
|
||||
unsigned Index) {
|
||||
ValueDecl *CapturedVar =
|
||||
InstantiatingScope ? InstantiatingScope->Captures[Index].getVariable()
|
||||
: LambdaClass->getCapture(Index)->getCapturedVar();
|
||||
assert(CapturedVar->isInitCapture());
|
||||
Scope.InstantiatedLocal(CapturedPattern, CapturedVar);
|
||||
};
|
||||
|
||||
for (const LambdaCapture &CapturePattern : LambdaPattern->captures()) {
|
||||
if (!CapturePattern.capturesVariable()) {
|
||||
Instantiated++;
|
||||
continue;
|
||||
}
|
||||
ValueDecl *CapturedPattern = CapturePattern.getCapturedVar();
|
||||
|
||||
if (!CapturedPattern->isInitCapture()) {
|
||||
Instantiated++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CapturedPattern->isParameterPack()) {
|
||||
AddSingleCapture(CapturedPattern, Instantiated++);
|
||||
} else {
|
||||
Scope.MakeInstantiatedLocalArgPack(CapturedPattern);
|
||||
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
|
||||
SemaRef.collectUnexpandedParameterPacks(
|
||||
dyn_cast<VarDecl>(CapturedPattern)->getInit(), Unexpanded);
|
||||
auto NumArgumentsInExpansion =
|
||||
getNumArgumentsInExpansionFromUnexpanded(Unexpanded, TemplateArgs);
|
||||
if (!NumArgumentsInExpansion)
|
||||
continue;
|
||||
for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg)
|
||||
AddSingleCapture(CapturedPattern, Instantiated++);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sema::SetupConstraintScope(
|
||||
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
|
||||
const MultiLevelTemplateArgumentList &MLTAL,
|
||||
|
@ -2412,6 +2412,74 @@ static FunctionDecl *getPatternFunctionDecl(FunctionDecl *FD) {
|
||||
return FTD->getTemplatedDecl();
|
||||
}
|
||||
|
||||
bool Sema::addInstantiatedCapturesToScope(
|
||||
FunctionDecl *Function, const FunctionDecl *PatternDecl,
|
||||
LocalInstantiationScope &Scope,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs) {
|
||||
const auto *LambdaClass = cast<CXXMethodDecl>(Function)->getParent();
|
||||
const auto *LambdaPattern = cast<CXXMethodDecl>(PatternDecl)->getParent();
|
||||
|
||||
unsigned Instantiated = 0;
|
||||
|
||||
// FIXME: This is a workaround for not having deferred lambda body
|
||||
// instantiation.
|
||||
// When transforming a lambda's body, if we encounter another call to a
|
||||
// nested lambda that contains a constraint expression, we add all of the
|
||||
// outer lambda's instantiated captures to the current instantiation scope to
|
||||
// facilitate constraint evaluation. However, these captures don't appear in
|
||||
// the CXXRecordDecl until after the lambda expression is rebuilt, so we
|
||||
// pull them out from the corresponding LSI.
|
||||
LambdaScopeInfo *InstantiatingScope = nullptr;
|
||||
if (LambdaPattern->capture_size() && !LambdaClass->capture_size()) {
|
||||
for (FunctionScopeInfo *Scope : llvm::reverse(FunctionScopes)) {
|
||||
auto *LSI = dyn_cast<LambdaScopeInfo>(Scope);
|
||||
if (!LSI || getPatternFunctionDecl(LSI->CallOperator) != PatternDecl)
|
||||
continue;
|
||||
InstantiatingScope = LSI;
|
||||
break;
|
||||
}
|
||||
assert(InstantiatingScope);
|
||||
}
|
||||
|
||||
auto AddSingleCapture = [&](const ValueDecl *CapturedPattern,
|
||||
unsigned Index) {
|
||||
ValueDecl *CapturedVar =
|
||||
InstantiatingScope ? InstantiatingScope->Captures[Index].getVariable()
|
||||
: LambdaClass->getCapture(Index)->getCapturedVar();
|
||||
assert(CapturedVar->isInitCapture());
|
||||
Scope.InstantiatedLocal(CapturedPattern, CapturedVar);
|
||||
};
|
||||
|
||||
for (const LambdaCapture &CapturePattern : LambdaPattern->captures()) {
|
||||
if (!CapturePattern.capturesVariable()) {
|
||||
Instantiated++;
|
||||
continue;
|
||||
}
|
||||
ValueDecl *CapturedPattern = CapturePattern.getCapturedVar();
|
||||
|
||||
if (!CapturedPattern->isInitCapture()) {
|
||||
Instantiated++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CapturedPattern->isParameterPack()) {
|
||||
AddSingleCapture(CapturedPattern, Instantiated++);
|
||||
} else {
|
||||
Scope.MakeInstantiatedLocalArgPack(CapturedPattern);
|
||||
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
|
||||
SemaRef.collectUnexpandedParameterPacks(
|
||||
dyn_cast<VarDecl>(CapturedPattern)->getInit(), Unexpanded);
|
||||
auto NumArgumentsInExpansion =
|
||||
getNumArgumentsInExpansionFromUnexpanded(Unexpanded, TemplateArgs);
|
||||
if (!NumArgumentsInExpansion)
|
||||
continue;
|
||||
for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg)
|
||||
AddSingleCapture(CapturedPattern, Instantiated++);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Sema::LambdaScopeForCallOperatorInstantiationRAII::
|
||||
LambdaScopeForCallOperatorInstantiationRAII(
|
||||
Sema &SemaRef, FunctionDecl *FD, MultiLevelTemplateArgumentList MLTAL,
|
||||
|
@ -325,3 +325,18 @@ template <class> void f() {
|
||||
template void f<int>();
|
||||
|
||||
}
|
||||
|
||||
namespace GH133719 {
|
||||
|
||||
template <class T>
|
||||
constexpr auto f{[] (auto arg) {
|
||||
return [a{arg}] {
|
||||
[] () requires true {}();
|
||||
};
|
||||
}};
|
||||
|
||||
void foo() {
|
||||
f<int>(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user