ymandel created this revision.
ymandel added reviewers: sgatev, xazax.hun, li.zhe.hua.
Herald added subscribers: tschuett, steakhal, rnkovacs.
Herald added a project: All.
ymandel requested review of this revision.
Herald added a project: clang.

The API for `AggregateStorageLocation` does not allow for missing fields (it 
asserts). Therefore, it is incorrect to filter out any fields at 
location-creation time which may be accessed by the code. Currently, we limit 
filtering to private, base-calss fields on the assumption that those can never 
be accessed. However, `friend` declarations can invalidate that assumption, 
thereby breaking our invariants.

This patch removes said field filtering to avoid violating the invariant of "no 
missing fields" for `AggregateStorageLocation`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D126420

Files:
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -1226,6 +1226,35 @@
       });
 }
 
+TEST_F(TransferTest, DerivedBaseMemberPrivateFriend) {
+  // Include an access to `Foo.Bar` to verify the analysis doesn't crash on that
+  // access.
+  std::string Code = R"(
+    class A {
+    private:
+      friend void target();
+      int Bar;
+    };
+    struct B : public A {
+    };
+
+    void target() {
+      B Foo;
+      (void)Foo.Bar;
+      // [[p]]
+    }
+  )";
+  runDataflow(
+      Code, [](llvm::ArrayRef<
+                   std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+                   Results,
+               ASTContext &ASTCtx) {
+        // The purpose of this test is to verify non-crashing behavior for the
+        // analysis of the access to `Foo.Bar`. So, we
+        EXPECT_THAT(Results, ElementsAre(Pair("p", _)));
+      });
+}
+
 TEST_F(TransferTest, ClassMember) {
   std::string Code = R"(
     class A {
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -148,6 +148,8 @@
   }
 }
 
+// FIXME: Does not precisely handle non-virtual diamond inheritance. A single
+// field decl will be modeled for all instances of the inherited field.
 static void
 getFieldsFromClassHierarchy(QualType Type, bool IgnorePrivateFields,
                             llvm::DenseSet<const FieldDecl *> &Fields) {
@@ -162,24 +164,40 @@
       continue;
     Fields.insert(Field);
   }
-  if (auto *CXXRecord = Type->getAsCXXRecordDecl()) {
-    for (const CXXBaseSpecifier &Base : CXXRecord->bases()) {
-      // Ignore private fields (including default access in C++ classes) in
-      // base classes, because they are not visible in derived classes.
-      getFieldsFromClassHierarchy(Base.getType(), /*IgnorePrivateFields=*/true,
-                                  Fields);
-    }
-  }
+  if (auto *CXXRecord = Type->getAsCXXRecordDecl())
+    for (const CXXBaseSpecifier &Base : CXXRecord->bases())
+      getFieldsFromClassHierarchy(
+          Base.getType(), IgnorePrivateFields, Fields);
 }
 
-/// Gets the set of all fields accesible from the type.
+/// Gets the set of all fields accesible from the type. Admits private fields of
+/// the type, but not of any base classes (which are presumably inaccessible.
 ///
-/// FIXME: Does not precisely handle non-virtual diamond inheritance. A single
-/// field decl will be modeled for all instances of the inherited field.
+/// FIXME: Does not account for friend declarations, which may make private
+/// fields visible.
 static llvm::DenseSet<const FieldDecl *>
 getAccessibleObjectFields(QualType Type) {
   llvm::DenseSet<const FieldDecl *> Fields;
-  // Don't ignore private fields for the class itself, only its super classes.
+  if (Type->isIncompleteType() || Type->isDependentType() ||
+      !Type->isRecordType())
+    return Fields;
+
+  for (const FieldDecl *Field : Type->getAsRecordDecl()->fields())
+    Fields.insert(Field);
+
+  if (auto *CXXRecord = Type->getAsCXXRecordDecl())
+    for (const CXXBaseSpecifier &Base : CXXRecord->bases())
+      // Ignore private fields (including default access in C++ classes) in base
+      // classes, because they are not visible in derived classes.
+      getFieldsFromClassHierarchy(Base.getType(), /*IgnorePrivateFields=*/true,
+                                  Fields);
+
+  return Fields;
+}
+
+/// Gets the set of all fields in the type.
+static llvm::DenseSet<const FieldDecl *> getObjectFields(QualType Type) {
+  llvm::DenseSet<const FieldDecl *> Fields;
   getFieldsFromClassHierarchy(Type, /*IgnorePrivateFields=*/false, Fields);
   return Fields;
 }
@@ -319,9 +337,8 @@
     // FIXME: Explore options to avoid eager initialization of fields as some of
     // them might not be needed for a particular analysis.
     llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
-    for (const FieldDecl *Field : getAccessibleObjectFields(Type)) {
+    for (const FieldDecl *Field : getObjectFields(Type))
       FieldLocs.insert({Field, &createStorageLocation(Field->getType())});
-    }
     return takeOwnership(
         std::make_unique<AggregateStorageLocation>(Type, std::move(FieldLocs)));
   }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to