From 7249c36bec6b5dfc2ee4368a04af6911ecba275c Mon Sep 17 00:00:00 2001 From: Eric Kuecks Date: Sun, 24 Mar 2024 17:49:11 -0700 Subject: [PATCH] Use trait indirection for Option FFI --- macro/src/expand.rs | 130 +++-- src/lib.rs | 5 +- src/rust_option.rs | 722 +++++++++++++++++----------- src/symbols/rust_option.rs | 78 +-- tests/ffi/Cargo.toml | 2 +- tests/ui/option_not_sized.rs | 3 +- tests/ui/option_not_sized.stderr | 62 +-- tests/ui/option_safe_unsized.rs | 3 - tests/ui/option_safe_unsized.stderr | 13 - tests/ui/option_safe_usize.rs | 3 - tests/ui/option_safe_usize.stderr | 13 - 11 files changed, 590 insertions(+), 444 deletions(-) delete mode 100644 tests/ui/option_safe_unsized.rs delete mode 100644 tests/ui/option_safe_unsized.stderr delete mode 100644 tests/ui/option_safe_usize.rs delete mode 100644 tests/ui/option_safe_usize.stderr diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 6082e61a1..38b42e4f1 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -553,36 +553,37 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { Type::RustVec(_) => quote_spanned!(span=> #var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), Type::RustOption(ty) => { let improper; + let convert = quote!(<#ty as ::cxx::private::OptionFfi>::into_ffi(#var)); let call = match &ty.inner { Type::RustBox(ty) => { improper = types.is_considered_improper_ctype(&ty.inner); - quote_spanned!(span=> ::cxx::private::RustOption::from(#var)) + quote_spanned!(span=> #convert) } Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => { improper = false; match ty.mutable { - false => quote_spanned!(span=> ::cxx::private::RustOption::from_option_string_ref(#var)), - true => quote_spanned!(span=> ::cxx::private::RustOption::from_option_string_mut(#var)), + false => quote_spanned!(span=> #convert.into_rust_option_rust_string_ref()), + true => quote_spanned!(span=> #convert.into_rust_option_rust_string_mut()), } }, Type::RustVec(vec) if vec.inner == RustString => { improper = false; match ty.mutable { - false => quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_string_ref(#var)), - true => quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_string_mut(#var)), + false => quote_spanned!(span=> #convert.into_rust_option_rust_vec_rust_string_ref()), + true => quote_spanned!(span=> #convert.into_rust_option_rust_vec_rust_string_mut()), } }, Type::RustVec(_) => { improper = false; match ty.mutable { - false => quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_ref(#var)), - true => quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_mut(#var)), + false => quote_spanned!(span=> #convert.into_rust_option_rust_vec_ref()), + true => quote_spanned!(span=> #convert.into_rust_option_rust_vec_mut()), } }, _ => { improper = types.is_considered_improper_ctype(&ty.inner); - quote!(::cxx::private::RustOption::from(#var)) + quote!(#convert) } }, _ => unreachable!(), @@ -721,39 +722,40 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { } } Type::RustOption(ty) => { - let inner = &ty.inner; + let abs_ty = quote!(<#ty as ::cxx::private::OptionFfi>); + let abs_ty_target = quote!(#abs_ty::Target); match &ty.inner { Type::Ref(r) => match &r.inner { Type::Ident(ident) if ident.rust == RustString => match r.mutable { false => { - quote_spanned!(span=> ::cxx::private::RustOption::<&_>::from_raw(#call).into_option_string_ref()) + quote_spanned!(span=> #abs_ty_target::into_option_string_ref(#abs_ty_target::from_raw_repr(#call))) } true => { - quote_spanned!(span=> ::cxx::private::RustOption::<&mut _>::from_raw(#call).into_option_string_mut()) + quote_spanned!(span=> #abs_ty_target::into_option_string_mut(#abs_ty_target::from_raw_repr(#call))) } }, Type::RustVec(vec) if vec.inner == RustString => match r.mutable { false => { - quote_spanned!(span=> ::cxx::private::RustOption::<&_>::from_raw(#call).into_option_vec_string_ref()) + quote_spanned!(span=> #abs_ty_target::into_option_vec_string_ref(#abs_ty_target::from_raw_double_repr(#call))) } true => { - quote_spanned!(span=> ::cxx::private::RustOption::<&mut _>::from_raw(#call).into_option_vec_string_mut()) + quote_spanned!(span=> #abs_ty_target::into_option_vec_string_mut(#abs_ty_target::from_raw_double_repr(#call))) } }, Type::RustVec(_) => match r.mutable { false => { - quote_spanned!(span=> ::cxx::private::RustOption::<&_>::from_raw(#call).into_option_vec_ref()) + quote_spanned!(span=> #abs_ty_target::into_option_vec_ref(#abs_ty_target::from_raw_repr(#call))) } true => { - quote_spanned!(span=> ::cxx::private::RustOption::<&mut _>::from_raw(#call).into_option_vec_mut()) + quote_spanned!(span=> #abs_ty_target::into_option_vec_mut(#abs_ty_target::from_raw_repr(#call))) } }, _ => { - quote_spanned!(span=> ::cxx::private::RustOption::<#inner>::from_raw(#call).into_option()) + quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#call))) } }, _ => { - quote_spanned!(span=> ::cxx::private::RustOption::<#inner>::from_raw(#call).into_option()) + quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#call))) } } } @@ -1092,26 +1094,37 @@ fn expand_rust_function_shim_impl( } Type::RustOption(ty) => { requires_unsafe = true; - let inner = &ty.inner; + let abs_ty = quote!(<#ty as ::cxx::private::OptionFfi>); + let abs_ty_target = quote!(#abs_ty::Target); match &ty.inner { Type::Ref(r) => match &r.inner { Type::Ident(i) if i.rust == RustString => match r.mutable { - true => quote_spanned!(span=> ::cxx::private::RustOption::<&mut _>::from_raw(#var).into_option_string_mut()), - false => quote_spanned!(span=> ::cxx::private::RustOption::<&_>::from_raw(#var).into_option_string_ref()), + false => { + quote_spanned!(span=> #abs_ty_target::into_option_string_ref(#abs_ty_target::from_raw_repr(#var))) + } + true => { + quote_spanned!(span=> #abs_ty_target::into_option_string_mut(#abs_ty_target::from_raw_repr(#var))) + } }, Type::RustVec(vec) if vec.inner == RustString => match r.mutable { - true => quote_spanned!(span=> ::cxx::private::RustOption::<&mut _>::from_raw(#var).into_option_vec_string_mut()), - false => quote_spanned!(span=> ::cxx::private::RustOption::<&_>::from_raw(#var).into_option_vec_string_ref()), - }, - Type::RustVec(_) => { - match r.mutable { - true => quote_spanned!(span=> ::cxx::private::RustOption::<&mut _>::from_raw(#var).into_option_vec_mut()), - false => quote_spanned!(span=> ::cxx::private::RustOption::<&_>::from_raw(#var).into_option_vec_ref()), + false => { + quote_spanned!(span=> #abs_ty_target::into_option_vec_string_ref(#abs_ty_target::from_raw_double_repr(#var))) + } + true => { + quote_spanned!(span=> #abs_ty_target::into_option_vec_string_mut(#abs_ty_target::from_raw_double_repr(#var))) } }, - _ => quote_spanned!(span=> ::cxx::private::RustOption::<#inner>::from_raw(#var).into_option()), - } - _ => quote_spanned!(span=> ::cxx::private::RustOption::<#inner>::from_raw(#var).into_option()), + Type::RustVec(_) => match r.mutable { + false => { + quote_spanned!(span=> #abs_ty_target::into_option_vec_ref(#abs_ty_target::from_raw_repr(#var))) + } + true => { + quote_spanned!(span=> #abs_ty_target::into_option_vec_mut(#abs_ty_target::from_raw_repr(#var))) + } + } + _ => quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#var))), + }, + _ => quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#var))), } } Type::UniquePtr(_) => { @@ -1187,27 +1200,29 @@ fn expand_rust_function_shim_impl( } Type::RustOption(ty) => { process_converted = Some(quote_spanned!(span=> into_raw)); + let abs_ty = quote!(<#ty as ::cxx::private::OptionFfi>); + let abs_ty_target = quote!(#abs_ty::Target); match &ty.inner { Type::RustBox(_) => { - Some(quote_spanned!(span=> ::cxx::private::RustOption::from)) + Some(quote_spanned!(span=> #abs_ty::into_ffi)) } Type::Ref(r) => { match &r.inner { Type::Ident(ident) if ident.rust == RustString => match r.mutable { - false => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_string_ref)), - true => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_string_mut)), + false => return Some(quote_spanned!(span=> #abs_ty_target::from_option_string_ref)), + true => return Some(quote_spanned!(span=> #abs_ty_target::from_option_string_mut)), }, Type::RustVec(vec) if vec.inner == RustString => match r.mutable { - false => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_string_ref)), - true => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_string_mut)), + false => return Some(quote_spanned!(span=> #abs_ty_target::from_option_vec_string_ref)), + true => return Some(quote_spanned!(span=> #abs_ty_target::from_option_vec_string_mut)), }, Type::RustVec(_) => match r.mutable { - false => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_ref)), - true => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_mut)), + false => return Some(quote_spanned!(span=> #abs_ty_target::from_option_vec_ref)), + true => return Some(quote_spanned!(span=> #abs_ty_target::from_option_vec_mut)), }, _ => {}, } - Some(quote_spanned!(span=> ::cxx::private::RustOption::from)) + Some(quote_spanned!(span=> #abs_ty::into_ffi)) } _ => unreachable!(), } @@ -1688,13 +1703,15 @@ fn expand_rust_option( OptionInner::RustBox(_) => quote! { #[doc(hidden)] #[export_name = #link_set] - unsafe extern "C" fn #local_set #impl_generics(this: *mut ::cxx::private::RustOption<#ty>, value: *mut ::core::mem::MaybeUninit<#ty>) { + unsafe extern "C" fn #local_set #impl_generics(this: *mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target, value: *mut ::core::mem::MaybeUninit<#ty>) { + use ::cxx::private::OptionFfiInverse; let value = core::mem::replace(value.as_mut().unwrap(), ::core::mem::MaybeUninit::zeroed()); this.as_mut().unwrap().set(unsafe { value.assume_init() }); } #[doc(hidden)] #[export_name = #link_value_const] - unsafe extern "C" fn #local_value_const #impl_generics(this: *const ::cxx::private::RustOption<#ty>) -> *const #ty { + unsafe extern "C" fn #local_value_const #impl_generics(this: *const <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) -> *const #ty { + use ::cxx::private::OptionFfiInverse; let this = unsafe { this.as_ref().unwrap() }; ::cxx::core::debug_assert!(this.has_value()); let v: &#ty = unsafe { this.as_option().as_ref().unwrap() }; @@ -1702,8 +1719,9 @@ fn expand_rust_option( } #[doc(hidden)] #[export_name = #link_value] - unsafe extern "C" fn #local_value #impl_generics(this: *mut ::cxx::private::RustOption<#ty>) -> *mut #ty { - let this: &mut ::cxx::private::RustOption<#ty> = unsafe { this.as_mut().unwrap() }; + unsafe extern "C" fn #local_value #impl_generics(this: *mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) -> *mut #ty { + use ::cxx::private::OptionFfiInverse; + let this: &mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target = unsafe { this.as_mut().unwrap() }; ::cxx::core::debug_assert!(this.has_value()); this.as_mut_option().as_mut().unwrap() as _ } @@ -1712,13 +1730,15 @@ fn expand_rust_option( // no value_const, value already is value_const #[doc(hidden)] #[export_name = #link_set] - unsafe extern "C" fn #local_set #impl_generics(this: *mut ::cxx::private::RustOption<#ty>, value: #ty_ptr) { + unsafe extern "C" fn #local_set #impl_generics(this: *mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target, value: #ty_ptr) { + use ::cxx::private::OptionFfiInverse; unsafe { this.as_mut().unwrap().set(&*value) }; } #[doc(hidden)] #[export_name = #link_value] - unsafe extern "C" fn #local_value #impl_generics(this: *const ::cxx::private::RustOption<#ty>) -> #ty_ptr { - let this: &::cxx::private::RustOption<#ty> = unsafe { this.as_ref().unwrap() }; + unsafe extern "C" fn #local_value #impl_generics(this: *const <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) -> #ty_ptr { + use ::cxx::private::OptionFfiInverse; + let this: &<::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target = unsafe { this.as_ref().unwrap() }; ::cxx::core::debug_assert!(this.has_value()); let option: &::core::option::Option<#ty> = this.as_option(); let option: ::core::option::Option<&#ty> = option.as_ref(); @@ -1728,12 +1748,14 @@ fn expand_rust_option( OptionInner::MutRef(_) | OptionInner::MutRefVec(_) => quote! { #[doc(hidden)] #[export_name = #link_set] - unsafe extern "C" fn #local_set #impl_generics(this: *mut ::cxx::private::RustOption<#ty>, value: #ty_ptr) { + unsafe extern "C" fn #local_set #impl_generics(this: *mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target, value: #ty_ptr) { + use ::cxx::private::OptionFfiInverse; unsafe { this.as_mut().unwrap().set(&mut *value) }; } #[doc(hidden)] #[export_name = #link_value_const] - unsafe extern "C" fn #local_value_const #impl_generics(this: *const ::cxx::private::RustOption<#ty>) -> #const_ty_ptr { + unsafe extern "C" fn #local_value_const #impl_generics(this: *const <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) -> #const_ty_ptr { + use ::cxx::private::OptionFfiInverse; let this = unsafe { this.as_ref().unwrap() }; ::cxx::core::debug_assert!(this.has_value()); let v: &#ty = unsafe { this.as_option().as_ref().unwrap() }; @@ -1741,8 +1763,9 @@ fn expand_rust_option( } #[doc(hidden)] #[export_name = #link_value] - unsafe extern "C" fn #local_value #impl_generics(this: *mut ::cxx::private::RustOption<#ty>) -> #ty_ptr { - let this: &mut ::cxx::private::RustOption<#ty> = unsafe { this.as_mut().unwrap() }; + unsafe extern "C" fn #local_value #impl_generics(this: *mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) -> #ty_ptr { + use ::cxx::private::OptionFfiInverse; + let this: &mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target = unsafe { this.as_mut().unwrap() }; ::cxx::core::debug_assert!(this.has_value()); let option: &mut ::core::option::Option<#ty> = this.as_mut_option(); let option: ::core::option::Option<&mut #ty> = option.as_mut(); @@ -1755,18 +1778,19 @@ fn expand_rust_option( #unsafe_token impl #impl_generics ::cxx::private::ImplOption<#ty> for #trait_impl_ty {} #[doc(hidden)] #[export_name = #link_new] - unsafe extern "C" fn #local_new #impl_generics(this: *mut ::cxx::private::RustOption<#ty>) { - unsafe { ::cxx::core::ptr::write(this, ::cxx::private::RustOption::new()) }; + unsafe extern "C" fn #local_new #impl_generics(this: *mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) { + use ::cxx::private::OptionFfiInverse; + unsafe { ::cxx::core::ptr::write(this, <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target::new()) }; } #[doc(hidden)] #[export_name = #link_drop] - unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::private::RustOption<#ty>) { + unsafe extern "C" fn #local_drop #impl_generics(this: *mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) { let __fn = concat!("<", module_path!(), #prevent_unwind_drop_label); ::cxx::private::prevent_unwind(__fn, || unsafe { ::cxx::core::ptr::drop_in_place(this) }); } #[doc(hidden)] #[export_name = #link_has_value] - unsafe extern "C" fn #local_has_value #impl_generics(this: *const ::cxx::private::RustOption<#ty>) -> bool { + unsafe extern "C" fn #local_has_value #impl_generics(this: *const <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) -> bool { unsafe { this.as_ref().unwrap().value().is_some() } } #set_value_impl diff --git a/src/lib.rs b/src/lib.rs index 8e77afe40..e21e361f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -481,7 +481,6 @@ pub use crate::cxx_vector::CxxVector; #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] pub use crate::exception::Exception; pub use crate::extern_type::{kind, ExternType}; -pub use crate::rust_option::RustOption; pub use crate::shared_ptr::SharedPtr; pub use crate::string::CxxString; pub use crate::unique_ptr::UniquePtr; @@ -513,8 +512,8 @@ pub mod private { pub use crate::opaque::Opaque; #[cfg(feature = "alloc")] pub use crate::result::{r#try, Result}; - pub use crate::rust_option::assert_option_safe; - pub use crate::rust_option::RustOption; + pub use crate::rust_option::OptionFfi; + pub use crate::rust_option::OptionFfiInverse; pub use crate::rust_slice::RustSlice; pub use crate::rust_str::RustStr; #[cfg(feature = "alloc")] diff --git a/src/rust_option.rs b/src/rust_option.rs index f31635518..b4c1fb95d 100644 --- a/src/rust_option.rs +++ b/src/rust_option.rs @@ -19,390 +19,552 @@ use core::pin::Pin; mod private { pub trait Sealed {} } -pub trait OptionTarget: private::Sealed {} +pub trait OptionPtrTarget: private::Sealed {} impl private::Sealed for &T {} -impl OptionTarget for &T {} +impl OptionPtrTarget for &T {} impl private::Sealed for &mut T {} -impl OptionTarget for &mut T {} +impl OptionPtrTarget for &mut T {} impl private::Sealed for Pin<&mut T> {} -impl OptionTarget for Pin<&mut T> {} +impl OptionPtrTarget for Pin<&mut T> {} #[cfg(feature = "alloc")] impl private::Sealed for Box {} #[cfg(feature = "alloc")] -impl OptionTarget for Box {} - -type Repr = - [mem::MaybeUninit; mem::size_of::>() / core::mem::size_of::()]; - -// ABI compatible with C++ rust::Option (not necessarily core::option::Option). -#[repr(C)] -pub struct RustOption { - repr: Repr, - marker: core::marker::PhantomData, +impl OptionPtrTarget for Box {} + +pub trait OptionFfi: private::Sealed { + type Target; + + fn new_ffi() -> Self::Target; + fn into_ffi(self) -> Self::Target; + fn as_ffi(&self) -> &Self::Target; + fn as_mut_ffi(&mut self) -> &mut Self::Target; + fn from_ffi(other: Self::Target) -> Self; + fn from_ffi_ref(other: &Self::Target) -> &Self; + fn from_ffi_mut(other: &mut Self::Target) -> &mut Self; } -pub const fn assert_option_safe() { - struct __SizeCheck(core::marker::PhantomData); - impl __SizeCheck { - const _IS_OPTION_SIZE: () = assert!(mem::size_of::>() == mem::size_of::()); - const _IS_USIZE: () = assert!(mem::size_of::() == mem::size_of::()); - const _IS_NICHE: () = assert!(mem::size_of::>() == mem::size_of::()); - const _IS_USIZE_ALIGN: () = assert!(mem::align_of::() == mem::align_of::()); - const _IS_OPTION_ALIGN: () = - assert!(mem::align_of::>() == mem::align_of::()); - } - // Force the constants to resolve (at compile time) - let _: () = __SizeCheck::::_IS_OPTION_SIZE; - let _: () = __SizeCheck::::_IS_USIZE; - let _: () = __SizeCheck::::_IS_NICHE; - let _: () = __SizeCheck::::_IS_USIZE_ALIGN; - let _: () = __SizeCheck::::_IS_OPTION_ALIGN; -} +pub trait OptionFfiInverse: private::Sealed + Sized { + type Target: OptionFfi; -impl RustOption { - pub fn new() -> Self { - let _: () = assert_option_safe::(); - Self::from(None) + fn new() -> Self { + ::new_ffi() } - pub fn into_option(mut self) -> Option { - let _: () = assert_option_safe::(); - self.as_mut_option().take() + fn into_option(self) -> Self::Target { + ::from_ffi(self) } - pub fn as_option(&self) -> &Option { - let _: () = assert_option_safe::(); - unsafe { &*(self as *const RustOption as *const Option) } + fn as_option(&self) -> &Self::Target { + ::from_ffi_ref(self) } - pub fn as_mut_option(&mut self) -> &mut Option { - let _: () = assert_option_safe::(); - unsafe { &mut *(self as *mut RustOption as *mut Option) } + fn as_mut_option(&mut self) -> &mut Self::Target { + ::from_ffi_mut(self) } - pub fn from(o: Option) -> Self { - let _: () = assert_option_safe::(); - let v = unsafe { core::mem::transmute_copy(&o) }; - core::mem::forget(o); - v + fn from_option(other: Self::Target) -> Self { + ::into_ffi(other) } - pub fn from_ref(o: &Option) -> &Self { - let _: () = assert_option_safe::(); - unsafe { &*(o as *const Option as *const RustOption) } + fn from_option_ref(other: &Self::Target) -> &Self { + ::as_ffi(other) } - pub fn from_mut(o: &mut Option) -> &mut Self { - let _: () = assert_option_safe::(); - unsafe { &mut *(o as *mut Option as *mut RustOption) } + fn from_option_mut(other: &mut Self::Target) -> &mut Self { + ::as_mut_ffi(other) } +} - pub fn value(&self) -> Option<&T> { - self.as_option().as_ref() - } +/// Defined a struct named RustOption and implements OptionFfi for Option with it as target +macro_rules! impl_option_ffi { + // Like `impl RustOption` where you need some bound on T + (<$generic:ident: $bound:path>, $repr:ty, $sizing:ty) => { + impl_option_ffi!(_private: generics=<>, bounded_generics=<$generic: $bound>, option_ty=$generic, repr=$repr, sizing=$sizing) + }; + // Like `impl RustOption>` for some concrete S and generic T + (<$generic:ident>, $t:ty, $repr:ty, $sizing:ty) => { + impl_option_ffi!(_private: generics=<$generic>, bounded_generics=<>, option_ty=$t, repr=$repr, sizing=$sizing) + }; + // Like `impl RustOption` for some non-generic T + (<$t:ident>, $repr:ty, $sizing:ty) => { + impl_option_ffi!(_private: generics=<>, bounded_generics=<>, option_ty=$t, repr=$repr, sizing=$sizing) + }; + // Private case. Does the actual implementation + (_private: generics=<$($generic1:ident),*>, bounded_generics=<$($generic2:ident: $bound:path),*>, option_ty=$option_ty:ty, repr=$repr:ty, sizing=$sizing:ty) => { + type Repr = [mem::MaybeUninit<$repr>; mem::size_of::>() / mem::size_of::<$repr>()]; + + // ABI compatible with C++ rust::Option for (not necessarily core::option::Option). + pub struct RustOption<$($generic1),* $($generic2: $bound),*> { + #[allow(dead_code)] + repr: Repr, + phantom: core::marker::PhantomData>, + } - pub fn has_value(&self) -> bool { - self.as_option().is_some() - } + pub const fn assert_option_safe() { + struct __SizeCheck(core::marker::PhantomData); + impl __SizeCheck { + const _IS_OPTION_SIZE: () = + assert!(mem::size_of::>() == mem::size_of::()); + const _IS_REPR_ALIGN: () = + assert!(mem::align_of::() == mem::align_of::<$repr>()); + const _IS_OPTION_ALIGN: () = + assert!(mem::align_of::>() == mem::align_of::()); + } + // Force the constants to resolve (at compile time) + let _: () = __SizeCheck::::_IS_OPTION_SIZE; + let _: () = __SizeCheck::::_IS_REPR_ALIGN; + let _: () = __SizeCheck::::_IS_OPTION_ALIGN; + } - pub fn set(&mut self, value: T) { - self.as_mut_option().replace(value); - } + impl<$($generic1),* $($generic2: $bound),*> private::Sealed for Option<$option_ty> {} + + impl<$($generic1),* $($generic2: $bound),*> OptionFfi for Option<$option_ty> { + type Target = RustOption<$($generic1),* $($generic2),*>; + + fn new_ffi() -> Self::Target { + Self::None.into_ffi() + } + + fn into_ffi(self) -> Self::Target { + let _: () = assert_option_safe::<$option_ty>(); + let v = unsafe { core::mem::transmute_copy(&self) }; + core::mem::forget(self); + v + } + + fn as_ffi(&self) -> &Self::Target { + let _: () = assert_option_safe::<$option_ty>(); + unsafe { &*(self as *const Self as *const Self::Target) } + } + + fn as_mut_ffi(&mut self) -> &mut Self::Target { + let _: () = assert_option_safe::<$option_ty>(); + unsafe { &mut *(self as *mut Self as *mut Self::Target) } + } + + fn from_ffi(mut other: Self::Target) -> Self { + let _: () = assert_option_safe::<$option_ty>(); + Self::from_ffi_mut(&mut other).take() + } + + fn from_ffi_ref(other: &Self::Target) -> &Self { + let _: () = assert_option_safe::<$option_ty>(); + unsafe { &*(other as *const Self::Target as *const Self) } + } + + fn from_ffi_mut(other: &mut Self::Target) -> &mut Self { + let _: () = assert_option_safe::<$option_ty>(); + unsafe { &mut *(other as *mut Self::Target as *mut Self) } + } + } - pub unsafe fn as_ref_mut_inner_unchecked(&mut self) -> &mut T { - unsafe { self.as_mut_option().as_mut().unwrap_unchecked() } - } + impl<$($generic1),* $($generic2: $bound),*> private::Sealed for RustOption<$($generic1),* $($generic2),*> {} + + impl<$($generic1),* $($generic2: $bound),*> OptionFfiInverse for RustOption<$($generic1),* $($generic2),*> { + type Target = Option<$option_ty>; + } + + impl<$($generic1),* $($generic2: $bound),*> Drop for RustOption<$($generic1),* $($generic2),*> { + fn drop(&mut self) { + self.as_mut_option().take(); + } + } + }; } -impl<'a, T> RustOption<&'a T> { - pub fn into_raw(self) -> *const T { - self.into_option() - .map_or(core::ptr::null(), |v| v as *const T) - } +// Pointer-sized pointer types with niche optimization +const _: () = { + impl_option_ffi! { , usize, &'static () } - pub fn into_raw_improper(self) -> *const core::ffi::c_void { - self.into_option().map_or(core::ptr::null(), |v| { - v as *const T as *const core::ffi::c_void - }) - } + impl RustOption { + pub fn value(&self) -> Option<&T> { + self.as_option().as_ref() + } - /// SAFETY: ptr must be valid for 'a - pub unsafe fn from_raw(ptr: *const T) -> Self { - let mut this = RustOption::new(); - if let Some(r) = unsafe { ptr.as_ref() } { - this.set(r); + pub fn has_value(&self) -> bool { + self.as_option().is_some() } - this - } - /// SAFETY: ptr must be valid for 'a, and castable to *const T - pub unsafe fn from_raw_improper(ptr: *const core::ffi::c_void) -> Self { - let mut this = RustOption::new(); - let ptr = ptr as *const T; - if let Some(r) = unsafe { ptr.as_ref() } { - this.set(r); + pub fn set(&mut self, value: T) { + self.as_mut_option().replace(value); } - this - } -} -impl<'a, T> RustOption<&'a mut T> { - pub fn into_raw(self) -> *mut T { - self.into_option() - .map_or(core::ptr::null_mut(), |v| v as *mut T) + pub unsafe fn as_ref_mut_inner_unchecked(&mut self) -> &mut T { + unsafe { self.as_mut_option().as_mut().unwrap_unchecked() } + } } - pub fn into_raw_improper(self) -> *mut core::ffi::c_void { - self.into_option().map_or(core::ptr::null_mut(), |v| { - v as *mut T as *mut core::ffi::c_void - }) - } + impl<'a, T> RustOption<&'a T> { + pub fn into_raw(self) -> *const T { + self.into_option() + .map_or(core::ptr::null(), |v| v as *const T) + } - /// SAFETY: ptr must be valid for 'a - pub unsafe fn from_raw(ptr: *mut T) -> Self { - let mut this = RustOption::new(); - if let Some(r) = unsafe { ptr.as_mut() } { - this.set(r); + pub fn into_raw_improper(self) -> *const core::ffi::c_void { + self.into_option().map_or(core::ptr::null(), |v| { + v as *const T as *const core::ffi::c_void + }) } - this - } - /// SAFETY: ptr must be valid for 'a, and castable to *mut T - pub unsafe fn from_raw_improper(ptr: *mut core::ffi::c_void) -> Self { - let mut this = RustOption::new(); - let ptr = ptr as *mut T; - if let Some(r) = unsafe { ptr.as_mut() } { - this.set(r); + /// SAFETY: ptr must be valid for 'a + pub unsafe fn from_raw(ptr: *const T) -> Self { + let mut this = RustOption::new(); + if let Some(r) = unsafe { ptr.as_ref() } { + this.set(r); + } + this } - this - } -} -impl<'a, T> RustOption> { - pub fn into_raw(self) -> *mut T { - self.into_option() - .map_or(core::ptr::null_mut(), |v| unsafe { - v.get_unchecked_mut() as *mut T - }) + /// SAFETY: ptr must be valid for 'a, and castable to *const T + pub unsafe fn from_raw_improper(ptr: *const core::ffi::c_void) -> Self { + let mut this = RustOption::new(); + let ptr = ptr as *const T; + if let Some(r) = unsafe { ptr.as_ref() } { + this.set(r); + } + this + } } - pub fn into_raw_improper(self) -> *mut core::ffi::c_void { - self.into_option() - .map_or(core::ptr::null_mut(), |v| unsafe { - v.get_unchecked_mut() as *mut T as *mut core::ffi::c_void + impl<'a, T> RustOption<&'a mut T> { + pub fn into_raw(self) -> *mut T { + self.into_option() + .map_or(core::ptr::null_mut(), |v| v as *mut T) + } + + pub fn into_raw_improper(self) -> *mut core::ffi::c_void { + self.into_option().map_or(core::ptr::null_mut(), |v| { + v as *mut T as *mut core::ffi::c_void }) - } + } - /// SAFETY: ptr must be valid for 'a - pub unsafe fn from_raw(ptr: *mut T) -> Self { - let mut this = RustOption::new(); - if let Some(r) = unsafe { ptr.as_mut() } { - this.set(unsafe { Pin::new_unchecked(r) }); + /// SAFETY: ptr must be valid for 'a + pub unsafe fn from_raw(ptr: *mut T) -> Self { + let mut this = RustOption::new(); + if let Some(r) = unsafe { ptr.as_mut() } { + this.set(r); + } + this } - this - } - /// SAFETY: ptr must be valid for 'a, and castable to *mut T - pub unsafe fn from_raw_improper(ptr: *mut core::ffi::c_void) -> Self { - let mut this = RustOption::new(); - let ptr = ptr as *mut T; - if let Some(r) = unsafe { ptr.as_mut() } { - this.set(unsafe { Pin::new_unchecked(r) }); + /// SAFETY: ptr must be valid for 'a, and castable to *mut T + pub unsafe fn from_raw_improper(ptr: *mut core::ffi::c_void) -> Self { + let mut this = RustOption::new(); + let ptr = ptr as *mut T; + if let Some(r) = unsafe { ptr.as_mut() } { + this.set(r); + } + this } - this } -} -#[cfg(feature = "alloc")] -impl RustOption> { - pub fn into_raw(self) -> *mut T { - self.into_option() - .map_or(core::ptr::null_mut(), |v| Box::into_raw(v)) - } + impl<'a, T> RustOption> { + pub fn into_raw(self) -> *mut T { + self.into_option() + .map_or(core::ptr::null_mut(), |v| unsafe { + v.get_unchecked_mut() as *mut T + }) + } - pub fn into_raw_improper(self) -> *mut core::ffi::c_void { - self.into_option().map_or(core::ptr::null_mut(), |v| { - Box::into_raw(v) as *mut core::ffi::c_void - }) - } + pub fn into_raw_improper(self) -> *mut core::ffi::c_void { + self.into_option() + .map_or(core::ptr::null_mut(), |v| unsafe { + v.get_unchecked_mut() as *mut T as *mut core::ffi::c_void + }) + } - /// SAFETY: ptr must have originated from a `Option>` - pub unsafe fn from_raw(ptr: *mut T) -> Self { - let mut this = RustOption::new(); - if !ptr.is_null() { - this.set(unsafe { Box::from_raw(ptr) }); + /// SAFETY: ptr must be valid for 'a + pub unsafe fn from_raw(ptr: *mut T) -> Self { + let mut this = RustOption::new(); + if let Some(r) = unsafe { ptr.as_mut() } { + this.set(unsafe { Pin::new_unchecked(r) }); + } + this + } + + /// SAFETY: ptr must be valid for 'a, and castable to *mut T + pub unsafe fn from_raw_improper(ptr: *mut core::ffi::c_void) -> Self { + let mut this = RustOption::new(); + let ptr = ptr as *mut T; + if let Some(r) = unsafe { ptr.as_mut() } { + this.set(unsafe { Pin::new_unchecked(r) }); + } + this } - this } - /// SAFETY: ptr must have originated from a `Option>` - pub unsafe fn from_raw_improper(ptr: *mut core::ffi::c_void) -> Self { - let mut this = RustOption::new(); - if !ptr.is_null() { - this.set(unsafe { Box::from_raw(ptr as *mut T) }); + #[cfg(feature = "alloc")] + impl RustOption> { + pub fn into_raw(self) -> *mut T { + self.into_option() + .map_or(core::ptr::null_mut(), |v| Box::into_raw(v)) + } + + pub fn into_raw_improper(self) -> *mut core::ffi::c_void { + self.into_option().map_or(core::ptr::null_mut(), |v| { + Box::into_raw(v) as *mut core::ffi::c_void + }) + } + + /// SAFETY: ptr must have originated from a `Option>` + pub unsafe fn from_raw(ptr: *mut T) -> Self { + let mut this = RustOption::new(); + if !ptr.is_null() { + this.set(unsafe { Box::from_raw(ptr) }); + } + this + } + + /// SAFETY: ptr must have originated from a `Option>` + pub unsafe fn from_raw_improper(ptr: *mut core::ffi::c_void) -> Self { + let mut this = RustOption::new(); + if !ptr.is_null() { + this.set(unsafe { Box::from_raw(ptr as *mut T) }); + } + this } - this } -} -#[cfg(feature = "alloc")] -impl<'a, T> RustOption<&'a RustVec> { - pub fn from_option_vec_ref(other: Option<&'a Vec>) -> Self { - unsafe { - core::mem::transmute::>, RustOption<&RustVec>>(RustOption::from( - other, - )) + #[cfg(feature = "alloc")] + impl<'a, T> RustOption<&'a Vec> { + pub fn from_option_vec_ref(other: Option<&'a Vec>) -> RustOption<&'a RustVec> { + unsafe { + core::mem::transmute::>, RustOption<&RustVec>>( + RustOption::from_option(other), + ) + } + } + + pub fn into_option_vec_ref(this: RustOption<&'a RustVec>) -> Option<&'a Vec> { + unsafe { core::mem::transmute::>, RustOption<&Vec>>(this) } + .into_option() + } + + pub fn as_option_vec_ref<'b>(this: &'b RustOption<&'a RustVec>) -> &'b Option<&'a Vec> { + unsafe { &*(this as *const RustOption<&RustVec> as *const RustOption<&Vec>) } + .as_option() + } + + pub fn as_option_vec_ref_mut<'b>(this: &'b mut RustOption<&'a RustVec>) -> &'b mut Option<&'a Vec> { + unsafe { &mut *(this as *mut RustOption<&RustVec> as *mut RustOption<&Vec>) } + .as_mut_option() + } + + /// SAFETY: ptr must be valid for 'a + pub unsafe fn from_raw_repr(ptr: *const RustVec) -> RustOption<&'a RustVec> { + unsafe { RustOption::<&RustVec>::from_raw(ptr) } + } + + pub fn into_rust_option_rust_vec_ref(self) -> RustOption<&'a RustVec> { + unsafe { core::mem::transmute::>, RustOption<&RustVec>>(self) } } } - pub fn into_option_vec_ref(self) -> Option<&'a Vec> { - unsafe { core::mem::transmute::>, RustOption<&Vec>>(self) } + #[cfg(feature = "alloc")] + impl<'a, T> RustOption<&'a mut Vec> { + pub fn from_option_vec_mut(other: Option<&'a mut Vec>) -> RustOption<&'a mut RustVec> { + unsafe { + core::mem::transmute::>, RustOption<&mut RustVec>>( + RustOption::from_option(other), + ) + } + } + + pub fn into_option_vec_mut(this: RustOption<&'a mut RustVec>) -> Option<&'a mut Vec> { + unsafe { + core::mem::transmute::>, RustOption<&mut Vec>>(this) + } .into_option() - } + } - pub fn as_option_vec_ref(&self) -> &Option<&'a Vec> { - unsafe { &*(self as *const RustOption<&RustVec> as *const RustOption<&Vec>) } + pub fn as_option_vec_mut<'b>(this: &'b RustOption<&'a mut RustVec>) -> &'b Option<&'a mut Vec> { + unsafe { + &*(this as *const RustOption<&'a mut RustVec> + as *const RustOption<&'a mut Vec>) + } .as_option() - } + } - pub fn as_option_vec_ref_mut(&mut self) -> &mut Option<&'a Vec> { - unsafe { &mut *(self as *mut RustOption<&RustVec> as *mut RustOption<&Vec>) } + pub fn as_option_vec_mut_mut<'b>(this: &'b mut RustOption<&'a mut RustVec>) -> &'b mut Option<&'a mut Vec> { + unsafe { + &mut *(this as *mut RustOption<&'a mut RustVec> + as *mut RustOption<&'a mut Vec>) + } .as_mut_option() - } -} + } -#[cfg(feature = "alloc")] -impl<'a, T> RustOption<&'a mut RustVec> { - pub fn from_option_vec_mut(other: Option<&'a mut Vec>) -> Self { - unsafe { - core::mem::transmute::>, RustOption<&mut RustVec>>( - RustOption::from(other), - ) + /// SAFETY: ptr must be valid for 'a + pub unsafe fn from_raw_repr(ptr: *mut RustVec) -> RustOption<&'a mut RustVec> { + unsafe { RustOption::<&mut RustVec>::from_raw(ptr) } } - } - pub fn into_option_vec_mut(self) -> Option<&'a mut Vec> { - unsafe { - core::mem::transmute::>, RustOption<&mut Vec>>(self) + pub fn into_rust_option_rust_vec_mut(self) -> RustOption<&'a mut RustVec> { + unsafe { + core::mem::transmute::>, RustOption<&mut RustVec>>(self) + } } - .into_option() } - pub fn as_option_vec_mut(&self) -> &Option<&'a mut Vec> { - unsafe { - &*(self as *const RustOption<&'a mut RustVec> as *const RustOption<&'a mut Vec>) + #[cfg(feature = "alloc")] + impl<'a> RustOption<&'a Vec> { + pub fn from_option_vec_string_ref(other: Option<&'a Vec>) -> RustOption<&'a RustVec> { + unsafe { + core::mem::transmute::>, RustOption<&RustVec>>( + RustOption::from_option(other), + ) + } } - .as_option() - } - pub fn as_option_vec_mut_mut(&mut self) -> &mut Option<&'a mut Vec> { - unsafe { - &mut *(self as *mut RustOption<&'a mut RustVec> as *mut RustOption<&'a mut Vec>) + pub fn into_option_vec_string_ref(this: RustOption<&'a RustVec>) -> Option<&'a Vec> { + unsafe { + core::mem::transmute::>, RustOption<&Vec>>( + this, + ) + } + .into_option() } - .as_mut_option() - } -} -#[cfg(feature = "alloc")] -impl<'a> RustOption<&'a RustVec> { - pub fn from_option_vec_string_ref(other: Option<&'a Vec>) -> Self { - unsafe { - core::mem::transmute::>, RustOption<&RustVec>>( - RustOption::from(other), - ) + pub fn as_option_vec_string_ref_mut<'b>(this: &'b mut RustOption<&'a RustVec>) -> &'b mut Option<&'a Vec> { + unsafe { + &mut *(this as *mut RustOption<&RustVec> + as *mut RustOption<&Vec>) + } + .as_mut_option() } - } - pub fn into_option_vec_string_ref(self) -> Option<&'a Vec> { - unsafe { - core::mem::transmute::>, RustOption<&Vec>>(self) + /// SAFETY: ptr must be valid for 'a + pub unsafe fn from_raw_double_repr(ptr: *const RustVec) -> RustOption<&'a RustVec> { + unsafe { RustOption::<&RustVec>::from_raw(ptr) } } - .into_option() - } - pub fn as_option_vec_string_ref_mut(&mut self) -> &mut Option<&'a Vec> { - unsafe { - &mut *(self as *mut RustOption<&RustVec> as *mut RustOption<&Vec>) + pub fn into_rust_option_rust_vec_rust_string_ref( + self, + ) -> RustOption<&'a RustVec> { + unsafe { + core::mem::transmute::>, RustOption<&RustVec>>( + self, + ) + } } - .as_mut_option() } -} -#[cfg(feature = "alloc")] -impl<'a> RustOption<&'a mut RustVec> { - pub fn from_option_vec_string_mut(other: Option<&'a mut Vec>) -> Self { - unsafe { - core::mem::transmute::>, RustOption<&mut RustVec>>( - RustOption::from(other), - ) + #[cfg(feature = "alloc")] + impl<'a> RustOption<&'a mut Vec> { + pub fn from_option_vec_string_mut(other: Option<&'a mut Vec>) -> RustOption<&'a mut RustVec> { + unsafe { + core::mem::transmute::< + RustOption<&mut Vec>, + RustOption<&mut RustVec>, + >(RustOption::from_option(other)) + } } - } - pub fn into_option_vec_string_mut(self) -> Option<&'a mut Vec> { - unsafe { core::mem::transmute::>, RustOption<&mut Vec>>(self) }.into_option() - } + pub fn into_option_vec_string_mut(this: RustOption<&'a mut RustVec>) -> Option<&'a mut Vec> { + unsafe { + core::mem::transmute::< + RustOption<&mut RustVec>, + RustOption<&mut Vec>, + >(this) + } + .into_option() + } - pub fn as_option_vec_string_mut_mut(&mut self) -> &mut Option<&'a mut Vec> { - unsafe { - (*(self as *mut RustOption<&mut RustVec> - as *mut RustOption<&mut Vec>)) + pub fn as_option_vec_string_mut_mut<'b>(this: &'b mut RustOption<&'a mut RustVec>) -> &'b mut Option<&'a mut Vec> { + unsafe { + core::mem::transmute::< + &mut RustOption<&mut RustVec>, + &mut RustOption<&mut Vec>, + >(this) .as_mut_option() + } } - } -} -#[cfg(feature = "alloc")] -impl<'a> RustOption<&'a RustString> { - pub fn from_option_string_ref(other: Option<&'a String>) -> Self { - unsafe { - core::mem::transmute::, RustOption<&RustString>>(RustOption::from( - other, - )) + /// SAFETY: ptr must be valid for 'a + pub unsafe fn from_raw_double_repr(ptr: *mut RustVec) -> RustOption<&'a mut RustVec> { + unsafe { RustOption::<&mut RustVec>::from_raw(ptr) } } - } - pub fn into_option_string_ref(self) -> Option<&'a String> { - unsafe { core::mem::transmute::, RustOption<&String>>(self) } - .into_option() + pub fn into_rust_option_rust_vec_rust_string_mut( + self, + ) -> RustOption<&'a mut RustVec> { + unsafe { + core::mem::transmute::< + RustOption<&mut Vec>, + RustOption<&mut RustVec>, + >(self) + } + } } - pub fn as_option_string_ref_mut(&mut self) -> &mut Option<&'a String> { - unsafe { &mut *(self as *mut RustOption<&RustString> as *mut RustOption<&String>) } - .as_mut_option() - } -} + #[cfg(feature = "alloc")] + impl<'a> RustOption<&'a String> { + pub fn from_option_string_ref(other: Option<&'a String>) -> RustOption<&'a RustString> { + unsafe { + core::mem::transmute::, RustOption<&RustString>>( + RustOption::from_option(other), + ) + } + } -#[cfg(feature = "alloc")] -impl<'a> RustOption<&'a mut RustString> { - pub fn from_option_string_mut(other: Option<&'a mut String>) -> Self { - unsafe { - core::mem::transmute::, RustOption<&mut RustString>>( - RustOption::from(other), - ) + pub fn into_option_string_ref(this: RustOption<&'a RustString>) -> Option<&'a String> { + unsafe { core::mem::transmute::, RustOption<&String>>(this) } + .into_option() } - } - pub fn into_option_string_mut(self) -> Option<&'a mut String> { - unsafe { - core::mem::transmute::, RustOption<&mut String>>(self) + pub fn as_option_string_ref_mut<'b>(this: &'b mut RustOption<&'a RustString>) -> &'b mut Option<&'a String> { + unsafe { &mut *(this as *mut RustOption<&RustString> as *mut RustOption<&String>) } + .as_mut_option() + } + + /// SAFETY: ptr must be valid for 'a + pub unsafe fn from_raw_repr(ptr: *const RustString) -> RustOption<&'a RustString> { + unsafe { RustOption::<&RustString>::from_raw(ptr) } + } + + pub fn into_rust_option_rust_string_ref(self) -> RustOption<&'a RustString> { + unsafe { core::mem::transmute::, RustOption<&RustString>>(self) } } - .into_option() } - pub fn as_option_string_mut_mut(&mut self) -> &mut Option<&'a mut String> { - unsafe { - (*(self as *mut RustOption<&mut RustString> as *mut RustOption<&mut String>)) + #[cfg(feature = "alloc")] + impl<'a> RustOption<&'a mut String> { + pub fn from_option_string_mut(other: Option<&'a mut String>) -> RustOption<&'a mut RustString> { + unsafe { + core::mem::transmute::, RustOption<&mut RustString>>( + RustOption::from_option(other), + ) + } + } + + pub fn into_option_string_mut(this: RustOption<&'a mut RustString>) -> Option<&'a mut String> { + unsafe { + core::mem::transmute::, RustOption<&mut String>>(this) + } + .into_option() + } + + pub fn as_option_string_mut_mut<'b>(this: &'b mut RustOption<&'a mut RustString>) -> &'b mut Option<&'a mut String> { + unsafe { + core::mem::transmute::<&mut RustOption<&mut RustString>, &mut RustOption<&mut String>>( + this, + ) .as_mut_option() + } } - } -} -impl Drop for RustOption { - fn drop(&mut self) { - self.as_mut_option().take(); + /// SAFETY: ptr must be valid for 'a + pub unsafe fn from_raw_repr(ptr: *mut RustString) -> RustOption<&'a mut RustString> { + unsafe { RustOption::<&mut RustString>::from_raw(ptr) } + } + + pub fn into_rust_option_rust_string_mut(self) -> RustOption<&'a mut RustString> { + unsafe { + core::mem::transmute::, RustOption<&mut RustString>>(self) + } + } } -} +}; diff --git a/src/symbols/rust_option.rs b/src/symbols/rust_option.rs index 4154ab8a5..9b4667184 100644 --- a/src/symbols/rust_option.rs +++ b/src/symbols/rust_option.rs @@ -1,5 +1,6 @@ use crate::c_char::c_char; -use crate::rust_option::RustOption; +use crate::rust_option::OptionFfi; +use crate::rust_option::OptionFfiInverse; #[cfg(feature = "alloc")] use crate::rust_string::RustString; #[cfg(feature = "alloc")] @@ -11,62 +12,62 @@ macro_rules! rust_option_shims { ($segment:expr, $ty:ty) => { const_assert_eq!( mem::size_of::>(), - mem::size_of::>() + mem::size_of::< as OptionFfi>::Target>() ); const_assert_eq!(mem::size_of::>(), mem::size_of::()); const _: () = { #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$new")] - unsafe extern "C" fn __const_new(this: *mut RustOption<&$ty>) { - unsafe { ptr::write(this, RustOption::<&$ty>::new()) }; + unsafe extern "C" fn __const_new(this: *mut as OptionFfi>::Target) { + unsafe { ptr::write(this, Option::<&$ty>::new_ffi()) }; } #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$drop")] - unsafe extern "C" fn __const_drop(this: *mut RustOption<&$ty>) { + unsafe extern "C" fn __const_drop(this: *mut as OptionFfi>::Target) { unsafe { ptr::drop_in_place(this) } } #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$has_value")] - unsafe extern "C" fn __const_has_value(this: *const RustOption<&$ty>) -> bool { - let o: &RustOption<&$ty> = unsafe { this.as_ref().unwrap() }; + unsafe extern "C" fn __const_has_value(this: *const as OptionFfi>::Target) -> bool { + let o: & as OptionFfi>::Target = unsafe { this.as_ref().unwrap() }; o.as_option().is_some() } #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$value")] - unsafe extern "C" fn __const_value(this: *const RustOption<&$ty>) -> *const $ty { + unsafe extern "C" fn __const_value(this: *const as OptionFfi>::Target) -> *const $ty { unsafe { this.as_ref().unwrap().as_option().as_ref().copied().unwrap() as *const $ty } } #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$set")] unsafe extern "C" fn __const_set( - this: *mut RustOption<&$ty>, + this: *mut as OptionFfi>::Target, value: *mut $ty, ) { unsafe { this.as_mut().unwrap().set(&*value) } } #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$new")] - unsafe extern "C" fn __new(this: *mut RustOption<&mut $ty>) { - unsafe { ptr::write(this, RustOption::<&mut $ty>::new()) } + unsafe extern "C" fn __new(this: *mut as OptionFfi>::Target) { + unsafe { ptr::write(this, Option::<&mut $ty>::new_ffi()) } } #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$drop")] - unsafe extern "C" fn __drop(this: *mut RustOption<&mut $ty>) { + unsafe extern "C" fn __drop(this: *mut as OptionFfi>::Target) { unsafe { ptr::drop_in_place(this) } } #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$has_value")] - unsafe extern "C" fn __has_value(this: *const RustOption<&mut $ty>) -> bool { - let o: &RustOption<&mut $ty> = unsafe { this.as_ref().unwrap() }; + unsafe extern "C" fn __has_value(this: *const as OptionFfi>::Target) -> bool { + let o: & as OptionFfi>::Target = unsafe { this.as_ref().unwrap() }; o.as_option().is_some() } #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$value_const")] - unsafe extern "C" fn __value_const(this: *const RustOption<&mut $ty>) -> *const $ty { + unsafe extern "C" fn __value_const(this: *const as OptionFfi>::Target) -> *const $ty { let v: &$ty = unsafe { this.as_ref().unwrap().as_option().as_ref().unwrap() }; v as *const $ty } #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$value")] - unsafe extern "C" fn __value(this: *mut RustOption<&mut $ty>) -> *mut $ty { + unsafe extern "C" fn __value(this: *mut as OptionFfi>::Target) -> *mut $ty { let this = unsafe { this.as_mut().unwrap() }; let ptr = this.as_mut_option().as_mut().unwrap(); *ptr as _ } #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$set")] unsafe extern "C" fn __set( - this: *mut RustOption<&mut $ty>, + this: *mut as OptionFfi>::Target, value: *mut $ty, ) { unsafe { this.as_mut().unwrap().set(&mut *value) } @@ -76,58 +77,59 @@ macro_rules! rust_option_shims { const _: () = { /* Vec impl */ #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$new")] - unsafe extern "C" fn __const_new(this: *mut RustOption<&RustVec<$ty>>) { - unsafe { ptr::write(this, RustOption::<&RustVec<$ty>>::new()) }; + unsafe extern "C" fn __const_new(this: *mut > as OptionFfi>::Target) { + unsafe { ptr::write(this, Option::<&RustVec<$ty>>::new_ffi()) }; } #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$drop")] - unsafe extern "C" fn __const_drop(this: *mut RustOption<&RustVec<$ty>>) { + unsafe extern "C" fn __const_drop(this: *mut > as OptionFfi>::Target) { unsafe { ptr::drop_in_place(this) } } #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$has_value")] - unsafe extern "C" fn __const_has_value(this: *const RustOption<&RustVec<$ty>>) -> bool { - let o: &RustOption<&RustVec<$ty>> = unsafe { this.as_ref().unwrap() }; - o.as_option_vec_ref().is_some() + unsafe extern "C" fn __const_has_value(this: *const > as OptionFfi>::Target) -> bool { + let o: &> as OptionFfi>::Target = unsafe { this.as_ref().unwrap() }; + > as OptionFfi>::Target::as_option_vec_ref(o).is_some() } #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$value")] - unsafe extern "C" fn __const_value(this: *const RustOption<&RustVec<$ty>>) -> *const RustVec<$ty> { + unsafe extern "C" fn __const_value(this: *const > as OptionFfi>::Target) -> *const RustVec<$ty> { unsafe { this.as_ref().unwrap().as_option().as_ref().copied().unwrap() as *const RustVec<$ty> } } #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$set")] unsafe extern "C" fn __const_set( - this: *mut RustOption<&RustVec<$ty>>, + this: *mut > as OptionFfi>::Target, value: *mut RustVec<$ty>, ) { - unsafe { this.as_mut().unwrap().as_option_vec_ref_mut().replace((&*value).as_vec()); } + unsafe { > as OptionFfi>::Target::as_option_vec_ref_mut(this.as_mut().unwrap()).replace((&*value).as_vec()); } + } #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$new")] - unsafe extern "C" fn __new(this: *mut RustOption<&mut RustVec<$ty>>) { - unsafe { ptr::write(this, RustOption::<&mut RustVec<$ty>>::new()) } + unsafe extern "C" fn __new(this: *mut > as OptionFfi>::Target) { + unsafe { ptr::write(this, Option::<&mut RustVec<$ty>>::new_ffi()) } } #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$drop")] - unsafe extern "C" fn __drop(this: *mut RustOption<&mut RustVec<$ty>>) { + unsafe extern "C" fn __drop(this: *mut > as OptionFfi>::Target) { unsafe { ptr::drop_in_place(this) } } #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$has_value")] - unsafe extern "C" fn __has_value(this: *const RustOption<&mut RustVec<$ty>>) -> bool { - let o: &RustOption<&mut RustVec<$ty>> = unsafe { this.as_ref().unwrap() }; - o.as_option_vec_mut().is_some() + unsafe extern "C" fn __has_value(this: *const > as OptionFfi>::Target) -> bool { + let o: &> as OptionFfi>::Target = unsafe { this.as_ref().unwrap() }; + > as OptionFfi>::Target::as_option_vec_mut(o).is_some() } #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$value_const")] - unsafe extern "C" fn __value_const(this: *const RustOption<&mut RustVec<$ty>>) -> *const RustVec<$ty> { - let v: &alloc::vec::Vec<_> = unsafe { this.as_ref().unwrap().as_option_vec_mut().as_ref().unwrap() }; + unsafe extern "C" fn __value_const(this: *const > as OptionFfi>::Target) -> *const RustVec<$ty> { + let v: &alloc::vec::Vec<_> = unsafe { > as OptionFfi>::Target::as_option_vec_mut(this.as_ref().unwrap()).as_ref().unwrap() }; v as *const alloc::vec::Vec<$ty> as *const RustVec<$ty> } #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$value")] - unsafe extern "C" fn __value(this: *mut RustOption<&mut RustVec<$ty>>) -> *mut RustVec<$ty> { - let ptr = unsafe { this.as_mut().unwrap().as_option_vec_mut_mut().as_mut().unwrap() }; + unsafe extern "C" fn __value(this: *mut > as OptionFfi>::Target) -> *mut RustVec<$ty> { + let ptr = unsafe { > as OptionFfi>::Target::as_option_vec_mut_mut(this.as_mut().unwrap()).as_mut().unwrap() }; *ptr as *mut alloc::vec::Vec<$ty> as *mut RustVec<$ty> } #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$set")] unsafe extern "C" fn __set( - this: *mut RustOption<&mut RustVec<$ty>>, + this: *mut > as OptionFfi>::Target, value: *mut RustVec<$ty>, ) { - unsafe { this.as_mut().unwrap().as_option_vec_mut_mut().replace((&mut *value).as_mut_vec()); } + unsafe { > as OptionFfi>::Target::as_option_vec_mut_mut(this.as_mut().unwrap()).replace((&mut *value).as_mut_vec()); } } }; }; diff --git a/tests/ffi/Cargo.toml b/tests/ffi/Cargo.toml index 167bbb02d..08c2beb12 100644 --- a/tests/ffi/Cargo.toml +++ b/tests/ffi/Cargo.toml @@ -9,7 +9,7 @@ publish = false path = "lib.rs" [dependencies] -cxx = { path = "../..", default-features = false } +cxx = { path = "../..", default-features = true } [build-dependencies] cxx-build = { path = "../../gen/build" } diff --git a/tests/ui/option_not_sized.rs b/tests/ui/option_not_sized.rs index f0294e4ef..75ae3c15e 100644 --- a/tests/ui/option_not_sized.rs +++ b/tests/ui/option_not_sized.rs @@ -1,5 +1,6 @@ fn foo() { - const _: cxx::RustOption::<&dyn core::fmt::Debug> = cxx::RustOption::<&dyn core::fmt::Debug>::new(); + const _: as ::cxx::private::OptionFfi>::Target = + as ::cxx::private::OptionFfi>::Target::new(); } fn main() {} diff --git a/tests/ui/option_not_sized.stderr b/tests/ui/option_not_sized.stderr index 9a11e8dbf..f3841f83c 100644 --- a/tests/ui/option_not_sized.stderr +++ b/tests/ui/option_not_sized.stderr @@ -1,48 +1,38 @@ error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time --> tests/ui/option_not_sized.rs:2:14 | -2 | const _: cxx::RustOption::<&dyn core::fmt::Debug> = cxx::RustOption::<&dyn core::fmt::Debug>::new(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time +2 | const _: as ::cxx::private::OptionFfi>::Target = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)`, which is required by `&'static (dyn Debug + 'static): cxx::rust_option::OptionTarget` - = help: the following other types implement trait `cxx::rust_option::OptionTarget`: - Box - Pin<&mut T> - &T - &mut T - = note: required for `&'static (dyn Debug + 'static)` to implement `cxx::rust_option::OptionTarget` -note: required by a bound in `RustOption` - --> src/rust_option.rs + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)`, which is required by `Option<&'static (dyn Debug + 'static)>: OptionFfi` + = help: the trait `OptionFfi` is implemented for `Option` + = note: required for `&'static (dyn Debug + 'static)` to implement `cxx::rust_option::OptionPtrTarget` + = note: required for `Option<&'static (dyn Debug + 'static)>` to implement `OptionFfi` + +error[E0599]: no function or associated item named `new` found for struct `cxx::rust_option::_::RustOption` in the current scope + --> tests/ui/option_not_sized.rs:3:79 | - | pub struct RustOption { - | ^^^^^^^^^^^^ required by this bound in `RustOption` +3 | as ::cxx::private::OptionFfi>::Target::new(); + | ^^^ function or associated item not found in `RustOption<&dyn Debug>` -error[E0599]: the function or associated item `new` exists for struct `RustOption<&dyn Debug>`, but its trait bounds were not satisfied - --> tests/ui/option_not_sized.rs:2:99 +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> tests/ui/option_not_sized.rs:3:9 | -2 | const _: cxx::RustOption::<&dyn core::fmt::Debug> = cxx::RustOption::<&dyn core::fmt::Debug>::new(); - | ^^^ function or associated item cannot be called on `RustOption<&dyn Debug>` due to unsatisfied trait bounds +3 | as ::cxx::private::OptionFfi>::Target::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = note: the following trait bounds were not satisfied: - `&dyn Debug: cxx::rust_option::OptionTarget` - `&dyn Debug: cxx::rust_option::private::Sealed` - which is required by `&dyn Debug: cxx::rust_option::OptionTarget` + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)`, which is required by `Option<&'static (dyn Debug + 'static)>: OptionFfi` + = help: the trait `OptionFfi` is implemented for `Option` + = note: required for `&'static (dyn Debug + 'static)` to implement `cxx::rust_option::OptionPtrTarget` + = note: required for `Option<&'static (dyn Debug + 'static)>` to implement `OptionFfi` error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time - --> tests/ui/option_not_sized.rs:2:57 - | -2 | const _: cxx::RustOption::<&dyn core::fmt::Debug> = cxx::RustOption::<&dyn core::fmt::Debug>::new(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + --> tests/ui/option_not_sized.rs:3:9 | - = help: the trait `Sized` is not implemented for `dyn Debug`, which is required by `&dyn Debug: cxx::rust_option::OptionTarget` - = help: the following other types implement trait `cxx::rust_option::OptionTarget`: - Box - Pin<&mut T> - &T - &mut T - = note: required for `&dyn Debug` to implement `cxx::rust_option::OptionTarget` -note: required by a bound in `RustOption` - --> src/rust_option.rs +3 | as ::cxx::private::OptionFfi>::Target::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - | pub struct RustOption { - | ^^^^^^^^^^^^ required by this bound in `RustOption` + = help: the trait `Sized` is not implemented for `dyn Debug`, which is required by `Option<&dyn Debug>: OptionFfi` + = help: the trait `OptionFfi` is implemented for `Option` + = note: required for `&dyn Debug` to implement `cxx::rust_option::OptionPtrTarget` + = note: required for `Option<&dyn Debug>` to implement `OptionFfi` diff --git a/tests/ui/option_safe_unsized.rs b/tests/ui/option_safe_unsized.rs deleted file mode 100644 index e9c913e1d..000000000 --- a/tests/ui/option_safe_unsized.rs +++ /dev/null @@ -1,3 +0,0 @@ -const _: () = cxx::private::assert_option_safe::<&str>(); - -fn main() {} diff --git a/tests/ui/option_safe_unsized.stderr b/tests/ui/option_safe_unsized.stderr deleted file mode 100644 index a228c142a..000000000 --- a/tests/ui/option_safe_unsized.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0080]: evaluation of `cxx::private::assert_option_safe::__SizeCheck::<&str>::_IS_OPTION_SIZE` failed - --> src/rust_option.rs - | - | const _IS_OPTION_SIZE: () = assert!(mem::size_of::>() == mem::size_of::()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: mem::size_of::>() == mem::size_of::()', $DIR/src/rust_option.rs:51:37 - | - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) - -note: erroneous constant encountered - --> src/rust_option.rs - | - | let _: () = __SizeCheck::::_IS_OPTION_SIZE; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/option_safe_usize.rs b/tests/ui/option_safe_usize.rs deleted file mode 100644 index 64231de14..000000000 --- a/tests/ui/option_safe_usize.rs +++ /dev/null @@ -1,3 +0,0 @@ -const _: () = cxx::private::assert_option_safe::(); - -fn main() {} diff --git a/tests/ui/option_safe_usize.stderr b/tests/ui/option_safe_usize.stderr deleted file mode 100644 index 8e51cf45c..000000000 --- a/tests/ui/option_safe_usize.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0080]: evaluation of `cxx::private::assert_option_safe::__SizeCheck::::_IS_OPTION_SIZE` failed - --> src/rust_option.rs - | - | const _IS_OPTION_SIZE: () = assert!(mem::size_of::>() == mem::size_of::()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: mem::size_of::>() == mem::size_of::()', $DIR/src/rust_option.rs:51:37 - | - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) - -note: erroneous constant encountered - --> src/rust_option.rs - | - | let _: () = __SizeCheck::::_IS_OPTION_SIZE; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^