[clang] [clang][Sema] Fix a bug when instantiating a lambda with requires clause (PR #65193)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 1 20:03:52 PDT 2023
https://github.com/0x59616e updated https://github.com/llvm/llvm-project/pull/65193:
>From a65cb213b6ea24a04e170a7cc210ed9d2d00a9ac Mon Sep 17 00:00:00 2001
From: Sheng <ox59616e at gmail.com>
Date: Wed, 30 Aug 2023 11:44:23 +0800
Subject: [PATCH] [clang][Sema] Fix a bug when instantiating a lambda with
requires clause
Instantiating a lambda at a scope different from its definition
scope will paralyze clang if the trailing require clause
refers to local variables of that definition scope.
This patch fixes this by re-adding the local variables to
`LocalInstantiationScope`.
Fixes #64462
---
clang/include/clang/Sema/Sema.h | 5 +-
clang/lib/Sema/SemaConcept.cpp | 136 ++++++++++++++++++++++++--------
clang/test/SemaCXX/pr64462.cpp | 20 +++++
3 files changed, 125 insertions(+), 36 deletions(-)
create mode 100644 clang/test/SemaCXX/pr64462.cpp
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1980571e6656f9..d33af3d113b90c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7394,7 +7394,8 @@ class Sema final {
/// function.
bool SetupConstraintScope(
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
- MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope);
+ MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope,
+ const bool shouldAddDeclsFromParentScope);
/// Used during constraint checking, sets up the constraint template argument
/// lists, and calls SetupConstraintScope to set up the
@@ -7402,7 +7403,7 @@ class Sema final {
std::optional<MultiLevelTemplateArgumentList>
SetupConstraintCheckingTemplateArgumentsAndScope(
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
- LocalInstantiationScope &Scope);
+ LocalInstantiationScope &Scope, const bool shouldAddDeclsFromParentScope);
private:
// The current stack of constraint satisfactions, so we can exit-early.
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index fa3dadf68229ee..a15324f2424265 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -567,9 +567,65 @@ bool Sema::addInstantiatedCapturesToScope(
return false;
}
+static void addDeclsFromParentScope(Sema &S, FunctionDecl *FD,
+ FunctionDecl *Pattern,
+ LocalInstantiationScope &Scope) {
+ LambdaScopeInfo *LSI = nullptr;
+ if (!S.getFunctionScopes().empty())
+ LSI = dyn_cast<LambdaScopeInfo>(S.getFunctionScopes().back());
+
+ auto captureVarIfNeeded = [&](VarDecl *VD) {
+ if (!LSI)
+ return;
+
+ LSI->addCapture(VD, /*isBlock=*/false, /*isByref=*/false,
+ /*isNested=*/false, VD->getBeginLoc(), SourceLocation(),
+ VD->getType(), /*Invalid=*/false);
+ };
+
+ FD = dyn_cast<FunctionDecl>(FD->getParent()->getParent());
+ Pattern = dyn_cast<FunctionDecl>(Pattern->getParent()->getParent());
+
+ if (!FD || !Pattern)
+ return;
+
+ for (unsigned I = 0; I < Pattern->getNumParams(); ++I) {
+ ParmVarDecl *PVD = Pattern->getParamDecl(I);
+ if (!PVD->isParameterPack()) {
+ Scope.InstantiatedLocal(PVD, FD->getParamDecl(I));
+ captureVarIfNeeded(FD->getParamDecl(I));
+ continue;
+ }
+
+ Scope.MakeInstantiatedLocalArgPack(PVD);
+
+ for (ParmVarDecl *Inst : FD->parameters().drop_front(I)) {
+ Scope.InstantiatedLocalPackArg(PVD, Inst);
+ captureVarIfNeeded(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);
+ captureVarIfNeeded(cast<VarDecl>(*it));
+ }
+}
+
bool Sema::SetupConstraintScope(
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
- MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope) {
+ MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope,
+ const bool shouldAddDeclsFromParentScope) {
if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) {
FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
InstantiatingTemplate Inst(
@@ -601,10 +657,14 @@ bool Sema::SetupConstraintScope(
Scope, MLTAL))
return true;
// Make sure the captures are also added to the instantiation scope.
- if (isLambdaCallOperator(FD) &&
- addInstantiatedCapturesToScope(FD, FromMemTempl->getTemplatedDecl(),
- Scope, MLTAL))
- return true;
+ if (isLambdaCallOperator(FD)) {
+ if (addInstantiatedCapturesToScope(FD, FromMemTempl->getTemplatedDecl(),
+ Scope, MLTAL))
+ return true;
+ if (shouldAddDeclsFromParentScope)
+ addDeclsFromParentScope(*this, FD, FromMemTempl->getTemplatedDecl(),
+ Scope);
+ }
}
return false;
@@ -631,9 +691,13 @@ bool Sema::SetupConstraintScope(
return true;
// Make sure the captures are also added to the instantiation scope.
- if (isLambdaCallOperator(FD) &&
- addInstantiatedCapturesToScope(FD, InstantiatedFrom, Scope, MLTAL))
- return true;
+ if (isLambdaCallOperator(FD)) {
+ if (addInstantiatedCapturesToScope(FD, InstantiatedFrom, Scope, MLTAL))
+ return true;
+
+ if (shouldAddDeclsFromParentScope)
+ addDeclsFromParentScope(*this, FD, InstantiatedFrom, Scope);
+ }
}
return false;
@@ -644,7 +708,7 @@ bool Sema::SetupConstraintScope(
std::optional<MultiLevelTemplateArgumentList>
Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
- LocalInstantiationScope &Scope) {
+ LocalInstantiationScope &Scope, const bool shouldAddDeclsFromParentScope) {
MultiLevelTemplateArgumentList MLTAL;
// Collect the list of template arguments relative to the 'primary' template.
@@ -655,7 +719,8 @@ Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);
- if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope))
+ if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope,
+ shouldAddDeclsFromParentScope))
return std::nullopt;
return MLTAL;
@@ -694,23 +759,10 @@ 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);
-
- if (!MLTAL)
- return true;
-
- Qualifiers ThisQuals;
- CXXRecordDecl *Record = nullptr;
- if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) {
- ThisQuals = Method->getMethodQualifiers();
- Record = const_cast<CXXRecordDecl *>(Method->getParent());
- }
- CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
// When checking the constraints of a lambda, we need to restore a
// LambdaScopeInfo populated with correct capture information so that the type
@@ -727,6 +779,22 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
FuncScope.disable();
}
+ std::optional<MultiLevelTemplateArgumentList> MLTAL =
+ SetupConstraintCheckingTemplateArgumentsAndScope(
+ const_cast<FunctionDecl *>(FD), {}, Scope,
+ shouldAddDeclsFromParentScope);
+
+ if (!MLTAL)
+ return true;
+
+ Qualifiers ThisQuals;
+ CXXRecordDecl *Record = nullptr;
+ if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) {
+ ThisQuals = Method->getMethodQualifiers();
+ Record = const_cast<CXXRecordDecl *>(Method->getParent());
+ }
+ CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
+
return CheckConstraintSatisfaction(
FD, {FD->getTrailingRequiresClause()}, *MLTAL,
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
@@ -900,9 +968,17 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
Sema::ContextRAII savedContext(*this, Decl);
LocalInstantiationScope Scope(*this);
+ FunctionScopeRAII FuncScope(*this);
+ if (isLambdaCallOperator(Decl)) {
+ LambdaScopeInfo *LSI = RebuildLambdaScopeInfo(cast<CXXMethodDecl>(Decl));
+ LSI->AfterParameterList = false;
+ } else {
+ FuncScope.disable();
+ }
+
std::optional<MultiLevelTemplateArgumentList> MLTAL =
- SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs,
- Scope);
+ SetupConstraintCheckingTemplateArgumentsAndScope(
+ Decl, TemplateArgs, Scope, /*shouldAddDeclsFromParentScope=*/true);
if (!MLTAL)
return true;
@@ -914,14 +990,6 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
Record = Method->getParent();
}
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
- FunctionScopeRAII FuncScope(*this);
-
- if (isLambdaCallOperator(Decl)) {
- LambdaScopeInfo *LSI = RebuildLambdaScopeInfo(cast<CXXMethodDecl>(Decl));
- LSI->AfterParameterList = false;
- } else {
- FuncScope.disable();
- }
llvm::SmallVector<Expr *, 1> Converted;
return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL,
diff --git a/clang/test/SemaCXX/pr64462.cpp b/clang/test/SemaCXX/pr64462.cpp
new file mode 100644
index 00000000000000..cc8b5510d1a823
--- /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}}
+}
More information about the cfe-commits
mailing list