Skip to content
Open

WIP #1453

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 53 additions & 13 deletions rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ pub enum BridgeRsTypeKind {
abi_rust: Rc<str>,
abi_cpp: Rc<str>,
generic_types: Rc<[RsTypeKind]>,
lifetimes: Rc<[Lifetime]>,
},
ProtoMessageBridge {
rust_name: Rc<str>,
Expand Down Expand Up @@ -824,6 +825,7 @@ impl BridgeRsTypeKind {
.iter()
.map(|template_arg| db.rs_type_kind(template_arg.clone()))
.collect::<Result<Rc<[RsTypeKind]>>>()?,
lifetimes: lifetimes.into(),
}
}
BridgeType::StdOptional(t) => {
Expand Down Expand Up @@ -962,7 +964,7 @@ impl RsTypeKind {
RsTypeKind::new_type_alias(db, type_alias, options, lifetimes)
}
Item::ExistingRustType(existing_rust_type) => {
RsTypeKind::new_existing_rust_type(db, existing_rust_type)
RsTypeKind::new_existing_rust_type(db, existing_rust_type, options, template_args)
}
other_item => bail!("Item does not define a type: {other_item:?}"),
}
Expand Down Expand Up @@ -1090,19 +1092,22 @@ impl RsTypeKind {
fn new_existing_rust_type<'db>(
db: impl Deref<Target = BindingsGenerator<'db>> + Copy,
existing_rust_type: Rc<ExistingRustType>,
options: &LifetimeOptions,
template_args: &Option<Rc<[CcType]>>,
) -> Result<Self> {
if existing_rust_type.rs_name() == SLICE_REF_NAME_RS {
let [template_arg] = &existing_rust_type.template_args() else {
bail!(
"SliceRef has {} template parameters, expected 1",
existing_rust_type.template_args().len()
);
};
let TemplateArg::Type(inner_cc_type) = template_arg else {
let inner_cc_type = if let Some(args) = template_args
&& let Some(arg) = args.first()
{
arg.clone()
} else if let [TemplateArg::Type(ty)] = &existing_rust_type.template_args()[..] {
ty.clone()
} else {
bail!("SliceRef should have a type as its singular template argument");
};

let inner_rs_type_kind = db.rs_type_kind(inner_cc_type.clone())?;
let inner_rs_type_kind =
db.rs_type_kind_with_lifetime_elision(inner_cc_type.clone(), *options)?;
ensure!(
inner_rs_type_kind.allowed_behind_multi_element_ptr(),
"SliceRef pointee type is not allowed behind a multi element pointer: {}",
Expand Down Expand Up @@ -2100,21 +2105,22 @@ impl RsTypeKind {
}
RsTypeKind::BridgeType { bridge_type, original_type } => {
match bridge_type {
BridgeRsTypeKind::Bridge { rust_name, generic_types, .. } => {
BridgeRsTypeKind::Bridge { rust_name, generic_types, lifetimes, .. } => {
let path = fully_qualify_type(
db,
ir::Item::Record(original_type.clone()),
rust_name,
);

// If there are no generic types, then we're done.
if generic_types.is_empty() {
// If there are no generic types and no lifetimes, then we're done.
if generic_types.is_empty() && lifetimes.is_empty() {
return path;
}

let lifetime_tokens = lifetimes.iter().map(|l| quote! { #l });
let generic_types_tokens =
generic_types.iter().map(|t| t.to_token_stream(db));
quote! { #path<#(#generic_types_tokens),*> }
quote! { #path<#(#lifetime_tokens,)* #(#generic_types_tokens),*> }
}
BridgeRsTypeKind::ProtoMessageBridge { rust_name } => {
fully_qualify_type(db, ir::Item::Record(original_type.clone()), rust_name)
Expand Down Expand Up @@ -2158,6 +2164,38 @@ impl RsTypeKind {
RsTypeKind::ExistingRustType { rust_type, .. } => rust_type.parse().expect("ExistingRustType.rust_type should parse as a TokenStream because it was constructed from one."),
}
}

pub fn to_token_stream_without_lifetimes<'a>(
&self,
db: impl Deref<Target = BindingsGenerator<'a>> + Copy,
) -> TokenStream {
match self {
RsTypeKind::Record { record, crate_path, uniform_repr_template_type, .. } => {
if let Some(generic_monomorphization) = uniform_repr_template_type {
return generic_monomorphization.to_token_stream(&db);
}
let ident = make_rs_ident(record.rs_name().as_str());
quote! { #crate_path #ident }
}
RsTypeKind::TypeAlias { type_alias, crate_path, lifetimes, .. } => {
let mut ident = make_rs_ident(type_alias.rs_name().as_str());
let mut crate_path = crate_path.clone();
if !lifetimes.is_empty()
&& let RsTypeKind::Record { record, .. } = self.unalias()
&& record.template_specialization().as_ref().is_some_and(|ts| {
matches!(ts.kind(), TemplateSpecializationKind::StdStringView)
})
{
ident = make_rs_ident("string_view");
let mut new_crate_path: CratePath = (*crate_path).clone();
new_crate_path.namespace_qualifier.namespaces.pop();
crate_path = Rc::new(new_crate_path);
};
quote! { #crate_path #ident }
}
_ => self.to_token_stream(db),
}
}
}

/// Take a user defined path, like `foo` or `::bar`, and convert it to
Expand Down Expand Up @@ -2327,6 +2365,8 @@ mod tests {
false,
false,
)),
&LifetimeOptions::default(),
&None,
)
.expect("Should succeed because all fallible operations come from BindingsGenerated, which EmptyDatabase cannot successfully deref to (it panics).")
}
Expand Down
10 changes: 5 additions & 5 deletions rs_bindings_from_cc/generate_bindings/generate_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2295,9 +2295,10 @@ pub fn generate_function(
quote! { #record_name }
} else {
// If the trait is being implemented for a different record than its enclosing one
// (e.g. for conversion operators) retrieve the fully qualified path.
// (e.g. for conversion operators) retrieve the fully qualified path without lifetime
// arguments, since `#trait_record_param_tokens` will be appended when generating the trait impl.
let t = db.rs_type_kind(trait_record.into())?;
t.to_token_stream(db)
t.to_token_stream_without_lifetimes(db)
};

let mut extra_body = if let Some(name) = associated_return_type {
Expand Down Expand Up @@ -2803,9 +2804,6 @@ fn function_signature(
.filter(|lifetime| !parent_lifetimes.contains(lifetime.0.as_ref()))
.collect();

let mut lifetimes_including_impl: Vec<Lifetime> =
unique_lifetimes(&*param_types, func.lifetime_inputs());

let mut lifetime_inputs_and_parents = func.lifetime_inputs().to_vec();
for lifetime in parent_lifetimes {
lifetime_inputs_and_parents.push(intern!(db.interner(), "{lifetime}"));
Expand All @@ -2816,6 +2814,8 @@ fn function_signature(
.filter(|lifetime| !lifetime.is_elided())
.collect();

let mut lifetimes_including_impl: Vec<Lifetime> = all_lifetimes.clone();

if matches!(
impl_kind,
ImplKind::Trait {
Expand Down
27 changes: 27 additions & 0 deletions rs_bindings_from_cc/generate_bindings/generate_function_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,33 @@ fn test_impl_eq_for_free_function() -> Result<()> {
Ok(())
}

#[gtest]
fn test_impl_eq_for_free_function_with_lifetime_params() -> Result<()> {
let ir = ir_from_assumed_lifetimes_cc(
r#"
namespace ns {
struct LIFETIME_PARAMS("a") SomeView final { const char* data; };
}
bool operator==(ns::SomeView lhs, ns::SomeView rhs) {
return lhs.data == rhs.data;
}
"#,
)?;
let rs_api = generate_bindings_tokens_for_test(ir)?.rs_api;
assert_rs_matches!(
rs_api,
quote! {
impl<'a, 'rhs> PartialEq<crate::ns::SomeView<'rhs>> for crate::ns::SomeView<'a> {
#[inline(always)]
fn eq<'lhs>(&self, rhs: &crate::ns::SomeView<'rhs>) -> bool {
unsafe { crate::detail::__rust_thunk___ZeqN2ns8SomeViewES0_(&mut self.clone(), &mut rhs.clone()) }
}
}
}
);
Ok(())
}

#[gtest]
fn test_impl_eq_ne_for_member_function() -> Result<()> {
let ir = ir_from_assumed_lifetimes_cc(
Expand Down
40 changes: 23 additions & 17 deletions rs_bindings_from_cc/generate_bindings/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ use database::code_snippet::{
use database::db::{BindingsGenerator, CodegenFunctions, Interner};
use database::intern;
use database::rs_snippet::{
BackingType, BridgeRsTypeKind, Callable, CustomizeMethodsKind, FnTrait, LifetimeOptions,
Mutability, PassingConvention, RsTypeKind, RustPtrKind, UniformReprTemplateType, UnsafeReason,
BackingType, BridgeRsTypeKind, Callable, CustomizeMethodsKind, FnTrait, Lifetime,
LifetimeOptions, Mutability, PassingConvention, RsTypeKind, RustPtrKind,
UniformReprTemplateType, UnsafeReason,
};
use dyn_format::Format;
use error_report::{bail, ErrorReporting, ReportFatalError};
Expand Down Expand Up @@ -1158,21 +1159,26 @@ fn all_static_lifetimes_internal(t: Rc<RsTypeKind>) -> Rc<RsTypeKind> {
RsTypeKind::Primitive(_) => t,
RsTypeKind::BridgeType { bridge_type, original_type } => Rc::new(RsTypeKind::BridgeType {
bridge_type: match bridge_type {
BridgeRsTypeKind::Bridge { rust_name, abi_rust, abi_cpp, generic_types } => {
BridgeRsTypeKind::Bridge {
rust_name: rust_name.clone(),
abi_rust: abi_rust.clone(),
abi_cpp: abi_cpp.clone(),
generic_types: generic_types
.iter()
.map(|generic_type| {
all_static_lifetimes_internal(Rc::new(generic_type.clone()))
.as_ref()
.clone()
})
.collect(),
}
}
BridgeRsTypeKind::Bridge {
rust_name,
abi_rust,
abi_cpp,
generic_types,
lifetimes,
} => BridgeRsTypeKind::Bridge {
rust_name: rust_name.clone(),
abi_rust: abi_rust.clone(),
abi_cpp: abi_cpp.clone(),
generic_types: generic_types
.iter()
.map(|generic_type| {
all_static_lifetimes_internal(Rc::new(generic_type.clone()))
.as_ref()
.clone()
})
.collect(),
lifetimes: lifetimes.iter().map(|_| Lifetime::new("static")).collect(),
},
BridgeRsTypeKind::ProtoMessageBridge { .. } => bridge_type.clone(),
BridgeRsTypeKind::StdOptional(element_type) => BridgeRsTypeKind::StdOptional(
all_static_lifetimes_internal(element_type.clone()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,21 @@ fn decl_lifetime_arity_impl(
// explicitly only need to check for StdStringView here (and not the more general
// rc.is_string_view()).
Item::Record(rc) => {
if let Some(ts) = &rc.template_specialization()
&& matches!(
if rc.is_string_view() {
return Ok(0);
}
let is_bridged_lifetime_arg = (if let Some(ts) = &rc.template_specialization() {
matches!(
ts.kind(),
ir::TemplateSpecializationKind::StdStringView
| ir::TemplateSpecializationKind::AbslSpan { .. }
| ir::TemplateSpecializationKind::C9Co { .. }
)
{
} else {
false
}) || (rc.bridge_type().is_some()
&& rc.cc_name().as_str().contains("string_view"));
if is_bridged_lifetime_arg {
Ok(1)
} else {
record_lifetime_arity(db, rc)
Expand All @@ -136,8 +143,12 @@ fn decl_lifetime_arity_impl(
bail!("Incomplete records unhandled for lifetimes: {:?}", item.cc_name_as_str())
}
Item::Enum(_ec) => bail!("Enums unhandled for lifetimes: {:?}", item.cc_name_as_str()),
Item::ExistingRustType(_ec) => {
bail!("Existing Rust types unhandled for lifetimes: {:?}", item.cc_name_as_str())
Item::ExistingRustType(ec) => {
if ec.rs_name() == "SliceRef" || ec.rs_name() == "StrRef" {
Ok(1)
} else {
bail!("Existing Rust types unhandled for lifetimes: {:?}", item.cc_name_as_str())
}
}
Item::Func(_) => {
bail!("Unexpected function found in type position: {:?}", item.cc_name_as_str())
Expand Down Expand Up @@ -336,9 +347,13 @@ impl<'a, 'db> LifetimeDefaults<'a, 'db> {
ty: &CcType,
) -> Result<LifetimeResult> {
match ty.variant() {
CcTypeVariant::Decl { id, .. } if self.decl_binds_lifetimes(id)? => {
CcTypeVariant::Decl { id, template_args } if self.decl_binds_lifetimes(id)? => {
let mut new_ty = ty.clone();
if let Some(type_arg) = self.type_arg_from_decl_id(*id) {
let type_arg = template_args
.as_ref()
.and_then(|args| args.first().cloned())
.or_else(|| self.type_arg_from_decl_id(*id));
if let Some(type_arg) = type_arg {
let LifetimeResult { ty: type_arg, .. } =
self.add_lifetime_to_input_type(false, name_hint, new_bindings, &type_arg)?;
*new_ty.variant_mut() =
Expand Down Expand Up @@ -444,7 +459,14 @@ impl<'a, 'db> LifetimeDefaults<'a, 'db> {
*new_ty.explicit_lifetimes_mut() = lifetime_hint.clone();
}
}
if let Some(elt) = self.type_arg_from_decl_id(*id) {
let type_arg = match ty.variant() {
CcTypeVariant::Decl { template_args, .. } => template_args
.as_ref()
.and_then(|args| args.first().cloned())
.or_else(|| self.type_arg_from_decl_id(*id)),
_ => self.type_arg_from_decl_id(*id),
};
if let Some(elt) = type_arg {
let elt_lowered =
self.add_lifetime_to_output_type(lifetime_hint, new_bindings, &elt)?;
*new_ty.variant_mut() =
Expand Down