[clang] Revert "[clang][dataflow] Propagate locations from result objects to initializers." (PR #88315)

via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 10 12:27:25 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: None (martinboehme)

<details>
<summary>Changes</summary>

Reverts llvm/llvm-project#<!-- -->87320

This is causing buildbots to fail because `isOriginalRecordConstructor()` is now unused.

---

Patch is 53.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/88315.diff


6 Files Affected:

- (modified) clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h (+20-44) 
- (modified) clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp (+98-307) 
- (modified) clang/lib/Analysis/FlowSensitive/Transfer.cpp (+98-78) 
- (modified) clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp (+10-3) 
- (modified) clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp (-43) 
- (modified) clang/unittests/Analysis/FlowSensitive/TransferTest.cpp (+57-115) 


``````````diff
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index 706664d7db1c25..9a65f76cdf56bc 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -30,7 +30,6 @@
 #include "llvm/ADT/MapVector.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
-#include <memory>
 #include <type_traits>
 #include <utility>
 
@@ -345,6 +344,17 @@ class Environment {
   /// location of the result object to pass in `this`, even though prvalues are
   /// otherwise not associated with storage locations.
   ///
+  /// FIXME: Currently, this simply returns a stable storage location for `E`,
+  /// but this doesn't do the right thing in scenarios like the following:
+  /// ```
+  /// MyClass c = some_condition()? MyClass(foo) : MyClass(bar);
+  /// ```
+  /// Here, `MyClass(foo)` and `MyClass(bar)` will have two different storage
+  /// locations, when in fact their storage locations should be the same.
+  /// Eventually, we want to propagate storage locations from result objects
+  /// down to the prvalues that initialize them, similar to the way that this is
+  /// done in Clang's CodeGen.
+  ///
   /// Requirements:
   ///  `E` must be a prvalue of record type.
   RecordStorageLocation &
@@ -452,13 +462,7 @@ class Environment {
   /// Initializes the fields (including synthetic fields) of `Loc` with values,
   /// unless values of the field type are not supported or we hit one of the
   /// limits at which we stop producing values.
-  /// If `Type` is provided, initializes only those fields that are modeled for
-  /// `Type`; this is intended for use in cases where `Loc` is a derived type
-  /// and we only want to initialize the fields of a base type.
-  void initializeFieldsWithValues(RecordStorageLocation &Loc, QualType Type);
-  void initializeFieldsWithValues(RecordStorageLocation &Loc) {
-    initializeFieldsWithValues(Loc, Loc.getType());
-  }
+  void initializeFieldsWithValues(RecordStorageLocation &Loc);
 
   /// Assigns `Val` as the value of `Loc` in the environment.
   void setValue(const StorageLocation &Loc, Value &Val);
@@ -649,9 +653,6 @@ class Environment {
   LLVM_DUMP_METHOD void dump(raw_ostream &OS) const;
 
 private:
-  using PrValueToResultObject =
-      llvm::DenseMap<const Expr *, RecordStorageLocation *>;
-
   // The copy-constructor is for use in fork() only.
   Environment(const Environment &) = default;
 
@@ -681,10 +682,8 @@ class Environment {
   /// Initializes the fields (including synthetic fields) of `Loc` with values,
   /// unless values of the field type are not supported or we hit one of the
   /// limits at which we stop producing values (controlled by `Visited`,
-  /// `Depth`, and `CreatedValuesCount`). If `Type` is different from
-  /// `Loc.getType()`, initializes only those fields that are modeled for
-  /// `Type`.
-  void initializeFieldsWithValues(RecordStorageLocation &Loc, QualType Type,
+  /// `Depth`, and `CreatedValuesCount`).
+  void initializeFieldsWithValues(RecordStorageLocation &Loc,
                                   llvm::DenseSet<QualType> &Visited, int Depth,
                                   int &CreatedValuesCount);
 
@@ -703,45 +702,22 @@ class Environment {
   /// and functions referenced in `FuncDecl`. `FuncDecl` must have a body.
   void initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl);
 
-  static PrValueToResultObject
-  buildResultObjectMap(DataflowAnalysisContext *DACtx,
-                       const FunctionDecl *FuncDecl,
-                       RecordStorageLocation *ThisPointeeLoc,
-                       RecordStorageLocation *LocForRecordReturnVal);
-
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
-  // FIXME: move the fields `CallStack`, `ResultObjectMap`, `ReturnVal`,
-  // `ReturnLoc` and `ThisPointeeLoc` into a separate call-context object,
-  // shared between environments in the same call.
+  // FIXME: move the fields `CallStack`, `ReturnVal`, `ReturnLoc` and
+  // `ThisPointeeLoc` into a separate call-context object, shared between
+  // environments in the same call.
   // https://github.com/llvm/llvm-project/issues/59005
 
   // `DeclContext` of the block being analysed if provided.
   std::vector<const DeclContext *> CallStack;
 
-  // Maps from prvalues of record type to their result objects. Shared between
-  // all environments for the same function.
-  // FIXME: It's somewhat unsatisfactory that we have to use a `shared_ptr`
-  // here, though the cost is acceptable: The overhead of a `shared_ptr` is
-  // incurred when it is copied, and this happens only relatively rarely (when
-  // we fork the environment). The need for a `shared_ptr` will go away once we
-  // introduce a shared call-context object (see above).
-  std::shared_ptr<PrValueToResultObject> ResultObjectMap;
-
-  // The following three member variables handle various different types of
-  // return values.
-  // - If the return type is not a reference and not a record: Value returned
-  //   by the function.
+  // Value returned by the function (if it has non-reference return type).
   Value *ReturnVal = nullptr;
-  // - If the return type is a reference: Storage location of the reference
-  //   returned by the function.
+  // Storage location of the reference returned by the function (if it has
+  // reference return type).
   StorageLocation *ReturnLoc = nullptr;
-  // - If the return type is a record or the function being analyzed is a
-  //   constructor: Storage location into which the return value should be
-  //   constructed.
-  RecordStorageLocation *LocForRecordReturnVal = nullptr;
-
   // The storage location of the `this` pointee. Should only be null if the
   // function being analyzed is only a function and not a method.
   RecordStorageLocation *ThisPointeeLoc = nullptr;
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index 6c796b4ad923e8..1bfa7ebcfd50c9 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -15,7 +15,6 @@
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
-#include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
@@ -27,8 +26,6 @@
 #include <cassert>
 #include <utility>
 
-#define DEBUG_TYPE "dataflow"
-
 namespace clang {
 namespace dataflow {
 
@@ -357,8 +354,6 @@ getFieldsGlobalsAndFuncs(const Stmt &S, FieldSet &Fields,
   for (auto *Child : S.children())
     if (Child != nullptr)
       getFieldsGlobalsAndFuncs(*Child, Fields, Vars, Funcs);
-  if (const auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(&S))
-    getFieldsGlobalsAndFuncs(*DefaultArg->getExpr(), Fields, Vars, Funcs);
   if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(&S))
     getFieldsGlobalsAndFuncs(*DefaultInit->getExpr(), Fields, Vars, Funcs);
 
@@ -391,186 +386,6 @@ getFieldsGlobalsAndFuncs(const Stmt &S, FieldSet &Fields,
   }
 }
 
-namespace {
-
-// Visitor that builds a map from record prvalues to result objects.
-// This traverses the body of the function to be analyzed; for each result
-// object that it encounters, it propagates the storage location of the result
-// object to all record prvalues that can initialize it.
-class ResultObjectVisitor : public RecursiveASTVisitor<ResultObjectVisitor> {
-public:
-  // `ResultObjectMap` will be filled with a map from record prvalues to result
-  // object. If the function being analyzed returns a record by value,
-  // `LocForRecordReturnVal` is the location to which this record should be
-  // written; otherwise, it is null.
-  explicit ResultObjectVisitor(
-      llvm::DenseMap<const Expr *, RecordStorageLocation *> &ResultObjectMap,
-      RecordStorageLocation *LocForRecordReturnVal,
-      DataflowAnalysisContext &DACtx)
-      : ResultObjectMap(ResultObjectMap),
-        LocForRecordReturnVal(LocForRecordReturnVal), DACtx(DACtx) {}
-
-  bool shouldVisitImplicitCode() { return true; }
-
-  bool shouldVisitLambdaBody() const { return false; }
-
-  // Traverse all member and base initializers of `Ctor`. This function is not
-  // called by `RecursiveASTVisitor`; it should be called manually if we are
-  // analyzing a constructor. `ThisPointeeLoc` is the storage location that
-  // `this` points to.
-  void TraverseConstructorInits(const CXXConstructorDecl *Ctor,
-                                RecordStorageLocation *ThisPointeeLoc) {
-    assert(ThisPointeeLoc != nullptr);
-    for (const CXXCtorInitializer *Init : Ctor->inits()) {
-      Expr *InitExpr = Init->getInit();
-      if (FieldDecl *Field = Init->getMember();
-          Field != nullptr && Field->getType()->isRecordType()) {
-        PropagateResultObject(InitExpr, cast<RecordStorageLocation>(
-                                            ThisPointeeLoc->getChild(*Field)));
-      } else if (Init->getBaseClass()) {
-        PropagateResultObject(InitExpr, ThisPointeeLoc);
-      }
-
-      // Ensure that any result objects within `InitExpr` (e.g. temporaries)
-      // are also propagated to the prvalues that initialize them.
-      TraverseStmt(InitExpr);
-
-      // If this is a `CXXDefaultInitExpr`, also propagate any result objects
-      // within the default expression.
-      if (auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(InitExpr))
-        TraverseStmt(DefaultInit->getExpr());
-    }
-  }
-
-  bool TraverseBindingDecl(BindingDecl *BD) {
-    // `RecursiveASTVisitor` doesn't traverse holding variables for
-    // `BindingDecl`s by itself, so we need to tell it to.
-    if (VarDecl *HoldingVar = BD->getHoldingVar())
-      TraverseDecl(HoldingVar);
-    return RecursiveASTVisitor<ResultObjectVisitor>::TraverseBindingDecl(BD);
-  }
-
-  bool VisitVarDecl(VarDecl *VD) {
-    if (VD->getType()->isRecordType() && VD->hasInit())
-      PropagateResultObject(
-          VD->getInit(),
-          &cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*VD)));
-    return true;
-  }
-
-  bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE) {
-    if (MTE->getType()->isRecordType())
-      PropagateResultObject(
-          MTE->getSubExpr(),
-          &cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*MTE)));
-    return true;
-  }
-
-  bool VisitReturnStmt(ReturnStmt *Return) {
-    Expr *RetValue = Return->getRetValue();
-    if (RetValue != nullptr && RetValue->getType()->isRecordType() &&
-        RetValue->isPRValue())
-      PropagateResultObject(RetValue, LocForRecordReturnVal);
-    return true;
-  }
-
-  bool VisitExpr(Expr *E) {
-    // Clang's AST can have record-type prvalues without a result object -- for
-    // example as full-expressions contained in a compound statement or as
-    // arguments of call expressions. We notice this if we get here and a
-    // storage location has not yet been associated with `E`. In this case,
-    // treat this as if it was a `MaterializeTemporaryExpr`.
-    if (E->isPRValue() && E->getType()->isRecordType() &&
-        !ResultObjectMap.contains(E))
-      PropagateResultObject(
-          E, &cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*E)));
-    return true;
-  }
-
-  // Assigns `Loc` as the result object location of `E`, then propagates the
-  // location to all lower-level prvalues that initialize the same object as
-  // `E` (or one of its base classes or member variables).
-  void PropagateResultObject(Expr *E, RecordStorageLocation *Loc) {
-    if (!E->isPRValue() || !E->getType()->isRecordType()) {
-      assert(false);
-      // Ensure we don't propagate the result object if we hit this in a
-      // release build.
-      return;
-    }
-
-    ResultObjectMap[E] = Loc;
-
-    // The following AST node kinds are "original initializers": They are the
-    // lowest-level AST node that initializes a given object, and nothing
-    // below them can initialize the same object (or part of it).
-    if (isa<CXXConstructExpr>(E) || isa<CallExpr>(E) || isa<LambdaExpr>(E) ||
-        isa<CXXDefaultArgExpr>(E) || isa<CXXDefaultInitExpr>(E) ||
-        isa<CXXStdInitializerListExpr>(E)) {
-      return;
-    }
-
-    if (auto *InitList = dyn_cast<InitListExpr>(E)) {
-      if (!InitList->isSemanticForm())
-        return;
-      if (InitList->isTransparent()) {
-        PropagateResultObject(InitList->getInit(0), Loc);
-        return;
-      }
-
-      RecordInitListHelper InitListHelper(InitList);
-
-      for (auto [Base, Init] : InitListHelper.base_inits()) {
-        assert(Base->getType().getCanonicalType() ==
-               Init->getType().getCanonicalType());
-
-        // Storage location for the base class is the same as that of the
-        // derived class because we "flatten" the object hierarchy and put all
-        // fields in `RecordStorageLocation` of the derived class.
-        PropagateResultObject(Init, Loc);
-      }
-
-      for (auto [Field, Init] : InitListHelper.field_inits()) {
-        // Fields of non-record type are handled in
-        // `TransferVisitor::VisitInitListExpr()`.
-        if (!Field->getType()->isRecordType())
-          continue;
-        PropagateResultObject(
-            Init, cast<RecordStorageLocation>(Loc->getChild(*Field)));
-      }
-      return;
-    }
-
-    if (auto *Op = dyn_cast<BinaryOperator>(E); Op && Op->isCommaOp()) {
-      PropagateResultObject(Op->getRHS(), Loc);
-      return;
-    }
-
-    if (auto *Cond = dyn_cast<AbstractConditionalOperator>(E)) {
-      PropagateResultObject(Cond->getTrueExpr(), Loc);
-      PropagateResultObject(Cond->getFalseExpr(), Loc);
-      return;
-    }
-
-    // All other expression nodes that propagate a record prvalue should have
-    // exactly one child.
-    SmallVector<Stmt *, 1> Children(E->child_begin(), E->child_end());
-    LLVM_DEBUG({
-      if (Children.size() != 1)
-        E->dump();
-    });
-    assert(Children.size() == 1);
-    for (Stmt *S : Children)
-      PropagateResultObject(cast<Expr>(S), Loc);
-  }
-
-private:
-  llvm::DenseMap<const Expr *, RecordStorageLocation *> &ResultObjectMap;
-  RecordStorageLocation *LocForRecordReturnVal;
-  DataflowAnalysisContext &DACtx;
-};
-
-} // namespace
-
 Environment::Environment(DataflowAnalysisContext &DACtx)
     : DACtx(&DACtx),
       FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {}
@@ -586,23 +401,17 @@ void Environment::initialize() {
   if (DeclCtx == nullptr)
     return;
 
-  const auto *FuncDecl = dyn_cast<FunctionDecl>(DeclCtx);
-  if (FuncDecl == nullptr)
-    return;
-
-  assert(FuncDecl->doesThisDeclarationHaveABody());
+  if (const auto *FuncDecl = dyn_cast<FunctionDecl>(DeclCtx)) {
+    assert(FuncDecl->doesThisDeclarationHaveABody());
 
-  initFieldsGlobalsAndFuncs(FuncDecl);
+    initFieldsGlobalsAndFuncs(FuncDecl);
 
-  for (const auto *ParamDecl : FuncDecl->parameters()) {
-    assert(ParamDecl != nullptr);
-    setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr));
+    for (const auto *ParamDecl : FuncDecl->parameters()) {
+      assert(ParamDecl != nullptr);
+      setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr));
+    }
   }
 
-  if (FuncDecl->getReturnType()->isRecordType())
-    LocForRecordReturnVal = &cast<RecordStorageLocation>(
-        createStorageLocation(FuncDecl->getReturnType()));
-
   if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(DeclCtx)) {
     auto *Parent = MethodDecl->getParent();
     assert(Parent != nullptr);
@@ -635,12 +444,6 @@ void Environment::initialize() {
         initializeFieldsWithValues(ThisLoc);
     }
   }
-
-  // We do this below the handling of `CXXMethodDecl` above so that we can
-  // be sure that the storage location for `this` has been set.
-  ResultObjectMap = std::make_shared<PrValueToResultObject>(
-      buildResultObjectMap(DACtx, FuncDecl, getThisPointeeStorageLocation(),
-                           LocForRecordReturnVal));
 }
 
 // FIXME: Add support for resetting globals after function calls to enable
@@ -681,18 +484,13 @@ void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) {
     if (getStorageLocation(*D) != nullptr)
       continue;
 
-    // We don't run transfer functions on the initializers of global variables,
-    // so they won't be associated with a value or storage location. We
-    // therefore intentionally don't pass an initializer to `createObject()`;
-    // in particular, this ensures that `createObject()` will initialize the
-    // fields of record-type variables with values.
-    setStorageLocation(*D, createObject(*D, nullptr));
+    setStorageLocation(*D, createObject(*D));
   }
 
   for (const FunctionDecl *FD : Funcs) {
     if (getStorageLocation(*FD) != nullptr)
       continue;
-    auto &Loc = createStorageLocation(*FD);
+    auto &Loc = createStorageLocation(FD->getType());
     setStorageLocation(*FD, Loc);
   }
 }
@@ -721,9 +519,6 @@ Environment Environment::pushCall(const CallExpr *Call) const {
     }
   }
 
-  if (Call->getType()->isRecordType() && Call->isPRValue())
-    Env.LocForRecordReturnVal = &Env.getResultObjectLocation(*Call);
-
   Env.pushCallInternal(Call->getDirectCallee(),
                        llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));
 
@@ -734,7 +529,6 @@ Environment Environment::pushCall(const CXXConstructExpr *Call) const {
   Environment Env(*this);
 
   Env.ThisPointeeLoc = &Env.getResultObjectLocation(*Call);
-  Env.LocForRecordReturnVal = &Env.getResultObjectLocation(*Call);
 
   Env.pushCallInternal(Call->getConstructor(),
                        llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));
@@ -763,10 +557,6 @@ void Environment::pushCallInternal(const FunctionDecl *FuncDecl,
     const VarDecl *Param = *ParamIt;
     setStorageLocation(*Param, createObject(*Param, Args[ArgIndex]));
   }
-
-  ResultObjectMap = std::make_shared<PrValueToResultObject>(
-      buildResultObjectMap(DACtx, FuncDecl, getThisPointeeStorageLocation(),
-                           LocForRecordReturnVal));
 }
 
 void Environment::popCall(const CallExpr *Call, const Environment &CalleeEnv) {
@@ -810,9 +600,6 @@ bool Environment::equivalentTo(const Environment &Other,
   if (ReturnLoc != Other.ReturnLoc)
     return false;
 
-  if (LocForRecordReturnVal != Other.LocForRecordReturnVal)
-    return false;
-
   if (ThisPointeeLoc != Other.ThisPointeeLoc)
     return false;
 
@@ -836,10 +623,8 @@ LatticeEffect Environment::widen(const Environment &PrevEnv,
   assert(DACtx == PrevEnv.DACtx);
   assert(ReturnVal == PrevEnv.ReturnVal);
   assert(ReturnLoc == PrevEnv.ReturnLoc);
-  assert(LocForRecordReturnVal == PrevEnv.LocForRecordReturnVal);
   assert(ThisPointeeLoc == PrevEnv.ThisPointeeLoc);
   assert(CallStack == PrevEnv.CallStack);
-  assert(ResultObjectMap == PrevEnv.ResultObjectMap);
 
   auto Effect = LatticeEffect::Unchanged;
 
@@ -871,16 +656,12 @@ Environment Environment::join(const Environment &EnvA, const Environment &EnvB,
                               Environment::ValueModel &Model,
                               ExprJoinBehavior ExprBehavior) {
   assert(EnvA.DACtx == EnvB.DACtx);
-  assert(EnvA.LocForRecordReturnVal == EnvB.LocForRecordReturnVal);
   assert(EnvA.ThisPointeeLoc == EnvB.ThisPointeeLoc);
   assert(EnvA.CallStack == EnvB.CallStack);
-  assert(EnvA.ResultObjectMap == EnvB.ResultObjectMap);
 
   Environment JoinedEnv(*EnvA.DACtx);
 
   JoinedEnv.CallStack = EnvA.CallStack;
-  JoinedEnv.ResultObjectMap = EnvA.ResultObjectMap;
-  JoinedEnv.LocForRecordReturnVal = EnvA.LocForRecordReturnVal;
   JoinedEnv.ThisPointeeLoc = EnvA.ThisPointeeLoc;
 
   if (EnvA.ReturnVal == nullptr || EnvB.ReturnVal == nullptr) {
@@ -949,12 +730,6 @@ StorageLocation &Environment::createStorageLocation(const Expr &E) {
 
 void Environment::setStorageLocation(const ValueDecl &D, StorageLocation &Loc) {
   assert(!DeclToLoc.contains(&D));
-  // The only kinds of declarations that may have a "variable" storage location
-  // are declarations of reference type and `BindingDecl`. For all other
-  // declaration, the storage location should be the stable storage location
- ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/88315


More information about the cfe-commits mailing list