From 017a96a166a26344c6cbe219a0ef3df37d4a1a14 Mon Sep 17 00:00:00 2001 From: Eric Kuecks Date: Mon, 2 Sep 2024 21:09:26 -0700 Subject: [PATCH 1/2] Use trait indirection for Option FFI --- macro/src/expand.rs | 163 +++---- src/lib.rs | 5 +- src/rust_option.rs | 726 +++++++++++++++++----------- src/symbols/rust_option.rs | 80 +-- tests/ffi/Cargo.toml | 2 +- tests/ui/option_not_sized.rs | 3 +- tests/ui/option_not_sized.stderr | 84 +--- 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, 588 insertions(+), 507 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..79514500f 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,41 +722,9 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { } } Type::RustOption(ty) => { - let inner = &ty.inner; - 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()) - } - true => { - quote_spanned!(span=> ::cxx::private::RustOption::<&mut _>::from_raw(#call).into_option_string_mut()) - } - }, - 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()) - } - true => { - quote_spanned!(span=> ::cxx::private::RustOption::<&mut _>::from_raw(#call).into_option_vec_string_mut()) - } - }, - Type::RustVec(_) => match r.mutable { - false => { - quote_spanned!(span=> ::cxx::private::RustOption::<&_>::from_raw(#call).into_option_vec_ref()) - } - true => { - quote_spanned!(span=> ::cxx::private::RustOption::<&mut _>::from_raw(#call).into_option_vec_mut()) - } - }, - _ => { - quote_spanned!(span=> ::cxx::private::RustOption::<#inner>::from_raw(#call).into_option()) - } - }, - _ => { - quote_spanned!(span=> ::cxx::private::RustOption::<#inner>::from_raw(#call).into_option()) - } - } + let abs_ty = quote!(<#ty as ::cxx::private::OptionFfi>); + let abs_ty_target = quote!(#abs_ty::Target); + quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#call as _))) } Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { @@ -1092,27 +1061,9 @@ fn expand_rust_function_shim_impl( } Type::RustOption(ty) => { requires_unsafe = true; - let inner = &ty.inner; - 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()), - }, - 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()), - } - }, - _ => quote_spanned!(span=> ::cxx::private::RustOption::<#inner>::from_raw(#var).into_option()), - } - _ => quote_spanned!(span=> ::cxx::private::RustOption::<#inner>::from_raw(#var).into_option()), - } + let abs_ty = quote!(<#ty as ::cxx::private::OptionFfi>); + let abs_ty_target = quote!(#abs_ty::Target); + quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#var as _))) } Type::UniquePtr(_) => { requires_unsafe = true; @@ -1187,27 +1138,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 +1641,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 +1657,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 +1668,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 +1686,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 +1701,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,19 +1716,20 @@ 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 { this.as_ref().unwrap().value().is_some() } + 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().has_value() } } #set_value_impl } @@ -2188,6 +2150,23 @@ fn indirect_return(sig: &Signature, types: &Types) -> bool { .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) } +fn expand_option_ffi_type(ty: &Type, types: &Types, proper: bool) -> TokenStream { + match ty { + Type::Ident(ident) if ident.rust == RustString => { + let span = ident.rust.span(); + quote_spanned!(span=> ::cxx::alloc::string::String) + } + Type::RustVec(ty) => { + let span = ty.name.span(); + let langle = ty.langle; + let elem = expand_option_ffi_type(&ty.inner, types, proper); + let rangle = ty.rangle; + quote_spanned!(span=> ::cxx::alloc::vec::Vec #langle #elem #rangle) + } + _ => expand_extern_type(ty, types, proper), + } +} + fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream { match ty { Type::Ident(ident) if ident.rust == RustString => { @@ -2253,7 +2232,13 @@ fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream { } } Type::RustBox(_) => expand_extern_type(&ty.inner, types, proper), - _ => unreachable!(), + _ => { + let span = ty.name.span(); + let langle = ty.langle; + let inner = expand_option_ffi_type(&ty.inner, types, proper); + let rangle = ty.rangle; + quote_spanned!(span=> #langle ::core::option::Option #langle #inner #rangle as ::cxx::private::OptionFfi #rangle ::Target) + } }, Type::Ref(ty) => { let ampersand = ty.ampersand; diff --git a/src/lib.rs b/src/lib.rs index d22e7f7f5..aea069634 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; @@ -512,8 +511,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..76cbead01 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 { + // The FFI type that this Rust type is mapped to + type Target; + + // Option -> RustOption + 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; + // RustOption -> Option + 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; -} +// Trait will be implemented by RustOption for some Ts +pub trait OptionFfiInverse: private::Sealed + Sized { + // The Rust type that this FFI type is mapped to + 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() + // RustOption -> Option + 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 + // Option -> RustOption + 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 } - 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) }); + /// 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 } -} -#[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 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_option_vec_ref(self) -> Option<&'a Vec> { - unsafe { core::mem::transmute::>, RustOption<&Vec>>(self) } - .into_option() - } + 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 as_option_vec_ref(&self) -> &Option<&'a Vec> { - unsafe { &*(self as *const RustOption<&RustVec> as *const RustOption<&Vec>) } - .as_option() - } + /// 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 + } - pub fn as_option_vec_ref_mut(&mut self) -> &mut Option<&'a Vec> { - unsafe { &mut *(self as *mut RustOption<&RustVec> as *mut RustOption<&Vec>) } - .as_mut_option() + /// 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 + } } -} -#[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), - ) + #[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_mut(self) -> Option<&'a mut Vec> { - unsafe { - core::mem::transmute::>, RustOption<&mut Vec>>(self) + pub fn into_option_vec_ref(this: RustOption<&'a RustVec>) -> Option<&'a Vec> { + unsafe { core::mem::transmute::>, RustOption<&Vec>>(this) } + .into_option() } - .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>) + 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() } - .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 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() } - .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 into_rust_option_rust_vec_ref(self) -> RustOption<&'a RustVec> { + unsafe { core::mem::transmute::>, RustOption<&RustVec>>(self) } } } - pub fn into_option_vec_string_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), + ) + } } - .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_option_vec_mut(this: RustOption<&'a mut RustVec>) -> Option<&'a mut Vec> { + unsafe { + core::mem::transmute::>, RustOption<&mut Vec>>(this) + } + .into_option() } - .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), - ) + 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 into_option_vec_string_mut(self) -> Option<&'a mut Vec> { - unsafe { core::mem::transmute::>, RustOption<&mut Vec>>(self) }.into_option() - } + 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() + } - 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>)) - .as_mut_option() + pub fn into_rust_option_rust_vec_mut(self) -> RustOption<&'a mut RustVec> { + unsafe { + core::mem::transmute::>, RustOption<&mut RustVec>>(self) + } } } -} -#[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, - )) + #[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), + ) + } } - } - pub fn into_option_string_ref(self) -> Option<&'a String> { - unsafe { core::mem::transmute::, RustOption<&String>>(self) } + pub fn into_option_vec_string_ref( + this: RustOption<&'a RustVec>, + ) -> Option<&'a Vec> { + unsafe { + core::mem::transmute::>, RustOption<&Vec>>( + this, + ) + } .into_option() - } + } - pub fn as_option_string_ref_mut(&mut self) -> &mut Option<&'a String> { - unsafe { &mut *(self as *mut RustOption<&RustString> as *mut RustOption<&String>) } + 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() - } -} + } -#[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_rust_option_rust_vec_rust_string_ref( + self, + ) -> RustOption<&'a RustVec> { + unsafe { + core::mem::transmute::>, RustOption<&RustVec>>( + self, + ) + } } } - pub fn into_option_string_mut(self) -> Option<&'a mut String> { - unsafe { - core::mem::transmute::, RustOption<&mut String>>(self) + #[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( + 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<'b>( + this: &'b mut RustOption<&'a mut RustVec>, + ) -> &'b mut Option<&'a mut Vec> { + unsafe { + &mut *(this as *mut RustOption<&mut RustVec> as *mut RustOption<&mut Vec>) + }.as_mut_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) + } } - .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 String> { + pub fn from_option_string_ref(other: Option<&'a String>) -> RustOption<&'a RustString> { + unsafe { + core::mem::transmute::, RustOption<&RustString>>( + RustOption::from_option(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 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() } + + pub fn into_rust_option_rust_string_ref(self) -> RustOption<&'a RustString> { + unsafe { core::mem::transmute::, RustOption<&RustString>>(self) } + } } -} -impl Drop for RustOption { - fn drop(&mut self) { - self.as_mut_option().take(); + #[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 { + &mut *(this as *mut RustOption<&mut RustString> as *mut RustOption<&mut String>) + }.as_mut_option() + } + + 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 d90600163..839280632 100644 --- a/src/symbols/rust_option.rs +++ b/src/symbols/rust_option.rs @@ -1,9 +1,10 @@ -use core::ffi::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")] use crate::rust_vec::RustVec; +use core::ffi::c_char; use core::mem; use core::ptr; @@ -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 b6fc17078..20bbdbedc 100644 --- a/tests/ui/option_not_sized.stderr +++ b/tests/ui/option_not_sized.stderr @@ -1,71 +1,35 @@ -error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time +error[E0277]: the trait bound `&'static (dyn Debug + 'static): cxx::rust_option::OptionPtrTarget` is not satisfied --> 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 = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Sized` is not implemented for `(dyn Debug + 'static)`, which is required by `Option<&'static (dyn Debug + 'static)>: OptionFfi` | - = 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`: - &T - &mut T - Box - Pin<&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 - | - | pub struct RustOption { - | ^^^^^^^^^^^^ required by this bound in `RustOption` + = 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]: 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 - | -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 - | - ::: $RUST/core/src/fmt/mod.rs +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 trait Debug { - | --------------- doesn't satisfy `dyn Debug: Sized` - | - = note: the following trait bounds were not satisfied: - `dyn Debug: Sized` - which is required by `&dyn Debug: cxx::rust_option::OptionTarget` +3 | as ::cxx::private::OptionFfi>::Target::new(); + | ^^^ function or associated item not found in `RustOption<&dyn Debug>` -error[E0277]: the size for values of type `(dyn Debug + 'static)` 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 +error[E0277]: the trait bound `&'static (dyn Debug + 'static): cxx::rust_option::OptionPtrTarget` is not satisfied + --> tests/ui/option_not_sized.rs:3:9 | - = 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`: - &T - &mut T - Box - Pin<&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 +3 | as ::cxx::private::OptionFfi>::Target::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Sized` is not implemented for `(dyn Debug + 'static)`, which is required by `Option<&'static (dyn Debug + 'static)>: OptionFfi` | - | pub struct RustOption { - | ^^^^^^^^^^^^ required by this bound in `RustOption` + = 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 +error[E0277]: the trait bound `&dyn Debug: cxx::rust_option::OptionPtrTarget` is not satisfied + --> 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`: - &T - &mut T - Box - Pin<&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(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Sized` is not implemented for `dyn Debug`, which is required by `Option<&dyn Debug>: OptionFfi` | - | pub struct RustOption { - | ^^^^^^^^^^^^ required by this bound in `RustOption` + = 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; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From b62a27e74f82c8c5f8c85dfe0b319f4d01bbd476 Mon Sep 17 00:00:00 2001 From: Eric Kuecks Date: Mon, 2 Sep 2024 23:14:24 -0700 Subject: [PATCH 2/2] Support Option and Option --- gen/src/builtin.rs | 2 + gen/src/write.rs | 135 +++++- include/cxx.h | 693 +++++++++++++++++++------------ macro/src/expand.rs | 132 +++++- src/cxx.cc | 224 ++++++++-- src/rust_option.rs | 117 +++--- src/symbols/rust_option.rs | 143 +++++-- syntax/check.rs | 6 + syntax/instantiate.rs | 16 + syntax/types.rs | 3 +- tests/ffi/lib.rs | 60 +++ tests/ffi/tests.cc | 68 +++ tests/ffi/tests.h | 8 + tests/test.rs | 20 + tests/ui/option_not_sized.stderr | 15 +- tests/ui/option_string.rs | 12 - tests/ui/option_string.stderr | 5 - tests/ui/option_vec.rs | 12 - tests/ui/option_vec.stderr | 5 - 19 files changed, 1215 insertions(+), 461 deletions(-) delete mode 100644 tests/ui/option_string.rs delete mode 100644 tests/ui/option_string.stderr delete mode 100644 tests/ui/option_vec.rs delete mode 100644 tests/ui/option_vec.stderr diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index 457a91974..da72e637d 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -52,6 +52,8 @@ pub(super) fn write(out: &mut OutFile) { let out = &mut builtin.content; if builtin.rust_option { + builtin.rust_string = true; + builtin.rust_vec = true; builtin.rust_box = true; } diff --git a/gen/src/write.rs b/gen/src/write.rs index e3df6f8e2..f129ae99b 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -820,9 +820,26 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { if i > 0 { write!(out, ", "); } - if let Type::RustBox(_) | Type::RustOption(_) = &arg.ty { + if let Type::RustBox(_) = &arg.ty { write_type(out, &arg.ty); write!(out, "::from_raw({})", arg.name.cxx); + } else if let Type::RustOption(inner) = &arg.ty { + match &inner.inner { + Type::RustVec(_) => { + out.builtin.unsafe_bitcopy = true; + write_type(out, &arg.ty); + write!(out, "(::rust::unsafe_bitcopy, *{})", arg.name.cxx,); + } + Type::Ident(ty) if ty.rust == RustString => { + out.builtin.unsafe_bitcopy = true; + write_type(out, &arg.ty); + write!(out, "(::rust::unsafe_bitcopy, *{})", arg.name.cxx,); + } + _ => { + 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); @@ -837,6 +854,8 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.builtin.unsafe_bitcopy = true; write_type(out, &arg.ty); write!(out, "(::rust::unsafe_bitcopy, *{})", arg.name.cxx); + } else if let Type::RustOption(_) = arg.ty { + write!(out, "std::move(* {})", arg.name.cxx); } else if out.types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, "::std::move(*{})", arg.name.cxx); @@ -846,7 +865,11 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { } write!(out, ")"); match &efn.ret { - Some(Type::RustBox(_) | Type::RustOption(_)) => write!(out, ".into_raw()"), + Some(Type::RustBox(_)) => write!(out, ".into_raw()"), + Some(Type::RustOption(inner)) => match inner.inner { + Type::Ident(_) | Type::RustVec(_) => {} + _ => write!(out, ".into_raw()"), + }, Some(Type::UniquePtr(_)) => write!(out, ".release()"), Some(Type::Str(_) | Type::SliceRef(_)) if !indirect_return => write!(out, ")"), _ => {} @@ -1051,10 +1074,17 @@ fn write_rust_function_shim_impl( } else if let Some(ret) = &sig.ret { write!(out, "return "); match ret { - Type::RustBox(_) | Type::RustOption(_) => { + Type::RustBox(_) => { write_type(out, ret); write!(out, "::from_raw("); } + Type::RustOption(inner) => match inner.inner { + Type::Ident(_) | Type::RustVec(_) => {} + _ => { + write_type(out, ret); + write!(out, "::from_raw("); + } + }, Type::UniquePtr(_) => { write_type(out, ret); write!(out, "("); @@ -1092,7 +1122,11 @@ fn write_rust_function_shim_impl( } write!(out, "{}", arg.name.cxx); match &arg.ty { - Type::RustBox(_) | Type::RustOption(_) => write!(out, ".into_raw()"), + Type::RustBox(_) => write!(out, ".into_raw()"), + Type::RustOption(inner) => match inner.inner { + Type::Ident(_) | Type::RustVec(_) => write!(out, "$.value"), + _ => write!(out, ".into_raw()"), + }, Type::UniquePtr(_) => write!(out, ".release()"), ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} @@ -1114,15 +1148,17 @@ fn write_rust_function_shim_impl( } write!(out, ")"); if !indirect_return { - if let Some( - Type::RustBox(_) - | Type::UniquePtr(_) - | Type::Str(_) - | Type::SliceRef(_) - | Type::RustOption(_), - ) = &sig.ret - { - write!(out, ")"); + if let Some(ret) = &sig.ret { + match &ret { + Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRef(_) => { + write!(out, ")"); + } + Type::RustOption(inner) => match &inner.inner { + Type::Ident(_) | Type::RustVec(_) => {} + _ => write!(out, ")"), + }, + _ => {} + } } } writeln!(out, ";"); @@ -1172,7 +1208,14 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { } write!(out, "*"); } - Type::RustOption(ty) => write_indirect_return_type(out, &ty.inner), + Type::RustOption(ty) => match &ty.inner { + Type::RustBox(_) | Type::Ref(_) => write_indirect_return_type(out, &ty.inner), + _ => { + write!(out, "::rust::Option<"); + write_indirect_return_type(out, &ty.inner); + write!(out, ">"); + } + }, _ => write_type(out, ty), } } @@ -1180,7 +1223,7 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { write_indirect_return_type(out, ty); match ty { - Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) | Type::RustOption(_) => {} + Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} Type::Str(_) | Type::SliceRef(_) => write!(out, " "), _ => write_space_after_type(out, ty), } @@ -1201,6 +1244,11 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: Option<&Type>) { } write!(out, "*"); } + Type::RustVec(_) | Type::Ident(_) => { + if out.types.needs_indirect_abi(&ty.inner) { + write!(out, "void "); + } + } _ => unreachable!(), }, Some(Type::Ref(ty)) => { @@ -1237,7 +1285,11 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var) { } write!(out, "*"); } - _ => unreachable!(), + _ => { + write!(out, "::rust::Option<"); + write_type_space(out, &ty.inner); + write!(out, "> const "); + } }, _ => write_type_space(out, &arg.ty), } @@ -1398,6 +1450,8 @@ enum RustOption<'a> { MutRef(&'a Ident), RefVec(&'a Ident), MutRefVec(&'a Ident), + Vec(&'a Ident), + Ident(&'a Ident), } trait ToTypename { @@ -1436,6 +1490,10 @@ impl<'a> ToTypename for RustOption<'a> { RustOption::MutRefVec(inner) => { format!("::rust::cxxbridge1::Vec<{}>&", inner.to_typename(types)) } + RustOption::Vec(inner) => { + format!("::rust::cxxbridge1::Vec<{}>", inner.to_typename(types)) + } + RustOption::Ident(ident) => ident.to_typename(types), } } } @@ -1464,13 +1522,17 @@ impl<'a> ToMangled for UniquePtr<'a> { impl<'a> ToMangled for RustOption<'a> { fn to_mangled(&self, types: &Types) -> Symbol { match self { - RustOption::RustBox(inner) => symbol::join(&[&"Box", &inner.to_mangled(types)]), - RustOption::Ref(inner) => symbol::join(&[&"const", &inner.to_mangled(types)]), - RustOption::MutRef(inner) => symbol::join(&[&inner.to_mangled(types)]), + RustOption::RustBox(inner) => symbol::join(&[&"rust_box", &inner.to_mangled(types)]), + RustOption::Ref(inner) => symbol::join(&[&"const", &"ref", &inner.to_mangled(types)]), + RustOption::MutRef(inner) => symbol::join(&[&"ref", &inner.to_mangled(types)]), RustOption::RefVec(inner) => { - symbol::join(&[&"const", &"Vec", &inner.to_mangled(types)]) + symbol::join(&[&"const", &"ref", &"rust_vec", &inner.to_mangled(types)]) + } + RustOption::MutRefVec(inner) => { + symbol::join(&[&"ref", &"rust_vec", &inner.to_mangled(types)]) } - RustOption::MutRefVec(inner) => symbol::join(&[&"Vec", &inner.to_mangled(types)]), + RustOption::Vec(inner) => symbol::join(&[&"rust_vec", &inner.to_mangled(types)]), + RustOption::Ident(ident) => symbol::join(&[&ident.to_mangled(types)]), } } } @@ -1628,6 +1690,25 @@ fn write_rust_option_extern(out: &mut OutFile, inner: OptionInner) { ); (RustOption::MutRefVec(key.rust), false, value_type) } + OptionInner::Vec(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + let resolve = out.types.resolve(&key); + let value_type = format!( + "::rust::cxxbridge1::Vec<{}>", + resolve.name.to_fully_qualified() + ); + (RustOption::Vec(key.rust), false, value_type) + } + OptionInner::Ident(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + let resolve = out.types.resolve(&key); + let value_type = resolve.name.to_fully_qualified(); + (RustOption::Ident(key.rust), false, value_type) + } }; let inner = element.to_typename(out.types); let instance = element.to_mangled(out.types); @@ -1824,6 +1905,18 @@ fn write_rust_option_impl(out: &mut OutFile, inner: OptionInner) { } (RustOption::MutRefVec(key.rust), false, false) } + OptionInner::Vec(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + (RustOption::Vec(key.rust), false, true) + } + OptionInner::Ident(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + (RustOption::Ident(key.rust), false, true) + } }; let inner = element.to_typename(out.types); let instance = element.to_mangled(out.types); diff --git a/include/cxx.h b/include/cxx.h index 3ac8c31c7..750583419 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -300,6 +300,90 @@ class Box final { }; #endif // CXXBRIDGE1_RUST_BOX +#ifndef CXXBRIDGE1_RUST_VEC +// https://cxx.rs/binding/vec.html +template +class Vec final { +public: + using value_type = T; + + Vec() noexcept; + Vec(std::initializer_list); + Vec(const Vec &); + Vec(Vec &&) noexcept; + ~Vec() noexcept; + + Vec &operator=(Vec &&) &noexcept; + Vec &operator=(const Vec &) &; + + std::size_t size() const noexcept; + bool empty() const noexcept; + const T *data() const noexcept; + T *data() noexcept; + std::size_t capacity() const noexcept; + + const T &operator[](std::size_t n) const noexcept; + const T &at(std::size_t n) const; + const T &front() const noexcept; + const T &back() const noexcept; + + T &operator[](std::size_t n) noexcept; + T &at(std::size_t n); + T &front() noexcept; + T &back() noexcept; + + void reserve(std::size_t new_cap); + void push_back(const T &value); + void push_back(T &&value); + template + void emplace_back(Args &&...args); + void truncate(std::size_t len); + void clear(); + + using iterator = typename Slice::iterator; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = typename Slice::iterator; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + void swap(Vec &) noexcept; + + // Internal API only intended for the cxxbridge code generator. + Vec(unsafe_bitcopy_t, const Vec &) noexcept; + +private: + void reserve_total(std::size_t new_cap) noexcept; + void set_len(std::size_t len) noexcept; + void drop() noexcept; + + friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } + + // Size and alignment statically verified by rust_vec.rs. + std::array repr; +}; +#endif // CXXBRIDGE1_RUST_VEC + +#ifndef CXXBRIDGE1_RUST_FN +// https://cxx.rs/binding/fn.html +template +class Fn; + +template +class Fn final { +public: + Ret operator()(Args... args) const noexcept; + Fn operator*() const noexcept; + +private: + Ret (*trampoline)(Args..., void *fn) noexcept; + void *fn; +}; +#endif // CXXBRIDGE1_RUST_FN + #ifndef CXXBRIDGE1_RUST_OPTION template class Option final {}; @@ -318,6 +402,9 @@ class Option { T& operator*() noexcept; T* operator->() noexcept; + bool operator==(const Option &) const noexcept; + bool operator!=(const Option &) const noexcept; + bool has_value() const noexcept; const T& value() const noexcept; T& value() noexcept; @@ -345,6 +432,9 @@ class Option { const T& operator*() const noexcept; const T* operator->() const noexcept; + bool operator==(const Option &) const noexcept; + bool operator!=(const Option &) const noexcept; + bool has_value() const noexcept; const T& value() const noexcept; void reset(); @@ -371,6 +461,9 @@ class Option> { Box& operator*() noexcept; Box* operator->() noexcept; + bool operator==(const Option &) const noexcept; + bool operator!=(const Option &) const noexcept; + bool has_value() const noexcept; const Box& value() const noexcept; Box& value() noexcept; @@ -386,91 +479,68 @@ class Option> { void drop() noexcept; }; -#endif // CXXBRIDGE1_RUST_OPTION -#ifndef CXXBRIDGE1_RUST_VEC -// https://cxx.rs/binding/vec.html template -class Vec final { +class Option> { public: - using value_type = T; - - Vec() noexcept; - Vec(std::initializer_list); - Vec(const Vec &); - Vec(Vec &&) noexcept; - ~Vec() noexcept; - - Vec &operator=(Vec &&) &noexcept; - Vec &operator=(const Vec &) &; - - std::size_t size() const noexcept; - bool empty() const noexcept; - const T *data() const noexcept; - T *data() noexcept; - std::size_t capacity() const noexcept; - - const T &operator[](std::size_t n) const noexcept; - const T &at(std::size_t n) const; - const T &front() const noexcept; - const T &back() const noexcept; - - T &operator[](std::size_t n) noexcept; - T &at(std::size_t n); - T &front() noexcept; - T &back() noexcept; - - void reserve(std::size_t new_cap); - void push_back(const T &value); - void push_back(T &&value); - template - void emplace_back(Args &&...args); - void truncate(std::size_t len); - void clear(); - - using iterator = typename Slice::iterator; - iterator begin() noexcept; - iterator end() noexcept; + Option() noexcept; + Option(Option&&) noexcept; + Option(Vec&&) noexcept; + ~Option() noexcept; - using const_iterator = typename Slice::iterator; - const_iterator begin() const noexcept; - const_iterator end() const noexcept; - const_iterator cbegin() const noexcept; - const_iterator cend() const noexcept; + Option>& operator=(Option&&) noexcept; + const Vec& operator*() const noexcept; + const Vec* operator->() const noexcept; + Vec& operator*() noexcept; + Vec* operator->() noexcept; - void swap(Vec &) noexcept; + bool has_value() const noexcept; + const Vec& value() const noexcept; + Vec& value() noexcept; + void reset(); + void set(Vec) noexcept; // Internal API only intended for the cxxbridge code generator. - Vec(unsafe_bitcopy_t, const Vec &) noexcept; - + Option(unsafe_bitcopy_t, const Option &) noexcept; private: - void reserve_total(std::size_t new_cap) noexcept; - void set_len(std::size_t len) noexcept; - void drop() noexcept; - - friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } - - // Size and alignment statically verified by rust_vec.rs. std::array repr; -}; -#endif // CXXBRIDGE1_RUST_VEC - -#ifndef CXXBRIDGE1_RUST_FN -// https://cxx.rs/binding/fn.html -template -class Fn; -template -class Fn final { -public: - Ret operator()(Args... args) const noexcept; - Fn operator*() const noexcept; + void drop() noexcept; +}; -private: - Ret (*trampoline)(Args..., void *fn) noexcept; - void *fn; +#define OPTION_DIRECT_DECL(INNER, REPR, COUNT) \ +template<> \ +class Option { \ +public: \ + Option() noexcept; \ + Option(Option&&) noexcept; \ + Option(INNER&&) noexcept; \ + ~Option() noexcept; \ + \ + Option& operator=(Option&&) noexcept; \ + const INNER& operator*() const noexcept; \ + const INNER* operator->() const noexcept; \ + INNER& operator*() noexcept; \ + INNER* operator->() noexcept; \ + \ + bool operator==(const Option &) const noexcept; \ + bool operator!=(const Option &) const noexcept; \ + \ + bool has_value() const noexcept; \ + const INNER& value() const noexcept; \ + INNER& value() noexcept; \ + void reset(); \ + void set(INNER) noexcept; \ + \ + Option(unsafe_bitcopy_t, const Option &) noexcept; \ +private: \ + std::array repr; \ + \ + void drop() noexcept; \ }; -#endif // CXXBRIDGE1_RUST_FN + +OPTION_DIRECT_DECL(String, std::uintptr_t, 3) +#endif // CXXBRIDGE1_RUST_OPTION #ifndef CXXBRIDGE1_RUST_ERROR #define CXXBRIDGE1_RUST_ERROR @@ -917,80 +987,273 @@ template Box::Box(uninit) noexcept {} #endif // CXXBRIDGE1_RUST_BOX +#ifndef CXXBRIDGE1_RUST_VEC +#define CXXBRIDGE1_RUST_VEC +template +Vec::Vec(std::initializer_list init) : Vec{} { + this->reserve_total(init.size()); + std::move(init.begin(), init.end(), std::back_inserter(*this)); +} -#ifndef CXXBRIDGE1_RUST_OPTION -#define CXXBRIDGE1_RUST_OPTION template -Option::Option(Option&& other) noexcept { - new (this) Option(); - if (other.has_value()) { - set(other.value()); - } - new (&other) Option(); +Vec::Vec(const Vec &other) : Vec() { + this->reserve_total(other.size()); + std::copy(other.begin(), other.end(), std::back_inserter(*this)); } template -Option::Option(T& value) noexcept { - new (this) Option(); - set(value); +Vec::Vec(Vec &&other) noexcept : repr(other.repr) { + new (&other) Vec(); } template -Option::~Option() noexcept { +Vec::~Vec() noexcept { this->drop(); } template -Option& Option::operator=(Option&& other) noexcept { - this->reset(); - if (other.has_value()) { - set(other.value()); +Vec &Vec::operator=(Vec &&other) &noexcept { + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + return *this; +} + +template +Vec &Vec::operator=(const Vec &other) & { + if (this != &other) { + this->drop(); + new (this) Vec(other); } - new (&other) Option(); return *this; } template -const T& Option::operator*() const noexcept { - return value(); +bool Vec::empty() const noexcept { + return this->size() == 0; } template -const T* Option::operator->() const noexcept { - return &value(); +T *Vec::data() noexcept { + return const_cast(const_cast *>(this)->data()); } template -T& Option::operator*() noexcept { - return value(); +const T &Vec::operator[](std::size_t n) const noexcept { + assert(n < this->size()); + auto data = reinterpret_cast(this->data()); + return *reinterpret_cast(data + n * size_of()); } template -T* Option::operator->() noexcept { - return &value(); +const T &Vec::at(std::size_t n) const { + if (n >= this->size()) { + panic("rust::Vec index out of range"); + } + return (*this)[n]; } template -void Option::reset() { - this->drop(); - new (this) Option(); +const T &Vec::front() const noexcept { + assert(!this->empty()); + return (*this)[0]; } template -Option Option::from_raw(T* ptr) noexcept { - Option opt{*ptr}; - return opt; +const T &Vec::back() const noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; } template -T* Option::into_raw() noexcept { - if (has_value()) { - return &value(); - } else { - return nullptr; - } +T &Vec::operator[](std::size_t n) noexcept { + assert(n < this->size()); + auto data = reinterpret_cast(this->data()); + return *reinterpret_cast(data + n * size_of()); } -//////// Option //////// + +template +T &Vec::at(std::size_t n) { + if (n >= this->size()) { + panic("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template +T &Vec::front() noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template +T &Vec::back() noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template +void Vec::reserve(std::size_t new_cap) { + this->reserve_total(new_cap); +} + +template +void Vec::push_back(const T &value) { + this->emplace_back(value); +} + +template +void Vec::push_back(T &&value) { + this->emplace_back(std::move(value)); +} + +template +template +void Vec::emplace_back(Args &&...args) { + auto size = this->size(); + this->reserve_total(size + 1); + ::new (reinterpret_cast(reinterpret_cast(this->data()) + + size * size_of())) + T(std::forward(args)...); + this->set_len(size + 1); +} + +template +void Vec::clear() { + this->truncate(0); +} + +template +typename Vec::iterator Vec::begin() noexcept { + return Slice(this->data(), this->size()).begin(); +} + +template +typename Vec::iterator Vec::end() noexcept { + return Slice(this->data(), this->size()).end(); +} + +template +typename Vec::const_iterator Vec::begin() const noexcept { + return this->cbegin(); +} + +template +typename Vec::const_iterator Vec::end() const noexcept { + return this->cend(); +} + +template +typename Vec::const_iterator Vec::cbegin() const noexcept { + return Slice(this->data(), this->size()).begin(); +} + +template +typename Vec::const_iterator Vec::cend() const noexcept { + return Slice(this->data(), this->size()).end(); +} + +template +void Vec::swap(Vec &rhs) noexcept { + using std::swap; + swap(this->repr, rhs.repr); +} + +// Internal API only intended for the cxxbridge code generator. +template +Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} +#endif // CXXBRIDGE1_RUST_VEC + +#ifndef CXXBRIDGE1_RUST_OPTION +#define CXXBRIDGE1_RUST_OPTION +//////// Option //////// +template +Option::Option(Option&& other) noexcept { + new (this) Option(); + if (other.has_value()) { + set(other.value()); + } + new (&other) Option(); +} + +template +Option::Option(T& value) noexcept { + new (this) Option(); + set(value); +} + +template +Option::~Option() noexcept { + this->drop(); +} + +template +Option& Option::operator=(Option&& other) noexcept { + this->reset(); + if (other.has_value()) { + set(other.value()); + } + new (&other) Option(); + return *this; +} + +template +const T& Option::operator*() const noexcept { + return value(); +} + +template +const T* Option::operator->() const noexcept { + return &value(); +} + +template +T& Option::operator*() noexcept { + return value(); +} + +template +T* Option::operator->() noexcept { + return &value(); +} + +template +bool Option::operator==(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() == other.value(); + } + return this->has_value() == other.has_value(); +} + +template +bool Option::operator!=(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() != other.value(); + } + return this->has_value() != other.has_value(); +} + +template +void Option::reset() { + this->drop(); + new (this) Option(); +} + +template +Option Option::from_raw(T* ptr) noexcept { + Option opt{*ptr}; + return opt; +} + +template +T* Option::into_raw() noexcept { + if (has_value()) { + return &value(); + } else { + return nullptr; + } +} +//////// Option //////// template Option::Option(const Option& other) noexcept { new (this) Option(); @@ -1048,6 +1311,22 @@ const T* Option::operator->() const noexcept { return &value(); } +template +bool Option::operator==(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() == other.value(); + } + return this->has_value() == other.has_value(); +} + +template +bool Option::operator!=(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() != other.value(); + } + return this->has_value() != other.has_value(); +} + template void Option::reset() { this->drop(); @@ -1119,6 +1398,22 @@ Box* Option>::operator->() noexcept { return &value(); } +template +bool Option>::operator==(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() == other.value(); + } + return this->has_value() == other.has_value(); +} + +template +bool Option>::operator!=(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() != other.value(); + } + return this->has_value() != other.has_value(); +} + template void Option>::reset() { this->drop(); @@ -1139,184 +1434,66 @@ T* Option>::into_raw() noexcept { return nullptr; } } -#endif // CXXBRIDGE1_RUST_OPTION - -#ifndef CXXBRIDGE1_RUST_VEC -#define CXXBRIDGE1_RUST_VEC -template -Vec::Vec(std::initializer_list init) : Vec{} { - this->reserve_total(init.size()); - std::move(init.begin(), init.end(), std::back_inserter(*this)); -} - -template -Vec::Vec(const Vec &other) : Vec() { - this->reserve_total(other.size()); - std::copy(other.begin(), other.end(), std::back_inserter(*this)); -} - +//////// Option> //////////// template -Vec::Vec(Vec &&other) noexcept : repr(other.repr) { - new (&other) Vec(); +Option>::Option(Option&& other) noexcept { + new (this) Option(); + if (other.has_value()) { + set(std::move(other.value())); + } + new (&other) Option(); } template -Vec::~Vec() noexcept { - this->drop(); +Option>::Option(Vec&& value) noexcept { + new (this) Option(); + set(std::move(value)); } template -Vec &Vec::operator=(Vec &&other) &noexcept { +Option>::~Option() noexcept { this->drop(); - this->repr = other.repr; - new (&other) Vec(); - return *this; } template -Vec &Vec::operator=(const Vec &other) & { - if (this != &other) { - this->drop(); - new (this) Vec(other); +Option>& Option>::operator=(Option&& other) noexcept { + this->reset(); + if (other.has_value()) { + set(std::move(other.value())); } + new (&other) Option(); return *this; } template -bool Vec::empty() const noexcept { - return this->size() == 0; -} - -template -T *Vec::data() noexcept { - return const_cast(const_cast *>(this)->data()); -} - -template -const T &Vec::operator[](std::size_t n) const noexcept { - assert(n < this->size()); - auto data = reinterpret_cast(this->data()); - return *reinterpret_cast(data + n * size_of()); -} - -template -const T &Vec::at(std::size_t n) const { - if (n >= this->size()) { - panic("rust::Vec index out of range"); - } - return (*this)[n]; -} - -template -const T &Vec::front() const noexcept { - assert(!this->empty()); - return (*this)[0]; -} - -template -const T &Vec::back() const noexcept { - assert(!this->empty()); - return (*this)[this->size() - 1]; -} - -template -T &Vec::operator[](std::size_t n) noexcept { - assert(n < this->size()); - auto data = reinterpret_cast(this->data()); - return *reinterpret_cast(data + n * size_of()); -} - -template -T &Vec::at(std::size_t n) { - if (n >= this->size()) { - panic("rust::Vec index out of range"); - } - return (*this)[n]; -} - -template -T &Vec::front() noexcept { - assert(!this->empty()); - return (*this)[0]; -} - -template -T &Vec::back() noexcept { - assert(!this->empty()); - return (*this)[this->size() - 1]; -} - -template -void Vec::reserve(std::size_t new_cap) { - this->reserve_total(new_cap); -} - -template -void Vec::push_back(const T &value) { - this->emplace_back(value); -} - -template -void Vec::push_back(T &&value) { - this->emplace_back(std::move(value)); -} - -template -template -void Vec::emplace_back(Args &&...args) { - auto size = this->size(); - this->reserve_total(size + 1); - ::new (reinterpret_cast(reinterpret_cast(this->data()) + - size * size_of())) - T(std::forward(args)...); - this->set_len(size + 1); -} - -template -void Vec::clear() { - this->truncate(0); -} - -template -typename Vec::iterator Vec::begin() noexcept { - return Slice(this->data(), this->size()).begin(); -} - -template -typename Vec::iterator Vec::end() noexcept { - return Slice(this->data(), this->size()).end(); -} - -template -typename Vec::const_iterator Vec::begin() const noexcept { - return this->cbegin(); +const Vec& Option>::operator*() const noexcept { + return value(); } template -typename Vec::const_iterator Vec::end() const noexcept { - return this->cend(); +const Vec* Option>::operator->() const noexcept { + return &value(); } template -typename Vec::const_iterator Vec::cbegin() const noexcept { - return Slice(this->data(), this->size()).begin(); +Vec& Option>::operator*() noexcept { + return value(); } template -typename Vec::const_iterator Vec::cend() const noexcept { - return Slice(this->data(), this->size()).end(); +Vec* Option>::operator->() noexcept { + return &value(); } template -void Vec::swap(Vec &rhs) noexcept { - using std::swap; - swap(this->repr, rhs.repr); +void Option>::reset() { + this->drop(); + new (this) Option(); } -// Internal API only intended for the cxxbridge code generator. template -Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} -#endif // CXXBRIDGE1_RUST_VEC +Option>::Option(unsafe_bitcopy_t, const Option &bits) noexcept : repr(bits.repr) {} +#endif // CXXBRIDGE1_RUST_OPTION #ifndef CXXBRIDGE1_IS_COMPLETE #define CXXBRIDGE1_IS_COMPLETE diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 79514500f..8369aea10 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -552,7 +552,8 @@ 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 mut improper = false; + let mut direct = false; let convert = quote!(<#ty as ::cxx::private::OptionFfi>::into_ffi(#var)); let call = match &ty.inner { Type::RustBox(ty) => { @@ -586,12 +587,18 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { quote!(#convert) } }, + Type::RustVec(_) | Type::Ident(_) => { + direct = true; + quote_spanned!(span=> #var.as_mut_ptr() as _) + }, _ => unreachable!(), }; - if improper { + if improper && !direct { quote_spanned!(span=> #call.into_raw_improper()) - } else { + } else if !direct { quote_spanned!(span=> #call.into_raw()) + } else { + quote_spanned!(span=> #call) } } Type::Ref(ty) => match &ty.inner { @@ -724,7 +731,14 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { Type::RustOption(ty) => { let abs_ty = quote!(<#ty as ::cxx::private::OptionFfi>); let abs_ty_target = quote!(#abs_ty::Target); - quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#call as _))) + match &ty.inner { + Type::RustVec(_) | Type::Ident(_) => { + quote_spanned!(span=> <#abs_ty_target as ::cxx::private::OptionFfiInverse>::into_option(#call)) + } + _ => { + quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#call as _))) + } + } } Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { @@ -1063,7 +1077,10 @@ fn expand_rust_function_shim_impl( requires_unsafe = true; let abs_ty = quote!(<#ty as ::cxx::private::OptionFfi>); let abs_ty_target = quote!(#abs_ty::Target); - quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#var as _))) + match &ty.inner { + Type::RustVec(_) | Type::Ident(_) => quote_spanned!(span=> ::cxx::core::mem::take(<#abs_ty_target as ::cxx::private::OptionFfiInverse>::as_mut_option(&mut *#var))), + _ => quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#var as _))), + } } Type::UniquePtr(_) => { requires_unsafe = true; @@ -1162,6 +1179,10 @@ fn expand_rust_function_shim_impl( } Some(quote_spanned!(span=> #abs_ty::into_ffi)) } + Type::RustVec(_) | Type::Ident(_) => { + process_converted = None; + Some(quote_spanned!(span=> <#abs_ty_target as ::cxx::private::OptionFfiInverse>::from_option)) + } _ => unreachable!(), } }, @@ -1525,7 +1546,10 @@ fn expand_rust_option( OptionInner::RustBox(key) => { let elem = key.rust; let resolve = types.resolve(elem); - let link_prefix = format!("cxxbridge1$rust_option$Box${}$", resolve.name.to_symbol()); + let link_prefix = format!( + "cxxbridge1$rust_option$rust_box${}$", + resolve.name.to_symbol() + ); let local_prefix = format_ident!("{}__box__option_", elem); (elem, key, resolve, link_prefix, local_prefix) } @@ -1535,28 +1559,31 @@ fn expand_rust_option( return TokenStream::new(); } let resolve = types.resolve(elem); - let link_prefix = format!("cxxbridge1$rust_option$const${}$", resolve.name.to_symbol()); + let link_prefix = format!( + "cxxbridge1$rust_option$const$ref${}$", + resolve.name.to_symbol() + ); let local_prefix = format_ident!("{}__const__ref__option_", elem); (elem, key, resolve, link_prefix, local_prefix) } OptionInner::MutRef(key) => { let elem = key.rust; - if types.try_resolve(key.rust).is_none() { + if types.try_resolve(key).is_none() { return TokenStream::new(); } let resolve = types.resolve(elem); - let link_prefix = format!("cxxbridge1$rust_option${}$", resolve.name.to_symbol()); + let link_prefix = format!("cxxbridge1$rust_option$ref${}$", resolve.name.to_symbol()); let local_prefix = format_ident!("{}__ref__option_", elem); (elem, key, resolve, link_prefix, local_prefix) } OptionInner::RefVec(key) => { let elem = key.rust; - if types.try_resolve(key.rust).is_none() { + if types.try_resolve(key).is_none() { return TokenStream::new(); } let resolve = types.resolve(elem); let link_prefix = format!( - "cxxbridge1$rust_option$const$Vec${}$", + "cxxbridge1$rust_option$const$ref$rust_vec${}$", resolve.name.to_symbol() ); let local_prefix = format_ident!("{}__vec__const__ref__option_", elem); @@ -1564,14 +1591,40 @@ fn expand_rust_option( } OptionInner::MutRefVec(key) => { let elem = key.rust; - if types.try_resolve(key.rust).is_none() { + if types.try_resolve(key).is_none() { return TokenStream::new(); } let resolve = types.resolve(elem); - let link_prefix = format!("cxxbridge1$rust_option$Vec${}$", resolve.name.to_symbol()); + let link_prefix = format!( + "cxxbridge1$rust_option$ref$rust_vec${}$", + resolve.name.to_symbol() + ); let local_prefix = format_ident!("{}__vec__ref__option_", elem); (elem, key, resolve, link_prefix, local_prefix) } + OptionInner::Vec(key) => { + let elem = key.rust; + if types.try_resolve(key).is_none() { + return TokenStream::new(); + } + let resolve = types.resolve(elem); + let link_prefix = format!( + "cxxbridge1$rust_option$rust_vec${}$", + resolve.name.to_symbol() + ); + let local_prefix = format_ident!("{}__vec__option_", elem); + (elem, key, resolve, link_prefix, local_prefix) + } + OptionInner::Ident(key) => { + let elem = key.rust; + if types.try_resolve(key).is_none() { + return TokenStream::new(); + } + let resolve = types.resolve(elem); + let link_prefix = format!("cxxbridge1$rust_option${}$", resolve.name.to_symbol()); + let local_prefix = format_ident!("{}__option_", elem); + (elem, key, resolve, link_prefix, local_prefix) + } }; let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -1593,8 +1646,9 @@ fn expand_rust_option( let ident = key.rust; let (impl_generics, ty_generics) = generics::split_for_impl(*key, explicit_impl, resolve); - let (ty, trait_impl_ty, ty_ptr, const_ty_ptr, prevent_unwind_drop_label) = match inner { + let (ffi_ty, ty, trait_impl_ty, ty_ptr, const_ty_ptr, prevent_unwind_drop_label) = match inner { OptionInner::RustBox(_) => ( + quote! { ::cxx::alloc::boxed::Box<#elem #ty_generics> }, quote! { ::cxx::alloc::boxed::Box<#elem #ty_generics> }, quote! { ::cxx::alloc::boxed::Box<#elem #ty_generics> }, quote! { *mut #elem #ty_generics }, @@ -1602,6 +1656,7 @@ fn expand_rust_option( format!("::alloc::boxed::Box<::{}> as Drop>::drop", ident), ), OptionInner::Ref(_) => ( + quote! { &#elem #ty_generics }, quote! { &#elem #ty_generics }, quote! { &#elem #ty_generics }, quote! { *const #elem #ty_generics }, @@ -1609,6 +1664,7 @@ fn expand_rust_option( format!("&::{}> as Drop>::drop", ident), ), OptionInner::MutRef(_) => ( + quote! { &mut #elem #ty_generics }, quote! { &mut #elem #ty_generics }, quote! { &mut #elem #ty_generics }, quote! { *mut #elem #ty_generics }, @@ -1624,18 +1680,30 @@ fn expand_rust_option( // but using the actual `Vec` here at least gives some idea of what's going on // if this shows up in an error message. quote! { &::cxx::private::RustVec<#elem #ty_generics> }, + quote! { &::cxx::private::RustVec<#elem #ty_generics> }, quote! { &#elem #ty_generics }, quote! { *const ::cxx::private::RustVec<#elem #ty_generics> }, quote! { *const ::cxx::private::RustVec<#elem #ty_generics> }, format!("&::alloc::vec::Vec<::{}> as Drop>::drop", ident), ), OptionInner::MutRefVec(_) => ( + quote! { &mut ::cxx::private::RustVec<#elem #ty_generics> }, quote! { &mut ::cxx::private::RustVec<#elem #ty_generics> }, quote! { &mut #elem #ty_generics }, quote! { *mut ::cxx::private::RustVec<#elem #ty_generics> }, quote! { *const ::cxx::private::RustVec<#elem #ty_generics> }, format!("&mut ::alloc::vec::Vec<::{}> as Drop>::drop", ident), ), + OptionInner::Vec(_) => ( + quote! { ::cxx::alloc::vec::Vec<#elem #ty_generics> }, + quote! { ::cxx::private::RustVec<#elem #ty_generics> }, + quote! { #elem #ty_generics }, + quote! { *mut ::cxx::private::RustVec<#elem #ty_generics> }, + quote! { *const ::cxx::private::RustVec<#elem #ty_generics> }, + format!("::alloc::vec::Vec<::{}> as Drop>::drop", ident), + ), + // These are all handled in cxx.rs because we do not allow user defined types here + OptionInner::Ident(_) => unreachable!(), }; let set_value_impl = match inner { OptionInner::RustBox(_) => quote! { @@ -1710,25 +1778,53 @@ fn expand_rust_option( *option.unwrap() as _ } }, + OptionInner::Vec(_) => quote! { + #[doc(hidden)] + #[export_name = #link_set] + unsafe extern "C" fn #local_set #impl_generics(this: *mut <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target, value: #ty_ptr) { + use ::cxx::private::OptionFfiInverse; + unsafe { this.as_mut().unwrap().set(::cxx::core::mem::take((*value).as_mut_vec())) }; + } + #[doc(hidden)] + #[export_name = #link_value_const] + unsafe extern "C" fn #local_value_const #impl_generics(this: *const <::core::option::Option<#ffi_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: &#ffi_ty = unsafe { this.as_option().as_ref().unwrap() }; + v as *const #ffi_ty as #const_ty_ptr + } + #[doc(hidden)] + #[export_name = #link_value] + unsafe extern "C" fn #local_value #impl_generics(this: *mut <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target) -> #ty_ptr { + use ::cxx::private::OptionFfiInverse; + let this: &mut <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target = unsafe { this.as_mut().unwrap() }; + ::cxx::core::debug_assert!(this.has_value()); + let option: &mut ::core::option::Option<#ffi_ty> = this.as_mut_option(); + let option: ::core::option::Option<&mut #ffi_ty> = option.as_mut(); + option.unwrap() as *mut #ffi_ty as #ty_ptr + } + }, + OptionInner::Ident(_) => unreachable!(), }; quote_spanned! {end_span=> // #[doc(hidden)] #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 <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) { + unsafe extern "C" fn #local_new #impl_generics(this: *mut <::core::option::Option<#ffi_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()) }; + unsafe { ::cxx::core::ptr::write(this, <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target::new()) }; } #[doc(hidden)] #[export_name = #link_drop] - unsafe extern "C" fn #local_drop #impl_generics(this: *mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) { + unsafe extern "C" fn #local_drop #impl_generics(this: *mut <::core::option::Option<#ffi_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 <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) -> bool { + unsafe extern "C" fn #local_has_value #impl_generics(this: *const <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target) -> bool { unsafe { this.as_ref().unwrap().has_value() } } #set_value_impl diff --git a/src/cxx.cc b/src/cxx.cc index d2246fce6..cc69faf37 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -789,164 +789,296 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), MACRO(isize, rust::isize) \ MACRO(string, std::string) -#define RUST_OPTION_EXTERNS(RUST_TYPE, CXX_TYPE) \ - void cxxbridge1$rust_option$const$##RUST_TYPE##$new( \ +#define RUST_OPTION_EXTERNS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge1$rust_option$const$ref$##RUST_TYPE##$new( \ rust::Option *ptr) noexcept; \ - void cxxbridge1$rust_option$const$##RUST_TYPE##$drop( \ + void cxxbridge1$rust_option$const$ref$##RUST_TYPE##$drop( \ rust::Option *ptr) noexcept; \ - bool cxxbridge1$rust_option$const$##RUST_TYPE##$has_value( \ + bool cxxbridge1$rust_option$const$ref$##RUST_TYPE##$has_value( \ const rust::Option *ptr) noexcept; \ - CXX_TYPE const * cxxbridge1$rust_option$const$##RUST_TYPE##$value( \ + CXX_TYPE const * cxxbridge1$rust_option$const$ref$##RUST_TYPE##$value( \ rust::Option const *ptr) noexcept; \ - void cxxbridge1$rust_option$const$##RUST_TYPE##$set( \ + void cxxbridge1$rust_option$const$ref$##RUST_TYPE##$set( \ rust::Option *ptr, CXX_TYPE const * value) noexcept; \ - void cxxbridge1$rust_option$##RUST_TYPE##$new( \ + void cxxbridge1$rust_option$ref$##RUST_TYPE##$new( \ rust::Option *ptr) noexcept; \ - void cxxbridge1$rust_option$##RUST_TYPE##$drop( \ + void cxxbridge1$rust_option$ref$##RUST_TYPE##$drop( \ rust::Option *ptr) noexcept; \ - bool cxxbridge1$rust_option$##RUST_TYPE##$has_value( \ + bool cxxbridge1$rust_option$ref$##RUST_TYPE##$has_value( \ const rust::Option *ptr) noexcept; \ - CXX_TYPE* cxxbridge1$rust_option$##RUST_TYPE##$value_const( \ + CXX_TYPE const * cxxbridge1$rust_option$ref$##RUST_TYPE##$value_const( \ rust::Option const *ptr) noexcept; \ - CXX_TYPE* cxxbridge1$rust_option$##RUST_TYPE##$value( \ + CXX_TYPE* cxxbridge1$rust_option$ref$##RUST_TYPE##$value( \ rust::Option *ptr) noexcept; \ - void cxxbridge1$rust_option$##RUST_TYPE##$set( \ + void cxxbridge1$rust_option$ref$##RUST_TYPE##$set( \ rust::Option *ptr, CXX_TYPE * value) noexcept; \ /* Vec implementation */ \ - void cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$new( \ + void cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$new( \ rust::Option const &> *ptr) noexcept; \ - void cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$drop( \ + void cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$drop( \ rust::Option const &> *ptr) noexcept; \ - bool cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$has_value( \ + bool cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$has_value( \ const rust::Option const &> *ptr) noexcept; \ - rust::Vec const * cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$value( \ + rust::Vec const * cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$value( \ rust::Option const &> const *ptr) noexcept; \ - void cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$set( \ + void cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$set( \ rust::Option const &> *ptr, rust::Vec const * value) noexcept; \ - void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$new( \ + void cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$new( \ rust::Option &> *ptr) noexcept; \ - void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$drop( \ + void cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$drop( \ rust::Option &> *ptr) noexcept; \ - bool cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$has_value( \ + bool cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$has_value( \ const rust::Option &> *ptr) noexcept; \ - rust::Vec* cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value_const( \ + rust::Vec const * cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$value_const( \ rust::Option &> const *ptr) noexcept; \ - rust::Vec* cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value( \ + rust::Vec* cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$value( \ rust::Option &> *ptr) noexcept; \ + void cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$set( \ + rust::Option &> *ptr, rust::Vec * value) noexcept; \ + void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$new( \ + rust::Option> *ptr) noexcept; \ + void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$drop( \ + rust::Option> *ptr) noexcept; \ + bool cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$has_value( \ + const rust::Option> *ptr) noexcept; \ + rust::Vec const * cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value_const( \ + rust::Option> const *ptr) noexcept; \ + rust::Vec * cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value( \ + rust::Option> *ptr) noexcept; \ void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$set( \ - rust::Option &> *ptr, rust::Vec * value) noexcept; + rust::Option> *ptr, rust::Vec *value) noexcept; + +#define RUST_OPTION_DIRECT_EXTERNS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge1$rust_option$##RUST_TYPE##$new( \ + rust::Option *ptr) noexcept; \ + void cxxbridge1$rust_option$##RUST_TYPE##$drop( \ + rust::Option *ptr) noexcept; \ + bool cxxbridge1$rust_option$##RUST_TYPE##$has_value( \ + const rust::Option *ptr) noexcept; \ + CXX_TYPE const * cxxbridge1$rust_option$##RUST_TYPE##$value_const( \ + rust::Option const *ptr) noexcept; \ + CXX_TYPE* cxxbridge1$rust_option$##RUST_TYPE##$value( \ + rust::Option *ptr) noexcept; \ + void cxxbridge1$rust_option$##RUST_TYPE##$set( \ + rust::Option *ptr, CXX_TYPE *value) noexcept; #define RUST_OPTION_OPS(RUST_TYPE, CXX_TYPE) \ template <> \ Option::Option() noexcept { \ - cxxbridge1$rust_option$const$##RUST_TYPE##$new(this); \ + cxxbridge1$rust_option$const$ref$##RUST_TYPE##$new(this); \ } \ template <> \ void Option::drop() noexcept { \ - cxxbridge1$rust_option$const$##RUST_TYPE##$drop(this); \ + cxxbridge1$rust_option$const$ref$##RUST_TYPE##$drop(this); \ } \ template <> \ bool Option::has_value() const noexcept { \ - return cxxbridge1$rust_option$const$##RUST_TYPE##$has_value(this); \ + return cxxbridge1$rust_option$const$ref$##RUST_TYPE##$has_value(this); \ } \ template <> \ CXX_TYPE const & Option::value() const noexcept { \ - return *cxxbridge1$rust_option$const$##RUST_TYPE##$value(this); \ + return *cxxbridge1$rust_option$const$ref$##RUST_TYPE##$value(this); \ } \ template <> \ void Option::set(CXX_TYPE const & value) noexcept { \ - return cxxbridge1$rust_option$const$##RUST_TYPE##$set( \ + return cxxbridge1$rust_option$const$ref$##RUST_TYPE##$set( \ this, &value); \ } \ template <> \ Option::Option() noexcept { \ - cxxbridge1$rust_option$##RUST_TYPE##$new(this); \ + cxxbridge1$rust_option$ref$##RUST_TYPE##$new(this); \ } \ template <> \ void Option::drop() noexcept { \ - cxxbridge1$rust_option$##RUST_TYPE##$drop(this); \ + cxxbridge1$rust_option$ref$##RUST_TYPE##$drop(this); \ } \ template <> \ bool Option::has_value() const noexcept { \ - return cxxbridge1$rust_option$##RUST_TYPE##$has_value(this); \ + return cxxbridge1$rust_option$ref$##RUST_TYPE##$has_value(this); \ } \ template <> \ - const CXX_TYPE& Option::value() const noexcept { \ - return *cxxbridge1$rust_option$##RUST_TYPE##$value_const(this); \ + const CXX_TYPE& Option::value() const noexcept { \ + return *cxxbridge1$rust_option$ref$##RUST_TYPE##$value_const(this); \ } \ template <> \ - CXX_TYPE& Option::value() noexcept { \ - return *cxxbridge1$rust_option$##RUST_TYPE##$value(this); \ + CXX_TYPE& Option::value() noexcept { \ + return *cxxbridge1$rust_option$ref$##RUST_TYPE##$value(this); \ } \ template <> \ void Option::set(CXX_TYPE & value) noexcept { \ - return cxxbridge1$rust_option$##RUST_TYPE##$set(this, &value); \ + return cxxbridge1$rust_option$ref$##RUST_TYPE##$set(this, &value); \ } \ /* Vec impl */ \ template <> \ Option const &>::Option() noexcept { \ - cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$new(this); \ + cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$new(this); \ } \ template <> \ void Option const &>::drop() noexcept { \ - cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$drop(this); \ + cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$drop(this); \ } \ template <> \ bool Option const &>::has_value() const noexcept { \ - return cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$has_value(this); \ + return cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$has_value(this); \ } \ template <> \ rust::Vec const & Option const &>::value() const noexcept { \ - return *cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$value(this); \ + return *cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$value(this); \ } \ template <> \ void Option const &>::set(rust::Vec const & value) noexcept { \ - return cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$set( \ + return cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$set( \ this, &value); \ } \ template <> \ Option &>::Option() noexcept { \ - cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$new(this); \ + cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$new(this); \ } \ template <> \ void Option &>::drop() noexcept { \ - cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$drop(this); \ + cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$drop(this); \ } \ template <> \ bool Option &>::has_value() const noexcept { \ + return cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$has_value(this); \ + } \ + template <> \ + const rust::Vec& Option &>::value() const noexcept { \ + return *cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$value_const(this); \ + } \ + template <> \ + rust::Vec& Option &>::value() noexcept { \ + return *cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$value(this); \ + } \ + template <> \ + void Option &>::set(rust::Vec & value) noexcept { \ + return cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$set(this, &value); \ + } \ + template <> \ + Option>::Option() noexcept { \ + cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$new(this); \ + } \ + template <> \ + void Option>::drop() noexcept { \ + cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$drop(this); \ + } \ + template <> \ + bool Option>::has_value() const noexcept { \ return cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$has_value(this); \ } \ template <> \ - const rust::Vec& Option &>::value() const noexcept { \ + const rust::Vec& Option>::value() const noexcept { \ return *cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value_const(this); \ } \ template <> \ - rust::Vec& Option &>::value() noexcept { \ + rust::Vec& Option>::value() noexcept { \ return *cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value(this); \ } \ template <> \ - void Option &>::set(rust::Vec & value) noexcept { \ + void Option>::set(rust::Vec value) noexcept { \ return cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$set(this, &value); \ } +#define RUST_OPTION_DIRECT_OPS(RUST_TYPE, CXX_TYPE) \ + Option::Option() noexcept { \ + cxxbridge1$rust_option$##RUST_TYPE##$new(this); \ + } \ + void Option::drop() noexcept { \ + cxxbridge1$rust_option$##RUST_TYPE##$drop(this); \ + } \ + bool Option::has_value() const noexcept { \ + return cxxbridge1$rust_option$##RUST_TYPE##$has_value(this); \ + } \ + const CXX_TYPE& Option::value() const noexcept { \ + return *cxxbridge1$rust_option$##RUST_TYPE##$value_const(this); \ + } \ + CXX_TYPE& Option::value() noexcept { \ + return *cxxbridge1$rust_option$##RUST_TYPE##$value(this); \ + } \ + void Option::set(CXX_TYPE value) noexcept { \ + return cxxbridge1$rust_option$##RUST_TYPE##$set(this, &value); \ + } + +#define RUST_OPTION_DIRECT_IMPL(CXX_TYPE) \ +Option::Option(Option&& other) noexcept { \ + new (this) Option(); \ + if (other.has_value()) { \ + set(std::move(other.value())); \ + } \ + new (&other) Option(); \ +} \ +Option::Option(String&& value) noexcept { \ + new (this) Option(); \ + set(std::move(value)); \ +} \ +Option::~Option() noexcept { \ + this->drop(); \ +} \ +Option& Option::operator=(Option&& other) noexcept { \ + this->reset(); \ + if (other.has_value()) { \ + set(std::move(other.value())); \ + } \ + new (&other) Option(); \ + return *this; \ +} \ +const CXX_TYPE& Option::operator*() const noexcept { \ + return value(); \ +} \ +const CXX_TYPE* Option::operator->() const noexcept { \ + return &value(); \ +} \ +CXX_TYPE& Option::operator*() noexcept { \ + return value(); \ +} \ +CXX_TYPE* Option::operator->() noexcept { \ + return &value(); \ +} \ +bool Option::operator==(const Option& other) const noexcept { \ + if (this->has_value() && other.has_value()) { \ + return this->value() == other.value(); \ + } \ + return this->has_value() == other.has_value(); \ +} \ +bool Option::operator!=(const Option& other) const noexcept { \ + if (this->has_value() && other.has_value()) { \ + return this->value() != other.value(); \ + } \ + return this->has_value() != other.has_value(); \ +} \ +void Option::reset() { \ + this->drop(); \ + new (this) Option(); \ +} \ +Option::Option(unsafe_bitcopy_t, const Option &bits) noexcept : repr(bits.repr) {} + #define FOR_EACH_RUST_OPTION(MACRO) \ FOR_EACH_NUMERIC(MACRO) \ MACRO(bool, bool) \ MACRO(char, char) \ MACRO(string, rust::String) +#define FOR_EACH_RUST_OPTION_DIRECT(MACRO) \ + MACRO(string, rust::String) + +#define FOR_EACH_RUST_OPTION_DIRECT_IMPL(MACRO) \ + MACRO(rust::String) + extern "C" { FOR_EACH_STD_VECTOR(STD_VECTOR_OPS) FOR_EACH_TRIVIAL_STD_VECTOR(STD_VECTOR_TRIVIAL_OPS) FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS) FOR_EACH_SHARED_PTR(SHARED_PTR_OPS) FOR_EACH_RUST_OPTION(RUST_OPTION_EXTERNS) +FOR_EACH_RUST_OPTION_DIRECT(RUST_OPTION_DIRECT_EXTERNS) } // extern "C" namespace rust { inline namespace cxxbridge1 { FOR_EACH_RUST_VEC(RUST_VEC_OPS) FOR_EACH_RUST_OPTION(RUST_OPTION_OPS) +FOR_EACH_RUST_OPTION_DIRECT(RUST_OPTION_DIRECT_OPS) +FOR_EACH_RUST_OPTION_DIRECT_IMPL(RUST_OPTION_DIRECT_IMPL) } // namespace cxxbridge1 } // namespace rust diff --git a/src/rust_option.rs b/src/rust_option.rs index 76cbead01..f8fba1b4a 100644 --- a/src/rust_option.rs +++ b/src/rust_option.rs @@ -20,6 +20,9 @@ mod private { pub trait Sealed {} } pub trait OptionPtrTarget: private::Sealed {} +pub trait OptionRepr: private::Sealed { + type Repr; +} impl private::Sealed for &T {} impl OptionPtrTarget for &T {} @@ -35,6 +38,19 @@ impl private::Sealed for Box {} #[cfg(feature = "alloc")] impl OptionPtrTarget for Box {} +pub const fn assert_option_safe() { + struct __SizeCheck(core::marker::PhantomData<(U, V, W)>); + impl __SizeCheck { + const _IS_OPTION_SIZE: () = assert!(mem::size_of::() == mem::size_of::()); + const _IS_REPR_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_REPR_ALIGN; + let _: () = __SizeCheck::::_IS_OPTION_ALIGN; +} + pub trait OptionFfi: private::Sealed { // The FFI type that this Rust type is mapped to type Target; @@ -89,93 +105,90 @@ pub trait OptionFfiInverse: private::Sealed + Sized { /// 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) + ($name:ident<$generic:ident: $bound:path>, $repr:ty, $sizing:ty) => { + impl_option_ffi!(_private: name=$name, 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) + ($name:ident<$generic:ident>, $t:ty, $repr:ty, $sizing:ty) => { + impl_option_ffi!(_private: name=$name, 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) + ($name:ident<$t:ident>, $repr:ty, $sizing:ty) => { + impl_option_ffi!(_private: name=$name, 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>()]; - + (_private: name=$name:ident, generics=<$($generic1:ident),*>, bounded_generics=<$($generic2:ident: $bound:path),*>, option_ty=$option_ty:ty, repr=$repr:ty, sizing=$sizing:ty) => { // ABI compatible with C++ rust::Option for (not necessarily core::option::Option). - pub struct RustOption<$($generic1),* $($generic2: $bound),*> { + pub struct $name<$($generic1),* $($generic2: $bound),*> { #[allow(dead_code)] - repr: Repr, + repr: [mem::MaybeUninit<$repr>; mem::size_of::>() / mem::size_of::<$repr>()], phantom: core::marker::PhantomData>, } - 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::()); + impl<$($generic1),* $($generic2: $bound),*> private::Sealed for $name<$($generic1),* $($generic2),*> {} + + impl<$($generic1),* $($generic2: $bound),*> OptionRepr for $name<$($generic1),* $($generic2),*> { + type Repr = [mem::MaybeUninit<$repr>; mem::size_of::>() / mem::size_of::<$repr>()]; + } + + impl<$($generic1),* $($generic2: $bound),*> $name<$($generic1),* $($generic2),*> { + pub fn has_value(&self) -> bool { + self.as_option().is_some() + } + + pub fn set(&mut self, value: $option_ty) { + self.as_mut_option().replace(value); } - // Force the constants to resolve (at compile time) - let _: () = __SizeCheck::::_IS_OPTION_SIZE; - let _: () = __SizeCheck::::_IS_REPR_ALIGN; - let _: () = __SizeCheck::::_IS_OPTION_ALIGN; } + impl<$($generic1),* $($generic2: $bound),*> private::Sealed for Option<$option_ty> {} impl<$($generic1),* $($generic2: $bound),*> OptionFfi for Option<$option_ty> { - type Target = RustOption<$($generic1),* $($generic2),*>; + type Target = $name<$($generic1),* $($generic2),*>; fn new_ffi() -> Self::Target { Self::None.into_ffi() } fn into_ffi(self) -> Self::Target { - let _: () = assert_option_safe::<$option_ty>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); 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>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); unsafe { &*(self as *const Self as *const Self::Target) } } fn as_mut_ffi(&mut self) -> &mut Self::Target { - let _: () = assert_option_safe::<$option_ty>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); unsafe { &mut *(self as *mut Self as *mut Self::Target) } } fn from_ffi(mut other: Self::Target) -> Self { - let _: () = assert_option_safe::<$option_ty>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); Self::from_ffi_mut(&mut other).take() } fn from_ffi_ref(other: &Self::Target) -> &Self { - let _: () = assert_option_safe::<$option_ty>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); 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>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); unsafe { &mut *(other as *mut Self::Target as *mut Self) } } } - impl<$($generic1),* $($generic2: $bound),*> private::Sealed for RustOption<$($generic1),* $($generic2),*> {} - - impl<$($generic1),* $($generic2: $bound),*> OptionFfiInverse for RustOption<$($generic1),* $($generic2),*> { + impl<$($generic1),* $($generic2: $bound),*> OptionFfiInverse for $name<$($generic1),* $($generic2),*> { type Target = Option<$option_ty>; } - impl<$($generic1),* $($generic2: $bound),*> Drop for RustOption<$($generic1),* $($generic2),*> { + impl<$($generic1),* $($generic2: $bound),*> Drop for $name<$($generic1),* $($generic2),*> { fn drop(&mut self) { self.as_mut_option().take(); } @@ -185,21 +198,9 @@ macro_rules! impl_option_ffi { // Pointer-sized pointer types with niche optimization const _: () = { - impl_option_ffi! { , usize, &'static () } + impl_option_ffi! { RustOption, usize, &'static () } impl RustOption { - pub fn value(&self) -> Option<&T> { - self.as_option().as_ref() - } - - pub fn has_value(&self) -> bool { - self.as_option().is_some() - } - - pub fn set(&mut self, value: T) { - self.as_mut_option().replace(value); - } - pub unsafe fn as_ref_mut_inner_unchecked(&mut self) -> &mut T { unsafe { self.as_mut_option().as_mut().unwrap_unchecked() } } @@ -489,8 +490,10 @@ const _: () = { this: &'b mut RustOption<&'a mut RustVec>, ) -> &'b mut Option<&'a mut Vec> { unsafe { - &mut *(this as *mut RustOption<&mut RustVec> as *mut RustOption<&mut Vec>) - }.as_mut_option() + &mut *(this as *mut RustOption<&mut RustVec> + as *mut RustOption<&mut Vec>) + } + .as_mut_option() } pub fn into_rust_option_rust_vec_rust_string_mut( @@ -558,7 +561,8 @@ const _: () = { ) -> &'b mut Option<&'a mut String> { unsafe { &mut *(this as *mut RustOption<&mut RustString> as *mut RustOption<&mut String>) - }.as_mut_option() + } + .as_mut_option() } pub fn into_rust_option_rust_string_mut(self) -> RustOption<&'a mut RustString> { @@ -568,3 +572,14 @@ const _: () = { } } }; + +// Growable containers (Vec, String), 3 pointer size with niche optimization +#[cfg(feature = "alloc")] +const _: () = { + impl_option_ffi! { RustOptionVec, Vec, usize, Vec<()> } +}; + +#[cfg(feature = "alloc")] +const _: () = { + impl_option_ffi! { RustOptionString, usize, String } +}; diff --git a/src/symbols/rust_option.rs b/src/symbols/rust_option.rs index 839280632..34e06d127 100644 --- a/src/symbols/rust_option.rs +++ b/src/symbols/rust_option.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case)] + use crate::rust_option::OptionFfi; use crate::rust_option::OptionFfiInverse; #[cfg(feature = "alloc")] @@ -17,55 +19,55 @@ macro_rules! rust_option_shims { const_assert_eq!(mem::size_of::>(), mem::size_of::()); const _: () = { - #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$new")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$", $segment, "$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")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$", $segment, "$drop")] 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")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$", $segment, "$has_value")] 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")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$", $segment, "$value")] 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")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$", $segment, "$set")] unsafe extern "C" fn __const_set( this: *mut as OptionFfi>::Target, value: *mut $ty, ) { unsafe { this.as_mut().unwrap().set(&*value) } } - #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$new")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$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")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$drop")] 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")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$has_value")] 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")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$value_const")] 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")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$value")] 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")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$set")] unsafe extern "C" fn __set( this: *mut as OptionFfi>::Target, value: *mut $ty, @@ -76,61 +78,90 @@ macro_rules! rust_option_shims { #[cfg(feature = "alloc")] const _: () = { /* Vec impl */ - #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$new")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$rust_vec$", $segment, "$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")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$rust_vec$", $segment, "$drop")] 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")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$rust_vec$", $segment, "$has_value")] 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")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$rust_vec$", $segment, "$value")] 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")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$rust_vec$", $segment, "$set")] unsafe extern "C" fn __const_set( this: *mut > as OptionFfi>::Target, value: *mut RustVec<$ty>, ) { 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 > as OptionFfi>::Target) { + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$new")] + unsafe extern "C" fn __mut_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 > as OptionFfi>::Target) { + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$drop")] + unsafe extern "C" fn __mut__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 > as OptionFfi>::Target) -> bool { + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$has_value")] + unsafe extern "C" fn __mut__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 > as OptionFfi>::Target) -> *const RustVec<$ty> { + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$value_const")] + unsafe extern "C" fn __mut__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 > as OptionFfi>::Target) -> *mut RustVec<$ty> { + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$value")] + unsafe extern "C" fn __mut__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( + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$set")] + unsafe extern "C" fn __mut__set( this: *mut > as OptionFfi>::Target, value: *mut RustVec<$ty>, ) { unsafe { > as OptionFfi>::Target::as_option_vec_mut_mut(this.as_mut().unwrap()).replace((&mut *value).as_mut_vec()); } } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$new")] + unsafe extern "C" fn __new(this: *mut > as OptionFfi>::Target) { + unsafe { ptr::write(this, Option::>::new_ffi()) } + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$drop")] + 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 > as OptionFfi>::Target) -> bool { + let o: &> as OptionFfi>::Target = unsafe { this.as_ref().unwrap() }; + > as OptionFfi>::Target::as_option(o).is_some() + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$value_const")] + 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(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 > as OptionFfi>::Target) -> *mut RustVec<$ty> { + let ptr = unsafe { > as OptionFfi>::Target::as_mut_option(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 > as OptionFfi>::Target, + value: *mut RustVec<$ty>, + ) { + unsafe { > as OptionFfi>::Target::as_mut_option(this.as_mut().unwrap()).replace(core::mem::take(value.as_mut().unwrap().as_mut_vec())); } + } }; }; } @@ -158,3 +189,57 @@ rust_option_shims_for_primitive!(f64); rust_option_shims!("char", c_char); #[cfg(feature = "alloc")] rust_option_shims!("string", RustString); + +// The String bindings are special cased because of the need to convert +// between String and RustString +#[cfg(feature = "alloc")] +const _: () = { + const_assert_eq!( + mem::size_of::>(), + mem::size_of::< as OptionFfi>::Target>() + ); + + #[export_name = "cxxbridge1$rust_option$string$new"] + unsafe extern "C" fn __new(this: *mut as OptionFfi>::Target) { + unsafe { ptr::write(this, Option::::new_ffi()) } + } + #[export_name = "cxxbridge1$rust_option$string$drop"] + unsafe extern "C" fn __drop(this: *mut as OptionFfi>::Target) { + unsafe { ptr::drop_in_place(this) } + } + #[export_name = "cxxbridge1$rust_option$string$has_value"] + 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 = "cxxbridge1$rust_option$string$value_const"] + unsafe extern "C" fn __value_const( + this: *const as OptionFfi>::Target, + ) -> *const RustString { + let v: &alloc::string::String = + unsafe { this.as_ref().unwrap().as_option().as_ref().unwrap() }; + v as *const alloc::string::String as *const RustString + } + #[export_name = "cxxbridge1$rust_option$string$value"] + unsafe extern "C" fn __value( + this: *mut as OptionFfi>::Target, + ) -> *mut alloc::string::String { + let this = unsafe { this.as_mut().unwrap() }; + let ptr = this.as_mut_option().as_mut().unwrap(); + ptr as _ + } + #[export_name = "cxxbridge1$rust_option$string$set"] + unsafe extern "C" fn __set( + this: *mut as OptionFfi>::Target, + value: *mut RustString, + ) { + unsafe { + this.as_mut() + .unwrap() + .set(core::mem::take(value.as_mut().unwrap().as_mut_string())); + } + } +}; diff --git a/syntax/check.rs b/syntax/check.rs index 47bf85176..baf287e98 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -146,6 +146,12 @@ fn check_type_rust_option(cx: &mut Check, ty: &Ty1) { match &ty.inner { Type::RustBox(_) => return, Type::Ref(_) => return, + Type::RustVec(_) => return, + Type::Ident(ident) => { + if let Some(RustString) = Atom::from(&ident.rust) { + return; + } + } _ => {} } diff --git a/syntax/instantiate.rs b/syntax/instantiate.rs index 183547e3d..346d7b936 100644 --- a/syntax/instantiate.rs +++ b/syntax/instantiate.rs @@ -21,6 +21,8 @@ pub(crate) enum OptionInner<'a> { MutRef(NamedImplKey<'a>), RefVec(NamedImplKey<'a>), MutRefVec(NamedImplKey<'a>), + Vec(NamedImplKey<'a>), + Ident(NamedImplKey<'a>), } #[derive(Copy, Clone)] @@ -80,6 +82,20 @@ impl Type { } _ => {} }, + Type::RustVec(_) => { + let impl_key = ty.inner.impl_key()?; + match impl_key { + ImplKey::RustVec(named) => { + return Some(ImplKey::RustOption(OptionInner::Vec(named))) + } + _ => unreachable!(), + } + } + Type::Ident(ident) => { + return Some(ImplKey::RustOption(OptionInner::Ident(NamedImplKey::new( + ty, ident, + )))) + } _ => {} } } else if let Type::UniquePtr(ty) = self { diff --git a/syntax/types.rs b/syntax/types.rs index efa8d1574..e02750538 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -243,7 +243,8 @@ impl<'a> Types<'a> { pub(crate) fn needs_indirect_abi(&self, ty: &Type) -> bool { match ty { - Type::RustBox(_) | Type::UniquePtr(_) | Type::RustOption(_) => false, + Type::RustBox(_) | Type::UniquePtr(_) => false, + Type::RustOption(inner) => self.needs_indirect_abi(&inner.inner), Type::Array(_) => true, _ => !self.is_guaranteed_pod(ty), } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index f63b5164e..92cb2ca3a 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -140,6 +140,10 @@ pub mod ffi { fn c_return_rust_mut_option_vec_string(v: &mut Vec) -> Option<&mut Vec>; fn c_return_rust_ref_option_string(s: &String) -> Option<&String>; fn c_return_rust_mut_option_string(s: &mut String) -> Option<&mut String>; + fn c_return_rust_option_vec_native() -> Option>; + fn c_return_rust_option_vec_shared() -> Option>; + fn c_return_rust_option_vec_string() -> Option>; + fn c_return_rust_option_string() -> Option; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; fn c_return_enum(n: u16) -> Enum; @@ -205,6 +209,10 @@ pub mod ffi { fn c_take_rust_mut_option_vec_string(rust: Option<&mut Vec>); fn c_take_rust_ref_option_string(rust: Option<&String>); fn c_take_rust_mut_option_string(rust: Option<&mut String>); + fn c_take_rust_option_vec_native(rust: Option>); + fn c_take_rust_option_vec_shared(rust: Option>); + fn c_take_rust_option_vec_string(rust: Option>); + fn c_take_rust_option_string(rust: Option); fn c_take_ref_shared_string(s: &SharedString) -> &SharedString; fn c_take_callback(callback: fn(String) -> usize); @@ -360,6 +368,10 @@ pub mod ffi { unsafe fn r_return_rust_option_pin_mut_native<'a>( native: Pin<&'a mut u8>, ) -> Option>; + fn r_return_rust_option_vec_native() -> Option>; + fn r_return_rust_option_vec_shared() -> Option>; + fn r_return_rust_option_vec_string() -> Option>; + fn r_return_rust_option_string() -> Option; fn r_return_identity(_: usize) -> usize; fn r_return_sum(_: usize, _: usize) -> usize; fn r_return_enum(n: u32) -> Enum; @@ -396,6 +408,10 @@ pub mod ffi { fn r_take_rust_option_mut_ref_vec(o: Option<&mut Vec>); fn r_take_rust_option_ref_vec_string(o: Option<&Vec>); fn r_take_rust_option_mut_ref_vec_string(o: Option<&mut Vec>); + fn r_take_rust_option_vec_native(o: Option>); + fn r_take_rust_option_vec_shared(o: Option>); + fn r_take_rust_option_vec_string(o: Option>); + fn r_take_rust_option_string(o: Option); fn r_take_enum(e: Enum); @@ -716,6 +732,22 @@ unsafe fn r_return_rust_option_pin_mut_native<'a>( Some(native) } +fn r_return_rust_option_vec_native() -> Option> { + Some(vec![20, 24]) +} + +fn r_return_rust_option_vec_shared() -> Option> { + Some(vec![ffi::Shared { z: 2024 }]) +} + +fn r_return_rust_option_vec_string() -> Option> { + Some(vec![String::from("2024")]) +} + +fn r_return_rust_option_string() -> Option { + Some(String::from("2024")) +} + fn r_return_identity(n: usize) -> usize { n } @@ -868,6 +900,34 @@ fn r_take_rust_option_mut_ref_vec_string(o: Option<&mut Vec>) { let _ = o; } +fn r_take_rust_option_vec_native(o: Option>) { + match o { + Some(v) => assert_eq!(&v, &[20, 24]), + None => unreachable!(), + } +} + +fn r_take_rust_option_vec_shared(o: Option>) { + match o { + Some(v) => assert_eq!(&v, &[ffi::Shared { z: 2024 }]), + None => unreachable!(), + } +} + +fn r_take_rust_option_vec_string(o: Option>) { + match o { + Some(v) => assert_eq!(&v, &["2024".to_string()]), + None => unreachable!(), + } +} + +fn r_take_rust_option_string(o: Option) { + match o { + Some(v) => assert_eq!(&v, &"2024"), + None => unreachable!(), + } +} + fn r_take_enum(e: ffi::Enum) { let _ = e; } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index e65e6d990..6d19305d6 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -767,6 +767,38 @@ void c_take_rust_mut_option_string(rust::Option opt) { } } +void c_take_rust_option_vec_native(rust::Option> opt) { + if (opt.has_value()) { + const auto& value = opt.value(); + if (value[0] == 20 && value[1] == 24) { + cxx_test_suite_set_correct(); + } + } +} + +void c_take_rust_option_vec_shared(rust::Option> opt) { + if (opt.has_value()) { + if (opt.value()[0].z == 2024) { + cxx_test_suite_set_correct(); + } + } +} + +void c_take_rust_option_vec_string(rust::Option> opt) { + if (opt.has_value()) { + const auto& value = opt.value(); + if (std::string(value[0]) == "2024") { + cxx_test_suite_set_correct(); + } + } +} + +void c_take_rust_option_string(rust::Option opt) { + if (opt.has_value() && std::string(opt.value()) == "2024") { + cxx_test_suite_set_correct(); + } +} + const SharedString &c_take_ref_shared_string(const SharedString &s) { if (std::string(s.msg) == "2020") { cxx_test_suite_set_correct(); @@ -890,6 +922,28 @@ rust::Option c_try_return_rust_mut_option_string() { throw std::runtime_error("unimplemented"); } +rust::Option> c_return_rust_option_vec_native() { + rust::Option> opt; + opt.set(rust::Vec{20, 24}); + return rust::Option>{std::move(opt)}; +} + +rust::Option> c_return_rust_option_vec_shared() { + rust::Option> opt; + opt.set(rust::Vec{Shared{2024}}); + return rust::Option>{std::move(opt)}; +} + +rust::Option> c_return_rust_option_vec_string() { + rust::Option> opt; + opt.set({rust::Vec{"2024"}}); + return rust::Option>{std::move(opt)}; +} + +rust::Option c_return_rust_option_string() { + return rust::Option{rust::String{"2024"}}; +} + const rust::String &c_try_return_ref(const rust::String &s) { return s; } rust::Str c_try_return_str(rust::Str s) { return s; } @@ -1113,6 +1167,13 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(r_return_enum(0) == Enum::AVal); ASSERT(r_return_enum(1) == Enum::BVal); ASSERT(r_return_enum(2021) == Enum::CVal); + auto option_vec_native = r_return_rust_option_vec_native(); + ASSERT(option_vec_native.has_value() && option_vec_native.value()[0] == 20 && option_vec_native.value()[1] == 24); + auto option_vec_shared = r_return_rust_option_vec_shared(); + ASSERT(option_vec_shared.has_value() && option_vec_shared.value()[0].z == 2024); + auto option_vec_string = r_return_rust_option_vec_string(); + ASSERT(option_vec_string.has_value() && option_vec_string.value()[0] == rust::String{"2024"}); + ASSERT(r_return_rust_option_string() == rust::Option{rust::String{"2024"}}); r_take_primitive(2020); r_take_shared(Shared{2020}); @@ -1130,6 +1191,10 @@ extern "C" const char *cxx_run_test() noexcept { empty_vector.reserve(10); r_take_ref_empty_vector(empty_vector); r_take_enum(Enum::AVal); + r_take_rust_option_vec_native(rust::Option>{rust::Vec{20, 24}}); + r_take_rust_option_vec_shared(rust::Option>{rust::Vec{Shared{2024}}}); + r_take_rust_option_vec_string(rust::Option>{rust::Vec{rust::String{"2024"}}}); + r_take_rust_option_string(rust::Option{rust::String{"2024"}}); ASSERT(r_try_return_primitive() == 2020); try { @@ -1241,6 +1306,9 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(sizeof(rust::Option>) == sizeof(void *)); ASSERT(sizeof(rust::Option) == sizeof(void *)); ASSERT(sizeof(rust::Option) == sizeof(void *)); + ASSERT(sizeof(rust::Option>) == sizeof(rust::Vec)); + ASSERT(sizeof(rust::Option>) == sizeof(rust::Vec)); + ASSERT(sizeof(rust::Option) == sizeof(rust::String)); cxx_test_suite_set_correct(); return nullptr; diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 8a6a76ed1..3d66148f6 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -133,6 +133,10 @@ rust::Option&> c_return_rust_ref_option_vec_string rust::Option&> c_return_rust_mut_option_vec_string(rust::Vec&); rust::Option c_return_rust_ref_option_string(const rust::String&); rust::Option c_return_rust_mut_option_string(rust::String&); +rust::Option> c_return_rust_option_vec_native(); +rust::Option> c_return_rust_option_vec_shared(); +rust::Option> c_return_rust_option_vec_string(); +rust::Option c_return_rust_option_string(); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); Enum c_return_enum(uint16_t n); @@ -204,6 +208,10 @@ void c_take_rust_ref_option_vec_string(rust::Option&>); void c_take_rust_ref_option_string(rust::Option); void c_take_rust_mut_option_string(rust::Option); +void c_take_rust_option_vec_native(rust::Option>); +void c_take_rust_option_vec_shared(rust::Option>); +void c_take_rust_option_vec_string(rust::Option>); +void c_take_rust_option_string(rust::Option); const SharedString &c_take_ref_shared_string(const SharedString &s); void c_take_callback(rust::Fn callback); void c_take_callback_ref(rust::Fn callback); diff --git a/tests/test.rs b/tests/test.rs index 7d3a69104..df88a4a69 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -38,6 +38,7 @@ macro_rules! check { } #[test] +#[allow(clippy::too_many_lines)] fn test_c_return() { let mut shared = ffi::Shared { z: 2020 }; let ns_shared = ffi::AShared { z: 2020 }; @@ -141,6 +142,16 @@ fn test_c_return() { Some(&mut "2020".to_string()), ffi::c_return_rust_mut_option_string(&mut "2020".to_string()) ); + assert_eq!(Some(vec![20, 24]), ffi::c_return_rust_option_vec_native(),); + assert_eq!( + Some(vec![ffi::Shared { z: 2024 }]), + ffi::c_return_rust_option_vec_shared(), + ); + assert_eq!( + Some(vec!["2024".to_string()]), + ffi::c_return_rust_option_vec_string() + ); + assert_eq!(Some("2024".to_string()), ffi::c_return_rust_option_string()); assert_eq!(2020, ffi::c_return_identity(2020)); assert_eq!(2021, ffi::c_return_sum(2020, 1)); match ffi::c_return_enum(0) { @@ -182,6 +193,7 @@ fn test_c_try_return() { } #[test] +#[allow(clippy::too_many_lines)] fn test_c_take() { let unique_ptr = ffi::c_return_unique_ptr(); let unique_ptr_ns = ffi2::c_return_ns_unique_ptr(); @@ -313,6 +325,14 @@ fn test_c_take() { check!(ffi::c_take_rust_mut_option_string(Some( &mut "2020".to_string() ))); + check!(ffi::c_take_rust_option_vec_native(Some(vec![20, 24]))); + check!(ffi::c_take_rust_option_vec_shared(Some(vec![ + ffi::Shared { z: 2024 } + ]))); + check!(ffi::c_take_rust_option_vec_string(Some(vec![ + "2024".to_string() + ]))); + check!(ffi::c_take_rust_option_string(Some("2024".to_string()))); check!(ffi::c_take_enum(ffi::Enum::AVal)); check!(ffi::c_take_ns_enum(ffi::AEnum::AAVal)); diff --git a/tests/ui/option_not_sized.stderr b/tests/ui/option_not_sized.stderr index 20bbdbedc..2b7155499 100644 --- a/tests/ui/option_not_sized.stderr +++ b/tests/ui/option_not_sized.stderr @@ -4,7 +4,10 @@ error[E0277]: the trait bound `&'static (dyn Debug + 'static): cxx::rust_option: 2 | const _: as ::cxx::private::OptionFfi>::Target = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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` + = help: the following other types implement trait `OptionFfi`: + Option + Option + 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` @@ -20,7 +23,10 @@ error[E0277]: the trait bound `&'static (dyn Debug + 'static): cxx::rust_option: 3 | as ::cxx::private::OptionFfi>::Target::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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` + = help: the following other types implement trait `OptionFfi`: + Option + Option + 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` @@ -30,6 +36,9 @@ error[E0277]: the trait bound `&dyn Debug: cxx::rust_option::OptionPtrTarget` is 3 | as ::cxx::private::OptionFfi>::Target::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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` + = help: the following other types implement trait `OptionFfi`: + Option + Option + 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_string.rs b/tests/ui/option_string.rs deleted file mode 100644 index 88ed98f62..000000000 --- a/tests/ui/option_string.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[cxx::bridge] -mod ffi { - extern "Rust" { - fn f() -> Option; - } -} - -fn f() -> Option { - unimplemented!() -} - -fn main() {} diff --git a/tests/ui/option_string.stderr b/tests/ui/option_string.stderr deleted file mode 100644 index 5245ee5c6..000000000 --- a/tests/ui/option_string.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unsupported element type of Option - --> tests/ui/option_string.rs:4:19 - | -4 | fn f() -> Option; - | ^^^^^^^^^^^^^^ diff --git a/tests/ui/option_vec.rs b/tests/ui/option_vec.rs deleted file mode 100644 index 20ba0dfaa..000000000 --- a/tests/ui/option_vec.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[cxx::bridge] -mod ffi { - extern "Rust" { - fn f() -> Option>; - } -} - -fn f() -> Option> { - unimplemented!() -} - -fn main() {} diff --git a/tests/ui/option_vec.stderr b/tests/ui/option_vec.stderr deleted file mode 100644 index 82c46eb83..000000000 --- a/tests/ui/option_vec.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unsupported element type of Option - --> tests/ui/option_vec.rs:4:19 - | -4 | fn f() -> Option>; - | ^^^^^^^^^^^^^^^