Automatically determine which lifetimes to declare in `impl` vs `fn`.
The problem with `declare_lifetimes` is that it's all-or-nothing -- either everything is a lifetime parameter of the `impl`, or of the `fn`. And so `declare_lifetimes: true` works for static functions, but not instance functions where _some_ lifetime parameters are on the `fn` (e.g. for `&self`), and others on the trait.
To do this requires structuring the trait names: rather than an opaque bag of tokens, it is a name + parameters. This feels like a good change, structure is good! And it allows us to do things like automatically determine which lifetimes must be parameters on the `impl` block. :)
PiperOrigin-RevId: 452617483
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 1dd8a67..bbde86f 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -271,15 +271,36 @@
/// The constructor trait for !Unpin types, with a list of parameter types.
/// For example, `CtorNew(vec![])` is the default constructor.
CtorNew(Vec<RsTypeKind<'ir>>),
- /// An Unpin constructor trait, e.g. From or Clone.
- UnpinConstructor(TokenStream),
+ /// An Unpin constructor trait, e.g. From or Clone, with a list of parameter
+ /// types.
+ UnpinConstructor { name: TokenStream, params: Vec<RsTypeKind<'ir>> },
/// Any other trait, e.g. Eq.
- Other { name: TokenStream, is_unsafe_fn: bool },
+ Other { name: TokenStream, params: Vec<RsTypeKind<'ir>>, is_unsafe_fn: bool },
}
+
+impl<'ir> TraitName<'ir> {
+ /// Returns the generic parameters in this trait name.
+ fn params(&self) -> impl Iterator<Item = &RsTypeKind<'ir>> {
+ match self {
+ Self::CtorNew(params)
+ | Self::UnpinConstructor { params, .. }
+ | Self::Other { params, .. } => params.iter(),
+ }
+ }
+
+ /// Returns the lifetimes used in this trait name.
+ pub fn lifetimes(&self) -> impl Iterator<Item = LifetimeId> + '_ {
+ self.params().flat_map(|p| p.lifetimes())
+ }
+}
+
impl<'ir> ToTokens for TraitName<'ir> {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
- Self::UnpinConstructor(name) | Self::Other { name, .. } => name.to_tokens(tokens),
+ Self::UnpinConstructor { name, params } | Self::Other { name, params, .. } => {
+ let params = format_generic_params(params);
+ quote! {#name #params}.to_tokens(tokens)
+ }
Self::CtorNew(arg_types) => {
let arg_types = format_tuple_except_singleton(arg_types);
quote! { ctor::CtorNew < #arg_types > }.to_tokens(tokens)
@@ -313,8 +334,6 @@
/// For example, `quote!{ From<i32> }`.
trait_name: TraitName<'ir>,
- /// Where to declare lifetimes: `impl<'b>` VS `fn foo<'b>`.
- declare_lifetimes: bool,
/// The generic params of trait `impl` (e.g. `<'b>`). These start
/// empty and only later are mutated into the correct value.
trait_generic_params: TokenStream,
@@ -332,20 +351,6 @@
ImplKind::Trait {
trait_name,
record_name,
- declare_lifetimes: false,
- trait_generic_params: quote! {},
- format_first_param_as_self,
- }
- }
- fn new_generic_trait(
- trait_name: TraitName<'ir>,
- record_name: Ident,
- format_first_param_as_self: bool,
- ) -> Self {
- ImplKind::Trait {
- trait_name,
- record_name,
- declare_lifetimes: true,
trait_generic_params: quote! {},
format_first_param_as_self,
}
@@ -399,13 +404,10 @@
RsTypeKind::Record { record: lhs_record, .. } => {
let lhs: Ident = make_rs_ident(&lhs_record.rs_name);
func_name = make_rs_ident("eq");
- // Not using `ImplKind::new_generic_trait`, because #rhs
- // should be stripped of references + because `&'a self`
- // needs to have its lifetime declared next to `fn`, not
- // next to `impl`.
impl_kind = ImplKind::new_trait(
TraitName::Other {
- name: quote! {PartialEq<#rhs>},
+ name: quote! {PartialEq},
+ params: vec![(**rhs).clone()],
is_unsafe_fn: false,
},
lhs,
@@ -457,14 +459,18 @@
}
if record.is_unpin() {
impl_kind = ImplKind::new_trait(
- TraitName::Other { name: quote! {Drop}, is_unsafe_fn: false },
+ TraitName::Other { name: quote! {Drop}, params: vec![], is_unsafe_fn: false },
make_rs_ident(&record.rs_name),
/* format_first_param_as_self= */ true,
);
func_name = make_rs_ident("drop");
} else {
impl_kind = ImplKind::new_trait(
- TraitName::Other { name: quote! {::ctor::PinnedDrop}, is_unsafe_fn: true },
+ TraitName::Other {
+ name: quote! {::ctor::PinnedDrop},
+ params: vec![],
+ is_unsafe_fn: true,
+ },
make_rs_ident(&record.rs_name),
/* format_first_param_as_self= */ true,
);
@@ -499,7 +505,7 @@
match param_types {
[] => bail!("Missing `__this` parameter in a constructor: {:?}", func),
[_this, params @ ..] => {
- impl_kind = ImplKind::new_generic_trait(
+ impl_kind = ImplKind::new_trait(
TraitName::CtorNew(params.iter().cloned().collect()),
record_name,
/* format_first_param_as_self= */ false,
@@ -511,7 +517,7 @@
0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
1 => {
impl_kind = ImplKind::new_trait(
- TraitName::UnpinConstructor(quote! {Default}),
+ TraitName::UnpinConstructor { name: quote! {Default}, params: vec![] },
record_name,
/* format_first_param_as_self= */ false,
);
@@ -524,7 +530,10 @@
return Ok(None);
} else {
impl_kind = ImplKind::new_trait(
- TraitName::UnpinConstructor(quote! {Clone}),
+ TraitName::UnpinConstructor {
+ name: quote! {Clone},
+ params: vec![],
+ },
record_name,
/* format_first_param_as_self= */ true,
);
@@ -532,8 +541,11 @@
}
} else if !instance_method_metadata.is_explicit_ctor {
let param_type = ¶m_types[1];
- impl_kind = ImplKind::new_generic_trait(
- TraitName::UnpinConstructor(quote! {From< #param_type >}),
+ impl_kind = ImplKind::new_trait(
+ TraitName::UnpinConstructor {
+ name: quote! {From},
+ params: vec![param_type.clone()],
+ },
record_name,
/* format_first_param_as_self= */ false,
);
@@ -599,7 +611,7 @@
let mut maybe_first_api_param = param_types.get(0);
if let ImplKind::Trait {
- trait_name: trait_name @ (TraitName::UnpinConstructor(..) | TraitName::CtorNew(..)),
+ trait_name: trait_name @ (TraitName::UnpinConstructor { .. } | TraitName::CtorNew(..)),
..
} = &impl_kind
{
@@ -680,7 +692,7 @@
})
}
}
- ImplKind::Trait { trait_name: TraitName::UnpinConstructor(..), .. } => {
+ ImplKind::Trait { trait_name: TraitName::UnpinConstructor { .. }, .. } => {
// SAFETY: A user-defined constructor is not guaranteed to
// initialize all the fields. To make the `assume_init()` call
// below safe, the memory is zero-initialized first. This is a
@@ -719,11 +731,14 @@
};
let fn_generic_params: TokenStream;
- if let ImplKind::Trait { declare_lifetimes: true, trait_generic_params, .. } =
- &mut impl_kind
- {
- *trait_generic_params = format_generic_params(lifetimes);
- fn_generic_params = quote! {}
+ if let ImplKind::Trait { trait_name, trait_generic_params, .. } = &mut impl_kind {
+ let trait_lifetimes: HashSet<LifetimeId> = trait_name.lifetimes().collect();
+ fn_generic_params = format_generic_params(
+ lifetimes.iter().filter(|lifetime| !trait_lifetimes.contains(&lifetime.id)),
+ );
+ *trait_generic_params = format_generic_params(
+ lifetimes.iter().filter(|lifetime| trait_lifetimes.contains(&lifetime.id)),
+ );
} else {
fn_generic_params = format_generic_params(lifetimes);
}