diff --git a/README.md b/README.md index bc7600640..e4ac67e64 100644 --- a/README.md +++ b/README.md @@ -323,6 +323,7 @@ returns of functions. Stringrust::String &strrust::Str &[u8]rust::Slice<const uint8_t>arbitrary &[T] not implemented yet +&mut [u8]rust::Slice<uint8_t>arbitrary &mut [T] not implemented yet CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 31cbb4624..e10c2ccbd 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -25,7 +25,7 @@ - [Reference: built-in bindings](bindings.md) - [String — rust::String](binding/string.md) - [&str — rust::Str](binding/str.md) - - [&[T] — rust::Slice\](binding/slice.md) + - [&[T], &mut [T] — rust::Slice\](binding/slice.md) - [CxxString — std::string](binding/cxxstring.md) - [Box\ — rust::Box\](binding/box.md) - [UniquePtr\ — std::unique\_ptr\](binding/uniqueptr.md) diff --git a/book/src/binding/slice.md b/book/src/binding/slice.md index aacd56b2b..4da7d7397 100644 --- a/book/src/binding/slice.md +++ b/book/src/binding/slice.md @@ -1,5 +1,8 @@ {{#title rust::Slice — Rust ♡ C++}} -# rust::Slice\ +# rust::Slice\, rust::Slice\ + +- Rust `&[T]` is written `rust::Slice` in C++ +- Rust `&mut [T]` is written `rust::Slice` in C++ ### Public API: @@ -15,7 +18,10 @@ public: Slice(const Slice &) noexcept; Slice(T *, size_t count) noexcept; + // if std::is_const { Slice &operator=(const Slice &) noexcept; + // } + Slice &operator=(Slice &&) noexcept; T *data() const noexcept; size_t size() const noexcept; @@ -27,11 +33,14 @@ public: ### Restrictions: -Only &\[u8\] i.e. rust::Slice\ is currently implemented. -Support for arbitrary &\[T\] is coming. +Only T=u8 i.e. rust::Slice\ and rust::Slice\ are +currently implemented. Support for arbitrary T is coming. Allowed as function argument or return value. Not supported in shared structs. -&mut \[T\] is not supported yet. + +Only rust::Slice\ is copy-assignable, not rust::Slice\. (Both are +move-assignable.) You'll need to write std::move occasionally as a reminder that +accidentally exposing overlapping &mut \[T\] to Rust is UB. ## Example diff --git a/book/src/bindings.md b/book/src/bindings.md index f19dc5d13..df6a8d7c4 100644 --- a/book/src/bindings.md +++ b/book/src/bindings.md @@ -12,6 +12,7 @@ returns of extern functions. Stringrust::String &strrust::Str &[u8]rust::Slice<const uint8_t>arbitrary &[T] not implemented yet +&mut [u8]rust::Slice<uint8_t>arbitrary &mut [T] not implemented yet CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type @@ -43,7 +44,6 @@ matter of designing a nice API for each in its non-native language. - diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index 9c5f4fd78..53facbfed 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -149,7 +149,7 @@ pub(super) fn write(out: &mut OutFile) { if builtin.ptr_len { out.begin_block(Block::Namespace("repr")); writeln!(out, "struct PtrLen final {{"); - writeln!(out, " const void *ptr;"); + writeln!(out, " void *ptr;"); writeln!(out, " size_t len;"); writeln!(out, "}};"); out.end_block(Block::Namespace("repr")); @@ -173,7 +173,10 @@ pub(super) fn write(out: &mut OutFile) { } if builtin.rust_str_repr { writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{"); - writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};"); + writeln!( + out, + " return repr::PtrLen{{const_cast(str.ptr), str.len}};", + ); writeln!(out, " }}"); } writeln!(out, "}};"); @@ -189,18 +192,21 @@ pub(super) fn write(out: &mut OutFile) { out, " static Slice slice(repr::PtrLen repr) noexcept {{", ); - writeln!( - out, - " return {{static_cast(repr.ptr), repr.len}};", - ); + writeln!(out, " return {{static_cast(repr.ptr), repr.len}};"); writeln!(out, " }}"); } if builtin.rust_slice_repr { + include.type_traits = true; writeln!( out, " static repr::PtrLen repr(Slice slice) noexcept {{", ); - writeln!(out, " return repr::PtrLen{{slice.ptr, slice.len}};"); + writeln!(out, " return repr::PtrLen{{"); + writeln!( + out, + " const_cast::type *>(slice.ptr),", + ); + writeln!(out, " slice.len}};"); writeln!(out, " }}"); } writeln!(out, "}};"); diff --git a/gen/src/write.rs b/gen/src/write.rs index d99778605..8b512daf2 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -436,9 +436,11 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.builtin.rust_str_repr = true; write!(out, "::rust::impl<::rust::Str>::repr("); } - Some(Type::SliceRefU8(_)) if !indirect_return => { + Some(ty @ Type::SliceRefU8(_)) if !indirect_return => { out.builtin.rust_slice_repr = true; - write!(out, "::rust::impl<::rust::Slice>::repr(") + write!(out, "::rust::impl<"); + write_type(out, ty); + write!(out, ">::repr("); } _ => {} } @@ -474,12 +476,13 @@ 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.ident); - } else if let Type::SliceRefU8(_) = arg.ty { - write!( - out, - "::rust::Slice(static_cast({0}.ptr), {0}.len)", - arg.ident, - ); + } else if let Type::SliceRefU8(slice) = &arg.ty { + write_type(out, &arg.ty); + write!(out, "(static_cast<"); + if slice.mutability.is_none() { + write!(out, "const "); + } + write!(out, "uint8_t *>({0}.ptr), {0}.len)", arg.ident); } else if out.types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, "::std::move(*{})", arg.ident); @@ -507,7 +510,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { writeln!(out, " throw$.len = ::std::strlen(catch$);"); writeln!( out, - " throw$.ptr = ::cxxbridge1$exception(catch$, throw$.len);", + " throw$.ptr = const_cast(::cxxbridge1$exception(catch$, throw$.len));", ); writeln!(out, " }});"); writeln!(out, " return throw$;"); @@ -694,7 +697,9 @@ fn write_rust_function_shim_impl( } Type::SliceRefU8(_) => { out.builtin.rust_slice_new = true; - write!(out, "::rust::impl<::rust::Slice>::slice("); + write!(out, "::rust::impl<"); + write_type(out, ret); + write!(out, ">::slice("); } _ => {} } @@ -720,7 +725,9 @@ fn write_rust_function_shim_impl( } Type::SliceRefU8(_) => { out.builtin.rust_slice_repr = true; - write!(out, "::rust::impl<::rust::Slice>::repr("); + write!(out, "::rust::impl<"); + write_type(out, &arg.ty); + write!(out, ">::repr("); } ty if out.types.needs_indirect_abi(ty) => write!(out, "&"), _ => {} @@ -890,8 +897,12 @@ fn write_type(out: &mut OutFile, ty: &Type) { Type::Str(_) => { write!(out, "::rust::Str"); } - Type::SliceRefU8(_) => { - write!(out, "::rust::Slice"); + Type::SliceRefU8(ty) => { + write!(out, "::rust::Slice<"); + if ty.mutability.is_none() { + write!(out, "const "); + } + write!(out, "uint8_t>"); } Type::Fn(f) => { write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); diff --git a/include/cxx.h b/include/cxx.h index 812278686..1b30497a2 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -87,16 +87,28 @@ class Str final { #endif // CXXBRIDGE1_RUST_STR #ifndef CXXBRIDGE1_RUST_SLICE -template -class Slice final { - static_assert(std::is_const::value, - "&[T] needs to be written as rust::Slice in C++"); +namespace detail { +template +struct copy_assignable_if {}; + +template <> +struct copy_assignable_if { + copy_assignable_if() noexcept = default; + copy_assignable_if(const copy_assignable_if &) noexcept = default; + copy_assignable_if &operator=(const copy_assignable_if &) noexcept = delete; + copy_assignable_if &operator=(copy_assignable_if &&) noexcept = default; +}; +} // namespace detail +template +class Slice final + : private detail::copy_assignable_if::value> { public: Slice() noexcept; Slice(T *, size_t count) noexcept; Slice &operator=(const Slice &) noexcept = default; + Slice &operator=(Slice &&) noexcept = default; T *data() const noexcept; size_t size() const noexcept; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index f29c4cc1e..1fee9286e 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -330,7 +330,10 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { _ => quote!(#var), }, Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)), - Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)), + Type::SliceRefU8(ty) => match ty.mutable { + false => quote!(::cxx::private::RustSliceU8::from_ref(#var)), + true => quote!(::cxx::private::RustSliceU8::from_mut(#var)), + }, ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()), _ => quote!(#var), } @@ -423,7 +426,10 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { _ => call, }, Type::Str(_) => quote!(#call.as_str()), - Type::SliceRefU8(_) => quote!(#call.as_slice()), + Type::SliceRefU8(ty) => match ty.mutable { + false => quote!(#call.as_slice()), + true => quote!(#call.as_mut_slice()), + }, _ => call, }, }; @@ -610,7 +616,10 @@ fn expand_rust_function_shim_impl( _ => quote!(#ident), }, Type::Str(_) => quote!(#ident.as_str()), - Type::SliceRefU8(_) => quote!(#ident.as_slice()), + Type::SliceRefU8(ty) => match ty.mutable { + false => quote!(#ident.as_slice()), + true => quote!(#ident.as_mut_slice()), + }, ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)), _ => quote!(#ident), } @@ -654,7 +663,10 @@ fn expand_rust_function_shim_impl( _ => None, }, Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)), - Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from)), + Type::SliceRefU8(ty) => match ty.mutable { + false => Some(quote!(::cxx::private::RustSliceU8::from_ref)), + true => Some(quote!(::cxx::private::RustSliceU8::from_mut)), + }, _ => None, }); diff --git a/src/cxx.cc b/src/cxx.cc index 923b6412d..28506ef6a 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -180,6 +180,17 @@ static_assert(std::is_trivially_move_assignable>::value, static_assert(std::is_trivially_destructible>::value, "trivial ~Slice()"); +static_assert(std::is_trivially_copy_constructible>::value, + "trivial Slice(const Slice &)"); +static_assert(std::is_trivially_move_constructible>::value, + "trivial Slice(Slice &&)"); +static_assert(!std::is_copy_assignable>::value, + "delete Slice::operator=(const Slice &) for mut slices"); +static_assert(std::is_trivially_move_assignable>::value, + "trivial Slice::operator=(Slice &&)"); +static_assert(std::is_trivially_destructible>::value, + "trivial ~Slice()"); + extern "C" { const char *cxxbridge1$error(const char *ptr, size_t len) { char *copy = new char[len]; diff --git a/src/lib.rs b/src/lib.rs index 0427af699..d1c894ee7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -332,6 +332,7 @@ //! //! //! +//! //! //! //! diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs index 32f8798bd..e14f98859 100644 --- a/src/rust_sliceu8.rs +++ b/src/rust_sliceu8.rs @@ -11,16 +11,27 @@ pub struct RustSliceU8 { } impl RustSliceU8 { - pub fn from(s: &[u8]) -> Self { + pub fn from_ref(s: &[u8]) -> Self { RustSliceU8 { ptr: NonNull::from(s).cast::(), len: s.len(), } } + pub fn from_mut(s: &mut [u8]) -> Self { + RustSliceU8 { + len: s.len(), + ptr: NonNull::from(s).cast::(), + } + } + pub unsafe fn as_slice<'a>(self) -> &'a [u8] { slice::from_raw_parts(self.ptr.as_ptr(), self.len) } + + pub unsafe fn as_mut_slice<'a>(self) -> &'a mut [u8] { + slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) + } } const_assert_eq!( diff --git a/syntax/check.rs b/syntax/check.rs index 2e94f4db2..1ec6178c1 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -178,7 +178,10 @@ fn check_type_ref(cx: &mut Check, ty: &Ref) { } fn check_type_slice(cx: &mut Check, ty: &Slice) { - cx.error(ty, "only &[u8] is supported so far, not other slice types"); + cx.error( + ty, + "only &[u8] and &mut [u8] are supported so far, not other slice types", + ); } fn check_type_fn(cx: &mut Check, ty: &Signature) { @@ -489,7 +492,10 @@ fn describe(cx: &mut Check, ty: &Type) -> String { Type::Str(_) => "&str".to_owned(), Type::CxxVector(_) => "C++ vector".to_owned(), Type::Slice(_) => "slice".to_owned(), - Type::SliceRefU8(_) => "&[u8]".to_owned(), + Type::SliceRefU8(ty) => match ty.mutable { + false => "&[u8]".to_owned(), + true => "&mut [u8]".to_owned(), + }, Type::Fn(_) => "function pointer".to_owned(), Type::Void(_) => "()".to_owned(), } diff --git a/syntax/parse.rs b/syntax/parse.rs index 38b3dccf4..c6d64804d 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -658,7 +658,7 @@ fn parse_type_reference(ty: &TypeReference, namespace: &Namespace) -> Result match &slice.inner { - Type::Ident(ident) if ident.rust == U8 && ty.mutability.is_none() => Type::SliceRefU8, + Type::Ident(ident) if ident.rust == U8 => Type::SliceRefU8, _ => Type::Ref, }, _ => Type::Ref, diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 762af4d67..af72fb46d 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -77,6 +77,7 @@ pub mod ffi { fn c_return_mut(shared: &mut Shared) -> &mut usize; fn c_return_str(shared: &Shared) -> &str; fn c_return_sliceu8(shared: &Shared) -> &[u8]; + fn c_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8]; fn c_return_rust_string() -> String; fn c_return_unique_ptr_string() -> UniquePtr; fn c_return_unique_ptr_vector_u8() -> UniquePtr>; @@ -140,6 +141,7 @@ pub mod ffi { fn c_try_return_ref(s: &String) -> Result<&String>; fn c_try_return_str(s: &str) -> Result<&str>; fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>; + fn c_try_return_mutsliceu8(s: &mut [u8]) -> Result<&mut [u8]>; fn c_try_return_rust_string() -> Result; fn c_try_return_unique_ptr_string() -> Result>; fn c_try_return_rust_vec() -> Result>; @@ -187,6 +189,7 @@ pub mod ffi { fn r_return_mut(shared: &mut Shared) -> &mut usize; fn r_return_str(shared: &Shared) -> &str; fn r_return_sliceu8(shared: &Shared) -> &[u8]; + fn r_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8]; fn r_return_rust_string() -> String; fn r_return_unique_ptr_string() -> UniquePtr; fn r_return_rust_vec() -> Vec; @@ -220,6 +223,7 @@ pub mod ffi { fn r_try_return_box() -> 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]>; fn get(self: &R) -> usize; fn set(self: &mut R, n: usize) -> usize; @@ -371,6 +375,10 @@ fn r_return_sliceu8(shared: &ffi::Shared) -> &[u8] { b"2020" } +fn r_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8] { + slice +} + fn r_return_rust_string() -> String { "2020".to_owned() } @@ -509,6 +517,10 @@ fn r_try_return_sliceu8(slice: &[u8]) -> Result<&[u8], Error> { Ok(slice) } +fn r_try_return_mutsliceu8(slice: &mut [u8]) -> Result<&mut [u8], Error> { + Ok(slice) +} + fn r_aliased_function(x: i32) -> String { x.to_string() } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index f1c293de9..238b1457d 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -77,6 +77,10 @@ rust::Slice c_return_sliceu8(const Shared &shared) { reinterpret_cast(SLICE_DATA), sizeof(SLICE_DATA)); } +rust::Slice c_return_mutsliceu8(rust::Slice slice) { + return slice; +} + rust::String c_return_rust_string() { return "2020"; } std::unique_ptr c_return_unique_ptr_string() { @@ -435,6 +439,10 @@ rust::Slice c_try_return_sliceu8(rust::Slice s) { return s; } +rust::Slice c_try_return_mutsliceu8(rust::Slice s) { + return s; +} + rust::String c_try_return_rust_string() { return c_return_rust_string(); } std::unique_ptr c_try_return_unique_ptr_string() { diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 5acf469db..6c288d724 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -84,6 +84,7 @@ const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared); size_t &c_return_mut(Shared &shared); rust::Str c_return_str(const Shared &shared); rust::Slice c_return_sliceu8(const Shared &shared); +rust::Slice c_return_mutsliceu8(rust::Slice slice); rust::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); std::unique_ptr> c_return_unique_ptr_vector_u8(); @@ -148,6 +149,7 @@ rust::Box c_try_return_box(); const rust::String &c_try_return_ref(const rust::String &); rust::Str c_try_return_str(rust::Str); rust::Slice c_try_return_sliceu8(rust::Slice); +rust::Slice c_try_return_mutsliceu8(rust::Slice); rust::String c_try_return_rust_string(); std::unique_ptr c_try_return_unique_ptr_string(); rust::Vec c_try_return_rust_vec();
name in Rustname in C++
&mut [T]rust::Slice<T>
BTreeMap<K, V>tbd
HashMap<K, V>tbd
Arc<T>tbd
Stringrust::String
&strrust::Str
&[u8]rust::Slice<const uint8_t>arbitrary &[T] not implemented yet
&mut [u8]rust::Slice<uint8_t>arbitrary &mut [T] not implemented yet
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type