[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:06:24 PDT 2023


https://github.com/0x59616e updated https://github.com/llvm/llvm-project/pull/65193:

>From 0da34437a4ec6604d27808a020f56c6cf3615b21 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/lib/Sema/SemaConcept.cpp | 61 ++++++++++++++++++++++++++++++++++
 clang/test/SemaCXX/pr64462.cpp | 20 +++++++++++
 2 files changed, 81 insertions(+)
 create mode 100644 clang/test/SemaCXX/pr64462.cpp

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}}
+}



More information about the cfe-commits mailing list