Separate out generate_func's API function name/impl production to a new function.
On top of that change, a couple of minor changes:
* (To allow that to happen at all) moves `TraitName`, `ImplKind` to the top level.
* Regenerates the quoted RsTypeKind where it's used, rather than passing in or returning param_types as another parameter.
(This was an aesthetic preference, but I think it's the right call.)
---
Changes I deliberately did not make: One could imagine that e.g. `api_func_shape` would, instead of being passed in the param types, actually return them (along with idents etc.). But this is mixing things up: the parameter types are actually for the *thunk*, and only with further processing do we get the parameter list for the API function.
The return value of `api_func_shape` is not and can't be (without much more refactoring) an actual AST. This is more like a "template"/"shape" that information that the thunk is used to fill out. As such, I've not done anything misleading like include parameter names/types, and I've stuck to naming it "shape" and the like.
PiperOrigin-RevId: 438538020
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index f8192f0..43a254b 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -219,133 +219,120 @@
})
}
-/// Generates Rust source code for a given `Func`.
+/// The name of a trait, with extra entries for specially-understood traits and
+/// families of traits.
+enum TraitName {
+ /// The constructor trait for !Unpin types. e.g. `CtorNew(quote! { ()
+ /// })` is the default constructor.
+ CtorNew(TokenStream),
+ /// An Unpin constructor trait, e.g. From or Clone.
+ UnpinConstructor(TokenStream),
+ /// Any other trait, e.g. Eq.
+ Other(TokenStream),
+}
+impl quote::ToTokens for TraitName {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ match self {
+ Self::UnpinConstructor(t) | Self::Other(t) => t.to_tokens(tokens),
+ Self::CtorNew(args) => quote! { ctor::CtorNew < #args > }.to_tokens(tokens),
+ }
+ }
+}
+
+/// The kind of the `impl` block the function needs to be generated in.
+enum ImplKind {
+ /// Used for free functions for which we don't want the `impl` block.
+ None { is_unsafe: bool },
+ /// Used for inherent methods for which we need an `impl SomeStruct { ... }`
+ /// block.
+ Struct {
+ /// For example, `SomeStruct`. Retrieved from
+ /// `func.member_func_metadata`.
+ record_name: Ident,
+ is_unsafe: bool,
+ /// Whether to format the first parameter as "self" (e.g. `__this:
+ /// &mut T` -> `&mut self`)
+ format_first_param_as_self: bool,
+ },
+ /// Used for trait methods for which we need an `impl TraitName for
+ /// SomeStruct { ... }` block.
+ Trait {
+ /// For example, `SomeStruct`.
+ /// Note that `record_name` might *not* be from
+ /// `func.member_func_metadata`.
+ record_name: Ident,
+ /// For example, `quote!{ From<i32> }`.
+ trait_name: TraitName,
+
+ /// 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,
+ /// Whether to format the first parameter as "self" (e.g. `__this:
+ /// &mut T` -> `&mut self`)
+ format_first_param_as_self: bool,
+ },
+}
+impl ImplKind {
+ fn new_trait(
+ trait_name: TraitName,
+ record_name: Ident,
+ format_first_param_as_self: bool,
+ ) -> Self {
+ 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,
+ 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,
+ }
+ }
+ fn format_first_param_as_self(&self) -> bool {
+ matches!(
+ self,
+ Self::Trait { format_first_param_as_self: true, .. }
+ | Self::Struct { format_first_param_as_self: true, .. }
+ )
+ }
+ /// Returns whether the function is defined as `unsafe fn ...`.
+ fn is_unsafe(&self) -> bool {
+ matches!(self, Self::None { is_unsafe: true, .. } | Self::Struct { is_unsafe: true, .. })
+ }
+}
+
+/// Returns the shape of the generated Rust API for a given function definition.
///
-/// Return values:
+/// Returns:
///
/// * `Err(_)`: something went wrong importing this function.
/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
/// destructor might be mapped to no `Drop` impl at all.)
-/// * `Ok((rs_api, rs_thunk, function_id))`: The Rust function definition,
-/// thunk FFI definition, and function ID.
-fn generate_func(func: &Func, ir: &IR) -> Result<Option<(RsSnippet, RsSnippet, FunctionId)>> {
- let lifetime_to_name = HashMap::<LifetimeId, String>::from_iter(
- func.lifetime_params.iter().map(|l| (l.id, l.name.clone())),
- );
- let param_type_kinds = func
- .params
- .iter()
- .map(|p| {
- RsTypeKind::new(&p.type_.rs_type, ir).with_context(|| {
- format!("Failed to process type of parameter {:?} on {:?}", p, func)
- })
- })
- .collect::<Result<Vec<_>>>()?;
- let param_types = param_type_kinds
- .iter()
- .map(|t| {
- t.format(ir, &lifetime_to_name)
- .with_context(|| format!("Failed to format parameter type {:?} on {:?}", t, func))
- })
- .collect::<Result<Vec<_>>>()?;
- let has_pointer_params =
- param_type_kinds.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }));
-
+/// * `Ok((func_name, impl_kind))`: The function name and ImplKind.
+fn api_func_shape(
+ func: &Func,
+ ir: &IR,
+ param_type_kinds: &[RsTypeKind],
+ lifetime_to_name: &HashMap<LifetimeId, String>,
+) -> Result<Option<(Ident, ImplKind)>> {
let maybe_record: Option<&Record> =
func.member_func_metadata.as_ref().map(|meta| meta.find_record(ir)).transpose()?;
-
- // Find the `func_name` and `impl_kind` of the API function to generate.
- enum TraitName {
- /// The constructor trait for !Unpin types. e.g. `CtorNew(quote! { ()
- /// })` is the default constructor.
- CtorNew(TokenStream),
- /// An Unpin constructor trait, e.g. From or Clone.
- UnpinConstructor(TokenStream),
- /// Any other trait, e.g. Eq.
- Other(TokenStream),
- }
- impl quote::ToTokens for TraitName {
- fn to_tokens(&self, tokens: &mut TokenStream) {
- match self {
- Self::UnpinConstructor(t) | Self::Other(t) => t.to_tokens(tokens),
- Self::CtorNew(args) => quote! { ctor::CtorNew < #args > }.to_tokens(tokens),
- }
- }
- }
- enum ImplKind {
- /// No `impl` needed
- None { is_unsafe: bool },
- // For example, `impl SomeStruct { ... }` (`SomeStruct` based on
- // func.member_func_metadata.)
- Struct {
- /// For example, `SomeStruct`.
- record_name: Ident,
- is_unsafe: bool,
- /// Whether to format the first parameter as "self" (e.g. `__this:
- /// &mut T` -> `&mut self`)
- format_first_param_as_self: bool,
- },
- Trait {
- /// For example, `SomeStruct`.
- /// Note that `record_name` might *not* be from
- /// `func.member_func_metadata`.
- record_name: Ident,
- /// For example, `quote!{ From<i32> }`.
- trait_name: TraitName,
-
- /// 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,
- /// Whether to format the first parameter as "self" (e.g. `__this:
- /// &mut T` -> `&mut self`)
- format_first_param_as_self: bool,
- },
- }
- impl ImplKind {
- fn new_trait(
- trait_name: TraitName,
- record_name: Ident,
- format_first_param_as_self: bool,
- ) -> Self {
- 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,
- 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,
- }
- }
- fn format_first_param_as_self(&self) -> bool {
- matches!(
- self,
- Self::Trait { format_first_param_as_self: true, .. }
- | Self::Struct { format_first_param_as_self: true, .. }
- )
- }
- /// Returns whether the function is defined as `unsafe fn ...`.
- fn is_unsafe(&self) -> bool {
- matches!(
- self,
- Self::None { is_unsafe: true, .. } | Self::Struct { is_unsafe: true, .. }
- )
- }
- }
- let mut impl_kind: ImplKind;
+ let has_pointer_params =
+ param_type_kinds.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }));
+ let impl_kind: ImplKind;
let func_name: syn::Ident;
match &func.name {
UnqualifiedIdentifier::Operator(op) if op.name == "==" => {
@@ -449,7 +436,7 @@
match func.params.len() {
0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
2 => {
- let param_type = ¶m_types[1];
+ let param_type = param_type_kinds[1].format(ir, lifetime_to_name)?;
impl_kind = ImplKind::new_generic_trait(
TraitName::CtorNew(param_type.clone()),
record_name,
@@ -488,7 +475,7 @@
func_name = make_rs_ident("clone");
}
} else if !instance_method_metadata.is_explicit_ctor {
- let param_type = ¶m_types[1];
+ let param_type = param_type_kinds[1].format(ir, lifetime_to_name)?;
impl_kind = ImplKind::new_generic_trait(
TraitName::UnpinConstructor(quote! {From< #param_type >}),
record_name,
@@ -507,12 +494,51 @@
}
}
}
+ Ok(Some((func_name, impl_kind)))
+}
+
+/// Generates Rust source code for a given `Func`.
+///
+/// Returns:
+///
+/// * `Err(_)`: couldn't import the function, emit an `UnsupportedItem`.
+/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
+/// destructor might be mapped to no `Drop` impl at all.)
+/// * `Ok((rs_api, rs_thunk, function_id))`: The Rust function definition,
+/// thunk FFI definition, and function ID.
+fn generate_func(func: &Func, ir: &IR) -> Result<Option<(RsSnippet, RsSnippet, FunctionId)>> {
+ let lifetime_to_name = HashMap::<LifetimeId, String>::from_iter(
+ func.lifetime_params.iter().map(|l| (l.id, l.name.clone())),
+ );
+ let param_type_kinds = func
+ .params
+ .iter()
+ .map(|p| {
+ RsTypeKind::new(&p.type_.rs_type, ir).with_context(|| {
+ format!("Failed to process type of parameter {:?} on {:?}", p, func)
+ })
+ })
+ .collect::<Result<Vec<_>>>()?;
+
+ let (func_name, mut impl_kind) =
+ if let Some(values) = api_func_shape(func, ir, ¶m_type_kinds, &lifetime_to_name)? {
+ values
+ } else {
+ return Ok(None);
+ };
let return_type_fragment = RsTypeKind::new(&func.return_type.rs_type, ir)
.and_then(|t| t.format_as_return_type_fragment(ir, &lifetime_to_name))
.with_context(|| format!("Failed to format return type for {:?}", func))?;
let param_idents =
func.params.iter().map(|p| make_rs_ident(&p.identifier.identifier)).collect_vec();
+ let param_types = param_type_kinds
+ .iter()
+ .map(|t| {
+ t.format(ir, &lifetime_to_name)
+ .with_context(|| format!("Failed to format parameter type {:?} on {:?}", t, func))
+ })
+ .collect::<Result<Vec<_>>>()?;
let thunk_ident = thunk_ident(func);
let api_func_def = {