[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 15 02:19:02 PDT 2023


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

>From 3eafb85ff74271456cba24ea5892dd5660c1d332 Mon Sep 17 00:00:00 2001
From: Sheng <ox59616e at gmail.com>
Date: Wed, 30 Aug 2023 11:44:23 +0800
Subject: [PATCH 1/2] [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 | 59 ++++++++++++++++++++++++++++++++++
 clang/test/SemaCXX/pr64462.cpp | 20 ++++++++++++
 2 files changed, 79 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..e48e0f743927a17 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -567,6 +567,58 @@ 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());
+
+  if (!FD || !FD->isTemplateInstantiation())
+    return;
+
+  FunctionDecl *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,6 +736,7 @@ 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));
@@ -705,6 +758,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 +952,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}}
+}

>From 68912e0a88fc38cc5467c52f305e3c693f310dba Mon Sep 17 00:00:00 2001
From: Sheng <ox59616e at gmail.com>
Date: Fri, 15 Sep 2023 02:02:34 +0800
Subject: [PATCH 2/2] Add support for nested lambda.

---
 clang/lib/Sema/SemaConcept.cpp | 78 ++++++++++++++++++++--------------
 clang/test/SemaCXX/pr64462.cpp | 25 +++++------
 2 files changed, 59 insertions(+), 44 deletions(-)

diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index e48e0f743927a17..dbe7a05660decf0 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -567,6 +567,20 @@ bool Sema::addInstantiatedCapturesToScope(
   return false;
 }
 
+static FunctionDecl *getPatternFunctionDecl(FunctionDecl *FD) {
+  if (FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization) {
+    while (FD->getInstantiatedFromMemberFunction())
+      FD = FD->getInstantiatedFromMemberFunction();
+    return FD;
+  }
+
+  FunctionTemplateDecl *FTD = FD->getPrimaryTemplate();
+  while (FTD->getInstantiatedFromMemberTemplate())
+    FTD = FTD->getInstantiatedFromMemberTemplate();
+
+  return FTD->getTemplatedDecl();
+}
+
 static void addDeclsFromParentScope(Sema &S, FunctionDecl *FD,
                                     LocalInstantiationScope &Scope) {
   assert(isLambdaCallOperator(FD) && "FD must be a lambda call operator");
@@ -579,43 +593,45 @@ static void addDeclsFromParentScope(Sema &S, FunctionDecl *FD,
                     VD->getType(), /*Invalid=*/false);
   };
 
-  FD = dyn_cast<FunctionDecl>(FD->getParent()->getParent());
-
-  if (!FD || !FD->isTemplateInstantiation())
-    return;
+  while (true) {
+    FD = dyn_cast_or_null<FunctionDecl>(FD->getParent()->getParent());
+    if (!FD || !FD->isTemplateInstantiation())
+      return;
 
-  FunctionDecl *Pattern = FD->getPrimaryTemplate()->getTemplatedDecl();
+    FunctionDecl *Pattern = getPatternFunctionDecl(FD);
 
-  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;
-    }
+    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);
+      Scope.MakeInstantiatedLocalArgPack(PVD);
 
-    for (ParmVarDecl *Inst : FD->parameters().drop_front(I)) {
-      Scope.InstantiatedLocalPackArg(PVD, Inst);
-      captureVar(Inst);
+      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;
+    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;
-    });
+      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.");
+      if (it == FD->decls().end())
+        continue;
 
-    Scope.InstantiatedLocal(decl, *it);
-    captureVar(cast<VarDecl>(*it));
+      Scope.InstantiatedLocal(decl, *it);
+      captureVar(cast<VarDecl>(*it));
+    }
   }
 }
 
@@ -736,10 +752,8 @@ 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));
+  LocalInstantiationScope Scope(*this, !ForOverloadResolution);
   std::optional<MultiLevelTemplateArgumentList> MLTAL =
       SetupConstraintCheckingTemplateArgumentsAndScope(
           const_cast<FunctionDecl *>(FD), {}, Scope);
@@ -758,7 +772,7 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
   LambdaScopeForCallOperatorInstantiationRAII LambdaScope(
       *this, const_cast<FunctionDecl *>(FD), *MLTAL, Scope);
 
-  if (isLambdaCallOperator(FD) && shouldAddDeclsFromParentScope)
+  if (isLambdaCallOperator(FD) && ForOverloadResolution)
     addDeclsFromParentScope(*this, const_cast<FunctionDecl *>(FD), Scope);
 
   return CheckConstraintSatisfaction(
diff --git a/clang/test/SemaCXX/pr64462.cpp b/clang/test/SemaCXX/pr64462.cpp
index cc8b5510d1a823a..7cffb3583dcd408 100644
--- a/clang/test/SemaCXX/pr64462.cpp
+++ b/clang/test/SemaCXX/pr64462.cpp
@@ -2,19 +2,20 @@
 
 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...)) {};
+  return [](auto) requires a {
+    constexpr bool b = true;
+    return []() requires a && b {
+      constexpr bool c = true;
+      return [](auto) requires a && b && c {
+        constexpr bool d = true;
+        // expected-note at +2{{because substituted constraint expression is ill-formed: no matching function for call to 'c1'}}
+        // expected-note at +1{{candidate function not viable: constraints not satisfied}}
+        return []() requires a && b && c && d && (c1(fs...)) {};
+      };
+    }();
+  }(1);
 }
 
 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}}
+  c1(true)(1.0)(); // expected-error{{no matching function for call to object of type}}
 }



More information about the cfe-commits mailing list