Add lifetimes to IR.

This only changes the IR data structures and fixes any tests that break. It does
not yet add lifetime-specific tests; this will come in a followup CL that
actually populates lifetimes and uses them in code generation. I'm choosing to
do that in a separate CL as this CL is already large enough as it is.

PiperOrigin-RevId: 413901182
diff --git a/rs_bindings_from_cc/ir.cc b/rs_bindings_from_cc/ir.cc
index eab4917..9cc51d0 100644
--- a/rs_bindings_from_cc/ir.cc
+++ b/rs_bindings_from_cc/ir.cc
@@ -38,6 +38,13 @@
   return result;
 }
 
+nlohmann::json Lifetime::ToJson() const {
+  nlohmann::json result;
+  result["name"] = name;
+  result["id"] = id.value();
+  return result;
+}
+
 nlohmann::json RsType::ToJson() const {
   nlohmann::json result;
 
@@ -46,6 +53,12 @@
   } else {
     result["name"] = name;
   }
+  std::vector<nlohmann::json> json_lifetime_args;
+  json_lifetime_args.reserve(lifetime_args.size());
+  for (const LifetimeId& lifetime_id : lifetime_args) {
+    json_lifetime_args.push_back(lifetime_id.value());
+  }
+  result["lifetime_args"] = json_lifetime_args;
   result["type_args"] = VectorToJson(type_args);
 
   return result;
@@ -144,6 +157,7 @@
   func["mangled_name"] = mangled_name;
   func["return_type"] = return_type.ToJson();
   func["params"] = VectorToJson(params);
+  func["lifetime_params"] = VectorToJson(lifetime_params);
   func["is_inline"] = is_inline;
   if (member_func_metadata.has_value()) {
     func["member_func_metadata"] = member_func_metadata->ToJson();
@@ -217,6 +231,7 @@
     record["doc_comment"] = *doc_comment;
   }
   record["fields"] = VectorToJson(fields);
+  record["lifetime_params"] = VectorToJson(lifetime_params);
   record["size"] = size;
   record["alignment"] = alignment;
   record["copy_constructor"] = copy_constructor.ToJson();
diff --git a/rs_bindings_from_cc/ir.h b/rs_bindings_from_cc/ir.h
index 2adcfc8..ec3ecdb 100644
--- a/rs_bindings_from_cc/ir.h
+++ b/rs_bindings_from_cc/ir.h
@@ -71,6 +71,27 @@
 // and records). We use DeclIds for this.
 DEFINE_STRONG_INT_TYPE(DeclId, uintptr_t);
 
+// A numerical ID that uniquely identifies a lifetime.
+DEFINE_STRONG_INT_TYPE(LifetimeId, int);
+
+// A lifetime.
+struct Lifetime {
+  nlohmann::json ToJson() const;
+
+  // Lifetime name. Unlike syn::Lifetime, this does not include the apostrophe.
+  //
+  // Note that this is not an identifier; the rules for what is a valid lifetime
+  // name are slightly different than for identifiers, so we simply use a
+  // std::string instead of an Identifier here.
+  std::string name;
+
+  LifetimeId id;
+};
+
+inline std::ostream& operator<<(std::ostream& o, const Lifetime& l) {
+  return o << std::setw(internal::kJsonIndent) << l.ToJson();
+}
+
 // A C++ type involved in the bindings. It has the knowledge of how the type
 // is spelled in C++.
 struct CcType {
@@ -107,6 +128,15 @@
   // Id of a decl that this type corresponds to. `nullopt` for primitive types.
   std::optional<DeclId> decl_id = std::nullopt;
 
+  // Lifetime arguments for a generic type. Examples:
+  //   *mut i32 has no lifetime arguments
+  //   &'a 32 has a single lifetime argument, 'a.
+  //   SomeType<'a, 'b> has two lifetime arguments, 'a and 'b.
+  // Lifetimes are identified by their unique ID. The corresponding Lifetime
+  // will be found within the lifetime_params of a Func or Record that uses
+  // this type.
+  std::vector<LifetimeId> lifetime_args = {};
+
   // Type arguments for a generic type. Examples:
   //   i32 has no type arguments.
   //   *mut i32 has a single type argument, i32.
@@ -265,6 +295,7 @@
   std::string mangled_name;
   MappedType return_type;
   std::vector<FuncParam> params;
+  std::vector<Lifetime> lifetime_params;
   bool is_inline;
   // If null, this is not a member function.
   std::optional<MemberFuncMetadata> member_func_metadata;
@@ -344,6 +375,7 @@
   Label owning_target;
   std::optional<std::string> doc_comment;
   std::vector<Field> fields;
+  std::vector<Lifetime> lifetime_params;
   // Size and alignment in bytes.
   int64_t size;
   int64_t alignment;
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index 9cf96d3..72be12a 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -62,9 +62,20 @@
     pub name: String,
 }
 
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Deserialize)]
+#[serde(transparent)]
+pub struct LifetimeId(pub i32);
+
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+pub struct Lifetime {
+    pub name: String,
+    pub id: LifetimeId,
+}
+
 #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
 pub struct RsType {
     pub name: Option<String>,
+    pub lifetime_args: Vec<LifetimeId>,
     pub type_args: Vec<RsType>,
     pub decl_id: Option<DeclId>,
 }
@@ -178,6 +189,7 @@
     pub doc_comment: Option<String>,
     pub return_type: MappedType,
     pub params: Vec<FuncParam>,
+    pub lifetime_params: Vec<Lifetime>,
     pub is_inline: bool,
     pub member_func_metadata: Option<MemberFuncMetadata>,
 }
@@ -220,6 +232,7 @@
     pub owning_target: Label,
     pub doc_comment: Option<String>,
     pub fields: Vec<Field>,
+    pub lifetime_params: Vec<Lifetime>,
     pub size: usize,
     pub alignment: usize,
     pub copy_constructor: SpecialMemberFunc,
@@ -453,7 +466,7 @@
                         {
                             "identifier": {"identifier": "public_int" },
                             "type": {
-                                "rs_type": {"name": "i32", "type_args": []},
+                                "rs_type": {"name": "i32", "lifetime_args": [], "type_args": []},
                                 "cc_type": {"name": "int", "is_const": false, "type_args": []}
                             },
                             "access": "Public",
@@ -462,7 +475,7 @@
                         {
                             "identifier": {"identifier": "protected_int" },
                             "type": {
-                                "rs_type": {"name": "i32", "type_args": []},
+                                "rs_type": {"name": "i32", "lifetime_args": [], "type_args": []},
                                 "cc_type": {"name": "int", "is_const": false, "type_args": []}
                             },
                             "access": "Protected",
@@ -471,13 +484,14 @@
                         {
                             "identifier": {"identifier": "private_int" },
                             "type": {
-                                "rs_type": {"name": "i32", "type_args": []},
+                                "rs_type": {"name": "i32", "lifetime_args": [], "type_args": []},
                                 "cc_type": {"name": "int", "is_const": false, "type_args": []}
                             },
                             "access": "Private",
                             "offset": 64
                         }
                     ],
+                    "lifetime_params": [],
                     "size": 12,
                     "alignment": 4,
                     "copy_constructor": {
@@ -513,6 +527,7 @@
                         type_: MappedType {
                             rs_type: RsType {
                                 name: "i32".to_string().into(),
+                                lifetime_args: vec![],
                                 type_args: vec![],
                                 decl_id: None,
                             },
@@ -532,6 +547,7 @@
                         type_: MappedType {
                             rs_type: RsType {
                                 name: "i32".to_string().into(),
+                                lifetime_args: vec![],
                                 type_args: vec![],
                                 decl_id: None,
                             },
@@ -551,6 +567,7 @@
                         type_: MappedType {
                             rs_type: RsType {
                                 name: "i32".to_string().into(),
+                                lifetime_args: vec![],
                                 type_args: vec![],
                                 decl_id: None,
                             },
@@ -565,6 +582,7 @@
                         offset: 64,
                     },
                 ],
+                lifetime_params: vec![],
                 size: 12,
                 alignment: 4,
                 copy_constructor: SpecialMemberFunc {
@@ -599,8 +617,8 @@
                         {
                             "identifier": {"identifier": "ptr" },
                             "type": {
-                                "rs_type": {"name": "*mut", "type_args": [
-                                    {"name": "SomeStruct", "type_args": [], "decl_id": 42}
+                                "rs_type": {"name": "*mut", "lifetime_args": [], "type_args": [
+                                    {"name": "SomeStruct", "lifetime_args": [], "type_args": [], "decl_id": 42}
                                 ]},
                                 "cc_type": { "name": "*", "is_const": false, "type_args": [
                                     {
@@ -615,6 +633,7 @@
                             "offset": 0
                         }
                     ],
+                    "lifetime_params": [],
                     "size": 8,
                     "alignment": 8,
                     "copy_constructor": {
@@ -650,8 +669,10 @@
                         rs_type: RsType {
                             name: "*mut".to_string().into(),
                             decl_id: None,
+                            lifetime_args: vec![],
                             type_args: vec![RsType {
                                 name: "SomeStruct".to_string().into(),
+                                lifetime_args: vec![],
                                 type_args: vec![],
                                 decl_id: Some(DeclId(42)),
                             }],
@@ -671,6 +692,7 @@
                     access: AccessSpecifier::Public,
                     offset: 0,
                 }],
+                lifetime_params: vec![],
                 size: 8,
                 alignment: 8,
                 move_constructor: SpecialMemberFunc {
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index c0dd145..c178e73 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -41,6 +41,7 @@
             doc_comment: None,
             return_type: ir_int(),
             params: vec![ir_int_param("a"), ir_int_param("b")],
+            lifetime_params: vec![],
             is_inline: false,
             member_func_metadata: None,
         })],
diff --git a/rs_bindings_from_cc/ir_test.cc b/rs_bindings_from_cc/ir_test.cc
index 12cea99..e5264cf 100644
--- a/rs_bindings_from_cc/ir_test.cc
+++ b/rs_bindings_from_cc/ir_test.cc
@@ -29,7 +29,8 @@
   nlohmann::json expected = nlohmann::json::parse(R"j({
       "rs_type": {
           "name": "CompoundRs",
-          "type_args": [{"name": "i32", "type_args": []}]
+          "lifetime_args": [],
+          "type_args": [{"name": "i32", "lifetime_args": [], "type_args": []}]
       },
       "cc_type": {
           "name": "CompoundCc",
@@ -49,7 +50,7 @@
 
 TEST(IrTest, TypeWithDeclIdToJson) {
   nlohmann::json expected = nlohmann::json::parse(R"j({
-      "rs_type": {"type_args": [], "decl_id": 42},
+      "rs_type": {"lifetime_args": [], "type_args": [], "decl_id": 42},
       "cc_type": {
         "is_const": false,
         "type_args": [],
@@ -75,7 +76,7 @@
                     {
                         "identifier": { "identifier": "public_int" },
                         "type": {
-                            "rs_type": { "name": "i32", "type_args": [] },
+                            "rs_type": { "name": "i32", "lifetime_args": [], "type_args": [] },
                             "cc_type": {
                                 "is_const": false,
                                 "name": "int",
@@ -88,7 +89,7 @@
                     {
                         "identifier": { "identifier": "protected_int" },
                         "type": {
-                            "rs_type": { "name": "i32", "type_args": [] },
+                            "rs_type": { "name": "i32", "lifetime_args": [], "type_args": [] },
                             "cc_type": {
                                 "is_const": false,
                                 "name": "int",
@@ -101,7 +102,7 @@
                     {
                         "identifier": { "identifier": "private_int" },
                         "type": {
-                            "rs_type": { "name": "i32", "type_args": [] },
+                            "rs_type": { "name": "i32", "lifetime_args": [], "type_args": [] },
                             "cc_type": {
                                 "is_const": false,
                                 "name": "int",
@@ -112,6 +113,7 @@
                         "offset": 64
                     }
                     ],
+                    "lifetime_params": [],
                     "size": 12,
                     "alignment": 4,
                     "copy_constructor": {
diff --git a/rs_bindings_from_cc/ir_testing.rs b/rs_bindings_from_cc/ir_testing.rs
index cfb512b..6541a32 100644
--- a/rs_bindings_from_cc/ir_testing.rs
+++ b/rs_bindings_from_cc/ir_testing.rs
@@ -52,7 +52,12 @@
 /// Creates a simple type instance for `int`/`i32`
 pub fn ir_int() -> MappedType {
     MappedType {
-        rs_type: RsType { name: "i32".to_string().into(), type_args: vec![], decl_id: None },
+        rs_type: RsType {
+            name: "i32".to_string().into(),
+            lifetime_args: vec![],
+            type_args: vec![],
+            decl_id: None,
+        },
         cc_type: CcType {
             name: "int".to_string().into(),
             type_args: vec![],
@@ -64,7 +69,12 @@
 
 pub fn ir_type(decl_id: usize) -> MappedType {
     MappedType {
-        rs_type: RsType { name: None, type_args: vec![], decl_id: Some(DeclId(decl_id)) },
+        rs_type: RsType {
+            name: None,
+            lifetime_args: vec![],
+            type_args: vec![],
+            decl_id: Some(DeclId(decl_id)),
+        },
         cc_type: CcType {
             name: None,
             type_args: vec![],
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 2982a75..de9ada7 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -725,6 +725,7 @@
             doc_comment: None,
             return_type: ir_int(),
             params: vec![ir_int_param("a"), ir_int_param("b")],
+            lifetime_params: vec![],
             is_inline: false,
             member_func_metadata: None,
         })])?;
@@ -765,6 +766,7 @@
                 doc_comment: None,
                 return_type: ir_int(),
                 params: vec![ir_int_param("a"), ir_int_param("b")],
+                lifetime_params: vec![],
                 is_inline: true,
                 member_func_metadata: None,
             })],
@@ -883,6 +885,7 @@
                     offset: 64,
                 },
             ],
+            lifetime_params: vec![],
             size: 12,
             alignment: 4,
             copy_constructor: ir_public_trivial_special(),
@@ -1003,8 +1006,10 @@
                 rs_type: RsType {
                     name: "*mut".to_string().into(),
                     decl_id: None,
+                    lifetime_args: vec![],
                     type_args: vec![RsType {
                         name: "i32".to_string().into(),
+                        lifetime_args: vec![],
                         type_args: vec![],
                         decl_id: None,
                     }],
@@ -1027,11 +1032,14 @@
                     rs_type: RsType {
                         name: "*const".to_string().into(),
                         decl_id: None,
+                        lifetime_args: vec![],
                         type_args: vec![RsType {
                             name: "*mut".to_string().into(),
                             decl_id: None,
+                            lifetime_args: vec![],
                             type_args: vec![RsType {
                                 name: "i32".to_string().into(),
+                                lifetime_args: vec![],
                                 type_args: vec![],
                                 decl_id: None,
                             }],
@@ -1055,6 +1063,7 @@
                     },
                 },
             }],
+            lifetime_params: vec![],
             is_inline: true,
             member_func_metadata: None,
         })])?;
@@ -1127,6 +1136,7 @@
             doc_comment: Some("Doc Comment".to_string()),
             return_type: ir_int(),
             params: vec![],
+            lifetime_params: vec![],
             is_inline: true,
             member_func_metadata: None,
         })])?;
@@ -1155,6 +1165,7 @@
                 access: AccessSpecifier::Public,
                 offset: 0,
             }],
+            lifetime_params: vec![],
             copy_constructor: ir_public_trivial_special(),
             move_constructor: ir_public_trivial_special(),
             destructor: ir_public_trivial_special(),