From 2c0864cbde83c41e23c64c4cce047aad71f3f3e2 Mon Sep 17 00:00:00 2001 From: kraktus Date: Wed, 4 Jan 2023 00:32:30 +0100 Subject: [PATCH] rust char support POC --- gen/src/builtin.rs | 2 ++ gen/src/write.rs | 2 ++ include/cxx.h | 16 ++++++++++++++++ src/cxx.cc | 15 +++++++++++++++ syntax/atom.rs | 5 ++++- syntax/check.rs | 8 ++++---- syntax/pod.rs | 2 +- tests/ffi/lib.rs | 12 ++++++++++++ tests/ffi/tests.cc | 10 ++++++++++ tests/ffi/tests.h | 2 ++ tests/test.rs | 1 + 11 files changed, 69 insertions(+), 6 deletions(-) diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index 277c64f8d..c886abc70 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -9,6 +9,7 @@ pub struct Builtins<'a> { pub rust_str: bool, pub rust_slice: bool, pub rust_box: bool, + pub rust_char: bool, pub rust_vec: bool, pub rust_fn: bool, pub rust_isize: bool, @@ -179,6 +180,7 @@ pub(super) fn write(out: &mut OutFile) { } ifndef::write(out, builtin.rust_string, "CXXBRIDGE1_RUST_STRING"); + ifndef::write(out, builtin.rust_char, "CXXBRIDGE1_RUST_CHAR"); ifndef::write(out, builtin.rust_str, "CXXBRIDGE1_RUST_STR"); ifndef::write(out, builtin.rust_slice, "CXXBRIDGE1_RUST_SLICE"); ifndef::write(out, builtin.rust_box, "CXXBRIDGE1_RUST_BOX"); diff --git a/gen/src/write.rs b/gen/src/write.rs index 6f535ccb9..2b44cf49e 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -210,6 +210,7 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { Some(Usize) => out.include.cstddef = true, Some(Isize) => out.builtin.rust_isize = true, Some(CxxString) => out.include.string = true, + Some(RustChar) => out.builtin.rust_char = true, Some(RustString) => out.builtin.rust_string = true, Some(Bool) | Some(Char) | Some(F32) | Some(F64) | None => {} }, @@ -1322,6 +1323,7 @@ fn write_atom(out: &mut OutFile, atom: Atom) { F32 => write!(out, "float"), F64 => write!(out, "double"), CxxString => write!(out, "::std::string"), + RustChar => write!(out, "::rust::Char"), RustString => write!(out, "::rust::String"), } } diff --git a/include/cxx.h b/include/cxx.h index 907ee829f..cc542b3ed 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -152,6 +152,22 @@ class Str final { }; #endif // CXXBRIDGE1_RUST_STR +#ifndef CXXBRIDGE1_RUST_CHAR +#define CXXBRIDGE1_RUST_CHAR +// TODO https://cxx.rs/binding/char.html +class Char final { +public: + // Throws std::invalid_argument if not valid Unicode value + Char(char32_t); + + char32_t get() const noexcept; + +private: + char32_t inner; + +}; +#endif // CXXBRIDGE1_RUST_CHAR + #ifndef CXXBRIDGE1_RUST_SLICE namespace detail { template diff --git a/src/cxx.cc b/src/cxx.cc index a7cb6797e..61a6348c1 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -388,6 +388,21 @@ std::size_t sliceLen(const void *self) noexcept { return cxxbridge1$slice$len(self); } +// copied from rust code source: char::from_u32 +inline bool is_invalid_unicode(char32_t c) { + return c >= 0x110000 || (c >= 0xD800 && c < 0xE000); +} + +// Throws std::invalid_argument if not valid Unicode value +Char::Char(char32_t c): inner(c) { + if (is_invalid_unicode(c)) { + panic("data for rust::Char is not unicode value"); + } +} + +char32_t Char::get() const noexcept { return this->inner; } + + // Rust specifies that usize is ABI compatible with C's uintptr_t. // https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize // However there is no direct Rust equivalent for size_t. C does not guarantee diff --git a/syntax/atom.rs b/syntax/atom.rs index d4ad78f17..dc4489e82 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Display}; #[derive(Copy, Clone, PartialEq)] pub enum Atom { Bool, - Char, // C char, not Rust char + Char, // C char, not Rust char, see `RustChar` U8, U16, U32, @@ -19,6 +19,7 @@ pub enum Atom { F32, F64, CxxString, + RustChar, RustString, } @@ -31,6 +32,7 @@ impl Atom { use self::Atom::*; match s { "bool" => Some(Bool), + "char" => Some(RustChar), "c_char" => Some(Char), "u8" => Some(U8), "u16" => Some(U16), @@ -76,6 +78,7 @@ impl AsRef for Atom { F32 => "f32", F64 => "f64", CxxString => "CxxString", + RustChar => "char", RustString => "String", } } diff --git a/syntax/check.rs b/syntax/check.rs index 66883be03..8d99eee52 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -125,7 +125,7 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { match Atom::from(&ident.rust) { None | Some(Bool) | Some(Char) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) - | Some(F32) | Some(F64) | Some(RustString) => return, + | Some(F32) | Some(F64) | Some(RustChar) | Some(RustString) => return, Some(CxxString) => {} } } @@ -165,7 +165,7 @@ fn check_type_shared_ptr(cx: &mut Check, ptr: &Ty1) { None | Some(Bool) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) | Some(CxxString) => return, - Some(Char) | Some(RustString) => {} + Some(Char) | Some(RustChar) | Some(RustString) => {} } } else if let Type::CxxVector(_) = &ptr.inner { cx.error(ptr, "std::shared_ptr is not supported yet"); @@ -185,7 +185,7 @@ fn check_type_weak_ptr(cx: &mut Check, ptr: &Ty1) { match Atom::from(&ident.rust) { None | Some(Bool) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) - | Some(F64) | Some(CxxString) => return, + | Some(F64) | Some(CxxString) | Some(RustChar) => return, Some(Char) | Some(RustString) => {} } } else if let Type::CxxVector(_) = &ptr.inner { @@ -211,7 +211,7 @@ fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) | Some(CxxString) => return, Some(Char) => { /* todo */ } - Some(Bool) | Some(RustString) => {} + Some(Bool) | Some(RustChar) | Some(RustString) => {} } } diff --git a/syntax/pod.rs b/syntax/pod.rs index 0bf152eea..ade84f89b 100644 --- a/syntax/pod.rs +++ b/syntax/pod.rs @@ -10,7 +10,7 @@ impl<'a> Types<'a> { match atom { Bool | Char | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64 => true, - CxxString | RustString => false, + RustChar | CxxString | RustString => false, } } else if let Some(strct) = self.structs.get(ident) { derive::contains(&strct.derives, Trait::Copy) diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index bad8128b3..3015ec43f 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -103,6 +103,7 @@ pub mod ffi { fn c_return_str(shared: &Shared) -> &str; fn c_return_slice_char(shared: &Shared) -> &[c_char]; fn c_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8]; + fn c_return_rust_char() -> char; fn c_return_rust_string() -> String; fn c_return_rust_string_lossy() -> String; fn c_return_unique_ptr_string() -> UniquePtr; @@ -129,6 +130,7 @@ pub mod ffi { fn c_return_mut_ptr(n: usize) -> *mut C; fn c_take_primitive(n: usize); + fn c_take_rust_char(c: char); fn c_take_shared(shared: Shared); fn c_take_box(r: Box); fn c_take_ref_r(r: &R); @@ -258,6 +260,7 @@ pub mod ffi { type R; fn r_return_primitive() -> usize; + fn r_return_rust_char() -> char; fn r_return_shared() -> Shared; fn r_return_box() -> Box; fn r_return_unique_ptr() -> UniquePtr; @@ -279,6 +282,7 @@ pub mod ffi { fn r_return_enum(n: u32) -> Enum; fn r_take_primitive(n: usize); + fn r_take_rust_char(c: char); fn r_take_shared(shared: Shared); fn r_take_box(r: Box); fn r_take_unique_ptr(c: UniquePtr); @@ -442,6 +446,10 @@ fn r_return_primitive() -> usize { 2020 } +fn r_return_rust_char() -> char { + '🙃' +} + fn r_return_shared() -> ffi::Shared { ffi::Shared { z: 2020 } } @@ -519,6 +527,10 @@ fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec { unimplemented!() } +fn r_take_rust_char(c: char) { + assert_eq!('🙃', c); +} + fn r_return_mut_rust_vec(shared: &mut ffi::Shared) -> &mut Vec { let _ = shared; unimplemented!() diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 984e22204..2927ea7b3 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -54,6 +54,8 @@ std::vector &C::get_v() { return this->v; } size_t c_return_primitive() { return 2020; } +rust::Char c_return_rust_char() { return rust::Char{U'\U0001f643'}; } + Shared c_return_shared() { return Shared{2020}; } ::A::AShared c_return_ns_shared() { return ::A::AShared{2020}; } @@ -223,6 +225,12 @@ void Borrow::const_member() const {} void Borrow::nonconst_member() {} +void c_take_rust_char(rust::Char c) { + if (c.get() == U'\U0001f643') { + cxx_test_suite_set_correct(); + } + } + std::unique_ptr c_return_borrow(const std::string &s) { return std::unique_ptr(new Borrow(s)); } @@ -770,6 +778,7 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(rust::size_of() == sizeof(size_t)); ASSERT(rust::align_of() == alignof(size_t)); + ASSERT(r_return_rust_char().get() == U'\U0001f643'); ASSERT(r_return_primitive() == 2020); ASSERT(r_return_shared().z == 2020); ASSERT(cxx_test_suite_r_is_correct(&*r_return_box())); @@ -786,6 +795,7 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(r_return_enum(2021) == Enum::CVal); r_take_primitive(2020); + r_take_rust_char(rust::Char(U'\U0001f643')); r_take_shared(Shared{2020}); r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_shared_ptr(std::shared_ptr(new C{2020})); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index dc02e4ff8..805631e86 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -87,6 +87,7 @@ struct Borrow { typedef char Buffer[12]; size_t c_return_primitive(); +rust::Char c_return_rust_char(); Shared c_return_shared(); ::A::AShared c_return_ns_shared(); ::A::B::ABShared c_return_nested_ns_shared(); @@ -125,6 +126,7 @@ std::unique_ptr c_return_borrow(const std::string &s); const C *c_return_const_ptr(size_t n); C *c_return_mut_ptr(size_t n); +void c_take_rust_char(rust::Char c); void c_take_primitive(size_t n); void c_take_shared(Shared shared); void c_take_ns_shared(::A::AShared shared); diff --git a/tests/test.rs b/tests/test.rs index bcf0a2cd1..6ee68e015 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -118,6 +118,7 @@ fn test_c_take() { let unique_ptr = ffi::c_return_unique_ptr(); let unique_ptr_ns = ffi2::c_return_ns_unique_ptr(); + check!(ffi::c_take_rust_char('🙃')); check!(ffi::c_take_primitive(2020)); check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); check!(ffi::c_take_ns_shared(ffi::AShared { z: 2020 }));