Repository: dtolnay/cxx (language: rust)

Issue:
Support c_char type
These should be implemented in terms of:

- char = https://doc.rust-lang.org/std/os/raw/type.c_char.html
- signed char = https://doc.rust-lang.org/std/os/raw/type.c_schar.html
- unsigned char = https://doc.rust-lang.org/std/os/raw/type.c_uchar.html

Useful for passing `&[c_char]` --\> `rust::Slice<char>` --\> `const char*`.


Discussion / hints:
Maybe only `char` is needed? I guess `signed char` and `unsigned char` will always be the same as `i8` and `u8` on platforms that Rust supports.

Fix patch:
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 1aa128ffb..60936a0b4 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -159,7 +159,7 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
                 Some(Isize) => out.builtin.rust_isize = true,
                 Some(CxxString) => out.include.string = true,
                 Some(RustString) => out.builtin.rust_string = true,
-                Some(Bool) | Some(F32) | Some(F64) | None => {}
+                Some(Bool) | Some(Char) | Some(F32) | Some(F64) | None => {}
             },
             Type::RustBox(_) => out.builtin.rust_box = true,
             Type::RustVec(_) => out.builtin.rust_vec = true,
@@ -924,6 +924,7 @@ fn write_type(out: &mut OutFile, ty: &Type) {
 fn write_atom(out: &mut OutFile, atom: Atom) {
     match atom {
         Bool => write!(out, "bool"),
+        Char => write!(out, "char"),
         U8 => write!(out, "uint8_t"),
         U16 => write!(out, "uint16_t"),
         U32 => write!(out, "uint32_t"),
diff --git a/src/cxx.cc b/src/cxx.cc
index cd72d82b8..2a8ba97ba 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -379,6 +379,7 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
 #define FOR_EACH_RUST_VEC(MACRO)                                               \
   FOR_EACH_NUMERIC(MACRO)                                                      \
   MACRO(bool, bool)                                                            \
+  MACRO(char, char)                                                            \
   MACRO(string, rust::String)
 
 extern "C" {
diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs
index df6dec6de..4f4b6ffc7 100644
--- a/src/symbols/rust_vec.rs
+++ b/src/symbols/rust_vec.rs
@@ -3,6 +3,7 @@ use crate::rust_vec::RustVec;
 use alloc::vec::Vec;
 use core::mem;
 use core::ptr;
+use std::os::raw::c_char;
 
 macro_rules! rust_vec_shims {
     ($segment:expr, $ty:ty) => {
@@ -74,4 +75,5 @@ rust_vec_shims_for_primitive!(i64);
 rust_vec_shims_for_primitive!(f32);
 rust_vec_shims_for_primitive!(f64);
 
+rust_vec_shims!("char", c_char);
 rust_vec_shims!("string", RustString);
diff --git a/syntax/atom.rs b/syntax/atom.rs
index 7d0ef6b62..d4ad78f17 100644
--- a/syntax/atom.rs
+++ b/syntax/atom.rs
@@ -5,6 +5,7 @@ use std::fmt::{self, Display};
 #[derive(Copy, Clone, PartialEq)]
 pub enum Atom {
     Bool,
+    Char, // C char, not Rust char
     U8,
     U16,
     U32,
@@ -30,6 +31,7 @@ impl Atom {
         use self::Atom::*;
         match s {
             "bool" => Some(Bool),
+            "c_char" => Some(Char),
             "u8" => Some(U8),
             "u16" => Some(U16),
             "u32" => Some(U32),
@@ -60,6 +62,7 @@ impl AsRef<str> for Atom {
         use self::Atom::*;
         match self {
             Bool => "bool",
+            Char => "c_char",
             U8 => "u8",
             U16 => "u16",
             U32 => "u32",
diff --git a/syntax/check.rs b/syntax/check.rs
index f5e8e00c0..85db24808 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -99,9 +99,9 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
         }
 
         match Atom::from(&ident.rust) {
-            None | 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,
+            None | 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(Bool) => { /* todo */ }
             Some(CxxString) => {}
         }
@@ -140,6 +140,7 @@ fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
             None | 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) => { /* todo */ }
             Some(Bool) | Some(RustString) => {}
         }
     }
@@ -503,6 +504,8 @@ fn describe(cx: &mut Check, ty: &Type) -> String {
                 "opaque Rust type".to_owned()
             } else if Atom::from(&ident.rust) == Some(CxxString) {
                 "C++ string".to_owned()
+            } else if Atom::from(&ident.rust) == Some(Char) {
+                "C char".to_owned()
             } else {
                 ident.rust.to_string()
             }
diff --git a/syntax/tokens.rs b/syntax/tokens.rs
index 8ce7ed9e5..d450f7306 100644
--- a/syntax/tokens.rs
+++ b/syntax/tokens.rs
@@ -11,7 +11,10 @@ impl ToTokens for Type {
     fn to_tokens(&self, tokens: &mut TokenStream) {
         match self {
             Type::Ident(ident) => {
-                if ident.rust == CxxString {
+                if ident.rust == Char {
+                    let span = ident.rust.span();
+                    tokens.extend(quote_spanned!(span=> ::std::os::raw::));
+                } else if ident.rust == CxxString {
                     let span = ident.rust.span();
                     tokens.extend(quote_spanned!(span=> ::cxx::));
                 }


Test patch:
diff --git a/tests/BUCK b/tests/BUCK
index 4bdcc46d0..b6699e28d 100644
--- a/tests/BUCK
+++ b/tests/BUCK
@@ -12,6 +12,7 @@ rust_test(
 rust_library(
     name = "ffi",
     srcs = [
+        "ffi/cast.rs",
         "ffi/lib.rs",
         "ffi/module.rs",
     ],
diff --git a/tests/BUILD b/tests/BUILD
index b255a3c52..9194ca4e7 100644
--- a/tests/BUILD
+++ b/tests/BUILD
@@ -15,6 +15,7 @@ rust_test(
 rust_library(
     name = "cxx_test_suite",
     srcs = [
+        "ffi/cast.rs",
         "ffi/lib.rs",
         "ffi/module.rs",
     ],
diff --git a/tests/ffi/cast.rs b/tests/ffi/cast.rs
new file mode 100644
index 000000000..30ecb6a8a
--- /dev/null
+++ b/tests/ffi/cast.rs
@@ -0,0 +1,14 @@
+use std::os::raw::c_char;
+use std::slice;
+
+pub fn c_char_to_unsigned(slice: &[c_char]) -> &[u8] {
+    let ptr = slice.as_ptr().cast::<u8>();
+    let len = slice.len();
+    unsafe { slice::from_raw_parts(ptr, len) }
+}
+
+pub fn unsigned_to_c_char(slice: &[u8]) -> &[c_char] {
+    let ptr = slice.as_ptr().cast::<c_char>();
+    let len = slice.len();
+    unsafe { slice::from_raw_parts(ptr, len) }
+}
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 5ea7584b2..41f129a9a 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -6,10 +6,12 @@
     clippy::unnecessary_wraps
 )]
 
+pub mod cast;
 pub mod module;
 
 use cxx::{CxxString, CxxVector, UniquePtr};
 use std::fmt::{self, Display};
+use std::os::raw::c_char;
 
 #[cxx::bridge(namespace = "tests")]
 pub mod ffi {
@@ -81,7 +83,7 @@ pub mod ffi {
         fn c_return_ref(shared: &Shared) -> &usize;
         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_slice_char(shared: &Shared) -> &[c_char];
         fn c_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8];
         fn c_return_rust_string() -> String;
         fn c_return_unique_ptr_string() -> UniquePtr<CxxString>;
@@ -110,7 +112,7 @@ pub mod ffi {
         fn c_take_ref_r(r: &R);
         fn c_take_ref_c(c: &C);
         fn c_take_str(s: &str);
-        fn c_take_sliceu8(s: &[u8]);
+        fn c_take_slice_char(s: &[c_char]);
         fn c_take_slice_shared(s: &[Shared]);
         fn c_take_rust_string(s: String);
         fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
@@ -214,7 +216,7 @@ pub mod ffi {
         fn r_take_ref_r(r: &R);
         fn r_take_ref_c(c: &C);
         fn r_take_str(s: &str);
-        fn r_take_sliceu8(s: &[u8]);
+        fn r_take_slice_char(s: &[c_char]);
         fn r_take_rust_string(s: String);
         fn r_take_unique_ptr_string(s: UniquePtr<CxxString>);
         fn r_take_ref_vector(v: &CxxVector<u8>);
@@ -472,8 +474,9 @@ fn r_take_rust_string(s: String) {
     assert_eq!(s, "2020");
 }
 
-fn r_take_sliceu8(s: &[u8]) {
+fn r_take_slice_char(s: &[c_char]) {
     assert_eq!(s.len(), 5);
+    let s = cast::c_char_to_unsigned(s);
     assert_eq!(std::str::from_utf8(s).unwrap(), "2020\0");
 }
 
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 4ba48bf53..7cd84b814 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -75,10 +75,9 @@ rust::Str c_return_str(const Shared &shared) {
   return "2020";
 }
 
-rust::Slice<const uint8_t> c_return_sliceu8(const Shared &shared) {
+rust::Slice<const char> c_return_slice_char(const Shared &shared) {
   (void)shared;
-  return rust::Slice<const uint8_t>(
-      reinterpret_cast<const uint8_t *>(SLICE_DATA), sizeof(SLICE_DATA));
+  return rust::Slice<const char>(SLICE_DATA, sizeof(SLICE_DATA));
 }
 
 rust::Slice<uint8_t> c_return_mutsliceu8(rust::Slice<uint8_t> slice) {
@@ -243,9 +242,8 @@ void c_take_str(rust::Str s) {
   }
 }
 
-void c_take_sliceu8(rust::Slice<const uint8_t> s) {
-  if (std::string(reinterpret_cast<const char *>(s.data()), s.size()) ==
-      "2020") {
+void c_take_slice_char(rust::Slice<const char> s) {
+  if (std::string(s.data(), s.size()) == "2020") {
     cxx_test_suite_set_correct();
   }
 }
@@ -626,8 +624,7 @@ extern "C" const char *cxx_run_test() noexcept {
   r_take_unique_ptr(std::unique_ptr<C>(new C{2020}));
   r_take_ref_c(C{2020});
   r_take_str(rust::Str("2020"));
-  r_take_sliceu8(rust::Slice<const uint8_t>(
-      reinterpret_cast<const uint8_t *>(SLICE_DATA), sizeof(SLICE_DATA)));
+  r_take_slice_char(rust::Slice<const char>(SLICE_DATA, sizeof(SLICE_DATA)));
   r_take_rust_string(rust::String("2020"));
   r_take_unique_ptr_string(
       std::unique_ptr<std::string>(new std::string("2020")));
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 3a4c3cbe6..57919836d 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -83,7 +83,7 @@ const size_t &c_return_ns_ref(const ::A::AShared &shared);
 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<const uint8_t> c_return_sliceu8(const Shared &shared);
+rust::Slice<const char> c_return_slice_char(const Shared &shared);
 rust::Slice<uint8_t> c_return_mutsliceu8(rust::Slice<uint8_t> slice);
 rust::String c_return_rust_string();
 std::unique_ptr<std::string> c_return_unique_ptr_string();
@@ -114,7 +114,7 @@ void c_take_ref_r(const R &r);
 void c_take_ref_c(const C &c);
 void c_take_ref_ns_c(const ::H::H &h);
 void c_take_str(rust::Str s);
-void c_take_sliceu8(rust::Slice<const uint8_t> s);
+void c_take_slice_char(rust::Slice<const char> s);
 void c_take_slice_shared(rust::Slice<const Shared> s);
 void c_take_rust_string(rust::String s);
 void c_take_unique_ptr_string(std::unique_ptr<std::string> s);
diff --git a/tests/test.rs b/tests/test.rs
index 98fbf459f..b4c39fade 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1,7 +1,7 @@
 #![allow(clippy::assertions_on_constants, clippy::float_cmp, clippy::unit_cmp)]
 
 use cxx_test_suite::module::ffi2;
-use cxx_test_suite::{ffi, R};
+use cxx_test_suite::{cast, ffi, R};
 use std::cell::Cell;
 use std::ffi::CStr;
 
@@ -41,7 +41,10 @@ fn test_c_return() {
     assert_eq!(2020, *ffi::c_return_ns_ref(&ns_shared));
     assert_eq!(2020, *ffi::c_return_nested_ns_ref(&nested_ns_shared));
     assert_eq!("2020", ffi::c_return_str(&shared));
-    assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared));
+    assert_eq!(
+        b"2020\0",
+        cast::c_char_to_unsigned(ffi::c_return_slice_char(&shared)),
+    );
     assert_eq!("2020", ffi::c_return_rust_string());
     assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap());
     assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len());
@@ -116,7 +119,7 @@ fn test_c_take() {
     check!(ffi2::c_take_ref_ns_c(&unique_ptr_ns));
     check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr));
     check!(ffi::c_take_str("2020"));
-    check!(ffi::c_take_sliceu8(b"2020"));
+    check!(ffi::c_take_slice_char(cast::unsigned_to_c_char(b"2020")));
     check!(ffi::c_take_slice_shared(&[
         ffi::Shared { z: 2020 },
         ffi::Shared { z: 2021 },