Remove TypeLifetimes.

This still might need modifications for supporting annotate_type (in particular, potentially changing Type to TypeLoc).

PiperOrigin-RevId: 436992434
diff --git a/lifetime_annotations/function_lifetimes.cc b/lifetime_annotations/function_lifetimes.cc
index 85e00f3..0f8d0cc 100644
--- a/lifetime_annotations/function_lifetimes.cc
+++ b/lifetime_annotations/function_lifetimes.cc
@@ -11,11 +11,14 @@
 #include "lifetime_annotations/lifetime.h"
 #include "lifetime_annotations/type_lifetimes.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/DeclCXX.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/AST/Type.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/Basic/LLVM.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/SmallVector.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Error.h"
 
 namespace devtools_rust {
-namespace {
 
+namespace {
 // Track a bijective mapping between 2 sets of Lifetimes.
 class LifetimeBijection {
  public:
@@ -40,50 +43,114 @@
 
 }  // namespace
 
-bool FunctionLifetimes::ContainsLifetimes() {
-  for (const auto& param : param_lifetimes) {
-    if (!param.empty()) {
-      return true;
+llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateForDecl(
+    const clang::FunctionDecl* func,
+    const FunctionLifetimeFactory& lifetime_factory) {
+  clang::QualType this_type;
+  if (auto method = clang::dyn_cast<clang::CXXMethodDecl>(func);
+      method && !method->isStatic()) {
+    this_type = method->getThisType();
+  }
+  return Create(func->getType()->getAs<clang::FunctionProtoType>(), this_type,
+                lifetime_factory);
+}
+
+bool FunctionLifetimes::IsValidForDecl(const clang::FunctionDecl* function) {
+  // TODO(veluca): also validate the types of the arguments, and/or the type of
+  // the function itself.
+  if (auto method = clang::dyn_cast<clang::CXXMethodDecl>(function);
+      method && !method->isStatic()) {
+    if (!this_lifetimes_.has_value()) return false;
+  }
+  return param_lifetimes_.size() == function->param_size();
+}
+
+llvm::Expected<FunctionLifetimes> FunctionLifetimes::Create(
+    const clang::FunctionProtoType* type, const clang::QualType this_type,
+    const FunctionLifetimeFactory& lifetime_factory) {
+  FunctionLifetimes ret;
+
+  if (!this_type.isNull()) {
+    ValueLifetimes tmp;
+    if (llvm::Error err =
+            ValueLifetimes::Create(this_type, [&](clang::QualType type,
+                                                  llvm::StringRef param) {
+              return lifetime_factory.CreateParamLifetime(type, param);
+            }).moveInto(tmp)) {
+      return std::move(err);
     }
+    ret.this_lifetimes_ = std::move(tmp);
   }
 
-  return !return_lifetimes.empty() || !this_lifetimes.empty();
+  ret.param_lifetimes_.reserve(type->getNumParams());
+  for (size_t i = 0; i < type->getNumParams(); i++) {
+    ValueLifetimes tmp;
+    if (llvm::Error err = ValueLifetimes::Create(
+                              type->getParamType(i),
+                              [&](clang::QualType type, llvm::StringRef param) {
+                                return lifetime_factory.CreateParamLifetime(
+                                    type, param);
+                              })
+                              .moveInto(tmp)) {
+      return std::move(err);
+    }
+    ret.param_lifetimes_.push_back(std::move(tmp));
+  }
+
+  if (llvm::Error err = ValueLifetimes::Create(
+                            type->getReturnType(),
+                            [&](clang::QualType type, llvm::StringRef param) {
+                              return lifetime_factory.CreateReturnLifetime(
+                                  type, param, ret.param_lifetimes_,
+                                  ret.this_lifetimes_);
+                            })
+                            .moveInto(ret.return_lifetimes_)) {
+    return std::move(err);
+  }
+
+  return ret;
+}
+
+void FunctionLifetimes::Traverse(
+    std::function<void(Lifetime&, Variance)> visitor) {
+  for (auto& param : param_lifetimes_) {
+    param.Traverse(visitor);
+  }
+  return_lifetimes_.Traverse(visitor);
+  if (this_lifetimes_.has_value()) {
+    this_lifetimes_->Traverse(visitor);
+  }
+}
+
+void FunctionLifetimes::Traverse(
+    std::function<void(const Lifetime&, Variance)> visitor) const {
+  const_cast<FunctionLifetimes*>(this)->Traverse(
+      [visitor](Lifetime& l, Variance v) { visitor(l, v); });
 }
 
 bool FunctionLifetimes::IsIsomorphic(const FunctionLifetimes& other) const {
   // We expect this function to only be called for 2 FunctionLifetime objects
   // that are for the same function, thus the number of parameters, and the
   // number of Lifetimes for each type, should always match.
-  assert(param_lifetimes.size() == other.param_lifetimes.size());
-  assert(this_lifetimes.size() == other.this_lifetimes.size());
-  assert(return_lifetimes.size() == other.return_lifetimes.size());
-  for (size_t i = 0; i < param_lifetimes.size(); ++i) {
-    assert(param_lifetimes[i].size() == other.param_lifetimes[i].size());
-  }
+  assert(param_lifetimes_.size() == other.param_lifetimes_.size());
+  assert(this_lifetimes_.has_value() == other.this_lifetimes_.has_value());
 
   // Map of equivalent lifetimes between `*this` and `other`.
   LifetimeBijection bijection;
 
-  auto compare_type_lifetimes = [&bijection](const TypeLifetimes& a,
-                                             const TypeLifetimes& b) {
-    assert(a.size() == b.size());
-    for (size_t i = 0; i < a.size(); ++i) {
-      if (!bijection.Add(a[i], b[i])) {
-        return false;
-      }
-    }
-    return true;
-  };
+  llvm::SmallVector<Lifetime> my_lifetimes;
+  Traverse(
+      [&my_lifetimes](Lifetime l, Variance) { my_lifetimes.push_back(l); });
+  llvm::SmallVector<Lifetime> other_lifetimes;
+  other.Traverse([&other_lifetimes](Lifetime l, Variance) {
+    other_lifetimes.push_back(l);
+  });
 
-  if (!compare_type_lifetimes(return_lifetimes, other.return_lifetimes)) {
-    return false;
-  }
-  if (!compare_type_lifetimes(this_lifetimes, other.this_lifetimes)) {
-    return false;
-  }
-  for (size_t i = 0; i < param_lifetimes.size(); ++i) {
-    if (!compare_type_lifetimes(param_lifetimes[i], other.param_lifetimes[i]))
+  assert(my_lifetimes.size() == other_lifetimes.size());
+  for (size_t i = 0; i < my_lifetimes.size(); ++i) {
+    if (!bijection.Add(my_lifetimes[i], other_lifetimes[i])) {
       return false;
+    }
   }
   return true;
 }
@@ -91,58 +158,29 @@
 std::string FunctionLifetimes::DebugString(LifetimeFormatter formatter) const {
   std::vector<std::string> formatted_param_lifetimes;
 
-  for (const auto& param : param_lifetimes) {
-    formatted_param_lifetimes.push_back(
-        ::devtools_rust::DebugString(param, formatter));
+  for (const auto& param : param_lifetimes_) {
+    formatted_param_lifetimes.push_back(param.DebugString(formatter));
   }
 
   std::string result;
-  if (!this_lifetimes.empty()) {
-    result = absl::StrCat(
-        ::devtools_rust::DebugString(this_lifetimes, formatter), ":");
+  if (this_lifetimes_.has_value()) {
+    result = absl::StrCat(this_lifetimes_->DebugString(formatter), ":");
   }
   if (!result.empty() && !formatted_param_lifetimes.empty()) {
     absl::StrAppend(&result, " ");
   }
   absl::StrAppend(&result, absl::StrJoin(formatted_param_lifetimes, ", "));
 
-  if (!return_lifetimes.empty()) {
+  if (return_lifetimes_.HasLifetimes()) {
     if (!result.empty()) {
       absl::StrAppend(&result, " ");
     }
-    absl::StrAppend(&result, "-> ",
-                    ::devtools_rust::DebugString(return_lifetimes, formatter));
+    absl::StrAppend(&result, "-> ", return_lifetimes_.DebugString(formatter));
   }
 
   return result;
 }
 
-bool FunctionLifetimes::Validate(const clang::FunctionDecl* func) const {
-  if (auto method = clang::dyn_cast<clang::CXXMethodDecl>(func)) {
-    if (CreateLifetimesForType(method->getThisType(), Lifetime::CreateLocal)
-            .size() != this_lifetimes.size()) {
-      return false;
-    }
-  } else if (!this_lifetimes.empty()) {
-    return false;
-  }
-  if (CreateLifetimesForType(func->getReturnType(), Lifetime::CreateLocal)
-          .size() != return_lifetimes.size()) {
-    return false;
-  }
-  if (param_lifetimes.size() != func->getNumParams()) {
-    return false;
-  }
-  for (size_t i = 0; i < param_lifetimes.size(); i++) {
-    if (CreateLifetimesForType(func->getParamDecl(i)->getType(),
-                               Lifetime::CreateLocal)
-            .size() != param_lifetimes[i].size()) {
-      return false;
-    }
-  }
-  return true;
-}
-
 std::ostream& operator<<(std::ostream& os,
                          const FunctionLifetimes& func_lifetimes) {
   return os << func_lifetimes.DebugString();
diff --git a/lifetime_annotations/function_lifetimes.h b/lifetime_annotations/function_lifetimes.h
index 0c80c0c..375058a 100644
--- a/lifetime_annotations/function_lifetimes.h
+++ b/lifetime_annotations/function_lifetimes.h
@@ -11,26 +11,76 @@
 
 #include "lifetime_annotations/type_lifetimes.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/Decl.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/AST/Type.h"
 #include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/SmallVector.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/StringRef.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Error.h"
 
 namespace devtools_rust {
 
+// Interface used to create lifetimes in FunctionLifetimes::CreateForDecl.
+// CreateReturnLifetime will be called with the ValueLifetimes that were created
+// through calls to CreateParamLifetimes.
+class FunctionLifetimeFactory {
+ public:
+  virtual ~FunctionLifetimeFactory() {}
+  virtual llvm::Expected<Lifetime> CreateParamLifetime(
+      clang::QualType type, llvm::StringRef ref) const = 0;
+  virtual llvm::Expected<Lifetime> CreateReturnLifetime(
+      clang::QualType type, llvm::StringRef ref,
+      const llvm::SmallVector<ValueLifetimes>& param_lifetimes,
+      const std::optional<ValueLifetimes>& this_lifetimes) const = 0;
+};
+
+// Implementation of FunctionLifetimeFactory that just uses a single callback.
+class FunctionLifetimeFactorySingleCallback : public FunctionLifetimeFactory {
+ public:
+  FunctionLifetimeFactorySingleCallback(LifetimeFactory factory)
+      : factory_(std::move(factory)) {}
+  llvm::Expected<Lifetime> CreateParamLifetime(
+      clang::QualType type, llvm::StringRef ref) const override {
+    return factory_(type, ref);
+  }
+  llvm::Expected<Lifetime> CreateReturnLifetime(
+      clang::QualType type, llvm::StringRef ref,
+      const llvm::SmallVector<ValueLifetimes>& /*param_lifetimes*/,
+      const std::optional<ValueLifetimes>& /*this_lifetimes*/) const override {
+    return factory_(type, ref);
+  }
+
+ private:
+  LifetimeFactory factory_;
+};
+
 // Lifetimes for the signature of a function.
-struct FunctionLifetimes {
-  // Lifetimes for the parameters.
+class FunctionLifetimes {
+ public:
+  // Returns lifetimes for the `i`-th parameter.
   // These are the same number and order as FunctionDecl::parameters().
-  llvm::SmallVector<TypeLifetimes> param_lifetimes;
+  const ValueLifetimes& GetParamLifetimes(size_t i) const {
+    return param_lifetimes_[i];
+  }
 
   // Lifetimes for the return type.
-  TypeLifetimes return_lifetimes;
+  const ValueLifetimes& GetReturnLifetimes() const { return return_lifetimes_; }
 
   // Lifetimes for the `this` parameter for non-static member functions.
-  TypeLifetimes this_lifetimes;
+  const ValueLifetimes& GetThisLifetimes() const {
+    assert(this_lifetimes_.has_value());
+    return *this_lifetimes_;
+  }
 
-  // Returns whether this object contains any lifetimes.
-  // Note that this returns false if `param_lifetimes` is non-empty but only
-  // contains empty TypeLifetimes.
-  bool ContainsLifetimes();
+  // Creates lifetimes for a function with a given decl.
+  // Only fails if lifetime_factory fails.
+  // Lifetimes will be created first for the object parameter, if any, then for
+  // parameters in increasing order, and finally for the return type.
+  static llvm::Expected<FunctionLifetimes> CreateForDecl(
+      const clang::FunctionDecl* function,
+      const FunctionLifetimeFactory& lifetime_factory);
+
+  // Checks if this FunctionLifetimes represents valid lifetimes for the given
+  // Decl.
+  bool IsValidForDecl(const clang::FunctionDecl* function);
 
   // Returns if the two FunctionLifetimes have the same structures, without
   // requiring them to have the same exact Lifetimes. They have the same
@@ -38,16 +88,26 @@
   // in the same positions.
   bool IsIsomorphic(const FunctionLifetimes& other) const;
 
-  // Returns true if this FunctionLifetimes object is valid for the given
-  // function.
-  bool Validate(const clang::FunctionDecl* func) const;
-
   // Returns a human-readable representation of `func_lifetimes`. Formats
   // lifetimes using `formatter`, or Lifetime::DebugString() if `formatter` is
   // null.
   std::string DebugString(LifetimeFormatter formatter = [](Lifetime l) {
     return l.DebugString();
   }) const;
+
+  // Traverses all the lifetimes in the function signature, recursively. The
+  // visit is done in post-order on the lifetime tree of this type.
+  void Traverse(std::function<void(Lifetime&, Variance)> visitor);
+  void Traverse(std::function<void(const Lifetime&, Variance)> visitor) const;
+
+ private:
+  llvm::SmallVector<ValueLifetimes> param_lifetimes_;
+  ValueLifetimes return_lifetimes_;
+  std::optional<ValueLifetimes> this_lifetimes_;
+
+  static llvm::Expected<FunctionLifetimes> Create(
+      const clang::FunctionProtoType* type, const clang::QualType this_type,
+      const FunctionLifetimeFactory& lifetime_factory);
 };
 
 std::ostream& operator<<(std::ostream& os,
@@ -55,7 +115,8 @@
 
 // An error that occurred while analyzing a function.
 struct FunctionAnalysisError {
-  explicit FunctionAnalysisError(llvm::StringRef message) : message(message) {}
+  explicit FunctionAnalysisError(llvm::StringRef message = "")
+      : message(message) {}
 
   explicit FunctionAnalysisError(const llvm::Error& err) {
     ::llvm::raw_string_ostream stream(message);
@@ -72,7 +133,7 @@
 //   the error out of the llvm::Expected
 // - llvm::Expected asserts in the destructor if we didn't check for an error
 using FunctionLifetimesOrError =
-    std::variant<FunctionLifetimes, FunctionAnalysisError>;
+    std::variant<FunctionAnalysisError, FunctionLifetimes>;
 
 }  // namespace devtools_rust
 
diff --git a/lifetime_annotations/function_lifetimes_test.cc b/lifetime_annotations/function_lifetimes_test.cc
deleted file mode 100644
index da3a7d2..0000000
--- a/lifetime_annotations/function_lifetimes_test.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Part of the Crubit project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#include "lifetime_annotations/function_lifetimes.h"
-
-#include "testing/base/public/gmock.h"
-#include "testing/base/public/gunit.h"
-
-namespace devtools_rust {
-namespace {
-
-TEST(FunctionLifetimesTest, ContainsLifetimes) {
-  EXPECT_FALSE(FunctionLifetimes().ContainsLifetimes());
-
-  {
-    FunctionLifetimes lifetimes;
-    lifetimes.param_lifetimes.push_back({});
-    EXPECT_FALSE(lifetimes.ContainsLifetimes());
-  }
-
-  {
-    FunctionLifetimes lifetimes;
-    lifetimes.param_lifetimes.push_back({Lifetime::Static()});
-    EXPECT_TRUE(lifetimes.ContainsLifetimes());
-  }
-
-  {
-    FunctionLifetimes lifetimes;
-    lifetimes.this_lifetimes = {Lifetime::Static()};
-    EXPECT_TRUE(lifetimes.ContainsLifetimes());
-  }
-
-  {
-    FunctionLifetimes lifetimes;
-    lifetimes.return_lifetimes = {Lifetime::Static()};
-    EXPECT_TRUE(lifetimes.ContainsLifetimes());
-  }
-}
-
-}  // namespace
-}  // namespace devtools_rust
diff --git a/lifetime_annotations/lifetime_annotations.cc b/lifetime_annotations/lifetime_annotations.cc
index cf5dd6f..f198ce0 100644
--- a/lifetime_annotations/lifetime_annotations.cc
+++ b/lifetime_annotations/lifetime_annotations.cc
@@ -12,112 +12,80 @@
 
 #include "third_party/absl/strings/str_cat.h"
 #include "lifetime_annotations/function_lifetimes.h"
+#include "lifetime_annotations/lifetime_symbol_table.h"
 #include "lifetime_annotations/pointee_type.h"
 #include "lifetime_annotations/type_lifetimes.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/APValue.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/ASTContext.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/Attr.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/Attrs.inc"
+#include "third_party/llvm/llvm-project/clang/include/clang/AST/Decl.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/DeclCXX.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/PrettyPrinter.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/AST/Type.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/Basic/LangOptions.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/Basic/SourceLocation.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/Lex/Pragma.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/Lex/Preprocessor.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/DenseSet.h"
 #include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/SmallVector.h"
 #include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/StringRef.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Error.h"
 
 namespace devtools_rust {
-
 namespace {
 
-llvm::Expected<llvm::SmallVector<Lifetime>> GetAnnotatedOrElidedLifetimes(
-    llvm::ArrayRef<const clang::Attr*> /*attrs*/, size_t num_expected,
-    LifetimeSymbolTable& /*symbol_table*/,
-    const std::function<llvm::Expected<Lifetime>()>& elided_lifetime_factory,
-    const clang::ASTContext& /*ast_context*/) {
-  assert(num_expected > 0);
+llvm::Expected<FunctionLifetimes> ParseLifetimeAnnotations(
+    const clang::FunctionDecl* func, LifetimeSymbolTable& symbol_table,
+    const std::string& lifetimes_str) {
+  clang::LangOptions lang_opts;
+  clang::Lexer lexer(clang::SourceLocation(), lang_opts, lifetimes_str.data(),
+                     lifetimes_str.data(),
+                     lifetimes_str.data() + lifetimes_str.size());
 
-  llvm::SmallVector<Lifetime> lifetimes;
-  lifetimes.reserve(num_expected);
+  const char* end = lifetimes_str.data() + lifetimes_str.size();
 
-  // TODO(mboehme): Extract lifetime annotations from `attrs` if present.
-
-  // No lifetime annotations: Use elided lifetimes.
-  for (size_t i = 0; i < num_expected; ++i) {
-    llvm::Expected<Lifetime> maybe_lifetime = elided_lifetime_factory();
-    if (maybe_lifetime) {
-      lifetimes.push_back(maybe_lifetime.get());
-    } else {
-      return maybe_lifetime.takeError();
+  auto tok = [&lexer, &lifetimes_str, end]() -> llvm::StringRef {
+    clang::Token token;
+    if (lexer.getBufferLocation() != end) {
+      lexer.LexFromRawLexer(token);
+      return llvm::StringRef(
+          lifetimes_str.data() + token.getLocation().getRawEncoding(),
+          token.getLength());
     }
-  }
-  return lifetimes;
-}
+    return "";
+  };
 
-llvm::Error AddLifetimeAnnotationsForType(
-    clang::QualType type, LifetimeSymbolTable& symbol_table,
-    const std::function<llvm::Expected<Lifetime>()>& elided_lifetime_factory,
-    const clang::ASTContext& ast_context, TypeLifetimes& lifetimes) {
-  assert(!type.isNull());
-  assert(elided_lifetime_factory);
-
-  llvm::SmallVector<const clang::Attr*> attrs;
-
-  size_t num_lifetime_params = GetLifetimeParameters(type).size();
-  if (num_lifetime_params > 0) {
-    llvm::Expected<llvm::SmallVector<Lifetime>> maybe_lifetime_args =
-        GetAnnotatedOrElidedLifetimes(attrs, num_lifetime_params, symbol_table,
-                                      elided_lifetime_factory, ast_context);
-    if (maybe_lifetime_args) {
-      lifetimes.append(maybe_lifetime_args.get());
-    } else {
-      return maybe_lifetime_args.takeError();
+  // TODO(veluca): this is too permissive.
+  auto next_lifetime = [&]() {
+    llvm::StringRef next = tok();
+    while (next == "(" || next == ")" || next == "," || next == "->" ||
+           next == ":") {
+      next = tok();
     }
+    return next;
+  };
+
+  FunctionLifetimeFactorySingleCallback factory(
+      [&symbol_table, &next_lifetime](
+          clang::QualType, llvm::StringRef) -> llvm::Expected<Lifetime> {
+        llvm::StringRef next = next_lifetime();
+        if (next.empty()) {
+          return llvm::createStringError(
+              llvm::inconvertibleErrorCode(),
+              "Invalid lifetime annotation: too few lifetimes");
+        }
+        return symbol_table.LookupNameAndMaybeDeclare(next);
+      });
+
+  auto ret = FunctionLifetimes::CreateForDecl(func, factory);
+
+  if (!next_lifetime().empty()) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "Invalid lifetime annotation: too many lifetimes");
   }
-
-  const llvm::ArrayRef<clang::TemplateArgument> template_args =
-      GetTemplateArgs(type);
-  if (!template_args.empty()) {
-    for (const clang::TemplateArgument& template_arg : template_args) {
-      if (llvm::Error err = AddLifetimeAnnotationsForType(
-              template_arg.getAsType(), symbol_table, elided_lifetime_factory,
-              ast_context, lifetimes)) {
-        return err;
-      }
-    }
-    return llvm::Error::success();
-  }
-
-  if (clang::QualType pointee_type = PointeeType(type);
-      !pointee_type.isNull()) {
-    if (llvm::Error err = AddLifetimeAnnotationsForType(
-            pointee_type, symbol_table, elided_lifetime_factory, ast_context,
-            lifetimes)) {
-      return err;
-    }
-
-    llvm::Expected<llvm::SmallVector<Lifetime>> maybe_pointee_lifetime =
-        GetAnnotatedOrElidedLifetimes(attrs, 1, symbol_table,
-                                      elided_lifetime_factory, ast_context);
-    if (maybe_pointee_lifetime) {
-      lifetimes.append(maybe_pointee_lifetime.get());
-    } else {
-      return maybe_pointee_lifetime.takeError();
-    }
-  }
-
-  return llvm::Error::success();
-}
-
-llvm::Expected<llvm::SmallVector<Lifetime>> GetThisLifetimes(
-    const clang::CXXMethodDecl* method, LifetimeSymbolTable& symbol_table,
-    const std::function<llvm::Expected<Lifetime>()>& elided_lifetime_factory) {
-  llvm::ArrayRef<const clang::Attr*> attrs;
-  if (method->hasAttrs()) {
-    attrs = method->getAttrs();
-  }
-  return GetAnnotatedOrElidedLifetimes(
-      attrs, 1, symbol_table, elided_lifetime_factory, method->getASTContext());
+  return ret;
 }
 
 // Parse a "(a, b): (a, b), (), a -> b"-style annotation into a
@@ -147,88 +115,12 @@
               .moveInto(lifetimes)) {
     return error(toString(std::move(err)));
   }
-  std::string lifetimes_str = lifetimes.str();
-  clang::LangOptions lang_opts;
-  clang::Lexer lexer(attr->getLoc(), lang_opts, lifetimes_str.data(),
-                     lifetimes_str.data(),
-                     lifetimes_str.data() + lifetimes_str.size());
-
-  const char* end = lifetimes_str.data() + lifetimes_str.size();
-
-  auto tok = [&lexer, &lifetimes_str, end, attr]() -> llvm::StringRef {
-    clang::Token token;
-    if (lexer.getBufferLocation() != end) {
-      lexer.LexFromRawLexer(token);
-      return llvm::StringRef(lifetimes_str.data() +
-                                 token.getLocation().getRawEncoding() -
-                                 attr->getLoc().getRawEncoding(),
-                             token.getLength());
-    }
-    return "";
-  };
-
-  llvm::SmallVector<TypeLifetimes> fn_lifetimes;
-  bool has_this_lifetimes = false;
-  bool has_return_lifetimes = false;
-
-  for (llvm::StringRef token; !(token = tok()).empty();) {
-    if (token == ",") {
-      continue;
-    }
-    if (has_return_lifetimes) {
-      return error();
-    }
-    if (token == ":") {
-      if (has_this_lifetimes || fn_lifetimes.size() != 1) {
-        return error();
-      }
-      has_this_lifetimes = true;
-      continue;
-    }
-    // Skip the -> and parse return lifetimes. No more lifetimes should be
-    // parsed afterwards.
-    if (token == "->") {
-      has_return_lifetimes = true;
-      token = tok();
-    }
-    fn_lifetimes.emplace_back();
-    if (token == "(") {
-      for (; (token = tok()) != ")" && !token.empty();) {
-        if (token == ",") continue;
-        fn_lifetimes.back().push_back(
-            symbol_table.LookupNameAndMaybeDeclare(token));
-      }
-    } else {
-      fn_lifetimes.back().push_back(
-          symbol_table.LookupNameAndMaybeDeclare(token));
-    }
-  }
-
-  FunctionLifetimes function_lifetimes;
-  size_t param_start = 0;
-  if (has_this_lifetimes) {
-    function_lifetimes.this_lifetimes = fn_lifetimes[0];
-    param_start = 1;
-  }
-  size_t param_end = fn_lifetimes.size();
-  if (has_return_lifetimes) {
-    function_lifetimes.return_lifetimes = fn_lifetimes.back();
-    param_end -= 1;
-  }
-  function_lifetimes.param_lifetimes.assign(fn_lifetimes.begin() + param_start,
-                                            fn_lifetimes.begin() + param_end);
-
-  if (function_lifetimes.Validate(func)) {
-    return function_lifetimes;
-  }
-  return error();
+  return ParseLifetimeAnnotations(func, symbol_table, lifetimes.str());
 }
 
 llvm::Expected<FunctionLifetimes> GetLifetimeAnnotationsInternal(
     const clang::FunctionDecl* func, LifetimeSymbolTable& symbol_table,
     bool elision_enabled) {
-  FunctionLifetimes result;
-
   const clang::AnnotateAttr* lifetime_annotation = nullptr;
   for (const clang::Attr* attr : func->attrs()) {
     if (auto annotate = clang::dyn_cast<clang::AnnotateAttr>(attr)) {
@@ -248,89 +140,77 @@
     return ParseLifetimeAnnotations(func, symbol_table, lifetime_annotation);
   }
 
-  std::function<llvm::Expected<Lifetime>()> elided_lifetime_factory;
-  if (elision_enabled) {
-    elided_lifetime_factory = [&symbol_table]() -> llvm::Expected<Lifetime> {
+  class Factory : public FunctionLifetimeFactory {
+   public:
+    Factory(bool elision_enabled, const clang::FunctionDecl* func,
+            LifetimeSymbolTable& symbol_table)
+        : elision_enabled(elision_enabled),
+          func(func),
+          symbol_table(symbol_table) {}
+
+   private:
+    llvm::Expected<Lifetime> CreateParamLifetime(
+        clang::QualType, llvm::StringRef) const override {
+      // TODO(mboehme): parse lifetime annotations from `type` if present.
+      if (!elision_enabled) {
+        return llvm::createStringError(
+            llvm::inconvertibleErrorCode(),
+            absl::StrCat("Lifetime elision not enabled for '",
+                         func->getNameAsString(), "'"));
+      }
       Lifetime lifetime = Lifetime::CreateVariable();
       symbol_table.LookupLifetimeAndMaybeDeclare(lifetime);
       return lifetime;
-    };
-  } else {
-    elided_lifetime_factory = [func]() -> llvm::Expected<Lifetime> {
-      return llvm::createStringError(
-          llvm::inconvertibleErrorCode(),
-          absl::StrCat("Lifetime elision not enabled for '",
-                       func->getNameAsString(), "'"));
-    };
-  }
-
-  if (const auto* method = clang::dyn_cast<clang::CXXMethodDecl>(func)) {
-    if (llvm::Error err =
-            GetThisLifetimes(method, symbol_table, elided_lifetime_factory)
-                .moveInto(result.this_lifetimes)) {
-      return std::move(err);
-    }
-  }
-
-  llvm::SmallVector<Lifetime> all_input_lifetimes;
-  result.param_lifetimes.resize(func->getNumParams());
-  for (unsigned i = 0; i < func->getNumParams(); i++) {
-    const clang::ParmVarDecl* param = func->getParamDecl(i);
-    if (llvm::Error err = AddLifetimeAnnotationsForType(
-            param->getType(), symbol_table, elided_lifetime_factory,
-            func->getASTContext(), result.param_lifetimes[i])) {
-      return std::move(err);
     }
 
-    all_input_lifetimes.append(result.param_lifetimes[i]);
-  }
+    llvm::Expected<Lifetime> CreateReturnLifetime(
+        clang::QualType, llvm::StringRef,
+        const llvm::SmallVector<ValueLifetimes>& param_lifetimes,
+        const std::optional<ValueLifetimes>& this_lifetimes) const override {
+      // TODO(mboehme): parse lifetime annotations from `type` if present.
+      if (!elision_enabled) {
+        return llvm::createStringError(
+            llvm::inconvertibleErrorCode(),
+            absl::StrCat("Lifetime elision not enabled for '",
+                         func->getNameAsString(), "'"));
+      }
 
-  std::function<llvm::Expected<Lifetime>()> elided_return_lifetime_factory;
-  if (!elision_enabled) {
-    elided_return_lifetime_factory = [func]() -> llvm::Expected<Lifetime> {
-      return llvm::createStringError(
-          llvm::inconvertibleErrorCode(),
-          absl::StrCat("Lifetime elision not enabled for '",
-                       func->getNameAsString(), "'"));
-    };
-  } else if (!result.this_lifetimes.empty()) {
-    // If we have an implicit `this` parameter, its lifetime is assigned to all
-    // output lifetimes.
-    elided_return_lifetime_factory =
-        [this_lifetime =
-             result.this_lifetimes.back()]() -> llvm::Expected<Lifetime> {
-      return this_lifetime;
-    };
-  } else if (all_input_lifetimes.size() == 1) {
-    // If we have a single input lifetime, its lifetime is assigned to all
-    // output lifetimes.
-    // Otherwise, elided_return_lifetime_factory remains empty, and we get an
-    // error if there are any elided lifetimes in the return type.
-    elided_return_lifetime_factory =
-        [return_lifetime =
-             all_input_lifetimes[0]]() -> llvm::Expected<Lifetime> {
-      return return_lifetime;
-    };
-  } else {
-    elided_return_lifetime_factory = [func]() -> llvm::Expected<Lifetime> {
-      return llvm::createStringError(
-          llvm::inconvertibleErrorCode(),
-          absl::StrCat(
-              "Cannot elide output lifetimes for '", func->getNameAsString(),
-              "' because it is a non-member function that does not have "
-              "exactly one input lifetime"));
-    };
-  }
+      // If we have an implicit `this` parameter, its lifetime is assigned to
+      // all lifetimes in the return type.
+      if (this_lifetimes.has_value()) {
+        return this_lifetimes->GetPointeeLifetimes().GetLifetime();
+      }
 
-  if (llvm::Error err = AddLifetimeAnnotationsForType(
-          func->getReturnType(), symbol_table, elided_return_lifetime_factory,
-          func->getASTContext(), result.return_lifetimes)) {
-    return std::move(err);
-  }
+      llvm::DenseSet<Lifetime> all_input_lifetimes;
+      for (const ValueLifetimes& v : param_lifetimes) {
+        v.Traverse([&all_input_lifetimes](Lifetime l, Variance) {
+          all_input_lifetimes.insert(l);
+        });
+      }
 
-  return result;
+      if (all_input_lifetimes.size() == 1) {
+        // If we have a single input lifetime, its lifetime is assigned to all
+        // output lifetimes.
+        return *all_input_lifetimes.begin();
+      } else {
+        // Otherwise, we don't know how to elide the output lifetime.
+        return llvm::createStringError(
+            llvm::inconvertibleErrorCode(),
+            absl::StrCat(
+                "Cannot elide output lifetimes for '", func->getNameAsString(),
+                "' because it is a non-member function that does not have "
+                "exactly one input lifetime"));
+      }
+    }
+
+    bool elision_enabled;
+    const clang::FunctionDecl* func;
+    LifetimeSymbolTable& symbol_table;
+  };
+
+  Factory factory(elision_enabled, func, symbol_table);
+  return FunctionLifetimes::CreateForDecl(func, factory);
 }
-
 }  // namespace
 
 llvm::Expected<FunctionLifetimes> GetLifetimeAnnotations(
@@ -355,6 +235,16 @@
   return GetLifetimeAnnotationsInternal(func, *symbol_table, elision_enabled);
 }
 
+llvm::Expected<FunctionLifetimes> ParseLifetimeAnnotations(
+    const clang::FunctionDecl* func, const std::string& lifetimes_str,
+    LifetimeSymbolTable* symbol_table) {
+  LifetimeSymbolTable throw_away_symbol_table;
+  if (!symbol_table) {
+    symbol_table = &throw_away_symbol_table;
+  }
+  return ParseLifetimeAnnotations(func, *symbol_table, lifetimes_str);
+}
+
 namespace {
 
 class LifetimeElisionPragmaHandler : public clang::PragmaHandler {
diff --git a/lifetime_annotations/lifetime_annotations.h b/lifetime_annotations/lifetime_annotations.h
index 1c3f218..e87c88e 100644
--- a/lifetime_annotations/lifetime_annotations.h
+++ b/lifetime_annotations/lifetime_annotations.h
@@ -36,6 +36,13 @@
     const clang::FunctionDecl* func, const LifetimeAnnotationContext& context,
     LifetimeSymbolTable* symbol_table = nullptr);
 
+// Parses "a: b, a -> b"-style lifetime annotations from `lifetimes_str` for the
+// function declaration `func`. Lifetimes are inserted into the given
+// `symbol_table`, or used from there if already known.
+llvm::Expected<FunctionLifetimes> ParseLifetimeAnnotations(
+    const clang::FunctionDecl* func, const std::string& lifetimes_str,
+    LifetimeSymbolTable* symbol_table = nullptr);
+
 // Adds handlers to `preprocessor` to populate `context`.
 // To be able to use GetLifetimeAnnotations(), call this function to add the
 // necessary handlers before compiling any code.
diff --git a/lifetime_annotations/lifetime_annotations_test.cc b/lifetime_annotations/lifetime_annotations_test.cc
index c3ea44e..b1ada7b 100644
--- a/lifetime_annotations/lifetime_annotations_test.cc
+++ b/lifetime_annotations/lifetime_annotations_test.cc
@@ -381,6 +381,16 @@
               IsOkAndHolds(LifetimesAre({{"S::f", "a: b -> a"}})));
 }
 
+TEST_F(LifetimeAnnotationsTest, LifetimeAnnotation_MethodWithLifetimeParams) {
+  EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
+        struct [[clang::annotate("lifetime_params", "x", "y")]] S {
+          [[clang::annotate("lifetimes", "x, y, a: -> x")]]
+          int* f();
+        };
+  )"),
+              IsOkAndHolds(LifetimesAre({{"S::f", "(x, y, a): -> x"}})));
+}
+
 TEST_F(LifetimeAnnotationsTest, LifetimeAnnotation_Invalid_MissingThis) {
   EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
         struct S {
diff --git a/lifetime_annotations/lifetime_symbol_table.cc b/lifetime_annotations/lifetime_symbol_table.cc
index 4b9ec89..e17f7cd 100644
--- a/lifetime_annotations/lifetime_symbol_table.cc
+++ b/lifetime_annotations/lifetime_symbol_table.cc
@@ -102,4 +102,14 @@
   lifetime_to_name_[lifetime] = name;
 }
 
+void LifetimeSymbolTable::Rebind(llvm::StringRef name, Lifetime lifetime) {
+  auto iter = name_to_lifetime_.find(name);
+  if (iter == name_to_lifetime_.end()) {
+    llvm::report_fatal_error("invalid call to rebind");
+  }
+  lifetime_to_name_.erase(iter->second);
+  lifetime_to_name_[lifetime] = name;
+  iter->second = lifetime;
+}
+
 }  // namespace devtools_rust
diff --git a/lifetime_annotations/lifetime_symbol_table.h b/lifetime_annotations/lifetime_symbol_table.h
index 952aebe..60d3ea5 100644
--- a/lifetime_annotations/lifetime_symbol_table.h
+++ b/lifetime_annotations/lifetime_symbol_table.h
@@ -49,6 +49,10 @@
   // to LookupLifetime() to have undefined behaviour.
   void Add(llvm::StringRef name, Lifetime lifetime);
 
+  // Re-binds a name to a different lifetime.
+  // The name *must* be already used.
+  void Rebind(llvm::StringRef name, Lifetime lifetime);
+
   // Accessor for hashing/debugging purposes.
   const llvm::StringMap<Lifetime>& GetMapping() const {
     return name_to_lifetime_;
diff --git a/lifetime_annotations/lifetime_symbol_table_test.cc b/lifetime_annotations/lifetime_symbol_table_test.cc
index 9e2f6b9..a3f3bd5 100644
--- a/lifetime_annotations/lifetime_symbol_table_test.cc
+++ b/lifetime_annotations/lifetime_symbol_table_test.cc
@@ -40,6 +40,20 @@
   EXPECT_EQ(table.LookupLifetime(b), "b");
 }
 
+TEST(LifetimeSymbolTableTest, RebindLifetime) {
+  LifetimeSymbolTable table;
+
+  Lifetime a = table.LookupNameAndMaybeDeclare("a");
+  Lifetime b = Lifetime::CreateVariable();
+
+  EXPECT_EQ(table.LookupLifetime(a), "a");
+
+  table.Rebind("a", b);
+  EXPECT_EQ(table.LookupName("a"), b);
+  EXPECT_EQ(table.LookupLifetime(a), std::nullopt);
+  EXPECT_EQ(table.LookupLifetime(b), "a");
+}
+
 TEST(LifetimeSymbolTableTest, LookupLifetimeAndMaybeDeclare) {
   {
     LifetimeSymbolTable table;
diff --git a/lifetime_annotations/type_lifetimes.cc b/lifetime_annotations/type_lifetimes.cc
index 66cca13..54c38b7 100644
--- a/lifetime_annotations/type_lifetimes.cc
+++ b/lifetime_annotations/type_lifetimes.cc
@@ -22,31 +22,18 @@
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/DeclTemplate.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/TemplateBase.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/Type.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/Basic/LLVM.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/Basic/SourceLocation.h"
 #include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/ArrayRef.h"
 #include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/DenseMapInfo.h"
 #include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/Hashing.h"
 #include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/SmallVector.h"
 #include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/StringRef.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Error.h"
 #include "third_party/llvm/llvm-project/llvm/include/llvm/Support/ErrorHandling.h"
 
 namespace devtools_rust {
 
-std::string DebugString(const TypeLifetimes& lifetimes,
-                        const LifetimeFormatter& formatter) {
-  std::vector<std::string> parts;
-  parts.reserve(lifetimes.size());
-
-  for (Lifetime l : lifetimes) {
-    parts.push_back(formatter(l));
-  }
-
-  if (parts.size() == 1) {
-    return parts[0];
-  }
-  return absl::StrFormat("(%s)", absl::StrJoin(parts, ", "));
-}
-
 llvm::SmallVector<std::string> GetLifetimeParameters(clang::QualType type) {
   // TODO(mboehme):
   // - Add support for type aliases with lifetime parameters
@@ -90,40 +77,6 @@
   return ret;
 }
 
-TypeLifetimes CreateLifetimesForType(
-    clang::QualType type, std::function<Lifetime()> lifetime_factory) {
-  assert(!type.isNull());
-  TypeLifetimes ret;
-
-  for (const auto& lftm_param : GetLifetimeParameters(type)) {
-    (void)lftm_param;
-    ret.push_back(lifetime_factory());
-  }
-
-  // Add implicit lifetime parameters for type template parameters.
-  llvm::ArrayRef<clang::TemplateArgument> template_args = GetTemplateArgs(type);
-  if (!template_args.empty()) {
-    for (const clang::TemplateArgument& arg : template_args) {
-      if (arg.getKind() == clang::TemplateArgument::Type) {
-        ret.append(CreateLifetimesForType(arg.getAsType(), lifetime_factory));
-      } else if (arg.getKind() == clang::TemplateArgument::Pack) {
-        for (const clang::TemplateArgument& inner_arg : arg.getPackAsArray()) {
-          if (inner_arg.getKind() == clang::TemplateArgument::Type) {
-            ret.append(CreateLifetimesForType(inner_arg.getAsType(),
-                                              lifetime_factory));
-          }
-        }
-      }
-    }
-    return ret;
-  }
-  clang::QualType pointee = PointeeType(type);
-  if (pointee.isNull()) return ret;
-  ret = CreateLifetimesForType(pointee, lifetime_factory);
-  ret.push_back(lifetime_factory());
-  return ret;
-}
-
 ValueLifetimes& ValueLifetimes::operator=(const ValueLifetimes& other) {
   type_ = other.type_;
   template_argument_lifetimes_ = other.template_argument_lifetimes_;
@@ -135,85 +88,74 @@
   return *this;
 }
 
-std::string ValueLifetimes::DebugString() const {
-  std::string ret;
-  if (!lifetime_parameters_by_name_.GetMapping().empty()) {
-    std::vector<std::string> lftm_args_strings;
-    for (const auto& lftm_arg : lifetime_parameters_by_name_.GetMapping()) {
-      lftm_args_strings.push_back(lftm_arg.second.DebugString());
-    }
-    absl::StrAppend(&ret, "(", absl::StrJoin(lftm_args_strings, ", "), ")");
-  }
-  if (!template_argument_lifetimes_.empty()) {
-    std::vector<std::string> tmpl_arg_strings;
-    for (const std::optional<ValueLifetimes>& tmpl_arg :
-         template_argument_lifetimes_) {
-      if (tmpl_arg) {
-        tmpl_arg_strings.push_back(tmpl_arg->DebugString());
-      } else {
-        tmpl_arg_strings.push_back("");
-      }
-    }
-    absl::StrAppend(&ret, "<", absl::StrJoin(tmpl_arg_strings, ", "), ">");
-  }
-  if (pointee_lifetimes_) {
-    absl::StrAppend(&ret, " -> ", pointee_lifetimes_->DebugString());
-  }
-  return ret;
-}
-
-void ValueLifetimes::ReverseVisitTemplateArgs(
-    llvm::ArrayRef<clang::TemplateArgument> template_args,
-    TypeLifetimesRef& type_lifetimes, ValueLifetimes& out) {
-  for (size_t i = template_args.size(); i-- > 0;) {
-    const clang::TemplateArgument& arg = template_args[i];
-    if (arg.getKind() == clang::TemplateArgument::Type) {
-      out.template_argument_lifetimes_.push_back(
-          FromTypeLifetimes(type_lifetimes, arg.getAsType()));
-    } else if (arg.getKind() == clang::TemplateArgument::Pack) {
-      ReverseVisitTemplateArgs(arg.getPackAsArray(), type_lifetimes, out);
-    } else {
-      out.template_argument_lifetimes_.push_back(std::nullopt);
-    }
-  }
-}
-
-// Here, `type_lifetimes` are the lifetimes of a prvalue of the given `type`,
-// unlike ObjectLifetimes::FromTypeLifetimes, which assumes a glvalue.
-ValueLifetimes ValueLifetimes::FromTypeLifetimes(
-    TypeLifetimesRef& type_lifetimes, clang::QualType type) {
+llvm::Expected<ValueLifetimes> ValueLifetimes::Create(
+    clang::QualType type, LifetimeFactory lifetime_factory) {
   assert(!type.isNull());
-
   ValueLifetimes ret(type);
 
-  llvm::SmallVector<std::string> params = GetLifetimeParameters(type);
-  // Visit in reverse order, as we are doing a post-order traversal.
-  for (size_t i = params.size(); i-- > 0;) {
-    if (ret.lifetime_parameters_by_name_.LookupName(params[i])) {
-      llvm::report_fatal_error("duplicate lifetime parameter name");
+  for (const auto& lftm_param : GetLifetimeParameters(type)) {
+    Lifetime l;
+    if (llvm::Error err = lifetime_factory(type, lftm_param).moveInto(l)) {
+      return std::move(err);
     }
-    ret.lifetime_parameters_by_name_.Add(params[i], type_lifetimes.back());
-    type_lifetimes = type_lifetimes.drop_back();
+    ret.lifetime_parameters_by_name_.Add(lftm_param, l);
   }
 
+  // Add implicit lifetime parameters for type template parameters.
   llvm::ArrayRef<clang::TemplateArgument> template_args = GetTemplateArgs(type);
   if (!template_args.empty()) {
-    // Since we are simulating reversing a post-order visit, we need to
-    // extract template arguments in reverse order.
-    ReverseVisitTemplateArgs(template_args, type_lifetimes, ret);
-    std::reverse(ret.template_argument_lifetimes_.begin(),
-                 ret.template_argument_lifetimes_.end());
+    for (const clang::TemplateArgument& arg : template_args) {
+      if (arg.getKind() == clang::TemplateArgument::Type) {
+        ValueLifetimes template_arg_lifetime;
+        if (llvm::Error err =
+                ValueLifetimes::Create(arg.getAsType(), lifetime_factory)
+                    .moveInto(template_arg_lifetime)) {
+          return std::move(err);
+        }
+        ret.template_argument_lifetimes_.push_back(template_arg_lifetime);
+      } else if (arg.getKind() == clang::TemplateArgument::Pack) {
+        for (const clang::TemplateArgument& inner_arg : arg.getPackAsArray()) {
+          if (inner_arg.getKind() == clang::TemplateArgument::Type) {
+            ValueLifetimes template_arg_lifetime;
+            if (llvm::Error err = ValueLifetimes::Create(inner_arg.getAsType(),
+                                                         lifetime_factory)
+                                      .moveInto(template_arg_lifetime)) {
+              return std::move(err);
+            }
+            ret.template_argument_lifetimes_.push_back(template_arg_lifetime);
+          }
+        }
+      }
+    }
     return ret;
   }
 
-  clang::QualType pointee_type = PointeeType(type);
-  if (!pointee_type.isNull()) {
-    ret.pointee_lifetimes_ = std::make_unique<ObjectLifetimes>(
-        ObjectLifetimes::FromTypeLifetimes(type_lifetimes, pointee_type));
+  clang::QualType pointee = PointeeType(type);
+  if (pointee.isNull()) return ret;
+  ObjectLifetimes obj_lftm;
+  if (llvm::Error err = ObjectLifetimes::Create(pointee, lifetime_factory)
+                            .moveInto(obj_lftm)) {
+    return std::move(err);
   }
+  ret.pointee_lifetimes_ =
+      std::make_unique<ObjectLifetimes>(std::move(obj_lftm));
   return ret;
 }
 
+llvm::Expected<ObjectLifetimes> ObjectLifetimes::Create(
+    clang::QualType type, LifetimeFactory lifetime_factory) {
+  ValueLifetimes v;
+  if (llvm::Error err =
+          ValueLifetimes::Create(type, lifetime_factory).moveInto(v)) {
+    return std::move(err);
+  }
+  Lifetime l;
+  if (llvm::Error err = lifetime_factory(type, "").moveInto(l)) {
+    return std::move(err);
+  }
+  return ObjectLifetimes(l, v);
+}
+
 ValueLifetimes ValueLifetimes::ForLifetimeLessType(clang::QualType type) {
   assert(PointeeType(type).isNull() && !type->isRecordType());
   return ValueLifetimes(type);
@@ -240,23 +182,56 @@
   return result;
 }
 
+// TODO(veluca): improve the formatting here.
+std::string ValueLifetimes::DebugString(
+    const LifetimeFormatter& formatter) const {
+  std::vector<std::string> lifetimes;
+  Traverse([&](Lifetime l, Variance) { lifetimes.push_back(formatter(l)); });
+  if (lifetimes.size() == 1) {
+    return lifetimes[0];
+  } else {
+    return absl::StrCat("(", absl::StrJoin(lifetimes, ", "), ")");
+  }
+}
+
 const ObjectLifetimes& ValueLifetimes::GetPointeeLifetimes() const {
   assert(!PointeeType(type_).isNull());
   return *pointee_lifetimes_;
 }
 
-ObjectLifetimes ObjectLifetimes::FromTypeLifetimes(
-    TypeLifetimesRef& type_lifetimes, clang::QualType type) {
-  assert(!type_lifetimes.empty());
-  assert(!type.isNull());
-  Lifetime self_lifetime = type_lifetimes.back();
-  type_lifetimes = type_lifetimes.drop_back();
-  return ObjectLifetimes(
-      self_lifetime, ValueLifetimes::FromTypeLifetimes(type_lifetimes, type));
+void ValueLifetimes::Traverse(std::function<void(Lifetime&, Variance)> visitor,
+                              Variance variance) {
+  for (std::optional<ValueLifetimes>& tmpl_arg : template_argument_lifetimes_) {
+    if (tmpl_arg) {
+      tmpl_arg->Traverse(visitor, kInvariant);
+    }
+  }
+  if (pointee_lifetimes_) {
+    pointee_lifetimes_->Traverse(visitor, variance, Type());
+  }
+  for (const auto& lftm_arg : GetLifetimeParameters(type_)) {
+    std::optional<Lifetime> lifetime =
+        lifetime_parameters_by_name_.LookupName(lftm_arg);
+    assert(lifetime.has_value());
+    Lifetime new_lifetime = *lifetime;
+    visitor(new_lifetime, variance);
+
+    // Note that this check is not an optimization, but a guard against UB:
+    // if one calls the const version of Traverse, with a callback that does not
+    // mutate the lifetime, on a const instance of the Value/ObjectLifetimes,
+    // then calling Rebind would be UB, even if Rebind would do nothing in
+    // practice.
+    if (new_lifetime != lifetime) {
+      lifetime_parameters_by_name_.Rebind(lftm_arg, new_lifetime);
+    }
+  }
 }
 
-std::string ObjectLifetimes::DebugString() const {
-  return absl::StrCat(lifetime_.DebugString(), value_lifetimes_.DebugString());
+void ValueLifetimes::Traverse(
+    std::function<void(const Lifetime&, Variance)> visitor,
+    Variance variance) const {
+  const_cast<ValueLifetimes*>(this)->Traverse(
+      [&visitor](Lifetime& l, Variance v) { visitor(l, v); }, variance);
 }
 
 const llvm::ArrayRef<clang::TemplateArgument> GetTemplateArgs(
@@ -343,12 +318,11 @@
           // Create a new ValueLifetimes of the type of the template parameter,
           // with lifetime `lifetime_`.
           // TODO(veluca): we need to propagate lifetime parameters here.
-          TypeLifetimes type_lifetimes = CreateLifetimesForType(
-              arg.getAsType(), [this]() { return this->lifetime_; });
-          TypeLifetimesRef type_lifetimes_ref(type_lifetimes);
           template_argument_lifetimes.push_back(
-              ValueLifetimes::FromTypeLifetimes(type_lifetimes_ref,
-                                                arg.getAsType()));
+              ValueLifetimes::Create(arg.getAsType(), [this](clang::QualType,
+                                                             llvm::StringRef) {
+                return this->lifetime_;
+              }).get());
         }
       } else {
         template_argument_lifetimes.push_back(std::nullopt);
@@ -378,6 +352,17 @@
                          ValueLifetimes::ForLifetimeLessType(type));
 }
 
+std::string ObjectLifetimes::DebugString(
+    const LifetimeFormatter& formatter) const {
+  std::vector<std::string> lifetimes;
+  Traverse([&](Lifetime l, Variance) { lifetimes.push_back(formatter(l)); });
+  if (lifetimes.size() == 1) {
+    return lifetimes[0];
+  } else {
+    return absl::StrCat("(", absl::StrJoin(lifetimes, ", "), ")");
+  }
+}
+
 ObjectLifetimes ObjectLifetimes::GetFieldOrBaseLifetimes(
     clang::QualType type,
     llvm::SmallVector<std::string> type_lifetime_args) const {
@@ -385,6 +370,26 @@
                                             "");
 }
 
+void ObjectLifetimes::Traverse(std::function<void(Lifetime&, Variance)> visitor,
+                               Variance variance,
+                               clang::QualType indirection_type) {
+  assert(indirection_type.isNull() ||
+         indirection_type->getPointeeType() == Type());
+  value_lifetimes_.Traverse(
+      visitor, indirection_type.isNull() || indirection_type.isConstQualified()
+                   ? kCovariant
+                   : kInvariant);
+  visitor(lifetime_, variance);
+}
+
+void ObjectLifetimes::Traverse(
+    std::function<void(const Lifetime&, Variance)> visitor, Variance variance,
+    clang::QualType indirection_type) const {
+  const_cast<ObjectLifetimes*>(this)->Traverse(
+      [&visitor](Lifetime& l, Variance v) { visitor(l, v); }, variance,
+      indirection_type);
+}
+
 llvm::Expected<llvm::StringRef> EvaluateAsStringLiteral(
     const clang::Expr* expr, const clang::ASTContext& ast_context) {
   auto error = []() {
diff --git a/lifetime_annotations/type_lifetimes.h b/lifetime_annotations/type_lifetimes.h
index 69d431d..95a1a8b 100644
--- a/lifetime_annotations/type_lifetimes.h
+++ b/lifetime_annotations/type_lifetimes.h
@@ -23,15 +23,6 @@
 
 namespace devtools_rust {
 
-// Lifetimes for a `QualType`.
-//
-// These are ordered according to a post-order traversal of the type.
-//
-// TODO(mboehme): Replace occurrences of this type with ObjectLifetimes
-// or otherwise clarify the relationship between the two types.
-using TypeLifetimes = llvm::SmallVector<Lifetime>;
-using TypeLifetimesRef = llvm::ArrayRef<Lifetime>;
-
 // Returns a lifetime in some human-readable format.
 using LifetimeFormatter = std::function<std::string(Lifetime)>;
 
@@ -41,30 +32,30 @@
   kInvariant,
 };
 
-// Returns a human-readable representation of `lifetimes`.
-std::string DebugString(
-    const TypeLifetimes& lifetimes,
-    const LifetimeFormatter& formatter = [](Lifetime l) {
-      return l.DebugString();
-    });
-
 // Extracts the lifetime parameters of the given type.
 llvm::SmallVector<std::string> GetLifetimeParameters(clang::QualType type);
 
-TypeLifetimes CreateLifetimesForType(
-    clang::QualType type, std::function<Lifetime()> lifetime_factory);
+// Takes as input the type of the object that the lifetime is being created for
+// and the name of the lifetime parameter, if any.
+using LifetimeFactory =
+    std::function<llvm::Expected<Lifetime>(clang::QualType, llvm::StringRef)>;
 
 class ObjectLifetimes;
 
 // Represents the lifetimes of a value; these may be 0 for non-reference-like
 // types, 1 for pointers/references, and an arbitrary number for structs with
 // template arguments/lifetime parameters.
-// This is a more structured representation than TypeLifetimes that is easier
-// to query.
 class ValueLifetimes {
  public:
-  static ValueLifetimes FromTypeLifetimes(TypeLifetimesRef& type_lifetimes,
-                                          clang::QualType type);
+  // Creates an invalid ValueLifetimes, which should not be used. This is
+  // provided only for usage with functions with output parameters.
+  ValueLifetimes() : type_(clang::QualType()) {}
+
+  // Creates a ValueLifetimes for a *value* of a given type.
+  // Only fails if lifetime_factory fails.
+  // Lifetimes will be created in post-order in the tree of lifetimes.
+  static llvm::Expected<ValueLifetimes> Create(
+      clang::QualType type, LifetimeFactory lifetime_factory);
 
   // Returns a ValueLifetimes for a lifetime-less type.
   // `type` must not be a pointer-like type or a record type.
@@ -88,7 +79,9 @@
 
   ValueLifetimes& operator=(const ValueLifetimes& other);
 
-  std::string DebugString() const;
+  std::string DebugString(const LifetimeFormatter& formatter = [](Lifetime l) {
+    return l.DebugString();
+  }) const;
 
   // Returns the type of the value.
   clang::QualType Type() const { return type_; }
@@ -117,13 +110,28 @@
     return ret.value();
   }
 
+  bool HasLifetimes() const {
+    return pointee_lifetimes_ != nullptr ||
+           !lifetime_parameters_by_name_.GetMapping().empty() ||
+           !template_argument_lifetimes_.empty();
+  }
+
+  // Traverses all the lifetimes in the object, recursively. The
+  // visit is done in post-order on the lifetime tree of this type.
+  // The callback may mutate the lifetime in an arbitrary way; `variance` will
+  // be set to signal the variance of the lifetime in the position it appears
+  // in; this is decided with the `variance` parameter to Traverse, which
+  // signals the variance of the current Value. Note that the visitor may be
+  // called multiple times with the same lifetime (but in different positions).
+  // TODO(veluca): verify that the handling of variance is correct in all cases.
+  void Traverse(std::function<void(Lifetime&, Variance)> visitor,
+                Variance variance = kCovariant);
+  void Traverse(std::function<void(const Lifetime&, Variance)> visitor,
+                Variance variance = kCovariant) const;
+
  private:
   explicit ValueLifetimes(clang::QualType type) : type_(type) {}
 
-  static void ReverseVisitTemplateArgs(
-      llvm::ArrayRef<clang::TemplateArgument> template_args,
-      TypeLifetimesRef& type_lifetimes, ValueLifetimes& out);
-
   // Note: only one of `pointee_lifetime` or `template_argument_lifetimes`
   // is non-empty.
   std::unique_ptr<ObjectLifetimes> pointee_lifetimes_;
@@ -151,17 +159,23 @@
   friend class llvm::DenseMapInfo<devtools_rust::ValueLifetimes>;
 };
 
-// Represents all the lifetimes of an object.
-// This is a more structured representation than TypeLifetimes that is easier
-// to query.
+// Represents all the lifetimes of an object. The object itself always has
+// a lifetime; in addition, there may be lifetimes associated with the value
+// of the object.
 class ObjectLifetimes {
  public:
-  // Constructs the ObjectLifetimes corresponding to the given `type_lifetimes`
-  // when interpreted as the lifetimes of a glvalue of the given
-  // `type`. Removes the consumed lifetimes from `type_lifetimes` (which
-  // simulates undoing a post-order visit of the lifetime_ tree).
-  static ObjectLifetimes FromTypeLifetimes(TypeLifetimesRef& type_lifetimes,
-                                           clang::QualType type);
+  // Creates an invalid ObjectLifetimes, which should not be used. This is
+  // provided only for usage with functions with output parameters.
+  ObjectLifetimes() {}
+
+  ObjectLifetimes(Lifetime lifetime, ValueLifetimes value_lifetimes)
+      : lifetime_(lifetime), value_lifetimes_(value_lifetimes) {}
+
+  // Creates lifetimes for an *object* of a given type.
+  // Only fails if lifetime_factory fails.
+  // Lifetimes will be created in post-order in the tree of lifetimes.
+  static llvm::Expected<ObjectLifetimes> Create(
+      clang::QualType type, LifetimeFactory lifetime_factory);
 
   // Returns the lifetime of the object itself.
   Lifetime GetLifetime() const { return lifetime_; }
@@ -169,13 +183,34 @@
   // Returns the lifetime of the contained value.
   const ValueLifetimes& GetValueLifetimes() const { return value_lifetimes_; }
 
-  std::string DebugString() const;
+  clang::QualType Type() const { return value_lifetimes_.Type(); }
+
+  std::string DebugString(const LifetimeFormatter& formatter = [](Lifetime l) {
+    return l.DebugString();
+  }) const;
 
   // Returns the ObjectLifetimes for a base class or a field of the given type.
   ObjectLifetimes GetFieldOrBaseLifetimes(
       clang::QualType type,
       llvm::SmallVector<std::string> type_lifetime_args) const;
 
+  // Traverses all the lifetimes in the object, recursively. The
+  // visit is done in post-order on the lifetime tree of this type.
+  // The callback may mutate the lifetime in an arbitrary way; `variance` will
+  // be set to signal the variance of the lifetime in the position it appears
+  // in; this is decided with the `variance` parameter to Traverse, which
+  // signals the variance of the current Object. Note that the visitor may be
+  // called multiple times with the same lifetime (but in different positions).
+  // `indirection_type` defines the type of the pointer (or reference) to this
+  // object; this is used to determine variance of its pointees.
+  // TODO(veluca): verify that the handling of variance is correct in all cases.
+  void Traverse(std::function<void(Lifetime&, Variance)> visitor,
+                Variance variance = kCovariant,
+                clang::QualType indirection_type = clang::QualType());
+  void Traverse(std::function<void(const Lifetime&, Variance)> visitor,
+                Variance variance = kCovariant,
+                clang::QualType indirection_type = clang::QualType()) const;
+
  private:
   // Returns the ObjectLifetimes for an object of a given type, whose lifetimes
   // are scoped within or derived from the object that `this`
@@ -187,9 +222,6 @@
       clang::QualType type, llvm::SmallVector<std::string> type_lifetime_args,
       llvm::StringRef object_lifetime_parameter) const;
 
-  ObjectLifetimes(Lifetime lifetime, ValueLifetimes value_lifetimes)
-      : lifetime_(lifetime), value_lifetimes_(value_lifetimes) {}
-
   friend class llvm::DenseMapInfo<devtools_rust::ObjectLifetimes>;
   Lifetime lifetime_;
   ValueLifetimes value_lifetimes_;
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index 4f35b30..ceeeedd 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -25,6 +25,7 @@
 #include "third_party/absl/strings/str_join.h"
 #include "third_party/absl/strings/string_view.h"
 #include "third_party/absl/strings/substitute.h"
+#include "lifetime_annotations/type_lifetimes.h"
 #include "rs_bindings_from_cc/ast_convert.h"
 #include "rs_bindings_from_cc/bazel_types.h"
 #include "rs_bindings_from_cc/ir.h"
@@ -488,7 +489,6 @@
       devtools_rust::GetLifetimeAnnotations(function_decl,
                                             *invocation_.lifetime_context_,
                                             &lifetime_symbol_table);
-  llvm::DenseSet<devtools_rust::Lifetime> all_lifetimes;
 
   std::vector<FuncParam> params;
   std::set<std::string> errors;
@@ -505,10 +505,9 @@
 
     // non-static member functions receive an implicit `this` parameter.
     if (method_decl->isInstance()) {
-      std::optional<devtools_rust::TypeLifetimes> this_lifetimes;
+      std::optional<devtools_rust::ValueLifetimes> this_lifetimes;
       if (lifetimes) {
-        this_lifetimes = lifetimes->this_lifetimes;
-        all_lifetimes.insert(this_lifetimes->begin(), this_lifetimes->end());
+        this_lifetimes = lifetimes->GetThisLifetimes();
       }
       auto param_type =
           ConvertQualType(method_decl->getThisType(), this_lifetimes,
@@ -523,15 +522,14 @@
   }
 
   if (lifetimes) {
-    CRUBIT_CHECK(lifetimes->param_lifetimes.size() ==
-                 function_decl->getNumParams());
+    CRUBIT_CHECK(lifetimes->IsValidForDecl(function_decl));
   }
+
   for (unsigned i = 0; i < function_decl->getNumParams(); ++i) {
     const clang::ParmVarDecl* param = function_decl->getParamDecl(i);
-    std::optional<devtools_rust::TypeLifetimes> param_lifetimes;
+    std::optional<devtools_rust::ValueLifetimes> param_lifetimes;
     if (lifetimes) {
-      param_lifetimes = lifetimes->param_lifetimes[i];
-      all_lifetimes.insert(param_lifetimes->begin(), param_lifetimes->end());
+      param_lifetimes = lifetimes->GetParamLifetimes(i);
     }
     auto param_type = ConvertQualType(param->getType(), param_lifetimes);
     if (!param_type.ok()) {
@@ -577,11 +575,11 @@
     }
   }
 
-  std::optional<devtools_rust::TypeLifetimes> return_lifetimes;
+  std::optional<devtools_rust::ValueLifetimes> return_lifetimes;
   if (lifetimes) {
-    return_lifetimes = lifetimes->return_lifetimes;
-    all_lifetimes.insert(return_lifetimes->begin(), return_lifetimes->end());
+    return_lifetimes = lifetimes->GetReturnLifetimes();
   }
+
   auto return_type =
       ConvertQualType(function_decl->getReturnType(), return_lifetimes);
   if (!return_type.ok()) {
@@ -589,6 +587,14 @@
                            return_type.status().message()));
   }
 
+  llvm::DenseSet<devtools_rust::Lifetime> all_lifetimes;
+  if (lifetimes) {
+    lifetimes->Traverse(
+        [&all_lifetimes](devtools_rust::Lifetime l, devtools_rust::Variance) {
+          all_lifetimes.insert(l);
+        });
+  }
+
   std::vector<Lifetime> lifetime_params;
   for (devtools_rust::Lifetime lifetime : all_lifetimes) {
     std::optional<llvm::StringRef> name =
@@ -827,7 +833,7 @@
         enum_decl,
         "Forward declared enums without type specifiers are not supported");
   }
-  std::optional<devtools_rust::TypeLifetimes> no_lifetimes;
+  std::optional<devtools_rust::ValueLifetimes> no_lifetimes;
   absl::StatusOr<MappedType> type = ConvertQualType(cc_type, no_lifetimes);
   if (!type.ok()) {
     return ImportUnsupportedItem(enum_decl, type.status().ToString());
@@ -901,7 +907,7 @@
   std::optional<Identifier> identifier =
       GetTranslatedIdentifier(typedef_name_decl);
   CRUBIT_CHECK(identifier.has_value());  // This should never happen.
-  std::optional<devtools_rust::TypeLifetimes> no_lifetimes;
+  std::optional<devtools_rust::ValueLifetimes> no_lifetimes;
   absl::StatusOr<MappedType> underlying_type =
       ConvertQualType(typedef_name_decl->getUnderlyingType(), no_lifetimes);
   if (underlying_type.ok()) {
@@ -974,7 +980,7 @@
 
 absl::StatusOr<MappedType> Importer::ConvertType(
     const clang::Type* type,
-    std::optional<devtools_rust::TypeLifetimes>& lifetimes,
+    std::optional<devtools_rust::ValueLifetimes>& lifetimes,
     bool nullable) const {
   // Qualifiers are handled separately in ConvertQualType().
   std::string type_string = clang::QualType(type, 0).getAsString();
@@ -986,9 +992,9 @@
     clang::QualType pointee_type = type->getPointeeType();
     std::optional<LifetimeId> lifetime;
     if (lifetimes.has_value()) {
-      CRUBIT_CHECK(!lifetimes->empty());
-      lifetime = LifetimeId(lifetimes->back().Id());
-      lifetimes->pop_back();
+      lifetime =
+          LifetimeId(lifetimes->GetPointeeLifetimes().GetLifetime().Id());
+      lifetimes = lifetimes->GetPointeeLifetimes().GetValueLifetimes();
     }
     if (const auto* func_type =
             pointee_type->getAs<clang::FunctionProtoType>()) {
@@ -1079,7 +1085,7 @@
 
 absl::StatusOr<MappedType> Importer::ConvertQualType(
     clang::QualType qual_type,
-    std::optional<devtools_rust::TypeLifetimes>& lifetimes,
+    std::optional<devtools_rust::ValueLifetimes>& lifetimes,
     bool nullable) const {
   std::string type_string = qual_type.getAsString();
   absl::StatusOr<MappedType> type =
@@ -1108,7 +1114,7 @@
   std::vector<Field> fields;
   const clang::ASTRecordLayout& layout = ctx_.getASTRecordLayout(record_decl);
   for (const clang::FieldDecl* field_decl : record_decl->fields()) {
-    std::optional<devtools_rust::TypeLifetimes> no_lifetimes;
+    std::optional<devtools_rust::ValueLifetimes> no_lifetimes;
     auto type = ConvertQualType(field_decl->getType(), no_lifetimes);
     if (!type.ok()) {
       return absl::UnimplementedError(absl::Substitute(
diff --git a/rs_bindings_from_cc/importer.h b/rs_bindings_from_cc/importer.h
index 54cd6b2..4ea7052 100644
--- a/rs_bindings_from_cc/importer.h
+++ b/rs_bindings_from_cc/importer.h
@@ -18,6 +18,7 @@
 #include "third_party/absl/status/statusor.h"
 #include "third_party/absl/types/span.h"
 #include "lifetime_annotations/lifetime_annotations.h"
+#include "lifetime_annotations/type_lifetimes.h"
 #include "rs_bindings_from_cc/bazel_types.h"
 #include "rs_bindings_from_cc/ir.h"
 #include "rs_bindings_from_cc/util/check.h"
@@ -162,11 +163,11 @@
   // nullability annotations.
   absl::StatusOr<MappedType> ConvertQualType(
       clang::QualType qual_type,
-      std::optional<devtools_rust::TypeLifetimes>& lifetimes,
+      std::optional<devtools_rust::ValueLifetimes>& lifetimes,
       bool nullable = true) const;
   absl::StatusOr<MappedType> ConvertType(
       const clang::Type* type,
-      std::optional<devtools_rust::TypeLifetimes>& lifetimes,
+      std::optional<devtools_rust::ValueLifetimes>& lifetimes,
       bool nullable) const;
   absl::StatusOr<MappedType> ConvertTypeDecl(const clang::TypeDecl* decl) const;