From 451be69cac4363af972675f35b7b83a114bc510e Mon Sep 17 00:00:00 2001 From: kuecks Date: Wed, 7 Jul 2021 17:38:52 +0000 Subject: [PATCH] Add bindings for Rust std::option::Option --- gen/src/builtin.rs | 1 + gen/src/write.rs | 125 +++++++++++- include/cxx.h | 81 ++++++++ macro/src/expand.rs | 271 ++++++++++++++++++++++++- src/cxx.cc | 34 ++++ src/lib.rs | 5 +- src/rust_option.rs | 251 +++++++++++++++++++++++ src/rust_type.rs | 2 + src/symbols/mod.rs | 1 + src/symbols/rust_option.rs | 55 +++++ syntax/check.rs | 40 +++- syntax/impls.rs | 1 + syntax/improper.rs | 1 + syntax/instantiate.rs | 35 ++++ syntax/mod.rs | 1 + syntax/parse.rs | 15 ++ syntax/pod.rs | 1 + syntax/resolve.rs | 4 +- syntax/tokens.rs | 6 +- syntax/types.rs | 5 +- syntax/visit.rs | 3 +- tests/compiletest.rs | 9 +- tests/ffi/lib.rs | 223 ++++++++++++++++++++ tests/ffi/tests.cc | 137 +++++++++++++ tests/ffi/tests.h | 28 +++ tests/test.rs | 24 +++ tests/ui/option_slice.rs | 12 ++ tests/ui/option_slice.stderr | 5 + tests/ui/option_str.rs | 12 ++ tests/ui/option_str.stderr | 5 + tests/ui/option_string.rs | 12 ++ tests/ui/option_string.stderr | 5 + tests/ui/option_vec.rs | 12 ++ tests/ui/option_vec.stderr | 5 + tests/ui_build/option_not_sized.rs | 5 + tests/ui_build/option_not_sized.stderr | 13 ++ tests/ui_pass/empty.rs | 4 + 37 files changed, 1424 insertions(+), 25 deletions(-) create mode 100644 src/rust_option.rs create mode 100644 src/symbols/rust_option.rs create mode 100644 tests/ui/option_slice.rs create mode 100644 tests/ui/option_slice.stderr create mode 100644 tests/ui/option_str.rs create mode 100644 tests/ui/option_str.stderr create mode 100644 tests/ui/option_string.rs create mode 100644 tests/ui/option_string.stderr create mode 100644 tests/ui/option_vec.rs create mode 100644 tests/ui/option_vec.stderr create mode 100644 tests/ui_build/option_not_sized.rs create mode 100644 tests/ui_build/option_not_sized.stderr create mode 100644 tests/ui_pass/empty.rs diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index d38473afc..8f54b8a15 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -10,6 +10,7 @@ pub(crate) struct Builtins<'a> { pub rust_slice: bool, pub rust_box: bool, pub rust_vec: bool, + pub rust_option: bool, pub rust_fn: bool, pub rust_isize: bool, pub opaque: bool, diff --git a/gen/src/write.rs b/gen/src/write.rs index 8eef0a76b..2670f8839 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -3,7 +3,7 @@ use crate::gen::nested::NamespaceEntries; use crate::gen::out::OutFile; use crate::gen::{builtin, include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::instantiate::{ImplKey, NamedImplKey}; +use crate::syntax::instantiate::{ImplKey, NamedImplKey, OptionInner}; use crate::syntax::map::UnorderedMap as Map; use crate::syntax::set::UnorderedSet; use crate::syntax::symbol::{self, Symbol}; @@ -214,6 +214,7 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { }, Type::RustBox(_) => out.builtin.rust_box = true, Type::RustVec(_) => out.builtin.rust_vec = true, + Type::RustOption(_) => out.builtin.rust_option = true, Type::UniquePtr(_) => out.include.memory = true, Type::SharedPtr(_) | Type::WeakPtr(_) => out.include.memory = true, Type::Str(_) => out.builtin.rust_str = true, @@ -836,6 +837,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); @@ -1235,6 +1238,21 @@ fn write_type(out: &mut OutFile, ty: &Type) { write_type(out, &ty.inner); write!(out, ">"); } + Type::RustOption(ty) => { + write!(out, "::rust::Option<"); + match &ty.inner { + Type::RustBox(_) => write_type(out, &ty.inner), + Type::Ref(r) => { + write_type_space(out, &r.inner); + if !r.mutable { + write!(out, "const "); + } + write!(out, " *"); + } + _ => unreachable!(), + } + write!(out, ">"); + } Type::UniquePtr(ptr) => { write!(out, "::std::unique_ptr<"); write_type(out, &ptr.inner); @@ -1340,6 +1358,7 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { | Type::Str(_) | Type::CxxVector(_) | Type::RustVec(_) + | Type::RustOption(_) | Type::SliceRef(_) | Type::Fn(_) | Type::Array(_) => write!(out, " "), @@ -1354,6 +1373,12 @@ enum UniquePtr<'a> { CxxVector(&'a Ident), } +enum RustOption<'a> { + RustBox(&'a Ident), + Ref(&'a Ident), + MutRef(&'a Ident), +} + trait ToTypename { fn to_typename(&self, types: &Types) -> String; } @@ -1375,6 +1400,18 @@ impl<'a> ToTypename for UniquePtr<'a> { } } +impl<'a> ToTypename for RustOption<'a> { + fn to_typename(&self, types: &Types) -> String { + match self { + RustOption::RustBox(inner) => { + format!("::rust::cxxbridge1::Box<{}>", inner.to_typename(types)) + } + RustOption::Ref(inner) => format!("const {}*", inner.to_typename(types)), + RustOption::MutRef(inner) => format!("{}*", inner.to_typename(types)), + } + } +} + trait ToMangled { fn to_mangled(&self, types: &Types) -> Symbol; } @@ -1396,6 +1433,22 @@ 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) => { + let symbol = symbol::join(&[&"const", &inner.to_mangled(types)]); + symbol + } + RustOption::MutRef(inner) => { + let symbol = symbol::join(&[&inner.to_mangled(types)]); + symbol + } + } + } +} + fn write_generic_instantiations(out: &mut OutFile) { if out.header { return; @@ -1409,6 +1462,7 @@ fn write_generic_instantiations(out: &mut OutFile) { match *impl_key { ImplKey::RustBox(ident) => write_rust_box_extern(out, ident), ImplKey::RustVec(ident) => write_rust_vec_extern(out, ident), + ImplKey::RustOption(ident) => write_rust_option_extern(out, ident), ImplKey::UniquePtr(ident) => write_unique_ptr(out, ident), ImplKey::SharedPtr(ident) => write_shared_ptr(out, ident), ImplKey::WeakPtr(ident) => write_weak_ptr(out, ident), @@ -1423,6 +1477,7 @@ fn write_generic_instantiations(out: &mut OutFile) { match *impl_key { ImplKey::RustBox(ident) => write_rust_box_impl(out, ident), ImplKey::RustVec(ident) => write_rust_vec_impl(out, ident), + ImplKey::RustOption(ident) => write_rust_option_impl(out, ident), _ => {} } } @@ -1501,6 +1556,38 @@ fn write_rust_vec_extern(out: &mut OutFile, key: NamedImplKey) { ); } +fn write_rust_option_extern(out: &mut OutFile, inner: OptionInner) { + out.include.cstddef = true; + let element = match inner { + OptionInner::RustBox(key) => RustOption::RustBox(key.rust), + OptionInner::Ref(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + RustOption::Ref(key.rust) + } + OptionInner::MutRef(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + RustOption::MutRef(key.rust) + } + }; + let inner = element.to_typename(out.types); + let instance = element.to_mangled(out.types); + + writeln!( + out, + "void cxxbridge1$rust_option${}$new(const ::rust::Option<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, + "void cxxbridge1$rust_option${}$drop(::rust::Option<{}> *ptr) noexcept;", + instance, inner, + ); +} + fn write_rust_box_impl(out: &mut OutFile, key: NamedImplKey) { let resolve = out.types.resolve(&key); let inner = resolve.name.to_fully_qualified(); @@ -1621,6 +1708,42 @@ fn write_rust_vec_impl(out: &mut OutFile, key: NamedImplKey) { writeln!(out, "}}"); } +fn write_rust_option_impl(out: &mut OutFile, inner: OptionInner) { + let element = match inner { + OptionInner::RustBox(key) => RustOption::RustBox(key.rust), + OptionInner::Ref(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + RustOption::Ref(key.rust) + } + OptionInner::MutRef(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + RustOption::MutRef(key.rust) + } + }; + let inner = element.to_typename(out.types); + let instance = element.to_mangled(out.types); + + writeln!(out, "template <>"); + begin_function_definition(out); + writeln!(out, "Option<{}>::Option() noexcept {{", inner); + writeln!(out, " cxxbridge1$rust_option${}$new(this);", instance); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + begin_function_definition(out); + writeln!(out, "void Option<{}>::drop() noexcept {{", inner); + writeln!( + out, + " return cxxbridge1$rust_option${}$drop(this);", + instance + ); + writeln!(out, "}}"); +} + fn write_unique_ptr(out: &mut OutFile, key: NamedImplKey) { let ty = UniquePtr::Ident(key.rust); write_unique_ptr_common(out, ty); diff --git a/include/cxx.h b/include/cxx.h index 002282551..716424d19 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -826,6 +826,87 @@ template Box::Box(uninit) noexcept {} #endif // CXXBRIDGE1_RUST_BOX +#ifndef CXXBRIDGE1_RUST_OPTION +template +class Option final { +public: + Option() noexcept; + Option(Option&&) noexcept; + Option(T&&) noexcept; + ~Option() noexcept; + void drop() noexcept; + + const T *operator->() const; + const T &operator*() const; + T *operator->(); + T &operator*(); + + bool has_value() const noexcept; + const T& value() const; + + union OptionContents { + T value; + size_t empty; + + OptionContents() { + empty = 0; + } + // Destruction handled in Option since it knows if type was created + ~OptionContents() {} + }; + OptionContents inner; // Avoid initialization +}; +#endif // CXXBRIDGE1_RUST_OPTION + +#ifndef CXXBRIDGE1_RUST_OPTION +#define CXXBRIDGE1_RUST_OPTION +template +Option::Option() noexcept {} + +template +Option::Option(Option&& other) noexcept { + if (other.inner.empty != 0) { + inner.value = std::move(other.inner.value); + } + other.inner.empty = 0; +} + +template +Option::~Option() noexcept { + this->drop(); +} + +template +const T *Option::operator->() const { + return &inner.value; +} + +template +const T &Option::operator*() const { + return inner.value; +} + +template +T *Option::operator->() { + return &inner.value; +} + +template +T &Option::operator*() { + return inner.value; +} + +template +bool Option::has_value() const noexcept { + return inner.empty != 0; +} + +template +const T& Option::value() const { + return inner.value; +} +#endif // CXXBRIDGE1_RUST_OPTION + #ifndef CXXBRIDGE1_RUST_VEC #define CXXBRIDGE1_RUST_VEC template diff --git a/macro/src/expand.rs b/macro/src/expand.rs index bcc660db5..4aea242bc 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -2,7 +2,7 @@ use crate::syntax::atom::Atom::*; use crate::syntax::attrs::{self, OtherAttrs}; use crate::syntax::cfg::CfgExpr; use crate::syntax::file::Module; -use crate::syntax::instantiate::{ImplKey, NamedImplKey}; +use crate::syntax::instantiate::{ImplKey, NamedImplKey, OptionInner}; use crate::syntax::qualified::QualifiedName; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; @@ -99,6 +99,9 @@ fn expand(ffi: Module, doc: Doc, attrs: OtherAttrs, apis: &[Api], types: &Types) ImplKey::RustVec(ident) => { hidden.extend(expand_rust_vec(ident, types, explicit_impl)); } + ImplKey::RustOption(ident) => { + hidden.extend(expand_rust_option(ident, types, explicit_impl)); + } ImplKey::UniquePtr(ident) => { expanded.extend(expand_unique_ptr(ident, types, explicit_impl)); } @@ -462,6 +465,8 @@ fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream { quote!(#var #colon *const #ty) } else if let Type::RustVec(_) = arg.ty { quote!(#var #colon *const #ty) + } else if let Type::RustOption(_) = arg.ty { + quote!(#var #colon *const #ty) } else if let Type::Fn(_) = arg.ty { quote!(#var #colon ::cxx::private::FatFunction) } else if types.needs_indirect_abi(&arg.ty) { @@ -544,6 +549,39 @@ 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) => { + match &ty.inner { + Type::RustBox(ty) => if types.is_considered_improper_ctype(&ty.inner) { + quote_spanned!(span=> &::cxx::RustOption::from_option_box_improper(#var.assume_init()) as _) + } else { + quote_spanned!(span=> &::cxx::RustOption::from_option_box(#var.assume_init()) as _) + }, + Type::Ref(r) => if types.is_considered_improper_ctype(&ty.inner) { + if r.mutable { + if r.pinned { + quote_spanned!(span=> &::cxx::RustOption::from_option_mut_improper_pinned(#var.assume_init()) as _) + } else { + quote_spanned!(span=> &::cxx::RustOption::from_option_mut_improper(#var.assume_init()) as _) + } + } else if r.pinned { + quote_spanned!(span=> &::cxx::RustOption::from_option_ref_improper_pinned(#var.assume_init()) as _) + } else { + quote_spanned!(span=> &::cxx::RustOption::from_option_ref_improper(#var.assume_init()) as _) + } + } else if r.mutable { + if r.pinned { + quote_spanned!(span=> &::cxx::RustOption::from_option_mut_pinned(#var.assume_init()) as _) + } else { + quote_spanned!(span=> &::cxx::RustOption::from_option_mut(#var.assume_init()) as _) + } + } else if r.pinned { + quote_spanned!(span=> &::cxx::RustOption::from_option_ref_pinned(#var.assume_init()) as _) + } else { + quote_spanned!(span=> &::cxx::RustOption::from_option_ref(#var.assume_init()) as _) + }, + _ => unreachable!(), + } + } Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { false => quote_spanned!(span=> ::cxx::private::RustString::from_ref(#var)), @@ -607,10 +645,16 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { .map(|arg| { let var = &arg.name.rust; let span = var.span(); - // These are arguments for which C++ has taken ownership of the data - // behind the mut reference it received. - quote_spanned! {span=> - let mut #var = ::cxx::core::mem::MaybeUninit::new(#var); + if let Type::RustOption(_) = arg.ty { + quote_spanned! {span=> + let #var = ::cxx::core::mem::MaybeUninit::new(#var); + } + } else { + // These are arguments for which C++ has taken ownership of the data + // behind the mut reference it received. + quote_spanned! {span=> + let mut #var = ::cxx::core::mem::MaybeUninit::new(#var); + } } }) .collect::(); @@ -664,6 +708,41 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { quote_spanned!(span=> #call.into_vec()) } } + Type::RustOption(ty) => match &ty.inner { + Type::RustBox(ty) => { + if types.is_considered_improper_ctype(&ty.inner) { + quote_spanned!(span=> #call.into_option_box_improper()) + } else { + quote_spanned!(span=> #call.into_option_box()) + } + } + Type::Ref(r) => { + if types.is_considered_improper_ctype(&ty.inner) { + if r.mutable { + if r.pinned { + quote_spanned!(span=> #call.into_option_mut_improper_pinned()) + } else { + quote_spanned!(span=> #call.into_option_mut_improper()) + } + } else if r.pinned { + quote_spanned!(span=> #call.into_option_ref_improper_pinned()) + } else { + quote_spanned!(span=> #call.into_option_ref_improper()) + } + } else if r.mutable { + if r.pinned { + quote_spanned!(span=> #call.into_option_mut_pinned()) + } else { + quote_spanned!(span=> #call.into_option_mut()) + } + } else if r.pinned { + quote_spanned!(span=> #call.into_option_ref_pinned()) + } else { + quote_spanned!(span=> #call.into_option_ref()) + } + } + _ => unreachable!(), + }, Type::UniquePtr(ty) => { if types.is_considered_improper_ctype(&ty.inner) { quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#call.cast())) @@ -1000,6 +1079,33 @@ fn expand_rust_function_shim_impl( quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_vec())) } } + Type::RustOption(ty) => match &ty.inner { + Type::RustBox(_) => quote_spanned!(span=> ::std::mem::take((*#var).as_mut_option_box())), + Type::Ref(r) => if types.is_considered_improper_ctype(&ty.inner) { + if r.mutable { + if r.pinned { + quote_spanned!(span=> std::mem::take((*#var).as_mut_option_mut_improper_pinned())) + } else { + quote_spanned!(span=> std::mem::take((*#var).as_mut_option_mut_improper())) + } + } else if r.pinned { + quote_spanned!(span=> std::mem::take((*#var).as_mut_option_ref_improper_pinned())) + } else { + quote_spanned!(span=> std::mem::take((*#var).as_mut_option_ref_improper())) + } + } else if r.mutable { + if r.pinned { + quote_spanned!(span=> std::mem::take((*#var).as_mut_option_mut_pinned())) + } else { + quote_spanned!(span=> std::mem::take((*#var).as_mut_option_mut())) + } + } else if r.pinned { + quote_spanned!(span=> std::mem::take((*#var).as_mut_option_ref_pinned())) + } else { + quote_spanned!(span=> std::mem::take((*#var).as_mut_option_ref())) + } + _ => unreachable!() + } Type::UniquePtr(_) => quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#var)), Type::Ref(ty) => match &ty.inner { Type::Ident(i) if i.rust == RustString => match ty.mutable { @@ -1061,6 +1167,37 @@ fn expand_rust_function_shim_impl( Some(quote_spanned!(span=> ::cxx::private::RustVec::from)) } } + Type::RustOption(ty) => match &ty.inner { + Type::RustBox(_) => { + Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_box)) + } + Type::Ref(r) => { + if types.is_considered_improper_ctype(&ty.inner) { + if r.mutable { + if r.pinned { + Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_mut_pinned)) + } else { + Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_mut)) + } + } else if r.pinned { + Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_ref_pinned)) + } else { + Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_ref)) + } + } else if r.mutable { + if r.pinned { + Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_mut_pinned)) + } else { + Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_mut)) + } + } else if r.pinned { + Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_ref_pinned)) + } else { + Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_ref)) + } + } + _ => unreachable!(), + }, Type::UniquePtr(_) => Some(quote_spanned!(span=> ::cxx::UniquePtr::into_raw)), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { @@ -1381,6 +1518,116 @@ fn expand_rust_vec(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl } } +fn expand_rust_option( + inner: OptionInner, + types: &Types, + explicit_impl: Option<&Impl>, +) -> TokenStream { + let (elem, key, resolve, link_prefix, local_prefix) = match &inner { + 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 local_prefix = format_ident!("{}__box__option_", elem); + (elem, key, Some(resolve), link_prefix, local_prefix) + } + OptionInner::Ref(key) => { + let elem = key.rust; + if types.try_resolve(key.rust).is_none() { + return TokenStream::new(); + } + let resolve = types.resolve(elem); + let link_prefix = format!("cxxbridge1$rust_option$const${}$", resolve.name.to_symbol()); + let local_prefix = format_ident!("{}__const__ref__option_", elem); + (elem, key, Some(resolve), link_prefix, local_prefix) + } + OptionInner::MutRef(key) => { + let elem = key.rust; + if types.try_resolve(key.rust).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!("{}__ref__option_", elem); + (elem, key, Some(resolve), link_prefix, local_prefix) + } + }; + let link_new = format!("{}new", link_prefix); + let link_drop = format!("{}drop", link_prefix); + + let local_new = format_ident!("{}new", local_prefix); + let local_drop = format_ident!("{}drop", local_prefix); + + let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span); + let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span.join()); + let unsafe_token = format_ident!("unsafe", span = begin_span); + + let ident = key.rust; + match inner { + OptionInner::RustBox(_) => { + let prevent_unwind_drop_label = + format!("::alloc::boxed::Box<::{}> as Drop>::drop", ident); + let (impl_generics, ty_generics) = + generics::split_for_impl(*key, explicit_impl, resolve.unwrap()); + quote_spanned! {end_span=> + #[doc(hidden)] + #unsafe_token impl #impl_generics ::cxx::private::ImplOption for ::cxx::alloc::boxed::Box<#elem> #ty_generics {} + #[doc(hidden)] + #[export_name = #link_new] + unsafe extern "C" fn #local_new #impl_generics(this: *mut ::cxx::private::RustOption<::cxx::alloc::boxed::Box<#elem #ty_generics>>) { + ::std::ptr::write(this, ::cxx::private::RustOption::new()); + } + #[doc(hidden)] + #[export_name = #link_drop] + unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::private::RustOption<::cxx::alloc::boxed::Box<#elem #ty_generics>>) { + let __fn = concat!("<", module_path!(), #prevent_unwind_drop_label); + ::cxx::private::prevent_unwind(__fn, || ::cxx::core::ptr::drop_in_place(this)); + } + } + } + OptionInner::Ref(_) => { + let prevent_unwind_drop_label = format!("&::{}> as Drop>::drop", ident); + let (impl_generics, ty_generics) = + generics::split_for_impl(*key, explicit_impl, resolve.unwrap()); + quote_spanned! {end_span=> + #[doc(hidden)] + #unsafe_token impl #impl_generics ::cxx::private::ImplOption for &#elem #ty_generics {} + #[doc(hidden)] + #[export_name = #link_new] + unsafe extern "C" fn #local_new #impl_generics(this: *mut ::cxx::private::RustOption<&#elem #ty_generics>) { + ::std::ptr::write(this, ::cxx::private::RustOption::new()); + } + #[doc(hidden)] + #[export_name = #link_drop] + unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::private::RustOption<*const #elem #ty_generics>) { + let __fn = concat!("<", module_path!(), #prevent_unwind_drop_label); + ::cxx::private::prevent_unwind(__fn, || ::cxx::core::ptr::drop_in_place(this)); + } + } + } + OptionInner::MutRef(_) => { + let prevent_unwind_drop_label = format!("&::{}> as Drop>::drop", ident); + let (impl_generics, ty_generics) = + generics::split_for_impl(*key, explicit_impl, resolve.unwrap()); + quote_spanned! {end_span=> + #[doc(hidden)] + #unsafe_token impl #impl_generics ::cxx::private::ImplOption for &mut #elem #ty_generics {} + #[doc(hidden)] + #[export_name = #link_new] + unsafe extern "C" fn #local_new #impl_generics(this: *mut ::cxx::private::RustOption<&mut #elem #ty_generics>) { + ::std::ptr::write(this, ::cxx::private::RustOption::new()); + } + #[doc(hidden)] + #[export_name = #link_drop] + unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::private::RustOption<*mut #elem #ty_generics>) { + let __fn = concat!("<", module_path!(), #prevent_unwind_drop_label); + ::cxx::private::prevent_unwind(__fn, || ::cxx::core::ptr::drop_in_place(this)); + } + } + } + } +} + fn expand_unique_ptr( key: NamedImplKey, types: &Types, @@ -1776,6 +2023,13 @@ fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream { let rangle = ty.rangle; quote_spanned!(span=> ::cxx::private::RustVec #langle #elem #rangle) } + Type::RustOption(ty) => { + let span = ty.name.span(); + let langle = ty.langle; + let elem = expand_extern_type(&ty.inner, types, proper); + let rangle = ty.rangle; + quote_spanned!(span=> ::cxx::private::RustOption #langle #elem #rangle) + } Type::Ref(ty) => { let ampersand = ty.ampersand; let lifetime = &ty.lifetime; @@ -1792,6 +2046,13 @@ fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream { let rangle = ty.rangle; quote_spanned!(span=> #ampersand #lifetime #mutability ::cxx::private::RustVec #langle #inner #rangle) } + Type::RustOption(ty) => { + let span = ty.name.span(); + let langle = ty.langle; + let inner = expand_extern_type(&ty.inner, types, proper); + let rangle = ty.rangle; + quote_spanned!(span=> #ampersand #lifetime #mutability ::cxx::private::RustOption #langle #inner #rangle) + } inner if proper && types.is_considered_improper_ctype(inner) => { let star = Token![*](ampersand.span); match ty.mutable { diff --git a/src/cxx.cc b/src/cxx.cc index 2522d61aa..8d5fd25e3 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -745,6 +745,7 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), self->~weak_ptr(); \ } + // Usize and isize are the same type as one of the below. #define FOR_EACH_NUMERIC(MACRO) \ MACRO(u8, std::uint8_t) \ @@ -783,15 +784,48 @@ 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( \ + rust::Option *ptr) noexcept; \ + void cxxbridge1$rust_option$const$##RUST_TYPE##$drop( \ + rust::Option *ptr) noexcept; \ + void cxxbridge1$rust_option$##RUST_TYPE##$new( \ + rust::Option *ptr) noexcept; \ + void cxxbridge1$rust_option$##RUST_TYPE##$drop( \ + rust::Option *ptr) noexcept; + +#define RUST_OPTION_OPS(RUST_TYPE, CXX_TYPE) \ + template <> \ + Option::Option() noexcept { \ + cxxbridge1$rust_option$const$##RUST_TYPE##$new(this); \ + } \ + template <> \ + void Option::drop() noexcept { \ + cxxbridge1$rust_option$const$##RUST_TYPE##$drop(this); \ + } \ + template <> \ + Option::Option() noexcept { \ + cxxbridge1$rust_option$##RUST_TYPE##$new(this); \ + } \ + template <> \ + void Option::drop() noexcept { \ + cxxbridge1$rust_option$##RUST_TYPE##$drop(this); \ + } + +#define FOR_EACH_RUST_OPTION(MACRO) \ + FOR_EACH_NUMERIC(MACRO) + 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) } // extern "C" namespace rust { inline namespace cxxbridge1 { FOR_EACH_RUST_VEC(RUST_VEC_OPS) +FOR_EACH_RUST_OPTION(RUST_OPTION_OPS) } // namespace cxxbridge1 } // namespace rust diff --git a/src/lib.rs b/src/lib.rs index 8971bae99..0e35eeffe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -//! [![github]](https://github.com/dtolnay/cxx) [![crates-io]](https://crates.io/crates/cxx) [![docs-rs]](https://docs.rs/cxx) //! //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust @@ -459,6 +458,7 @@ mod lossy; pub mod memory; mod opaque; mod result; +mod rust_option; mod rust_slice; mod rust_str; mod rust_string; @@ -479,6 +479,7 @@ pub use crate::cxx_vector::CxxVector; #[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; @@ -510,10 +511,12 @@ pub mod private { pub use crate::opaque::Opaque; #[cfg(feature = "alloc")] pub use crate::result::{r#try, Result}; + pub use crate::rust_option::RustOption; pub use crate::rust_slice::RustSlice; pub use crate::rust_str::RustStr; #[cfg(feature = "alloc")] pub use crate::rust_string::RustString; + pub use crate::rust_type::ImplOption; pub use crate::rust_type::{ImplBox, ImplVec, RustType}; #[cfg(feature = "alloc")] pub use crate::rust_vec::RustVec; diff --git a/src/rust_option.rs b/src/rust_option.rs new file mode 100644 index 000000000..f5fe7f293 --- /dev/null +++ b/src/rust_option.rs @@ -0,0 +1,251 @@ +#![allow(missing_docs)] + +#[cfg(feature = "alloc")] +use alloc::boxed::Box; +use core::mem::ManuallyDrop; +use core::ops::Deref; +use core::pin::Pin; + +#[repr(C)] +union OptionInner { + value: ManuallyDrop, + empty: usize, +} + +// ABI compatible with C++ rust::Option (not necessarily core::option::Option). +#[repr(C)] +pub struct RustOption { + inner: OptionInner, +} + +trait OptionType {} + +impl OptionType for &T {} +impl OptionType for &mut T {} +impl OptionType for Pin<&mut T> {} +impl OptionType for *const T {} +impl OptionType for *mut T {} +#[cfg(feature = "alloc")] +impl OptionType for Box {} + +impl RustOption { + pub const fn new() -> Self { + struct __SizeCheck(core::marker::PhantomData); + impl __SizeCheck { + const _IS_NICHE: () = + assert!(core::mem::size_of::>() == core::mem::size_of::()); + const _IS_USIZE_SIZE: () = + assert!(core::mem::size_of::>() == core::mem::size_of::()); + } + // Force the constants to resolve (at compile time) + #[allow(clippy::let_unit_value)] + let _: () = __SizeCheck::::_IS_NICHE; + #[allow(clippy::let_unit_value)] + let _: () = __SizeCheck::::_IS_USIZE_SIZE; + RustOption { + inner: OptionInner { empty: 0 }, + } + } + + pub fn value(&self) -> Option<&T> { + if unsafe { self.inner.empty != 0 } { + unsafe { Some(self.inner.value.deref()) } + } else { + None + } + } + + pub unsafe fn into_inner_unchecked(mut self) -> T { + unsafe { ManuallyDrop::take(&mut self.inner.value) } + } +} + +impl<'a, T> RustOption<&'a T> { + pub const fn from_option_ref(other: Option<&'a T>) -> Self { + unsafe { core::mem::transmute::, RustOption<&'a T>>(other) } + } + + pub const fn into_option_ref(self) -> Option<&'a T> { + unsafe { core::mem::transmute::, Option<&'a T>>(self) } + } + + pub fn as_mut_option_ref(&mut self) -> &mut Option<&'a T> { + unsafe { &mut *(self as *mut RustOption<&'a T> as *mut Option<&'a T>) } + } +} + +impl<'a, T> RustOption<&'a mut T> { + pub fn from_option_mut(other: Option<&'a mut T>) -> Self { + unsafe { core::mem::transmute::, RustOption<&'a mut T>>(other) } + } + + pub fn into_option_mut(self) -> Option<&'a mut T> { + unsafe { core::mem::transmute::, Option<&'a mut T>>(self) } + } + + pub fn as_mut_option_mut(&mut self) -> &mut Option<&'a mut T> { + unsafe { &mut *(self as *mut RustOption<&'a mut T> as *mut Option<&'a mut T>) } + } +} + +impl<'a, T> RustOption> { + pub fn from_option_mut_pinned(other: Option>) -> Self { + unsafe { core::mem::transmute::>, RustOption>>(other) } + } + + pub fn into_option_mut_pinned(self) -> Option> { + unsafe { core::mem::transmute::>, Option>>(self) } + } + + pub fn as_mut_option_mut_pinned(&mut self) -> &mut Option> { + unsafe { &mut *(self as *mut RustOption> as *mut Option>) } + } + + pub fn into_option_mut_improper_pinned(self) -> Option> { + unsafe { core::mem::transmute::>, Option>>(self) } + } + + pub fn as_mut_option_mut_improper_pinned(&mut self) -> &mut Option> { + unsafe { &mut *(self as *mut RustOption> as *mut Option>) } + } +} + +impl RustOption<*const T> { + /// SAFETY: pointer must have been constructed as a Box + #[cfg(feature="alloc")] + pub const unsafe fn into_option_box(self) -> Option> { + unsafe { core::mem::transmute::, Option>>(self) } + } + + /// SAFETY: pointer must have been constructed as a Box + #[cfg(feature="alloc")] + pub unsafe fn as_mut_option_box(&mut self) -> &mut Option> { + unsafe { &mut *(self as *mut RustOption<*const T> as *mut Option>) } + } + + pub const fn into_option_ref<'a>(self) -> Option<&'a T> { + unsafe { core::mem::transmute::, Option<&'a T>>(self) } + } + + pub fn as_mut_option_ref<'a>(&mut self) -> &mut Option<&'a T> { + unsafe { &mut *(self as *mut RustOption<*const T> as *mut Option<&'a T>) } + } +} + +impl RustOption<*mut T> { + #[cfg(feature="alloc")] + pub const fn from_option_box(other: Option>) -> Self { + unsafe { core::mem::transmute::>, RustOption<*mut T>>(other) } + } + + /// SAFETY: pointer must have been constructed as a Box + #[cfg(feature="alloc")] + pub const unsafe fn into_option_box(self) -> Option> { + unsafe { core::mem::transmute::, Option>>(self) } + } + + /// SAFETY: pointer must have been constructed as a Box + #[cfg(feature="alloc")] + pub unsafe fn as_mut_option_box(&mut self) -> &mut Option> { + unsafe { &mut *(self as *mut RustOption<*mut T> as *mut Option>) } + } + + /// SAFETY: Pointer must not be aliased + pub unsafe fn into_option_mut<'a>(self) -> Option<&'a mut T> { + unsafe { core::mem::transmute::, Option<&'a mut T>>(self) } + } + + /// SAFETY: Pointer must not be aliased + pub unsafe fn as_mut_option_mut<'a>(&mut self) -> &mut Option<&'a mut T> { + unsafe { &mut *(self as *mut RustOption<*mut T> as *mut Option<&'a mut T>) } + } +} + +impl RustOption<*const core::ffi::c_void> { + pub const fn from_option_ref_improper<'a, T>(other: Option<&'a T>) -> Self { + unsafe { + core::mem::transmute::, RustOption<*const core::ffi::c_void>>(other) + } + } + + pub const fn from_option_ref_improper_pinned<'a, T>(other: Option>) -> Self { + unsafe { + core::mem::transmute::>, RustOption<*const core::ffi::c_void>>(other) + } + } + + /// SAFETY: Pointer must have been constructed as a Box + #[cfg(feature="alloc")] + pub const unsafe fn into_option_box_improper(self) -> Option> { + unsafe { + core::mem::transmute::, Option>>(self) + } + } + + /// SAFETY: Pointer must have been constructed as a Box + #[cfg(feature="alloc")] + pub unsafe fn as_mut_option_box_improper(&mut self) -> &mut Option> { + unsafe { &mut *(self as *mut RustOption<*const core::ffi::c_void> as *mut Option>) } + } + + pub const fn into_option_ref_improper<'a, T>(self) -> Option<&'a T> { + unsafe { core::mem::transmute::, Option<&'a T>>(self) } + } + + pub fn as_mut_option_ref_improper<'a, T>(&mut self) -> &mut Option<&'a T> { + unsafe { &mut *(self as *mut RustOption<*const core::ffi::c_void> as *mut Option<&'a T>) } + } +} + +impl RustOption<*mut core::ffi::c_void> { + #[cfg(feature="alloc")] + pub const fn from_option_box_improper(other: Option>) -> Self { + unsafe { core::mem::transmute::>, RustOption<*mut core::ffi::c_void>>(other) } + } + + pub fn from_option_mut_improper<'a, T>(other: Option<&'a mut T>) -> Self { + unsafe { + core::mem::transmute::, RustOption<*mut core::ffi::c_void>>(other) + } + } + + pub fn from_option_mut_improper_pinned<'a, T>(other: Option>) -> Self { + unsafe { + core::mem::transmute::>, RustOption<*mut core::ffi::c_void>>( + other, + ) + } + } + + /// SAFETY: Pointer must have been constructed as a Box + #[cfg(feature="alloc")] + pub const unsafe fn into_option_box_improper(self) -> Option> { + unsafe { core::mem::transmute::, Option>>(self) } + } + + /// SAFETY: Pointer must have been constructed as a Box + #[cfg(feature="alloc")] + pub unsafe fn as_mut_option_box_improper(&mut self) -> &mut Option> { + unsafe { &mut *(self as *mut RustOption<*mut core::ffi::c_void> as *mut Option>) } + } + + /// SAFETY: Pointer must not be aliased + pub unsafe fn into_option_mut_improper<'a, T>(self) -> Option<&'a mut T> { + unsafe { + core::mem::transmute::, Option<&'a mut T>>(self) + } + } + + /// SAFETY: Pointer must not be aliased + pub unsafe fn as_mut_option_mut_improper<'a, T>(&mut self) -> &mut Option<&'a mut T> { + unsafe { &mut *(self as *mut RustOption<*mut core::ffi::c_void> as *mut Option<&'a mut T>) } + } +} + +impl Drop for RustOption { + fn drop(&mut self) { + if unsafe { self.inner.empty != 0 } { + unsafe { ManuallyDrop::drop(&mut self.inner.value) } + } + } +} diff --git a/src/rust_type.rs b/src/rust_type.rs index eacb5309f..142673820 100644 --- a/src/rust_type.rs +++ b/src/rust_type.rs @@ -3,3 +3,5 @@ pub unsafe trait RustType {} pub unsafe trait ImplBox {} pub unsafe trait ImplVec {} + +pub unsafe trait ImplOption {} diff --git a/src/symbols/mod.rs b/src/symbols/mod.rs index e00bb550e..fe88196a4 100644 --- a/src/symbols/mod.rs +++ b/src/symbols/mod.rs @@ -1,4 +1,5 @@ mod exception; +mod rust_option; mod rust_slice; mod rust_str; mod rust_string; diff --git a/src/symbols/rust_option.rs b/src/symbols/rust_option.rs new file mode 100644 index 000000000..ff3448c48 --- /dev/null +++ b/src/symbols/rust_option.rs @@ -0,0 +1,55 @@ +use crate::c_char::c_char; +use crate::rust_option::RustOption; +use core::mem; +use core::ptr; + +macro_rules! rust_option_shims { + ($segment:expr, $ty:ty) => { + const_assert_eq!( + mem::size_of::>(), + mem::size_of::>() + ); + 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::new()) }; + } + #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$drop")] + unsafe extern "C" fn __const_drop(this: *mut RustOption<&$ty>) { + unsafe { ptr::drop_in_place(this) } + } + #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$new")] + unsafe extern "C" fn __new(this: *mut RustOption<&mut $ty>) { + unsafe { ptr::write(this, RustOption::new()) } + } + #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$drop")] + unsafe extern "C" fn __drop(this: *mut RustOption<&mut $ty>) { + unsafe { ptr::drop_in_place(this) } + } + }; + }; +} + +macro_rules! rust_option_shims_for_primitive { + ($ty:ident) => { + rust_option_shims!(stringify!($ty), $ty); + }; +} + +rust_option_shims_for_primitive!(u8); +rust_option_shims_for_primitive!(bool); +rust_option_shims_for_primitive!(u16); +rust_option_shims_for_primitive!(u32); +rust_option_shims_for_primitive!(u64); +rust_option_shims_for_primitive!(usize); +rust_option_shims_for_primitive!(i8); +rust_option_shims_for_primitive!(i16); +rust_option_shims_for_primitive!(i32); +rust_option_shims_for_primitive!(i64); +rust_option_shims_for_primitive!(isize); +rust_option_shims_for_primitive!(f32); +rust_option_shims_for_primitive!(f64); + +rust_option_shims!("char", c_char); diff --git a/syntax/check.rs b/syntax/check.rs index b5fd45e11..9ccdd6f90 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -47,6 +47,7 @@ fn do_typecheck(cx: &mut Check) { Type::Ident(ident) => check_type_ident(cx, ident), Type::RustBox(ptr) => check_type_box(cx, ptr), Type::RustVec(ty) => check_type_rust_vec(cx, ty), + Type::RustOption(ty) => check_type_rust_option(cx, ty), Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), Type::SharedPtr(ptr) => check_type_shared_ptr(cx, ptr), Type::WeakPtr(ptr) => check_type_weak_ptr(cx, ptr), @@ -93,20 +94,23 @@ fn check_type_ident(cx: &mut Check, name: &NamedType) { } fn check_type_box(cx: &mut Check, ptr: &Ty1) { - if let Type::Ident(ident) = &ptr.inner { - if cx.types.cxx.contains(&ident.rust) - && !cx.types.aliases.contains_key(&ident.rust) - && !cx.types.structs.contains_key(&ident.rust) - && !cx.types.enums.contains_key(&ident.rust) - { - cx.error(ptr, error::BOX_CXX_TYPE.msg); - } + match &ptr.inner { + Type::Ident(ident) => { + if cx.types.cxx.contains(&ident.rust) + && !cx.types.aliases.contains_key(&ident.rust) + && !cx.types.structs.contains_key(&ident.rust) + && !cx.types.enums.contains_key(&ident.rust) + { + cx.error(ptr, error::BOX_CXX_TYPE.msg); + } - if Atom::from(&ident.rust).is_none() { - return; + if Atom::from(&ident.rust).is_none() { + return; + } } + Type::RustOption(_) => return, + _ => {} } - cx.error(ptr, "unsupported target type of Box"); } @@ -138,6 +142,16 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { cx.error(ty, "unsupported element type of Vec"); } +fn check_type_rust_option(cx: &mut Check, ty: &Ty1) { + match &ty.inner { + Type::RustBox(_) => return, + Type::Ref(_) => return, + _ => {} + } + + cx.error(ty, "unsupported element type of Option"); +} + fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { if cx.types.rust.contains(&ident.rust) { @@ -523,6 +537,7 @@ fn check_api_impl(cx: &mut Check, imp: &Impl) { match ty { Type::RustBox(ty) | Type::RustVec(ty) + | Type::RustOption(ty) | Type::UniquePtr(ty) | Type::SharedPtr(ty) | Type::WeakPtr(ty) @@ -609,6 +624,7 @@ fn check_reserved_name(cx: &mut Check, ident: &Ident) { || ident == "WeakPtr" || ident == "Vec" || ident == "CxxVector" + || ident == "Option" || ident == "str" || Atom::from(ident).is_some() { @@ -651,6 +667,7 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { Type::CxxVector(_) | Type::Fn(_) | Type::Void(_) => true, Type::RustBox(_) | Type::RustVec(_) + | Type::RustOption(_) | Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) @@ -725,6 +742,7 @@ fn describe(cx: &mut Check, ty: &Type) -> String { } Type::RustBox(_) => "Box".to_owned(), Type::RustVec(_) => "Vec".to_owned(), + Type::RustOption(_) => "Option".to_owned(), Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::SharedPtr(_) => "shared_ptr".to_owned(), Type::WeakPtr(_) => "weak_ptr".to_owned(), diff --git a/syntax/impls.rs b/syntax/impls.rs index 36e1f322a..506967648 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -53,6 +53,7 @@ impl Hash for Type { Type::Str(t) => t.hash(state), Type::RustVec(t) => t.hash(state), Type::CxxVector(t) => t.hash(state), + Type::RustOption(t) => t.hash(state), Type::Fn(t) => t.hash(state), Type::SliceRef(t) => t.hash(state), Type::Array(t) => t.hash(state), diff --git a/syntax/improper.rs b/syntax/improper.rs index a19f5b7d6..e411c8ff6 100644 --- a/syntax/improper.rs +++ b/syntax/improper.rs @@ -24,6 +24,7 @@ impl<'a> Types<'a> { } Type::RustBox(_) | Type::RustVec(_) + | Type::RustOption(_) | Type::Str(_) | Type::Fn(_) | Type::Void(_) diff --git a/syntax/instantiate.rs b/syntax/instantiate.rs index dda306982..5b55d0eae 100644 --- a/syntax/instantiate.rs +++ b/syntax/instantiate.rs @@ -7,12 +7,20 @@ use syn::Token; pub(crate) enum ImplKey<'a> { RustBox(NamedImplKey<'a>), RustVec(NamedImplKey<'a>), + RustOption(OptionInner<'a>), UniquePtr(NamedImplKey<'a>), SharedPtr(NamedImplKey<'a>), WeakPtr(NamedImplKey<'a>), CxxVector(NamedImplKey<'a>), } +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) enum OptionInner<'a> { + RustBox(NamedImplKey<'a>), + Ref(NamedImplKey<'a>), + MutRef(NamedImplKey<'a>), +} + #[derive(Copy, Clone)] pub(crate) struct NamedImplKey<'a> { #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build @@ -36,6 +44,33 @@ impl Type { if let Type::Ident(ident) = &ty.inner { return Some(ImplKey::RustVec(NamedImplKey::new(ty, ident))); } + } else if let Type::RustOption(ty) = self { + match &ty.inner { + Type::RustBox(_) => { + let impl_key = ty.inner.impl_key()?; + match impl_key { + ImplKey::RustBox(named) => { + return Some(ImplKey::RustOption(OptionInner::RustBox(named))) + } + _ => unreachable!(), + } + } + Type::Ref(r) => match &r.inner { + Type::Ident(ident) => { + if r.mutable { + return Some(ImplKey::RustOption(OptionInner::MutRef( + NamedImplKey::new(ty, ident), + ))); + } else { + return Some(ImplKey::RustOption(OptionInner::Ref(NamedImplKey::new( + ty, ident, + )))); + } + } + _ => {} + }, + _ => {} + } } else if let Type::UniquePtr(ty) = self { if let Type::Ident(ident) = &ty.inner { return Some(ImplKey::UniquePtr(NamedImplKey::new(ty, ident))); diff --git a/syntax/mod.rs b/syntax/mod.rs index 5ff343b4d..0ee2a85aa 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -263,6 +263,7 @@ pub(crate) enum Type { Ident(NamedType), RustBox(Box), RustVec(Box), + RustOption(Box), UniquePtr(Box), SharedPtr(Box), WeakPtr(Box), diff --git a/syntax/parse.rs b/syntax/parse.rs index 850dcc8d1..95fc30a67 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1063,6 +1063,7 @@ fn parse_impl(cx: &mut Errors, imp: ItemImpl) -> Result { let ty_generics = match &ty { Type::RustBox(ty) | Type::RustVec(ty) + | Type::RustOption(ty) | Type::UniquePtr(ty) | Type::SharedPtr(ty) | Type::WeakPtr(ty) @@ -1281,6 +1282,20 @@ fn parse_type_path(ty: &TypePath) -> Result { rangle: generic.gt_token, }))); } + } else if ident == "Option" { + if generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + let inner = parse_type(arg)?; + return Ok(Type::RustOption(Box::new(Ty1 { + name: ident, + langle: generic.lt_token, + inner, + rangle: generic.gt_token, + }))); + } + } else { + panic!("{}", generic.args.len()); + } } else if ident == "Pin" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; diff --git a/syntax/pod.rs b/syntax/pod.rs index 506e53cb5..de6e45834 100644 --- a/syntax/pod.rs +++ b/syntax/pod.rs @@ -24,6 +24,7 @@ impl<'a> Types<'a> { } Type::RustBox(_) | Type::RustVec(_) + | Type::RustOption(_) | Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) diff --git a/syntax/resolve.rs b/syntax/resolve.rs index b0a4782c3..375b4e1cb 100644 --- a/syntax/resolve.rs +++ b/syntax/resolve.rs @@ -13,7 +13,9 @@ impl<'a> Types<'a> { let ident = ident.ident(); match self.try_resolve(ident) { Some(resolution) => resolution, - None => panic!("Unable to resolve type `{}`", ident), + None => { + panic!("Unable to resolve type `{}`", ident) + } } } diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 05eddc703..d10de4c30 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -28,7 +28,8 @@ impl ToTokens for Type { | Type::SharedPtr(ty) | Type::WeakPtr(ty) | Type::CxxVector(ty) - | Type::RustVec(ty) => ty.to_tokens(tokens), + | Type::RustVec(ty) + | Type::RustOption(ty) => ty.to_tokens(tokens), Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), Type::Ptr(p) => p.to_tokens(tokens), Type::Array(a) => a.to_tokens(tokens), @@ -75,6 +76,9 @@ impl ToTokens for Ty1 { "Vec" => { tokens.extend(quote_spanned!(span=> ::cxx::alloc::vec::)); } + "Option" => { + tokens.extend(quote_spanned!(span=> ::std::option::)); + } _ => {} } name.to_tokens(tokens); diff --git a/syntax/types.rs b/syntax/types.rs index 623a8b8d6..96325a49b 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -176,14 +176,15 @@ impl<'a> Types<'a> { None => continue, }; let implicit_impl = match impl_key { - ImplKey::RustBox(ident) - | ImplKey::RustVec(ident) + ImplKey::RustVec(ident) + | ImplKey::RustBox(ident) | ImplKey::UniquePtr(ident) | ImplKey::SharedPtr(ident) | ImplKey::WeakPtr(ident) | ImplKey::CxxVector(ident) => { Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust) } + ImplKey::RustOption(_) => true, }; if implicit_impl && !impls.contains_key(&impl_key) { impls.insert(impl_key, None); diff --git a/syntax/visit.rs b/syntax/visit.rs index e31b8c41b..16c097901 100644 --- a/syntax/visit.rs +++ b/syntax/visit.rs @@ -17,7 +17,8 @@ where | Type::SharedPtr(ty) | Type::WeakPtr(ty) | Type::CxxVector(ty) - | Type::RustVec(ty) => visitor.visit_type(&ty.inner), + | Type::RustVec(ty) + | Type::RustOption(ty) => visitor.visit_type(&ty.inner), Type::Ref(r) => visitor.visit_type(&r.inner), Type::Ptr(p) => visitor.visit_type(&p.inner), Type::Array(a) => visitor.visit_type(&a.inner), diff --git a/tests/compiletest.rs b/tests/compiletest.rs index cd58514f1..84e8e06c7 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -4,6 +4,11 @@ #[cfg_attr(miri, ignore)] #[test] fn ui() { - let t = trybuild::TestCases::new(); - t.compile_fail("tests/ui/*.rs"); + let check = trybuild::TestCases::new(); + check.compile_fail("tests/ui/*.rs"); + // Tests that require cargo build instead of cargo check + let build = trybuild::TestCases::new(); + // Having passing cases forces cargo build instead of cargo check + build.pass("tests/ui_pass/*.rs"); + build.compile_fail("tests/ui_build/*.rs"); } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 41ba03184..74198bc13 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -21,6 +21,7 @@ use cxx::{type_id, CxxString, CxxVector, ExternType, SharedPtr, UniquePtr}; use std::fmt::{self, Display}; use std::mem::MaybeUninit; use std::os::raw::c_char; +use std::pin::Pin; #[cxx::bridge(namespace = "tests")] pub mod ffi { @@ -121,6 +122,15 @@ pub mod ffi { fn c_return_mut_rust_vec(c: Pin<&mut C>) -> &mut Vec; fn c_return_rust_vec_string() -> Vec; fn c_return_rust_vec_bool() -> Vec; + fn c_return_rust_option_box() -> Option>; + fn c_return_rust_ref_option_shared() -> Option<&'static Shared>; + fn c_return_rust_mut_option_shared() -> Option<&'static mut Shared>; + fn c_return_rust_pin_mut_option_shared() -> Option>; + fn c_return_rust_ref_option_opaque() -> Option<&'static C>; + fn c_return_rust_pin_mut_option_opaque() -> Option>; + fn c_return_rust_ref_option_native() -> Option<&'static u8>; + fn c_return_rust_mut_option_native() -> Option<&'static mut u8>; + fn c_return_rust_pin_mut_option_native() -> Option>; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; fn c_return_enum(n: u16) -> Enum; @@ -163,6 +173,17 @@ pub mod ffi { fn c_take_ref_rust_vec_string(v: &Vec); fn c_take_ref_rust_vec_index(v: &Vec); fn c_take_ref_rust_vec_copy(v: &Vec); + fn c_take_rust_option_box(rust: Option>); + fn c_take_rust_ref_option_shared(rust: Option<&Shared>); + fn c_take_rust_mut_option_shared(rust: Option<&mut Shared>); + fn c_take_rust_pin_mut_option_shared(rust: Option>); + fn c_take_rust_ref_option_opaque(rust: Option<&R>); + fn c_take_rust_mut_option_opaque(rust: Option<&mut R>); + fn c_take_rust_pin_mut_option_opaque(rust: Option>); + fn c_take_rust_ref_option_native(rust: Option<&u8>); + fn c_take_rust_mut_option_native(rust: Option<&mut u8>); + fn c_take_rust_pin_mut_option_native(rust: Option>); + fn c_take_ref_shared_string(s: &SharedString) -> &SharedString; fn c_take_callback(callback: fn(String) -> usize); fn c_take_callback_ref(callback: fn(&String)); @@ -192,6 +213,15 @@ pub mod ffi { fn c_try_return_rust_vec() -> Result>; fn c_try_return_rust_vec_string() -> Result>; fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec>; + fn c_try_return_rust_option_box() -> Result>>; + fn c_try_return_rust_ref_option_shared() -> Result>; + fn c_try_return_rust_mut_option_shared() -> Result>; + fn c_try_return_rust_pin_mut_option_shared() -> Result>>; + fn c_try_return_rust_ref_option_opaque() -> Result>; + fn c_try_return_rust_pin_mut_option_opaque() -> Result>>; + fn c_try_return_rust_ref_option_native() -> Result>; + fn c_try_return_rust_mut_option_native() -> Result>; + fn c_try_return_rust_pin_mut_option_native() -> Result>>; fn get(self: &C) -> usize; fn set(self: Pin<&mut C>, n: usize) -> usize; @@ -277,6 +307,24 @@ pub mod ffi { fn r_return_rust_vec_extern_struct() -> Vec; fn r_return_ref_rust_vec(shared: &Shared) -> &Vec; fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec; + fn r_return_rust_option_box() -> Option>; + unsafe fn r_return_rust_option_ref_shared<'a>(shared: &'a Shared) -> Option<&'a Shared>; + unsafe fn r_return_rust_option_mut_shared<'a>( + shared: &'a mut Shared, + ) -> Option<&'a mut Shared>; + unsafe fn r_return_rust_option_pin_mut_shared<'a>( + shared: Pin<&'a mut Shared>, + ) -> Option>; + unsafe fn r_return_rust_option_ref_opaque<'a>(opaque: &'a R) -> Option<&'a R>; + unsafe fn r_return_rust_option_mut_opaque<'a>(opaque: &'a mut R) -> Option<&'a mut R>; + unsafe fn r_return_rust_option_pin_mut_opaque<'a>( + opaque: Pin<&'a mut R>, + ) -> Option>; + unsafe fn r_return_rust_option_ref_native<'a>(native: &'a u8) -> Option<&'a u8>; + unsafe fn r_return_rust_option_mut_native<'a>(native: &'a mut u8) -> Option<&'a mut u8>; + unsafe fn r_return_rust_option_pin_mut_native<'a>( + native: Pin<&'a mut u8>, + ) -> Option>; fn r_return_identity(_: usize) -> usize; fn r_return_sum(_: usize, _: usize) -> usize; fn r_return_enum(n: u32) -> Enum; @@ -298,11 +346,46 @@ pub mod ffi { fn r_take_rust_vec_string(v: Vec); fn r_take_ref_rust_vec(v: &Vec); fn r_take_ref_rust_vec_string(v: &Vec); + fn r_take_rust_option_box(o: Option>); + fn r_take_rust_option_ref_shared(o: Option<&Shared>); + fn r_take_rust_option_mut_shared(o: Option<&mut Shared>); + fn r_take_rust_option_pin_mut_shared(o: Option>); + fn r_take_rust_option_ref_opaque(o: Option<&C>); + fn r_take_rust_option_pin_mut_opaque(o: Option>); + fn r_take_rust_option_ref_native(o: Option<&u8>); + fn r_take_rust_option_mut_native(o: Option<&mut u8>); + fn r_take_rust_option_pin_mut_native(o: Option>); + fn r_take_enum(e: Enum); fn r_try_return_void() -> Result<()>; fn r_try_return_primitive() -> Result; fn r_try_return_box() -> Result>; + fn r_try_return_rust_option_box() -> Result>>; + unsafe fn r_try_return_rust_option_ref_shared<'a>( + shared: &'a Shared, + ) -> Result>; + unsafe fn r_try_return_rust_option_mut_shared<'a>( + shared: &'a mut Shared, + ) -> Result>; + unsafe fn r_try_return_rust_option_pin_mut_shared<'a>( + shared: Pin<&'a mut Shared>, + ) -> Result>>; + unsafe fn r_try_return_rust_option_ref_opaque<'a>(opaque: &'a R) -> Result>; + unsafe fn r_try_return_rust_option_mut_opaque<'a>( + opaque: &'a mut R, + ) -> Result>; + unsafe fn r_try_return_rust_option_pin_mut_opaque<'a>( + opaque: Pin<&'a mut R>, + ) -> Result>>; + unsafe fn r_try_return_rust_option_ref_native<'a>(native: &'a u8) + -> Result>; + unsafe fn r_try_return_rust_option_mut_native<'a>( + native: &'a mut u8, + ) -> Result>; + unsafe fn r_try_return_rust_option_pin_mut_native<'a>( + native: Pin<&'a mut u8>, + ) -> Result>>; fn r_fail_return_primitive() -> Result; fn r_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>; fn r_try_return_mutsliceu8(s: &mut [u8]) -> Result<&mut [u8]>; @@ -527,6 +610,54 @@ fn r_return_mut_rust_vec(shared: &mut ffi::Shared) -> &mut Vec { unimplemented!() } +fn r_return_rust_option_box() -> Option> { + Some(Box::new(ffi::Shared { z: 42 })) +} + +unsafe fn r_return_rust_option_ref_shared<'a>(shared: &'a ffi::Shared) -> Option<&'a ffi::Shared> { + Some(shared) +} + +unsafe fn r_return_rust_option_mut_shared<'a>( + shared: &'a mut ffi::Shared, +) -> Option<&'a mut ffi::Shared> { + Some(shared) +} + +unsafe fn r_return_rust_option_pin_mut_shared<'a>( + shared: Pin<&'a mut ffi::Shared>, +) -> Option> { + Some(shared) +} + +unsafe fn r_return_rust_option_ref_opaque<'a>(opaque: &'a R) -> Option<&'a R> { + Some(opaque) +} + +unsafe fn r_return_rust_option_mut_opaque<'a>(opaque: &'a mut R) -> Option<&'a mut R> { + Some(opaque) +} + +unsafe fn r_return_rust_option_pin_mut_opaque<'a>( + opaque: Pin<&'a mut R>, +) -> Option> { + Some(opaque) +} + +unsafe fn r_return_rust_option_ref_native<'a>(native: &'a u8) -> Option<&'a u8> { + Some(native) +} + +unsafe fn r_return_rust_option_mut_native<'a>(native: &'a mut u8) -> Option<&'a mut u8> { + Some(native) +} + +unsafe fn r_return_rust_option_pin_mut_native<'a>( + native: Pin<&'a mut u8>, +) -> Option> { + Some(native) +} + fn r_return_identity(n: usize) -> usize { n } @@ -617,6 +748,44 @@ fn r_take_ref_rust_vec_string(v: &Vec) { let _ = v; } +fn r_take_rust_option_box(o: Option>) { + let _ = o; +} + +fn r_take_rust_option_ref_shared(o: Option<&ffi::Shared>) { + let _ = o; +} + +fn r_take_rust_option_mut_shared(o: Option<&mut ffi::Shared>) { + let _ = o; +} + +fn r_take_rust_option_pin_mut_shared(o: Option>) { + let _ = o; +} + +fn r_take_rust_option_ref_opaque(o: Option<&ffi::C>) { + let _ = o; +} + +fn r_take_rust_option_pin_mut_opaque(o: Option>) { + let _ = o; +} + +fn r_take_rust_option_ref_native(o: Option<&u8>) { + let _ = o; +} + +fn r_take_rust_option_mut_native(o: Option<&mut u8>) { + if let Some(x) = o { + *x += 1; + } +} + +fn r_take_rust_option_pin_mut_native(o: Option>) { + let _ = o; +} + fn r_take_enum(e: ffi::Enum) { let _ = e; } @@ -633,6 +802,60 @@ fn r_try_return_box() -> Result, Error> { Ok(Box::new(R(2020))) } +fn r_try_return_rust_option_box() -> Result>, Error> { + Ok(Some(Box::new(ffi::Shared { z: 42 }))) +} + +unsafe fn r_try_return_rust_option_ref_shared<'a>( + shared: &'a ffi::Shared, +) -> Result, Error> { + Ok(Some(shared)) +} + +unsafe fn r_try_return_rust_option_mut_shared<'a>( + shared: &'a mut ffi::Shared, +) -> Result, Error> { + Ok(Some(shared)) +} + +unsafe fn r_try_return_rust_option_pin_mut_shared<'a>( + shared: Pin<&'a mut ffi::Shared>, +) -> Result>, Error> { + Ok(Some(shared)) +} + +unsafe fn r_try_return_rust_option_ref_opaque<'a>(opaque: &'a R) -> Result, Error> { + Ok(Some(opaque)) +} + +unsafe fn r_try_return_rust_option_mut_opaque<'a>( + opaque: &'a mut R, +) -> Result, Error> { + Ok(Some(opaque)) +} + +unsafe fn r_try_return_rust_option_pin_mut_opaque<'a>( + opaque: Pin<&'a mut R>, +) -> Result>, Error> { + Ok(Some(opaque)) +} + +unsafe fn r_try_return_rust_option_ref_native<'a>(native: &'a u8) -> Result, Error> { + Ok(Some(native)) +} + +unsafe fn r_try_return_rust_option_mut_native<'a>( + native: &'a mut u8, +) -> Result, Error> { + Ok(Some(native)) +} + +unsafe fn r_try_return_rust_option_pin_mut_native<'a>( + native: Pin<&'a mut u8>, +) -> Result>, Error> { + Ok(Some(native)) +} + fn r_fail_return_primitive() -> Result { Err(Error) } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 8cf74bebb..bed030773 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -181,6 +181,42 @@ rust::Vec c_return_rust_vec_string() { rust::Vec c_return_rust_vec_bool() { return {true, true, false}; } +rust::Option> c_return_rust_option_box() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_return_rust_ref_option_shared() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_return_rust_mut_option_shared() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_return_rust_pin_mut_option_shared() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_return_rust_ref_option_opaque() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_return_rust_pin_mut_option_opaque() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_return_rust_ref_option_native() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_return_rust_mut_option_native() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_return_rust_pin_mut_option_native() { + throw std::runtime_error("unimplemented"); +} + size_t c_return_identity(size_t n) { return n; } size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; } @@ -484,6 +520,7 @@ void c_take_ref_rust_vec(const rust::Vec &v) { } } + void c_take_ref_rust_vec_string(const rust::Vec &v) { (void)v; cxx_test_suite_set_correct(); @@ -508,6 +545,70 @@ void c_take_ref_rust_vec_copy(const rust::Vec &v) { } } +void c_take_rust_option_box(rust::Option> opt) { + if (opt.has_value() && opt.value()->z == 42) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_ref_option_shared(rust::Option opt) { + if (opt.has_value() && opt.value()->z == 42) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_mut_option_shared(rust::Option opt) { + if (opt.has_value() && opt.value()->z == 42) { + opt.value()->z = 43; + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_pin_mut_option_shared(rust::Option opt) { + if (opt.has_value() && opt.value()->z == 42) { + opt.value()->z = 43; + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_ref_option_opaque(rust::Option opt) { + if (opt.has_value()) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_mut_option_opaque(rust::Option opt) { + if (opt.has_value()) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_pin_mut_option_opaque(rust::Option opt) { + if (opt.has_value()) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_ref_option_native(rust::Option opt) { + if (opt.has_value() && *(opt.value()) == 42) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_mut_option_native(rust::Option opt) { + if (opt.has_value() && *(opt.value()) == 42) { + *(opt.value()) = 43; + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_pin_mut_option_native(rust::Option opt) { + if (opt.has_value() && *(opt.value()) == 42) { + *(opt.value()) = 43; + 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(); @@ -563,6 +664,42 @@ size_t c_fail_return_primitive() { throw std::logic_error("logic error"); } rust::Box c_try_return_box() { return c_return_box(); } +rust::Option> c_try_return_rust_option_box() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_try_return_rust_ref_option_shared() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_try_return_rust_mut_option_shared() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_try_return_rust_pin_mut_option_shared() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_try_return_rust_ref_option_opaque() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_try_return_rust_pin_mut_option_opaque() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_try_return_rust_ref_option_native() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_try_return_rust_mut_option_native() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_try_return_rust_pin_mut_option_native() { + throw std::runtime_error("unimplemented"); +} + const rust::String &c_try_return_ref(const rust::String &s) { return s; } rust::Str c_try_return_str(rust::Str s) { return s; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index dc02e4ff8..35fe0049f 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -116,6 +116,15 @@ const rust::Vec &c_return_ref_rust_vec(const C &c); rust::Vec &c_return_mut_rust_vec(C &c); rust::Vec c_return_rust_vec_string(); rust::Vec c_return_rust_vec_bool(); +rust::Option> c_return_rust_option_box(); +rust::Option c_return_rust_ref_option_shared(); +rust::Option c_return_rust_mut_option_shared(); +rust::Option c_return_rust_pin_mut_option_shared(); +rust::Option c_return_rust_ref_option_opaque(); +rust::Option c_return_rust_pin_mut_option_opaque(); +rust::Option c_return_rust_ref_option_native(); +rust::Option c_return_rust_mut_option_native(); +rust::Option c_return_rust_pin_mut_option_native(); 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); @@ -164,6 +173,16 @@ void c_take_ref_rust_vec(const rust::Vec &v); void c_take_ref_rust_vec_string(const rust::Vec &v); void c_take_ref_rust_vec_index(const rust::Vec &v); void c_take_ref_rust_vec_copy(const rust::Vec &v); +void c_take_rust_option_box(rust::Option>); +void c_take_rust_ref_option_shared(rust::Option); +void c_take_rust_mut_option_shared(rust::Option); +void c_take_rust_pin_mut_option_shared(rust::Option); +void c_take_rust_ref_option_opaque(rust::Option); +void c_take_rust_mut_option_opaque(rust::Option); +void c_take_rust_pin_mut_option_opaque(rust::Option); +void c_take_rust_ref_option_native(rust::Option); +void c_take_rust_mut_option_native(rust::Option); +void c_take_rust_pin_mut_option_native(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); @@ -187,6 +206,15 @@ std::unique_ptr c_try_return_unique_ptr_string(); rust::Vec c_try_return_rust_vec(); rust::Vec c_try_return_rust_vec_string(); const rust::Vec &c_try_return_ref_rust_vec(const C &c); +rust::Option> c_try_return_rust_option_box(); +rust::Option c_try_return_rust_ref_option_shared(); +rust::Option c_try_return_rust_mut_option_shared(); +rust::Option c_try_return_rust_pin_mut_option_shared(); +rust::Option c_try_return_rust_ref_option_opaque(); +rust::Option c_try_return_rust_pin_mut_option_opaque(); +rust::Option c_try_return_rust_ref_option_native(); +rust::Option c_try_return_rust_mut_option_native(); +rust::Option c_try_return_rust_pin_mut_option_native(); size_t c_get_use_count(const std::weak_ptr &weak) noexcept; diff --git a/tests/test.rs b/tests/test.rs index 6ef9a8293..f85548e01 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -15,6 +15,7 @@ use cxx_test_suite::module::ffi2; use cxx_test_suite::{cast, ffi, R}; use std::cell::Cell; use std::ffi::CStr; +use std::pin::Pin; thread_local! { static CORRECT: Cell = Cell::new(false); @@ -199,6 +200,29 @@ fn test_c_take() { nested_ns_shared_test_vec )); + check!(ffi::c_take_rust_option_box(Some(Box::new(ffi::Shared { + z: 42 + })))); + check!(ffi::c_take_rust_ref_option_shared(Some(&ffi::Shared { + z: 42 + }))); + check!(ffi::c_take_rust_mut_option_shared(Some(&mut ffi::Shared { + z: 42 + }))); + check!(ffi::c_take_rust_pin_mut_option_shared(Some(Pin::new( + &mut ffi::Shared { z: 42 } + )))); + check!(ffi::c_take_rust_ref_option_opaque(Some(&R(42)))); + check!(ffi::c_take_rust_mut_option_opaque(Some(&mut R(42)))); + check!(ffi::c_take_rust_pin_mut_option_opaque(Some(Pin::new( + &mut R(42) + )))); + check!(ffi::c_take_rust_ref_option_native(Some(&42))); + check!(ffi::c_take_rust_mut_option_native(Some(&mut 42))); + check!(ffi::c_take_rust_pin_mut_option_native(Some(Pin::new( + &mut 42 + )))); + check!(ffi::c_take_enum(ffi::Enum::AVal)); check!(ffi::c_take_ns_enum(ffi::AEnum::AAVal)); check!(ffi::c_take_nested_ns_enum(ffi::ABEnum::ABAVal)); diff --git a/tests/ui/option_slice.rs b/tests/ui/option_slice.rs new file mode 100644 index 000000000..8379b6383 --- /dev/null +++ b/tests/ui/option_slice.rs @@ -0,0 +1,12 @@ +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn f() -> Option<&'static [u8]>; + } +} + +fn f() -> Option<&'static [u8]> { + unimplemented!() +} + +fn main() {} diff --git a/tests/ui/option_slice.stderr b/tests/ui/option_slice.stderr new file mode 100644 index 000000000..0e94e313e --- /dev/null +++ b/tests/ui/option_slice.stderr @@ -0,0 +1,5 @@ +error: unsupported element type of Option + --> tests/ui/option_slice.rs:4:19 + | +4 | fn f() -> Option<&'static [u8]>; + | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/option_str.rs b/tests/ui/option_str.rs new file mode 100644 index 000000000..4bf3ec7d9 --- /dev/null +++ b/tests/ui/option_str.rs @@ -0,0 +1,12 @@ +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn f() -> Option<&'static str>; + } +} + +fn f() -> Option<&'static str> { + unimplemented!() +} + +fn main() {} diff --git a/tests/ui/option_str.stderr b/tests/ui/option_str.stderr new file mode 100644 index 000000000..5b156cb66 --- /dev/null +++ b/tests/ui/option_str.stderr @@ -0,0 +1,5 @@ +error: unsupported element type of Option + --> tests/ui/option_str.rs:4:19 + | +4 | fn f() -> Option<&'static str>; + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/option_string.rs b/tests/ui/option_string.rs new file mode 100644 index 000000000..88ed98f62 --- /dev/null +++ b/tests/ui/option_string.rs @@ -0,0 +1,12 @@ +#[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 new file mode 100644 index 000000000..5245ee5c6 --- /dev/null +++ b/tests/ui/option_string.stderr @@ -0,0 +1,5 @@ +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 new file mode 100644 index 000000000..20ba0dfaa --- /dev/null +++ b/tests/ui/option_vec.rs @@ -0,0 +1,12 @@ +#[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 new file mode 100644 index 000000000..82c46eb83 --- /dev/null +++ b/tests/ui/option_vec.stderr @@ -0,0 +1,5 @@ +error: unsupported element type of Option + --> tests/ui/option_vec.rs:4:19 + | +4 | fn f() -> Option>; + | ^^^^^^^^^^^^^^^ diff --git a/tests/ui_build/option_not_sized.rs b/tests/ui_build/option_not_sized.rs new file mode 100644 index 000000000..f0294e4ef --- /dev/null +++ b/tests/ui_build/option_not_sized.rs @@ -0,0 +1,5 @@ +fn foo() { + const _: cxx::RustOption::<&dyn core::fmt::Debug> = cxx::RustOption::<&dyn core::fmt::Debug>::new(); +} + +fn main() {} diff --git a/tests/ui_build/option_not_sized.stderr b/tests/ui_build/option_not_sized.stderr new file mode 100644 index 000000000..3e8701904 --- /dev/null +++ b/tests/ui_build/option_not_sized.stderr @@ -0,0 +1,13 @@ +error[E0080]: evaluation of `cxx::RustOption::::new::__SizeCheck::<&dyn std::fmt::Debug>::_IS_USIZE_SIZE` failed + --> src/rust_option.rs + | + | assert!(core::mem::size_of::>() == core::mem::size_of::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: core::mem::size_of::>() == core::mem::size_of::()', $DIR/src/rust_option.rs:38:17 + | + = 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_USIZE_SIZE; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui_pass/empty.rs b/tests/ui_pass/empty.rs new file mode 100644 index 000000000..081a61102 --- /dev/null +++ b/tests/ui_pass/empty.rs @@ -0,0 +1,4 @@ +// This file only exists so that we have a passing example which changes trybuild +/// to use cargo build instead of cargo check. Some of the tests only fail with a +/// full build +fn main() {}