mboehme created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
mboehme requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

To model the fact that values get deleted by a delete expression, this also
necessitates adding `DataflowEnvironment::unsetValue(StorageLocation &)` and
`StructValue::unsetValue(ValueDecl &)`.

Modeling object deletion in this way isn't perfect. In the framework, a value
that isn't present typically simply means "we didn't compute a value for this
storage location or struct field". Still, removing the values still seems better
than leaving them in place.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D147698

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/include/clang/Analysis/FlowSensitive/Value.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.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
@@ -5170,4 +5170,94 @@
       });
 }
 
+TEST(TransferTest, NewAndDeleteExpressions) {
+  std::string Code = R"(
+    void target() {
+      int *p = new int(42);
+      // [[after_new]]
+      delete p;
+      // [[after_delete]]
+    }
+  )";
+  runDataflow(
+      Code,
+      [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+         ASTContext &ASTCtx) {
+        const Environment &EnvAfterNew =
+            getEnvironmentAtAnnotation(Results, "after_new");
+        const Environment &EnvAfterDelete =
+            getEnvironmentAtAnnotation(Results, "after_delete");
+
+        auto &PAfterNew =
+            getValueForDecl<PointerValue>(ASTCtx, EnvAfterNew, "p");
+        auto &PAfterDelete =
+            getValueForDecl<PointerValue>(ASTCtx, EnvAfterNew, "p");
+
+        EXPECT_EQ(&PAfterNew, &PAfterDelete);
+
+        EXPECT_THAT(EnvAfterNew.getValue(PAfterNew.getPointeeLoc()), NotNull());
+        EXPECT_THAT(EnvAfterDelete.getValue(PAfterDelete.getPointeeLoc()),
+                    IsNull());
+      });
+}
+
+TEST(TransferTest, NewAndDeleteExpressions_Structs) {
+  std::string Code = R"(
+    struct Inner {
+      int InnerField;
+    };
+
+    struct Outer {
+      Inner OuterField;
+    };
+
+    void target() {
+      Outer *p = new Outer;
+      // Access the fields to make sure the analysis actually generates children
+      // for them in the `AggregateStorageLoc` and `StructValue`.
+      p->OuterField.InnerField;
+      // [[after_new]]
+      delete p;
+      // [[after_delete]]
+    }
+  )";
+  runDataflow(
+      Code,
+      [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+         ASTContext &ASTCtx) {
+        const Environment &EnvAfterNew =
+            getEnvironmentAtAnnotation(Results, "after_new");
+        const Environment &EnvAfterDelete =
+            getEnvironmentAtAnnotation(Results, "after_delete");
+
+        const ValueDecl *OuterField = findValueDecl(ASTCtx, "OuterField");
+        const ValueDecl *InnerField = findValueDecl(ASTCtx, "InnerField");
+
+        auto &PAfterNew =
+            getValueForDecl<PointerValue>(ASTCtx, EnvAfterNew, "p");
+        auto &PAfterDelete =
+            getValueForDecl<PointerValue>(ASTCtx, EnvAfterNew, "p");
+
+        EXPECT_EQ(&PAfterNew, &PAfterDelete);
+
+        // Storage locations always exist. (They _can't_ be different because
+        // PAfterNew is identical to PAfterDelete.)
+        auto &OuterLoc =
+            cast<AggregateStorageLocation>(PAfterNew.getPointeeLoc());
+        auto &OuterFieldLoc =
+            cast<AggregateStorageLocation>(OuterLoc.getChild(*OuterField));
+        auto &InnerFieldLoc = OuterFieldLoc.getChild(*InnerField);
+
+        // Values for the struct and all fields exist after the new.
+        EXPECT_THAT(EnvAfterNew.getValue(OuterLoc), NotNull());
+        EXPECT_THAT(EnvAfterNew.getValue(OuterFieldLoc), NotNull());
+        EXPECT_THAT(EnvAfterNew.getValue(InnerFieldLoc), NotNull());
+
+        // But the values no longer exist after the delete.
+        EXPECT_THAT(EnvAfterDelete.getValue(OuterLoc), IsNull());
+        EXPECT_THAT(EnvAfterDelete.getValue(OuterFieldLoc), IsNull());
+        EXPECT_THAT(EnvAfterDelete.getValue(InnerFieldLoc), IsNull());
+      });
+}
+
 } // namespace
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -469,6 +469,27 @@
     Env.setValue(Loc, Env.create<PointerValue>(*ThisPointeeLoc));
   }
 
+  void VisitCXXNewExpr(const CXXNewExpr *S) {
+    auto &Loc = Env.createStorageLocation(*S);
+    Env.setStorageLocation(*S, Loc);
+    if (Value *Val = Env.createValue(S->getType()))
+      Env.setValue(Loc, *Val);
+  }
+
+  void VisitCXXDeleteExpr(const CXXDeleteExpr *S) {
+    const auto *PointerVal = cast_or_null<PointerValue>(
+        Env.getValue(*S->getArgument(), SkipPast::Reference));
+    if (PointerVal == nullptr)
+      return;
+
+    Env.unsetValue(PointerVal->getPointeeLoc());
+
+    // We consciously don't destroy the `PointerValue` itself because some
+    // analyses may want to be able to continue to use it after a `delete`.
+    // For example, an analysis that diagnoses double deletes may want to attach
+    // properties to the `PointerValue` and access those after the first delete.
+  }
+
   void VisitReturnStmt(const ReturnStmt *S) {
     if (!Env.getAnalysisOptions().ContextSensitiveOpts)
       return;
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -634,6 +634,46 @@
   }
 }
 
+void Environment::unsetValue(const StorageLocation &Loc) {
+  auto It = LocToVal.find(&Loc);
+
+  if (It == LocToVal.end()) {
+    assert(false);
+    return;
+  }
+
+  Value &Val = *It->second;
+
+  LocToVal.erase(It);
+
+  if (auto *StructVal = dyn_cast<StructValue>(&Val)) {
+    auto &AggregateLoc = *cast<AggregateStorageLocation>(&Loc);
+
+    const QualType Type = AggregateLoc.getType();
+    assert(Type->isRecordType());
+
+    for (const FieldDecl *Field : DACtx->getReferencedFields(Type)) {
+      assert(Field != nullptr);
+      StorageLocation &FieldLoc = AggregateLoc.getChild(*Field);
+      MemberLocToStruct.erase(&FieldLoc);
+      if (auto *FieldVal = StructVal->getChild(*Field))
+        unsetValue(FieldLoc);
+    }
+  }
+
+  auto MemberLocToStructIt = MemberLocToStruct.find(&Loc);
+  if (MemberLocToStructIt != MemberLocToStruct.end()) {
+    // `Loc` is the location of a struct member so we need to also unset the
+    // value of the member in the corresponding `StructValue`.
+
+    auto [StructVal, Member] = MemberLocToStructIt->second;
+    assert(StructVal != nullptr);
+    assert(Member != nullptr);
+
+    StructVal->unsetChild(*Member);
+  }
+}
+
 Value *Environment::getValue(const StorageLocation &Loc) const {
   auto It = LocToVal.find(&Loc);
   return It == LocToVal.end() ? nullptr : It->second;
Index: clang/include/clang/Analysis/FlowSensitive/Value.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/Value.h
+++ clang/include/clang/Analysis/FlowSensitive/Value.h
@@ -307,6 +307,20 @@
   /// Assigns `Val` as the child value for `D`.
   void setChild(const ValueDecl &D, Value &Val) { Children[&D] = &Val; }
 
+  /// Removes the association between `D` and its current child value.
+  ///
+  /// Requirements:
+  ///
+  ///  `D` must currently be associated with a value.
+  void unsetChild(const ValueDecl &D) {
+    auto It = Children.find(&D);
+    if (It == Children.end()) {
+      assert(false);
+      return;
+    }
+    Children.erase(It);
+  }
+
 private:
   llvm::DenseMap<const ValueDecl *, Value *> Children;
 };
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -307,6 +307,13 @@
   /// Assigns `Val` as the value of `Loc` in the environment.
   void setValue(const StorageLocation &Loc, Value &Val);
 
+  /// Removes the association between `Loc` and its current value.
+  ///
+  /// Requirements:
+  ///
+  ///  There must be a value currently associated with `Loc`.
+  void unsetValue(const StorageLocation &Loc);
+
   /// Returns the value assigned to `Loc` in the environment or null if `Loc`
   /// isn't assigned a value in the environment.
   Value *getValue(const StorageLocation &Loc) const;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to