diff --git a/gen/src/out.rs b/gen/src/out.rs index c18fd11bd..89c37bda7 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -122,6 +122,13 @@ impl<'a> Write for Content<'a> { } } +impl<'a> Write for OutFile<'a> { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.content.borrow_mut().write(s); + Ok(()) + } +} + impl<'a> PartialEq for Content<'a> { fn eq(&self, _other: &Self) -> bool { true diff --git a/gen/src/write.rs b/gen/src/write.rs index a4bee513d..6cbbd6533 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -10,13 +10,12 @@ use crate::syntax::map::UnorderedMap as Map; use crate::syntax::namespace::Namespace; use crate::syntax::primitive::{self, PrimitiveKind}; use crate::syntax::set::UnorderedSet; -use crate::syntax::symbol::{self, Symbol}; +use crate::syntax::symbol::Symbol; use crate::syntax::trivial::{self, TrivialReason}; use crate::syntax::{ derive, mangle, Api, Doc, Enum, ExternFn, ExternType, FnKind, Lang, Pair, Signature, Struct, Trait, Type, TypeAlias, Types, Var, }; -use proc_macro2::Ident; pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec { let mut out_file = OutFile::new(header, opt, types); @@ -1409,95 +1408,110 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var) { } fn write_type(out: &mut OutFile, ty: &Type) { + write!(out, "{}", stringify_type(ty, out.types)); +} + +fn stringify_type(ty: &Type, types: &Types) -> String { + let mut s = String::new(); + write_type_to_generic_writer(&mut s, ty, types).unwrap(); + s +} + +fn write_type_to_generic_writer( + out: &mut impl std::fmt::Write, + ty: &Type, + types: &Types, +) -> std::fmt::Result { match ty { Type::Ident(ident) => match Atom::from(&ident.rust) { - Some(atom) => write_atom(out, atom), - None => write!( - out, - "{}", - out.types.resolve(ident).name.to_fully_qualified(), - ), + Some(atom) => write_atom_to_generic_writer(out, atom), + None => write!(out, "{}", types.resolve(ident).name.to_fully_qualified()), }, Type::RustBox(ty) => { - write!(out, "::rust::Box<"); - write_type(out, &ty.inner); - write!(out, ">"); + write!(out, "::rust::Box<")?; + write_type_to_generic_writer(out, &ty.inner, types)?; + write!(out, ">") } Type::RustVec(ty) => { - write!(out, "::rust::Vec<"); - write_type(out, &ty.inner); - write!(out, ">"); + write!(out, "::rust::Vec<")?; + write_type_to_generic_writer(out, &ty.inner, types)?; + write!(out, ">") } Type::UniquePtr(ptr) => { - write!(out, "::std::unique_ptr<"); - write_type(out, &ptr.inner); - write!(out, ">"); + write!(out, "::std::unique_ptr<")?; + write_type_to_generic_writer(out, &ptr.inner, types)?; + write!(out, ">") } Type::SharedPtr(ptr) => { - write!(out, "::std::shared_ptr<"); - write_type(out, &ptr.inner); - write!(out, ">"); + write!(out, "::std::shared_ptr<")?; + write_type_to_generic_writer(out, &ptr.inner, types)?; + write!(out, ">") } Type::WeakPtr(ptr) => { - write!(out, "::std::weak_ptr<"); - write_type(out, &ptr.inner); - write!(out, ">"); + write!(out, "::std::weak_ptr<")?; + write_type_to_generic_writer(out, &ptr.inner, types)?; + write!(out, ">") } Type::CxxVector(ty) => { - write!(out, "::std::vector<"); - write_type(out, &ty.inner); - write!(out, ">"); + write!(out, "::std::vector<")?; + write_type_to_generic_writer(out, &ty.inner, types)?; + write!(out, ">") } Type::Ref(r) => { - write_type_space(out, &r.inner); + write_type_space_to_generic_writer(out, &r.inner, types)?; if !r.mutable { - write!(out, "const "); + write!(out, "const ")?; } - write!(out, "&"); + write!(out, "&") } Type::Ptr(p) => { - write_type_space(out, &p.inner); + write_type_space_to_generic_writer(out, &p.inner, types)?; if !p.mutable { - write!(out, "const "); + write!(out, "const ")?; } - write!(out, "*"); + write!(out, "*") } Type::Str(_) => { - write!(out, "::rust::Str"); + write!(out, "::rust::Str") } Type::SliceRef(slice) => { - write!(out, "::rust::Slice<"); - write_type_space(out, &slice.inner); + write!(out, "::rust::Slice<")?; + write_type_space_to_generic_writer(out, &slice.inner, types)?; if slice.mutability.is_none() { - write!(out, "const"); + write!(out, "const")?; } - write!(out, ">"); + write!(out, ">") } Type::Fn(f) => { - write!(out, "::rust::Fn<"); + write!(out, "::rust::Fn<")?; match &f.ret { - Some(ret) => write_type(out, ret), - None => write!(out, "void"), + Some(ret) => write_type_to_generic_writer(out, ret, types)?, + None => write!(out, "void")?, } - write!(out, "("); + write!(out, "(")?; for (i, arg) in f.args.iter().enumerate() { if i > 0 { - write!(out, ", "); + write!(out, ", ")?; } - write_type(out, &arg.ty); + write_type_to_generic_writer(out, &arg.ty, types)?; } - write!(out, ")>"); + write!(out, ")>") } Type::Array(a) => { - write!(out, "::std::array<"); - write_type(out, &a.inner); - write!(out, ", {}>", &a.len); + write!(out, "::std::array<")?; + write_type_to_generic_writer(out, &a.inner, types)?; + write!(out, ", {}>", &a.len) } Type::Void(_) => unreachable!(), } } fn write_atom(out: &mut OutFile, atom: Atom) { + // `unwrap`, because `OutFile`'s impl of `fmt::Write` is infallible. + write_atom_to_generic_writer(out, atom).unwrap(); +} + +fn write_atom_to_generic_writer(out: &mut impl std::fmt::Write, atom: Atom) -> std::fmt::Result { match atom { Bool => write!(out, "bool"), Char => write!(out, "char"), @@ -1523,7 +1537,24 @@ fn write_type_space(out: &mut OutFile, ty: &Type) { write_space_after_type(out, ty); } +fn write_type_space_to_generic_writer( + out: &mut impl std::fmt::Write, + ty: &Type, + types: &Types, +) -> std::fmt::Result { + write_type_to_generic_writer(out, ty, types)?; + write_space_after_type_to_generic_writer(out, ty) +} + fn write_space_after_type(out: &mut OutFile, ty: &Type) { + // `unwrap`, because `OutFile`'s impl of `fmt::Write` is infallible. + write_space_after_type_to_generic_writer(out, ty).unwrap(); +} + +fn write_space_after_type_to_generic_writer( + out: &mut impl std::fmt::Write, + ty: &Type, +) -> std::fmt::Result { match ty { Type::Ident(_) | Type::RustBox(_) @@ -1536,59 +1567,11 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { | Type::SliceRef(_) | Type::Fn(_) | Type::Array(_) => write!(out, " "), - Type::Ref(_) | Type::Ptr(_) => {} + Type::Ref(_) | Type::Ptr(_) => Ok(()), Type::Void(_) => unreachable!(), } } -#[derive(Copy, Clone)] -enum UniquePtr<'a> { - Ident(&'a Ident), - CxxVector(&'a Ident), -} - -trait ToTypename { - fn to_typename(&self, types: &Types) -> String; -} - -impl ToTypename for Ident { - fn to_typename(&self, types: &Types) -> String { - types.resolve(self).name.to_fully_qualified() - } -} - -impl<'a> ToTypename for UniquePtr<'a> { - fn to_typename(&self, types: &Types) -> String { - match self { - UniquePtr::Ident(ident) => ident.to_typename(types), - UniquePtr::CxxVector(element) => { - format!("::std::vector<{}>", element.to_typename(types)) - } - } - } -} - -trait ToMangled { - fn to_mangled(&self, types: &Types) -> Symbol; -} - -impl ToMangled for Ident { - fn to_mangled(&self, types: &Types) -> Symbol { - types.resolve(self).name.to_symbol() - } -} - -impl<'a> ToMangled for UniquePtr<'a> { - fn to_mangled(&self, types: &Types) -> Symbol { - match self { - UniquePtr::Ident(ident) => ident.to_mangled(types), - UniquePtr::CxxVector(element) => { - symbol::join(&[&"std", &"vector", &element.to_mangled(types)]) - } - } - } -} - fn write_generic_instantiations(out: &mut OutFile) { if out.header { return; @@ -1624,9 +1607,8 @@ fn write_generic_instantiations(out: &mut OutFile) { } fn write_rust_box_extern(out: &mut OutFile, key: &NamedImplKey) { - let resolve = out.types.resolve(key); - let inner = resolve.name.to_fully_qualified(); - let instance = resolve.name.to_symbol(); + let inner = stringify_type(key.inner, out.types); + let instance = &key.symbol; out.pragma.dollar_in_identifier = true; @@ -1648,9 +1630,8 @@ fn write_rust_box_extern(out: &mut OutFile, key: &NamedImplKey) { } fn write_rust_vec_extern(out: &mut OutFile, key: &NamedImplKey) { - let element = key.rust; - let inner = element.to_typename(out.types); - let instance = element.to_mangled(out.types); + let inner = stringify_type(key.inner, out.types); + let instance = &key.symbol; out.include.cstddef = true; out.pragma.dollar_in_identifier = true; @@ -1698,9 +1679,8 @@ fn write_rust_vec_extern(out: &mut OutFile, key: &NamedImplKey) { } fn write_rust_box_impl(out: &mut OutFile, key: &NamedImplKey) { - let resolve = out.types.resolve(key); - let inner = resolve.name.to_fully_qualified(); - let instance = resolve.name.to_symbol(); + let inner = stringify_type(key.inner, out.types); + let instance = &key.symbol; out.pragma.dollar_in_identifier = true; @@ -1732,9 +1712,8 @@ fn write_rust_box_impl(out: &mut OutFile, key: &NamedImplKey) { } fn write_rust_vec_impl(out: &mut OutFile, key: &NamedImplKey) { - let element = key.rust; - let inner = element.to_typename(out.types); - let instance = element.to_mangled(out.types); + let inner = stringify_type(key.inner, out.types); + let instance = &key.symbol; out.include.cstddef = true; out.pragma.dollar_in_identifier = true; @@ -1821,28 +1800,25 @@ fn write_rust_vec_impl(out: &mut OutFile, key: &NamedImplKey) { } fn write_unique_ptr(out: &mut OutFile, key: &NamedImplKey) { - let ty = UniquePtr::Ident(key.rust); - write_unique_ptr_common(out, ty); + write_unique_ptr_common(out, key.inner); } // Shared by UniquePtr and UniquePtr>. -fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) { +fn write_unique_ptr_common(out: &mut OutFile, ty: &Type) { out.include.new = true; out.include.utility = true; out.pragma.dollar_in_identifier = true; out.pragma.missing_declarations = true; - let inner = ty.to_typename(out.types); - let instance = ty.to_mangled(out.types); + let inner = stringify_type(ty, out.types); + let instance = crate::syntax::mangle::type_(ty) + .expect("Earlier syntax/check.rs checks should filter out non-mangle-able types"); - let can_construct_from_value = match ty { - // Some aliases are to opaque types; some are to trivial types. We can't - // know at code generation time, so we generate both C++ and Rust side - // bindings for a "new" method anyway. But the Rust code can't be called - // for Opaque types because the 'new' method is not implemented. - UniquePtr::Ident(ident) => out.types.is_maybe_trivial(ident), - UniquePtr::CxxVector(_) => false, - }; + // Some aliases are to opaque types; some are to trivial types. We can't + // know at code generation time, so we generate both C++ and Rust side + // bindings for a "new" method anyway. But the Rust code can't be called + // for Opaque types because the 'new' method is not implemented. + let can_construct_from_value = out.types.is_maybe_trivial(ty); out.builtin.is_complete = true; writeln!( @@ -1931,10 +1907,8 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) { } fn write_shared_ptr(out: &mut OutFile, key: &NamedImplKey) { - let ident = key.rust; - let resolve = out.types.resolve(ident); - let inner = resolve.name.to_fully_qualified(); - let instance = resolve.name.to_symbol(); + let inner = stringify_type(key.inner, out.types); + let instance = &key.symbol; out.include.new = true; out.include.utility = true; @@ -1945,7 +1919,7 @@ fn write_shared_ptr(out: &mut OutFile, key: &NamedImplKey) { // know at code generation time, so we generate both C++ and Rust side // bindings for a "new" method anyway. But the Rust code can't be called for // Opaque types because the 'new' method is not implemented. - let can_construct_from_value = out.types.is_maybe_trivial(ident); + let can_construct_from_value = out.types.is_maybe_trivial(key.inner); writeln!( out, @@ -2029,9 +2003,8 @@ fn write_shared_ptr(out: &mut OutFile, key: &NamedImplKey) { } fn write_weak_ptr(out: &mut OutFile, key: &NamedImplKey) { - let resolve = out.types.resolve(key); - let inner = resolve.name.to_fully_qualified(); - let instance = resolve.name.to_symbol(); + let inner = stringify_type(key.inner, out.types); + let instance = &key.symbol; out.include.new = true; out.include.utility = true; @@ -2100,9 +2073,8 @@ fn write_weak_ptr(out: &mut OutFile, key: &NamedImplKey) { } fn write_cxx_vector(out: &mut OutFile, key: &NamedImplKey) { - let element = key.rust; - let inner = element.to_typename(out.types); - let instance = element.to_mangled(out.types); + let inner = stringify_type(key.inner, out.types); + let instance = &key.symbol; out.include.cstddef = true; out.include.utility = true; @@ -2160,7 +2132,7 @@ fn write_cxx_vector(out: &mut OutFile, key: &NamedImplKey) { ); writeln!(out, "}}"); - if out.types.is_maybe_trivial(element) { + if out.types.is_maybe_trivial(key.inner) { begin_function_definition(out); writeln!( out, @@ -2183,5 +2155,5 @@ fn write_cxx_vector(out: &mut OutFile, key: &NamedImplKey) { } out.include.memory = true; - write_unique_ptr_common(out, UniquePtr::CxxVector(element)); + write_unique_ptr_common(out, key.outer); } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index b6366b0df..977ea968a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1661,19 +1661,19 @@ fn expand_rust_box( types: &Types, conditional_impl: &ConditionalImpl, ) -> TokenStream { - let ident = key.rust; - let resolve = types.resolve(ident); - let link_prefix = format!("cxxbridge1$box${}$", resolve.name.to_symbol()); + let inner = key.inner; + let link_prefix = format!("cxxbridge1$box${}$", key.symbol); let link_alloc = format!("{}alloc", link_prefix); let link_dealloc = format!("{}dealloc", link_prefix); let link_drop = format!("{}drop", link_prefix); - let local_prefix = format_ident!("{}__box_", ident); + let local_prefix = format_ident!("{}__box_", key.symbol); let local_alloc = format_ident!("{}alloc", local_prefix); let local_dealloc = format_ident!("{}dealloc", local_prefix); let local_drop = format_ident!("{}drop", local_prefix); - let (impl_generics, ty_generics) = generics::split_for_impl(key, conditional_impl, resolve); + let (impl_generics, ty_generics) = + generics::get_impl_and_ty_generics(inner, conditional_impl, types); let cfg = conditional_impl.cfg.into_attr(); let begin_span = conditional_impl @@ -1683,18 +1683,19 @@ fn expand_rust_box( .explicit_impl .map_or(key.end_span, |explicit| explicit.brace_token.span.join()); let unsafe_token = format_ident!("unsafe", span = begin_span); - let prevent_unwind_drop_label = format!("::{} as Drop>::drop", ident); + let prevent_unwind_drop_label = quote! { #inner }.to_string(); quote_spanned! {end_span=> #cfg #[automatically_derived] #[doc(hidden)] - #unsafe_token impl #impl_generics ::cxx::private::ImplBox for #ident #ty_generics {} + #unsafe_token impl #impl_generics ::cxx::private::ImplBox for #inner #ty_generics {} #cfg #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_alloc)] - unsafe extern "C" fn #local_alloc #impl_generics() -> *mut ::cxx::core::mem::MaybeUninit<#ident #ty_generics> { + unsafe extern "C" fn #local_alloc #impl_generics() + -> *mut ::cxx::core::mem::MaybeUninit<#inner> { // No prevent_unwind: the global allocator is not allowed to panic. // // TODO: replace with Box::new_uninit when stable. @@ -1706,7 +1707,7 @@ fn expand_rust_box( #cfg #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_dealloc)] - unsafe extern "C" fn #local_dealloc #impl_generics(ptr: *mut ::cxx::core::mem::MaybeUninit<#ident #ty_generics>) { + unsafe extern "C" fn #local_dealloc #impl_generics(ptr: *mut ::cxx::core::mem::MaybeUninit<#inner>) { // No prevent_unwind: the global allocator is not allowed to panic. let _ = unsafe { ::cxx::alloc::boxed::Box::from_raw(ptr) }; } @@ -1714,7 +1715,7 @@ fn expand_rust_box( #cfg #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_drop)] - unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::alloc::boxed::Box<#ident #ty_generics>) { + unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::alloc::boxed::Box<#inner>) { let __fn = ::cxx::core::concat!("<", ::cxx::core::module_path!(), #prevent_unwind_drop_label); ::cxx::private::prevent_unwind(__fn, || unsafe { ::cxx::core::ptr::drop_in_place(this) }); } @@ -1726,9 +1727,8 @@ fn expand_rust_vec( types: &Types, conditional_impl: &ConditionalImpl, ) -> TokenStream { - let elem = key.rust; - let resolve = types.resolve(elem); - let link_prefix = format!("cxxbridge1$rust_vec${}$", resolve.name.to_symbol()); + let inner = key.inner; + let link_prefix = format!("cxxbridge1$rust_vec${}$", key.symbol); let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); @@ -1738,7 +1738,7 @@ fn expand_rust_vec( let link_set_len = format!("{}set_len", link_prefix); let link_truncate = format!("{}truncate", link_prefix); - let local_prefix = format_ident!("{}__vec_", elem); + let local_prefix = format_ident!("{}__vec_", key.symbol); let local_new = format_ident!("{}new", local_prefix); let local_drop = format_ident!("{}drop", local_prefix); let local_len = format_ident!("{}len", local_prefix); @@ -1748,7 +1748,8 @@ fn expand_rust_vec( let local_set_len = format_ident!("{}set_len", local_prefix); let local_truncate = format_ident!("{}truncate", local_prefix); - let (impl_generics, ty_generics) = generics::split_for_impl(key, conditional_impl, resolve); + let (impl_generics, ty_generics) = + generics::get_impl_and_ty_generics(inner, conditional_impl, types); let cfg = conditional_impl.cfg.into_attr(); let begin_span = conditional_impl @@ -1758,18 +1759,18 @@ fn expand_rust_vec( .explicit_impl .map_or(key.end_span, |explicit| explicit.brace_token.span.join()); let unsafe_token = format_ident!("unsafe", span = begin_span); - let prevent_unwind_drop_label = format!("::{} as Drop>::drop", elem); + let prevent_unwind_drop_label = quote! { #inner }.to_string(); quote_spanned! {end_span=> #cfg #[automatically_derived] #[doc(hidden)] - #unsafe_token impl #impl_generics ::cxx::private::ImplVec for #elem #ty_generics {} + #unsafe_token impl #impl_generics ::cxx::private::ImplVec for #inner #ty_generics {} #cfg #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_new)] - unsafe extern "C" fn #local_new #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>) { + unsafe extern "C" fn #local_new #impl_generics(this: *mut ::cxx::private::RustVec<#inner>) { // No prevent_unwind: cannot panic. unsafe { ::cxx::core::ptr::write(this, ::cxx::private::RustVec::new()); @@ -1779,7 +1780,7 @@ fn expand_rust_vec( #cfg #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_drop)] - unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>) { + unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::private::RustVec<#inner>) { let __fn = ::cxx::core::concat!("<", ::cxx::core::module_path!(), #prevent_unwind_drop_label); ::cxx::private::prevent_unwind( __fn, @@ -1790,7 +1791,7 @@ fn expand_rust_vec( #cfg #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_len)] - unsafe extern "C" fn #local_len #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> ::cxx::core::primitive::usize { + unsafe extern "C" fn #local_len #impl_generics(this: *const ::cxx::private::RustVec<#inner>) -> ::cxx::core::primitive::usize { // No prevent_unwind: cannot panic. unsafe { (*this).len() } } @@ -1798,7 +1799,7 @@ fn expand_rust_vec( #cfg #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_capacity)] - unsafe extern "C" fn #local_capacity #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> ::cxx::core::primitive::usize { + unsafe extern "C" fn #local_capacity #impl_generics(this: *const ::cxx::private::RustVec<#inner>) -> ::cxx::core::primitive::usize { // No prevent_unwind: cannot panic. unsafe { (*this).capacity() } } @@ -1806,7 +1807,7 @@ fn expand_rust_vec( #cfg #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_data)] - unsafe extern "C" fn #local_data #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> *const #elem #ty_generics { + unsafe extern "C" fn #local_data #impl_generics(this: *const ::cxx::private::RustVec<#inner>) -> *const #inner { // No prevent_unwind: cannot panic. unsafe { (*this).as_ptr() } } @@ -1814,7 +1815,7 @@ fn expand_rust_vec( #cfg #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_reserve_total)] - unsafe extern "C" fn #local_reserve_total #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, new_cap: ::cxx::core::primitive::usize) { + unsafe extern "C" fn #local_reserve_total #impl_generics(this: *mut ::cxx::private::RustVec<#inner>, new_cap: ::cxx::core::primitive::usize) { // No prevent_unwind: the global allocator is not allowed to panic. unsafe { (*this).reserve_total(new_cap); @@ -1824,7 +1825,7 @@ fn expand_rust_vec( #cfg #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_set_len)] - unsafe extern "C" fn #local_set_len #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, len: ::cxx::core::primitive::usize) { + unsafe extern "C" fn #local_set_len #impl_generics(this: *mut ::cxx::private::RustVec<#inner>, len: ::cxx::core::primitive::usize) { // No prevent_unwind: cannot panic. unsafe { (*this).set_len(len); @@ -1834,7 +1835,7 @@ fn expand_rust_vec( #cfg #[doc(hidden)] #[#UnsafeAttr(#ExportNameAttr = #link_truncate)] - unsafe extern "C" fn #local_truncate #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, len: ::cxx::core::primitive::usize) { + unsafe extern "C" fn #local_truncate #impl_generics(this: *mut ::cxx::private::RustVec<#inner>, len: ::cxx::core::primitive::usize) { let __fn = ::cxx::core::concat!("<", ::cxx::core::module_path!(), #prevent_unwind_drop_label); ::cxx::private::prevent_unwind( __fn, @@ -1849,10 +1850,8 @@ fn expand_unique_ptr( types: &Types, conditional_impl: &ConditionalImpl, ) -> TokenStream { - let ident = key.rust; - let name = ident.to_string(); - let resolve = types.resolve(ident); - let prefix = format!("cxxbridge1$unique_ptr${}$", resolve.name.to_symbol()); + let inner = key.inner; + let prefix = format!("cxxbridge1$unique_ptr${}$", key.symbol); let link_null = format!("{}null", prefix); let link_uninit = format!("{}uninit", prefix); let link_raw = format!("{}raw", prefix); @@ -1860,9 +1859,10 @@ fn expand_unique_ptr( let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); - let (impl_generics, ty_generics) = generics::split_for_impl(key, conditional_impl, resolve); + let (impl_generics, ty_generics) = + generics::get_impl_and_ty_generics(inner, conditional_impl, types); - let can_construct_from_value = types.is_maybe_trivial(ident); + let can_construct_from_value = types.is_maybe_trivial(inner); let new_method = if can_construct_from_value { let raw_mut = if rustversion::cfg!(since(1.82)) { quote!(&raw mut) @@ -1877,7 +1877,7 @@ fn expand_unique_ptr( } let mut repr = ::cxx::core::mem::MaybeUninit::uninit(); unsafe { - __uninit(#raw_mut repr).cast::<#ident #ty_generics>().write(value); + __uninit(#raw_mut repr).cast::<#inner>().write(value); } repr } @@ -1908,9 +1908,9 @@ fn expand_unique_ptr( quote_spanned! {end_span=> #cfg #[automatically_derived] - #unsafe_token impl #impl_generics ::cxx::memory::UniquePtrTarget for #ident #ty_generics { + #unsafe_token impl #impl_generics ::cxx::memory::UniquePtrTarget for #inner #ty_generics { fn __typename(f: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { - f.write_str(#name) + f.write_str(stringify!(#inner)) } fn __null() -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { #UnsafeExtern extern "C" { @@ -1967,10 +1967,8 @@ fn expand_shared_ptr( types: &Types, conditional_impl: &ConditionalImpl, ) -> TokenStream { - let ident = key.rust; - let name = ident.to_string(); - let resolve = types.resolve(ident); - let prefix = format!("cxxbridge1$shared_ptr${}$", resolve.name.to_symbol()); + let inner = key.inner; + let prefix = format!("cxxbridge1$shared_ptr${}$", key.symbol); let link_null = format!("{}null", prefix); let link_uninit = format!("{}uninit", prefix); let link_raw = format!("{}raw", prefix); @@ -1978,9 +1976,10 @@ fn expand_shared_ptr( let link_get = format!("{}get", prefix); let link_drop = format!("{}drop", prefix); - let (impl_generics, ty_generics) = generics::split_for_impl(key, conditional_impl, resolve); + let (impl_generics, ty_generics) = + generics::get_impl_and_ty_generics(inner, conditional_impl, types); - let can_construct_from_value = types.is_maybe_trivial(ident); + let can_construct_from_value = types.is_maybe_trivial(inner); let new_method = if can_construct_from_value { Some(quote! { unsafe fn __new(value: Self, new: *mut ::cxx::core::ffi::c_void) { @@ -1989,7 +1988,7 @@ fn expand_shared_ptr( fn __uninit(new: *mut ::cxx::core::ffi::c_void) -> *mut ::cxx::core::ffi::c_void; } unsafe { - __uninit(new).cast::<#ident #ty_generics>().write(value); + __uninit(new).cast::<#inner>().write(value); } } }) @@ -2005,14 +2004,13 @@ fn expand_shared_ptr( .explicit_impl .map_or(key.end_span, |explicit| explicit.brace_token.span.join()); let unsafe_token = format_ident!("unsafe", span = begin_span); - let not_destructible_err = format!("{} is not destructible", display_namespaced(resolve.name)); quote_spanned! {end_span=> #cfg #[automatically_derived] - #unsafe_token impl #impl_generics ::cxx::memory::SharedPtrTarget for #ident #ty_generics { + #unsafe_token impl #impl_generics ::cxx::memory::SharedPtrTarget for #inner #ty_generics { fn __typename(f: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { - f.write_str(#name) + f.write_str(stringify!(#inner)) } unsafe fn __null(new: *mut ::cxx::core::ffi::c_void) { #UnsafeExtern extern "C" { @@ -2031,7 +2029,10 @@ fn expand_shared_ptr( fn __raw(new: *const ::cxx::core::ffi::c_void, raw: *mut ::cxx::core::ffi::c_void) -> ::cxx::core::primitive::bool; } if !unsafe { __raw(new, raw as *mut ::cxx::core::ffi::c_void) } { - ::cxx::core::panic!(#not_destructible_err); + ::cxx::core::panic!( + "{} provides bindings to a C++ type that is not destructible", + ::std::any::type_name::(), + ); } } unsafe fn __clone(this: *const ::cxx::core::ffi::c_void, new: *mut ::cxx::core::ffi::c_void) { @@ -2068,17 +2069,16 @@ fn expand_weak_ptr( types: &Types, conditional_impl: &ConditionalImpl, ) -> TokenStream { - let ident = key.rust; - let name = ident.to_string(); - let resolve = types.resolve(ident); - let prefix = format!("cxxbridge1$weak_ptr${}$", resolve.name.to_symbol()); + let inner = key.inner; + let prefix = format!("cxxbridge1$weak_ptr${}$", key.symbol); let link_null = format!("{}null", prefix); let link_clone = format!("{}clone", prefix); let link_downgrade = format!("{}downgrade", prefix); let link_upgrade = format!("{}upgrade", prefix); let link_drop = format!("{}drop", prefix); - let (impl_generics, ty_generics) = generics::split_for_impl(key, conditional_impl, resolve); + let (impl_generics, ty_generics) = + generics::get_impl_and_ty_generics(inner, conditional_impl, types); let cfg = conditional_impl.cfg.into_attr(); let begin_span = conditional_impl @@ -2092,9 +2092,9 @@ fn expand_weak_ptr( quote_spanned! {end_span=> #cfg #[automatically_derived] - #unsafe_token impl #impl_generics ::cxx::memory::WeakPtrTarget for #ident #ty_generics { + #unsafe_token impl #impl_generics ::cxx::memory::WeakPtrTarget for #inner #ty_generics { fn __typename(f: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { - f.write_str(#name) + f.write_str(stringify!(#inner)) } unsafe fn __null(new: *mut ::cxx::core::ffi::c_void) { #UnsafeExtern extern "C" { @@ -2150,10 +2150,8 @@ fn expand_cxx_vector( conditional_impl: &ConditionalImpl, types: &Types, ) -> TokenStream { - let elem = key.rust; - let name = elem.to_string(); - let resolve = types.resolve(elem); - let prefix = format!("cxxbridge1$std$vector${}$", resolve.name.to_symbol()); + let inner = key.inner; + let prefix = format!("cxxbridge1$std$vector${}$", key.symbol); let link_new = format!("{}new", prefix); let link_size = format!("{}size", prefix); let link_capacity = format!("{}capacity", prefix); @@ -2161,17 +2159,15 @@ fn expand_cxx_vector( let link_reserve = format!("{}reserve", prefix); let link_push_back = format!("{}push_back", prefix); let link_pop_back = format!("{}pop_back", prefix); - let unique_ptr_prefix = format!( - "cxxbridge1$unique_ptr$std$vector${}$", - resolve.name.to_symbol(), - ); + let unique_ptr_prefix = format!("cxxbridge1$unique_ptr$std$vector${}$", key.symbol,); let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); let link_unique_ptr_release = format!("{}release", unique_ptr_prefix); let link_unique_ptr_drop = format!("{}drop", unique_ptr_prefix); - let (impl_generics, ty_generics) = generics::split_for_impl(key, conditional_impl, resolve); + let (impl_generics, ty_generics) = + generics::get_impl_and_ty_generics(inner, conditional_impl, types); let cfg = conditional_impl.cfg.into_attr(); let begin_span = conditional_impl @@ -2182,7 +2178,7 @@ fn expand_cxx_vector( .map_or(key.end_span, |explicit| explicit.brace_token.span.join()); let unsafe_token = format_ident!("unsafe", span = begin_span); - let can_pass_element_by_value = types.is_maybe_trivial(elem); + let can_pass_element_by_value = types.is_maybe_trivial(inner); let by_value_methods = if can_pass_element_by_value { Some(quote_spanned! {end_span=> unsafe fn __push_back( @@ -2192,7 +2188,7 @@ fn expand_cxx_vector( #UnsafeExtern extern "C" { #[link_name = #link_push_back] fn __push_back #impl_generics( - this: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#elem #ty_generics>>, + this: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#inner>>, value: *mut ::cxx::core::ffi::c_void, ); } @@ -2210,7 +2206,7 @@ fn expand_cxx_vector( #UnsafeExtern extern "C" { #[link_name = #link_pop_back] fn __pop_back #impl_generics( - this: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#elem #ty_generics>>, + this: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#inner>>, out: *mut ::cxx::core::ffi::c_void, ); } @@ -2237,36 +2233,31 @@ fn expand_cxx_vector( quote_spanned!(end_span=> &mut) }; - let not_move_constructible_err = format!( - "{} is not move constructible", - display_namespaced(resolve.name), - ); - quote_spanned! {end_span=> #cfg #[automatically_derived] - #unsafe_token impl #impl_generics ::cxx::vector::VectorElement for #elem #ty_generics { + #unsafe_token impl #impl_generics ::cxx::vector::VectorElement for #inner #ty_generics { fn __typename(f: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { - f.write_str(#name) + f.write_str(stringify!(#inner)) } fn __vector_new() -> *mut ::cxx::CxxVector { #UnsafeExtern extern "C" { #[link_name = #link_new] - fn __vector_new #impl_generics() -> *mut ::cxx::CxxVector<#elem #ty_generics>; + fn __vector_new #impl_generics() -> *mut ::cxx::CxxVector<#inner>; } unsafe { __vector_new() } } fn __vector_size(v: &::cxx::CxxVector) -> ::cxx::core::primitive::usize { #UnsafeExtern extern "C" { #[link_name = #link_size] - fn __vector_size #impl_generics(_: &::cxx::CxxVector<#elem #ty_generics>) -> ::cxx::core::primitive::usize; + fn __vector_size #impl_generics(_: &::cxx::CxxVector<#inner>) -> ::cxx::core::primitive::usize; } unsafe { __vector_size(v) } } fn __vector_capacity(v: &::cxx::CxxVector) -> ::cxx::core::primitive::usize { #UnsafeExtern extern "C" { #[link_name = #link_capacity] - fn __vector_capacity #impl_generics(_: &::cxx::CxxVector<#elem #ty_generics>) -> ::cxx::core::primitive::usize; + fn __vector_capacity #impl_generics(_: &::cxx::CxxVector<#inner>) -> ::cxx::core::primitive::usize; } unsafe { __vector_capacity(v) } } @@ -2274,7 +2265,7 @@ fn expand_cxx_vector( #UnsafeExtern extern "C" { #[link_name = #link_get_unchecked] fn __get_unchecked #impl_generics( - v: *mut ::cxx::CxxVector<#elem #ty_generics>, + v: *mut ::cxx::CxxVector<#inner>, pos: ::cxx::core::primitive::usize, ) -> *mut ::cxx::core::ffi::c_void; } @@ -2284,12 +2275,15 @@ fn expand_cxx_vector( #UnsafeExtern extern "C" { #[link_name = #link_reserve] fn __reserve #impl_generics( - v: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#elem #ty_generics>>, + v: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#inner>>, new_cap: ::cxx::core::primitive::usize, ) -> ::cxx::core::primitive::bool; } if !unsafe { __reserve(v, new_cap) } { - ::cxx::core::panic!(#not_move_constructible_err); + ::cxx::core::panic!( + "{} provides bindings to a C++ type that is not move constructible", + ::std::any::type_name::(), + ); } } #by_value_methods @@ -2307,7 +2301,7 @@ fn expand_cxx_vector( unsafe fn __unique_ptr_raw(raw: *mut ::cxx::CxxVector) -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { #UnsafeExtern extern "C" { #[link_name = #link_unique_ptr_raw] - fn __unique_ptr_raw #impl_generics(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>, raw: *mut ::cxx::CxxVector<#elem #ty_generics>); + fn __unique_ptr_raw #impl_generics(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>, raw: *mut ::cxx::CxxVector<#inner>); } let mut repr = ::cxx::core::mem::MaybeUninit::uninit(); unsafe { @@ -2318,14 +2312,14 @@ fn expand_cxx_vector( unsafe fn __unique_ptr_get(repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *const ::cxx::CxxVector { #UnsafeExtern extern "C" { #[link_name = #link_unique_ptr_get] - fn __unique_ptr_get #impl_generics(this: *const ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *const ::cxx::CxxVector<#elem #ty_generics>; + fn __unique_ptr_get #impl_generics(this: *const ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *const ::cxx::CxxVector<#inner>; } unsafe { __unique_ptr_get(#raw_const repr) } } unsafe fn __unique_ptr_release(mut repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *mut ::cxx::CxxVector { #UnsafeExtern extern "C" { #[link_name = #link_unique_ptr_release] - fn __unique_ptr_release #impl_generics(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *mut ::cxx::CxxVector<#elem #ty_generics>; + fn __unique_ptr_release #impl_generics(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) -> *mut ::cxx::CxxVector<#inner>; } unsafe { __unique_ptr_release(#raw_mut repr) } } diff --git a/macro/src/generics.rs b/macro/src/generics.rs index 4d720354e..9e8dbdb7b 100644 --- a/macro/src/generics.rs +++ b/macro/src/generics.rs @@ -1,65 +1,85 @@ -use crate::syntax::instantiate::NamedImplKey; -use crate::syntax::resolve::Resolution; use crate::syntax::types::ConditionalImpl; -use crate::syntax::{Impl, Lifetimes}; +use crate::syntax::{Lifetimes, Type, Types}; use proc_macro2::TokenStream; use quote::ToTokens; -use syn::{Lifetime, Token}; +use syn::Lifetime; -pub(crate) struct ImplGenerics<'a> { - explicit_impl: Option<&'a Impl>, - resolve: Resolution<'a>, -} - -pub(crate) struct TyGenerics<'a> { - key: &'a NamedImplKey<'a>, - explicit_impl: Option<&'a Impl>, - resolve: Resolution<'a>, -} - -pub(crate) fn split_for_impl<'a>( - key: &'a NamedImplKey<'a>, +/// Gets `(impl_generics, ty_generics)` pair that can be used when generating an `impl` for a +/// generic type using something like: +/// `quote! { impl #impl_generics SomeTrait for #inner #ty_generics }`. +/// +/// Parameters: +/// +/// * `inner` is the generic type argument (e.g. `T` in something like `UniquePtr`) +/// * `explicit_impl` corresponds to https://cxx.rs/extern-c++.html#explicit-shim-trait-impls +pub(crate) fn get_impl_and_ty_generics<'a>( + inner: &'a Type, conditional_impl: &ConditionalImpl<'a>, - resolve: Resolution<'a>, -) -> (ImplGenerics<'a>, TyGenerics<'a>) { - let impl_generics = ImplGenerics { - explicit_impl: conditional_impl.explicit_impl, - resolve, - }; - let ty_generics = TyGenerics { - key, - explicit_impl: conditional_impl.explicit_impl, - resolve, - }; - (impl_generics, ty_generics) + types: &'a Types, +) -> (&'a Lifetimes, Option<&'a Lifetimes>) { + match conditional_impl.explicit_impl { + Some(explicit_impl) => { + let impl_generics = &explicit_impl.impl_generics; + (impl_generics, None /* already covered via `#inner` */) + } + None => { + // Check whether `explicit_generics` are present. In the example below, + // there are not `explicit_generics` in the return type. + // + // mod ffi { + // unsafe extern "C++" { + // type Borrowed<'a>; + // fn borrowed(arg: &i32) -> UniquePtr; + // } + // } + // + // But this could have also been spelled with `explicit_generics`: + // + // fn borrowed<'a>(arg: &'a i32) -> UniquePtr>; + let explicit_generics = get_generic_lifetimes(inner); + if explicit_generics.lifetimes.is_empty() { + // In the example above, we want to use generics from `type Borrowed<'a>`. + let resolved_generics = resolve_generic_lifetimes(inner, types); + (resolved_generics, Some(resolved_generics)) + } else { + ( + explicit_generics, + None, /* already covered via `#inner` */ + ) + } + } + } } -impl<'a> ToTokens for ImplGenerics<'a> { - fn to_tokens(&self, tokens: &mut TokenStream) { - if let Some(imp) = self.explicit_impl { - imp.impl_generics.to_tokens(tokens); - } else { - self.resolve.generics.to_tokens(tokens); - } +/// Gets explicit / non-inferred generic lifetimes from `ty`. This will recursively +/// return lifetimes in cases like `CxxVector>`. +/// +/// See also: resolve_generic_lifetimes. +fn get_generic_lifetimes<'a>(ty: &'a Type) -> &'a Lifetimes { + match ty { + Type::Ident(named_type) => &named_type.generics, + Type::CxxVector(ty1) => get_generic_lifetimes(&ty1.inner), + _ => unreachable!("syntax/check.rs should reject other types"), } } -impl<'a> ToTokens for TyGenerics<'a> { - fn to_tokens(&self, tokens: &mut TokenStream) { - if let Some(imp) = self.explicit_impl { - imp.ty_generics.to_tokens(tokens); - } else if !self.resolve.generics.lifetimes.is_empty() { - let span = self.key.rust.span(); - self.key - .lt_token - .unwrap_or_else(|| Token![<](span)) - .to_tokens(tokens); - self.resolve.generics.lifetimes.to_tokens(tokens); - self.key - .gt_token - .unwrap_or_else(|| Token![>](span)) - .to_tokens(tokens); - } +/// Gets generic lifetimes resolved from declaration of `ty`. For example, this will return `'a` +/// lifetime when `type_` represents `CxxVector>` (no explicit lifetime here!) in +/// presence of the following bridge declaration: +/// +/// ```rust,ignore +/// unsafe extern "C++" { +/// type Borrowed<'a>; // <= **this** lifetime will be returned +/// fn borrowed(arg: &i32) -> CxxVector; +/// } +/// ``` +/// +/// See also: get_generic_lifetimes. +fn resolve_generic_lifetimes<'a>(ty: &'a Type, types: &'a Types) -> &'a Lifetimes { + match ty { + Type::Ident(named_type) => types.resolve(&named_type.rust).generics, + Type::CxxVector(ty1) => resolve_generic_lifetimes(&ty1.inner, types), + _ => unreachable!("syntax/check.rs should reject other types"), } } diff --git a/syntax/instantiate.rs b/syntax/instantiate.rs index a1fb47e74..312d140ac 100644 --- a/syntax/instantiate.rs +++ b/syntax/instantiate.rs @@ -1,7 +1,7 @@ -use crate::syntax::{NamedType, Ty1, Type}; -use proc_macro2::{Ident, Span}; +use crate::syntax::types::Types; +use crate::syntax::{mangle, Symbol, Ty1, Type}; +use proc_macro2::Span; use std::hash::{Hash, Hasher}; -use syn::Token; #[derive(PartialEq, Eq, Hash)] pub(crate) enum ImplKey<'a> { @@ -13,52 +13,75 @@ pub(crate) enum ImplKey<'a> { CxxVector(NamedImplKey<'a>), } +impl<'a> ImplKey<'a> { + /// Whether to generate an implicit instantiation/monomorphization of a given generic type + /// binding. ("implicit" = without an explicit `impl Foo {}` - see + /// ). + /// + /// The main consideration is avoiding introducing conflicting/overlapping impls: + /// + /// * The `cxx` crate already provides impls for cases where `T` is a primitive + /// type like `u32` + /// * Some generics (e.g. Rust bindings for C++ templates like `CxxVector`, `UniquePtr`, + /// etc.) require an `impl` of a `trait` provided by the `cxx` crate (such as + /// [`cxx::vector::VectorElement`] or [`cxx::memory::UniquePtrTarget`]). To avoid violating + /// [Rust orphan rule](https://doc.rust-lang.org/reference/items/implementations.html#r-items.impl.trait.orphan-rule.intro) + /// we restrict `T` to be a local type + /// (TODO: or a fundamental type like `Box`). + /// * Other generics (e.g. C++ bindings for Rust generics like `Vec` or `Box`) + /// don't necessarily need to follow the orphan rule, but we conservatively also + /// only generate implicit impls if `T` is a local type. TODO: revisit? + pub(crate) fn is_implicit_impl_ok(&self, types: &Types) -> bool { + // TODO: relax this for Rust generics to allow Vec> etc. + types.is_local(self.inner()) + } + + /// Returns the generic type parameter `T` associated with `self`. + /// For example, if `self` represents `UniquePtr` then this will return `u32`. + pub(crate) fn inner(&self) -> &'a Type { + let named_impl_key = match self { + ImplKey::RustBox(key) + | ImplKey::RustVec(key) + | ImplKey::UniquePtr(key) + | ImplKey::SharedPtr(key) + | ImplKey::WeakPtr(key) + | ImplKey::CxxVector(key) => key, + }; + named_impl_key.inner + } +} + pub(crate) struct NamedImplKey<'a> { #[cfg_attr(not(proc_macro), expect(dead_code))] pub begin_span: Span, - pub rust: &'a Ident, - #[cfg_attr(not(proc_macro), expect(dead_code))] - pub lt_token: Option, - #[cfg_attr(not(proc_macro), expect(dead_code))] - pub gt_token: Option]>, - #[cfg_attr(not(proc_macro), expect(dead_code))] + /// Mangled form of the `outer` type. + pub symbol: Symbol, + /// Generic type - e.g. `UniquePtr`. + #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro + pub outer: &'a Type, + /// Generic type argument - e.g. `u8` from `UniquePtr`. + pub inner: &'a Type, + #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub end_span: Span, } impl Type { pub(crate) fn impl_key(&self) -> Option { - if let Type::RustBox(ty) = self { - if let Type::Ident(ident) = &ty.inner { - return Some(ImplKey::RustBox(NamedImplKey::new(ty, ident))); - } - } else if let Type::RustVec(ty) = self { - if let Type::Ident(ident) = &ty.inner { - return Some(ImplKey::RustVec(NamedImplKey::new(ty, ident))); - } - } else if let Type::UniquePtr(ty) = self { - if let Type::Ident(ident) = &ty.inner { - return Some(ImplKey::UniquePtr(NamedImplKey::new(ty, ident))); - } - } else if let Type::SharedPtr(ty) = self { - if let Type::Ident(ident) = &ty.inner { - return Some(ImplKey::SharedPtr(NamedImplKey::new(ty, ident))); - } - } else if let Type::WeakPtr(ty) = self { - if let Type::Ident(ident) = &ty.inner { - return Some(ImplKey::WeakPtr(NamedImplKey::new(ty, ident))); - } - } else if let Type::CxxVector(ty) = self { - if let Type::Ident(ident) = &ty.inner { - return Some(ImplKey::CxxVector(NamedImplKey::new(ty, ident))); - } + match self { + Type::RustBox(ty) => Some(ImplKey::RustBox(NamedImplKey::new(self, ty)?)), + Type::RustVec(ty) => Some(ImplKey::RustVec(NamedImplKey::new(self, ty)?)), + Type::UniquePtr(ty) => Some(ImplKey::UniquePtr(NamedImplKey::new(self, ty)?)), + Type::SharedPtr(ty) => Some(ImplKey::SharedPtr(NamedImplKey::new(self, ty)?)), + Type::WeakPtr(ty) => Some(ImplKey::WeakPtr(NamedImplKey::new(self, ty)?)), + Type::CxxVector(ty) => Some(ImplKey::CxxVector(NamedImplKey::new(self, ty)?)), + _ => None, } - None } } impl<'a> PartialEq for NamedImplKey<'a> { fn eq(&self, other: &Self) -> bool { - PartialEq::eq(self.rust, other.rust) + PartialEq::eq(&self.symbol, &other.symbol) } } @@ -66,18 +89,19 @@ impl<'a> Eq for NamedImplKey<'a> {} impl<'a> Hash for NamedImplKey<'a> { fn hash(&self, hasher: &mut H) { - self.rust.hash(hasher); + self.symbol.hash(hasher); } } impl<'a> NamedImplKey<'a> { - fn new(outer: &Ty1, inner: &'a NamedType) -> Self { - NamedImplKey { - begin_span: outer.name.span(), - rust: &inner.rust, - lt_token: inner.generics.lt_token, - gt_token: inner.generics.gt_token, - end_span: outer.rangle.span, - } + fn new(outer: &'a Type, ty1: &'a Ty1) -> Option { + let inner = &ty1.inner; + Some(NamedImplKey { + symbol: mangle::type_(inner)?, + begin_span: ty1.name.span(), + outer, + inner, + end_span: ty1.rangle.span, + }) } } diff --git a/syntax/mangle.rs b/syntax/mangle.rs index 1b10fb7e7..5308541af 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -74,7 +74,7 @@ // - CXXBRIDGE1_ENUM_Enabled use crate::syntax::symbol::{self, Symbol}; -use crate::syntax::{ExternFn, Pair, Types}; +use crate::syntax::{ExternFn, Pair, Type, Types}; const CXXBRIDGE: &str = "cxxbridge1"; @@ -118,3 +118,23 @@ pub(crate) fn c_trampoline(efn: &ExternFn, var: &Pair, types: &Types) -> Symbol pub(crate) fn r_trampoline(efn: &ExternFn, var: &Pair, types: &Types) -> Symbol { join!(extern_fn(efn, types), var.rust, 1) } + +/// Attempts to mangle the type `t` (e.g. representing `Box`) +/// into a symbol (e.g. `box$org$rust$Struct`) +/// that can be used as a **part** of monomorphized/instantiated thunk names +/// (e.g. `cxxbridge1$box$org$rust$Struct$alloc`). +/// +/// Not all type names can be mangled at this point - `None` will be returned +/// if mangling fails. +pub(crate) fn type_(t: &Type) -> Option { + match t { + Type::Ident(named_type) => Some(join!(named_type.rust)), + Type::RustBox(ty1) => type_(&ty1.inner).map(|s| join!("box", s)), + Type::RustVec(ty1) => type_(&ty1.inner).map(|s| join!("rust_vec", s)), + Type::UniquePtr(ty1) => type_(&ty1.inner).map(|s| join!("unique_ptr", s)), + Type::SharedPtr(ty1) => type_(&ty1.inner).map(|s| join!("shared_ptr", s)), + Type::WeakPtr(ty1) => type_(&ty1.inner).map(|s| join!("weak_ptr", s)), + Type::CxxVector(ty1) => type_(&ty1.inner).map(|s| join!("std", "vector", s)), + _ => None, + } +} diff --git a/syntax/map.rs b/syntax/map.rs index 5db99d3d9..6b6293dcb 100644 --- a/syntax/map.rs +++ b/syntax/map.rs @@ -27,6 +27,10 @@ mod ordered { { self.0.contains_key(key) } + + pub(crate) fn iter<'a>(&'a self) -> impl Iterator { + self.0.iter() + } } impl OrderedMap diff --git a/syntax/mod.rs b/syntax/mod.rs index 4252f3080..873606046 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -185,8 +185,6 @@ pub(crate) struct Impl { #[expect(dead_code)] pub negative: bool, pub ty: Type, - #[cfg_attr(not(proc_macro), expect(dead_code))] - pub ty_generics: Lifetimes, pub brace_token: Brace, pub negative_token: Option, } diff --git a/syntax/names.rs b/syntax/names.rs index 7afa5a9e3..79e78c2a7 100644 --- a/syntax/names.rs +++ b/syntax/names.rs @@ -13,6 +13,7 @@ pub(crate) struct ForeignName { } impl Pair { + #[allow(dead_code)] // only used by cxx-gen, not cxxbridge-macro pub(crate) fn to_symbol(&self) -> Symbol { let segments = self .namespace diff --git a/syntax/parse.rs b/syntax/parse.rs index e0dc8a918..cdf0a8536 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1090,25 +1090,6 @@ fn parse_impl(cx: &mut Errors, imp: ItemImpl) -> Result { } let ty = parse_type(&self_ty)?; - let ty_generics = match &ty { - Type::RustBox(ty) - | Type::RustVec(ty) - | Type::UniquePtr(ty) - | Type::SharedPtr(ty) - | Type::WeakPtr(ty) - | Type::CxxVector(ty) => match &ty.inner { - Type::Ident(ident) => ident.generics.clone(), - _ => Lifetimes::default(), - }, - Type::Ident(_) - | Type::Ref(_) - | Type::Ptr(_) - | Type::Str(_) - | Type::Fn(_) - | Type::Void(_) - | Type::SliceRef(_) - | Type::Array(_) => Lifetimes::default(), - }; let negative = negative_token.is_some(); let brace_token = imp.brace_token; @@ -1120,7 +1101,6 @@ fn parse_impl(cx: &mut Errors, imp: ItemImpl) -> Result { impl_generics, negative, ty, - ty_generics, brace_token, negative_token, })) diff --git a/syntax/resolve.rs b/syntax/resolve.rs index 63b514117..cc89d142a 100644 --- a/syntax/resolve.rs +++ b/syntax/resolve.rs @@ -1,5 +1,4 @@ use crate::syntax::attrs::OtherAttrs; -use crate::syntax::instantiate::NamedImplKey; use crate::syntax::{Lifetimes, NamedType, Pair, Types}; use proc_macro2::Ident; @@ -41,9 +40,3 @@ impl UnresolvedName for NamedType { &self.rust } } - -impl<'a> UnresolvedName for NamedImplKey<'a> { - fn ident(&self) -> &Ident { - self.rust - } -} diff --git a/syntax/symbol.rs b/syntax/symbol.rs index 8602b64ef..f2f439230 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -1,11 +1,18 @@ use crate::syntax::namespace::Namespace; use crate::syntax::{ForeignName, Pair}; use proc_macro2::{Ident, TokenStream}; -use quote::ToTokens; +use quote::{IdentFragment, ToTokens}; use std::fmt::{self, Display, Write}; // A mangled symbol consisting of segments separated by '$'. -// For example: cxxbridge1$string$new +// +// Segments are expected to only contain characters that are valid inside +// both C++ and Rust identifiers ( +// [XID_Start or XID_Continue](https://doc.rust-lang.org/reference/identifiers.html), +// but not a `$` sign). +// +// Example: cxxbridge1$string$new +#[derive(Eq, Hash, PartialEq)] pub(crate) struct Symbol(String); impl Display for Symbol { @@ -20,6 +27,28 @@ impl ToTokens for Symbol { } } +impl IdentFragment for Symbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Need to escape non-identifier-characters + // (`$` is the only such character allowed in `Symbol`s). + // + // The escaping scheme needs to be + // [an injection](https://en.wikipedia.org/wiki/Injective_function). + // This means that we also need to escape the escape character `_`. + for c in self.0.chars() { + match c { + '_' => f.write_str("_u")?, + '$' => f.write_str("_d")?, + c => { + // TODO: Assert that `c` is XID_Start or XID_Continue? + f.write_fmt(format_args!("{}", c))?; + } + } + } + Ok(()) + } +} + impl Symbol { fn push(&mut self, segment: &dyn Display) { let len_before = self.0.len(); @@ -30,6 +59,7 @@ impl Symbol { assert!(self.0.len() > len_before); } + #[allow(dead_code)] // only used by cxx-gen, not cxxbridge-macro pub(crate) fn from_idents<'a>(it: impl Iterator) -> Self { let mut symbol = Symbol(String::new()); for segment in it { diff --git a/syntax/tokens.rs b/syntax/tokens.rs index b94032e86..bc6bf2ce6 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -227,7 +227,6 @@ impl ToTokens for Impl { impl_generics, negative: _, ty, - ty_generics: _, brace_token, negative_token, } = self; diff --git a/syntax/types.rs b/syntax/types.rs index e31b20ad7..1291f21b6 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -216,30 +216,6 @@ impl<'a> Types<'a> { } } - for (ty, cfg) in &all { - let Some(impl_key) = ty.impl_key() else { - continue; - }; - let implicit_impl = match &impl_key { - ImplKey::RustBox(ident) - | ImplKey::RustVec(ident) - | ImplKey::UniquePtr(ident) - | ImplKey::SharedPtr(ident) - | ImplKey::WeakPtr(ident) - | ImplKey::CxxVector(ident) => { - Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust) - } - }; - if implicit_impl { - match impls.entry(impl_key) { - Entry::Vacant(entry) => { - entry.insert(ConditionalImpl::from(cfg.clone())); - } - Entry::Occupied(mut entry) => entry.get_mut().cfg.merge_or(cfg.clone()), - } - } - } - // All these APIs may contain types passed by value. We need to ensure // we check that this is permissible. We do this _after_ scanning all // the APIs above, in case some function or struct references a type @@ -269,6 +245,21 @@ impl<'a> Types<'a> { types.toposorted_structs = toposort::sort(cx, apis, &types); + let implicit_impls = types + .all + .iter() + .filter_map(|(ty, cfg)| Type::impl_key(ty).map(|impl_key| (impl_key, cfg))) + .filter(|(impl_key, _cfg)| impl_key.is_implicit_impl_ok(&types)) + .collect::>(); + for (impl_key, cfg) in implicit_impls { + match types.impls.entry(impl_key) { + Entry::Vacant(entry) => { + entry.insert(ConditionalImpl::from(cfg.clone())); + } + Entry::Occupied(mut entry) => entry.get_mut().cfg.merge_or(cfg.clone()), + } + } + let mut unresolved_structs = types.structs.keys(); let mut new_information = true; while new_information { @@ -326,10 +317,17 @@ impl<'a> Types<'a> { // Types which we need to assume could possibly exist by value on the Rust // side. - pub(crate) fn is_maybe_trivial(&self, ty: &Ident) -> bool { - self.structs.contains_key(ty) - || self.enums.contains_key(ty) - || self.aliases.contains_key(ty) + pub(crate) fn is_maybe_trivial(&self, ty: &Type) -> bool { + match ty { + Type::Ident(named_type) => { + let ident = &named_type.rust; + self.structs.contains_key(ident) + || self.enums.contains_key(ident) + || self.aliases.contains_key(ident) + } + Type::CxxVector(_) => false, + _ => unreachable!("syntax/check.rs should reject other types"), + } } pub(crate) fn contains_elided_lifetime(&self, ty: &Type) -> bool { @@ -353,6 +351,32 @@ impl<'a> Types<'a> { Type::Fn(_) | Type::Void(_) => false, } } + + /// Returns `true` if `ty` is a defined or declared within the current `#[cxx::bridge]`. + pub(crate) fn is_local(&self, ty: &Type) -> bool { + match ty { + Type::Ident(ident) => { + Atom::from(&ident.rust).is_none() && !self.aliases.contains_key(&ident.rust) + } + Type::RustBox(_) => { + // TODO: We should treat Box as local to match + // https://doc.rust-lang.org/reference/items/implementations.html#r-items.impl.trait.fundamental + false + } + Type::Array(_) + | Type::CxxVector(_) + | Type::Fn(_) + | Type::Void(_) + | Type::RustVec(_) + | Type::UniquePtr(_) + | Type::SharedPtr(_) + | Type::WeakPtr(_) + | Type::Ref(_) + | Type::Ptr(_) + | Type::Str(_) + | Type::SliceRef(_) => false, + } + } } impl<'t, 'a> IntoIterator for &'t Types<'a> { diff --git a/tests/test.rs b/tests/test.rs index 5e8aca475..f4dafaa46 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -331,19 +331,19 @@ fn test_shared_ptr_from_raw() { } #[test] -#[should_panic = "tests::Undefined is not destructible"] +#[should_panic = "cxx_test_suite::ffi::Undefined provides bindings to a C++ type that is not destructible"] fn test_shared_ptr_from_raw_undefined() { unsafe { SharedPtr::::from_raw(ptr::null_mut()) }; } #[test] -#[should_panic = "tests::Private is not destructible"] +#[should_panic = "cxx_test_suite::ffi::Private provides bindings to a C++ type that is not destructible"] fn test_shared_ptr_from_raw_private() { unsafe { SharedPtr::::from_raw(ptr::null_mut()) }; } #[test] -#[should_panic = "tests::Unmovable is not move constructible"] +#[should_panic = "cxx_test_suite::ffi::Unmovable provides bindings to a C++ type that is not move constructible"] fn test_vector_reserve_unmovable() { let mut vector = CxxVector::::new(); vector.pin_mut().reserve(10);