Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions gen/src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(");
Expand Down Expand Up @@ -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);
Expand Down
40 changes: 28 additions & 12 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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,
Expand Down
13 changes: 12 additions & 1 deletion syntax/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,38 +152,49 @@ 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
}
}

impl Hash for Ref {
fn hash<H: Hasher>(&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);
Expand Down
2 changes: 2 additions & 0 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,13 @@ pub struct Ty1 {

pub struct Ref {
pub pinned: bool,
pub option: bool,
pub ampersand: Token![&],
pub lifetime: Option<Lifetime>,
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<Token![mut]>,
}

Expand Down
18 changes: 17 additions & 1 deletion syntax/parse.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -22,6 +22,7 @@ use syn::{
};

pub mod kw {
syn::custom_keyword!(Option);
syn::custom_keyword!(Pin);
syn::custom_keyword!(Result);
}
Expand Down Expand Up @@ -1080,7 +1081,9 @@ fn parse_type_reference(ty: &TypeReference) -> Result<Type> {

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" => {
Expand All @@ -1093,11 +1096,13 @@ fn parse_type_reference(ty: &TypeReference) -> Result<Type> {
_ => Type::Ref,
}(Box::new(Ref {
pinned,
option,
ampersand,
lifetime,
mutable,
inner,
pin_tokens,
option_tokens,
mutability,
})))
}
Expand Down Expand Up @@ -1198,6 +1203,17 @@ fn parse_type_path(ty: &TypePath) -> Result<Type> {
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;
Expand Down
9 changes: 9 additions & 0 deletions syntax/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions tests/ffi/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ pub mod ffi {
fn c_return_shared_ptr() -> SharedPtr<C>;
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];
Expand Down Expand Up @@ -130,6 +132,10 @@ pub mod ffi {
fn c_take_box(r: Box<R>);
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<Pin<&mut C>>);
fn c_take_str(s: &str);
fn c_take_slice_char(s: &[c_char]);
fn c_take_slice_shared(s: &[Shared]);
Expand Down
5 changes: 5 additions & 0 deletions tests/ffi/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Pin<&D>>);
fn c_take_trivial_opt_pin_mut_ref(d: Option<Pin<&mut D>>);
fn c_take_trivial_ref_method(self: &D);
fn c_take_trivial_mut_ref_method(self: &mut D);
fn c_take_trivial(d: D);
Expand All @@ -56,6 +60,7 @@ pub mod ffi2 {
fn c_return_ns_opaque_ptr() -> UniquePtr<F>;
fn c_return_ns_unique_ptr() -> UniquePtr<H>;
fn c_take_ref_ns_c(h: &H);
fn c_roundtrip_opaque_opt_mut_pin_ref(e: Option<Pin<&mut E>>) -> Option<Pin<&mut E>>;

#[namespace = "other"]
fn ns_c_take_trivial(d: D);
Expand Down
46 changes: 46 additions & 0 deletions tests/ffi/tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
11 changes: 11 additions & 0 deletions tests/ffi/tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<const char> c_return_slice_char(const Shared &shared);
rust::Slice<uint8_t> c_return_mutsliceu8(rust::Slice<uint8_t> slice);
Expand Down Expand Up @@ -129,7 +131,12 @@ void c_take_box(rust::Box<R> r);
void c_take_unique_ptr(std::unique_ptr<C> 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<const char> s);
void c_take_slice_shared(rust::Slice<const Shared> s);
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/option_option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#[cxx::bridge]
mod ffi {
extern "C++" {
unsafe fn f(_: Option<Option<&u32>>);
}
}

fn main() {}