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(),