Skip to content

&mut [u8] support #452

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions gen/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,9 @@ pub(super) fn write(out: &mut OutFile) {

if builtin.ptr_len {
out.begin_block(Block::Namespace("repr"));
writeln!(out, "template <typename T>");
writeln!(out, "struct PtrLen final {{");
writeln!(out, " const void *ptr;");
writeln!(out, " T *ptr;");
writeln!(out, " size_t len;");
writeln!(out, "}};");
out.end_block(Block::Namespace("repr"));
Expand All @@ -163,7 +164,7 @@ pub(super) fn write(out: &mut OutFile) {
if builtin.rust_str_new_unchecked {
writeln!(
out,
" static Str new_unchecked(repr::PtrLen repr) noexcept {{",
" static Str new_unchecked(repr::PtrLen<const void> repr) noexcept {{",
);
writeln!(out, " Str str;");
writeln!(out, " str.ptr = static_cast<const char *>(repr.ptr);");
Expand All @@ -172,8 +173,8 @@ pub(super) fn write(out: &mut OutFile) {
writeln!(out, " }}");
}
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, " static repr::PtrLen<const void> repr(Str str) noexcept {{");
writeln!(out, " return repr::PtrLen<const void>{{str.ptr, str.len}};");
writeln!(out, " }}");
}
writeln!(out, "}};");
Expand All @@ -187,7 +188,7 @@ pub(super) fn write(out: &mut OutFile) {
if builtin.rust_slice_new {
writeln!(
out,
" static Slice<T> slice(repr::PtrLen repr) noexcept {{",
" static Slice<T> slice(repr::PtrLen<const void> repr) noexcept {{",
);
writeln!(
out,
Expand All @@ -196,11 +197,20 @@ pub(super) fn write(out: &mut OutFile) {
writeln!(out, " }}");
}
if builtin.rust_slice_repr {
writeln!(out, " template <typename U = T>");
writeln!(
out,
" static typename std::enable_if<std::is_const<U>::value, repr::PtrLen<const void>>::type repr(Slice<T> slice) noexcept {{",
);
writeln!(out, " return repr::PtrLen<const void>{{slice.ptr, slice.len}};");
writeln!(out, " }}");

writeln!(out, " template <typename U = T>");
writeln!(
out,
" static repr::PtrLen repr(Slice<T> slice) noexcept {{",
" static typename std::enable_if<!std::is_const<U>::value, repr::PtrLen<void>>::type repr(Slice<T> slice) noexcept {{",
);
writeln!(out, " return repr::PtrLen{{slice.ptr, slice.len}};");
writeln!(out, " return repr::PtrLen<void>{{slice.ptr, slice.len}};");
writeln!(out, " }}");
}
writeln!(out, "}};");
Expand All @@ -211,7 +221,7 @@ pub(super) fn write(out: &mut OutFile) {
writeln!(out, "template <>");
writeln!(out, "class impl<Error> final {{");
writeln!(out, "public:");
writeln!(out, " static Error error(repr::PtrLen repr) noexcept {{");
writeln!(out, " static Error error(repr::PtrLen<const void> repr) noexcept {{");
writeln!(out, " Error error;");
writeln!(out, " error.msg = static_cast<const char *>(repr.ptr);");
writeln!(out, " error.len = repr.len;");
Expand Down
63 changes: 45 additions & 18 deletions gen/src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
}
if efn.throws {
out.builtin.ptr_len = true;
write!(out, "::rust::repr::PtrLen ");
write!(out, "::rust::repr::PtrLen<const void> ");
} else {
write_extern_return_type_space(out, &efn.ret);
}
Expand Down Expand Up @@ -417,7 +417,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
if efn.throws {
out.builtin.ptr_len = true;
out.builtin.trycatch = true;
writeln!(out, "::rust::repr::PtrLen throw$;");
writeln!(out, "::rust::repr::PtrLen<const void> throw$;");
writeln!(out, " ::rust::behavior::trycatch(");
writeln!(out, " [&] {{");
write!(out, " ");
Expand All @@ -436,10 +436,14 @@ 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(Type::SliceRefU8(ty)) if !indirect_return && ty.mutability.is_none() => {
out.builtin.rust_slice_repr = true;
write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::repr(")
}
Some(Type::SliceRefU8(_)) if !indirect_return => {
out.builtin.rust_slice_repr = true;
write!(out, "::rust::impl<::rust::Slice<uint8_t>>::repr(")
}
_ => {}
}
match &efn.receiver {
Expand Down Expand Up @@ -474,12 +478,19 @@ 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<const uint8_t>(static_cast<const uint8_t *>({0}.ptr), {0}.len)",
arg.ident,
);
} else if let Type::SliceRefU8(ref ty) = arg.ty {
match ty.mutability {
None => write!(
out,
"::rust::Slice<const uint8_t>(static_cast<const uint8_t *>({0}.ptr), {0}.len)",
arg.ident,
),
Some(_) => write!(
out,
"::rust::Slice<uint8_t>(static_cast<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);
Expand Down Expand Up @@ -555,7 +566,7 @@ fn write_rust_function_decl_impl(
out.next_section();
if sig.throws {
out.builtin.ptr_len = true;
write!(out, "::rust::repr::PtrLen ");
write!(out, "::rust::repr::PtrLen<const void> ");
} else {
write_extern_return_type_space(out, &sig.ret);
}
Expand Down Expand Up @@ -701,7 +712,7 @@ fn write_rust_function_shim_impl(
}
if sig.throws {
out.builtin.ptr_len = true;
write!(out, "::rust::repr::PtrLen error$ = ");
write!(out, "::rust::repr::PtrLen<const void> error$ = ");
}
write!(out, "{}(", invoke);
let mut needs_comma = false;
Expand All @@ -718,9 +729,13 @@ fn write_rust_function_shim_impl(
out.builtin.rust_str_repr = true;
write!(out, "::rust::impl<::rust::Str>::repr(");
}
Type::SliceRefU8(_) => {
Type::SliceRefU8(ty) => {
out.builtin.rust_slice_repr = true;
write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::repr(");
if ty.mutability.is_none() {
write!(out, "::rust::impl<::rust::Slice<const uint8_t>>::repr(");
} else {
write!(out, "::rust::impl<::rust::Slice<uint8_t>>::repr(");
}
}
ty if out.types.needs_indirect_abi(ty) => write!(out, "&"),
_ => {}
Expand Down Expand Up @@ -823,9 +838,14 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option<Type>) {
write_type(out, &ty.inner);
write!(out, " *");
}
Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) => {
Some(Type::Str(ty)) | Some(Type::SliceRefU8(ty)) => {
out.builtin.ptr_len = true;
write!(out, "::rust::repr::PtrLen ");

if ty.mutable {
write!(out, "::rust::repr::PtrLen<void> ");
} else {
write!(out, "::rust::repr::PtrLen<const void> ");
}
}
Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "),
_ => write_return_type(out, ty),
Expand All @@ -838,9 +858,13 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var) {
write_type_space(out, &ty.inner);
write!(out, "*");
}
Type::Str(_) | Type::SliceRefU8(_) => {
Type::Str(ty) | Type::SliceRefU8(ty) => {
out.builtin.ptr_len = true;
write!(out, "::rust::repr::PtrLen ");
if ty.mutable {
write!(out, "::rust::repr::PtrLen<void> ");
} else {
write!(out, "::rust::repr::PtrLen<const void> ");
}
}
_ => write_type_space(out, &arg.ty),
}
Expand Down Expand Up @@ -890,9 +914,12 @@ fn write_type(out: &mut OutFile, ty: &Type) {
Type::Str(_) => {
write!(out, "::rust::Str");
}
Type::SliceRefU8(_) => {
Type::SliceRefU8(ty) if ty.mutability.is_none() => {
write!(out, "::rust::Slice<const uint8_t>");
}
Type::SliceRefU8(_) => {
write!(out, "::rust::Slice<uint8_t>");
}
Type::Fn(f) => {
write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" });
match &f.ret {
Expand Down
7 changes: 2 additions & 5 deletions include/cxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,18 @@ class Str final {
#ifndef CXXBRIDGE1_RUST_SLICE
template <typename T>
class Slice final {
static_assert(std::is_const<T>::value,
"&[T] needs to be written as rust::Slice<const T> in C++");

public:
Slice() noexcept;
Slice(T *, size_t count) noexcept;

Slice &operator=(const Slice<T> &) noexcept = default;
Slice &operator=(const Slice<T> &that) noexcept = default;

T *data() const noexcept;
size_t size() const noexcept;
size_t length() const noexcept;

// Important in order for System V ABI to pass in registers.
Slice(const Slice<T> &) noexcept = default;
Slice(const Slice<T> &that) noexcept = default;
~Slice() noexcept = default;

private:
Expand Down
15 changes: 10 additions & 5 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ 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) if ty.mutability.is_none() => quote!(::cxx::private::RustSliceU8::from(#var)),
Type::SliceRefU8(_) => quote!(::cxx::private::RustMutSliceU8::from(#var)),
ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
_ => quote!(#var),
}
Expand Down Expand Up @@ -423,7 +424,8 @@ 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) if ty.mutability.is_none() => quote!(#call.as_slice()),
Type::SliceRefU8(ty) if ty.mutability.is_some() => quote!(#call.as_mut_slice()),
_ => call,
},
};
Expand Down Expand Up @@ -610,7 +612,8 @@ fn expand_rust_function_shim_impl(
_ => quote!(#ident),
},
Type::Str(_) => quote!(#ident.as_str()),
Type::SliceRefU8(_) => quote!(#ident.as_slice()),
Type::SliceRefU8(ty) if ty.mutability.is_none() => quote!(#ident.as_slice()),
Type::SliceRefU8(ty) if ty.mutability.is_some() => quote!(#ident.as_mut_slice()),
ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
_ => quote!(#ident),
}
Expand Down Expand Up @@ -654,7 +657,8 @@ 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) if ty.mutability.is_none() => Some(quote!(::cxx::private::RustSliceU8::from)),
Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustMutSliceU8::from)),
_ => None,
});

Expand Down Expand Up @@ -1093,7 +1097,8 @@ fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream {
}
}
Type::Str(_) => quote!(::cxx::private::RustStr),
Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
Type::SliceRefU8(ty) if ty.mutability.is_none() => quote!(::cxx::private::RustSliceU8),
Type::SliceRefU8(_) => quote!(::cxx::private::RustMutSliceU8),
_ => quote!(#ty),
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ mod function;
mod opaque;
mod result;
mod rust_sliceu8;
mod rust_mutsliceu8;
mod rust_str;
mod rust_string;
mod rust_type;
Expand Down Expand Up @@ -441,6 +442,7 @@ pub mod private {
pub use crate::opaque::Opaque;
pub use crate::result::{r#try, Result};
pub use crate::rust_sliceu8::RustSliceU8;
pub use crate::rust_mutsliceu8::RustMutSliceU8;
pub use crate::rust_str::RustStr;
pub use crate::rust_string::RustString;
pub use crate::rust_type::RustType;
Expand Down
30 changes: 30 additions & 0 deletions src/rust_mutsliceu8.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use core::mem;
use core::ptr::NonNull;
use core::slice;

// Not necessarily ABI compatible with &mut [u8]. Codegen performs the translation.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct RustMutSliceU8 {
pub(crate) ptr: NonNull<u8>,
pub(crate) len: usize,
}

impl RustMutSliceU8 {
pub fn from(s: &mut [u8]) -> Self {
let len = s.len();
RustMutSliceU8 {
ptr: NonNull::from(s).cast::<u8>(),
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!(
mem::size_of::<Option<RustMutSliceU8>>(),
mem::size_of::<RustMutSliceU8>(),
);
5 changes: 3 additions & 2 deletions syntax/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ 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) {
Expand Down Expand Up @@ -489,7 +489,8 @@ 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) if ty.mutability.is_none() => "&[u8]".to_owned(),
Type::SliceRefU8(_) => "&mut [u8]".to_owned(),
Type::Fn(_) => "function pointer".to_owned(),
Type::Void(_) => "()".to_owned(),
}
Expand Down
2 changes: 1 addition & 1 deletion syntax/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ fn parse_type_reference(ty: &TypeReference, namespace: &Namespace) -> Result<Typ
}
}
Type::Slice(slice) => 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,
Expand Down
4 changes: 2 additions & 2 deletions tests/cxx_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn test_extern_c_function() {
let output = str::from_utf8(&generated.implementation).unwrap();
// To avoid continual breakage we won't test every byte.
// Let's look for the major features.
assert!(output.contains("void cxxbridge1$do_cpp_thing(::rust::repr::PtrLen foo)"));
assert!(output.contains("void cxxbridge1$do_cpp_thing(::rust::repr::PtrLen<const void> foo)"));
}

#[test]
Expand All @@ -30,5 +30,5 @@ fn test_impl_annotation() {
let source = BRIDGE0.parse().unwrap();
let generated = generate_header_and_cc(source, &opt).unwrap();
let output = str::from_utf8(&generated.implementation).unwrap();
assert!(output.contains("ANNOTATION void cxxbridge1$do_cpp_thing(::rust::repr::PtrLen foo)"));
assert!(output.contains("ANNOTATION void cxxbridge1$do_cpp_thing(::rust::repr::PtrLen<const void> foo)"));
}
Loading