Finish support for defaulted default constructors.

PiperOrigin-RevId: 458216601
diff --git a/lifetime_analysis/analyze.cc b/lifetime_analysis/analyze.cc
index 0e2411d..2ed33ea 100644
--- a/lifetime_analysis/analyze.cc
+++ b/lifetime_analysis/analyze.cc
@@ -664,21 +664,110 @@
   return false;
 }
 
+const CXXConstructorDecl* GetDefaultConstructor(const CXXRecordDecl* record) {
+  for (const CXXConstructorDecl* ctor : record->ctors()) {
+    if (ctor->isDefaultConstructor()) {
+      return ctor;
+    }
+  }
+  return nullptr;
+}
+
+llvm::Error TransferDefaultConstructor(
+    const clang::CXXConstructorDecl* default_ctor, Object this_object,
+    ObjectRepository& object_repository, PointsToMap& points_to_map,
+    const llvm::DenseMap<const clang::FunctionDecl*, FunctionLifetimesOrError>&
+        callee_lifetimes) {
+  assert(callee_lifetimes.count(default_ctor->getCanonicalDecl()));
+
+  const FunctionLifetimesOrError& ctor_lifetimes_or_error =
+      callee_lifetimes.lookup(default_ctor->getCanonicalDecl());
+  if (!std::holds_alternative<FunctionLifetimes>(ctor_lifetimes_or_error)) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        absl::StrCat("No lifetimes for constructor ",
+                     default_ctor->getNameAsString()));
+  }
+  const FunctionLifetimes& ctor_lifetimes =
+      std::get<FunctionLifetimes>(ctor_lifetimes_or_error);
+
+  std::vector<FunctionParameter> fn_params;
+  Object this_ptr =
+      Object::Create(Lifetime::CreateLocal(), default_ctor->getThisType());
+  points_to_map.SetPointerPointsToSet(this_ptr, {this_object});
+  fn_params.push_back(FunctionParameter{
+      this_ptr.Type(), ctor_lifetimes.GetThisLifetimes(), this_ptr});
+  TransferLifetimesForCall(
+      // Passing `nullptr` for `call` is OK here because it's only required if
+      // the return value contains lifetimes.
+      /*call=*/nullptr, fn_params,
+      ValueLifetimes::ForLifetimeLessType(default_ctor->getReturnType()),
+      object_repository, points_to_map, default_ctor->getASTContext());
+
+  return llvm::Error::success();
+}
+
+llvm::Error AnalyzeDefaultedDefaultConstructor(
+    const clang::CXXConstructorDecl* ctor,
+    const llvm::DenseMap<const clang::FunctionDecl*, FunctionLifetimesOrError>&
+        callee_lifetimes,
+    ObjectRepository& object_repository, PointsToMap& points_to_map) {
+  assert(ctor->isDefaulted() && ctor->isDefaultConstructor());
+
+  std::optional<Object> this_object_maybe = object_repository.GetThisObject();
+  if (!this_object_maybe.has_value()) {
+    llvm::report_fatal_error("didn't find `this` object for constructor");
+  }
+  Object this_object = *this_object_maybe;
+
+  const clang::CXXRecordDecl* record = ctor->getParent();
+  for (const CXXBaseSpecifier& base : record->bases()) {
+    if (const clang::CXXRecordDecl* base_record =
+            base.getType()->getAsCXXRecordDecl()) {
+      if (const clang::CXXConstructorDecl* base_ctor =
+              GetDefaultConstructor(base_record)) {
+        Object base_this_object =
+            object_repository.GetBaseClassObject(this_object, base.getType());
+        if (llvm::Error err = TransferDefaultConstructor(
+                base_ctor, base_this_object, object_repository, points_to_map,
+                callee_lifetimes)) {
+          return err;
+        }
+      }
+    }
+  }
+  for (const clang::FieldDecl* field : record->fields()) {
+    if (const clang::CXXRecordDecl* field_record =
+            field->getType()->getAsCXXRecordDecl()) {
+      if (const clang::CXXConstructorDecl* field_ctor =
+              GetDefaultConstructor(field_record)) {
+        Object field_this_object =
+            object_repository.GetFieldObject(this_object, field);
+        if (llvm::Error err = TransferDefaultConstructor(
+                field_ctor, field_this_object, object_repository, points_to_map,
+                callee_lifetimes)) {
+          return err;
+        }
+      }
+    }
+  }
+
+  return llvm::Error::success();
+}
+
 llvm::Error AnalyzeDefaultedFunction(
     const clang::FunctionDecl* func,
     const llvm::DenseMap<const clang::FunctionDecl*, FunctionLifetimesOrError>&
-    /*callee_lifetimes*/,
-    ObjectRepository& /*object_repository*/, PointsToMap& /*points_to_map*/) {
+        callee_lifetimes,
+    ObjectRepository& object_repository, PointsToMap& points_to_map) {
   assert(func->isDefaulted());
 
   // TODO(b/230693710): Add complete support for defaulted functions.
 
   if (const auto* ctor = clang::dyn_cast<clang::CXXConstructorDecl>(func)) {
     if (ctor->isDefaultConstructor()) {
-      const clang::CXXRecordDecl* record = ctor->getParent();
-      if (record->getNumBases() == 0 && !HasRecordTypeFields(record)) {
-        return llvm::Error::success();
-      }
+      return AnalyzeDefaultedDefaultConstructor(
+          ctor, callee_lifetimes, object_repository, points_to_map);
     }
   }
 
@@ -767,7 +856,25 @@
   func = func->getDefinition();
   assert(func != nullptr);
 
-  if (func->getBody()) {
+  // Unconditionally use our custom logic to analyze defaulted functions, even
+  // if they happen to have a body (because something caused Sema to create a
+  // body for them). We don't want the code path for defaulted functions to
+  // change based on whether a body happened to be created for them, and we
+  // want to make sure we always exercise our logic for defaulted functions in
+  // tests.
+  // TODO(b/230693710): We currently only support analyzing defaulted default
+  // constructors, so for other defaulted functions, we currently fall back to
+  // AnalyzeFunctionBody() (if they do have a body).
+  const auto* ctor = clang::dyn_cast<clang::CXXConstructorDecl>(func);
+  bool can_analyze_defaulted_func =
+      ctor != nullptr && ctor->isDefaultConstructor();
+  if (func->isDefaulted() && can_analyze_defaulted_func) {
+    if (llvm::Error err = AnalyzeDefaultedFunction(func, callee_lifetimes,
+                                                   analysis.object_repository,
+                                                   analysis.points_to_map)) {
+      return std::move(err);
+    }
+  } else if (func->getBody()) {
     std::string* cfg_dot = debug_info ? &(*debug_info)[func].cfg_dot : nullptr;
     if (llvm::Error err = AnalyzeFunctionBody(
             func, callee_lifetimes, diag_reporter, analysis.object_repository,
@@ -775,21 +882,8 @@
       return std::move(err);
     }
   } else {
-    if (!func->isDefaulted()) {
-      return llvm::createStringError(llvm::inconvertibleErrorCode(),
-                                     "Declaration-only!");
-    }
-
-    // TODO(b/230693710): Do this unconditionally for defaulted functions, even
-    // if they happen to have a body (because something caused Sema to create a
-    // body for them). We can't do this yet because we don't have full support
-    // for defaulted functions yet, so we would break tests where we happen to
-    // have a body for the defaulted function today.
-    if (llvm::Error err = AnalyzeDefaultedFunction(func, callee_lifetimes,
-                                                   analysis.object_repository,
-                                                   analysis.points_to_map)) {
-      return std::move(err);
-    }
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "Declaration-only!");
   }
 
   if (debug_info) {
@@ -935,10 +1029,27 @@
 
   if (const auto* ctor = clang::dyn_cast<clang::CXXConstructorDecl>(func)) {
     if (ctor->isDefaultConstructor()) {
+      llvm::DenseSet<const clang::FunctionDecl*> callees;
       const clang::CXXRecordDecl* record = ctor->getParent();
-      if (record->getNumBases() == 0 && !HasRecordTypeFields(record)) {
-        return llvm::DenseSet<const clang::FunctionDecl*>();
+      for (const CXXBaseSpecifier& base : record->bases()) {
+        if (const clang::CXXRecordDecl* base_record =
+                base.getType()->getAsCXXRecordDecl()) {
+          if (const clang::CXXConstructorDecl* base_ctor =
+                  GetDefaultConstructor(base_record)) {
+            callees.insert(base_ctor);
+          }
+        }
       }
+      for (const clang::FieldDecl* field : record->fields()) {
+        if (const clang::CXXRecordDecl* field_record =
+                field->getType()->getAsCXXRecordDecl()) {
+          if (const clang::CXXConstructorDecl* field_ctor =
+                  GetDefaultConstructor(field_record)) {
+            callees.insert(field_ctor);
+          }
+        }
+      }
+      return callees;
     }
   }