[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