From 70610dc91f5ade0d0977aa4b2462b523f54ca4a9 Mon Sep 17 00:00:00 2001 From: Eric Kuecks Date: Mon, 2 Sep 2024 23:14:24 -0700 Subject: [PATCH] Support Option and Option --- gen/src/builtin.rs | 2 + gen/src/write.rs | 197 +++++++-- include/cxx.h | 693 +++++++++++++++++++------------ macro/src/expand.rs | 130 +++++- src/cxx.cc | 224 ++++++++-- src/rust_option.rs | 108 ++--- src/symbols/rust_option.rs | 143 +++++-- syntax/check.rs | 6 + syntax/instantiate.rs | 16 + syntax/types.rs | 3 +- tests/ffi/lib.rs | 60 +++ tests/ffi/tests.cc | 62 +++ tests/ffi/tests.h | 8 + tests/test.rs | 20 + tests/ui/option_not_sized.stderr | 15 +- tests/ui/option_string.rs | 12 - tests/ui/option_string.stderr | 5 - tests/ui/option_vec.rs | 12 - tests/ui/option_vec.stderr | 5 - 19 files changed, 1246 insertions(+), 475 deletions(-) delete mode 100644 tests/ui/option_string.rs delete mode 100644 tests/ui/option_string.stderr delete mode 100644 tests/ui/option_vec.rs delete mode 100644 tests/ui/option_vec.stderr diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index 457a91974..da72e637d 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -52,6 +52,8 @@ pub(super) fn write(out: &mut OutFile) { let out = &mut builtin.content; if builtin.rust_option { + builtin.rust_string = true; + builtin.rust_vec = true; builtin.rust_box = true; } diff --git a/gen/src/write.rs b/gen/src/write.rs index e3df6f8e2..6d0df1d84 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -820,9 +820,26 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { if i > 0 { write!(out, ", "); } - if let Type::RustBox(_) | Type::RustOption(_) = &arg.ty { + if let Type::RustBox(_) = &arg.ty { write_type(out, &arg.ty); write!(out, "::from_raw({})", arg.name.cxx); + } else if let Type::RustOption(inner) = &arg.ty { + match &inner.inner { + Type::RustVec(_) => { + out.builtin.unsafe_bitcopy = true; + write_type(out, &arg.ty); + write!(out, "(::rust::unsafe_bitcopy, *{})", arg.name.cxx,); + } + Type::Ident(ty) if ty.rust == RustString => { + out.builtin.unsafe_bitcopy = true; + write_type(out, &arg.ty); + write!(out, "(::rust::unsafe_bitcopy, *{})", arg.name.cxx,); + } + _ => { + write_type(out, &arg.ty); + write!(out, "::from_raw({})", arg.name.cxx); + } + } } else if let Type::UniquePtr(_) = &arg.ty { write_type(out, &arg.ty); write!(out, "({})", arg.name.cxx); @@ -837,6 +854,8 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.builtin.unsafe_bitcopy = true; write_type(out, &arg.ty); write!(out, "(::rust::unsafe_bitcopy, *{})", arg.name.cxx); + } else if let Type::RustOption(_) = arg.ty { + write!(out, "std::move(* {})", arg.name.cxx); } else if out.types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, "::std::move(*{})", arg.name.cxx); @@ -846,7 +865,11 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { } write!(out, ")"); match &efn.ret { - Some(Type::RustBox(_) | Type::RustOption(_)) => write!(out, ".into_raw()"), + Some(Type::RustBox(_)) => write!(out, ".into_raw()"), + Some(Type::RustOption(inner)) => match inner.inner { + Type::Ident(_) | Type::RustVec(_) => {} + _ => write!(out, ".into_raw()"), + }, Some(Type::UniquePtr(_)) => write!(out, ".release()"), Some(Type::Str(_) | Type::SliceRef(_)) if !indirect_return => write!(out, ")"), _ => {} @@ -1051,10 +1074,17 @@ fn write_rust_function_shim_impl( } else if let Some(ret) = &sig.ret { write!(out, "return "); match ret { - Type::RustBox(_) | Type::RustOption(_) => { + Type::RustBox(_) => { write_type(out, ret); write!(out, "::from_raw("); } + Type::RustOption(inner) => match inner.inner { + Type::Ident(_) | Type::RustVec(_) => {} + _ => { + write_type(out, ret); + write!(out, "::from_raw("); + } + } Type::UniquePtr(_) => { write_type(out, ret); write!(out, "("); @@ -1092,7 +1122,11 @@ fn write_rust_function_shim_impl( } write!(out, "{}", arg.name.cxx); match &arg.ty { - Type::RustBox(_) | Type::RustOption(_) => write!(out, ".into_raw()"), + Type::RustBox(_) => write!(out, ".into_raw()"), + Type::RustOption(inner) => match inner.inner { + Type::Ident(_) | Type::RustVec(_) => write!(out, "$.value"), + _ => write!(out, ".into_raw()"), + }, Type::UniquePtr(_) => write!(out, ".release()"), ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} @@ -1114,15 +1148,21 @@ fn write_rust_function_shim_impl( } write!(out, ")"); if !indirect_return { - if let Some( - Type::RustBox(_) - | Type::UniquePtr(_) - | Type::Str(_) - | Type::SliceRef(_) - | Type::RustOption(_), - ) = &sig.ret - { - write!(out, ")"); + if let Some(ret) = &sig.ret { + if let + Type::RustBox(_) + | Type::UniquePtr(_) + | Type::Str(_) + | Type::SliceRef(_) + = ret + { + write!(out, ")"); + } else if let Type::RustOption(inner) = &ret { + match &inner.inner { + Type::Ident(_) | Type::RustVec(_) => {} + _ => write!(out, ")"), + } + } } } writeln!(out, ";"); @@ -1172,7 +1212,14 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { } write!(out, "*"); } - Type::RustOption(ty) => write_indirect_return_type(out, &ty.inner), + Type::RustOption(ty) => match &ty.inner { + Type::RustBox(_) | Type::Ref(_) => write_indirect_return_type(out, &ty.inner), + _ => { + write!(out, "::rust::Option<"); + write_indirect_return_type(out, &ty.inner); + write!(out, ">"); + } + }, _ => write_type(out, ty), } } @@ -1180,7 +1227,7 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { write_indirect_return_type(out, ty); match ty { - Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) | Type::RustOption(_) => {} + Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} Type::Str(_) | Type::SliceRef(_) => write!(out, " "), _ => write_space_after_type(out, ty), } @@ -1200,6 +1247,11 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: Option<&Type>) { write!(out, "const "); } write!(out, "*"); + } + Type::RustVec(_) | Type::Ident(_) => { + if out.types.needs_indirect_abi(&ty.inner) { + write!(out, "void "); + } } _ => unreachable!(), }, @@ -1237,7 +1289,11 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var) { } write!(out, "*"); } - _ => unreachable!(), + _ => { + write!(out, "::rust::Option<"); + write_type_space(out, &ty.inner); + write!(out, "> const "); + } }, _ => write_type_space(out, &arg.ty), } @@ -1398,6 +1454,8 @@ enum RustOption<'a> { MutRef(&'a Ident), RefVec(&'a Ident), MutRefVec(&'a Ident), + Vec(&'a Ident), + Ident(&'a Ident), } trait ToTypename { @@ -1436,6 +1494,10 @@ impl<'a> ToTypename for RustOption<'a> { RustOption::MutRefVec(inner) => { format!("::rust::cxxbridge1::Vec<{}>&", inner.to_typename(types)) } + RustOption::Vec(inner) => { + format!("::rust::cxxbridge1::Vec<{}>", inner.to_typename(types)) + } + RustOption::Ident(ident) => ident.to_typename(types), } } } @@ -1464,13 +1526,17 @@ impl<'a> ToMangled for UniquePtr<'a> { impl<'a> ToMangled for RustOption<'a> { fn to_mangled(&self, types: &Types) -> Symbol { match self { - RustOption::RustBox(inner) => symbol::join(&[&"Box", &inner.to_mangled(types)]), - RustOption::Ref(inner) => symbol::join(&[&"const", &inner.to_mangled(types)]), - RustOption::MutRef(inner) => symbol::join(&[&inner.to_mangled(types)]), + RustOption::RustBox(inner) => symbol::join(&[&"rust_box", &inner.to_mangled(types)]), + RustOption::Ref(inner) => symbol::join(&[&"const", &"ref", &inner.to_mangled(types)]), + RustOption::MutRef(inner) => symbol::join(&[&"ref", &inner.to_mangled(types)]), RustOption::RefVec(inner) => { - symbol::join(&[&"const", &"Vec", &inner.to_mangled(types)]) + symbol::join(&[&"const", &"ref", &"rust_vec", &inner.to_mangled(types)]) + } + RustOption::MutRefVec(inner) => { + symbol::join(&[&"ref", &"rust_vec", &inner.to_mangled(types)]) } - RustOption::MutRefVec(inner) => symbol::join(&[&"Vec", &inner.to_mangled(types)]), + RustOption::Vec(inner) => symbol::join(&[&"rust_vec", &inner.to_mangled(types)]), + RustOption::Ident(ident) => symbol::join(&[&ident.to_mangled(types)]), } } } @@ -1584,11 +1650,11 @@ 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, is_const, value_type) = match inner { + let (element, is_const, is_direct, value_type) = match inner { OptionInner::RustBox(key) => { let element = RustOption::RustBox(key.rust); let value_type = element.to_typename(out.types); - (element, false, value_type) + (element, false, false, value_type) } OptionInner::Ref(key) => { if out.types.try_resolve(key.rust).is_none() { @@ -1596,7 +1662,7 @@ fn write_rust_option_extern(out: &mut OutFile, inner: OptionInner) { } let resolve = out.types.resolve(&key); let value_type = resolve.name.to_fully_qualified(); - (RustOption::Ref(key.rust), true, value_type) + (RustOption::Ref(key.rust), true, false, value_type) } OptionInner::MutRef(key) => { if out.types.try_resolve(key.rust).is_none() { @@ -1604,7 +1670,7 @@ fn write_rust_option_extern(out: &mut OutFile, inner: OptionInner) { } let resolve = out.types.resolve(&key); let value_type = resolve.name.to_fully_qualified(); - (RustOption::MutRef(key.rust), false, value_type) + (RustOption::MutRef(key.rust), false, false, value_type) } OptionInner::RefVec(key) => { if out.types.try_resolve(key.rust).is_none() { @@ -1615,7 +1681,7 @@ fn write_rust_option_extern(out: &mut OutFile, inner: OptionInner) { "::rust::cxxbridge1::Vec<{}>", resolve.name.to_fully_qualified() ); - (RustOption::RefVec(key.rust), true, value_type) + (RustOption::RefVec(key.rust), true, false, value_type) } OptionInner::MutRefVec(key) => { if out.types.try_resolve(key.rust).is_none() { @@ -1626,7 +1692,26 @@ fn write_rust_option_extern(out: &mut OutFile, inner: OptionInner) { "::rust::cxxbridge1::Vec<{}>", resolve.name.to_fully_qualified() ); - (RustOption::MutRefVec(key.rust), false, value_type) + (RustOption::MutRefVec(key.rust), false, false, value_type) + } + OptionInner::Vec(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + let resolve = out.types.resolve(&key); + let value_type = format!( + "::rust::cxxbridge1::Vec<{}>", + resolve.name.to_fully_qualified() + ); + (RustOption::Vec(key.rust), false, true, value_type) + } + OptionInner::Ident(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + let resolve = out.types.resolve(&key); + let value_type = resolve.name.to_fully_qualified(); + (RustOption::Ident(key.rust), false, true, value_type) } }; let inner = element.to_typename(out.types); @@ -1658,6 +1743,22 @@ fn write_rust_option_extern(out: &mut OutFile, inner: OptionInner) { "void cxxbridge1$rust_option${}$set(::rust::Option<{}> *ptr, {} const *value) noexcept;", instance, inner, value_type ); + } else if is_direct { + writeln!( + out, + "{} const * cxxbridge1$rust_option${}$value_const(::rust::Option<{}> const *ptr) noexcept;", + value_type, instance, inner + ); + writeln!( + out, + "{}* cxxbridge1$rust_option${}$value(::rust::Option<{}> *ptr) noexcept;", + value_type, instance, inner + ); + writeln!( + out, + "void cxxbridge1$rust_option${}$set(::rust::Option<{}> *ptr, {} value) noexcept;", + instance, inner, value_type + ); } else { writeln!( out, @@ -1798,31 +1899,43 @@ fn write_rust_vec_impl(out: &mut OutFile, key: NamedImplKey) { } fn write_rust_option_impl(out: &mut OutFile, inner: OptionInner) { - let (element, is_const, value_needs_ref) = match inner { - OptionInner::RustBox(key) => (RustOption::RustBox(key.rust), false, true), + let (element, is_const, is_direct, value_needs_ref) = match inner { + OptionInner::RustBox(key) => (RustOption::RustBox(key.rust), false, false, true), OptionInner::Ref(key) => { if out.types.try_resolve(key.rust).is_none() { return; } - (RustOption::Ref(key.rust), true, false) + (RustOption::Ref(key.rust), true, false, false) } OptionInner::MutRef(key) => { if out.types.try_resolve(key.rust).is_none() { return; } - (RustOption::MutRef(key.rust), false, false) + (RustOption::MutRef(key.rust), false, false, false) } OptionInner::RefVec(key) => { if out.types.try_resolve(key.rust).is_none() { return; } - (RustOption::RefVec(key.rust), true, false) + (RustOption::RefVec(key.rust), true, false, false) } OptionInner::MutRefVec(key) => { if out.types.try_resolve(key.rust).is_none() { return; } - (RustOption::MutRefVec(key.rust), false, false) + (RustOption::MutRefVec(key.rust), false, false, false) + } + OptionInner::Vec(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + (RustOption::Vec(key.rust), false, true, true) + } + OptionInner::Ident(key) => { + if out.types.try_resolve(key.rust).is_none() { + return; + } + (RustOption::Ident(key.rust), false, true, true) } }; let inner = element.to_typename(out.types); @@ -1897,11 +2010,19 @@ fn write_rust_option_impl(out: &mut OutFile, inner: OptionInner) { 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, &value);", - instance - ); + if is_direct { + writeln!( + out, + " return cxxbridge1$rust_option${}$set(this, value);", + instance + ); + } else { + writeln!( + out, + " return cxxbridge1$rust_option${}$set(this, &value);", + instance + ); + } writeln!(out, "}}"); } diff --git a/include/cxx.h b/include/cxx.h index 3ac8c31c7..750583419 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -300,6 +300,90 @@ class Box final { }; #endif // CXXBRIDGE1_RUST_BOX +#ifndef CXXBRIDGE1_RUST_VEC +// https://cxx.rs/binding/vec.html +template +class Vec final { +public: + using value_type = T; + + Vec() noexcept; + Vec(std::initializer_list); + Vec(const Vec &); + Vec(Vec &&) noexcept; + ~Vec() noexcept; + + Vec &operator=(Vec &&) &noexcept; + Vec &operator=(const Vec &) &; + + std::size_t size() const noexcept; + bool empty() const noexcept; + const T *data() const noexcept; + T *data() noexcept; + std::size_t capacity() const noexcept; + + const T &operator[](std::size_t n) const noexcept; + const T &at(std::size_t n) const; + const T &front() const noexcept; + const T &back() const noexcept; + + T &operator[](std::size_t n) noexcept; + T &at(std::size_t n); + T &front() noexcept; + T &back() noexcept; + + void reserve(std::size_t new_cap); + void push_back(const T &value); + void push_back(T &&value); + template + void emplace_back(Args &&...args); + void truncate(std::size_t len); + void clear(); + + using iterator = typename Slice::iterator; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = typename Slice::iterator; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + void swap(Vec &) noexcept; + + // Internal API only intended for the cxxbridge code generator. + Vec(unsafe_bitcopy_t, const Vec &) noexcept; + +private: + void reserve_total(std::size_t new_cap) noexcept; + void set_len(std::size_t len) noexcept; + void drop() noexcept; + + friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } + + // Size and alignment statically verified by rust_vec.rs. + std::array repr; +}; +#endif // CXXBRIDGE1_RUST_VEC + +#ifndef CXXBRIDGE1_RUST_FN +// https://cxx.rs/binding/fn.html +template +class Fn; + +template +class Fn final { +public: + Ret operator()(Args... args) const noexcept; + Fn operator*() const noexcept; + +private: + Ret (*trampoline)(Args..., void *fn) noexcept; + void *fn; +}; +#endif // CXXBRIDGE1_RUST_FN + #ifndef CXXBRIDGE1_RUST_OPTION template class Option final {}; @@ -318,6 +402,9 @@ class Option { T& operator*() noexcept; T* operator->() noexcept; + bool operator==(const Option &) const noexcept; + bool operator!=(const Option &) const noexcept; + bool has_value() const noexcept; const T& value() const noexcept; T& value() noexcept; @@ -345,6 +432,9 @@ class Option { const T& operator*() const noexcept; const T* operator->() const noexcept; + bool operator==(const Option &) const noexcept; + bool operator!=(const Option &) const noexcept; + bool has_value() const noexcept; const T& value() const noexcept; void reset(); @@ -371,6 +461,9 @@ class Option> { Box& operator*() noexcept; Box* operator->() noexcept; + bool operator==(const Option &) const noexcept; + bool operator!=(const Option &) const noexcept; + bool has_value() const noexcept; const Box& value() const noexcept; Box& value() noexcept; @@ -386,91 +479,68 @@ class Option> { void drop() noexcept; }; -#endif // CXXBRIDGE1_RUST_OPTION -#ifndef CXXBRIDGE1_RUST_VEC -// https://cxx.rs/binding/vec.html template -class Vec final { +class Option> { public: - using value_type = T; - - Vec() noexcept; - Vec(std::initializer_list); - Vec(const Vec &); - Vec(Vec &&) noexcept; - ~Vec() noexcept; - - Vec &operator=(Vec &&) &noexcept; - Vec &operator=(const Vec &) &; - - std::size_t size() const noexcept; - bool empty() const noexcept; - const T *data() const noexcept; - T *data() noexcept; - std::size_t capacity() const noexcept; - - const T &operator[](std::size_t n) const noexcept; - const T &at(std::size_t n) const; - const T &front() const noexcept; - const T &back() const noexcept; - - T &operator[](std::size_t n) noexcept; - T &at(std::size_t n); - T &front() noexcept; - T &back() noexcept; - - void reserve(std::size_t new_cap); - void push_back(const T &value); - void push_back(T &&value); - template - void emplace_back(Args &&...args); - void truncate(std::size_t len); - void clear(); - - using iterator = typename Slice::iterator; - iterator begin() noexcept; - iterator end() noexcept; + Option() noexcept; + Option(Option&&) noexcept; + Option(Vec&&) noexcept; + ~Option() noexcept; - using const_iterator = typename Slice::iterator; - const_iterator begin() const noexcept; - const_iterator end() const noexcept; - const_iterator cbegin() const noexcept; - const_iterator cend() const noexcept; + Option>& operator=(Option&&) noexcept; + const Vec& operator*() const noexcept; + const Vec* operator->() const noexcept; + Vec& operator*() noexcept; + Vec* operator->() noexcept; - void swap(Vec &) noexcept; + bool has_value() const noexcept; + const Vec& value() const noexcept; + Vec& value() noexcept; + void reset(); + void set(Vec) noexcept; // Internal API only intended for the cxxbridge code generator. - Vec(unsafe_bitcopy_t, const Vec &) noexcept; - + Option(unsafe_bitcopy_t, const Option &) noexcept; private: - void reserve_total(std::size_t new_cap) noexcept; - void set_len(std::size_t len) noexcept; - void drop() noexcept; - - friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } - - // Size and alignment statically verified by rust_vec.rs. std::array repr; -}; -#endif // CXXBRIDGE1_RUST_VEC - -#ifndef CXXBRIDGE1_RUST_FN -// https://cxx.rs/binding/fn.html -template -class Fn; -template -class Fn final { -public: - Ret operator()(Args... args) const noexcept; - Fn operator*() const noexcept; + void drop() noexcept; +}; -private: - Ret (*trampoline)(Args..., void *fn) noexcept; - void *fn; +#define OPTION_DIRECT_DECL(INNER, REPR, COUNT) \ +template<> \ +class Option { \ +public: \ + Option() noexcept; \ + Option(Option&&) noexcept; \ + Option(INNER&&) noexcept; \ + ~Option() noexcept; \ + \ + Option& operator=(Option&&) noexcept; \ + const INNER& operator*() const noexcept; \ + const INNER* operator->() const noexcept; \ + INNER& operator*() noexcept; \ + INNER* operator->() noexcept; \ + \ + bool operator==(const Option &) const noexcept; \ + bool operator!=(const Option &) const noexcept; \ + \ + bool has_value() const noexcept; \ + const INNER& value() const noexcept; \ + INNER& value() noexcept; \ + void reset(); \ + void set(INNER) noexcept; \ + \ + Option(unsafe_bitcopy_t, const Option &) noexcept; \ +private: \ + std::array repr; \ + \ + void drop() noexcept; \ }; -#endif // CXXBRIDGE1_RUST_FN + +OPTION_DIRECT_DECL(String, std::uintptr_t, 3) +#endif // CXXBRIDGE1_RUST_OPTION #ifndef CXXBRIDGE1_RUST_ERROR #define CXXBRIDGE1_RUST_ERROR @@ -917,80 +987,273 @@ template Box::Box(uninit) noexcept {} #endif // CXXBRIDGE1_RUST_BOX +#ifndef CXXBRIDGE1_RUST_VEC +#define CXXBRIDGE1_RUST_VEC +template +Vec::Vec(std::initializer_list init) : Vec{} { + this->reserve_total(init.size()); + std::move(init.begin(), init.end(), std::back_inserter(*this)); +} -#ifndef CXXBRIDGE1_RUST_OPTION -#define CXXBRIDGE1_RUST_OPTION template -Option::Option(Option&& other) noexcept { - new (this) Option(); - if (other.has_value()) { - set(other.value()); - } - new (&other) Option(); +Vec::Vec(const Vec &other) : Vec() { + this->reserve_total(other.size()); + std::copy(other.begin(), other.end(), std::back_inserter(*this)); } template -Option::Option(T& value) noexcept { - new (this) Option(); - set(value); +Vec::Vec(Vec &&other) noexcept : repr(other.repr) { + new (&other) Vec(); } template -Option::~Option() noexcept { +Vec::~Vec() noexcept { this->drop(); } template -Option& Option::operator=(Option&& other) noexcept { - this->reset(); - if (other.has_value()) { - set(other.value()); +Vec &Vec::operator=(Vec &&other) &noexcept { + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + return *this; +} + +template +Vec &Vec::operator=(const Vec &other) & { + if (this != &other) { + this->drop(); + new (this) Vec(other); } - new (&other) Option(); return *this; } template -const T& Option::operator*() const noexcept { - return value(); +bool Vec::empty() const noexcept { + return this->size() == 0; } template -const T* Option::operator->() const noexcept { - return &value(); +T *Vec::data() noexcept { + return const_cast(const_cast *>(this)->data()); } template -T& Option::operator*() noexcept { - return value(); +const T &Vec::operator[](std::size_t n) const noexcept { + assert(n < this->size()); + auto data = reinterpret_cast(this->data()); + return *reinterpret_cast(data + n * size_of()); } template -T* Option::operator->() noexcept { - return &value(); +const T &Vec::at(std::size_t n) const { + if (n >= this->size()) { + panic("rust::Vec index out of range"); + } + return (*this)[n]; } template -void Option::reset() { - this->drop(); - new (this) Option(); +const T &Vec::front() const noexcept { + assert(!this->empty()); + return (*this)[0]; } template -Option Option::from_raw(T* ptr) noexcept { - Option opt{*ptr}; - return opt; +const T &Vec::back() const noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; } template -T* Option::into_raw() noexcept { - if (has_value()) { - return &value(); - } else { - return nullptr; - } +T &Vec::operator[](std::size_t n) noexcept { + assert(n < this->size()); + auto data = reinterpret_cast(this->data()); + return *reinterpret_cast(data + n * size_of()); } -//////// Option //////// + +template +T &Vec::at(std::size_t n) { + if (n >= this->size()) { + panic("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template +T &Vec::front() noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template +T &Vec::back() noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template +void Vec::reserve(std::size_t new_cap) { + this->reserve_total(new_cap); +} + +template +void Vec::push_back(const T &value) { + this->emplace_back(value); +} + +template +void Vec::push_back(T &&value) { + this->emplace_back(std::move(value)); +} + +template +template +void Vec::emplace_back(Args &&...args) { + auto size = this->size(); + this->reserve_total(size + 1); + ::new (reinterpret_cast(reinterpret_cast(this->data()) + + size * size_of())) + T(std::forward(args)...); + this->set_len(size + 1); +} + +template +void Vec::clear() { + this->truncate(0); +} + +template +typename Vec::iterator Vec::begin() noexcept { + return Slice(this->data(), this->size()).begin(); +} + +template +typename Vec::iterator Vec::end() noexcept { + return Slice(this->data(), this->size()).end(); +} + +template +typename Vec::const_iterator Vec::begin() const noexcept { + return this->cbegin(); +} + +template +typename Vec::const_iterator Vec::end() const noexcept { + return this->cend(); +} + +template +typename Vec::const_iterator Vec::cbegin() const noexcept { + return Slice(this->data(), this->size()).begin(); +} + +template +typename Vec::const_iterator Vec::cend() const noexcept { + return Slice(this->data(), this->size()).end(); +} + +template +void Vec::swap(Vec &rhs) noexcept { + using std::swap; + swap(this->repr, rhs.repr); +} + +// Internal API only intended for the cxxbridge code generator. +template +Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} +#endif // CXXBRIDGE1_RUST_VEC + +#ifndef CXXBRIDGE1_RUST_OPTION +#define CXXBRIDGE1_RUST_OPTION +//////// Option //////// +template +Option::Option(Option&& other) noexcept { + new (this) Option(); + if (other.has_value()) { + set(other.value()); + } + new (&other) Option(); +} + +template +Option::Option(T& value) noexcept { + new (this) Option(); + set(value); +} + +template +Option::~Option() noexcept { + this->drop(); +} + +template +Option& Option::operator=(Option&& other) noexcept { + this->reset(); + if (other.has_value()) { + set(other.value()); + } + new (&other) Option(); + return *this; +} + +template +const T& Option::operator*() const noexcept { + return value(); +} + +template +const T* Option::operator->() const noexcept { + return &value(); +} + +template +T& Option::operator*() noexcept { + return value(); +} + +template +T* Option::operator->() noexcept { + return &value(); +} + +template +bool Option::operator==(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() == other.value(); + } + return this->has_value() == other.has_value(); +} + +template +bool Option::operator!=(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() != other.value(); + } + return this->has_value() != other.has_value(); +} + +template +void Option::reset() { + this->drop(); + new (this) Option(); +} + +template +Option Option::from_raw(T* ptr) noexcept { + Option opt{*ptr}; + return opt; +} + +template +T* Option::into_raw() noexcept { + if (has_value()) { + return &value(); + } else { + return nullptr; + } +} +//////// Option //////// template Option::Option(const Option& other) noexcept { new (this) Option(); @@ -1048,6 +1311,22 @@ const T* Option::operator->() const noexcept { return &value(); } +template +bool Option::operator==(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() == other.value(); + } + return this->has_value() == other.has_value(); +} + +template +bool Option::operator!=(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() != other.value(); + } + return this->has_value() != other.has_value(); +} + template void Option::reset() { this->drop(); @@ -1119,6 +1398,22 @@ Box* Option>::operator->() noexcept { return &value(); } +template +bool Option>::operator==(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() == other.value(); + } + return this->has_value() == other.has_value(); +} + +template +bool Option>::operator!=(const Option& other) const noexcept { + if (this->has_value() && other.has_value()) { + return this->value() != other.value(); + } + return this->has_value() != other.has_value(); +} + template void Option>::reset() { this->drop(); @@ -1139,184 +1434,66 @@ T* Option>::into_raw() noexcept { return nullptr; } } -#endif // CXXBRIDGE1_RUST_OPTION - -#ifndef CXXBRIDGE1_RUST_VEC -#define CXXBRIDGE1_RUST_VEC -template -Vec::Vec(std::initializer_list init) : Vec{} { - this->reserve_total(init.size()); - std::move(init.begin(), init.end(), std::back_inserter(*this)); -} - -template -Vec::Vec(const Vec &other) : Vec() { - this->reserve_total(other.size()); - std::copy(other.begin(), other.end(), std::back_inserter(*this)); -} - +//////// Option> //////////// template -Vec::Vec(Vec &&other) noexcept : repr(other.repr) { - new (&other) Vec(); +Option>::Option(Option&& other) noexcept { + new (this) Option(); + if (other.has_value()) { + set(std::move(other.value())); + } + new (&other) Option(); } template -Vec::~Vec() noexcept { - this->drop(); +Option>::Option(Vec&& value) noexcept { + new (this) Option(); + set(std::move(value)); } template -Vec &Vec::operator=(Vec &&other) &noexcept { +Option>::~Option() noexcept { this->drop(); - this->repr = other.repr; - new (&other) Vec(); - return *this; } template -Vec &Vec::operator=(const Vec &other) & { - if (this != &other) { - this->drop(); - new (this) Vec(other); +Option>& Option>::operator=(Option&& other) noexcept { + this->reset(); + if (other.has_value()) { + set(std::move(other.value())); } + new (&other) Option(); return *this; } template -bool Vec::empty() const noexcept { - return this->size() == 0; -} - -template -T *Vec::data() noexcept { - return const_cast(const_cast *>(this)->data()); -} - -template -const T &Vec::operator[](std::size_t n) const noexcept { - assert(n < this->size()); - auto data = reinterpret_cast(this->data()); - return *reinterpret_cast(data + n * size_of()); -} - -template -const T &Vec::at(std::size_t n) const { - if (n >= this->size()) { - panic("rust::Vec index out of range"); - } - return (*this)[n]; -} - -template -const T &Vec::front() const noexcept { - assert(!this->empty()); - return (*this)[0]; -} - -template -const T &Vec::back() const noexcept { - assert(!this->empty()); - return (*this)[this->size() - 1]; -} - -template -T &Vec::operator[](std::size_t n) noexcept { - assert(n < this->size()); - auto data = reinterpret_cast(this->data()); - return *reinterpret_cast(data + n * size_of()); -} - -template -T &Vec::at(std::size_t n) { - if (n >= this->size()) { - panic("rust::Vec index out of range"); - } - return (*this)[n]; -} - -template -T &Vec::front() noexcept { - assert(!this->empty()); - return (*this)[0]; -} - -template -T &Vec::back() noexcept { - assert(!this->empty()); - return (*this)[this->size() - 1]; -} - -template -void Vec::reserve(std::size_t new_cap) { - this->reserve_total(new_cap); -} - -template -void Vec::push_back(const T &value) { - this->emplace_back(value); -} - -template -void Vec::push_back(T &&value) { - this->emplace_back(std::move(value)); -} - -template -template -void Vec::emplace_back(Args &&...args) { - auto size = this->size(); - this->reserve_total(size + 1); - ::new (reinterpret_cast(reinterpret_cast(this->data()) + - size * size_of())) - T(std::forward(args)...); - this->set_len(size + 1); -} - -template -void Vec::clear() { - this->truncate(0); -} - -template -typename Vec::iterator Vec::begin() noexcept { - return Slice(this->data(), this->size()).begin(); -} - -template -typename Vec::iterator Vec::end() noexcept { - return Slice(this->data(), this->size()).end(); -} - -template -typename Vec::const_iterator Vec::begin() const noexcept { - return this->cbegin(); +const Vec& Option>::operator*() const noexcept { + return value(); } template -typename Vec::const_iterator Vec::end() const noexcept { - return this->cend(); +const Vec* Option>::operator->() const noexcept { + return &value(); } template -typename Vec::const_iterator Vec::cbegin() const noexcept { - return Slice(this->data(), this->size()).begin(); +Vec& Option>::operator*() noexcept { + return value(); } template -typename Vec::const_iterator Vec::cend() const noexcept { - return Slice(this->data(), this->size()).end(); +Vec* Option>::operator->() noexcept { + return &value(); } template -void Vec::swap(Vec &rhs) noexcept { - using std::swap; - swap(this->repr, rhs.repr); +void Option>::reset() { + this->drop(); + new (this) Option(); } -// Internal API only intended for the cxxbridge code generator. template -Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} -#endif // CXXBRIDGE1_RUST_VEC +Option>::Option(unsafe_bitcopy_t, const Option &bits) noexcept : repr(bits.repr) {} +#endif // CXXBRIDGE1_RUST_OPTION #ifndef CXXBRIDGE1_IS_COMPLETE #define CXXBRIDGE1_IS_COMPLETE diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 79514500f..56f910a24 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -552,7 +552,8 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { } Type::RustVec(_) => quote_spanned!(span=> #var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), Type::RustOption(ty) => { - let improper; + let mut improper = false; + let mut direct = false; let convert = quote!(<#ty as ::cxx::private::OptionFfi>::into_ffi(#var)); let call = match &ty.inner { Type::RustBox(ty) => { @@ -586,12 +587,18 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { quote!(#convert) } }, + Type::RustVec(_) | Type::Ident(_) => { + direct = true; + quote_spanned!(span=> #var.as_mut_ptr() as _) + }, _ => unreachable!(), }; - if improper { + if improper && !direct { quote_spanned!(span=> #call.into_raw_improper()) - } else { + } else if !direct { quote_spanned!(span=> #call.into_raw()) + } else { + quote_spanned!(span=> #call) } } Type::Ref(ty) => match &ty.inner { @@ -724,7 +731,12 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { Type::RustOption(ty) => { let abs_ty = quote!(<#ty as ::cxx::private::OptionFfi>); let abs_ty_target = quote!(#abs_ty::Target); - quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#call as _))) + match &ty.inner { + Type::RustVec(_) | Type::Ident(_) => { + quote_spanned!(span=> <#abs_ty_target as ::cxx::private::OptionFfiInverse>::into_option(#call)) + }, + _ => quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#call as _))), + } } Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { @@ -1063,7 +1075,10 @@ fn expand_rust_function_shim_impl( requires_unsafe = true; let abs_ty = quote!(<#ty as ::cxx::private::OptionFfi>); let abs_ty_target = quote!(#abs_ty::Target); - quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#var as _))) + match &ty.inner { + Type::RustVec(_) | Type::Ident(_) => quote_spanned!(span=> ::cxx::core::mem::take(<#abs_ty_target as ::cxx::private::OptionFfiInverse>::as_mut_option(&mut *#var))), + _ => quote_spanned!(span=> #abs_ty::from_ffi(#abs_ty_target::from_raw(#var as _))), + } } Type::UniquePtr(_) => { requires_unsafe = true; @@ -1162,6 +1177,10 @@ fn expand_rust_function_shim_impl( } Some(quote_spanned!(span=> #abs_ty::into_ffi)) } + Type::RustVec(_) | Type::Ident(_) => { + process_converted = None; + Some(quote_spanned!(span=> <#abs_ty_target as ::cxx::private::OptionFfiInverse>::from_option)) + } _ => unreachable!(), } }, @@ -1525,7 +1544,10 @@ fn expand_rust_option( OptionInner::RustBox(key) => { let elem = key.rust; let resolve = types.resolve(elem); - let link_prefix = format!("cxxbridge1$rust_option$Box${}$", resolve.name.to_symbol()); + let link_prefix = format!( + "cxxbridge1$rust_option$rust_box${}$", + resolve.name.to_symbol() + ); let local_prefix = format_ident!("{}__box__option_", elem); (elem, key, resolve, link_prefix, local_prefix) } @@ -1535,28 +1557,31 @@ fn expand_rust_option( return TokenStream::new(); } let resolve = types.resolve(elem); - let link_prefix = format!("cxxbridge1$rust_option$const${}$", resolve.name.to_symbol()); + let link_prefix = format!( + "cxxbridge1$rust_option$const$ref${}$", + resolve.name.to_symbol() + ); let local_prefix = format_ident!("{}__const__ref__option_", elem); (elem, key, resolve, link_prefix, local_prefix) } OptionInner::MutRef(key) => { let elem = key.rust; - if types.try_resolve(key.rust).is_none() { + if types.try_resolve(key).is_none() { return TokenStream::new(); } let resolve = types.resolve(elem); - let link_prefix = format!("cxxbridge1$rust_option${}$", resolve.name.to_symbol()); + let link_prefix = format!("cxxbridge1$rust_option$ref${}$", resolve.name.to_symbol()); let local_prefix = format_ident!("{}__ref__option_", elem); (elem, key, resolve, link_prefix, local_prefix) } OptionInner::RefVec(key) => { let elem = key.rust; - if types.try_resolve(key.rust).is_none() { + if types.try_resolve(key).is_none() { return TokenStream::new(); } let resolve = types.resolve(elem); let link_prefix = format!( - "cxxbridge1$rust_option$const$Vec${}$", + "cxxbridge1$rust_option$const$ref$rust_vec${}$", resolve.name.to_symbol() ); let local_prefix = format_ident!("{}__vec__const__ref__option_", elem); @@ -1564,14 +1589,40 @@ fn expand_rust_option( } OptionInner::MutRefVec(key) => { let elem = key.rust; - if types.try_resolve(key.rust).is_none() { + if types.try_resolve(key).is_none() { return TokenStream::new(); } let resolve = types.resolve(elem); - let link_prefix = format!("cxxbridge1$rust_option$Vec${}$", resolve.name.to_symbol()); + let link_prefix = format!( + "cxxbridge1$rust_option$ref$rust_vec${}$", + resolve.name.to_symbol() + ); let local_prefix = format_ident!("{}__vec__ref__option_", elem); (elem, key, resolve, link_prefix, local_prefix) } + OptionInner::Vec(key) => { + let elem = key.rust; + if types.try_resolve(key).is_none() { + return TokenStream::new(); + } + let resolve = types.resolve(elem); + let link_prefix = format!( + "cxxbridge1$rust_option$rust_vec${}$", + resolve.name.to_symbol() + ); + let local_prefix = format_ident!("{}__vec__option_", elem); + (elem, key, resolve, link_prefix, local_prefix) + } + OptionInner::Ident(key) => { + let elem = key.rust; + if types.try_resolve(key).is_none() { + return TokenStream::new(); + } + let resolve = types.resolve(elem); + let link_prefix = format!("cxxbridge1$rust_option${}$", resolve.name.to_symbol()); + let local_prefix = format_ident!("{}__option_", elem); + (elem, key, resolve, link_prefix, local_prefix) + } }; let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -1593,8 +1644,9 @@ fn expand_rust_option( let ident = key.rust; let (impl_generics, ty_generics) = generics::split_for_impl(*key, explicit_impl, resolve); - let (ty, trait_impl_ty, ty_ptr, const_ty_ptr, prevent_unwind_drop_label) = match inner { + let (ffi_ty, ty, trait_impl_ty, ty_ptr, const_ty_ptr, prevent_unwind_drop_label) = match inner { OptionInner::RustBox(_) => ( + quote! { ::cxx::alloc::boxed::Box<#elem #ty_generics> }, quote! { ::cxx::alloc::boxed::Box<#elem #ty_generics> }, quote! { ::cxx::alloc::boxed::Box<#elem #ty_generics> }, quote! { *mut #elem #ty_generics }, @@ -1602,6 +1654,7 @@ fn expand_rust_option( format!("::alloc::boxed::Box<::{}> as Drop>::drop", ident), ), OptionInner::Ref(_) => ( + quote! { &#elem #ty_generics }, quote! { &#elem #ty_generics }, quote! { &#elem #ty_generics }, quote! { *const #elem #ty_generics }, @@ -1609,6 +1662,7 @@ fn expand_rust_option( format!("&::{}> as Drop>::drop", ident), ), OptionInner::MutRef(_) => ( + quote! { &mut #elem #ty_generics }, quote! { &mut #elem #ty_generics }, quote! { &mut #elem #ty_generics }, quote! { *mut #elem #ty_generics }, @@ -1624,18 +1678,30 @@ fn expand_rust_option( // but using the actual `Vec` here at least gives some idea of what's going on // if this shows up in an error message. quote! { &::cxx::private::RustVec<#elem #ty_generics> }, + quote! { &::cxx::private::RustVec<#elem #ty_generics> }, quote! { &#elem #ty_generics }, quote! { *const ::cxx::private::RustVec<#elem #ty_generics> }, quote! { *const ::cxx::private::RustVec<#elem #ty_generics> }, format!("&::alloc::vec::Vec<::{}> as Drop>::drop", ident), ), OptionInner::MutRefVec(_) => ( + quote! { &mut ::cxx::private::RustVec<#elem #ty_generics> }, quote! { &mut ::cxx::private::RustVec<#elem #ty_generics> }, quote! { &mut #elem #ty_generics }, quote! { *mut ::cxx::private::RustVec<#elem #ty_generics> }, quote! { *const ::cxx::private::RustVec<#elem #ty_generics> }, format!("&mut ::alloc::vec::Vec<::{}> as Drop>::drop", ident), ), + OptionInner::Vec(_) => ( + quote! { ::cxx::alloc::vec::Vec<#elem #ty_generics> }, + quote! { ::cxx::private::RustVec<#elem #ty_generics> }, + quote! { #elem #ty_generics }, + quote! { *mut ::cxx::private::RustVec<#elem #ty_generics> }, + quote! { *const ::cxx::private::RustVec<#elem #ty_generics> }, + format!("::alloc::vec::Vec<::{}> as Drop>::drop", ident), + ), + // These are all handled in cxx.rs because we do not allow user defined types here + OptionInner::Ident(_) => unreachable!(), }; let set_value_impl = match inner { OptionInner::RustBox(_) => quote! { @@ -1710,25 +1776,53 @@ fn expand_rust_option( *option.unwrap() as _ } }, + OptionInner::Vec(_) => quote! { + #[doc(hidden)] + #[export_name = #link_set] + unsafe extern "C" fn #local_set #impl_generics(this: *mut <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target, value: #ty_ptr) { + use ::cxx::private::OptionFfiInverse; + unsafe { this.as_mut().unwrap().set(::cxx::core::mem::take((*value).as_mut_vec())) }; + } + #[doc(hidden)] + #[export_name = #link_value_const] + unsafe extern "C" fn #local_value_const #impl_generics(this: *const <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target) -> #const_ty_ptr { + use ::cxx::private::OptionFfiInverse; + let this = unsafe { this.as_ref().unwrap() }; + ::cxx::core::debug_assert!(this.has_value()); + let v: &#ffi_ty = unsafe { this.as_option().as_ref().unwrap() }; + v as *const #ffi_ty as #const_ty_ptr + } + #[doc(hidden)] + #[export_name = #link_value] + unsafe extern "C" fn #local_value #impl_generics(this: *mut <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target) -> #ty_ptr { + use ::cxx::private::OptionFfiInverse; + let this: &mut <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target = unsafe { this.as_mut().unwrap() }; + ::cxx::core::debug_assert!(this.has_value()); + let option: &mut ::core::option::Option<#ffi_ty> = this.as_mut_option(); + let option: ::core::option::Option<&mut #ffi_ty> = option.as_mut(); + option.unwrap() as *mut #ffi_ty as #ty_ptr + } + }, + OptionInner::Ident(_) => unreachable!(), }; quote_spanned! {end_span=> // #[doc(hidden)] #unsafe_token impl #impl_generics ::cxx::private::ImplOption<#ty> for #trait_impl_ty {} #[doc(hidden)] #[export_name = #link_new] - unsafe extern "C" fn #local_new #impl_generics(this: *mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) { + unsafe extern "C" fn #local_new #impl_generics(this: *mut <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target) { use ::cxx::private::OptionFfiInverse; - unsafe { ::cxx::core::ptr::write(this, <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target::new()) }; + unsafe { ::cxx::core::ptr::write(this, <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target::new()) }; } #[doc(hidden)] #[export_name = #link_drop] - unsafe extern "C" fn #local_drop #impl_generics(this: *mut <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) { + unsafe extern "C" fn #local_drop #impl_generics(this: *mut <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target) { let __fn = concat!("<", module_path!(), #prevent_unwind_drop_label); ::cxx::private::prevent_unwind(__fn, || unsafe { ::cxx::core::ptr::drop_in_place(this) }); } #[doc(hidden)] #[export_name = #link_has_value] - unsafe extern "C" fn #local_has_value #impl_generics(this: *const <::core::option::Option<#ty> as ::cxx::private::OptionFfi>::Target) -> bool { + unsafe extern "C" fn #local_has_value #impl_generics(this: *const <::core::option::Option<#ffi_ty> as ::cxx::private::OptionFfi>::Target) -> bool { unsafe { this.as_ref().unwrap().has_value() } } #set_value_impl diff --git a/src/cxx.cc b/src/cxx.cc index d2246fce6..cc69faf37 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -789,164 +789,296 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), MACRO(isize, rust::isize) \ MACRO(string, std::string) -#define RUST_OPTION_EXTERNS(RUST_TYPE, CXX_TYPE) \ - void cxxbridge1$rust_option$const$##RUST_TYPE##$new( \ +#define RUST_OPTION_EXTERNS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge1$rust_option$const$ref$##RUST_TYPE##$new( \ rust::Option *ptr) noexcept; \ - void cxxbridge1$rust_option$const$##RUST_TYPE##$drop( \ + void cxxbridge1$rust_option$const$ref$##RUST_TYPE##$drop( \ rust::Option *ptr) noexcept; \ - bool cxxbridge1$rust_option$const$##RUST_TYPE##$has_value( \ + bool cxxbridge1$rust_option$const$ref$##RUST_TYPE##$has_value( \ const rust::Option *ptr) noexcept; \ - CXX_TYPE const * cxxbridge1$rust_option$const$##RUST_TYPE##$value( \ + CXX_TYPE const * cxxbridge1$rust_option$const$ref$##RUST_TYPE##$value( \ rust::Option const *ptr) noexcept; \ - void cxxbridge1$rust_option$const$##RUST_TYPE##$set( \ + void cxxbridge1$rust_option$const$ref$##RUST_TYPE##$set( \ rust::Option *ptr, CXX_TYPE const * value) noexcept; \ - void cxxbridge1$rust_option$##RUST_TYPE##$new( \ + void cxxbridge1$rust_option$ref$##RUST_TYPE##$new( \ rust::Option *ptr) noexcept; \ - void cxxbridge1$rust_option$##RUST_TYPE##$drop( \ + void cxxbridge1$rust_option$ref$##RUST_TYPE##$drop( \ rust::Option *ptr) noexcept; \ - bool cxxbridge1$rust_option$##RUST_TYPE##$has_value( \ + bool cxxbridge1$rust_option$ref$##RUST_TYPE##$has_value( \ const rust::Option *ptr) noexcept; \ - CXX_TYPE* cxxbridge1$rust_option$##RUST_TYPE##$value_const( \ + CXX_TYPE const * cxxbridge1$rust_option$ref$##RUST_TYPE##$value_const( \ rust::Option const *ptr) noexcept; \ - CXX_TYPE* cxxbridge1$rust_option$##RUST_TYPE##$value( \ + CXX_TYPE* cxxbridge1$rust_option$ref$##RUST_TYPE##$value( \ rust::Option *ptr) noexcept; \ - void cxxbridge1$rust_option$##RUST_TYPE##$set( \ + void cxxbridge1$rust_option$ref$##RUST_TYPE##$set( \ rust::Option *ptr, CXX_TYPE * value) noexcept; \ /* Vec implementation */ \ - void cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$new( \ + void cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$new( \ rust::Option const &> *ptr) noexcept; \ - void cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$drop( \ + void cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$drop( \ rust::Option const &> *ptr) noexcept; \ - bool cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$has_value( \ + bool cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$has_value( \ const rust::Option const &> *ptr) noexcept; \ - rust::Vec const * cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$value( \ + rust::Vec const * cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$value( \ rust::Option const &> const *ptr) noexcept; \ - void cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$set( \ + void cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$set( \ rust::Option const &> *ptr, rust::Vec const * value) noexcept; \ - void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$new( \ + void cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$new( \ rust::Option &> *ptr) noexcept; \ - void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$drop( \ + void cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$drop( \ rust::Option &> *ptr) noexcept; \ - bool cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$has_value( \ + bool cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$has_value( \ const rust::Option &> *ptr) noexcept; \ - rust::Vec* cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value_const( \ + rust::Vec const * cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$value_const( \ rust::Option &> const *ptr) noexcept; \ - rust::Vec* cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value( \ + rust::Vec* cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$value( \ rust::Option &> *ptr) noexcept; \ + void cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$set( \ + rust::Option &> *ptr, rust::Vec * value) noexcept; \ + void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$new( \ + rust::Option> *ptr) noexcept; \ + void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$drop( \ + rust::Option> *ptr) noexcept; \ + bool cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$has_value( \ + const rust::Option> *ptr) noexcept; \ + rust::Vec const * cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value_const( \ + rust::Option> const *ptr) noexcept; \ + rust::Vec * cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value( \ + rust::Option> *ptr) noexcept; \ void cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$set( \ - rust::Option &> *ptr, rust::Vec * value) noexcept; + rust::Option> *ptr, rust::Vec *value) noexcept; + +#define RUST_OPTION_DIRECT_EXTERNS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge1$rust_option$##RUST_TYPE##$new( \ + rust::Option *ptr) noexcept; \ + void cxxbridge1$rust_option$##RUST_TYPE##$drop( \ + rust::Option *ptr) noexcept; \ + bool cxxbridge1$rust_option$##RUST_TYPE##$has_value( \ + const rust::Option *ptr) noexcept; \ + CXX_TYPE const * cxxbridge1$rust_option$##RUST_TYPE##$value_const( \ + rust::Option const *ptr) noexcept; \ + CXX_TYPE* cxxbridge1$rust_option$##RUST_TYPE##$value( \ + rust::Option *ptr) noexcept; \ + void cxxbridge1$rust_option$##RUST_TYPE##$set( \ + rust::Option *ptr, CXX_TYPE *value) noexcept; #define RUST_OPTION_OPS(RUST_TYPE, CXX_TYPE) \ template <> \ Option::Option() noexcept { \ - cxxbridge1$rust_option$const$##RUST_TYPE##$new(this); \ + cxxbridge1$rust_option$const$ref$##RUST_TYPE##$new(this); \ } \ template <> \ void Option::drop() noexcept { \ - cxxbridge1$rust_option$const$##RUST_TYPE##$drop(this); \ + cxxbridge1$rust_option$const$ref$##RUST_TYPE##$drop(this); \ } \ template <> \ bool Option::has_value() const noexcept { \ - return cxxbridge1$rust_option$const$##RUST_TYPE##$has_value(this); \ + return cxxbridge1$rust_option$const$ref$##RUST_TYPE##$has_value(this); \ } \ template <> \ CXX_TYPE const & Option::value() const noexcept { \ - return *cxxbridge1$rust_option$const$##RUST_TYPE##$value(this); \ + return *cxxbridge1$rust_option$const$ref$##RUST_TYPE##$value(this); \ } \ template <> \ void Option::set(CXX_TYPE const & value) noexcept { \ - return cxxbridge1$rust_option$const$##RUST_TYPE##$set( \ + return cxxbridge1$rust_option$const$ref$##RUST_TYPE##$set( \ this, &value); \ } \ template <> \ Option::Option() noexcept { \ - cxxbridge1$rust_option$##RUST_TYPE##$new(this); \ + cxxbridge1$rust_option$ref$##RUST_TYPE##$new(this); \ } \ template <> \ void Option::drop() noexcept { \ - cxxbridge1$rust_option$##RUST_TYPE##$drop(this); \ + cxxbridge1$rust_option$ref$##RUST_TYPE##$drop(this); \ } \ template <> \ bool Option::has_value() const noexcept { \ - return cxxbridge1$rust_option$##RUST_TYPE##$has_value(this); \ + return cxxbridge1$rust_option$ref$##RUST_TYPE##$has_value(this); \ } \ template <> \ - const CXX_TYPE& Option::value() const noexcept { \ - return *cxxbridge1$rust_option$##RUST_TYPE##$value_const(this); \ + const CXX_TYPE& Option::value() const noexcept { \ + return *cxxbridge1$rust_option$ref$##RUST_TYPE##$value_const(this); \ } \ template <> \ - CXX_TYPE& Option::value() noexcept { \ - return *cxxbridge1$rust_option$##RUST_TYPE##$value(this); \ + CXX_TYPE& Option::value() noexcept { \ + return *cxxbridge1$rust_option$ref$##RUST_TYPE##$value(this); \ } \ template <> \ void Option::set(CXX_TYPE & value) noexcept { \ - return cxxbridge1$rust_option$##RUST_TYPE##$set(this, &value); \ + return cxxbridge1$rust_option$ref$##RUST_TYPE##$set(this, &value); \ } \ /* Vec impl */ \ template <> \ Option const &>::Option() noexcept { \ - cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$new(this); \ + cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$new(this); \ } \ template <> \ void Option const &>::drop() noexcept { \ - cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$drop(this); \ + cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$drop(this); \ } \ template <> \ bool Option const &>::has_value() const noexcept { \ - return cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$has_value(this); \ + return cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$has_value(this); \ } \ template <> \ rust::Vec const & Option const &>::value() const noexcept { \ - return *cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$value(this); \ + return *cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$value(this); \ } \ template <> \ void Option const &>::set(rust::Vec const & value) noexcept { \ - return cxxbridge1$rust_option$const$rust_vec$##RUST_TYPE##$set( \ + return cxxbridge1$rust_option$const$ref$rust_vec$##RUST_TYPE##$set( \ this, &value); \ } \ template <> \ Option &>::Option() noexcept { \ - cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$new(this); \ + cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$new(this); \ } \ template <> \ void Option &>::drop() noexcept { \ - cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$drop(this); \ + cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$drop(this); \ } \ template <> \ bool Option &>::has_value() const noexcept { \ + return cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$has_value(this); \ + } \ + template <> \ + const rust::Vec& Option &>::value() const noexcept { \ + return *cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$value_const(this); \ + } \ + template <> \ + rust::Vec& Option &>::value() noexcept { \ + return *cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$value(this); \ + } \ + template <> \ + void Option &>::set(rust::Vec & value) noexcept { \ + return cxxbridge1$rust_option$ref$rust_vec$##RUST_TYPE##$set(this, &value); \ + } \ + template <> \ + Option>::Option() noexcept { \ + cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$new(this); \ + } \ + template <> \ + void Option>::drop() noexcept { \ + cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$drop(this); \ + } \ + template <> \ + bool Option>::has_value() const noexcept { \ return cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$has_value(this); \ } \ template <> \ - const rust::Vec& Option &>::value() const noexcept { \ + const rust::Vec& Option>::value() const noexcept { \ return *cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value_const(this); \ } \ template <> \ - rust::Vec& Option &>::value() noexcept { \ + rust::Vec& Option>::value() noexcept { \ return *cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$value(this); \ } \ template <> \ - void Option &>::set(rust::Vec & value) noexcept { \ + void Option>::set(rust::Vec value) noexcept { \ return cxxbridge1$rust_option$rust_vec$##RUST_TYPE##$set(this, &value); \ } +#define RUST_OPTION_DIRECT_OPS(RUST_TYPE, CXX_TYPE) \ + Option::Option() noexcept { \ + cxxbridge1$rust_option$##RUST_TYPE##$new(this); \ + } \ + void Option::drop() noexcept { \ + cxxbridge1$rust_option$##RUST_TYPE##$drop(this); \ + } \ + bool Option::has_value() const noexcept { \ + return cxxbridge1$rust_option$##RUST_TYPE##$has_value(this); \ + } \ + const CXX_TYPE& Option::value() const noexcept { \ + return *cxxbridge1$rust_option$##RUST_TYPE##$value_const(this); \ + } \ + CXX_TYPE& Option::value() noexcept { \ + return *cxxbridge1$rust_option$##RUST_TYPE##$value(this); \ + } \ + void Option::set(CXX_TYPE value) noexcept { \ + return cxxbridge1$rust_option$##RUST_TYPE##$set(this, &value); \ + } + +#define RUST_OPTION_DIRECT_IMPL(CXX_TYPE) \ +Option::Option(Option&& other) noexcept { \ + new (this) Option(); \ + if (other.has_value()) { \ + set(std::move(other.value())); \ + } \ + new (&other) Option(); \ +} \ +Option::Option(String&& value) noexcept { \ + new (this) Option(); \ + set(std::move(value)); \ +} \ +Option::~Option() noexcept { \ + this->drop(); \ +} \ +Option& Option::operator=(Option&& other) noexcept { \ + this->reset(); \ + if (other.has_value()) { \ + set(std::move(other.value())); \ + } \ + new (&other) Option(); \ + return *this; \ +} \ +const CXX_TYPE& Option::operator*() const noexcept { \ + return value(); \ +} \ +const CXX_TYPE* Option::operator->() const noexcept { \ + return &value(); \ +} \ +CXX_TYPE& Option::operator*() noexcept { \ + return value(); \ +} \ +CXX_TYPE* Option::operator->() noexcept { \ + return &value(); \ +} \ +bool Option::operator==(const Option& other) const noexcept { \ + if (this->has_value() && other.has_value()) { \ + return this->value() == other.value(); \ + } \ + return this->has_value() == other.has_value(); \ +} \ +bool Option::operator!=(const Option& other) const noexcept { \ + if (this->has_value() && other.has_value()) { \ + return this->value() != other.value(); \ + } \ + return this->has_value() != other.has_value(); \ +} \ +void Option::reset() { \ + this->drop(); \ + new (this) Option(); \ +} \ +Option::Option(unsafe_bitcopy_t, const Option &bits) noexcept : repr(bits.repr) {} + #define FOR_EACH_RUST_OPTION(MACRO) \ FOR_EACH_NUMERIC(MACRO) \ MACRO(bool, bool) \ MACRO(char, char) \ MACRO(string, rust::String) +#define FOR_EACH_RUST_OPTION_DIRECT(MACRO) \ + MACRO(string, rust::String) + +#define FOR_EACH_RUST_OPTION_DIRECT_IMPL(MACRO) \ + MACRO(rust::String) + extern "C" { FOR_EACH_STD_VECTOR(STD_VECTOR_OPS) FOR_EACH_TRIVIAL_STD_VECTOR(STD_VECTOR_TRIVIAL_OPS) FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS) FOR_EACH_SHARED_PTR(SHARED_PTR_OPS) FOR_EACH_RUST_OPTION(RUST_OPTION_EXTERNS) +FOR_EACH_RUST_OPTION_DIRECT(RUST_OPTION_DIRECT_EXTERNS) } // extern "C" namespace rust { inline namespace cxxbridge1 { FOR_EACH_RUST_VEC(RUST_VEC_OPS) FOR_EACH_RUST_OPTION(RUST_OPTION_OPS) +FOR_EACH_RUST_OPTION_DIRECT(RUST_OPTION_DIRECT_OPS) +FOR_EACH_RUST_OPTION_DIRECT_IMPL(RUST_OPTION_DIRECT_IMPL) } // namespace cxxbridge1 } // namespace rust diff --git a/src/rust_option.rs b/src/rust_option.rs index 76cbead01..0041ca27d 100644 --- a/src/rust_option.rs +++ b/src/rust_option.rs @@ -20,6 +20,9 @@ mod private { pub trait Sealed {} } pub trait OptionPtrTarget: private::Sealed {} +pub trait OptionRepr: private::Sealed { + type Repr; +} impl private::Sealed for &T {} impl OptionPtrTarget for &T {} @@ -35,6 +38,19 @@ impl private::Sealed for Box {} #[cfg(feature = "alloc")] impl OptionPtrTarget for Box {} +pub const fn assert_option_safe() { + struct __SizeCheck(core::marker::PhantomData<(U, V, W)>); + impl __SizeCheck { + const _IS_OPTION_SIZE: () = assert!(mem::size_of::() == mem::size_of::()); + const _IS_REPR_ALIGN: () = assert!(mem::align_of::() == mem::align_of::()); + const _IS_OPTION_ALIGN: () = assert!(mem::align_of::() == mem::align_of::()); + } + // Force the constants to resolve (at compile time) + let _: () = __SizeCheck::::_IS_OPTION_SIZE; + let _: () = __SizeCheck::::_IS_REPR_ALIGN; + let _: () = __SizeCheck::::_IS_OPTION_ALIGN; +} + pub trait OptionFfi: private::Sealed { // The FFI type that this Rust type is mapped to type Target; @@ -89,93 +105,90 @@ pub trait OptionFfiInverse: private::Sealed + Sized { /// Defined a struct named RustOption and implements OptionFfi for Option with it as target macro_rules! impl_option_ffi { // Like `impl RustOption` where you need some bound on T - (<$generic:ident: $bound:path>, $repr:ty, $sizing:ty) => { - impl_option_ffi!(_private: generics=<>, bounded_generics=<$generic: $bound>, option_ty=$generic, repr=$repr, sizing=$sizing) + ($name:ident<$generic:ident: $bound:path>, $repr:ty, $sizing:ty) => { + impl_option_ffi!(_private: name=$name, generics=<>, bounded_generics=<$generic: $bound>, option_ty=$generic, repr=$repr, sizing=$sizing) }; // Like `impl RustOption>` for some concrete S and generic T - (<$generic:ident>, $t:ty, $repr:ty, $sizing:ty) => { - impl_option_ffi!(_private: generics=<$generic>, bounded_generics=<>, option_ty=$t, repr=$repr, sizing=$sizing) + ($name:ident<$generic:ident>, $t:ty, $repr:ty, $sizing:ty) => { + impl_option_ffi!(_private: name=$name, generics=<$generic>, bounded_generics=<>, option_ty=$t, repr=$repr, sizing=$sizing) }; // Like `impl RustOption` for some non-generic T - (<$t:ident>, $repr:ty, $sizing:ty) => { - impl_option_ffi!(_private: generics=<>, bounded_generics=<>, option_ty=$t, repr=$repr, sizing=$sizing) + ($name:ident<$t:ident>, $repr:ty, $sizing:ty) => { + impl_option_ffi!(_private: name=$name, generics=<>, bounded_generics=<>, option_ty=$t, repr=$repr, sizing=$sizing) }; // Private case. Does the actual implementation - (_private: generics=<$($generic1:ident),*>, bounded_generics=<$($generic2:ident: $bound:path),*>, option_ty=$option_ty:ty, repr=$repr:ty, sizing=$sizing:ty) => { - type Repr = [mem::MaybeUninit<$repr>; mem::size_of::>() / mem::size_of::<$repr>()]; - + (_private: name=$name:ident, generics=<$($generic1:ident),*>, bounded_generics=<$($generic2:ident: $bound:path),*>, option_ty=$option_ty:ty, repr=$repr:ty, sizing=$sizing:ty) => { // ABI compatible with C++ rust::Option for (not necessarily core::option::Option). - pub struct RustOption<$($generic1),* $($generic2: $bound),*> { + pub struct $name<$($generic1),* $($generic2: $bound),*> { #[allow(dead_code)] - repr: Repr, + repr: [mem::MaybeUninit<$repr>; mem::size_of::>() / mem::size_of::<$repr>()], phantom: core::marker::PhantomData>, } - pub const fn assert_option_safe() { - struct __SizeCheck(core::marker::PhantomData); - impl __SizeCheck { - const _IS_OPTION_SIZE: () = - assert!(mem::size_of::>() == mem::size_of::()); - const _IS_REPR_ALIGN: () = - assert!(mem::align_of::() == mem::align_of::<$repr>()); - const _IS_OPTION_ALIGN: () = - assert!(mem::align_of::>() == mem::align_of::()); + impl<$($generic1),* $($generic2: $bound),*> private::Sealed for $name<$($generic1),* $($generic2),*> {} + + impl<$($generic1),* $($generic2: $bound),*> OptionRepr for $name<$($generic1),* $($generic2),*> { + type Repr = [mem::MaybeUninit<$repr>; mem::size_of::>() / mem::size_of::<$repr>()]; + } + + impl<$($generic1),* $($generic2: $bound),*> $name<$($generic1),* $($generic2),*> { + pub fn has_value(&self) -> bool { + self.as_option().is_some() + } + + pub fn set(&mut self, value: $option_ty) { + self.as_mut_option().replace(value); } - // Force the constants to resolve (at compile time) - let _: () = __SizeCheck::::_IS_OPTION_SIZE; - let _: () = __SizeCheck::::_IS_REPR_ALIGN; - let _: () = __SizeCheck::::_IS_OPTION_ALIGN; } + impl<$($generic1),* $($generic2: $bound),*> private::Sealed for Option<$option_ty> {} impl<$($generic1),* $($generic2: $bound),*> OptionFfi for Option<$option_ty> { - type Target = RustOption<$($generic1),* $($generic2),*>; + type Target = $name<$($generic1),* $($generic2),*>; fn new_ffi() -> Self::Target { Self::None.into_ffi() } fn into_ffi(self) -> Self::Target { - let _: () = assert_option_safe::<$option_ty>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); let v = unsafe { core::mem::transmute_copy(&self) }; core::mem::forget(self); v } fn as_ffi(&self) -> &Self::Target { - let _: () = assert_option_safe::<$option_ty>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); unsafe { &*(self as *const Self as *const Self::Target) } } fn as_mut_ffi(&mut self) -> &mut Self::Target { - let _: () = assert_option_safe::<$option_ty>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); unsafe { &mut *(self as *mut Self as *mut Self::Target) } } fn from_ffi(mut other: Self::Target) -> Self { - let _: () = assert_option_safe::<$option_ty>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); Self::from_ffi_mut(&mut other).take() } fn from_ffi_ref(other: &Self::Target) -> &Self { - let _: () = assert_option_safe::<$option_ty>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); unsafe { &*(other as *const Self::Target as *const Self) } } fn from_ffi_mut(other: &mut Self::Target) -> &mut Self { - let _: () = assert_option_safe::<$option_ty>(); + let _: () = assert_option_safe::<$option_ty, ::Repr, Option<$sizing>>(); unsafe { &mut *(other as *mut Self::Target as *mut Self) } } } - impl<$($generic1),* $($generic2: $bound),*> private::Sealed for RustOption<$($generic1),* $($generic2),*> {} - - impl<$($generic1),* $($generic2: $bound),*> OptionFfiInverse for RustOption<$($generic1),* $($generic2),*> { + impl<$($generic1),* $($generic2: $bound),*> OptionFfiInverse for $name<$($generic1),* $($generic2),*> { type Target = Option<$option_ty>; } - impl<$($generic1),* $($generic2: $bound),*> Drop for RustOption<$($generic1),* $($generic2),*> { + impl<$($generic1),* $($generic2: $bound),*> Drop for $name<$($generic1),* $($generic2),*> { fn drop(&mut self) { self.as_mut_option().take(); } @@ -185,21 +198,9 @@ macro_rules! impl_option_ffi { // Pointer-sized pointer types with niche optimization const _: () = { - impl_option_ffi! { , usize, &'static () } + impl_option_ffi! { RustOption, usize, &'static () } impl RustOption { - pub fn value(&self) -> Option<&T> { - self.as_option().as_ref() - } - - pub fn has_value(&self) -> bool { - self.as_option().is_some() - } - - pub fn set(&mut self, value: T) { - self.as_mut_option().replace(value); - } - pub unsafe fn as_ref_mut_inner_unchecked(&mut self) -> &mut T { unsafe { self.as_mut_option().as_mut().unwrap_unchecked() } } @@ -568,3 +569,14 @@ const _: () = { } } }; + +// Growable containers (Vec, String), 3 pointer size with niche optimization +#[cfg(feature = "alloc")] +const _: () = { + impl_option_ffi! { RustOptionVec, Vec, usize, Vec<()> } +}; + +#[cfg(feature = "alloc")] +const _: () = { + impl_option_ffi! { RustOptionString, usize, String } +}; diff --git a/src/symbols/rust_option.rs b/src/symbols/rust_option.rs index 839280632..34e06d127 100644 --- a/src/symbols/rust_option.rs +++ b/src/symbols/rust_option.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case)] + use crate::rust_option::OptionFfi; use crate::rust_option::OptionFfiInverse; #[cfg(feature = "alloc")] @@ -17,55 +19,55 @@ macro_rules! rust_option_shims { const_assert_eq!(mem::size_of::>(), mem::size_of::()); const _: () = { - #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$new")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$", $segment, "$new")] unsafe extern "C" fn __const_new(this: *mut as OptionFfi>::Target) { unsafe { ptr::write(this, Option::<&$ty>::new_ffi()) }; } - #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$drop")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$", $segment, "$drop")] unsafe extern "C" fn __const_drop(this: *mut as OptionFfi>::Target) { unsafe { ptr::drop_in_place(this) } } - #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$has_value")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$", $segment, "$has_value")] unsafe extern "C" fn __const_has_value(this: *const as OptionFfi>::Target) -> bool { let o: & as OptionFfi>::Target = unsafe { this.as_ref().unwrap() }; o.as_option().is_some() } - #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$value")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$", $segment, "$value")] unsafe extern "C" fn __const_value(this: *const as OptionFfi>::Target) -> *const $ty { unsafe { this.as_ref().unwrap().as_option().as_ref().copied().unwrap() as *const $ty } } - #[export_name = concat!("cxxbridge1$rust_option$const$", $segment, "$set")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$", $segment, "$set")] unsafe extern "C" fn __const_set( this: *mut as OptionFfi>::Target, value: *mut $ty, ) { unsafe { this.as_mut().unwrap().set(&*value) } } - #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$new")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$new")] unsafe extern "C" fn __new(this: *mut as OptionFfi>::Target) { unsafe { ptr::write(this, Option::<&mut $ty>::new_ffi()) } } - #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$drop")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$drop")] unsafe extern "C" fn __drop(this: *mut as OptionFfi>::Target) { unsafe { ptr::drop_in_place(this) } } - #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$has_value")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$has_value")] unsafe extern "C" fn __has_value(this: *const as OptionFfi>::Target) -> bool { let o: & as OptionFfi>::Target = unsafe { this.as_ref().unwrap() }; o.as_option().is_some() } - #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$value_const")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$value_const")] unsafe extern "C" fn __value_const(this: *const as OptionFfi>::Target) -> *const $ty { let v: &$ty = unsafe { this.as_ref().unwrap().as_option().as_ref().unwrap() }; v as *const $ty } - #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$value")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$value")] unsafe extern "C" fn __value(this: *mut as OptionFfi>::Target) -> *mut $ty { let this = unsafe { this.as_mut().unwrap() }; let ptr = this.as_mut_option().as_mut().unwrap(); *ptr as _ } - #[export_name = concat!("cxxbridge1$rust_option$", $segment, "$set")] + #[export_name = concat!("cxxbridge1$rust_option$ref$", $segment, "$set")] unsafe extern "C" fn __set( this: *mut as OptionFfi>::Target, value: *mut $ty, @@ -76,61 +78,90 @@ macro_rules! rust_option_shims { #[cfg(feature = "alloc")] const _: () = { /* Vec impl */ - #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$new")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$rust_vec$", $segment, "$new")] unsafe extern "C" fn __const_new(this: *mut > as OptionFfi>::Target) { unsafe { ptr::write(this, Option::<&RustVec<$ty>>::new_ffi()) }; } - #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$drop")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$rust_vec$", $segment, "$drop")] unsafe extern "C" fn __const_drop(this: *mut > as OptionFfi>::Target) { unsafe { ptr::drop_in_place(this) } } - #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$has_value")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$rust_vec$", $segment, "$has_value")] unsafe extern "C" fn __const_has_value(this: *const > as OptionFfi>::Target) -> bool { let o: &> as OptionFfi>::Target = unsafe { this.as_ref().unwrap() }; > as OptionFfi>::Target::as_option_vec_ref(o).is_some() } - #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$value")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$rust_vec$", $segment, "$value")] unsafe extern "C" fn __const_value(this: *const > as OptionFfi>::Target) -> *const RustVec<$ty> { unsafe { this.as_ref().unwrap().as_option().as_ref().copied().unwrap() as *const RustVec<$ty> } } - #[export_name = concat!("cxxbridge1$rust_option$const$rust_vec$", $segment, "$set")] + #[export_name = concat!("cxxbridge1$rust_option$const$ref$rust_vec$", $segment, "$set")] unsafe extern "C" fn __const_set( this: *mut > as OptionFfi>::Target, value: *mut RustVec<$ty>, ) { unsafe { > as OptionFfi>::Target::as_option_vec_ref_mut(this.as_mut().unwrap()).replace((&*value).as_vec()); } - } - #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$new")] - unsafe extern "C" fn __new(this: *mut > as OptionFfi>::Target) { + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$new")] + unsafe extern "C" fn __mut_new(this: *mut > as OptionFfi>::Target) { unsafe { ptr::write(this, Option::<&mut RustVec<$ty>>::new_ffi()) } } - #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$drop")] - unsafe extern "C" fn __drop(this: *mut > as OptionFfi>::Target) { + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$drop")] + unsafe extern "C" fn __mut__drop(this: *mut > as OptionFfi>::Target) { unsafe { ptr::drop_in_place(this) } } - #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$has_value")] - unsafe extern "C" fn __has_value(this: *const > as OptionFfi>::Target) -> bool { + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$has_value")] + unsafe extern "C" fn __mut__has_value(this: *const > as OptionFfi>::Target) -> bool { let o: &> as OptionFfi>::Target = unsafe { this.as_ref().unwrap() }; > as OptionFfi>::Target::as_option_vec_mut(o).is_some() } - #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$value_const")] - unsafe extern "C" fn __value_const(this: *const > as OptionFfi>::Target) -> *const RustVec<$ty> { + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$value_const")] + unsafe extern "C" fn __mut__value_const(this: *const > as OptionFfi>::Target) -> *const RustVec<$ty> { let v: &alloc::vec::Vec<_> = unsafe { > as OptionFfi>::Target::as_option_vec_mut(this.as_ref().unwrap()).as_ref().unwrap() }; v as *const alloc::vec::Vec<$ty> as *const RustVec<$ty> } - #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$value")] - unsafe extern "C" fn __value(this: *mut > as OptionFfi>::Target) -> *mut RustVec<$ty> { + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$value")] + unsafe extern "C" fn __mut__value(this: *mut > as OptionFfi>::Target) -> *mut RustVec<$ty> { let ptr = unsafe { > as OptionFfi>::Target::as_option_vec_mut_mut(this.as_mut().unwrap()).as_mut().unwrap() }; *ptr as *mut alloc::vec::Vec<$ty> as *mut RustVec<$ty> } - #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$set")] - unsafe extern "C" fn __set( + #[export_name = concat!("cxxbridge1$rust_option$ref$rust_vec$", $segment, "$set")] + unsafe extern "C" fn __mut__set( this: *mut > as OptionFfi>::Target, value: *mut RustVec<$ty>, ) { unsafe { > as OptionFfi>::Target::as_option_vec_mut_mut(this.as_mut().unwrap()).replace((&mut *value).as_mut_vec()); } } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$new")] + unsafe extern "C" fn __new(this: *mut > as OptionFfi>::Target) { + unsafe { ptr::write(this, Option::>::new_ffi()) } + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$drop")] + unsafe extern "C" fn __drop(this: *mut > as OptionFfi>::Target) { + unsafe { ptr::drop_in_place(this) } + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$has_value")] + unsafe extern "C" fn __has_value(this: *const > as OptionFfi>::Target) -> bool { + let o: &> as OptionFfi>::Target = unsafe { this.as_ref().unwrap() }; + > as OptionFfi>::Target::as_option(o).is_some() + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$value_const")] + unsafe extern "C" fn __value_const(this: *const > as OptionFfi>::Target) -> *const RustVec<$ty> { + let v: &alloc::vec::Vec<_> = unsafe { > as OptionFfi>::Target::as_option(this.as_ref().unwrap()).as_ref().unwrap() }; + v as *const alloc::vec::Vec<$ty> as *const RustVec<$ty> + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$value")] + unsafe extern "C" fn __value(this: *mut > as OptionFfi>::Target) -> *mut RustVec<$ty> { + let ptr = unsafe { > as OptionFfi>::Target::as_mut_option(this.as_mut().unwrap()).as_mut().unwrap() }; + ptr as *mut alloc::vec::Vec<$ty> as *mut RustVec<$ty> + } + #[export_name = concat!("cxxbridge1$rust_option$rust_vec$", $segment, "$set")] + unsafe extern "C" fn __set( + this: *mut > as OptionFfi>::Target, + value: *mut RustVec<$ty>, + ) { + unsafe { > as OptionFfi>::Target::as_mut_option(this.as_mut().unwrap()).replace(core::mem::take(value.as_mut().unwrap().as_mut_vec())); } + } }; }; } @@ -158,3 +189,57 @@ rust_option_shims_for_primitive!(f64); rust_option_shims!("char", c_char); #[cfg(feature = "alloc")] rust_option_shims!("string", RustString); + +// The String bindings are special cased because of the need to convert +// between String and RustString +#[cfg(feature = "alloc")] +const _: () = { + const_assert_eq!( + mem::size_of::>(), + mem::size_of::< as OptionFfi>::Target>() + ); + + #[export_name = "cxxbridge1$rust_option$string$new"] + unsafe extern "C" fn __new(this: *mut as OptionFfi>::Target) { + unsafe { ptr::write(this, Option::::new_ffi()) } + } + #[export_name = "cxxbridge1$rust_option$string$drop"] + unsafe extern "C" fn __drop(this: *mut as OptionFfi>::Target) { + unsafe { ptr::drop_in_place(this) } + } + #[export_name = "cxxbridge1$rust_option$string$has_value"] + unsafe extern "C" fn __has_value( + this: *const as OptionFfi>::Target, + ) -> bool { + let o: & as OptionFfi>::Target = + unsafe { this.as_ref().unwrap() }; + o.as_option().is_some() + } + #[export_name = "cxxbridge1$rust_option$string$value_const"] + unsafe extern "C" fn __value_const( + this: *const as OptionFfi>::Target, + ) -> *const RustString { + let v: &alloc::string::String = + unsafe { this.as_ref().unwrap().as_option().as_ref().unwrap() }; + v as *const alloc::string::String as *const RustString + } + #[export_name = "cxxbridge1$rust_option$string$value"] + unsafe extern "C" fn __value( + this: *mut as OptionFfi>::Target, + ) -> *mut alloc::string::String { + let this = unsafe { this.as_mut().unwrap() }; + let ptr = this.as_mut_option().as_mut().unwrap(); + ptr as _ + } + #[export_name = "cxxbridge1$rust_option$string$set"] + unsafe extern "C" fn __set( + this: *mut as OptionFfi>::Target, + value: *mut RustString, + ) { + unsafe { + this.as_mut() + .unwrap() + .set(core::mem::take(value.as_mut().unwrap().as_mut_string())); + } + } +}; diff --git a/syntax/check.rs b/syntax/check.rs index 47bf85176..baf287e98 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -146,6 +146,12 @@ fn check_type_rust_option(cx: &mut Check, ty: &Ty1) { match &ty.inner { Type::RustBox(_) => return, Type::Ref(_) => return, + Type::RustVec(_) => return, + Type::Ident(ident) => { + if let Some(RustString) = Atom::from(&ident.rust) { + return; + } + } _ => {} } diff --git a/syntax/instantiate.rs b/syntax/instantiate.rs index 183547e3d..346d7b936 100644 --- a/syntax/instantiate.rs +++ b/syntax/instantiate.rs @@ -21,6 +21,8 @@ pub(crate) enum OptionInner<'a> { MutRef(NamedImplKey<'a>), RefVec(NamedImplKey<'a>), MutRefVec(NamedImplKey<'a>), + Vec(NamedImplKey<'a>), + Ident(NamedImplKey<'a>), } #[derive(Copy, Clone)] @@ -80,6 +82,20 @@ impl Type { } _ => {} }, + Type::RustVec(_) => { + let impl_key = ty.inner.impl_key()?; + match impl_key { + ImplKey::RustVec(named) => { + return Some(ImplKey::RustOption(OptionInner::Vec(named))) + } + _ => unreachable!(), + } + } + Type::Ident(ident) => { + return Some(ImplKey::RustOption(OptionInner::Ident(NamedImplKey::new( + ty, ident, + )))) + } _ => {} } } else if let Type::UniquePtr(ty) = self { diff --git a/syntax/types.rs b/syntax/types.rs index efa8d1574..e02750538 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -243,7 +243,8 @@ impl<'a> Types<'a> { pub(crate) fn needs_indirect_abi(&self, ty: &Type) -> bool { match ty { - Type::RustBox(_) | Type::UniquePtr(_) | Type::RustOption(_) => false, + Type::RustBox(_) | Type::UniquePtr(_) => false, + Type::RustOption(inner) => self.needs_indirect_abi(&inner.inner), Type::Array(_) => true, _ => !self.is_guaranteed_pod(ty), } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index f63b5164e..92cb2ca3a 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -140,6 +140,10 @@ pub mod ffi { fn c_return_rust_mut_option_vec_string(v: &mut Vec) -> Option<&mut Vec>; fn c_return_rust_ref_option_string(s: &String) -> Option<&String>; fn c_return_rust_mut_option_string(s: &mut String) -> Option<&mut String>; + fn c_return_rust_option_vec_native() -> Option>; + fn c_return_rust_option_vec_shared() -> Option>; + fn c_return_rust_option_vec_string() -> Option>; + fn c_return_rust_option_string() -> Option; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; fn c_return_enum(n: u16) -> Enum; @@ -205,6 +209,10 @@ pub mod ffi { fn c_take_rust_mut_option_vec_string(rust: Option<&mut Vec>); fn c_take_rust_ref_option_string(rust: Option<&String>); fn c_take_rust_mut_option_string(rust: Option<&mut String>); + fn c_take_rust_option_vec_native(rust: Option>); + fn c_take_rust_option_vec_shared(rust: Option>); + fn c_take_rust_option_vec_string(rust: Option>); + fn c_take_rust_option_string(rust: Option); fn c_take_ref_shared_string(s: &SharedString) -> &SharedString; fn c_take_callback(callback: fn(String) -> usize); @@ -360,6 +368,10 @@ pub mod ffi { unsafe fn r_return_rust_option_pin_mut_native<'a>( native: Pin<&'a mut u8>, ) -> Option>; + fn r_return_rust_option_vec_native() -> Option>; + fn r_return_rust_option_vec_shared() -> Option>; + fn r_return_rust_option_vec_string() -> Option>; + fn r_return_rust_option_string() -> Option; fn r_return_identity(_: usize) -> usize; fn r_return_sum(_: usize, _: usize) -> usize; fn r_return_enum(n: u32) -> Enum; @@ -396,6 +408,10 @@ pub mod ffi { fn r_take_rust_option_mut_ref_vec(o: Option<&mut Vec>); fn r_take_rust_option_ref_vec_string(o: Option<&Vec>); fn r_take_rust_option_mut_ref_vec_string(o: Option<&mut Vec>); + fn r_take_rust_option_vec_native(o: Option>); + fn r_take_rust_option_vec_shared(o: Option>); + fn r_take_rust_option_vec_string(o: Option>); + fn r_take_rust_option_string(o: Option); fn r_take_enum(e: Enum); @@ -716,6 +732,22 @@ unsafe fn r_return_rust_option_pin_mut_native<'a>( Some(native) } +fn r_return_rust_option_vec_native() -> Option> { + Some(vec![20, 24]) +} + +fn r_return_rust_option_vec_shared() -> Option> { + Some(vec![ffi::Shared { z: 2024 }]) +} + +fn r_return_rust_option_vec_string() -> Option> { + Some(vec![String::from("2024")]) +} + +fn r_return_rust_option_string() -> Option { + Some(String::from("2024")) +} + fn r_return_identity(n: usize) -> usize { n } @@ -868,6 +900,34 @@ fn r_take_rust_option_mut_ref_vec_string(o: Option<&mut Vec>) { let _ = o; } +fn r_take_rust_option_vec_native(o: Option>) { + match o { + Some(v) => assert_eq!(&v, &[20, 24]), + None => unreachable!(), + } +} + +fn r_take_rust_option_vec_shared(o: Option>) { + match o { + Some(v) => assert_eq!(&v, &[ffi::Shared { z: 2024 }]), + None => unreachable!(), + } +} + +fn r_take_rust_option_vec_string(o: Option>) { + match o { + Some(v) => assert_eq!(&v, &["2024".to_string()]), + None => unreachable!(), + } +} + +fn r_take_rust_option_string(o: Option) { + match o { + Some(v) => assert_eq!(&v, &"2024"), + None => unreachable!(), + } +} + fn r_take_enum(e: ffi::Enum) { let _ = e; } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index e65e6d990..b5c7dad5c 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -767,6 +767,38 @@ void c_take_rust_mut_option_string(rust::Option opt) { } } +void c_take_rust_option_vec_native(rust::Option> opt) { + if (opt.has_value()) { + const auto& value = opt.value(); + if (value[0] == 20 && value[1] == 24) { + cxx_test_suite_set_correct(); + } + } +} + +void c_take_rust_option_vec_shared(rust::Option> opt) { + if (opt.has_value()) { + if (opt.value()[0].z == 2024) { + cxx_test_suite_set_correct(); + } + } +} + +void c_take_rust_option_vec_string(rust::Option> opt) { + if (opt.has_value()) { + const auto& value = opt.value(); + if (std::string(value[0]) == "2024") { + cxx_test_suite_set_correct(); + } + } +} + +void c_take_rust_option_string(rust::Option opt) { + if (opt.has_value() && std::string(opt.value()) == "2024") { + cxx_test_suite_set_correct(); + } +} + const SharedString &c_take_ref_shared_string(const SharedString &s) { if (std::string(s.msg) == "2020") { cxx_test_suite_set_correct(); @@ -890,6 +922,22 @@ rust::Option c_try_return_rust_mut_option_string() { throw std::runtime_error("unimplemented"); } +rust::Option> c_return_rust_option_vec_native() { + return rust::Option>{rust::Vec{20, 24}}; +} + +rust::Option> c_return_rust_option_vec_shared() { + return rust::Option>{rust::Vec{Shared{2024}}}; +} + +rust::Option> c_return_rust_option_vec_string() { + return rust::Option>{rust::Vec{"2024"}}; +} + +rust::Option c_return_rust_option_string() { + return rust::Option{rust::String{"2024"}}; +} + const rust::String &c_try_return_ref(const rust::String &s) { return s; } rust::Str c_try_return_str(rust::Str s) { return s; } @@ -1113,6 +1161,13 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(r_return_enum(0) == Enum::AVal); ASSERT(r_return_enum(1) == Enum::BVal); ASSERT(r_return_enum(2021) == Enum::CVal); + auto option_vec_native = r_return_rust_option_vec_native(); + ASSERT(option_vec_native.has_value() && option_vec_native.value()[0] == 20 && option_vec_native.value()[1] == 24); + auto option_vec_shared = r_return_rust_option_vec_shared(); + ASSERT(option_vec_shared.has_value() && option_vec_shared.value()[0].z == 2024); + auto option_vec_string = r_return_rust_option_vec_string(); + ASSERT(option_vec_string.has_value() && option_vec_string.value()[0] == rust::String{"2024"}); + ASSERT(r_return_rust_option_string() == rust::Option{rust::String{"2024"}}); r_take_primitive(2020); r_take_shared(Shared{2020}); @@ -1130,6 +1185,10 @@ extern "C" const char *cxx_run_test() noexcept { empty_vector.reserve(10); r_take_ref_empty_vector(empty_vector); r_take_enum(Enum::AVal); + r_take_rust_option_vec_native(rust::Option>{rust::Vec{20, 24}}); + r_take_rust_option_vec_shared(rust::Option>{rust::Vec{Shared{2024}}}); + r_take_rust_option_vec_string(rust::Option>{rust::Vec{rust::String{"2024"}}}); + r_take_rust_option_string(rust::Option{rust::String{"2024"}}); ASSERT(r_try_return_primitive() == 2020); try { @@ -1241,6 +1300,9 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(sizeof(rust::Option>) == sizeof(void *)); ASSERT(sizeof(rust::Option) == sizeof(void *)); ASSERT(sizeof(rust::Option) == sizeof(void *)); + ASSERT(sizeof(rust::Option>) == sizeof(rust::Vec)); + ASSERT(sizeof(rust::Option>) == sizeof(rust::Vec)); + ASSERT(sizeof(rust::Option) == sizeof(rust::String)); cxx_test_suite_set_correct(); return nullptr; diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 8a6a76ed1..3d66148f6 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -133,6 +133,10 @@ rust::Option&> c_return_rust_ref_option_vec_string rust::Option&> c_return_rust_mut_option_vec_string(rust::Vec&); rust::Option c_return_rust_ref_option_string(const rust::String&); rust::Option c_return_rust_mut_option_string(rust::String&); +rust::Option> c_return_rust_option_vec_native(); +rust::Option> c_return_rust_option_vec_shared(); +rust::Option> c_return_rust_option_vec_string(); +rust::Option c_return_rust_option_string(); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); Enum c_return_enum(uint16_t n); @@ -204,6 +208,10 @@ void c_take_rust_ref_option_vec_string(rust::Option&>); void c_take_rust_ref_option_string(rust::Option); void c_take_rust_mut_option_string(rust::Option); +void c_take_rust_option_vec_native(rust::Option>); +void c_take_rust_option_vec_shared(rust::Option>); +void c_take_rust_option_vec_string(rust::Option>); +void c_take_rust_option_string(rust::Option); const SharedString &c_take_ref_shared_string(const SharedString &s); void c_take_callback(rust::Fn callback); void c_take_callback_ref(rust::Fn callback); diff --git a/tests/test.rs b/tests/test.rs index 7d3a69104..df88a4a69 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -38,6 +38,7 @@ macro_rules! check { } #[test] +#[allow(clippy::too_many_lines)] fn test_c_return() { let mut shared = ffi::Shared { z: 2020 }; let ns_shared = ffi::AShared { z: 2020 }; @@ -141,6 +142,16 @@ fn test_c_return() { Some(&mut "2020".to_string()), ffi::c_return_rust_mut_option_string(&mut "2020".to_string()) ); + assert_eq!(Some(vec![20, 24]), ffi::c_return_rust_option_vec_native(),); + assert_eq!( + Some(vec![ffi::Shared { z: 2024 }]), + ffi::c_return_rust_option_vec_shared(), + ); + assert_eq!( + Some(vec!["2024".to_string()]), + ffi::c_return_rust_option_vec_string() + ); + assert_eq!(Some("2024".to_string()), ffi::c_return_rust_option_string()); assert_eq!(2020, ffi::c_return_identity(2020)); assert_eq!(2021, ffi::c_return_sum(2020, 1)); match ffi::c_return_enum(0) { @@ -182,6 +193,7 @@ fn test_c_try_return() { } #[test] +#[allow(clippy::too_many_lines)] fn test_c_take() { let unique_ptr = ffi::c_return_unique_ptr(); let unique_ptr_ns = ffi2::c_return_ns_unique_ptr(); @@ -313,6 +325,14 @@ fn test_c_take() { check!(ffi::c_take_rust_mut_option_string(Some( &mut "2020".to_string() ))); + check!(ffi::c_take_rust_option_vec_native(Some(vec![20, 24]))); + check!(ffi::c_take_rust_option_vec_shared(Some(vec![ + ffi::Shared { z: 2024 } + ]))); + check!(ffi::c_take_rust_option_vec_string(Some(vec![ + "2024".to_string() + ]))); + check!(ffi::c_take_rust_option_string(Some("2024".to_string()))); check!(ffi::c_take_enum(ffi::Enum::AVal)); check!(ffi::c_take_ns_enum(ffi::AEnum::AAVal)); diff --git a/tests/ui/option_not_sized.stderr b/tests/ui/option_not_sized.stderr index 20bbdbedc..2b7155499 100644 --- a/tests/ui/option_not_sized.stderr +++ b/tests/ui/option_not_sized.stderr @@ -4,7 +4,10 @@ error[E0277]: the trait bound `&'static (dyn Debug + 'static): cxx::rust_option: 2 | const _: as ::cxx::private::OptionFfi>::Target = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Sized` is not implemented for `(dyn Debug + 'static)`, which is required by `Option<&'static (dyn Debug + 'static)>: OptionFfi` | - = help: the trait `OptionFfi` is implemented for `Option` + = help: the following other types implement trait `OptionFfi`: + Option + Option + Option> = note: required for `&'static (dyn Debug + 'static)` to implement `cxx::rust_option::OptionPtrTarget` = note: required for `Option<&'static (dyn Debug + 'static)>` to implement `OptionFfi` @@ -20,7 +23,10 @@ error[E0277]: the trait bound `&'static (dyn Debug + 'static): cxx::rust_option: 3 | as ::cxx::private::OptionFfi>::Target::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Sized` is not implemented for `(dyn Debug + 'static)`, which is required by `Option<&'static (dyn Debug + 'static)>: OptionFfi` | - = help: the trait `OptionFfi` is implemented for `Option` + = help: the following other types implement trait `OptionFfi`: + Option + Option + Option> = note: required for `&'static (dyn Debug + 'static)` to implement `cxx::rust_option::OptionPtrTarget` = note: required for `Option<&'static (dyn Debug + 'static)>` to implement `OptionFfi` @@ -30,6 +36,9 @@ error[E0277]: the trait bound `&dyn Debug: cxx::rust_option::OptionPtrTarget` is 3 | as ::cxx::private::OptionFfi>::Target::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Sized` is not implemented for `dyn Debug`, which is required by `Option<&dyn Debug>: OptionFfi` | - = help: the trait `OptionFfi` is implemented for `Option` + = help: the following other types implement trait `OptionFfi`: + Option + Option + Option> = note: required for `&dyn Debug` to implement `cxx::rust_option::OptionPtrTarget` = note: required for `Option<&dyn Debug>` to implement `OptionFfi` diff --git a/tests/ui/option_string.rs b/tests/ui/option_string.rs deleted file mode 100644 index 88ed98f62..000000000 --- a/tests/ui/option_string.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[cxx::bridge] -mod ffi { - extern "Rust" { - fn f() -> Option; - } -} - -fn f() -> Option { - unimplemented!() -} - -fn main() {} diff --git a/tests/ui/option_string.stderr b/tests/ui/option_string.stderr deleted file mode 100644 index 5245ee5c6..000000000 --- a/tests/ui/option_string.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unsupported element type of Option - --> tests/ui/option_string.rs:4:19 - | -4 | fn f() -> Option; - | ^^^^^^^^^^^^^^ diff --git a/tests/ui/option_vec.rs b/tests/ui/option_vec.rs deleted file mode 100644 index 20ba0dfaa..000000000 --- a/tests/ui/option_vec.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[cxx::bridge] -mod ffi { - extern "Rust" { - fn f() -> Option>; - } -} - -fn f() -> Option> { - unimplemented!() -} - -fn main() {} diff --git a/tests/ui/option_vec.stderr b/tests/ui/option_vec.stderr deleted file mode 100644 index 82c46eb83..000000000 --- a/tests/ui/option_vec.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unsupported element type of Option - --> tests/ui/option_vec.rs:4:19 - | -4 | fn f() -> Option>; - | ^^^^^^^^^^^^^^^