[clang] [clang][Sema] Fix a bug when instantiating a lambda with requires clause (PR #65193)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 12 05:07:27 PDT 2023
llvmbot wrote:
@llvm/pr-subscribers-clang
<details>
<summary>Changes</summary>
Instantiating a lambda at a scope different from where it is defined will paralyze clang if the trailing require clause refers to local variables. This patch fixes this by re-adding the local variables to `LocalInstantiationScope`.
Fixes #64462
--
Full diff: https://github.com/llvm/llvm-project/pull/65193.diff
2 Files Affected:
- (modified) clang/lib/Sema/SemaConcept.cpp (+61)
- (added) clang/test/SemaCXX/pr64462.cpp (+20)
<pre>
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index d1fa8e7831225b7..45a25cdcb88c8d3 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -567,6 +567,59 @@ bool Sema::addInstantiatedCapturesToScope(
return false;
}
+static void addDeclsFromParentScope(Sema &S, FunctionDecl *FD,
+ LocalInstantiationScope &Scope) {
+ assert(isLambdaCallOperator(FD) && "FD must be a lambda call operator");
+
+ LambdaScopeInfo *LSI = cast<LambdaScopeInfo>(S.getFunctionScopes().back());
+
+ auto captureVar = [&](VarDecl *VD) {
+ LSI->addCapture(VD, /*isBlock=*/false, /*isByref=*/false,
+ /*isNested=*/false, VD->getBeginLoc(), SourceLocation(),
+ VD->getType(), /*Invalid=*/false);
+ };
+
+ FD = dyn_cast<FunctionDecl>(FD->getParent()->getParent());
+ FunctionDecl *Pattern = nullptr;
+
+ if (!FD || !FD->isTemplateInstantiation())
+ return;
+
+ Pattern = FD->getPrimaryTemplate()->getTemplatedDecl();
+
+ for (unsigned I = 0; I < Pattern->getNumParams(); ++I) {
+ ParmVarDecl *PVD = Pattern->getParamDecl(I);
+ if (!PVD->isParameterPack()) {
+ Scope.InstantiatedLocal(PVD, FD->getParamDecl(I));
+ captureVar(FD->getParamDecl(I));
+ continue;
+ }
+
+ Scope.MakeInstantiatedLocalArgPack(PVD);
+
+ for (ParmVarDecl *Inst : FD->parameters().drop_front(I)) {
+ Scope.InstantiatedLocalPackArg(PVD, Inst);
+ captureVar(Inst);
+ }
+ }
+
+ for (auto *decl : Pattern->decls()) {
+ if (!isa<VarDecl>(decl) || isa<ParmVarDecl>(decl))
+ continue;
+
+ IdentifierInfo *II = cast<NamedDecl>(decl)->getIdentifier();
+ auto it = llvm::find_if(FD->decls(), [&](Decl *inst) {
+ VarDecl *VD = dyn_cast<VarDecl>(inst);
+ return VD && VD->isLocalVarDecl() && VD->getIdentifier() == II;
+ });
+
+ assert(it != FD->decls().end() && "Cannot find the instantiated variable.");
+
+ Scope.InstantiatedLocal(decl, *it);
+ captureVar(cast<VarDecl>(*it));
+ }
+}
+
bool Sema::SetupConstraintScope(
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope) {
@@ -684,9 +737,11 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
CtxToSave = CtxToSave->getNonTransparentContext();
}
+ const bool shouldAddDeclsFromParentScope = !CtxToSave->Encloses(CurContext);
ContextRAII SavedContext{*this, CtxToSave};
LocalInstantiationScope Scope(*this, !ForOverloadResolution ||
isLambdaCallOperator(FD));
+
std::optional<MultiLevelTemplateArgumentList> MLTAL =
SetupConstraintCheckingTemplateArgumentsAndScope(
const_cast<FunctionDecl *>(FD), {}, Scope);
@@ -705,6 +760,9 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
LambdaScopeForCallOperatorInstantiationRAII LambdaScope(
*this, const_cast<FunctionDecl *>(FD), *MLTAL, Scope);
+ if (isLambdaCallOperator(FD) && shouldAddDeclsFromParentScope)
+ addDeclsFromParentScope(*this, const_cast<FunctionDecl *>(FD), Scope);
+
return CheckConstraintSatisfaction(
FD, {FD->getTrailingRequiresClause()}, *MLTAL,
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
@@ -896,6 +954,9 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
LambdaScopeForCallOperatorInstantiationRAII LambdaScope(
*this, const_cast<FunctionDecl *>(Decl), *MLTAL, Scope);
+ if (isLambdaCallOperator(Decl))
+ addDeclsFromParentScope(*this, Decl, Scope);
+
llvm::SmallVector<Expr *, 1> Converted;
return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL,
PointOfInstantiation, Satisfaction);
diff --git a/clang/test/SemaCXX/pr64462.cpp b/clang/test/SemaCXX/pr64462.cpp
new file mode 100644
index 000000000000000..cc8b5510d1a823a
--- /dev/null
+++ b/clang/test/SemaCXX/pr64462.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
+
+auto c1(auto f, auto ...fs) {
+ constexpr bool a = true;
+ // expected-note at +2{{because substituted constraint expression is ill-formed: no matching function for call to 'c1'}}
+ // expected-note at +1{{candidate template ignored: constraints not satisfied [with auto:1 = bool}}
+ return [](auto) requires a && (c1(fs...)) {};
+}
+
+auto c2(auto f, auto ...fs) {
+ constexpr bool a = true;
+ // expected-note at +2{{because substituted constraint expression is ill-formed: no matching function for call to 'c2'}}
+ // expected-note at +1{{candidate function not viable: constraints not satisfied}}
+ return []() requires a && (c2(fs...)) {};
+}
+
+void foo() {
+ c1(true)(true); // expected-error {{no matching function for call to object of type}}
+ c2(true)(); // expected-error {{no matching function for call to object of type}}
+}
</pre>
</details>
https://github.com/llvm/llvm-project/pull/65193
More information about the cfe-commits
mailing list