Skip to content

Conversation

bazuzi
Copy link
Contributor

@bazuzi bazuzi commented Sep 24, 2025

…opy/move assignments.

This mirrors the handling of copy/move constructors.

Also fix a couple capitalizations of variables in an adjacent and related test.

…opy/move assignments.

This mirrors the handling of copy/move constructors.
@bazuzi bazuzi requested a review from jvoung September 24, 2025 16:53
@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 Sep 24, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 24, 2025

@llvm/pr-subscribers-clang

Author: Samira Bakon (bazuzi)

Changes

…opy/move assignments.

This mirrors the handling of copy/move constructors.

Also fix a couple capitalizations of variables in an adjacent and related test.


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

2 Files Affected:

  • (modified) clang/lib/Analysis/FlowSensitive/Transfer.cpp (+6-1)
  • (modified) clang/unittests/Analysis/FlowSensitive/TransferTest.cpp (+30-3)
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 60371d9498c25..f59bd6555aff5 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -657,7 +657,12 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
       if (LocSrc == nullptr || LocDst == nullptr)
         return;
 
-      copyRecord(*LocSrc, *LocDst, Env);
+      // If the destination object here is of a derived class, `Arg0` may be a
+      // cast of that object to a base class, and the destination object may be
+      // of a sibling derived class. To handle these cases, ensure we are
+      // copying only the fields for `Arg0`'s type, not the type of the
+      // underlying `RecordStorageLocation`.
+      copyRecord(*LocSrc, *LocDst, Env, Arg0->getType());
 
       // The assignment operator can have an arbitrary return type. We model the
       // return value only if the return type is the same as or a base class of
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index d97e2b0c2425a..cbd55966a3d88 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -1554,8 +1554,8 @@ TEST(TransferTest, BaseClassInitializerFromSiblingDerivedInstance) {
     struct DerivedTwo : public Base {
       int DerivedTwoField;
 
-      DerivedTwo(const DerivedOne& d1)
-          : Base(d1), DerivedTwoField(d1.DerivedOneField) {
+      DerivedTwo(const DerivedOne& D1)
+          : Base(D1), DerivedTwoField(D1.DerivedOneField) {
           (void)BaseField;
           }
     };
@@ -1565,7 +1565,34 @@ TEST(TransferTest, BaseClassInitializerFromSiblingDerivedInstance) {
       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
          ASTContext &ASTCtx) {
         // Regression test only; we used to crash when transferring the base
-        // class initializer from the DerivedToBase-cast `d1`.
+        // class initializer from the DerivedToBase-cast `D1`.
+      },
+      LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "DerivedTwo");
+}
+
+TEST(TransferTest, CopyAssignmentToDerivedToBase) {
+  std::string Code = R"cc(
+  struct Base {};
+
+struct DerivedOne : public Base {
+    int DerivedOneField;
+};
+
+struct DerivedTwo : public Base {
+    int DerivedTwoField;
+
+    explicit DerivedTwo(const DerivedOne& D1) {
+        *static_cast<Base*>(this) = D1;
+    }
+};
+)cc";
+
+  runDataflow(
+      Code,
+      [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+         ASTContext &ASTCtx) {
+        // Regression test only; we used to crash when transferring the copy
+        // assignment operator in the constructor for `DerivedTwo`.
       },
       LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "DerivedTwo");
 }

@llvmbot
Copy link
Member

llvmbot commented Sep 24, 2025

@llvm/pr-subscribers-clang-analysis

Author: Samira Bakon (bazuzi)

Changes

…opy/move assignments.

This mirrors the handling of copy/move constructors.

Also fix a couple capitalizations of variables in an adjacent and related test.


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

2 Files Affected:

  • (modified) clang/lib/Analysis/FlowSensitive/Transfer.cpp (+6-1)
  • (modified) clang/unittests/Analysis/FlowSensitive/TransferTest.cpp (+30-3)
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 60371d9498c25..f59bd6555aff5 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -657,7 +657,12 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
       if (LocSrc == nullptr || LocDst == nullptr)
         return;
 
-      copyRecord(*LocSrc, *LocDst, Env);
+      // If the destination object here is of a derived class, `Arg0` may be a
+      // cast of that object to a base class, and the destination object may be
+      // of a sibling derived class. To handle these cases, ensure we are
+      // copying only the fields for `Arg0`'s type, not the type of the
+      // underlying `RecordStorageLocation`.
+      copyRecord(*LocSrc, *LocDst, Env, Arg0->getType());
 
       // The assignment operator can have an arbitrary return type. We model the
       // return value only if the return type is the same as or a base class of
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index d97e2b0c2425a..cbd55966a3d88 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -1554,8 +1554,8 @@ TEST(TransferTest, BaseClassInitializerFromSiblingDerivedInstance) {
     struct DerivedTwo : public Base {
       int DerivedTwoField;
 
-      DerivedTwo(const DerivedOne& d1)
-          : Base(d1), DerivedTwoField(d1.DerivedOneField) {
+      DerivedTwo(const DerivedOne& D1)
+          : Base(D1), DerivedTwoField(D1.DerivedOneField) {
           (void)BaseField;
           }
     };
@@ -1565,7 +1565,34 @@ TEST(TransferTest, BaseClassInitializerFromSiblingDerivedInstance) {
       [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
          ASTContext &ASTCtx) {
         // Regression test only; we used to crash when transferring the base
-        // class initializer from the DerivedToBase-cast `d1`.
+        // class initializer from the DerivedToBase-cast `D1`.
+      },
+      LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "DerivedTwo");
+}
+
+TEST(TransferTest, CopyAssignmentToDerivedToBase) {
+  std::string Code = R"cc(
+  struct Base {};
+
+struct DerivedOne : public Base {
+    int DerivedOneField;
+};
+
+struct DerivedTwo : public Base {
+    int DerivedTwoField;
+
+    explicit DerivedTwo(const DerivedOne& D1) {
+        *static_cast<Base*>(this) = D1;
+    }
+};
+)cc";
+
+  runDataflow(
+      Code,
+      [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+         ASTContext &ASTCtx) {
+        // Regression test only; we used to crash when transferring the copy
+        // assignment operator in the constructor for `DerivedTwo`.
       },
       LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "DerivedTwo");
 }

Copy link
Contributor

@jvoung jvoung left a comment

Choose a reason for hiding this comment

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

Thanks!

And the other copyRecord further down "copyRecord(*LocDst, Env.getResultObjectLocation(*S), Env)" is safe because there is a stricter check "DstDecl->isDerivedFrom(ReturnDecl)"?

@bazuzi
Copy link
Contributor Author

bazuzi commented Sep 24, 2025

And the other copyRecord further down "copyRecord(*LocDst, Env.getResultObjectLocation(*S), Env)" is safe because there is a stricter check "DstDecl->isDerivedFrom(ReturnDecl)"?

Yes, the types there are already more restricted than the CompatibleTypes check inside of copyRecord. There may be some complex inheritance patterns that are compatible but not modeled because that check is more strict than necessary, but it is sufficient to make the copyRecord call safe, as far as I can tell.

Unrelated, I noticed a mistake in the comment before the copyRecord call I modified. I'll correct "destination object may be of a sibling derived class" to "source object may be of a sibling derived class" before merging.

@bazuzi bazuzi enabled auto-merge (squash) September 24, 2025 18:05
@bazuzi bazuzi merged commit f95aaca into llvm:main Sep 24, 2025
9 checks passed
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Oct 3, 2025
llvm#160557)

…opy/move assignments.

This mirrors the handling of copy/move constructors.

Also fix a couple capitalizations of variables in an adjacent and
related test.
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