From da8358f6cf409a0d080a4e940dda243ae5c6e975 Mon Sep 17 00:00:00 2001 From: kuecks Date: Wed, 7 Jul 2021 17:38:52 +0000 Subject: [PATCH 1/3] Add bindings for Rust std::option::Option --- gen/src/builtin.rs | 1 + gen/src/write.rs | 170 +++++++++++- include/cxx.h | 87 ++++++ macro/src/derive.rs | 2 +- macro/src/expand.rs | 280 +++++++++++++++++++- src/cxx.cc | 71 +++++ src/lib.rs | 5 + src/rust_option.rs | 349 +++++++++++++++++++++++++ src/rust_type.rs | 2 + src/symbols/mod.rs | 1 + src/symbols/rust_option.rs | 85 ++++++ syntax/check.rs | 52 +++- syntax/impls.rs | 1 + syntax/improper.rs | 1 + syntax/instantiate.rs | 35 +++ syntax/mod.rs | 1 + syntax/parse.rs | 11 + syntax/pod.rs | 1 + syntax/tokens.rs | 6 +- syntax/types.rs | 1 + syntax/visit.rs | 3 +- tests/compiletest.rs | 9 +- tests/ffi/lib.rs | 245 +++++++++++++++++ tests/ffi/tests.cc | 175 +++++++++++++ tests/ffi/tests.h | 28 ++ tests/test.rs | 57 +++- tests/ui/option_ref.rs | 10 + tests/ui/option_ref.stderr | 5 + tests/ui/option_safe_string.rs | 3 + tests/ui/option_safe_string.stderr | 13 + tests/ui/option_safe_unsized.rs | 3 + tests/ui/option_safe_unsized.stderr | 13 + tests/ui/option_safe_usize.rs | 3 + tests/ui/option_safe_usize.stderr | 13 + 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_string_ref.rs | 12 + tests/ui/option_string_ref.stderr | 5 + tests/ui/option_vec.rs | 12 + tests/ui/option_vec.stderr | 5 + tests/ui/option_vec_ref.rs | 12 + tests/ui/option_vec_ref.stderr | 5 + tests/ui_build/option_not_sized.rs | 5 + tests/ui_build/option_not_sized.stderr | 62 +++++ tests/ui_pass/empty.rs | 4 + 49 files changed, 1891 insertions(+), 24 deletions(-) create mode 100644 src/rust_option.rs create mode 100644 src/symbols/rust_option.rs create mode 100644 tests/ui/option_ref.rs create mode 100644 tests/ui/option_ref.stderr create mode 100644 tests/ui/option_safe_string.rs create mode 100644 tests/ui/option_safe_string.stderr create mode 100644 tests/ui/option_safe_unsized.rs create mode 100644 tests/ui/option_safe_unsized.stderr create mode 100644 tests/ui/option_safe_usize.rs create mode 100644 tests/ui/option_safe_usize.stderr 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_string_ref.rs create mode 100644 tests/ui/option_string_ref.stderr create mode 100644 tests/ui/option_vec.rs create mode 100644 tests/ui/option_vec.stderr create mode 100644 tests/ui/option_vec_ref.rs create mode 100644 tests/ui/option_vec_ref.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..2aa1fc3df 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,53 @@ 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, + ); + writeln!( + out, + "bool cxxbridge1$rust_option${}$has_value(::rust::Option<{}> const *ptr) noexcept;", + instance, inner + ); + writeln!( + out, + "{}* cxxbridge1$rust_option${}$value_ptr(::rust::Option<{0}> *ptr) noexcept;", + inner, instance + ); + writeln!( + out, + "void cxxbridge1$rust_option${}$set(::rust::Option<{1}> *ptr, {1}&& value) 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 +1723,72 @@ 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, "}}"); + + writeln!(out, "template <>"); + begin_function_definition(out); + writeln!(out, "bool Option<{}>::has_value() const noexcept {{", inner); + writeln!( + out, + " return cxxbridge1$rust_option${}$has_value(this);", + instance + ); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + begin_function_definition(out); + writeln!(out, "{0}* Option<{0}>::value_ptr() noexcept {{", inner); + writeln!( + out, + " return cxxbridge1$rust_option${}$value_ptr(this);", + instance + ); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + begin_function_definition(out); + writeln!(out, "void Option<{0}>::set({0}&& value) noexcept {{", inner); + writeln!( + out, + " return cxxbridge1$rust_option${}$set(this, ::std::move(value));", + 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..2b0bff87e 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -826,6 +826,93 @@ 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; + + const T *operator->() const; + const T &operator*() const; + T *operator->(); + T &operator*(); + + Option& operator=(Option&&) noexcept; + + bool has_value() const noexcept; + T& value() noexcept; + void reset(); + void set(T&&) noexcept; +private: + void* empty_; + + T* value_ptr() noexcept; + void drop() noexcept; +}; +#endif // CXXBRIDGE1_RUST_OPTION + +#ifndef CXXBRIDGE1_RUST_OPTION +#define CXXBRIDGE1_RUST_OPTION +template +Option::Option(Option&& other) noexcept { + new (this) Option(); + if (other.has_value()) { + set(std::move(other.value())); + } + new (&other) Option(); +} + +template +Option::~Option() noexcept { + this->drop(); +} + +template +Option& Option::operator=(Option&& other) noexcept { + this->reset(); + if (other.has_value()) { + set(std::move(other.value())); + } + new (&other) Option(); + return *this; +} + +template +const T *Option::operator->() const { + return &value(); +} + +template +const T &Option::operator*() const { + return value(); +} + +template +T *Option::operator->() { + return &value(); +} + +template +T &Option::operator*() { + return value(); +} + +template +T &Option::value() noexcept { + return *value_ptr(); +} + +template +void Option::reset() { + this->drop(); + new (this) Option(); +} + +#endif // CXXBRIDGE1_RUST_OPTION + #ifndef CXXBRIDGE1_RUST_VEC #define CXXBRIDGE1_RUST_VEC template diff --git a/macro/src/derive.rs b/macro/src/derive.rs index 1c06ad892..a439bf907 100644 --- a/macro/src/derive.rs +++ b/macro/src/derive.rs @@ -1,4 +1,4 @@ -use crate::syntax::{derive, Enum, Struct, Trait}; +use crate::syntax::{derive, Enum, Struct}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 8a0db43fb..8e9673fe7 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)); } @@ -465,6 +468,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) { @@ -547,6 +552,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)), @@ -610,10 +648,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::(); @@ -667,6 +711,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())) @@ -1006,6 +1085,36 @@ fn expand_rust_function_shim_impl( quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_vec())) } } + Type::RustOption(ty) => { + requires_unsafe = true; + match &ty.inner { + Type::RustBox(_) => quote_spanned!(span=> ::cxx::core::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=> ::cxx::core::mem::take((*#var).as_mut_option_mut_improper_pinned())) + } else { + quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_mut_improper())) + } + } else if r.pinned { + quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_ref_improper_pinned())) + } else { + quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_ref_improper())) + } + } else if r.mutable { + if r.pinned { + quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_mut_pinned())) + } else { + quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_mut())) + } + } else if r.pinned { + quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_ref_pinned())) + } else { + quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_ref())) + } + _ => unreachable!() + } + } Type::UniquePtr(_) => { requires_unsafe = true; quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#var)) @@ -1076,6 +1185,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 { @@ -1419,6 +1559,122 @@ 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, 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, 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, resolve, link_prefix, local_prefix) + } + }; + let link_new = format!("{}new", link_prefix); + let link_drop = format!("{}drop", link_prefix); + let link_has_value = format!("{}has_value", link_prefix); + let link_value_ptr = format!("{}value_ptr", link_prefix); + let link_set = format!("{}set", link_prefix); + + let local_new = format_ident!("{}new", local_prefix); + let local_drop = format_ident!("{}drop", local_prefix); + let local_has_value = format_ident!("{}has_value", local_prefix); + let local_value_ptr = format_ident!("{}value_ptr", local_prefix); + let local_set = format_ident!("{}set", 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; + let (impl_generics, ty_generics) = generics::split_for_impl(*key, explicit_impl, resolve); + let (ty, ty_ptr, prevent_unwind_drop_label) = match inner { + OptionInner::RustBox(_) => ( + quote! { ::cxx::alloc::boxed::Box<#elem #ty_generics> }, + quote! { ::cxx::alloc::boxed::Box<#elem #ty_generics> }, + format!("::alloc::boxed::Box<::{}> as Drop>::drop", ident), + ), + OptionInner::Ref(_) => ( + quote! { &#elem #ty_generics }, + quote! { *const #elem #ty_generics }, + format!("&::{}> as Drop>::drop", ident), + ), + OptionInner::MutRef(_) => ( + quote! { &mut #elem #ty_generics }, + quote! { *mut #elem #ty_generics }, + format!("&mut ::{}> as Drop>::drop", ident), + ), + }; + let set_impl = match inner { + OptionInner::RustBox(_) => quote! { + #[doc(hidden)] + #[export_name = #link_set] + unsafe extern "C" fn #local_set #impl_generics(this: *mut ::cxx::private::RustOption<#ty>, value: *mut ::core::mem::MaybeUninit<#ty>) { + let value = core::mem::replace(value.as_mut().unwrap(), ::core::mem::MaybeUninit::zeroed()); + this.as_mut().unwrap().set(unsafe { value.assume_init() }); + } + }, + _ => quote! { + #[doc(hidden)] + #[export_name = #link_set] + unsafe extern "C" fn #local_set #impl_generics(this: *mut ::cxx::private::RustOption<#ty_ptr>, value: *mut #ty_ptr) { + unsafe { this.as_mut().unwrap().set(*value) }; + } + }, + }; + quote_spanned! {end_span=> + #[doc(hidden)] + #unsafe_token impl #impl_generics ::cxx::private::ImplOption for #ty {} + #[doc(hidden)] + #[export_name = #link_new] + unsafe extern "C" fn #local_new #impl_generics(this: *mut ::cxx::private::RustOption<#ty>) { + unsafe { ::cxx::core::ptr::write(this, ::cxx::private::RustOption::new()) }; + } + #[doc(hidden)] + #[export_name = #link_drop] + unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::private::RustOption<#ty>) { + let __fn = concat!("<", module_path!(), #prevent_unwind_drop_label); + ::cxx::private::prevent_unwind(__fn, || unsafe { ::cxx::core::ptr::drop_in_place(this) }); + } + #[doc(hidden)] + #[export_name = #link_has_value] + unsafe extern "C" fn #local_has_value #impl_generics(this: *mut ::cxx::private::RustOption<#ty>) -> bool { + unsafe { this.as_ref().unwrap().value().is_some() } + } + #[doc(hidden)] + #[export_name = #link_value_ptr] + unsafe extern "C" fn #local_value_ptr #impl_generics(this: *mut ::cxx::private::RustOption<#ty_ptr>) -> *mut #ty_ptr { + let this = unsafe { this.as_mut().unwrap() }; + ::cxx::core::debug_assert!(this.has_value()); + unsafe { this.as_ref_mut_inner_unchecked() as _ } + } + #set_impl + } +} + fn expand_unique_ptr( key: NamedImplKey, types: &Types, @@ -1856,6 +2112,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; @@ -1872,6 +2135,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..e6eb21ae7 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,85 @@ 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; \ + bool cxxbridge1$rust_option$const$##RUST_TYPE##$has_value( \ + const rust::Option *ptr) noexcept; \ + CXX_TYPE const ** cxxbridge1$rust_option$const$##RUST_TYPE##$value_ptr( \ + rust::Option *ptr) noexcept; \ + void cxxbridge1$rust_option$const$##RUST_TYPE##$set( \ + rust::Option *ptr, CXX_TYPE const *&& value) noexcept; \ + void cxxbridge1$rust_option$##RUST_TYPE##$new( \ + rust::Option *ptr) noexcept; \ + void cxxbridge1$rust_option$##RUST_TYPE##$drop( \ + rust::Option *ptr) noexcept; \ + bool cxxbridge1$rust_option$##RUST_TYPE##$has_value( \ + const rust::Option *ptr) noexcept; \ + CXX_TYPE** cxxbridge1$rust_option$##RUST_TYPE##$value_ptr( \ + rust::Option *ptr) noexcept; \ + void cxxbridge1$rust_option$##RUST_TYPE##$set( \ + rust::Option *ptr, CXX_TYPE *&& value) noexcept; + +#define RUST_OPTION_OPS(RUST_TYPE, CXX_TYPE) \ + template <> \ + Option::Option() noexcept { \ + cxxbridge1$rust_option$const$##RUST_TYPE##$new(this); \ + } \ + template <> \ + void Option::drop() noexcept { \ + cxxbridge1$rust_option$const$##RUST_TYPE##$drop(this); \ + } \ + template <> \ + bool Option::has_value() const noexcept { \ + return cxxbridge1$rust_option$const$##RUST_TYPE##$has_value(this); \ + } \ + template <> \ + CXX_TYPE const ** Option::value_ptr() noexcept { \ + return cxxbridge1$rust_option$const$##RUST_TYPE##$value_ptr(this); \ + } \ + template <> \ + void Option::set(CXX_TYPE const *&& value) noexcept { \ + return cxxbridge1$rust_option$const$##RUST_TYPE##$set( \ + this, std::move(value)); \ + } \ + template <> \ + Option::Option() noexcept { \ + cxxbridge1$rust_option$##RUST_TYPE##$new(this); \ + } \ + template <> \ + void Option::drop() noexcept { \ + cxxbridge1$rust_option$##RUST_TYPE##$drop(this); \ + } \ + template <> \ + bool Option::has_value() const noexcept { \ + return cxxbridge1$rust_option$##RUST_TYPE##$has_value(this); \ + } \ + template <> \ + CXX_TYPE** Option::value_ptr() noexcept { \ + return cxxbridge1$rust_option$##RUST_TYPE##$value_ptr(this); \ + } \ + template <> \ + void Option::set(CXX_TYPE *&& value) noexcept { \ + return cxxbridge1$rust_option$##RUST_TYPE##$set(this, std::move(value)); \ + } + +#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 e9e1a4e1a..36327266e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -458,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 +480,7 @@ pub use crate::cxx_vector::CxxVector; #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] pub use crate::exception::Exception; pub use crate::extern_type::{kind, ExternType}; +pub use crate::rust_option::RustOption; pub use crate::shared_ptr::SharedPtr; pub use crate::string::CxxString; pub use crate::unique_ptr::UniquePtr; @@ -510,10 +512,13 @@ pub mod private { pub use crate::opaque::Opaque; #[cfg(feature = "alloc")] pub use crate::result::{r#try, Result}; + pub use crate::rust_option::assert_option_safe; + pub use crate::rust_option::RustOption; pub use crate::rust_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..11e0fab4b --- /dev/null +++ b/src/rust_option.rs @@ -0,0 +1,349 @@ +#![allow(missing_docs)] +#![allow(clippy::let_unit_value)] + +#[cfg(feature = "alloc")] +use alloc::boxed::Box; +use core::mem::ManuallyDrop; +use core::ops::Deref; +use core::ops::DerefMut; +use core::pin::Pin; + +mod private { + pub trait Sealed {} +} +pub trait OptionTarget: private::Sealed {} + +impl private::Sealed for &T {} +impl OptionTarget for &T {} + +impl private::Sealed for &mut T {} +impl OptionTarget for &mut T {} + +impl private::Sealed for Pin<&mut T> {} +impl OptionTarget for Pin<&mut T> {} + +impl private::Sealed for *const T {} +impl OptionTarget for *const T {} + +impl private::Sealed for *mut T {} +impl OptionTarget for *mut T {} + +#[cfg(feature = "alloc")] +impl private::Sealed for Box {} +#[cfg(feature = "alloc")] +impl OptionTarget for Box {} + +#[repr(C)] +union OptionInner { + value: ManuallyDrop, + empty: usize, +} + +impl OptionInner { + fn has_value(&self) -> bool { + let _: () = assert_option_safe::<&T>(); + unsafe { self.empty != 0 } + } + + fn into_inner_unchecked(mut self) -> T { + let value = unsafe { ManuallyDrop::take(&mut self.value) }; + unsafe { core::ptr::write(&mut self as _, OptionInner { empty: 0 }) }; + value + } +} + +impl Drop for OptionInner { + fn drop(&mut self) { + if self.has_value() { + unsafe { ManuallyDrop::drop(&mut self.value) }; + } + } +} + +// ABI compatible with C++ rust::Option (not necessarily core::option::Option). +#[repr(C)] +pub struct RustOption { + inner: OptionInner, +} + +pub const fn assert_option_safe() { + 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) + let _: () = __SizeCheck::::_IS_NICHE; + let _: () = __SizeCheck::::_IS_USIZE_SIZE; +} + +impl RustOption { + pub fn new() -> Self { + let _: () = assert_option_safe::<&mut T>(); + RustOption { + inner: OptionInner { empty: 0 }, + } + } + + pub fn value(&self) -> Option<&T> { + if self.has_value() { + unsafe { Some(self.inner.value.deref()) } + } else { + None + } + } + + pub fn has_value(&self) -> bool { + self.inner.has_value() + } + + pub fn set(&mut self, value: T) { + self.inner = OptionInner { + value: ManuallyDrop::new(value), + } + } + + pub unsafe fn into_inner_unchecked(self) -> T { + self.inner.into_inner_unchecked() + } + + pub unsafe fn as_ref_inner_unchecked(&self) -> &T { + unsafe { self.inner.value.deref() } + } + + pub unsafe fn as_ref_mut_inner_unchecked(&mut self) -> &mut T { + unsafe { self.inner.value.deref_mut() } + } +} + +impl<'a, T> RustOption<&'a T> +where + &'a T: OptionTarget, +{ + pub fn from_option_ref(other: Option<&'a T>) -> Self { + let _: () = assert_option_safe::<&T>(); + unsafe { core::mem::transmute::, RustOption<&'a T>>(other) } + } + + pub fn into_option_ref(self) -> Option<&'a T> { + let _: () = assert_option_safe::<&T>(); + unsafe { core::mem::transmute::, Option<&'a T>>(self) } + } + + pub fn as_mut_option_ref(&mut self) -> &mut Option<&'a T> { + let _: () = assert_option_safe::<&T>(); + unsafe { &mut *(self as *mut RustOption<&'a T> as *mut Option<&'a T>) } + } +} + +impl<'a, T> RustOption<&'a mut T> +where + &'a mut T: OptionTarget, +{ + pub fn from_option_mut(other: Option<&'a mut T>) -> Self { + let _: () = assert_option_safe::<&mut T>(); + unsafe { core::mem::transmute::, RustOption<&'a mut T>>(other) } + } + + pub fn into_option_mut(self) -> Option<&'a mut T> { + let _: () = assert_option_safe::<&mut T>(); + unsafe { core::mem::transmute::, Option<&'a mut T>>(self) } + } + + pub fn as_mut_option_mut(&mut self) -> &mut Option<&'a mut T> { + let _: () = assert_option_safe::<&mut T>(); + unsafe { &mut *(self as *mut RustOption<&'a mut T> as *mut Option<&'a mut T>) } + } +} + +impl<'a, T> RustOption> +where + Pin<&'a mut T>: OptionTarget, +{ + pub fn from_option_mut_pinned(other: Option>) -> Self { + let _: () = assert_option_safe::>(); + unsafe { core::mem::transmute::>, RustOption>>(other) } + } + + pub fn into_option_mut_pinned(self) -> Option> { + let _: () = assert_option_safe::>(); + unsafe { core::mem::transmute::>, Option>>(self) } + } + + pub fn as_mut_option_mut_pinned(&mut self) -> &mut Option> { + let _: () = assert_option_safe::>(); + unsafe { &mut *(self as *mut RustOption> as *mut Option>) } + } + + pub fn into_option_mut_improper_pinned(self) -> Option> { + let _: () = assert_option_safe::>(); + unsafe { core::mem::transmute::>, Option>>(self) } + } + + pub fn as_mut_option_mut_improper_pinned(&mut self) -> &mut Option> { + let _: () = assert_option_safe::>(); + unsafe { &mut *(self as *mut RustOption> as *mut Option>) } + } +} + +impl RustOption<*const T> +where + *const T: OptionTarget, +{ + /// SAFETY: self must have been constructed as `Option>` + #[cfg(feature = "alloc")] + pub unsafe fn into_option_box(self) -> Option> { + let _: () = assert_option_safe::>(); + unsafe { core::mem::transmute::, Option>>(self) } + } + + /// SAFETY: self must have been constructed as `Option>` + #[cfg(feature = "alloc")] + pub unsafe fn as_mut_option_box(&mut self) -> &mut Option> { + let _: () = assert_option_safe::>(); + unsafe { &mut *(self as *mut RustOption<*const T> as *mut Option>) } + } + + pub fn into_option_ref<'a>(self) -> Option<&'a T> { + let _: () = assert_option_safe::<&T>(); + unsafe { core::mem::transmute::, Option<&'a T>>(self) } + } + + pub fn as_mut_option_ref<'a>(&mut self) -> &mut Option<&'a T> { + let _: () = assert_option_safe::<&T>(); + unsafe { &mut *(self as *mut RustOption<*const T> as *mut Option<&'a T>) } + } +} + +impl RustOption<*mut T> +where + *mut T: OptionTarget, +{ + #[cfg(feature = "alloc")] + pub fn from_option_box(other: Option>) -> Self { + let _: () = assert_option_safe::>(); + unsafe { core::mem::transmute::>, RustOption<*mut T>>(other) } + } + + /// SAFETY: self must have been constructed as `Option>` + #[cfg(feature = "alloc")] + pub unsafe fn into_option_box(self) -> Option> { + let _: () = assert_option_safe::>(); + unsafe { core::mem::transmute::, Option>>(self) } + } + + /// SAFETY: self must have been constructed as `Option>` + #[cfg(feature = "alloc")] + pub unsafe fn as_mut_option_box(&mut self) -> &mut Option> { + let _: () = assert_option_safe::>(); + unsafe { &mut *(self as *mut RustOption<*mut T> as *mut Option>) } + } + + /// SAFETY: Pointer must not be aliased and must have been constructed as an `Option<&mut T>` + pub unsafe fn into_option_mut<'a>(self) -> Option<&'a mut T> { + let _: () = assert_option_safe::<&mut T>(); + unsafe { core::mem::transmute::, Option<&'a mut T>>(self) } + } + + /// SAFETY: Pointer must not be aliased and must have been constructed as an `Option<&mut T>` + pub unsafe fn as_mut_option_mut<'a>(&mut self) -> &mut Option<&'a mut T> { + let _: () = assert_option_safe::<&mut T>(); + unsafe { &mut *(self as *mut RustOption<*mut T> as *mut Option<&'a mut T>) } + } +} + +impl RustOption<*const core::ffi::c_void> { + pub fn from_option_ref_improper<'a, T>(other: Option<&'a T>) -> Self { + let _: () = assert_option_safe::<&T>(); + unsafe { + core::mem::transmute::, RustOption<*const core::ffi::c_void>>(other) + } + } + + pub fn from_option_ref_improper_pinned<'a, T>(other: Option>) -> Self { + let _: () = assert_option_safe::>(); + unsafe { + core::mem::transmute::>, RustOption<*const core::ffi::c_void>>(other) + } + } + + /// SAFETY: self must have been constructed as `Option>` + #[cfg(feature = "alloc")] + pub unsafe fn into_option_box_improper(self) -> Option> { + let _: () = assert_option_safe::>(); + unsafe { + core::mem::transmute::, Option>>(self) + } + } + + /// SAFETY: self must have been constructed as `Option>` + #[cfg(feature = "alloc")] + pub unsafe fn as_mut_option_box_improper(&mut self) -> &mut Option> { + let _: () = assert_option_safe::>(); + unsafe { &mut *(self as *mut RustOption<*const core::ffi::c_void> as *mut Option>) } + } + + pub fn into_option_ref_improper<'a, T>(self) -> Option<&'a T> { + let _: () = assert_option_safe::<&T>(); + unsafe { core::mem::transmute::, Option<&'a T>>(self) } + } + + pub fn as_mut_option_ref_improper<'a, T>(&mut self) -> &mut Option<&'a T> { + let _: () = assert_option_safe::<&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 fn from_option_box_improper(other: Option>) -> Self { + let _: () = assert_option_safe::>(); + 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 { + let _: () = assert_option_safe::<&mut T>(); + unsafe { + core::mem::transmute::, RustOption<*mut core::ffi::c_void>>(other) + } + } + + pub fn from_option_mut_improper_pinned<'a, T>(other: Option>) -> Self { + let _: () = assert_option_safe::>(); + unsafe { + core::mem::transmute::>, RustOption<*mut core::ffi::c_void>>( + other, + ) + } + } + + /// SAFETY: self must have been constructed as `Option>` + #[cfg(feature = "alloc")] + pub unsafe fn into_option_box_improper(self) -> Option> { + let _: () = assert_option_safe::>(); + unsafe { core::mem::transmute::, Option>>(self) } + } + + /// SAFETY: self must have been constructed as `Option>` + #[cfg(feature = "alloc")] + pub unsafe fn as_mut_option_box_improper(&mut self) -> &mut Option> { + let _: () = assert_option_safe::>(); + unsafe { &mut *(self as *mut RustOption<*mut core::ffi::c_void> as *mut Option>) } + } + + /// SAFETY: Pointer must not be aliased and must have been constructed as an `Option<&mut T>` + pub unsafe fn into_option_mut_improper<'a, T>(self) -> Option<&'a mut T> { + let _: () = assert_option_safe::<&mut T>(); + unsafe { + core::mem::transmute::, Option<&'a mut T>>(self) + } + } + + /// SAFETY: Pointer must not be aliased and must have been constructed as an `Option<&mut T>` + pub unsafe fn as_mut_option_mut_improper<'a, T>(&mut self) -> &mut Option<&'a mut T> { + let _: () = assert_option_safe::<&mut T>(); + unsafe { &mut *(self as *mut RustOption<*mut core::ffi::c_void> as *mut Option<&'a mut T>) } + } +} 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..869c942e3 --- /dev/null +++ b/src/symbols/rust_option.rs @@ -0,0 +1,85 @@ +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$const$", $segment, "$has_value")] + unsafe extern "C" fn __const_has_value(this: *mut RustOption<&$ty>) -> bool { + unsafe { this.as_ref().unwrap().value().is_some() } + } + #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$value_ptr")] + unsafe extern "C" fn __const_value_ptr(this: *mut RustOption<&$ty>) -> *mut &$ty { + unsafe { this.as_mut().unwrap().as_ref_mut_inner_unchecked() as _ } + } + #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$set")] + unsafe extern "C" fn __const_set<'__cxx>( + this: *mut RustOption<&'__cxx $ty>, + value: *mut &'__cxx $ty, + ) { + unsafe { this.as_mut().unwrap().set(*value) } + } + #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$new")] + unsafe extern "C" fn __new(this: *mut RustOption<&mut $ty>) { + unsafe { ptr::write(this, RustOption::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) } + } + #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$has_value")] + unsafe extern "C" fn __has_value(this: *mut RustOption<&mut $ty>) -> bool { + unsafe { this.as_ref().unwrap().value().is_some() } + } + #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$value_ptr")] + unsafe extern "C" fn __value_ptr(this: *mut RustOption<&mut $ty>) -> *mut &mut $ty { + unsafe { this.as_mut().unwrap().as_ref_mut_inner_unchecked() as _ } + } + #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$set")] + unsafe extern "C" fn __set<'__cxx>( + this: *mut RustOption<&'__cxx mut $ty>, + value: *mut &'__cxx mut $ty, + ) { + unsafe { this.as_mut().unwrap().set(*value) } + } + }; + }; +} + +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..47402bca6 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,26 @@ 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(r) => { + // Vec and String are not supported + match &r.inner { + Type::RustVec(_) => {} + Type::Ident(ident) => match Atom::from(&ident.rust) { + Some(RustString) => {} + _ => return, + }, + _ => 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) { @@ -245,7 +269,7 @@ fn check_type_ref(cx: &mut Check, ty: &Ref) { } match ty.inner { - Type::Fn(_) | Type::Void(_) => {} + Type::Fn(_) | Type::Void(_) | Type::RustOption(_) => {} Type::Ref(_) => { cx.error(ty, "C++ does not allow references to references"); return; @@ -523,6 +547,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 +634,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 +677,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 +752,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 eacba5541..56d75213b 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -264,6 +264,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..c48910494 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,16 @@ fn parse_type_path(ty: &TypePath) -> Result { rangle: generic.gt_token, }))); } + } else if ident == "Option" && 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 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/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..237eaec69 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -184,6 +184,7 @@ impl<'a> Types<'a> { | 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 f3a8310f1..5dc86b8e0 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -22,6 +22,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 { @@ -122,6 +123,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(s: &Shared) -> Option<&Shared>; + fn c_return_rust_mut_option_shared(s: &mut Shared) -> Option<&mut Shared>; + fn c_return_rust_pin_mut_option_shared(s: Pin<&mut Shared>) -> Option>; + fn c_return_rust_ref_option_opaque(c: &C) -> Option<&C>; + fn c_return_rust_pin_mut_option_opaque(c: Pin<&mut C>) -> Option>; + fn c_return_rust_ref_option_native(u: &u8) -> Option<&u8>; + fn c_return_rust_mut_option_native(u: &mut u8) -> Option<&mut u8>; + fn c_return_rust_pin_mut_option_native(u: Pin<&mut u8>) -> Option>; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; fn c_return_enum(n: u16) -> Enum; @@ -164,6 +174,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)); @@ -193,6 +214,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; @@ -278,6 +308,29 @@ 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>; + fn r_return_rust_option_box_generic() -> Option>>; + unsafe fn r_return_rust_option_ref_shared<'a>(shared: &'a Shared) -> Option<&'a Shared>; + fn r_return_rust_option_ref_generic() -> Option<&'static StructWithLifetime<'static>>; + unsafe fn r_return_rust_option_mut_shared<'a>( + shared: &'a mut Shared, + ) -> Option<&'a mut Shared>; + fn r_return_rust_option_mut_generic() -> Option<&'static mut StructWithLifetime<'static>>; + unsafe fn r_return_rust_option_pin_mut_shared<'a>( + shared: Pin<&'a mut Shared>, + ) -> Option>; + fn r_return_rust_option_pin_mut_generic( + ) -> 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; @@ -299,11 +352,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]>; @@ -528,6 +616,71 @@ 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 })) +} + +fn r_return_rust_option_box_generic() -> Option>> { + Some(Box::new(ffi::StructWithLifetime { s: &"abc" })) +} + +unsafe fn r_return_rust_option_ref_shared<'a>(shared: &'a ffi::Shared) -> Option<&'a ffi::Shared> { + Some(shared) +} + +fn r_return_rust_option_ref_generic() -> Option<&'static ffi::StructWithLifetime<'static>> { + None +} + +unsafe fn r_return_rust_option_mut_shared<'a>( + shared: &'a mut ffi::Shared, +) -> Option<&'a mut ffi::Shared> { + Some(shared) +} + +fn r_return_rust_option_mut_generic() -> Option<&'static mut ffi::StructWithLifetime<'static>> { + None +} + +unsafe fn r_return_rust_option_pin_mut_shared<'a>( + shared: Pin<&'a mut ffi::Shared>, +) -> Option> { + Some(shared) +} + +fn r_return_rust_option_pin_mut_generic( +) -> Option>> { + None +} + +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 } @@ -618,6 +771,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; } @@ -634,6 +825,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..b55235f10 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -181,6 +181,69 @@ 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() { + rust::Option> opt; // Default constructor + opt.set(rust::Box::in_place(2020)); // Set value + rust::Option> opt2(std::move(opt)); // Move constructor + return opt2; +} + +rust::Option c_return_rust_ref_option_shared(const Shared& s) { + rust::Option opt; // Default constructor + opt.set(&s); // Set value + rust::Option opt2(std::move(opt)); // Move constructor + return opt2; +} + +rust::Option c_return_rust_mut_option_shared(Shared& s) { + rust::Option opt; // Default constructor + opt.set(&s); // Set value + rust::Option opt2(std::move(opt)); // Move constructor + return opt2; +} + +rust::Option c_return_rust_pin_mut_option_shared(Shared& s) { + rust::Option opt; // Default constructor + opt.set(&s); // Set value + rust::Option opt2(std::move(opt)); // Move constructor + return opt2; +} + +rust::Option c_return_rust_ref_option_opaque(const C& c) { + rust::Option opt; // Default constructor + opt.set(&c); // Set value + rust::Option opt2(std::move(opt)); // Move constructor + return opt2; +} + +rust::Option c_return_rust_pin_mut_option_opaque(C& c) { + rust::Option opt; // Default constructor + opt.set(&c); // Set value + rust::Option opt2(std::move(opt)); // Move constructor + return opt2; +} + +rust::Option c_return_rust_ref_option_native(const uint8_t& u) { + rust::Option opt; // Default constructor + opt.set(&u); // Set value + rust::Option opt2(std::move(opt)); // Move constructor + return opt2; +} + +rust::Option c_return_rust_mut_option_native(uint8_t& u) { + rust::Option opt; // Default constructor + opt.set(&u); // Set value + rust::Option opt2(std::move(opt)); // Move constructor + return opt2; +} + +rust::Option c_return_rust_pin_mut_option_native(uint8_t& u) { + rust::Option opt; // Default constructor + opt.set(&u); // Set value + rust::Option opt2(std::move(opt)); // Move constructor + return opt2; +} + 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 +547,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 +572,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 == 2020) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_ref_option_shared(rust::Option opt) { + if (opt.has_value() && opt.value()->z == 2020) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_mut_option_shared(rust::Option opt) { + if (opt.has_value() && opt.value()->z == 2020) { + opt.value()->z = 2021; + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_pin_mut_option_shared(rust::Option opt) { + if (opt.has_value() && opt.value()->z == 2020) { + opt.value()->z = 2021; + 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()) == 200) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_mut_option_native(rust::Option opt) { + if (opt.has_value() && *(opt.value()) == 200) { + *(opt.value()) = 201; + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_pin_mut_option_native(rust::Option opt) { + if (opt.has_value() && *(opt.value()) == 200) { + *(opt.value()) = 201; + 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 +691,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; } @@ -898,6 +1062,17 @@ extern "C" const char *cxx_run_test() noexcept { (void)rust::Vec(); (void)rust::Vec(); + rust::Option> opt; // Default constructor + ASSERT(!opt.has_value()); + opt.set(rust::Box::in_place(2020)); // Set value + ASSERT(opt.has_value()); + rust::Option> opt2(std::move(opt)); // Move constructor + ASSERT(!opt.has_value()); + ASSERT(opt2.has_value()); + ASSERT(sizeof(rust::Option>) == sizeof(void *)); + ASSERT(sizeof(rust::Option) == sizeof(void *)); + ASSERT(sizeof(rust::Option) == sizeof(void *)); + cxx_test_suite_set_correct(); return nullptr; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index dc02e4ff8..7f4d071a8 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(const Shared&); +rust::Option c_return_rust_mut_option_shared(Shared&); +rust::Option c_return_rust_pin_mut_option_shared(Shared&); +rust::Option c_return_rust_ref_option_opaque(const C&); +rust::Option c_return_rust_pin_mut_option_opaque(C&); +rust::Option c_return_rust_ref_option_native(const uint8_t&); +rust::Option c_return_rust_mut_option_native(uint8_t&); +rust::Option c_return_rust_pin_mut_option_native(uint8_t&); 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 1611d9717..595e7ad05 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 = const { Cell::new(false) }; @@ -35,13 +36,14 @@ macro_rules! check { #[test] fn test_c_return() { - let shared = ffi::Shared { z: 2020 }; + let mut shared = ffi::Shared { z: 2020 }; let ns_shared = ffi::AShared { z: 2020 }; let nested_ns_shared = ffi::ABShared { z: 2020 }; assert_eq!(2020, ffi::c_return_primitive()); assert_eq!(2020, ffi::c_return_shared().z); assert_eq!(2020, ffi::c_return_box().0); + assert_eq!(2020, ffi::c_return_rust_option_box().unwrap().z); ffi::c_return_unique_ptr(); ffi2::c_return_ns_unique_ptr(); assert_eq!(2020, *ffi::c_return_ref(&shared)); @@ -74,6 +76,36 @@ fn test_c_return() { ); assert_eq!(b"\x02\0\x02\0"[..], ffi::c_return_rust_vec_u8()); assert_eq!([true, true, false][..], ffi::c_return_rust_vec_bool()); + assert_eq!( + ffi::c_return_rust_option_box(), + Some(Box::new(ffi::Shared { z: 2020 })) + ); + assert_eq!( + ffi::c_return_rust_ref_option_shared(&shared.clone()), + Some(&shared) + ); + assert_eq!( + ffi::c_return_rust_mut_option_shared(&mut shared.clone()), + Some(&mut shared) + ); + assert_eq!( + ffi::c_return_rust_pin_mut_option_shared(Pin::new(&mut shared.clone())), + Some(Pin::new(&mut shared)) + ); + let mut c = ffi::c_return_unique_ptr(); + let cref = c.as_ref().unwrap(); + assert!(ffi::c_return_rust_ref_option_opaque(cref).is_some()); + let cpin = c.as_mut().unwrap(); + assert!(ffi::c_return_rust_pin_mut_option_opaque(cpin).is_some()); + assert_eq!(ffi::c_return_rust_ref_option_native(&200), Some(&200)); + assert_eq!( + ffi::c_return_rust_mut_option_native(&mut 200), + Some(&mut 200) + ); + assert_eq!( + ffi::c_return_rust_pin_mut_option_native(Pin::new(&mut 200)), + Some(Pin::new(&mut 200)) + ); assert_eq!(2020, ffi::c_return_identity(2020)); assert_eq!(2021, ffi::c_return_sum(2020, 1)); match ffi::c_return_enum(0) { @@ -199,6 +231,29 @@ fn test_c_take() { nested_ns_shared_test_vec )); + check!(ffi::c_take_rust_option_box(Some(Box::new(ffi::Shared { + z: 2020 + })))); + check!(ffi::c_take_rust_ref_option_shared(Some(&ffi::Shared { + z: 2020 + }))); + check!(ffi::c_take_rust_mut_option_shared(Some(&mut ffi::Shared { + z: 2020 + }))); + check!(ffi::c_take_rust_pin_mut_option_shared(Some(Pin::new( + &mut ffi::Shared { z: 2020 } + )))); + check!(ffi::c_take_rust_ref_option_opaque(Some(&R(2020)))); + check!(ffi::c_take_rust_mut_option_opaque(Some(&mut R(2020)))); + check!(ffi::c_take_rust_pin_mut_option_opaque(Some(Pin::new( + &mut R(2020) + )))); + check!(ffi::c_take_rust_ref_option_native(Some(&200))); + check!(ffi::c_take_rust_mut_option_native(Some(&mut 200))); + check!(ffi::c_take_rust_pin_mut_option_native(Some(Pin::new( + &mut 200 + )))); + 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_ref.rs b/tests/ui/option_ref.rs new file mode 100644 index 000000000..85426c5b7 --- /dev/null +++ b/tests/ui/option_ref.rs @@ -0,0 +1,10 @@ +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn f(_o: &Option<&u8>); + } +} + +fn f(_o: &Option<&u8>) {} + +fn main() {} diff --git a/tests/ui/option_ref.stderr b/tests/ui/option_ref.stderr new file mode 100644 index 000000000..8b92085f5 --- /dev/null +++ b/tests/ui/option_ref.stderr @@ -0,0 +1,5 @@ +error: unsupported reference type + --> tests/ui/option_ref.rs:4:18 + | +4 | fn f(_o: &Option<&u8>); + | ^^^^^^^^^^^^ diff --git a/tests/ui/option_safe_string.rs b/tests/ui/option_safe_string.rs new file mode 100644 index 000000000..d13b208e0 --- /dev/null +++ b/tests/ui/option_safe_string.rs @@ -0,0 +1,3 @@ +const _: () = cxx::private::assert_option_safe::(); + +fn main() {} diff --git a/tests/ui/option_safe_string.stderr b/tests/ui/option_safe_string.stderr new file mode 100644 index 000000000..c01f74e01 --- /dev/null +++ b/tests/ui/option_safe_string.stderr @@ -0,0 +1,13 @@ +error[E0080]: evaluation of `cxx::private::assert_option_safe::__SizeCheck::::_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:75:13 + | + = 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/option_safe_unsized.rs b/tests/ui/option_safe_unsized.rs new file mode 100644 index 000000000..e9c913e1d --- /dev/null +++ b/tests/ui/option_safe_unsized.rs @@ -0,0 +1,3 @@ +const _: () = cxx::private::assert_option_safe::<&str>(); + +fn main() {} diff --git a/tests/ui/option_safe_unsized.stderr b/tests/ui/option_safe_unsized.stderr new file mode 100644 index 000000000..683dbfb84 --- /dev/null +++ b/tests/ui/option_safe_unsized.stderr @@ -0,0 +1,13 @@ +error[E0080]: evaluation of `cxx::private::assert_option_safe::__SizeCheck::<&str>::_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:75:13 + | + = 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/option_safe_usize.rs b/tests/ui/option_safe_usize.rs new file mode 100644 index 000000000..64231de14 --- /dev/null +++ b/tests/ui/option_safe_usize.rs @@ -0,0 +1,3 @@ +const _: () = cxx::private::assert_option_safe::(); + +fn main() {} diff --git a/tests/ui/option_safe_usize.stderr b/tests/ui/option_safe_usize.stderr new file mode 100644 index 000000000..1135e104e --- /dev/null +++ b/tests/ui/option_safe_usize.stderr @@ -0,0 +1,13 @@ +error[E0080]: evaluation of `cxx::private::assert_option_safe::__SizeCheck::::_IS_NICHE` 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:73:13 + | + = 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_NICHE; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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_string_ref.rs b/tests/ui/option_string_ref.rs new file mode 100644 index 000000000..03c2d9af5 --- /dev/null +++ b/tests/ui/option_string_ref.rs @@ -0,0 +1,12 @@ +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn f() -> Option<&'static String>; + } +} + +fn f() -> Option<&'static String> { + unimplemented!() +} + +fn main() {} diff --git a/tests/ui/option_string_ref.stderr b/tests/ui/option_string_ref.stderr new file mode 100644 index 000000000..c83c400c5 --- /dev/null +++ b/tests/ui/option_string_ref.stderr @@ -0,0 +1,5 @@ +error: unsupported element type of Option + --> tests/ui/option_string_ref.rs:4:19 + | +4 | fn f() -> Option<&'static String>; + | ^^^^^^^^^^^^^^^^^^^^^^^ 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/option_vec_ref.rs b/tests/ui/option_vec_ref.rs new file mode 100644 index 000000000..72881bd2c --- /dev/null +++ b/tests/ui/option_vec_ref.rs @@ -0,0 +1,12 @@ +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn f() -> Option<&'static Vec>; + } +} + +fn f() -> Option<&'static Vec> { + unimplemented!() +} + +fn main() {} diff --git a/tests/ui/option_vec_ref.stderr b/tests/ui/option_vec_ref.stderr new file mode 100644 index 000000000..2fcef269c --- /dev/null +++ b/tests/ui/option_vec_ref.stderr @@ -0,0 +1,5 @@ +error: unsupported element type of Option + --> tests/ui/option_vec_ref.rs:4:19 + | +4 | fn f() -> Option<&'static Vec>; + | ^^^^^^^^^^^^^^^^^^^^^^^^ 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..5f6557550 --- /dev/null +++ b/tests/ui_build/option_not_sized.stderr @@ -0,0 +1,62 @@ +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> tests/ui_build/option_not_sized.rs:2:14 + | +2 | const _: cxx::RustOption::<&dyn core::fmt::Debug> = cxx::RustOption::<&dyn core::fmt::Debug>::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)`, which is required by `&'static (dyn Debug + 'static): cxx::rust_option::OptionTarget` + = help: the following other types implement trait `cxx::rust_option::OptionTarget`: + Box + Pin<&mut T> + *const T + *mut T + &T + &mut T + = note: required for `&'static (dyn Debug + 'static)` to implement `cxx::rust_option::OptionTarget` +note: required by a bound in `RustOption` + --> src/rust_option.rs + | + | pub struct RustOption { + | ^^^^^^^^^^^^ required by this bound in `RustOption` + +error[E0599]: the function or associated item `new` exists for struct `RustOption<&dyn Debug>`, but its trait bounds were not satisfied + --> tests/ui_build/option_not_sized.rs:2:99 + | +2 | const _: cxx::RustOption::<&dyn core::fmt::Debug> = cxx::RustOption::<&dyn core::fmt::Debug>::new(); + | ^^^ function or associated item cannot be called on `RustOption<&dyn Debug>` due to unsatisfied trait bounds + | +note: if you're trying to build a new `RustOption<&dyn Debug>` consider using one of the following associated functions: + RustOption::::new + RustOption::<&'a T>::from_option_ref + --> src/rust_option.rs + | + | pub fn new() -> Self { + | ^^^^^^^^^^^^^^^^^^^^ +... + | pub fn from_option_ref(other: Option<&'a T>) -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: the following trait bounds were not satisfied: + `&dyn Debug: cxx::rust_option::OptionTarget` + `&dyn Debug: cxx::rust_option::private::Sealed` + which is required by `&dyn Debug: cxx::rust_option::OptionTarget` + +error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time + --> tests/ui_build/option_not_sized.rs:2:57 + | +2 | const _: cxx::RustOption::<&dyn core::fmt::Debug> = cxx::RustOption::<&dyn core::fmt::Debug>::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Debug`, which is required by `&dyn Debug: cxx::rust_option::OptionTarget` + = help: the following other types implement trait `cxx::rust_option::OptionTarget`: + Box + Pin<&mut T> + *const T + *mut T + &T + &mut T + = note: required for `&dyn Debug` to implement `cxx::rust_option::OptionTarget` +note: required by a bound in `RustOption` + --> src/rust_option.rs + | + | pub struct RustOption { + | ^^^^^^^^^^^^ required by this bound in `RustOption` 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() {} From 9b925786726b0268d329635a349e3d42488bd8b6 Mon Sep 17 00:00:00 2001 From: Eric Kuecks Date: Mon, 15 Jan 2024 15:02:59 -0800 Subject: [PATCH 2/3] Add support for Option<&String> and Option<&Vec> --- gen/src/write.rs | 41 ++++++ macro/src/expand.rs | 212 ++++++++++++++++++++-------- src/cxx.cc | 69 ++++++++- src/rust_option.rs | 201 +++++++++++++++++++++++++- src/rust_type.rs | 2 +- src/symbols/rust_option.rs | 56 ++++++++ syntax/check.rs | 12 +- syntax/instantiate.rs | 11 ++ tests/ffi/lib.rs | 54 +++++++ tests/ffi/tests.cc | 132 +++++++++++++++++ tests/ffi/tests.h | 24 ++++ tests/test.rs | 52 +++++++ tests/ui/option_safe_string.stderr | 2 +- tests/ui/option_safe_unsized.stderr | 2 +- tests/ui/option_safe_usize.stderr | 2 +- tests/ui/option_string_ref.rs | 12 -- tests/ui/option_string_ref.stderr | 5 - tests/ui/option_vec_ref.rs | 12 -- tests/ui/option_vec_ref.stderr | 5 - 19 files changed, 795 insertions(+), 111 deletions(-) delete mode 100644 tests/ui/option_string_ref.rs delete mode 100644 tests/ui/option_string_ref.stderr delete mode 100644 tests/ui/option_vec_ref.rs delete mode 100644 tests/ui/option_vec_ref.stderr diff --git a/gen/src/write.rs b/gen/src/write.rs index 2aa1fc3df..36b18c332 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1377,6 +1377,8 @@ enum RustOption<'a> { RustBox(&'a Ident), Ref(&'a Ident), MutRef(&'a Ident), + RefVec(&'a Ident), + MutRefVec(&'a Ident), } trait ToTypename { @@ -1408,6 +1410,13 @@ impl<'a> ToTypename for RustOption<'a> { } RustOption::Ref(inner) => format!("const {}*", inner.to_typename(types)), RustOption::MutRef(inner) => format!("{}*", inner.to_typename(types)), + RustOption::RefVec(inner) => format!( + "const ::rust::cxxbridge1::Vec<{}>*", + inner.to_typename(types) + ), + RustOption::MutRefVec(inner) => { + format!("::rust::cxxbridge1::Vec<{}>*", inner.to_typename(types)) + } } } } @@ -1445,6 +1454,14 @@ impl<'a> ToMangled for RustOption<'a> { let symbol = symbol::join(&[&inner.to_mangled(types)]); symbol } + RustOption::RefVec(inner) => { + let symbol = symbol::join(&[&"const", &"Vec", &inner.to_mangled(types)]); + symbol + } + RustOption::MutRefVec(inner) => { + let symbol = symbol::join(&[&"Vec", &inner.to_mangled(types)]); + symbol + } } } } @@ -1572,6 +1589,18 @@ fn write_rust_option_extern(out: &mut OutFile, inner: OptionInner) { } RustOption::MutRef(key.rust) } + OptionInner::RefVec(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + RustOption::RefVec(key.rust) + } + OptionInner::MutRefVec(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + RustOption::MutRefVec(key.rust) + } }; let inner = element.to_typename(out.types); let instance = element.to_mangled(out.types); @@ -1738,6 +1767,18 @@ fn write_rust_option_impl(out: &mut OutFile, inner: OptionInner) { } RustOption::MutRef(key.rust) } + OptionInner::RefVec(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + RustOption::RefVec(key.rust) + } + OptionInner::MutRefVec(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + RustOption::MutRefVec(key.rust) + } }; let inner = element.to_typename(out.types); let instance = element.to_mangled(out.types); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 8e9673fe7..fecdf4b5a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -555,33 +555,53 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { 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 { + 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) => { + // NOTE: We do not handle Option> or Option>, but Pin<&mut Vec> and + // Pin<&mut String> actually aren't even supported in general either (and have no real reason to ever + // be used) + match &r.inner { + Type::Ident(ident) if ident.rust == RustString => match r.mutable { + false => return quote_spanned!(span=> &::cxx::private::RustOption::from_option_string_ref(#var.assume_init()) as _), + true => return quote_spanned!(span=> &::cxx::private::RustOption::from_option_string_mut(#var.assume_init()) as _), + }, + Type::RustVec(vec) if vec.inner == RustString => match r.mutable { + false => return quote_spanned!(span=> &::cxx::private::RustOption::from_option_vec_string_ref(#var.assume_init()) as _), + true => return quote_spanned!(span=> &::cxx::private::RustOption::from_option_vec_string_mut(#var.assume_init()) as _), + }, + Type::RustVec(_) => match r.mutable { + false => return quote_spanned!(span=> &::cxx::private::RustOption::from_option_vec_ref(#var.assume_init()) as _), + true => return quote_spanned!(span=> &::cxx::private::RustOption::from_option_vec_mut(#var.assume_init()) as _), + }, + _ => {}, + } + 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_improper_pinned(#var.assume_init()) as _) + quote_spanned!(span=> &::cxx::RustOption::from_option_mut_pinned(#var.assume_init()) as _) } else { - quote_spanned!(span=> &::cxx::RustOption::from_option_mut_improper(#var.assume_init()) as _) + quote_spanned!(span=> &::cxx::RustOption::from_option_mut(#var.assume_init()) as _) } } else if r.pinned { - quote_spanned!(span=> &::cxx::RustOption::from_option_ref_improper_pinned(#var.assume_init()) as _) + quote_spanned!(span=> &::cxx::RustOption::from_option_ref_pinned(#var.assume_init()) as _) } else { - quote_spanned!(span=> &::cxx::RustOption::from_option_ref_improper(#var.assume_init()) as _) + quote_spanned!(span=> &::cxx::RustOption::from_option_ref(#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!(), } } @@ -719,31 +739,45 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { quote_spanned!(span=> #call.into_option_box()) } } - Type::Ref(r) => { - if types.is_considered_improper_ctype(&ty.inner) { - if r.mutable { + Type::Ref(r) => match &r.inner { + Type::Ident(ident) if ident.rust == RustString => match r.mutable { + false => quote_spanned!(span=> #call.into_option_string_ref()), + true => quote_spanned!(span=> #call.into_option_string_mut()), + }, + Type::RustVec(vec) if vec.inner == RustString => match r.mutable { + false => quote_spanned!(span=> #call.into_option_vec_string_ref()), + true => quote_spanned!(span=> #call.into_option_vec_string_mut()), + }, + Type::RustVec(_) => match r.mutable { + false => quote_spanned!(span=> #call.into_option_vec_ref()), + true => quote_spanned!(span=> #call.into_option_vec_mut()), + }, + _ => { + 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_improper_pinned()) + quote_spanned!(span=> #call.into_option_mut_pinned()) } else { - quote_spanned!(span=> #call.into_option_mut_improper()) + quote_spanned!(span=> #call.into_option_mut()) } } else if r.pinned { - quote_spanned!(span=> #call.into_option_ref_improper_pinned()) + quote_spanned!(span=> #call.into_option_ref_pinned()) } else { - quote_spanned!(span=> #call.into_option_ref_improper()) + quote_spanned!(span=> #call.into_option_ref()) } - } 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) => { @@ -1089,28 +1123,30 @@ fn expand_rust_function_shim_impl( requires_unsafe = true; match &ty.inner { Type::RustBox(_) => quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_box())), - Type::Ref(r) => if types.is_considered_improper_ctype(&ty.inner) { - if r.mutable { + Type::Ref(r) => match &r.inner { + Type::Ident(i) if i.rust == RustString => match r.mutable { + true => quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_option_string_mut())), + false => quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_option_string_ref())), + }, + Type::RustVec(vec) if vec.inner == RustString => match r.mutable { + true => quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_option_vec_string_mut())), + false => quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_option_vec_string_ref())), + }, + Type::RustVec(_) => match r.mutable { + true => quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_option_vec_mut())), + false => quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_option_vec_ref())), + }, + _ => if r.mutable { if r.pinned { - quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_mut_improper_pinned())) + quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_mut_pinned())) } else { - quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_mut_improper())) + quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_mut())) } } else if r.pinned { - quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_ref_improper_pinned())) + quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_ref_pinned())) } else { - quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_ref_improper())) + quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_ref())) } - } else if r.mutable { - if r.pinned { - quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_mut_pinned())) - } else { - quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_mut())) - } - } else if r.pinned { - quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_ref_pinned())) - } else { - quote_spanned!(span=> ::cxx::core::mem::take((*#var).as_mut_option_ref())) } _ => unreachable!() } @@ -1190,6 +1226,21 @@ fn expand_rust_function_shim_impl( Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_box)) } Type::Ref(r) => { + match &r.inner { + Type::Ident(ident) if ident.rust == RustString => match r.mutable { + false => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_string_ref)), + true => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_string_mut)), + }, + Type::RustVec(vec) if vec.inner == RustString => match r.mutable { + false => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_string_ref)), + true => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_string_mut)), + }, + Type::RustVec(_) => match r.mutable { + false => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_ref)), + true => return Some(quote_spanned!(span=> ::cxx::private::RustOption::from_option_vec_mut)), + }, + _ => {}, + } if types.is_considered_improper_ctype(&ty.inner) { if r.mutable { if r.pinned { @@ -1592,6 +1643,29 @@ fn expand_rust_option( let local_prefix = format_ident!("{}__ref__option_", elem); (elem, key, resolve, link_prefix, local_prefix) } + OptionInner::RefVec(key) => { + let elem = key.rust; + if types.try_resolve(key.rust).is_none() { + return TokenStream::new(); + } + let resolve = types.resolve(elem); + let link_prefix = format!( + "cxxbridge1$rust_option$const$Vec${}$", + resolve.name.to_symbol() + ); + let local_prefix = format_ident!("{}__vec__const__ref__option_", elem); + (elem, key, resolve, link_prefix, local_prefix) + } + OptionInner::MutRefVec(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$Vec${}$", resolve.name.to_symbol()); + let local_prefix = format_ident!("{}__vec__ref__option_", elem); + (elem, key, resolve, link_prefix, local_prefix) + } }; let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -1611,22 +1685,44 @@ fn expand_rust_option( let ident = key.rust; let (impl_generics, ty_generics) = generics::split_for_impl(*key, explicit_impl, resolve); - let (ty, ty_ptr, prevent_unwind_drop_label) = match inner { + let (ty, trait_impl_ty, ty_ptr, prevent_unwind_drop_label) = match inner { OptionInner::RustBox(_) => ( + quote! { ::cxx::alloc::boxed::Box<#elem #ty_generics> }, quote! { ::cxx::alloc::boxed::Box<#elem #ty_generics> }, quote! { ::cxx::alloc::boxed::Box<#elem #ty_generics> }, format!("::alloc::boxed::Box<::{}> as Drop>::drop", ident), ), OptionInner::Ref(_) => ( + quote! { &#elem #ty_generics }, quote! { &#elem #ty_generics }, quote! { *const #elem #ty_generics }, format!("&::{}> as Drop>::drop", ident), ), OptionInner::MutRef(_) => ( + quote! { &mut #elem #ty_generics }, quote! { &mut #elem #ty_generics }, quote! { *mut #elem #ty_generics }, format!("&mut ::{}> as Drop>::drop", ident), ), + OptionInner::RefVec(_) => ( + // We cannot `impl ImplOption for Vec<...>` because of orphan rules so instead we + // make `ImplOption` and pass the `Vec` in there. Since ImplOption is just a + // marker trait that validates that this is a local type, we really could have picked + // any unique type for T here - it just needs to be different than the T picked + // for a direct (mut) ref above (e.g. it could have been a const bool generic) + // but using the actual `Vec` here at least gives some idea of what's going on + // if this shows up in an error message. + quote! { &::cxx::alloc::vec::Vec<#elem #ty_generics> }, + quote! { &#elem #ty_generics }, + quote! { *const ::cxx::alloc::vec::Vec<#elem #ty_generics> }, + format!("&::alloc::vec::Vec<::{}> as Drop>::drop", ident), + ), + OptionInner::MutRefVec(_) => ( + quote! { &mut ::cxx::alloc::vec::Vec<#elem #ty_generics> }, + quote! { &mut #elem #ty_generics }, + quote! { *mut ::cxx::alloc::vec::Vec<#elem #ty_generics> }, + format!("&mut ::alloc::vec::Vec<::{}> as Drop>::drop", ident), + ), }; let set_impl = match inner { OptionInner::RustBox(_) => quote! { @@ -1646,8 +1742,8 @@ fn expand_rust_option( }, }; quote_spanned! {end_span=> - #[doc(hidden)] - #unsafe_token impl #impl_generics ::cxx::private::ImplOption for #ty {} + // #[doc(hidden)] + #unsafe_token impl #impl_generics ::cxx::private::ImplOption<#ty> for #trait_impl_ty {} #[doc(hidden)] #[export_name = #link_new] unsafe extern "C" fn #local_new #impl_generics(this: *mut ::cxx::private::RustOption<#ty>) { diff --git a/src/cxx.cc b/src/cxx.cc index e6eb21ae7..993752ced 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -804,7 +804,29 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), CXX_TYPE** cxxbridge1$rust_option$##RUST_TYPE##$value_ptr( \ rust::Option *ptr) noexcept; \ void cxxbridge1$rust_option$##RUST_TYPE##$set( \ - rust::Option *ptr, CXX_TYPE *&& value) noexcept; + rust::Option *ptr, CXX_TYPE *&& value) noexcept; \ + /* Vec implementation */ \ + void cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$new( \ + rust::Option const *> *ptr) noexcept; \ + void cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$drop( \ + rust::Option const *> *ptr) noexcept; \ + bool cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$has_value( \ + const rust::Option const *> *ptr) noexcept; \ + rust::Vec const ** cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$value_ptr( \ + rust::Option const *> *ptr) noexcept; \ + void cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$set( \ + rust::Option const *> *ptr, rust::Vec const *&& value) noexcept; \ + void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$new( \ + rust::Option *> *ptr) noexcept; \ + void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$drop( \ + rust::Option *> *ptr) noexcept; \ + bool cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$has_value( \ + const rust::Option *> *ptr) noexcept; \ + rust::Vec** cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value_ptr( \ + rust::Option *> *ptr) noexcept; \ + void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$set( \ + rust::Option *> *ptr, rust::Vec *&& value) noexcept; + #define RUST_OPTION_OPS(RUST_TYPE, CXX_TYPE) \ template <> \ @@ -847,10 +869,53 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), template <> \ void Option::set(CXX_TYPE *&& value) noexcept { \ return cxxbridge1$rust_option$##RUST_TYPE##$set(this, std::move(value)); \ + } \ + /* Vec impl */ \ + template <> \ + Option const *>::Option() noexcept { \ + cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$new(this); \ + } \ + template <> \ + void Option const *>::drop() noexcept { \ + cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$drop(this); \ + } \ + template <> \ + bool Option const *>::has_value() const noexcept { \ + return cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$has_value(this); \ + } \ + template <> \ + rust::Vec const ** Option const *>::value_ptr() noexcept { \ + return cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$value_ptr(this); \ + } \ + template <> \ + void Option const *>::set(rust::Vec const *&& value) noexcept { \ + return cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$set( \ + this, std::move(value)); \ + } \ + template <> \ + Option *>::Option() noexcept { \ + cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$new(this); \ + } \ + template <> \ + void Option *>::drop() noexcept { \ + cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$drop(this); \ + } \ + template <> \ + bool Option *>::has_value() const noexcept { \ + return cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$has_value(this); \ + } \ + template <> \ + rust::Vec** Option *>::value_ptr() noexcept { \ + return cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value_ptr(this); \ + } \ + template <> \ + void Option *>::set(rust::Vec *&& value) noexcept { \ + return cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$set(this, std::move(value)); \ } #define FOR_EACH_RUST_OPTION(MACRO) \ - FOR_EACH_NUMERIC(MACRO) + FOR_EACH_NUMERIC(MACRO) \ + MACRO(string, rust::String) extern "C" { FOR_EACH_STD_VECTOR(STD_VECTOR_OPS) diff --git a/src/rust_option.rs b/src/rust_option.rs index 11e0fab4b..29febaba1 100644 --- a/src/rust_option.rs +++ b/src/rust_option.rs @@ -1,8 +1,16 @@ #![allow(missing_docs)] #![allow(clippy::let_unit_value)] +#[cfg(feature = "alloc")] +use crate::private::RustString; +#[cfg(feature = "alloc")] +use crate::private::RustVec; #[cfg(feature = "alloc")] use alloc::boxed::Box; +#[cfg(feature = "alloc")] +use alloc::string::String; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use core::mem::ManuallyDrop; use core::ops::Deref; use core::ops::DerefMut; @@ -40,6 +48,17 @@ union OptionInner { } impl OptionInner { + fn none() -> Self { + Self { empty: 0 } + } + + #[cfg(feature = "alloc")] + fn new(t: T) -> Self { + Self { + value: ManuallyDrop::new(t), + } + } + fn has_value(&self) -> bool { let _: () = assert_option_safe::<&T>(); unsafe { self.empty != 0 } @@ -47,7 +66,7 @@ impl OptionInner { fn into_inner_unchecked(mut self) -> T { let value = unsafe { ManuallyDrop::take(&mut self.value) }; - unsafe { core::ptr::write(&mut self as _, OptionInner { empty: 0 }) }; + unsafe { core::ptr::write(&mut self as _, OptionInner::none()) }; value } } @@ -83,7 +102,7 @@ impl RustOption { pub fn new() -> Self { let _: () = assert_option_safe::<&mut T>(); RustOption { - inner: OptionInner { empty: 0 }, + inner: OptionInner::none(), } } @@ -95,6 +114,14 @@ impl RustOption { } } + pub fn into_value(self) -> Option { + if self.has_value() { + unsafe { Some(self.into_inner_unchecked()) } + } else { + None + } + } + pub fn has_value(&self) -> bool { self.inner.has_value() } @@ -156,6 +183,11 @@ where let _: () = assert_option_safe::<&mut T>(); unsafe { &mut *(self as *mut RustOption<&'a mut T> as *mut Option<&'a mut T>) } } + + pub fn as_mut_option_mut_improper(&mut self) -> &mut Option<&'a mut T> { + let _: () = assert_option_safe::<&mut T>(); + unsafe { &mut *(self as *mut RustOption<&'a mut T> as *mut Option<&'a mut T>) } + } } impl<'a, T> RustOption> @@ -347,3 +379,168 @@ impl RustOption<*mut core::ffi::c_void> { unsafe { &mut *(self as *mut RustOption<*mut core::ffi::c_void> as *mut Option<&'a mut T>) } } } + +#[cfg(feature = "alloc")] +impl<'a, T> RustOption<&'a RustVec> { + pub fn from_option_vec_ref(other: Option<&'a Vec>) -> Self { + let _: () = assert_option_safe::<&'a Vec>(); + match other { + None => Self::new(), + Some(r) => Self { + inner: OptionInner::new(RustVec::from_ref(r)), + }, + } + } + + pub fn into_option_vec_ref(self) -> Option<&'a Vec> { + let _: () = assert_option_safe::<&Vec>(); + match self.into_value() { + None => None, + Some(r) => Some(r.as_vec()), + } + } + + pub fn as_option_vec_ref(&mut self) -> &mut Option<&'a Vec> { + let _: () = assert_option_safe::<&Vec>(); + unsafe { &mut *(self as *mut RustOption<&'a RustVec> as *mut Option<&'a Vec>) } + } +} + +#[cfg(feature = "alloc")] +impl<'a, T> RustOption<&'a mut RustVec> { + pub fn from_option_vec_mut(other: Option<&'a mut Vec>) -> Self { + let _: () = assert_option_safe::<&mut Vec>(); + match other { + None => Self::new(), + Some(r) => Self { + inner: OptionInner::new(RustVec::from_mut(r)), + }, + } + } + + pub fn into_option_vec_mut(self) -> Option<&'a mut Vec> { + let _: () = assert_option_safe::<&mut Vec>(); + match self.into_value() { + None => None, + Some(r) => Some(r.as_mut_vec()), + } + } + + pub fn as_option_vec_mut(&mut self) -> &mut Option<&'a mut Vec> { + let _: () = assert_option_safe::<&mut Vec>(); + unsafe { + &mut *(self as *mut RustOption<&'a mut RustVec> as *mut Option<&'a mut Vec>) + } + } +} + +#[cfg(feature = "alloc")] +impl<'a> RustOption<&'a RustVec> { + pub fn from_option_vec_string_ref(other: Option<&'a Vec>) -> Self { + let _: () = assert_option_safe::<&Vec>(); + match other { + None => Self::new(), + Some(r) => Self { + inner: OptionInner::new(RustVec::from_ref_vec_string(r)), + }, + } + } + + pub fn into_option_vec_string_ref(self) -> Option<&'a Vec> { + let _: () = assert_option_safe::<&Vec>(); + match self.into_value() { + None => None, + Some(r) => Some(r.as_vec_string()), + } + } + + pub fn as_option_vec_string_ref(&mut self) -> &mut Option<&'a Vec> { + let _: () = assert_option_safe::<&Vec>(); + unsafe { + &mut *(self as *mut RustOption<&'a RustVec> as *mut Option<&'a Vec>) + } + } +} + +#[cfg(feature = "alloc")] +impl<'a> RustOption<&'a mut RustVec> { + pub fn from_option_vec_string_mut(other: Option<&'a mut Vec>) -> Self { + let _: () = assert_option_safe::<&mut Vec>(); + match other { + None => Self::new(), + Some(r) => Self { + inner: OptionInner::new(RustVec::from_mut_vec_string(r)), + }, + } + } + + pub fn into_option_vec_string_mut(self) -> Option<&'a mut Vec> { + let _: () = assert_option_safe::<&mut Vec>(); + match self.into_value() { + None => None, + Some(r) => Some(r.as_mut_vec_string()), + } + } + + pub fn as_option_vec_string_mut(&mut self) -> &mut Option<&'a mut Vec> { + let _: () = assert_option_safe::<&mut Vec>(); + unsafe { + &mut *(self as *mut RustOption<&'a mut RustVec> + as *mut Option<&'a mut Vec>) + } + } +} + +#[cfg(feature = "alloc")] +impl<'a> RustOption<&'a RustString> { + pub fn from_option_string_ref(other: Option<&'a String>) -> Self { + let _: () = assert_option_safe::<&String>(); + match other { + None => Self::new(), + Some(r) => Self { + inner: OptionInner::new(RustString::from_ref(r)), + }, + } + } + + pub fn into_option_string_ref(self) -> Option<&'a String> { + let _: () = assert_option_safe::<&String>(); + match self.into_value() { + None => None, + Some(r) => Some(r.as_string()), + } + } + + pub fn as_option_string_ref(&mut self) -> &mut Option<&'a String> { + let _: () = assert_option_safe::<&String>(); + unsafe { &mut *(self as *mut RustOption<&'a RustString> as *mut Option<&'a String>) } + } +} + +#[cfg(feature = "alloc")] +impl<'a> RustOption<&'a mut RustString> { + pub fn from_option_string_mut(other: Option<&'a mut String>) -> Self { + let _: () = assert_option_safe::<&mut String>(); + match other { + None => Self::new(), + Some(r) => Self { + inner: OptionInner::new(RustString::from_mut(r)), + }, + } + } + + pub fn into_option_string_mut(self) -> Option<&'a mut String> { + let _: () = assert_option_safe::<&mut String>(); + match self.into_value() { + None => None, + Some(r) => Some(r.as_mut_string()), + } + } + + pub fn as_option_string_mut(&mut self) -> &mut Option<&'a mut String> { + let _: () = assert_option_safe::<&mut String>(); + unsafe { + &mut *(self as *mut RustOption<&'a mut RustString> as *mut Option<&'a mut String>) + } + } +} diff --git a/src/rust_type.rs b/src/rust_type.rs index 142673820..381b2af6c 100644 --- a/src/rust_type.rs +++ b/src/rust_type.rs @@ -4,4 +4,4 @@ pub unsafe trait RustType {} pub unsafe trait ImplBox {} pub unsafe trait ImplVec {} -pub unsafe trait ImplOption {} +pub unsafe trait ImplOption {} diff --git a/src/symbols/rust_option.rs b/src/symbols/rust_option.rs index 869c942e3..eeb3aad8c 100644 --- a/src/symbols/rust_option.rs +++ b/src/symbols/rust_option.rs @@ -1,5 +1,9 @@ use crate::c_char::c_char; use crate::rust_option::RustOption; +#[cfg(feature = "alloc")] +use crate::rust_string::RustString; +#[cfg(feature = "alloc")] +use crate::rust_vec::RustVec; use core::mem; use core::ptr; @@ -59,6 +63,56 @@ macro_rules! rust_option_shims { unsafe { this.as_mut().unwrap().set(*value) } } }; + #[cfg(feature = "alloc")] + const _: () = { + /* Vec impl */ + #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$new")] + unsafe extern "C" fn __const_new(this: *mut RustOption<&RustVec<$ty>>) { + unsafe { ptr::write(this, RustOption::new()) }; + } + #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$drop")] + unsafe extern "C" fn __const_drop(this: *mut RustOption<&RustVec<$ty>>) { + unsafe { ptr::drop_in_place(this) } + } + #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$has_value")] + unsafe extern "C" fn __const_has_value(this: *mut RustOption<&RustVec<$ty>>) -> bool { + unsafe { this.as_ref().unwrap().value().is_some() } + } + #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$value_ptr")] + unsafe extern "C" fn __const_value_ptr(this: *mut RustOption<&RustVec<$ty>>) -> *mut &RustVec<$ty> { + unsafe { this.as_mut().unwrap().as_ref_mut_inner_unchecked() as _ } + } + #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$set")] + unsafe extern "C" fn __const_set<'__cxx>( + this: *mut RustOption<&'__cxx RustVec<$ty>>, + value: *mut &'__cxx RustVec<$ty>, + ) { + unsafe { this.as_mut().unwrap().set(*value) } + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$new")] + unsafe extern "C" fn __new(this: *mut RustOption<&mut RustVec<$ty>>) { + unsafe { ptr::write(this, RustOption::new()) } + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$drop")] + unsafe extern "C" fn __drop(this: *mut RustOption<&mut RustVec<$ty>>) { + unsafe { ptr::drop_in_place(this) } + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$has_value")] + unsafe extern "C" fn __has_value(this: *mut RustOption<&mut RustVec<$ty>>) -> bool { + unsafe { this.as_ref().unwrap().value().is_some() } + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$value_ptr")] + unsafe extern "C" fn __value_ptr(this: *mut RustOption<&mut RustVec<$ty>>) -> *mut &mut RustVec<$ty> { + unsafe { this.as_mut().unwrap().as_ref_mut_inner_unchecked() as _ } + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$set")] + unsafe extern "C" fn __set<'__cxx>( + this: *mut RustOption<&'__cxx mut RustVec<$ty>>, + value: *mut &'__cxx mut RustVec<$ty>, + ) { + unsafe { this.as_mut().unwrap().set(*value) } + } + }; }; } @@ -83,3 +137,5 @@ rust_option_shims_for_primitive!(f32); rust_option_shims_for_primitive!(f64); rust_option_shims!("char", c_char); +#[cfg(feature = "alloc")] +rust_option_shims!("string", RustString); diff --git a/syntax/check.rs b/syntax/check.rs index 47402bca6..555436796 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -145,17 +145,7 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { fn check_type_rust_option(cx: &mut Check, ty: &Ty1) { match &ty.inner { Type::RustBox(_) => return, - Type::Ref(r) => { - // Vec and String are not supported - match &r.inner { - Type::RustVec(_) => {} - Type::Ident(ident) => match Atom::from(&ident.rust) { - Some(RustString) => {} - _ => return, - }, - _ => return, - } - } + Type::Ref(_) => return, _ => {} } diff --git a/syntax/instantiate.rs b/syntax/instantiate.rs index 5b55d0eae..183547e3d 100644 --- a/syntax/instantiate.rs +++ b/syntax/instantiate.rs @@ -19,6 +19,8 @@ pub(crate) enum OptionInner<'a> { RustBox(NamedImplKey<'a>), Ref(NamedImplKey<'a>), MutRef(NamedImplKey<'a>), + RefVec(NamedImplKey<'a>), + MutRefVec(NamedImplKey<'a>), } #[derive(Copy, Clone)] @@ -56,6 +58,15 @@ impl Type { } } Type::Ref(r) => match &r.inner { + Type::RustVec(_) => { + if let Some(ImplKey::RustVec(impl_key)) = r.inner.impl_key() { + if r.mutable { + return Some(ImplKey::RustOption(OptionInner::MutRefVec(impl_key))); + } else { + return Some(ImplKey::RustOption(OptionInner::RefVec(impl_key))); + } + } + } Type::Ident(ident) => { if r.mutable { return Some(ImplKey::RustOption(OptionInner::MutRef( diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 5dc86b8e0..d38efee51 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -132,6 +132,14 @@ pub mod ffi { fn c_return_rust_ref_option_native(u: &u8) -> Option<&u8>; fn c_return_rust_mut_option_native(u: &mut u8) -> Option<&mut u8>; fn c_return_rust_pin_mut_option_native(u: Pin<&mut u8>) -> Option>; + fn c_return_rust_ref_option_vec_native(v: &Vec) -> Option<&Vec>; + fn c_return_rust_mut_option_vec_native(v: &mut Vec) -> Option<&mut Vec>; + fn c_return_rust_ref_option_vec_shared(v: &Vec) -> Option<&Vec>; + fn c_return_rust_mut_option_vec_shared(v: &mut Vec) -> Option<&mut Vec>; + fn c_return_rust_ref_option_vec_string(v: &Vec) -> Option<&Vec>; + fn c_return_rust_mut_option_vec_string(v: &mut Vec) -> Option<&mut Vec>; + fn c_return_rust_ref_option_string(s: &String) -> Option<&String>; + fn c_return_rust_mut_option_string(s: &mut String) -> Option<&mut String>; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; fn c_return_enum(n: u16) -> Enum; @@ -184,6 +192,14 @@ pub mod ffi { 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_rust_ref_option_vec_native(rust: Option<&Vec>); + fn c_take_rust_mut_option_vec_native(rust: Option<&mut Vec>); + fn c_take_rust_ref_option_vec_shared(rust: Option<&Vec>); + fn c_take_rust_mut_option_vec_shared(rust: Option<&mut Vec>); + fn c_take_rust_ref_option_vec_string(rust: Option<&Vec>); + fn c_take_rust_mut_option_vec_string(rust: Option<&mut Vec>); + fn c_take_rust_ref_option_string(rust: Option<&String>); + fn c_take_rust_mut_option_string(rust: Option<&mut String>); fn c_take_ref_shared_string(s: &SharedString) -> &SharedString; fn c_take_callback(callback: fn(String) -> usize); @@ -223,6 +239,14 @@ pub mod ffi { 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 c_try_return_rust_ref_option_vec_native() -> Option<&'static Vec>; + fn c_try_return_rust_mut_option_vec_native() -> Option<&'static mut Vec>; + fn c_try_return_rust_ref_option_vec_shared() -> Option<&'static Vec>; + fn c_try_return_rust_mut_option_vec_shared() -> Option<&'static mut Vec>; + fn c_try_return_rust_ref_option_vec_string() -> Option<&'static Vec>; + fn c_try_return_rust_mut_option_vec_string() -> Option<&'static mut Vec>; + fn c_try_return_rust_ref_option_string() -> Option<&'static String>; + fn c_try_return_rust_mut_option_string() -> Option<&'static mut String>; fn get(self: &C) -> usize; fn set(self: Pin<&mut C>, n: usize) -> usize; @@ -361,6 +385,12 @@ pub mod ffi { 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_rust_option_ref_string(o: Option<&String>); + fn r_take_rust_option_mut_ref_string(o: Option<&mut String>); + fn r_take_rust_option_ref_vec(o: Option<&Vec>); + fn r_take_rust_option_mut_ref_vec(o: Option<&mut Vec>); + fn r_take_rust_option_ref_vec_string(o: Option<&Vec>); + fn r_take_rust_option_mut_ref_vec_string(o: Option<&mut Vec>); fn r_take_enum(e: Enum); @@ -809,6 +839,30 @@ fn r_take_rust_option_pin_mut_native(o: Option>) { let _ = o; } +fn r_take_rust_option_ref_string(o: Option<&String>) { + let _ = o; +} + +fn r_take_rust_option_mut_ref_string(o: Option<&mut String>) { + let _ = o; +} + +fn r_take_rust_option_ref_vec(o: Option<&Vec>) { + let _ = o; +} + +fn r_take_rust_option_mut_ref_vec(o: Option<&mut Vec>) { + let _ = o; +} + +fn r_take_rust_option_ref_vec_string(o: Option<&Vec>) { + let _ = o; +} + +fn r_take_rust_option_mut_ref_vec_string(o: Option<&mut Vec>) { + let _ = o; +} + fn r_take_enum(e: ffi::Enum) { let _ = e; } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index b55235f10..d29650383 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -244,6 +244,54 @@ rust::Option c_return_rust_pin_mut_option_native(uint8_t& u) { return opt2; } +rust::Option*> c_return_rust_ref_option_vec_native(const rust::Vec& v) { + rust::Option*> opt; // Default constructor + opt.set(&v); // Set value + return opt; +} + +rust::Option*> c_return_rust_mut_option_vec_native(rust::Vec& v) { + rust::Option*> opt; // Default constructor + opt.set(&v); // Set value + return opt; +} + +rust::Option*> c_return_rust_ref_option_vec_shared(const rust::Vec& v) { + rust::Option*> opt; // Default constructor + opt.set(&v); // Set value + return opt; +} + +rust::Option*> c_return_rust_mut_option_vec_shared(rust::Vec& v) { + rust::Option*> opt; // Default constructor + opt.set(&v); // Set value + return opt; +} + +rust::Option*> c_return_rust_ref_option_vec_string(const rust::Vec& v) { + rust::Option*> opt; // Default constructor + opt.set(&v); // Set value + return opt; +} + +rust::Option*> c_return_rust_mut_option_vec_string(rust::Vec& v) { + rust::Option*> opt; // Default constructor + opt.set(&v); // Set value + return opt; +} + +rust::Option c_return_rust_ref_option_string(const rust::String& s) { + rust::Option opt; // Default constructor + opt.set(&s); // Set value + return opt; +} + +rust::Option c_return_rust_mut_option_string(rust::String& s) { + rust::Option opt; // Default constructor + opt.set(&s); // Set value + return opt; +} + size_t c_return_identity(size_t n) { return n; } size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; } @@ -636,6 +684,58 @@ void c_take_rust_pin_mut_option_native(rust::Option opt) { } } +void c_take_rust_ref_option_vec_shared(rust::Option*> opt) { + if (opt.has_value() && opt.value()->size() == 1 && (*opt.value())[0].z == 2020) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_mut_option_vec_shared(rust::Option*> opt) { + if (opt.has_value() && opt.value()->size() == 1 && (*opt.value())[0].z == 2020) { + (*opt.value())[0].z = 43; + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_ref_option_vec_native(rust::Option*> opt) { + if (opt.has_value() && opt.value()->size() == 1 && (*(opt.value()))[0] == 200) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_mut_option_vec_native(rust::Option*> opt) { + if (opt.has_value() && opt.value()->size() == 1 && (*(opt.value()))[0] == 200) { + (*opt.value())[0] = 201; + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_ref_option_vec_string(rust::Option*> opt) { + if (opt.has_value() && opt.value()->size() == 1 && (*(opt.value()))[0] == "2020") { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_mut_option_vec_string(rust::Option*> opt) { + if (opt.has_value() && opt.value()->size() == 1 && (*(opt.value()))[0] == "2020") { + (*opt.value())[0] = "2021"; + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_ref_option_string(rust::Option opt) { + if (opt.has_value() && (*(opt.value())) == "2020") { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_mut_option_string(rust::Option opt) { + if (opt.has_value() && *(opt.value()) == "2020") { + *(opt.value()) = "2021"; + 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(); @@ -727,6 +827,38 @@ rust::Option c_try_return_rust_pin_mut_option_native() { throw std::runtime_error("unimplemented"); } +rust::Option*> c_try_return_rust_ref_option_vec_native() { + throw std::runtime_error("unimplemented"); +} + +rust::Option*> c_try_return_rust_mut_option_vec_native() { + throw std::runtime_error("unimplemented"); +} + +rust::Option*> c_try_return_rust_ref_option_vec_shared() { + throw std::runtime_error("unimplemented"); +} + +rust::Option*> c_try_return_rust_mut_option_vec_shared() { + throw std::runtime_error("unimplemented"); +} + +rust::Option*> c_try_return_rust_ref_option_vec_string() { + throw std::runtime_error("unimplemented"); +} + +rust::Option*> c_try_return_rust_mut_option_vec_string() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_try_return_rust_ref_option_string() { + throw std::runtime_error("unimplemented"); +} + +rust::Option c_try_return_rust_mut_option_string() { + 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 7f4d071a8..0b5eacf6e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -125,6 +125,14 @@ rust::Option c_return_rust_pin_mut_option_opaque(C&); rust::Option c_return_rust_ref_option_native(const uint8_t&); rust::Option c_return_rust_mut_option_native(uint8_t&); rust::Option c_return_rust_pin_mut_option_native(uint8_t&); +rust::Option*> c_return_rust_ref_option_vec_native(const rust::Vec&); +rust::Option*> c_return_rust_mut_option_vec_native(rust::Vec&); +rust::Option*> c_return_rust_ref_option_vec_shared(const rust::Vec&); +rust::Option*> c_return_rust_mut_option_vec_shared(rust::Vec&); +rust::Option*> c_return_rust_ref_option_vec_string(const rust::Vec&); +rust::Option*> c_return_rust_mut_option_vec_string(rust::Vec&); +rust::Option c_return_rust_ref_option_string(const rust::String&); +rust::Option c_return_rust_mut_option_string(rust::String&); 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); @@ -183,6 +191,14 @@ 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); +void c_take_rust_ref_option_vec_native(rust::Option*>); +void c_take_rust_mut_option_vec_native(rust::Option*>); +void c_take_rust_ref_option_vec_shared(rust::Option*>); +void c_take_rust_mut_option_vec_shared(rust::Option*>); +void c_take_rust_ref_option_vec_string(rust::Option*>); +void c_take_rust_mut_option_vec_string(rust::Option*>); +void c_take_rust_ref_option_string(rust::Option); +void c_take_rust_mut_option_string(rust::Option); const SharedString &c_take_ref_shared_string(const SharedString &s); void c_take_callback(rust::Fn callback); void c_take_callback_ref(rust::Fn callback); @@ -215,6 +231,14 @@ 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(); +rust::Option*> c_try_return_rust_ref_option_vec_native(); +rust::Option*> c_try_return_rust_mut_option_vec_native(); +rust::Option*> c_try_return_rust_ref_option_vec_shared(); +rust::Option*> c_try_return_rust_mut_option_vec_shared(); +rust::Option*> c_try_return_rust_ref_option_vec_string(); +rust::Option*> c_try_return_rust_mut_option_vec_string(); +rust::Option c_try_return_rust_ref_option_string(); +rust::Option c_try_return_rust_mut_option_string(); size_t c_get_use_count(const std::weak_ptr &weak) noexcept; diff --git a/tests/test.rs b/tests/test.rs index 595e7ad05..70c476a07 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -106,6 +106,38 @@ fn test_c_return() { ffi::c_return_rust_pin_mut_option_native(Pin::new(&mut 200)), Some(Pin::new(&mut 200)) ); + assert_eq!( + Some(&vec![1, 2, 3]), + ffi::c_return_rust_ref_option_vec_native(&vec![1, 2, 3]) + ); + assert_eq!( + Some(&mut vec![1, 2, 3]), + ffi::c_return_rust_mut_option_vec_native(&mut vec![1, 2, 3]) + ); + assert_eq!( + Some(&vec![shared.clone()]), + ffi::c_return_rust_ref_option_vec_shared(&vec![shared.clone()]) + ); + assert_eq!( + Some(&mut vec![shared.clone()]), + ffi::c_return_rust_mut_option_vec_shared(&mut vec![shared.clone()]) + ); + assert_eq!( + Some(&vec!["2020".to_string()]), + ffi::c_return_rust_ref_option_vec_string(&vec!["2020".to_string()]) + ); + assert_eq!( + Some(&mut vec!["2020".to_string()]), + ffi::c_return_rust_mut_option_vec_string(&mut vec!["2020".to_string()]) + ); + assert_eq!( + Some(&"2020".to_string()), + ffi::c_return_rust_ref_option_string(&"2020".to_string()) + ); + assert_eq!( + Some(&mut "2020".to_string()), + ffi::c_return_rust_mut_option_string(&mut "2020".to_string()) + ); assert_eq!(2020, ffi::c_return_identity(2020)); assert_eq!(2021, ffi::c_return_sum(2020, 1)); match ffi::c_return_enum(0) { @@ -253,6 +285,26 @@ fn test_c_take() { check!(ffi::c_take_rust_pin_mut_option_native(Some(Pin::new( &mut 200 )))); + check!(ffi::c_take_rust_ref_option_vec_shared(Some(&vec![ + ffi::Shared { z: 2020 } + ]))); + check!(ffi::c_take_rust_mut_option_vec_shared(Some(&mut vec![ + ffi::Shared { z: 2020 } + ]))); + check!(ffi::c_take_rust_ref_option_vec_native(Some(&vec![200]))); + check!(ffi::c_take_rust_mut_option_vec_native(Some(&mut vec![200]))); + check!(ffi::c_take_rust_ref_option_vec_string(Some(&vec![ + "2020".to_string() + ]))); + check!(ffi::c_take_rust_mut_option_vec_string(Some(&mut vec![ + "2020".to_string() + ]))); + check!(ffi::c_take_rust_ref_option_string(Some( + &"2020".to_string() + ))); + check!(ffi::c_take_rust_mut_option_string(Some( + &mut "2020".to_string() + ))); check!(ffi::c_take_enum(ffi::Enum::AVal)); check!(ffi::c_take_ns_enum(ffi::AEnum::AAVal)); diff --git a/tests/ui/option_safe_string.stderr b/tests/ui/option_safe_string.stderr index c01f74e01..af227f30b 100644 --- a/tests/ui/option_safe_string.stderr +++ b/tests/ui/option_safe_string.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of `cxx::private::assert_option_safe::__SizeCheck:: 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:75:13 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: core::mem::size_of::>() == core::mem::size_of::()', $DIR/src/rust_option.rs:94:13 | = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/option_safe_unsized.stderr b/tests/ui/option_safe_unsized.stderr index 683dbfb84..ce272f60d 100644 --- a/tests/ui/option_safe_unsized.stderr +++ b/tests/ui/option_safe_unsized.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of `cxx::private::assert_option_safe::__SizeCheck::<&st --> 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:75:13 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: core::mem::size_of::>() == core::mem::size_of::()', $DIR/src/rust_option.rs:94:13 | = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/option_safe_usize.stderr b/tests/ui/option_safe_usize.stderr index 1135e104e..4014220b1 100644 --- a/tests/ui/option_safe_usize.stderr +++ b/tests/ui/option_safe_usize.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of `cxx::private::assert_option_safe::__SizeCheck:: 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:73:13 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: core::mem::size_of::>() == core::mem::size_of::()', $DIR/src/rust_option.rs:92:13 | = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/option_string_ref.rs b/tests/ui/option_string_ref.rs deleted file mode 100644 index 03c2d9af5..000000000 --- a/tests/ui/option_string_ref.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[cxx::bridge] -mod ffi { - extern "Rust" { - fn f() -> Option<&'static String>; - } -} - -fn f() -> Option<&'static String> { - unimplemented!() -} - -fn main() {} diff --git a/tests/ui/option_string_ref.stderr b/tests/ui/option_string_ref.stderr deleted file mode 100644 index c83c400c5..000000000 --- a/tests/ui/option_string_ref.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unsupported element type of Option - --> tests/ui/option_string_ref.rs:4:19 - | -4 | fn f() -> Option<&'static String>; - | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/option_vec_ref.rs b/tests/ui/option_vec_ref.rs deleted file mode 100644 index 72881bd2c..000000000 --- a/tests/ui/option_vec_ref.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[cxx::bridge] -mod ffi { - extern "Rust" { - fn f() -> Option<&'static Vec>; - } -} - -fn f() -> Option<&'static Vec> { - unimplemented!() -} - -fn main() {} diff --git a/tests/ui/option_vec_ref.stderr b/tests/ui/option_vec_ref.stderr deleted file mode 100644 index 2fcef269c..000000000 --- a/tests/ui/option_vec_ref.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unsupported element type of Option - --> tests/ui/option_vec_ref.rs:4:19 - | -4 | fn f() -> Option<&'static Vec>; - | ^^^^^^^^^^^^^^^^^^^^^^^^ From db3ae2853227d627b44783311e52522433870a93 Mon Sep 17 00:00:00 2001 From: Eric Kuecks Date: Mon, 26 Feb 2024 11:03:24 -0800 Subject: [PATCH 3/3] [Option] Add book documentation --- book/src/SUMMARY.md | 1 + book/src/binding/option.md | 89 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 book/src/binding/option.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 2d2502ee7..3bc3f5bd7 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -32,6 +32,7 @@ - [SharedPtr\ — std::shared\_ptr\](binding/sharedptr.md) - [Vec\ — rust::Vec\](binding/vec.md) - [CxxVector\ — std::vector\](binding/cxxvector.md) + - [Option\ — rust::Option\](binding/option.md) - [*mut T, *const T raw pointers](binding/rawptr.md) - [Function pointers](binding/fn.md) - [Result\](binding/result.md) diff --git a/book/src/binding/option.md b/book/src/binding/option.md new file mode 100644 index 000000000..c9996df01 --- /dev/null +++ b/book/src/binding/option.md @@ -0,0 +1,89 @@ +{{#title rust::Option — Rust ♡ C++}} +# rust::Option\ + +### Public API: + +```cpp,hidelines=... +// rust/cxx.h +... +...namespace rust { + +template +class Option final { +public: + Option() noexcept; + Option(Option&&) noexcept; + Option(T&&) noexcept; + ~Option() noexcept; + + const T *operator->() const; + const T &operator*() const; + T *operator->(); + T &operator*(); + + Option& operator=(Option&&) noexcept; + + bool has_value() const noexcept; + T& value() noexcept; + void reset(); + void set(T&&) noexcept; +}; +...} // namespace rust +``` + +### Restrictions: + +Option only supports pointer-sized references and Box-es; that is, no +fat pointers like &str (though &String is supported) or Box<[u8]>. On the +C++ side, Option<&T> becomes rust::Option (and similar for +mutable references), but the pointer is guaranteed to be non-null if the +Option has a value. Also, you can only pass Options themselves by value. +&Option is not allowed. + +## Example + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + struct Shared { + v: u32, + } + + unsafe extern "C++" { + include!("example/include/example.h"); + + fn f(elements: Option<&Shared>); + } +} + +fn main() { + let shared = Shared { v: 3 }; + ffi::f(Some(&shared)); + ffi::f(None); +} +``` + +```cpp +// include/example.h + +#pragma once +#include "example/src/main.rs.h" +#include "rust/cxx.h" + +void f(rust::Option); +``` + +```cpp +// src/example.cc + +#include "example/include/example.h" + +void f(rust::Option o) { + if (o.has_value()) { + // Pointer is guaranteed to be non-null + std::cout << shared.value()->v << std::endl; + } +} +```