Simplify handling of function calls, and prepare support for callbacks.

Function calls are now handled by creating a generic FunctionLifetimes for each
call, and imposing constraints on that FunctionLifetimes and objects that are
passed to the call.

This should be reasonably easy to extend to callbacks.

There are a couple of yet-unsolved issues with this approach, related to the
handling of flow-sensitiveness:
- Function calls are no longer a point where flow sensitiveness might cause
  variable lifetimes to change, hence this causes some overly-constraining results.
- After a function call, a local variable may no longer be overwritable; some infra
  is in place to handle this, but as of now there is no testcase that shows that
  this might happen, and the code is not complete.

PiperOrigin-RevId: 502403144
diff --git a/lifetime_analysis/object_repository.cc b/lifetime_analysis/object_repository.cc
index 61c6008..6b6c441 100644
--- a/lifetime_analysis/object_repository.cc
+++ b/lifetime_analysis/object_repository.cc
@@ -99,34 +99,30 @@
       assert(InitializedObjectWasPropagatedTo(call_expr));
     }
 
-    // For calls to members, the type of the callee is a "bound member function
-    // type", so we look at the declaration instead.
-    if (auto member_call =
-            clang::dyn_cast<clang::CXXMemberCallExpr>(call_expr)) {
-      const clang::FunctionDecl* callee = call_expr->getDirectCallee();
-      // TODO(veluca): pointers-to-members are not supported (yet?)
-      assert(callee);
-      AddObjectsForArguments(call_expr, callee->getType(),
-                             /*index_shift=*/0);
-      auto method = clang::cast<clang::CXXMethodDecl>(callee);
-      clang::QualType type = method->getThisType();
-      object_repository_.call_expr_this_pointers_[call_expr] =
-          CreateLocalObject(type);
-    } else if (auto op_call =
-                   clang::dyn_cast<clang::CXXOperatorCallExpr>(call_expr)) {
-      const clang::FunctionDecl* callee = call_expr->getDirectCallee();
-      auto method = clang::dyn_cast<clang::CXXMethodDecl>(callee);
-      AddObjectsForArguments(call_expr, callee->getType(),
-                             /*index_shift=*/method ? 1 : 0);
-      if (method) {
-        clang::QualType type = method->getThisType();
-        object_repository_.call_expr_this_pointers_[call_expr] =
-            CreateLocalObject(type);
-      }
+    FunctionLifetimeFactorySingleCallback lifetime_factory(
+        [](auto) { return Lifetime::CreateVariable(); });
+
+    // If we have a direct callee, construct a FunctionLifetimes out of the
+    // function/method definition.
+    if (auto callee = call_expr->getDirectCallee()) {
+      bool is_operator_call = clang::isa<clang::CXXOperatorCallExpr>(call_expr);
+      bool is_method = clang::isa<clang::CXXMethodDecl>(callee);
+      object_repository_.call_expr_virtual_lifetimes_[call_expr] =
+          FunctionLifetimes::CreateForDecl(callee, lifetime_factory).get();
+      PrepareFunctionCall(
+          call_expr, /*index_shift=*/is_operator_call && is_method ? 1 : 0);
     } else {
       // Always a function pointer.
-      clang::QualType callee_type = call_expr->getCallee()->getType();
-      AddObjectsForArguments(call_expr, callee_type, /*index_shift=*/0);
+      // TODO(veluca): pointers-to-members are not supported (yet?)
+      clang::QualType callee_type =
+          call_expr->getCallee()->getType()->getPointeeType().IgnoreParens();
+      // TODO(veluca): what about FunctionNoProtoType??
+      object_repository_.call_expr_virtual_lifetimes_[call_expr] =
+          FunctionLifetimes::CreateForFunctionType(
+              clang::cast<clang::FunctionProtoType>(callee_type),
+              lifetime_factory)
+              .get();
+      PrepareFunctionCall(call_expr, /*index_shift=*/0);
     }
 
     return true;
@@ -135,13 +131,13 @@
   bool VisitCXXConstructExpr(clang::CXXConstructExpr* construct_expr) {
     assert(InitializedObjectWasPropagatedTo(construct_expr));
 
-    // Create objects for constructor arguments.
+    FunctionLifetimeFactorySingleCallback lifetime_factory(
+        [](auto) { return Lifetime::CreateVariable(); });
     const clang::FunctionDecl* constructor = construct_expr->getConstructor();
-    AddObjectsForArguments(construct_expr, constructor->getType(),
-                           /*index_shift=*/0);
-    clang::QualType type = construct_expr->getConstructor()->getThisType();
-    object_repository_.call_expr_this_pointers_[construct_expr] =
-        CreateLocalObject(type);
+    object_repository_.call_expr_virtual_lifetimes_[construct_expr] =
+        FunctionLifetimes::CreateForDecl(constructor, lifetime_factory).get();
+    PrepareFunctionCall(construct_expr,
+                        /*index_shift=*/0);
     return true;
   }
 
@@ -172,41 +168,28 @@
     return true;
   }
 
-  const Object* CreateLocalObject(clang::QualType type) {
-    const Object* object =
-        object_repository_.CreateObject(Lifetime::CreateLocal(), type);
-    object_repository_.CreateObjects(
-        object, type,
-        [](const clang::Expr*) { return Lifetime::CreateVariable(); },
-        /*transitive=*/false);
-    return object;
-  }
-
-  void AddObjectsForArguments(const clang::Expr* expr,
-                              clang::QualType callee_type, size_t index_shift) {
-    if (callee_type->isDependentType()) {
-      // TODO(veluca): the fact that we reach this point is a clang bug: it
-      // should not be possible to reach dependent types from a template
-      // instantiation. See also the following discussion, where richardsmith@
-      // agrees this looks like a Clang bug and suggests how it might be fixed:
-      // https://chat.google.com/room/AAAAb6i7WDQ/OvLC9NgO91A
-      return;
-    }
-    if (callee_type->isPointerType()) {
-      callee_type = callee_type->getPointeeType();
-    }
-    // TODO(veluca): figure out how to create a test where the callee is a
-    // ParenType.
-    // For reference, this was triggered in the implementation of `bsearch`.
-    callee_type = callee_type.IgnoreParens();
-    assert(callee_type->isFunctionType());
-    // TODO(veluca): could this be a clang::FunctionNoProtoType??
-    const auto* fn_type = clang::cast<clang::FunctionProtoType>(callee_type);
-    for (size_t i = 0; i < fn_type->getNumParams(); ++i) {
+  void PrepareFunctionCall(const clang::Expr* expr, size_t index_shift) {
+    const auto& func_lifetimes =
+        object_repository_.call_expr_virtual_lifetimes_[expr];
+    auto make_object = [this](const ValueLifetimes& lifetime) {
+      const Object* object = object_repository_.CreateObject(
+          Lifetime::CreateLocal(), lifetime.Type());
+      object_repository_.CreateObjectsWithLifetimes(
+          object, lifetime,
+          /*transitive=*/true, object_repository_.initial_points_to_map_);
+      return object;
+    };
+    for (size_t i = 0; i < func_lifetimes.GetNumParams(); ++i) {
       object_repository_
           .call_expr_args_objects_[std::make_pair(expr, i + index_shift)] =
-          CreateLocalObject(fn_type->getParamType(i));
+          make_object(func_lifetimes.GetParamLifetimes(i));
     }
+    if (func_lifetimes.IsNonStaticMethod()) {
+      object_repository_.call_expr_this_pointers_[expr] =
+          make_object(func_lifetimes.GetThisLifetimes());
+    }
+    object_repository_.call_expr_ret_objects_[expr] =
+        make_object(func_lifetimes.GetReturnLifetimes());
   }
 
   void AddObjectForVar(clang::VarDecl* var) {
@@ -237,10 +220,8 @@
     const Object* object =
         object_repository_.CreateObject(lifetime, var->getType());
 
-    object_repository_.CreateObjects(
-        object, var->getType(), lifetime_factory,
-        /*transitive=*/clang::isa<clang::ParmVarDecl>(var) ||
-            lifetime == Lifetime::Static());
+    object_repository_.CreateObjects(object, var->getType(), lifetime_factory,
+                                     /*transitive=*/true);
 
     object_repository_.object_repository_[var] = object;
     if (!var->getType()->isArrayType()) {
@@ -275,7 +256,7 @@
     object_repository_.CreateObjects(
         object, type,
         [](const clang::Expr*) { return Lifetime::CreateVariable(); },
-        /*transitive=*/false);
+        /*transitive=*/true);
 
     if (type->isRecordType()) {
       PropagateInitializedObject(expr, object);
@@ -614,6 +595,30 @@
   return iter->second;
 }
 
+const Object* ObjectRepository::GetCallExprRetObject(
+    const clang::Expr* expr) const {
+  auto iter = call_expr_ret_objects_.find(expr);
+  if (iter == call_expr_ret_objects_.end()) {
+    llvm::errs() << "Didn't find object for return value of call:\n";
+    expr->dump();
+    llvm::errs() << "\n" << DebugString();
+    llvm::report_fatal_error("Didn't find object for return value");
+  }
+  return iter->second;
+}
+
+const FunctionLifetimes& ObjectRepository::GetCallExprVirtualLifetimes(
+    const clang::Expr* expr) const {
+  auto iter = call_expr_virtual_lifetimes_.find(expr);
+  if (iter == call_expr_virtual_lifetimes_.end()) {
+    llvm::errs() << "Didn't find object for return value of call:\n";
+    expr->dump();
+    llvm::errs() << "\n" << DebugString();
+    llvm::report_fatal_error("Didn't find object for return value");
+  }
+  return iter->second;
+}
+
 const Object* ObjectRepository::GetCallExprThisPointer(
     const clang::CallExpr* expr) const {
   auto iter = call_expr_this_pointers_.find(expr);
@@ -731,14 +736,24 @@
   return object;
 }
 
-void ObjectRepository::CreateObjects(const Object* root_object,
-                                     clang::QualType type,
-                                     LifetimeFactory lifetime_factory,
-                                     bool transitive) {
+const Object* ObjectRepository::CreateObjectsRecursively(
+    const ObjectLifetimes& object_lifetimes, PointsToMap& points_to_map) {
+  const auto* obj =
+      CreateObject(object_lifetimes.GetLifetime(), object_lifetimes.Type());
+  CreateObjectsWithLifetimes(obj, object_lifetimes.GetValueLifetimes(),
+                             /*transitive=*/true, points_to_map);
+  return obj;
+}
+
+void ObjectRepository::CreateObjectsWithLifetimes(
+    const Object* root_object, const ValueLifetimes& value_lifetimes,
+    bool transitive, PointsToMap& points_to_map) {
   class Visitor : public LifetimeVisitor {
    public:
-    Visitor(ObjectRepository& object_repository, bool create_transitive_objects)
+    Visitor(ObjectRepository& object_repository, PointsToMap& points_to_map,
+            bool create_transitive_objects)
         : object_repository_(object_repository),
+          points_to_map_(points_to_map),
           create_transitive_objects_(create_transitive_objects) {}
 
     const Object* GetFieldObject(const ObjectSet& objects,
@@ -810,25 +825,33 @@
         child_pointee = iter->second;
       }
 
-      object_repository_.initial_points_to_map_.SetPointerPointsToSet(
-          objects, {child_pointee});
+      points_to_map_.SetPointerPointsToSet(objects, {child_pointee});
       return ObjectSet{child_pointee};
     }
 
    private:
     ObjectRepository& object_repository_;
+    PointsToMap& points_to_map_;
     bool create_transitive_objects_;
     // Inside of a given VarDecl, we re-use the same Object for all the
     // sub-objects with the same type and lifetimes. This avoids infinite loops
     // in the case of structs like lists.
     llvm::DenseMap<ObjectLifetimes, const Object*> object_cache_;
   };
-  Visitor visitor(*this, transitive);
+  Visitor visitor(*this, points_to_map, transitive);
   initial_object_lifetimes_[root_object] =
-      ObjectLifetimes(root_object->GetLifetime(),
-                      ValueLifetimes::Create(type, lifetime_factory).get());
-  VisitLifetimes({root_object}, type, initial_object_lifetimes_[root_object],
-                 visitor);
+      ObjectLifetimes(root_object->GetLifetime(), value_lifetimes);
+  VisitLifetimes({root_object}, value_lifetimes.Type(),
+                 initial_object_lifetimes_[root_object], visitor);
+}
+
+void ObjectRepository::CreateObjects(const Object* root_object,
+                                     clang::QualType type,
+                                     LifetimeFactory lifetime_factory,
+                                     bool transitive) {
+  CreateObjectsWithLifetimes(
+      root_object, ValueLifetimes::Create(type, lifetime_factory).get(),
+      transitive, initial_points_to_map_);
 }
 
 // Clones an object and its base classes and fields, if any.