diff --git a/book/src/binding/uniqueptr.md b/book/src/binding/uniqueptr.md index eefbc34a3..47d33ad63 100644 --- a/book/src/binding/uniqueptr.md +++ b/book/src/binding/uniqueptr.md @@ -8,8 +8,9 @@ the link for documentation of the Rust API. ### Restrictions: -Only `std::unique_ptr>` is currently supported. Custom -deleters may be supported in the future. +If a custom deleter is used, it needs to be a type that is +[shared between C++ and Rust](../shared.md) so that the instance of UniquePtr can still +be passed by value in Rust code. UniquePtr\ does not support T being an opaque Rust type. You should use a Box\ (C++ [rust::Box\](box.md)) instead for transferring ownership of diff --git a/gen/src/write.rs b/gen/src/write.rs index 8eef0a76b..5adc42643 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -823,8 +823,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { write_type(out, &arg.ty); write!(out, "::from_raw({})", arg.name.cxx); } else if let Type::UniquePtr(_) = &arg.ty { - write_type(out, &arg.ty); - write!(out, "({})", arg.name.cxx); + write!(out, "::std::move(*{})", arg.name.cxx); } else if arg.ty == RustString { out.builtin.unsafe_bitcopy = true; write!( @@ -846,7 +845,6 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { write!(out, ")"); match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), - Some(Type::UniquePtr(_)) => write!(out, ".release()"), Some(Type::Str(_) | Type::SliceRef(_)) if !indirect_return => write!(out, ")"), _ => {} } @@ -1092,7 +1090,6 @@ fn write_rust_function_shim_impl( write!(out, "{}", arg.name.cxx); match &arg.ty { Type::RustBox(_) => write!(out, ".into_raw()"), - Type::UniquePtr(_) => write!(out, ".release()"), ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} } @@ -1155,10 +1152,19 @@ fn indirect_return(sig: &Signature, types: &Types) -> bool { fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { match ty { - Type::RustBox(ty) | Type::UniquePtr(ty) => { + Type::RustBox(ty) => { write_type_space(out, &ty.inner); write!(out, "*"); } + Type::UniquePtr(ty) => { + write!(out, "::std::unique_ptr<"); + write_type(out, &ty.first); + if let Some(deleter) = &ty.second { + write!(out, ", "); + write_type(out, deleter); + } + write!(out, "> "); + } Type::Ref(ty) => { write_type_space(out, &ty.inner); if !ty.mutable { @@ -1181,7 +1187,7 @@ fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { fn write_extern_return_type_space(out: &mut OutFile, ty: &Option) { match ty { - Some(Type::RustBox(ty) | Type::UniquePtr(ty)) => { + Some(Type::RustBox(ty)) => { write_type_space(out, &ty.inner); write!(out, "*"); } @@ -1203,7 +1209,7 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option) { fn write_extern_arg(out: &mut OutFile, arg: &Var) { match &arg.ty { - Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => { + Type::RustBox(ty) | Type::CxxVector(ty) => { write_type_space(out, &ty.inner); write!(out, "*"); } @@ -1237,7 +1243,11 @@ fn write_type(out: &mut OutFile, ty: &Type) { } Type::UniquePtr(ptr) => { write!(out, "::std::unique_ptr<"); - write_type(out, &ptr.inner); + write_type(out, &ptr.first); + if let Some(deleter) = &ptr.second { + write!(out, ", "); + write_type(out, deleter); + } write!(out, ">"); } Type::SharedPtr(ptr) => { @@ -1350,7 +1360,7 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { #[derive(Copy, Clone)] enum UniquePtr<'a> { - Ident(&'a Ident), + Ident(&'a Ident, Option<&'a Ident>), CxxVector(&'a Ident), } @@ -1367,7 +1377,7 @@ impl ToTypename for Ident { impl<'a> ToTypename for UniquePtr<'a> { fn to_typename(&self, types: &Types) -> String { match self { - UniquePtr::Ident(ident) => ident.to_typename(types), + UniquePtr::Ident(ident, _) => ident.to_typename(types), UniquePtr::CxxVector(element) => { format!("::std::vector<{}>", element.to_typename(types)) } @@ -1388,7 +1398,7 @@ impl ToMangled for Ident { impl<'a> ToMangled for UniquePtr<'a> { fn to_mangled(&self, types: &Types) -> Symbol { match self { - UniquePtr::Ident(ident) => ident.to_mangled(types), + UniquePtr::Ident(ident, _) => ident.to_mangled(types), UniquePtr::CxxVector(element) => { symbol::join(&[&"std", &"vector", &element.to_mangled(types)]) } @@ -1409,7 +1419,7 @@ fn write_generic_instantiations(out: &mut OutFile) { match *impl_key { ImplKey::RustBox(ident) => write_rust_box_extern(out, ident), ImplKey::RustVec(ident) => write_rust_vec_extern(out, ident), - ImplKey::UniquePtr(ident) => write_unique_ptr(out, ident), + ImplKey::UniquePtr(ident, deleter) => write_unique_ptr(out, ident, deleter), ImplKey::SharedPtr(ident) => write_shared_ptr(out, ident), ImplKey::WeakPtr(ident) => write_weak_ptr(out, ident), ImplKey::CxxVector(ident) => write_cxx_vector(out, ident), @@ -1621,8 +1631,8 @@ fn write_rust_vec_impl(out: &mut OutFile, key: NamedImplKey) { writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, key: NamedImplKey) { - let ty = UniquePtr::Ident(key.rust); +fn write_unique_ptr(out: &mut OutFile, key: NamedImplKey, deleter: Option<&Ident>) { + let ty = UniquePtr::Ident(key.rust, deleter); write_unique_ptr_common(out, ty); } @@ -1632,18 +1642,34 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) { out.include.utility = true; let inner = ty.to_typename(out.types); let instance = ty.to_mangled(out.types); + let (cxx_type_string, mangled_prefix, deleter_name) = + if let UniquePtr::Ident(_, Some(deleter)) = &ty { + let deleter_instance = deleter.to_mangled(out.types); + let deleter_inner = deleter.to_typename(out.types); + ( + format!("::std::unique_ptr<{}, {}>", inner, &deleter_inner), + format!("cxxbridge1$unique_ptr${}${}$", instance, deleter_instance), + Some(deleter_inner), + ) + } else { + ( + format!("::std::unique_ptr<{}>", inner), + format!("cxxbridge1$unique_ptr${}$", instance), + None, + ) + }; 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::Ident(ident, _) => out.types.is_maybe_trivial(ident), UniquePtr::CxxVector(_) => false, }; let conditional_delete = match ty { - UniquePtr::Ident(ident) => { + UniquePtr::Ident(ident, _) => { !out.types.structs.contains_key(ident) && !out.types.enums.contains_key(ident) } UniquePtr::CxxVector(_) => false, @@ -1652,7 +1678,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) { if conditional_delete { out.builtin.is_complete = true; let definition = match ty { - UniquePtr::Ident(ty) => &out.types.resolve(ty).name.cxx, + UniquePtr::Ident(ty, _) => &out.types.resolve(ty).name.cxx, UniquePtr::CxxVector(_) => unreachable!(), }; writeln!( @@ -1661,22 +1687,36 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) { inner, definition, ); } - writeln!( - out, - "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");", - inner, - ); - writeln!( - out, - "static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");", - inner, - ); + if let Some(deleter_name) = &deleter_name { + writeln!( + out, + "static_assert(sizeof(::std::unique_ptr<{inner}, {deleter_name}>) >= (sizeof(void *) + \ + sizeof({deleter_name})), \"Implausible size of unique_ptr with deleter {deleter_name}\");", + ); + out.include.type_traits = true; + writeln!( + out, + "static_assert(std::is_trivially_move_constructible<{deleter_name}>::value && std::is_trivially_destructible<{deleter_name}>::value, \ + \"Only trivial types are supported as deleter for unique_ptr\");", + ) + } else { + writeln!( + out, + "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");", + inner, + ); + writeln!( + out, + "static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");", + inner, + ); + } begin_function_definition(out); writeln!( out, - "void cxxbridge1$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", - instance, inner, + "void {}null(::std::unique_ptr<{}> *ptr) noexcept {{", + mangled_prefix, inner, ); writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>();", inner); writeln!(out, "}}"); @@ -1686,8 +1726,8 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) { begin_function_definition(out); writeln!( out, - "{} *cxxbridge1$unique_ptr${}$uninit(::std::unique_ptr<{}> *ptr) noexcept {{", - inner, instance, inner, + "{} *{}uninit(::std::unique_ptr<{}> *ptr) noexcept {{", + inner, mangled_prefix, inner, ); writeln!( out, @@ -1702,8 +1742,8 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) { begin_function_definition(out); writeln!( out, - "void cxxbridge1$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", - instance, inner, inner, + "void {}raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", + mangled_prefix, inner, inner, ); writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>(raw);", inner); writeln!(out, "}}"); @@ -1711,8 +1751,8 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) { begin_function_definition(out); writeln!( out, - "{} const *cxxbridge1$unique_ptr${}$get(::std::unique_ptr<{}> const &ptr) noexcept {{", - inner, instance, inner, + "{} const *{}get(::std::unique_ptr<{}> const &ptr) noexcept {{", + inner, mangled_prefix, inner, ); writeln!(out, " return ptr.get();"); writeln!(out, "}}"); @@ -1720,8 +1760,8 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) { begin_function_definition(out); writeln!( out, - "{} *cxxbridge1$unique_ptr${}$release(::std::unique_ptr<{}> &ptr) noexcept {{", - inner, instance, inner, + "{} *{}release(::std::unique_ptr<{}> &ptr) noexcept {{", + inner, mangled_prefix, inner, ); writeln!(out, " return ptr.release();"); writeln!(out, "}}"); @@ -1729,9 +1769,17 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) { begin_function_definition(out); writeln!( out, - "void cxxbridge1$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", - instance, inner, + "void {}drop({} *ptr) noexcept {{", + mangled_prefix, &cxx_type_string, ); + if deleter_name.is_some() { + // Check whether the deleter is really the first member as expected by the Rust type + out.include.cassert = true; + #[cfg(not(target_os = "macos"))] + writeln!(out, " assert(reinterpret_cast(&ptr->get_deleter()) == reinterpret_cast(ptr));"); + #[cfg(target_os = "macos")] + writeln!(out, " assert(reinterpret_cast(&ptr->get_deleter()) >= reinterpret_cast(ptr) + sizeof(void*));"); + } if conditional_delete { out.builtin.deleter_if = true; writeln!( diff --git a/macro/src/expand.rs b/macro/src/expand.rs index ff1ed2076..610558bc1 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -99,8 +99,8 @@ fn expand(ffi: Module, doc: Doc, attrs: OtherAttrs, apis: &[Api], types: &Types) ImplKey::RustVec(ident) => { hidden.extend(expand_rust_vec(ident, types, explicit_impl)); } - ImplKey::UniquePtr(ident) => { - expanded.extend(expand_unique_ptr(ident, types, explicit_impl)); + ImplKey::UniquePtr(ident, deleter) => { + expanded.extend(expand_unique_ptr(ident, types, explicit_impl, deleter)); } ImplKey::SharedPtr(ident) => { expanded.extend(expand_shared_ptr(ident, types, explicit_impl)); @@ -538,12 +538,8 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { quote_spanned!(span=> ::cxx::alloc::boxed::Box::into_raw(#var)) } } - Type::UniquePtr(ty) => { - if types.is_considered_improper_ctype(&ty.inner) { - quote_spanned!(span=> ::cxx::UniquePtr::into_raw(#var).cast()) - } else { - quote_spanned!(span=> ::cxx::UniquePtr::into_raw(#var)) - } + Type::UniquePtr(_) => { + quote_spanned!(span=> #var.as_mut_ptr()) } Type::RustVec(_) => quote_spanned!(span=> #var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), Type::Ref(ty) => match &ty.inner { @@ -666,13 +662,6 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { quote_spanned!(span=> #call.into_vec()) } } - Type::UniquePtr(ty) => { - if types.is_considered_improper_ctype(&ty.inner) { - quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#call.cast())) - } else { - quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#call)) - } - } Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { false => quote_spanned!(span=> #call.as_string()), @@ -1007,7 +996,7 @@ fn expand_rust_function_shim_impl( } Type::UniquePtr(_) => { requires_unsafe = true; - quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#var)) + quote_spanned!(span=> ::cxx::core::ptr::read(#var)) } Type::Ref(ty) => match &ty.inner { Type::Ident(i) if i.rust == RustString => match ty.mutable { @@ -1075,7 +1064,6 @@ fn expand_rust_function_shim_impl( Some(quote_spanned!(span=> ::cxx::private::RustVec::from)) } } - Type::UniquePtr(_) => Some(quote_spanned!(span=> ::cxx::UniquePtr::into_raw)), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { false => Some(quote_spanned!(span=> ::cxx::private::RustString::from_ref)), @@ -1422,11 +1410,38 @@ fn expand_unique_ptr( key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>, + deleter: Option<&Ident>, ) -> 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 unnamed_lifetimes = if key.lifetimes.is_empty() { + TokenStream::default() + } else { + let unnamed = quote! { '_ }; + let lt_iter = key.lifetimes.iter().map(|_| &unnamed); + quote! { <#(#lt_iter),*> } + }; + + let (deleter_token, unique_ptr_token, prefix) = if let Some(deleter) = deleter { + let resolve_deleter = types.resolve(deleter); + ( + quote! { <#deleter> }, + quote! { ::cxx::UniquePtr<#ident #unnamed_lifetimes, #deleter> }, + format!( + "cxxbridge1$unique_ptr${}${}$", + resolve.name.to_symbol(), + resolve_deleter.name.to_symbol() + ), + ) + } else { + ( + TokenStream::default(), + quote! { ::cxx::UniquePtr<#ident #unnamed_lifetimes> }, + format!("cxxbridge1$unique_ptr${}$", resolve.name.to_symbol()), + ) + }; + let link_null = format!("{}null", prefix); let link_uninit = format!("{}uninit", prefix); let link_raw = format!("{}raw", prefix); @@ -1460,7 +1475,7 @@ fn expand_unique_ptr( let unsafe_token = format_ident!("unsafe", span = begin_span); quote_spanned! {end_span=> - #unsafe_token impl #impl_generics ::cxx::private::UniquePtrTarget for #ident #ty_generics { + #unsafe_token impl #impl_generics ::cxx::private::UniquePtrTarget #deleter_token for #ident #ty_generics { fn __typename(f: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { f.write_str(#name) } @@ -1501,13 +1516,13 @@ fn expand_unique_ptr( } unsafe { __release(&mut repr).cast() } } - unsafe fn __drop(mut repr: ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>) { + unsafe fn __drop(this: &mut #unique_ptr_token) where Self: Sized { extern "C" { #[link_name = #link_drop] - fn __drop(this: *mut ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void>); + fn __drop(this: &mut #unique_ptr_token); } unsafe { - __drop(&mut repr); + __drop(this); } } } @@ -1839,7 +1854,7 @@ fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream { let span = ident.rust.span(); quote_spanned!(span=> ::cxx::private::RustString) } - Type::RustBox(ty) | Type::UniquePtr(ty) => { + Type::RustBox(ty) => { let span = ty.name.span(); if proper && types.is_considered_improper_ctype(&ty.inner) { quote_spanned!(span=> *mut ::cxx::core::ffi::c_void) @@ -1848,6 +1863,16 @@ fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream { quote_spanned!(span=> *mut #inner) } } + Type::UniquePtr(ty) => { + let span = ty.name.span(); + let inner = expand_extern_type(&ty.first, types, proper); + if let Some(deleter) = &ty.second { + let deleter = expand_extern_type(deleter, types, proper); + quote_spanned!(span=> ::cxx::UniquePtr<#inner, #deleter>) + } else { + quote_spanned!(span=> ::cxx::UniquePtr<#inner>) + } + } Type::RustVec(ty) => { let span = ty.name.span(); let langle = ty.langle; diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 33992059e..61769b288 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -9,61 +9,119 @@ use core::marker::PhantomData; use core::mem::{self, MaybeUninit}; use core::ops::{Deref, DerefMut}; use core::pin::Pin; +use cxx::type_id; -/// Binding to C++ `std::unique_ptr>`. +/// Rust representation of the default deleter of std::unique_ptr, `std::default_delete` +#[derive(Default, Copy, Clone, Debug)] #[repr(C)] -pub struct UniquePtr -where - T: UniquePtrTarget, -{ +pub struct DefaultDeleter([u8; 0]); + +unsafe impl ExternType for DefaultDeleter { + type Id = type_id!("std::default_delete"); + type Kind = Trivial; +} + +#[cfg(not(target_os = "macos"))] +#[repr(C)] +struct UniquePtrInner { + deleter: D, repr: MaybeUninit<*mut c_void>, ty: PhantomData, } -impl UniquePtr +#[cfg(target_os = "macos")] +#[repr(C)] +struct UniquePtrInner { + repr: MaybeUninit<*mut c_void>, + deleter: D, + ty: PhantomData, +} + +/// Binding to C++ `std::unique_ptr`. +/// +/// This representation assumes that the deleter is +/// 1. the first member of the smart pointer. +/// 2. optimized away by some form of empty base class optimization in case the deleter is stateless +/// (which is the case for the default deleter). For this, a zero-sized type is used on the Rust +/// side. +/// 3. trivial to copy and move. This is trivially true for all stateless deleters as well as +/// deleters that just contain references/pointers (e.g. when using an std::pmr::memory_resource) +#[repr(transparent)] +pub struct UniquePtr +where + T: UniquePtrTarget, + D: ExternType, +{ + inner: UniquePtrInner, +} + +impl UniquePtr where - T: UniquePtrTarget, + T: UniquePtrTarget, + D: ExternType, { + fn make_ptr(repr: MaybeUninit<*mut c_void>, deleter: D) -> Self { + UniquePtr { + inner: UniquePtrInner { + deleter, + repr, + ty: PhantomData, + }, + } + } + /// Makes a new UniquePtr wrapping a null pointer. /// /// Matches the behavior of default-constructing a std::unique\_ptr. - pub fn null() -> Self { - UniquePtr { - repr: T::__null(), - ty: PhantomData, - } + pub fn null() -> Self + where + D: Default, + { + UniquePtr::make_ptr(T::__null(), D::default()) } /// Allocates memory on the heap and makes a UniquePtr pointing to it. + /// + /// The deleter will be default-constructed. Please note that the memory is allocated using + /// operator ::new. The deleter needs to be able to handle such a pointer on deletion. pub fn new(value: T) -> Self where T: ExternType, + D: Default, { - UniquePtr { - repr: T::__new(value), - ty: PhantomData, - } + UniquePtr::make_ptr(T::__new(value), D::default()) + } + + /// Allocates memory on the heap and makes a UniquePtr pointing to it. + /// + /// Please note that the memory is allocated using operator ::new. The deleter needs to be able + /// to handle such a pointer on deletion. + pub fn with_deleter(value: T, deleter: D) -> Self + where + T: ExternType, + { + UniquePtr::make_ptr(T::__new(value), deleter) } /// Checks whether the UniquePtr does not own an object. /// /// This is the opposite of [std::unique_ptr\::operator bool](https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_bool). pub fn is_null(&self) -> bool { - let ptr = unsafe { T::__get(self.repr) }; + let ptr = unsafe { T::__get(self.inner.repr) }; ptr.is_null() } /// Returns a reference to the object owned by this UniquePtr if any, /// otherwise None. pub fn as_ref(&self) -> Option<&T> { - unsafe { T::__get(self.repr).as_ref() } + unsafe { T::__get(self.inner.repr).as_ref() } } /// Returns a mutable pinned reference to the object owned by this UniquePtr /// if any, otherwise None. pub fn as_mut(&mut self) -> Option> { unsafe { - let mut_reference = (T::__get(self.repr) as *mut T).as_mut()?; + let mut_reference = (T::__get(self.inner.repr) as *mut T).as_mut()?; Some(Pin::new_unchecked(mut_reference)) } } @@ -88,7 +146,7 @@ where /// /// Matches the behavior of [std::unique_ptr\::release](https://en.cppreference.com/w/cpp/memory/unique_ptr/release). pub fn into_raw(self) -> *mut T { - let ptr = unsafe { T::__release(self.repr) }; + let ptr = unsafe { T::__release(self.inner.repr) }; mem::forget(self); ptr } @@ -100,34 +158,72 @@ where /// /// This function is unsafe because improper use may lead to memory /// problems. For example a double-free may occur if the function is called - /// twice on the same raw pointer. - pub unsafe fn from_raw(raw: *mut T) -> Self { - UniquePtr { - repr: unsafe { T::__raw(raw) }, - ty: PhantomData, - } + /// twice on the same raw pointer. In addition, the caller must make sure + /// that the used deleter is able to handle the given pointer when default + /// constructed. + pub unsafe fn from_raw(raw: *mut T) -> Self + where + D: Default, + { + UniquePtr::make_ptr(unsafe { T::__raw(raw) }, D::default()) + } + + /// Constructs a UniquePtr retaking ownership of a pointer previously + /// obtained from `into_raw`. + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead to memory + /// problems. For example a double-free may occur if the function is called + /// twice on the same raw pointer. In addition, the given deleter needs to + /// be able to handle the pointer. + pub unsafe fn from_raw_with_deleter(raw: *mut T, deleter: D) -> Self { + UniquePtr::make_ptr(unsafe { T::__raw(raw) }, deleter) + } + + /// Returns a reference to the deleter used during destruction or releasing of the pointee. + pub fn get_deleter(&self) -> &D { + &self.inner.deleter } } -unsafe impl Send for UniquePtr where T: Send + UniquePtrTarget {} -unsafe impl Sync for UniquePtr where T: Sync + UniquePtrTarget {} +unsafe impl Send for UniquePtr +where + T: Send + UniquePtrTarget, + D: Send + ExternType, +{ +} + +unsafe impl Sync for UniquePtr +where + T: Sync + UniquePtrTarget, + D: Sync + ExternType, +{ +} // UniquePtr is not a self-referential type and is safe to move out of a Pin, // regardless whether the pointer's target is Unpin. -impl Unpin for UniquePtr where T: UniquePtrTarget {} +impl Unpin for UniquePtr +where + T: UniquePtrTarget, + D: Unpin + ExternType, +{ +} -impl Drop for UniquePtr +impl Drop for UniquePtr where - T: UniquePtrTarget, + T: UniquePtrTarget, + D: ExternType, { fn drop(&mut self) { - unsafe { T::__drop(self.repr) } + unsafe { T::__drop(self) } } } -impl Deref for UniquePtr +impl Deref for UniquePtr where - T: UniquePtrTarget, + T: UniquePtrTarget, + D: ExternType, { type Target = T; @@ -142,9 +238,10 @@ where } } -impl DerefMut for UniquePtr +impl DerefMut for UniquePtr where - T: UniquePtrTarget + Unpin, + T: UniquePtrTarget + Unpin, + D: ExternType, { fn deref_mut(&mut self) -> &mut Self::Target { match self.as_mut() { @@ -157,9 +254,10 @@ where } } -impl Debug for UniquePtr +impl Debug for UniquePtr where - T: Debug + UniquePtrTarget, + T: Debug + UniquePtrTarget, + D: ExternType, { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self.as_ref() { @@ -169,9 +267,10 @@ where } } -impl Display for UniquePtr +impl Display for UniquePtr where - T: Display + UniquePtrTarget, + T: Display + UniquePtrTarget, + D: ExternType, { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self.as_ref() { @@ -206,7 +305,7 @@ where /// /// Writing the same generic function without a `UniquePtrTarget` trait bound /// would not compile. -pub unsafe trait UniquePtrTarget { +pub unsafe trait UniquePtrTarget { #[doc(hidden)] fn __typename(f: &mut fmt::Formatter) -> fmt::Result; #[doc(hidden)] @@ -228,7 +327,10 @@ pub unsafe trait UniquePtrTarget { #[doc(hidden)] unsafe fn __release(repr: MaybeUninit<*mut c_void>) -> *mut Self; #[doc(hidden)] - unsafe fn __drop(repr: MaybeUninit<*mut c_void>); + unsafe fn __drop(ptr: &mut UniquePtr) + where + Self: Sized, + D: ExternType; } extern "C" { @@ -266,8 +368,11 @@ unsafe impl UniquePtrTarget for CxxString { unsafe fn __release(mut repr: MaybeUninit<*mut c_void>) -> *mut Self { unsafe { unique_ptr_std_string_release(&mut repr) } } - unsafe fn __drop(mut repr: MaybeUninit<*mut c_void>) { - unsafe { unique_ptr_std_string_drop(&mut repr) } + unsafe fn __drop(ptr: &mut UniquePtr) + where + Self: Sized, + { + unsafe { unique_ptr_std_string_drop(&mut ptr.inner.repr) } } } @@ -290,7 +395,10 @@ where unsafe fn __release(repr: MaybeUninit<*mut c_void>) -> *mut Self { unsafe { T::__unique_ptr_release(repr) } } - unsafe fn __drop(repr: MaybeUninit<*mut c_void>) { - unsafe { T::__unique_ptr_drop(repr) } + unsafe fn __drop(ptr: &mut UniquePtr) + where + Self: Sized, + { + unsafe { T::__unique_ptr_drop(ptr.inner.repr) } } } diff --git a/syntax/check.rs b/syntax/check.rs index b5fd45e11..719583332 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -3,7 +3,8 @@ use crate::syntax::report::Errors; use crate::syntax::visit::{self, Visit}; use crate::syntax::{ error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, Lifetimes, - NamedType, Ptr, Receiver, Ref, Signature, SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types, + NamedType, Ptr, Receiver, Ref, Signature, SliceRef, Struct, Trait, Ty1, Ty2, Type, TypeAlias, + Types, }; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; @@ -138,8 +139,8 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { cx.error(ty, "unsupported element type of Vec"); } -fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { - if let Type::Ident(ident) = &ptr.inner { +fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty2) { + if let Type::Ident(ident) = &ptr.first { if cx.types.rust.contains(&ident.rust) { cx.error(ptr, "unique_ptr of a Rust type is not supported yet"); return; @@ -149,7 +150,7 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { None | Some(CxxString) => return, _ => {} } - } else if let Type::CxxVector(_) = &ptr.inner { + } else if let Type::CxxVector(_) = &ptr.first { return; } @@ -523,7 +524,6 @@ fn check_api_impl(cx: &mut Check, imp: &Impl) { match ty { Type::RustBox(ty) | Type::RustVec(ty) - | Type::UniquePtr(ty) | Type::SharedPtr(ty) | Type::WeakPtr(ty) | Type::CxxVector(ty) => { @@ -533,6 +533,13 @@ fn check_api_impl(cx: &mut Check, imp: &Impl) { } } } + Type::UniquePtr(ty) => { + if let Type::Ident(inner) = &ty.first { + if Atom::from(&inner.rust).is_none() { + return; + } + } + } _ => {} } diff --git a/syntax/impls.rs b/syntax/impls.rs index 36e1f322a..2eb55e2f2 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,5 +1,6 @@ use crate::syntax::{ - Array, ExternFn, Include, Lifetimes, Ptr, Receiver, Ref, Signature, SliceRef, Ty1, Type, Var, + Array, ExternFn, Include, Lifetimes, Ptr, Receiver, Ref, Signature, SliceRef, Ty1, Ty2, Type, + Var, }; use std::hash::{Hash, Hasher}; use std::mem; @@ -148,6 +149,22 @@ impl Hash for Ty1 { } } +impl Eq for Ty2 {} + +impl PartialEq for Ty2 { + fn eq(&self, other: &Self) -> bool { + self.first == other.first && self.second == other.second && self.name == other.name + } +} + +impl Hash for Ty2 { + fn hash(&self, state: &mut H) { + self.name.hash(state); + self.first.hash(state); + self.second.hash(state); + } +} + impl Eq for Ref {} impl PartialEq for Ref { diff --git a/syntax/instantiate.rs b/syntax/instantiate.rs index dda306982..ee5369195 100644 --- a/syntax/instantiate.rs +++ b/syntax/instantiate.rs @@ -1,13 +1,13 @@ -use crate::syntax::{NamedType, Ty1, Type}; +use crate::syntax::{NamedType, Ty1, Ty2, Type}; use proc_macro2::{Ident, Span}; use std::hash::{Hash, Hasher}; -use syn::Token; +use syn::{Lifetime, Token, punctuated::Punctuated}; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub(crate) enum ImplKey<'a> { RustBox(NamedImplKey<'a>), RustVec(NamedImplKey<'a>), - UniquePtr(NamedImplKey<'a>), + UniquePtr(NamedImplKey<'a>, Option<&'a Ident>), SharedPtr(NamedImplKey<'a>), WeakPtr(NamedImplKey<'a>), CxxVector(NamedImplKey<'a>), @@ -21,6 +21,8 @@ pub(crate) struct NamedImplKey<'a> { #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub lt_token: Option, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build + pub lifetimes: &'a Punctuated, + #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub gt_token: Option]>, #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build pub end_span: Span, @@ -37,8 +39,18 @@ impl Type { 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))); + let deleter = ty.second.as_ref().and_then(|ty| { + if let Type::Ident(ident) = ty { + Some(&ident.rust) + } else { + None + } + }); + if let Type::Ident(ident) = &ty.first { + return Some(ImplKey::UniquePtr( + NamedImplKey::new_ty2(ty, ident), + deleter, + )); } } else if let Type::SharedPtr(ty) = self { if let Type::Ident(ident) = &ty.inner { @@ -77,6 +89,18 @@ impl<'a> NamedImplKey<'a> { begin_span: outer.name.span(), rust: &inner.rust, lt_token: inner.generics.lt_token, + lifetimes: &inner.generics.lifetimes, + gt_token: inner.generics.gt_token, + end_span: outer.rangle.span, + } + } + + fn new_ty2(outer: &Ty2, inner: &'a NamedType) -> Self { + NamedImplKey { + begin_span: outer.name.span(), + rust: &inner.rust, + lt_token: inner.generics.lt_token, + lifetimes: &inner.generics.lifetimes, gt_token: inner.generics.gt_token, end_span: outer.rangle.span, } diff --git a/syntax/mod.rs b/syntax/mod.rs index eacba5541..c16776a26 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -264,7 +264,7 @@ pub(crate) enum Type { Ident(NamedType), RustBox(Box), RustVec(Box), - UniquePtr(Box), + UniquePtr(Box), SharedPtr(Box), WeakPtr(Box), Ref(Box), @@ -284,6 +284,14 @@ pub(crate) struct Ty1 { pub rangle: Token![>], } +pub(crate) struct Ty2 { + pub name: Ident, + pub langle: Token![<], + pub first: Type, + pub second: Option, + pub rangle: Token![>], +} + pub(crate) struct Ref { pub pinned: bool, pub ampersand: Token![&], diff --git a/syntax/parse.rs b/syntax/parse.rs index 850dcc8d1..8200083bf 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -7,7 +7,7 @@ use crate::syntax::Atom::*; use crate::syntax::{ attrs, error, Api, Array, Derive, Doc, Enum, EnumRepr, ExternFn, ExternType, ForeignName, Impl, Include, IncludeKind, Lang, Lifetimes, NamedType, Namespace, Pair, Ptr, Receiver, Ref, - Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var, Variant, + Signature, SliceRef, Struct, Ty1, Ty2, Type, TypeAlias, Var, Variant, }; use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; @@ -1063,13 +1063,16 @@ fn parse_impl(cx: &mut Errors, imp: ItemImpl) -> Result { 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::UniquePtr(ty) => match &ty.first { + Type::Ident(ident) => ident.generics.clone(), + _ => Lifetimes::default(), + }, Type::Ident(_) | Type::Ref(_) | Type::Ptr(_) @@ -1221,13 +1224,27 @@ fn parse_type_path(ty: &TypePath) -> Result { match &segment.arguments { PathArguments::None => return Ok(Type::Ident(NamedType::new(ident))), PathArguments::AngleBracketed(generic) => { - if ident == "UniquePtr" && generic.args.len() == 1 { - if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; - return Ok(Type::UniquePtr(Box::new(Ty1 { + if ident == "UniquePtr" && (1..=2).contains(&generic.args.len()) { + let first = if let GenericArgument::Type(arg) = &generic.args[0] { + Some(parse_type(arg)?) + } else { + None + }; + let second = if generic.args.len() == 2 { + if let GenericArgument::Type(arg) = &generic.args[1] { + Some(parse_type(arg)?) + } else { + None + } + } else { + None + }; + if let Some(first) = first { + return Ok(Type::UniquePtr(Box::new(Ty2 { name: ident, langle: generic.lt_token, - inner, + first, + second, rangle: generic.gt_token, }))); } diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 05eddc703..47f8bba25 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ Array, Atom, Derive, Enum, EnumRepr, ExternFn, ExternType, Impl, Lifetimes, NamedType, Ptr, - Ref, Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var, + Ref, Signature, SliceRef, Struct, Ty1, Ty2, Type, TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; @@ -24,11 +24,11 @@ impl ToTokens for Type { ident.to_tokens(tokens); } Type::RustBox(ty) - | Type::UniquePtr(ty) | Type::SharedPtr(ty) | Type::WeakPtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => ty.to_tokens(tokens), + Type::UniquePtr(ty) => ty.to_tokens(tokens), Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), Type::Ptr(p) => p.to_tokens(tokens), Type::Array(a) => a.to_tokens(tokens), @@ -84,6 +84,26 @@ impl ToTokens for Ty1 { } } +impl ToTokens for Ty2 { + fn to_tokens(&self, tokens: &mut TokenStream) { + let span = self.name.span(); + match self.name.to_string().as_str() { + "UniquePtr" => { + tokens.extend(quote_spanned!(span=> ::cxx::)); + } + _ => panic!("Unknown type name"), + } + self.name.to_tokens(tokens); + self.langle.to_tokens(tokens); + self.first.to_tokens(tokens); + if let Some(ty) = &self.second { + Token![,](span).to_tokens(tokens); + ty.to_tokens(tokens); + } + self.rangle.to_tokens(tokens); + } +} + impl ToTokens for Ref { fn to_tokens(&self, tokens: &mut TokenStream) { let Ref { diff --git a/syntax/types.rs b/syntax/types.rs index 623a8b8d6..2279f7528 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -178,7 +178,7 @@ impl<'a> Types<'a> { let implicit_impl = match impl_key { ImplKey::RustBox(ident) | ImplKey::RustVec(ident) - | ImplKey::UniquePtr(ident) + | ImplKey::UniquePtr(ident, _) | ImplKey::SharedPtr(ident) | ImplKey::WeakPtr(ident) | ImplKey::CxxVector(ident) => { @@ -243,8 +243,8 @@ impl<'a> Types<'a> { pub(crate) fn needs_indirect_abi(&self, ty: &Type) -> bool { match ty { - Type::RustBox(_) | Type::UniquePtr(_) => false, - Type::Array(_) => true, + Type::RustBox(_) => false, + Type::Array(_) | Type::UniquePtr(_) => true, _ => !self.is_guaranteed_pod(ty), } } diff --git a/syntax/visit.rs b/syntax/visit.rs index e31b8c41b..34dbf4421 100644 --- a/syntax/visit.rs +++ b/syntax/visit.rs @@ -13,11 +13,11 @@ where match ty { Type::Ident(_) | Type::Str(_) | Type::Void(_) => {} Type::RustBox(ty) - | Type::UniquePtr(ty) | Type::SharedPtr(ty) | Type::WeakPtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => visitor.visit_type(&ty.inner), + Type::UniquePtr(ty) => visitor.visit_type(&ty.first), Type::Ref(r) => visitor.visit_type(&r.inner), Type::Ptr(p) => visitor.visit_type(&p.inner), Type::Array(a) => visitor.visit_type(&a.inner), diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index a1a64b7f0..1b40ce332 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -13,6 +13,8 @@ fn main() { build.warnings_into_errors(cfg!(deny_warnings)); if cfg!(not(target_env = "msvc")) { build.define("CXX_TEST_INSTANTIATIONS", None); + } else { + build.flag_if_supported("/Zc:__cplusplus"); } build.compile("cxx-test-suite"); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index f3a8310f1..f57a53040 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -19,10 +19,27 @@ pub mod cast; pub mod module; use cxx::{type_id, CxxString, CxxVector, ExternType, SharedPtr, UniquePtr}; +use std::ffi::c_void; use std::fmt::{self, Display}; use std::mem::MaybeUninit; use std::os::raw::c_char; +#[repr(C)] +pub struct polymorphic_allocator { + memory_resource: *mut c_void, +} + +#[repr(C)] +pub struct PmrDeleterForC { + alloc: polymorphic_allocator, + deleter_callback: fn(), +} + +unsafe impl ExternType for PmrDeleterForC { + type Id = type_id!("tests::PmrDeleterForC"); + type Kind = cxx::kind::Trivial; +} + #[cxx::bridge(namespace = "tests")] pub mod ffi { #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -92,6 +109,10 @@ pub mod ffi { s: &'a str, } + extern "C++" { + type PmrDeleterForC = crate::PmrDeleterForC; + } + unsafe extern "C++" { include!("tests/ffi/tests.h"); @@ -101,6 +122,7 @@ pub mod ffi { fn c_return_shared() -> Shared; fn c_return_box() -> Box; fn c_return_unique_ptr() -> UniquePtr; + fn c_return_unique_ptr_with_deleter() -> UniquePtr; fn c_return_shared_ptr() -> SharedPtr; fn c_return_ref(shared: &Shared) -> &usize; fn c_return_mut(shared: &mut Shared) -> &mut usize; @@ -339,6 +361,7 @@ pub mod ffi { } impl Box {} + impl CxxVector {} } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 8cf74bebb..48c3f76f9 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -2,6 +2,7 @@ #include "tests/ffi/lib.rs.h" #include #include +#include #include #include #include @@ -12,12 +13,77 @@ extern "C" void cxx_test_suite_set_correct() noexcept; extern "C" tests::R *cxx_test_suite_get_box() noexcept; extern "C" bool cxx_test_suite_r_is_correct(const tests::R *) noexcept; +extern "C" bool PmrDeleterForC_callback_used; + +bool PmrDeleterForC_callback_used{false}; namespace tests { +#if __cplusplus < 201703L + +class default_resource final : public memory_resource { + private: + static constexpr std::size_t BASE_PTR_BYTES{sizeof(std::uint8_t*) + alignof(std::uint8_t*)-1U}; // Additional space after the data holding a pointer to the allocated buffer + static std::uint8_t** aligned_base_ptr(void* const ptr) { + void* base_ptr = ptr; + size_t base_ptr_bytes = BASE_PTR_BYTES; + return reinterpret_cast(std::align(alignof(std::uint8_t*), sizeof(std::uint8_t*), base_ptr, base_ptr_bytes)); + } + + void* do_allocate(std::size_t bytes, std::size_t alignment) override { + std::size_t buffer_bytes = bytes + alignment-1; // Actual buffer for the data, with headroom for alignment + const std::size_t total_bytes = buffer_bytes + BASE_PTR_BYTES; // Number of bytes needed for the data and the bookkeeping + auto* const base_buffer = new std::uint8_t[total_bytes]; + void* buffer = base_buffer; + if (std::align(alignment, bytes, buffer, buffer_bytes) == nullptr) { + delete[] base_buffer; + return nullptr; + } + auto* const buffer_end = static_cast(buffer) + bytes; // Pointer to the first byte after the data + std::uint8_t** const base_ptr = aligned_base_ptr(buffer_end); + if (base_ptr == nullptr) { + delete[] base_buffer; + return nullptr; + } + + *base_ptr = base_buffer; // Store a pointer to the allocated buffer + return buffer; + } + + void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override { + auto* const buffer_end = static_cast(p) + bytes; + std::uint8_t** const base_ptr = aligned_base_ptr(buffer_end); + auto* const base_buffer = *base_ptr; + delete[] base_buffer; + } + + bool do_is_equal(const memory_resource& other) const noexcept override { + auto* other_me = dynamic_cast(&other); + return other_me != nullptr; + } +}; + +memory_resource* new_delete_resource() noexcept { + static default_resource instance; + return &instance; +} + +#else // __cplusplus < 201703L + +namespace { +memory_resource* new_delete_resource() { + return ::std::pmr::new_delete_resource(); +} +} + +#endif // __cplusplus < 201703L + static constexpr char SLICE_DATA[] = "2020"; -C::C(size_t n) : n(n) {} +C::C(size_t n) + : n(n), + v{} +{} size_t C::get() const { return this->n; } @@ -76,6 +142,20 @@ std::unique_ptr c_return_unique_ptr() { return std::unique_ptr(new C{2020}); } +void PmrDeleterForC::operator()(C* obj) { + alloc.destroy(obj); + alloc.deallocate(obj, 1); + PmrDeleterForC_callback_used = true; +} + +std::unique_ptr c_return_unique_ptr_with_deleter() { + auto* resource{new_delete_resource()}; + polymorphic_allocator alloc{resource}; + C* ptr{alloc.allocate(1)}; + alloc.construct(ptr, 42U); + return std::unique_ptr{ptr, {alloc}}; +} + std::shared_ptr c_return_shared_ptr() { return std::shared_ptr(new C{2020}); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index dc02e4ff8..e401a7c6f 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -3,6 +3,10 @@ #include #include +#if __cplusplus >= 201703L +#include +#endif + namespace A { struct AShared; enum class AEnum : uint16_t; @@ -86,12 +90,81 @@ struct Borrow { typedef char Buffer[12]; +#if __cplusplus < 201703L + +class memory_resource { +public: + virtual ~memory_resource() = default; + + void* allocate(std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) { + return do_allocate(bytes, alignment); + } + void deallocate(void* p, std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) { + do_deallocate(p, bytes, alignment); + } + bool is_equal(const memory_resource& other) const noexcept { + return do_is_equal(other); + } + +private: + virtual void* do_allocate(std::size_t bytes, std::size_t alignment) = 0; + virtual void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) = 0; + virtual bool do_is_equal(const memory_resource& other) const noexcept = 0; +}; + +memory_resource* get_default_resource() noexcept; + +template +class polymorphic_allocator { +public: + polymorphic_allocator(memory_resource* r) : memory_resource_{r} {} + + T* allocate(std::size_t n) { + return static_cast(memory_resource_->allocate(sizeof(T) * n, alignof(T))); + } + + void deallocate(T* p, std::size_t n) { + memory_resource_->deallocate(p, sizeof(T) * n, alignof(T)); + } + + template + void construct(U* p, Args... args) { + new (p) U{std::forward(args)...}; + } + + template + void destroy(U* p) { + p->~U(); + } + + memory_resource* resource() const { return memory_resource_; } +private: + memory_resource* memory_resource_; +}; + +#else // __cplusplus < 201703L + +#include + +using memory_resource = ::std::pmr::memory_resource; + +template +using polymorphic_allocator = ::std::pmr::polymorphic_allocator; + +#endif // __cplusplus < 201703L + +struct PmrDeleterForC final { + polymorphic_allocator alloc; + void operator()(C* obj); +}; + size_t c_return_primitive(); Shared c_return_shared(); ::A::AShared c_return_ns_shared(); ::A::B::ABShared c_return_nested_ns_shared(); rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); +std::unique_ptr c_return_unique_ptr_with_deleter(); std::shared_ptr c_return_shared_ptr(); std::unique_ptr<::H::H> c_return_ns_unique_ptr(); const size_t &c_return_ref(const Shared &shared); diff --git a/tests/test.rs b/tests/test.rs index 6ef9a8293..c9da564cf 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -33,6 +33,10 @@ macro_rules! check { }}; } +extern "C" { + static PmrDeleterForC_callback_used: bool; +} + #[test] fn test_c_return() { let shared = ffi::Shared { z: 2020 }; @@ -43,6 +47,12 @@ fn test_c_return() { assert_eq!(2020, ffi::c_return_shared().z); assert_eq!(2020, ffi::c_return_box().0); ffi::c_return_unique_ptr(); + { + ffi::c_return_unique_ptr_with_deleter(); + } + unsafe { + assert!(PmrDeleterForC_callback_used); + } ffi2::c_return_ns_unique_ptr(); assert_eq!(2020, *ffi::c_return_ref(&shared)); assert_eq!(2020, *ffi::c_return_ns_ref(&ns_shared)); @@ -367,7 +377,7 @@ fn test_extern_opaque() { #[test] fn test_raw_ptr() { let c = ffi::c_return_mut_ptr(2023); - let mut c_unique = unsafe { cxx::UniquePtr::from_raw(c) }; + let mut c_unique = unsafe { cxx::UniquePtr::::from_raw(c) }; assert_eq!(2023, c_unique.pin_mut().set_succeed(2023).unwrap()); // c will be dropped as it's now in a UniquePtr diff --git a/tests/ui/unique_ptr_to_opaque.stderr b/tests/ui/unique_ptr_to_opaque.stderr index 7aa5d8ae9..e680432ae 100644 --- a/tests/ui/unique_ptr_to_opaque.stderr +++ b/tests/ui/unique_ptr_to_opaque.stderr @@ -11,11 +11,11 @@ note: expected this to be `Trivial` | 8 | type Kind = cxx::kind::Opaque; | ^^^^^^^^^^^^^^^^^ -note: required by a bound in `UniquePtr::::new` +note: required by a bound in `UniquePtr::::new` --> src/unique_ptr.rs | | pub fn new(value: T) -> Self | --- required by a bound in this associated function | where | T: ExternType, - | ^^^^^^^^^^^^^^ required by this bound in `UniquePtr::::new` + | ^^^^^^^^^^^^^^ required by this bound in `UniquePtr::::new`