Skip to content

[clang][dataflow] Add captured parameters to ReferencedDecls for lamb… #117771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 27, 2024

Conversation

bazuzi
Copy link
Contributor

@bazuzi bazuzi commented Nov 26, 2024

…da call operators.

This doesn't require that they be used in the operator's body, unlike other ReferencedDecls. This is most obviously different from captured local variables, which can be captured but will not appear in ReferencedDecls unless they appear in the operator's body.

This difference simplifies the collection of the captured parameters, but probably could be eliminated if desirable.

…da call operators.

This doesn't require that they be used in the operator's body, unlike other ReferencedDecls. This is most obviously different from captured local variables, which can be captured but will not appear in ReferencedDecls unless they appear in the operator's body.

This difference simplifies the collection of the captured parameters, but probably could be eliminated if desirable.
@bazuzi bazuzi requested a review from ymand November 26, 2024 19:23
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html clang:analysis labels Nov 26, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 26, 2024

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-analysis

Author: Samira Bazuzi (bazuzi)

Changes

…da call operators.

This doesn't require that they be used in the operator's body, unlike other ReferencedDecls. This is most obviously different from captured local variables, which can be captured but will not appear in ReferencedDecls unless they appear in the operator's body.

This difference simplifies the collection of the captured parameters, but probably could be eliminated if desirable.


Full diff: https://github.com/llvm/llvm-project/pull/117771.diff

3 Files Affected:

  • (modified) clang/include/clang/Analysis/FlowSensitive/ASTOps.h (+4)
  • (modified) clang/lib/Analysis/FlowSensitive/ASTOps.cpp (+12)
  • (modified) clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp (+41)
diff --git a/clang/include/clang/Analysis/FlowSensitive/ASTOps.h b/clang/include/clang/Analysis/FlowSensitive/ASTOps.h
index 6294c810626a70..eee355f3cbab16 100644
--- a/clang/include/clang/Analysis/FlowSensitive/ASTOps.h
+++ b/clang/include/clang/Analysis/FlowSensitive/ASTOps.h
@@ -146,6 +146,10 @@ struct ReferencedDecls {
   /// Free functions and member functions which are referenced (but not
   /// necessarily called).
   llvm::DenseSet<const FunctionDecl *> Functions;
+  /// Parameters of other functions, captured by reference by a lambda. This is
+  /// empty except when ReferencedDecls are computed for a lambda's call
+  /// operator.
+  llvm::DenseSet<const ParmVarDecl *> LambdaCapturedParams;
 };
 
 /// Returns declarations that are declared in or referenced from `FD`.
diff --git a/clang/lib/Analysis/FlowSensitive/ASTOps.cpp b/clang/lib/Analysis/FlowSensitive/ASTOps.cpp
index 9e7821bfc1e89e..52e54deaaf0dc3 100644
--- a/clang/lib/Analysis/FlowSensitive/ASTOps.cpp
+++ b/clang/lib/Analysis/FlowSensitive/ASTOps.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Analysis/FlowSensitive/ASTOps.h"
+#include "clang/AST/ASTLambda.h"
 #include "clang/AST/ComputeDependence.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
@@ -281,6 +282,17 @@ ReferencedDecls getReferencedDecls(const FunctionDecl &FD) {
   Visitor.TraverseStmt(FD.getBody());
   if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD))
     Visitor.traverseConstructorInits(CtorDecl);
+  if (const auto *Method = dyn_cast<CXXMethodDecl>(&FD);
+      Method && isLambdaCallOperator(Method)) {
+    for (const auto &Capture : Method->getParent()->captures()) {
+      if (Capture.capturesVariable()) {
+        if (const auto *Param =
+                dyn_cast<ParmVarDecl>(Capture.getCapturedVar())) {
+          Result.LambdaCapturedParams.insert(Param);
+        }
+      }
+    }
+  }
 
   return Result;
 }
diff --git a/clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp b/clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp
index 834aa7f4c2ac2e..e086ea3c892f16 100644
--- a/clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp
@@ -8,15 +8,23 @@
 
 #include "clang/Analysis/FlowSensitive/ASTOps.h"
 #include "TestingSupport.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/Tooling.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include <memory>
+#include <string>
 
 namespace {
 
 using namespace clang;
 using namespace dataflow;
 
+using ast_matchers::cxxMethodDecl;
 using ast_matchers::cxxRecordDecl;
 using ast_matchers::hasName;
 using ast_matchers::hasType;
@@ -107,4 +115,37 @@ TEST(ASTOpsTest, ReferencedDeclsLocalsNotParamsOrStatics) {
               UnorderedElementsAre(LocalDecl));
 }
 
+TEST(ASTOpsTest, LambdaCaptures) {
+  std::string Code = R"cc(
+    void func(int CapturedByRef, int CapturedByValue, int NotCaptured) {
+    int Local;
+      auto Lambda = [&CapturedByRef, CapturedByValue, &Local](int LambdaParam) {
+      };
+    }
+  )cc";
+  std::unique_ptr<ASTUnit> Unit =
+      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
+  auto &ASTCtx = Unit->getASTContext();
+
+  ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U);
+
+  auto *LambdaCallOp = selectFirst<CXXMethodDecl>(
+      "l", match(cxxMethodDecl(hasName("operator()")).bind("l"), ASTCtx));
+  ASSERT_NE(LambdaCallOp, nullptr);
+  auto *Func = cast<FunctionDecl>(findValueDecl(ASTCtx, "func"));
+  ASSERT_NE(Func, nullptr);
+  auto *CapturedByRefDecl = Func->getParamDecl(0);
+  ASSERT_NE(CapturedByRefDecl, nullptr);
+  auto *CapturedByValueDecl = Func->getParamDecl(1);
+  ASSERT_NE(CapturedByValueDecl, nullptr);
+
+  EXPECT_THAT(getReferencedDecls(*Func).LambdaCapturedParams, IsEmpty());
+  ReferencedDecls ForLambda = getReferencedDecls(*LambdaCallOp);
+  EXPECT_THAT(ForLambda.LambdaCapturedParams,
+              UnorderedElementsAre(CapturedByRefDecl, CapturedByValueDecl));
+  // Captured locals must be seen in the body for them to appear in
+  // ReferencedDecls.
+  EXPECT_THAT(ForLambda.Locals, IsEmpty());
+}
+
 } // namespace

Copy link
Collaborator

@ymand ymand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comments could be a bit stronger, but the code looks fine.

@bazuzi bazuzi requested a review from ymand November 26, 2024 23:46
@bazuzi bazuzi merged commit 198fb5e into llvm:main Nov 27, 2024
8 checks passed
@bazuzi bazuzi deleted the piper_export_cl_700402918 branch November 27, 2024 15:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:analysis clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants