diff --git a/gen/src/write.rs b/gen/src/write.rs index c6d59d8b9..3971e5754 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -769,7 +769,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { write!(out, "return "); } match &efn.ret { - Some(Type::Ref(_)) => write!(out, "&"), + Some(Type::Ref(ty)) if !ty.option => write!(out, "&"), Some(Type::Str(_)) if !indirect_return => { out.builtin.rust_str_repr = true; write!(out, "::rust::impl<::rust::Str>::repr("); @@ -1231,7 +1231,11 @@ fn write_type(out: &mut OutFile, ty: &Type) { } Type::Ref(r) => { write_pointee_type(out, &r.inner, r.mutable); - write!(out, " &"); + if r.option { + write!(out, " *") + } else { + write!(out, " &"); + } } Type::Ptr(p) => { write_pointee_type(out, &p.inner, p.mutable); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index bc3f000d6..966c2c7eb 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -527,15 +527,19 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { true => quote_spanned!(span=> ::cxx::private::RustVec::from_mut(#var)), }, inner if types.is_considered_improper_ctype(inner) => { - let var = match ty.pinned { - false => quote!(#var), - true => quote_spanned!(span=> ::std::pin::Pin::into_inner_unchecked(#var)), + let unpin = match ty.pinned { + false => quote!(), + true => quote_spanned!(span=> ::std::pin::Pin::into_inner_unchecked), }; - match ty.mutable { - false => { - quote_spanned!(span=> #var as *const #inner as *const ::std::ffi::c_void) - } - true => quote_spanned!(span=> #var as *mut #inner as *mut ::std::ffi::c_void), + let raw_mutability = match ty.mutable { + false => quote_spanned!(span=> const), + true => quote_spanned!(span=> mut), + }; + let to_ptr = quote_spanned!(span=> |var| #unpin(var) as *#raw_mutability #inner as *#raw_mutability ::std::ffi::c_void); + match (ty.option, ty.mutable) { + (false, _) => quote_spanned!(span=> #[allow(clippy::redundant_closure_call)] (#to_ptr)(#var)), + (true, false) => quote_spanned!(span=> #var.map_or(::std::ptr::null(), #to_ptr)), + (true, true) => quote_spanned!(span=> #var.map_or(::std::ptr::null_mut(), #to_ptr)), } } _ => quote!(#var), @@ -655,12 +659,24 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { }, inner if types.is_considered_improper_ctype(inner) => { let mutability = ty.mutability; - let deref_mut = quote_spanned!(span=> &#mutability *#call.cast()); - match ty.pinned { - false => deref_mut, + let call = quote_spanned!(span=> let __ptr = #call); + let unpinned = quote_spanned!(span=> &#mutability *__ptr.cast()); + let maybe_pinned = match ty.pinned { + false => unpinned, true => { - quote_spanned!(span=> ::std::pin::Pin::new_unchecked(#deref_mut)) + quote_spanned!(span=> ::std::pin::Pin::new_unchecked(#unpinned)) } + }; + match ty.option { + false => quote_spanned!(span=> #call; #maybe_pinned), + true => quote_spanned! {span=> + #call; + if __ptr.is_null() { + ::std::option::Option::None + } else { + ::std::option::Option::Some(#maybe_pinned) + } + }, } } _ => call, diff --git a/syntax/impls.rs b/syntax/impls.rs index 06d68dc22..a99d61b74 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -152,23 +152,31 @@ impl PartialEq for Ref { fn eq(&self, other: &Self) -> bool { let Ref { pinned, + option, ampersand: _, lifetime, mutable, inner, pin_tokens: _, + option_tokens: _, mutability: _, } = self; let Ref { pinned: pinned2, + option: option2, ampersand: _, lifetime: lifetime2, mutable: mutable2, inner: inner2, pin_tokens: _, + option_tokens: _, mutability: _, } = other; - pinned == pinned2 && lifetime == lifetime2 && mutable == mutable2 && inner == inner2 + pinned == pinned2 + && option == option2 + && lifetime == lifetime2 + && mutable == mutable2 + && inner == inner2 } } @@ -176,14 +184,17 @@ impl Hash for Ref { fn hash(&self, state: &mut H) { let Ref { pinned, + option, ampersand: _, lifetime, mutable, inner, pin_tokens: _, + option_tokens: _, mutability: _, } = self; pinned.hash(state); + option.hash(state); lifetime.hash(state); mutable.hash(state); inner.hash(state); diff --git a/syntax/mod.rs b/syntax/mod.rs index 1d9863455..f5514ca4c 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -231,11 +231,13 @@ pub struct Ty1 { pub struct Ref { pub pinned: bool, + pub option: bool, pub ampersand: Token![&], pub lifetime: Option, pub mutable: bool, pub inner: Type, pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>, + pub option_tokens: Option<(kw::Option, Token![<], Token![>])>, pub mutability: Option, } diff --git a/syntax/parse.rs b/syntax/parse.rs index 32d36eb34..e3ab6cf8b 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,8 +1,8 @@ -use crate::syntax::attrs::OtherAttrs; use crate::syntax::discriminant::DiscriminantSet; use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; +use crate::syntax::OtherAttrs; use crate::syntax::{ attrs, error, Api, Array, Derive, Doc, Enum, EnumRepr, ExternFn, ExternType, ForeignName, Impl, Include, IncludeKind, Lang, Lifetimes, NamedType, Namespace, Pair, Ptr, Receiver, Ref, @@ -22,6 +22,7 @@ use syn::{ }; pub mod kw { + syn::custom_keyword!(Option); syn::custom_keyword!(Pin); syn::custom_keyword!(Result); } @@ -1080,7 +1081,9 @@ fn parse_type_reference(ty: &TypeReference) -> Result { let inner = parse_type(&ty.elem)?; let pinned = false; + let option = false; let pin_tokens = None; + let option_tokens = None; Ok(match &inner { Type::Ident(ident) if ident.rust == "str" => { @@ -1093,11 +1096,13 @@ fn parse_type_reference(ty: &TypeReference) -> Result { _ => Type::Ref, }(Box::new(Ref { pinned, + option, ampersand, lifetime, mutable, inner, pin_tokens, + option_tokens, mutability, }))) } @@ -1198,6 +1203,17 @@ fn parse_type_path(ty: &TypePath) -> Result { return Ok(Type::Ref(inner)); } } + } else if ident == "Option" && generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + let inner = parse_type(arg)?; + let option_token = kw::Option(ident.span()); + if let Type::Ref(mut inner) = inner { + inner.option = true; + inner.option_tokens = + Some((option_token, generic.lt_token, generic.gt_token)); + return Ok(Type::Ref(inner)); + } + } } else { let mut lifetimes = Punctuated::new(); let mut only_lifetimes = true; diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 33f20fa3c..0d4aeebbd 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -81,13 +81,19 @@ impl ToTokens for Ref { fn to_tokens(&self, tokens: &mut TokenStream) { let Ref { pinned: _, + option: _, ampersand, lifetime, mutable: _, inner, pin_tokens, + option_tokens, mutability, } = self; + if let Some((option, langle, _rangle)) = option_tokens { + tokens.extend(quote_spanned!(option.span=> ::std::option::Option)); + langle.to_tokens(tokens); + } if let Some((pin, langle, _rangle)) = pin_tokens { tokens.extend(quote_spanned!(pin.span=> ::std::pin::Pin)); langle.to_tokens(tokens); @@ -99,6 +105,9 @@ impl ToTokens for Ref { if let Some((_pin, _langle, rangle)) = pin_tokens { rangle.to_tokens(tokens); } + if let Some((_option, _langle, rangle)) = option_tokens { + rangle.to_tokens(tokens); + } } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 25b4ccdc8..561edb99c 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -99,6 +99,8 @@ pub mod ffi { fn c_return_shared_ptr() -> SharedPtr; fn c_return_ref(shared: &Shared) -> &usize; fn c_return_mut(shared: &mut Shared) -> &mut usize; + fn c_return_opt_ref(shared: &Shared) -> Option<&usize>; + fn c_return_opt_mut(shared: &mut Shared) -> Option<&mut usize>; fn c_return_str(shared: &Shared) -> &str; fn c_return_slice_char(shared: &Shared) -> &[c_char]; fn c_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8]; @@ -130,6 +132,10 @@ pub mod ffi { fn c_take_box(r: Box); fn c_take_ref_r(r: &R); fn c_take_ref_c(c: &C); + fn c_take_opt_ref_r(r: Option<&R>); + fn c_take_opt_mut_r(r: Option<&mut R>); + fn c_take_opt_ref_c(r: Option<&C>); + fn c_take_opt_mut_c(r: Option>); fn c_take_str(s: &str); fn c_take_slice_char(s: &[c_char]); fn c_take_slice_shared(s: &[Shared]); diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs index 21a86206d..67203854b 100644 --- a/tests/ffi/module.rs +++ b/tests/ffi/module.rs @@ -35,6 +35,10 @@ pub mod ffi2 { fn c_take_trivial_mut_ref(d: &mut D); fn c_take_trivial_pin_ref(d: Pin<&D>); fn c_take_trivial_pin_mut_ref(d: Pin<&mut D>); + fn c_take_trivial_opt_ref(d: Option<&D>); + fn c_take_trivial_opt_mut_ref(d: Option<&mut D>); + fn c_take_trivial_opt_pin_ref(d: Option>); + fn c_take_trivial_opt_pin_mut_ref(d: Option>); fn c_take_trivial_ref_method(self: &D); fn c_take_trivial_mut_ref_method(self: &mut D); fn c_take_trivial(d: D); @@ -56,6 +60,7 @@ pub mod ffi2 { fn c_return_ns_opaque_ptr() -> UniquePtr; fn c_return_ns_unique_ptr() -> UniquePtr; fn c_take_ref_ns_c(h: &H); + fn c_roundtrip_opaque_opt_mut_pin_ref(e: Option>) -> Option>; #[namespace = "other"] fn ns_c_take_trivial(d: D); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index ff215ca00..1da8f9bbb 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -94,6 +94,10 @@ const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared) { size_t &c_return_mut(Shared &shared) { return shared.z; } +const size_t *c_return_opt_ref(const Shared &shared) { return &shared.z; } + +size_t *c_return_opt_mut(Shared &shared) { return &shared.z; } + rust::Str c_return_str(const Shared &shared) { (void)shared; return "2020"; @@ -269,12 +273,46 @@ void c_take_ref_c(const C &c) { } } +void c_take_opt_ref_r(const R *r) { + if (r) { + c_take_ref_r(*r); + } else { + cxx_test_suite_set_correct(); + } +} + +void c_take_opt_mut_r(R *r) { + if (r) { + c_take_ref_r(*r); + } else { + cxx_test_suite_set_correct(); + } +} + +void c_take_opt_ref_c(const C *c) { + if (c) { + c_take_ref_c(*c); + } else { + cxx_test_suite_set_correct(); + } +} + +void c_take_opt_mut_c(C *c) { + if (c) { + c_take_ref_c(*c); + } else { + cxx_test_suite_set_correct(); + } +} + void c_take_ref_ns_c(const ::H::H &h) { if (h.h == "hello") { cxx_test_suite_set_correct(); } } +E *c_roundtrip_opaque_opt_mut_pin_ref(E *e) { return e; } + void c_take_str(rust::Str s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); @@ -620,6 +658,14 @@ void c_take_trivial_pin_ref(const D &d) { (void)d; } void c_take_trivial_pin_mut_ref(D &d) { (void)d; } +void c_take_trivial_opt_ref(const D *d) { (void)d; } + +void c_take_trivial_opt_mut_ref(D *d) { (void)d; } + +void c_take_trivial_opt_pin_ref(const D *d) { (void)d; } + +void c_take_trivial_opt_pin_mut_ref(D *d) { (void)d; } + void D::c_take_trivial_ref_method() const { if (d == 30) { cxx_test_suite_set_correct(); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index b624d3933..47cf81f0c 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -96,6 +96,8 @@ const size_t &c_return_ref(const Shared &shared); const size_t &c_return_ns_ref(const ::A::AShared &shared); const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared); size_t &c_return_mut(Shared &shared); +const size_t *c_return_opt_ref(const Shared &shared); +size_t *c_return_opt_mut(Shared &shared); rust::Str c_return_str(const Shared &shared); rust::Slice c_return_slice_char(const Shared &shared); rust::Slice c_return_mutsliceu8(rust::Slice slice); @@ -129,7 +131,12 @@ void c_take_box(rust::Box r); void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); +void c_take_opt_ref_r(const R *r); +void c_take_opt_mut_r(R *r); +void c_take_opt_ref_c(const C *c); +void c_take_opt_mut_c(C *c); void c_take_ref_ns_c(const ::H::H &h); +E *c_roundtrip_opaque_opt_mut_pin_ref(E *e); void c_take_str(rust::Str s); void c_take_slice_char(rust::Slice s); void c_take_slice_shared(rust::Slice s); @@ -189,6 +196,10 @@ void c_take_trivial_ref(const D &d); void c_take_trivial_mut_ref(D &d); void c_take_trivial_pin_ref(const D &d); void c_take_trivial_pin_mut_ref(D &d); +void c_take_trivial_opt_ref(const D *d); +void c_take_trivial_opt_mut_ref(D *d); +void c_take_trivial_opt_pin_ref(const D *d); +void c_take_trivial_opt_pin_mut_ref(D *d); void c_take_trivial(D d); void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g); diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs new file mode 100644 index 000000000..4632f0265 --- /dev/null +++ b/tests/ui/option_option.rs @@ -0,0 +1,8 @@ +#[cxx::bridge] +mod ffi { + extern "C++" { + unsafe fn f(_: Option>); + } +} + +fn main() {}