diff --git a/ctest-next/src/ast/type_alias.rs b/ctest-next/src/ast/type_alias.rs index 7faeeb1e8e32..4947e934dde5 100644 --- a/ctest-next/src/ast/type_alias.rs +++ b/ctest-next/src/ast/type_alias.rs @@ -5,7 +5,6 @@ use crate::BoxStr; pub struct Type { pub(crate) public: bool, pub(crate) ident: BoxStr, - #[expect(unused)] pub(crate) ty: syn::Type, } diff --git a/ctest-next/src/ffi_items.rs b/ctest-next/src/ffi_items.rs index db3dd12487cd..d0deede28c8f 100644 --- a/ctest-next/src/ffi_items.rs +++ b/ctest-next/src/ffi_items.rs @@ -37,7 +37,6 @@ impl FfiItems { } /// Return a list of all type aliases found. - #[cfg_attr(not(test), expect(unused))] pub(crate) fn aliases(&self) -> &Vec { &self.aliases } diff --git a/ctest-next/src/generator.rs b/ctest-next/src/generator.rs index 96960459957f..211c639ced91 100644 --- a/ctest-next/src/generator.rs +++ b/ctest-next/src/generator.rs @@ -44,6 +44,7 @@ pub struct TestGenerator { array_arg: Option, skip_private: bool, skip_roundtrip: Option, + pub(crate) skip_signededness: Option, } #[derive(Debug, Error)] @@ -832,6 +833,28 @@ impl TestGenerator { self } + /// Configures whether a type's signededness is tested or not. + /// + /// The closure is given the name of a Rust type, and returns whether the + /// type should be tested as having the right sign (positive or negative). + /// + /// By default all signededness checks are performed. + /// + /// # Examples + /// + /// ```no_run + /// use ctest_next::TestGenerator; + /// + /// let mut cfg = TestGenerator::new(); + /// cfg.skip_signededness(|s| { + /// s.starts_with("foo_") + /// }); + /// ``` + pub fn skip_signededness(&mut self, f: impl Fn(&str) -> bool + 'static) -> &mut Self { + self.skip_signededness = Some(Box::new(f)); + self + } + /// Generate the Rust and C testing files. /// /// Returns the path to the generated file. diff --git a/ctest-next/src/template.rs b/ctest-next/src/template.rs index 933f38c8b2ba..8d5c2e66615c 100644 --- a/ctest-next/src/template.rs +++ b/ctest-next/src/template.rs @@ -46,12 +46,15 @@ impl CTestTemplate { /// Stores all information necessary for generation of tests for all items. #[derive(Clone, Debug, Default)] pub(crate) struct TestTemplate { + pub signededness_tests: Vec, + pub size_align_tests: Vec, pub const_cstr_tests: Vec, pub const_tests: Vec, pub test_idents: Vec, } impl TestTemplate { + /// Populate all tests for all items depending on the configuration provided. pub(crate) fn new( ffi_items: &FfiItems, generator: &TestGenerator, @@ -62,15 +65,20 @@ impl TestTemplate { translator: Translator::new(), }; - /* Figure out which tests are to be generated. */ - // FIXME(ctest): Populate more test information, maybe extract into separate methods. - // The workflow would be to create a struct that stores information for the new test, - // and populating that struct here, so that the also things that have to be added to - // the test templates are the new tests parameterized by that struct. + let mut template = Self::default(); + template.populate_const_and_cstr_tests(&helper)?; + template.populate_size_align_tests(&helper)?; + template.populate_signededness_tests(&helper)?; - let mut const_tests = vec![]; - let mut const_cstr_tests = vec![]; - for constant in ffi_items.constants() { + Ok(template) + } + + /// Populates tests for constants and C-str constants, keeping track of the names of each test. + fn populate_const_and_cstr_tests( + &mut self, + helper: &TranslateHelper, + ) -> Result<(), TranslationError> { + for constant in helper.ffi_items.constants() { if let syn::Type::Ptr(ptr) = &constant.ty && let syn::Type::Path(path) = &*ptr.elem && path.path.segments.last().unwrap().ident == "c_char" @@ -82,29 +90,95 @@ impl TestTemplate { rust_val: constant.ident().into(), c_val: helper.c_ident(constant).into(), }; - const_cstr_tests.push(item) + self.const_cstr_tests.push(item.clone()); + self.test_idents.push(item.test_name); } else { let item = TestConst { id: constant.ident().into(), test_name: const_test_ident(constant.ident()), - rust_val: constant.ident.clone(), + rust_val: constant.ident().into(), rust_ty: constant.ty.to_token_stream().to_string().into_boxed_str(), c_val: helper.c_ident(constant).into(), c_ty: helper.c_type(constant)?.into(), }; - const_tests.push(item) + self.const_tests.push(item.clone()); + self.test_idents.push(item.test_name); } } - let mut test_idents = vec![]; - test_idents.extend(const_cstr_tests.iter().map(|test| test.test_name.clone())); - test_idents.extend(const_tests.iter().map(|test| test.test_name.clone())); + Ok(()) + } - Ok(Self { - const_cstr_tests, - const_tests, - test_idents, - }) + /// Populates size and alignment tests for aliases, structs, and unions. + /// + /// It also keeps track of the names of each test. + fn populate_size_align_tests( + &mut self, + helper: &TranslateHelper, + ) -> Result<(), TranslationError> { + for alias in helper.ffi_items.aliases() { + let item = TestSizeAlign { + test_name: size_align_test_ident(alias.ident()), + id: alias.ident().into(), + rust_ty: alias.ident().into(), + c_ty: helper.c_type(alias)?.into(), + }; + self.size_align_tests.push(item.clone()); + self.test_idents.push(item.test_name); + } + for struct_ in helper.ffi_items.structs() { + let item = TestSizeAlign { + test_name: size_align_test_ident(struct_.ident()), + id: struct_.ident().into(), + rust_ty: struct_.ident().into(), + c_ty: helper.c_type(struct_)?.into(), + }; + self.size_align_tests.push(item.clone()); + self.test_idents.push(item.test_name); + } + for union_ in helper.ffi_items.unions() { + let item = TestSizeAlign { + test_name: size_align_test_ident(union_.ident()), + id: union_.ident().into(), + rust_ty: union_.ident().into(), + c_ty: helper.c_type(union_)?.into(), + }; + self.size_align_tests.push(item.clone()); + self.test_idents.push(item.test_name); + } + + Ok(()) + } + + /// Populates signededness tests for aliases. + /// + /// It also keeps track of the names of each test. + fn populate_signededness_tests( + &mut self, + helper: &TranslateHelper, + ) -> Result<(), TranslationError> { + for alias in helper.ffi_items.aliases() { + let should_skip_signededness_test = helper + .generator + .skip_signededness + .as_ref() + .is_some_and(|skip| skip(alias.ident())); + + if !helper.translator.is_signed(helper.ffi_items, &alias.ty) + || should_skip_signededness_test + { + continue; + } + let item = TestSignededness { + test_name: signededness_test_ident(alias.ident()), + id: alias.ident().into(), + c_ty: helper.c_type(alias)?.into(), + }; + self.signededness_tests.push(item.clone()); + self.test_idents.push(item.test_name); + } + + Ok(()) } } @@ -119,6 +193,21 @@ impl TestTemplate { * - `c_ty`: The C type of the constant, qualified with `struct` or `union` if needed. */ +#[derive(Clone, Debug)] +pub(crate) struct TestSignededness { + pub test_name: BoxStr, + pub id: BoxStr, + pub c_ty: BoxStr, +} + +#[derive(Clone, Debug)] +pub(crate) struct TestSizeAlign { + pub test_name: BoxStr, + pub id: BoxStr, + pub rust_ty: BoxStr, + pub c_ty: BoxStr, +} + /// Information required to test a constant CStr. #[derive(Clone, Debug)] pub(crate) struct TestCStr { @@ -139,16 +228,18 @@ pub(crate) struct TestConst { pub c_ty: BoxStr, } -/// The Rust name of the cstr test. -/// -/// The C name of this same test is the same with `__` prepended. +fn signededness_test_ident(ident: &str) -> BoxStr { + format!("ctest_signededness_{ident}").into() +} + +fn size_align_test_ident(ident: &str) -> BoxStr { + format!("ctest_size_align_{ident}").into() +} + fn cstr_test_ident(ident: &str) -> BoxStr { format!("ctest_const_cstr_{ident}").into() } -/// The Rust name of the const test. -/// -/// The C name of this test is the same with `__` prepended. fn const_test_ident(ident: &str) -> BoxStr { format!("ctest_const_{ident}").into() } diff --git a/ctest-next/src/translator.rs b/ctest-next/src/translator.rs index b968497563e0..94921ed4c433 100644 --- a/ctest-next/src/translator.rs +++ b/ctest-next/src/translator.rs @@ -11,6 +11,7 @@ use syn::spanned::Spanned; use thiserror::Error; use crate::BoxStr; +use crate::ffi_items::FfiItems; /// An error that occurs during translation, detailing cause and location. #[derive(Debug, Error)] @@ -314,6 +315,31 @@ impl Translator { } } } + + /// Determine whether a C type is a signed type. + /// + /// For primitive types it checks against a known list of signed types, but for aliases + /// which are the only thing other than primitives that can be signed, it recursively checks + /// the underlying type of the alias. + pub(crate) fn is_signed(&self, ffi_items: &FfiItems, ty: &syn::Type) -> bool { + match ty { + syn::Type::Path(path) => { + let ident = path.path.segments.last().unwrap().ident.clone(); + if let Some(aliased) = ffi_items.aliases().iter().find(|a| ident == a.ident()) { + return self.is_signed(ffi_items, &aliased.ty); + } + match self.translate_primitive_type(&ident).as_str() { + "char" | "short" | "long" | "long long" | "size_t" | "ssize_t" => true, + s => { + s.starts_with("int") + || s.starts_with("uint") | s.starts_with("signed ") + || s.starts_with("unsigned ") + } + } + } + _ => false, + } + } } /// Translate a simple Rust expression to C. diff --git a/ctest-next/templates/test.c b/ctest-next/templates/test.c index 4e00cacf19c3..c2405094d92e 100644 --- a/ctest-next/templates/test.c +++ b/ctest-next/templates/test.c @@ -34,3 +34,22 @@ static {{ constant.c_ty }} ctest_const_{{ constant.id }}_val_static = {{ constan return &ctest_const_{{ constant.id }}_val_static; } {%- endfor +%} + +{%- for item in ctx.size_align_tests +%} + +// Return the size of a type. +uint64_t ctest_size_of__{{ item.id }}(void) { return sizeof({{ item.c_ty }}); } + +// Return the alignment of a type. +uint64_t ctest_align_of__{{ item.id }}(void) { return _Alignof({{ item.c_ty }}); } +{%- endfor +%} + +{%- for alias in ctx.signededness_tests +%} + +// Return `1` if the type is signed, otherwise return `0`. +// Casting -1 to the aliased type if signed evaluates to `-1 < 0`, if unsigned to `MAX_VALUE < 0` +uint32_t ctest_signededness_of__{{ alias.id }}(void) { + {{ alias.c_ty }} all_ones = ({{ alias.c_ty }}) -1; + return all_ones < 0; +} +{%- endfor +%} diff --git a/ctest-next/templates/test.rs b/ctest-next/templates/test.rs index d85da91b92c0..6678eddef54f 100644 --- a/ctest-next/templates/test.rs +++ b/ctest-next/templates/test.rs @@ -98,6 +98,45 @@ mod generated_tests { } } {%- endfor +%} + +{%- for item in ctx.size_align_tests +%} + + /// Compare the size and alignment of the type in Rust and C, making sure they are the same. + pub fn {{ item.test_name }}() { + extern "C" { + fn ctest_size_of__{{ item.id }}() -> u64; + fn ctest_align_of__{{ item.id }}() -> u64; + } + + let rust_size = size_of::<{{ item.rust_ty }}>() as u64; + let c_size = unsafe { ctest_size_of__{{ item.id }}() }; + + let rust_align = align_of::<{{ item.rust_ty }}>() as u64; + let c_align = unsafe { ctest_align_of__{{ item.id }}() }; + + check_same(rust_size, c_size, "{{ item.id }} size"); + check_same(rust_align, c_align, "{{ item.id }} align"); + } +{%- endfor +%} + +{%- for alias in ctx.signededness_tests +%} + + /// Make sure that the signededness of a type alias in Rust and C is the same. + /// + /// This is done by casting 0 to that type and flipping all of its bits. For unsigned types, + /// this would result in a value larger than zero. For signed types, this results in a value + /// smaller than 0. + pub fn {{ alias.test_name }}() { + extern "C" { + fn ctest_signededness_of__{{ alias.id }}() -> u32; + } + let all_ones = !(0 as {{ alias.id }}); + let all_zeros = 0 as {{ alias.id }}; + let c_is_signed = unsafe { ctest_signededness_of__{{ alias.id }}() }; + + check_same((all_ones < all_zeros) as u32, c_is_signed, "{{ alias.id }} signed"); + } +{%- endfor +%} } use generated_tests::*; diff --git a/ctest-next/tests/input/hierarchy.out.c b/ctest-next/tests/input/hierarchy.out.c index 97448f7e9549..f7dbaf08abf3 100644 --- a/ctest-next/tests/input/hierarchy.out.c +++ b/ctest-next/tests/input/hierarchy.out.c @@ -14,3 +14,16 @@ static bool ctest_const_ON_val_static = ON; bool *ctest_const__ON(void) { return &ctest_const_ON_val_static; } + +// Return the size of a type. +uint64_t ctest_size_of__in6_addr(void) { return sizeof(in6_addr); } + +// Return the alignment of a type. +uint64_t ctest_align_of__in6_addr(void) { return _Alignof(in6_addr); } + +// Return `1` if the type is signed, otherwise return `0`. +// Casting -1 to the aliased type if signed evaluates to `-1 < 0`, if unsigned to `MAX_VALUE < 0` +uint32_t ctest_signededness_of__in6_addr(void) { + in6_addr all_ones = (in6_addr) -1; + return all_ones < 0; +} diff --git a/ctest-next/tests/input/hierarchy.out.rs b/ctest-next/tests/input/hierarchy.out.rs index 214dfe986d7a..d9da3923bcfa 100644 --- a/ctest-next/tests/input/hierarchy.out.rs +++ b/ctest-next/tests/input/hierarchy.out.rs @@ -66,6 +66,39 @@ mod generated_tests { check_same_hex(b1, b2, &format!("ON value at byte {}", i)); } } + + /// Compare the size and alignment of the type in Rust and C, making sure they are the same. + pub fn ctest_size_align_in6_addr() { + extern "C" { + fn ctest_size_of__in6_addr() -> u64; + fn ctest_align_of__in6_addr() -> u64; + } + + let rust_size = size_of::() as u64; + let c_size = unsafe { ctest_size_of__in6_addr() }; + + let rust_align = align_of::() as u64; + let c_align = unsafe { ctest_align_of__in6_addr() }; + + check_same(rust_size, c_size, "in6_addr size"); + check_same(rust_align, c_align, "in6_addr align"); + } + + /// Make sure that the signededness of a type alias in Rust and C is the same. + /// + /// This is done by casting 0 to that type and flipping all of its bits. For unsigned types, + /// this would result in a value larger than zero. For signed types, this results in a value + /// smaller than 0. + pub fn ctest_signededness_in6_addr() { + extern "C" { + fn ctest_signededness_of__in6_addr() -> u32; + } + let all_ones = !(0 as in6_addr); + let all_zeros = 0 as in6_addr; + let c_is_signed = unsafe { ctest_signededness_of__in6_addr() }; + + check_same((all_ones < all_zeros) as u32, c_is_signed, "in6_addr signed"); + } } use generated_tests::*; @@ -86,4 +119,6 @@ fn main() { // Run all tests by calling the functions that define them. fn run_all() { ctest_const_ON(); + ctest_size_align_in6_addr(); + ctest_signededness_in6_addr(); } diff --git a/ctest-next/tests/input/macro.out.c b/ctest-next/tests/input/macro.out.c index faaf70bea02a..414d948695d2 100644 --- a/ctest-next/tests/input/macro.out.c +++ b/ctest-next/tests/input/macro.out.c @@ -6,3 +6,15 @@ #include #include + +// Return the size of a type. +uint64_t ctest_size_of__VecU8(void) { return sizeof(struct VecU8); } + +// Return the alignment of a type. +uint64_t ctest_align_of__VecU8(void) { return _Alignof(struct VecU8); } + +// Return the size of a type. +uint64_t ctest_size_of__VecU16(void) { return sizeof(struct VecU16); } + +// Return the alignment of a type. +uint64_t ctest_align_of__VecU16(void) { return _Alignof(struct VecU16); } diff --git a/ctest-next/tests/input/macro.out.rs b/ctest-next/tests/input/macro.out.rs index 61c7b4a3a4f9..e021f3cbdf3c 100644 --- a/ctest-next/tests/input/macro.out.rs +++ b/ctest-next/tests/input/macro.out.rs @@ -40,6 +40,40 @@ mod generated_tests { NTESTS.fetch_add(1, Ordering::Relaxed); } } + + /// Compare the size and alignment of the type in Rust and C, making sure they are the same. + pub fn ctest_size_align_VecU8() { + extern "C" { + fn ctest_size_of__VecU8() -> u64; + fn ctest_align_of__VecU8() -> u64; + } + + let rust_size = size_of::() as u64; + let c_size = unsafe { ctest_size_of__VecU8() }; + + let rust_align = align_of::() as u64; + let c_align = unsafe { ctest_align_of__VecU8() }; + + check_same(rust_size, c_size, "VecU8 size"); + check_same(rust_align, c_align, "VecU8 align"); + } + + /// Compare the size and alignment of the type in Rust and C, making sure they are the same. + pub fn ctest_size_align_VecU16() { + extern "C" { + fn ctest_size_of__VecU16() -> u64; + fn ctest_align_of__VecU16() -> u64; + } + + let rust_size = size_of::() as u64; + let c_size = unsafe { ctest_size_of__VecU16() }; + + let rust_align = align_of::() as u64; + let c_align = unsafe { ctest_align_of__VecU16() }; + + check_same(rust_size, c_size, "VecU16 size"); + check_same(rust_align, c_align, "VecU16 align"); + } } use generated_tests::*; @@ -59,4 +93,6 @@ fn main() { // Run all tests by calling the functions that define them. fn run_all() { + ctest_size_align_VecU8(); + ctest_size_align_VecU16(); } diff --git a/ctest-next/tests/input/simple.out.with-renames.c b/ctest-next/tests/input/simple.out.with-renames.c index b955ae31d559..1a0768e9ea3b 100644 --- a/ctest-next/tests/input/simple.out.with-renames.c +++ b/ctest-next/tests/input/simple.out.with-renames.c @@ -22,3 +22,28 @@ static char *ctest_const_B_val_static = C_B; char *ctest_const_cstr__B(void) { return ctest_const_B_val_static; } + +// Return the size of a type. +uint64_t ctest_size_of__Byte(void) { return sizeof(Byte); } + +// Return the alignment of a type. +uint64_t ctest_align_of__Byte(void) { return _Alignof(Byte); } + +// Return the size of a type. +uint64_t ctest_size_of__Person(void) { return sizeof(struct Person); } + +// Return the alignment of a type. +uint64_t ctest_align_of__Person(void) { return _Alignof(struct Person); } + +// Return the size of a type. +uint64_t ctest_size_of__Word(void) { return sizeof(union Word); } + +// Return the alignment of a type. +uint64_t ctest_align_of__Word(void) { return _Alignof(union Word); } + +// Return `1` if the type is signed, otherwise return `0`. +// Casting -1 to the aliased type if signed evaluates to `-1 < 0`, if unsigned to `MAX_VALUE < 0` +uint32_t ctest_signededness_of__Byte(void) { + Byte all_ones = (Byte) -1; + return all_ones < 0; +} diff --git a/ctest-next/tests/input/simple.out.with-renames.rs b/ctest-next/tests/input/simple.out.with-renames.rs index f1a74e663faf..4f37ee9bc912 100644 --- a/ctest-next/tests/input/simple.out.with-renames.rs +++ b/ctest-next/tests/input/simple.out.with-renames.rs @@ -86,6 +86,73 @@ mod generated_tests { check_same(r_val, c_val, "const B string"); } + + /// Compare the size and alignment of the type in Rust and C, making sure they are the same. + pub fn ctest_size_align_Byte() { + extern "C" { + fn ctest_size_of__Byte() -> u64; + fn ctest_align_of__Byte() -> u64; + } + + let rust_size = size_of::() as u64; + let c_size = unsafe { ctest_size_of__Byte() }; + + let rust_align = align_of::() as u64; + let c_align = unsafe { ctest_align_of__Byte() }; + + check_same(rust_size, c_size, "Byte size"); + check_same(rust_align, c_align, "Byte align"); + } + + /// Compare the size and alignment of the type in Rust and C, making sure they are the same. + pub fn ctest_size_align_Person() { + extern "C" { + fn ctest_size_of__Person() -> u64; + fn ctest_align_of__Person() -> u64; + } + + let rust_size = size_of::() as u64; + let c_size = unsafe { ctest_size_of__Person() }; + + let rust_align = align_of::() as u64; + let c_align = unsafe { ctest_align_of__Person() }; + + check_same(rust_size, c_size, "Person size"); + check_same(rust_align, c_align, "Person align"); + } + + /// Compare the size and alignment of the type in Rust and C, making sure they are the same. + pub fn ctest_size_align_Word() { + extern "C" { + fn ctest_size_of__Word() -> u64; + fn ctest_align_of__Word() -> u64; + } + + let rust_size = size_of::() as u64; + let c_size = unsafe { ctest_size_of__Word() }; + + let rust_align = align_of::() as u64; + let c_align = unsafe { ctest_align_of__Word() }; + + check_same(rust_size, c_size, "Word size"); + check_same(rust_align, c_align, "Word align"); + } + + /// Make sure that the signededness of a type alias in Rust and C is the same. + /// + /// This is done by casting 0 to that type and flipping all of its bits. For unsigned types, + /// this would result in a value larger than zero. For signed types, this results in a value + /// smaller than 0. + pub fn ctest_signededness_Byte() { + extern "C" { + fn ctest_signededness_of__Byte() -> u32; + } + let all_ones = !(0 as Byte); + let all_zeros = 0 as Byte; + let c_is_signed = unsafe { ctest_signededness_of__Byte() }; + + check_same((all_ones < all_zeros) as u32, c_is_signed, "Byte signed"); + } } use generated_tests::*; @@ -107,4 +174,8 @@ fn main() { fn run_all() { ctest_const_cstr_A(); ctest_const_cstr_B(); + ctest_size_align_Byte(); + ctest_size_align_Person(); + ctest_size_align_Word(); + ctest_signededness_Byte(); } diff --git a/ctest-next/tests/input/simple.out.with-skips.c b/ctest-next/tests/input/simple.out.with-skips.c index d1ad63d083f4..e9ec4618e43f 100644 --- a/ctest-next/tests/input/simple.out.with-skips.c +++ b/ctest-next/tests/input/simple.out.with-skips.c @@ -14,3 +14,28 @@ static char *ctest_const_A_val_static = A; char *ctest_const_cstr__A(void) { return ctest_const_A_val_static; } + +// Return the size of a type. +uint64_t ctest_size_of__Byte(void) { return sizeof(Byte); } + +// Return the alignment of a type. +uint64_t ctest_align_of__Byte(void) { return _Alignof(Byte); } + +// Return the size of a type. +uint64_t ctest_size_of__Person(void) { return sizeof(struct Person); } + +// Return the alignment of a type. +uint64_t ctest_align_of__Person(void) { return _Alignof(struct Person); } + +// Return the size of a type. +uint64_t ctest_size_of__Word(void) { return sizeof(union Word); } + +// Return the alignment of a type. +uint64_t ctest_align_of__Word(void) { return _Alignof(union Word); } + +// Return `1` if the type is signed, otherwise return `0`. +// Casting -1 to the aliased type if signed evaluates to `-1 < 0`, if unsigned to `MAX_VALUE < 0` +uint32_t ctest_signededness_of__Byte(void) { + Byte all_ones = (Byte) -1; + return all_ones < 0; +} diff --git a/ctest-next/tests/input/simple.out.with-skips.rs b/ctest-next/tests/input/simple.out.with-skips.rs index 25168dcd6b97..c8888feeb8ff 100644 --- a/ctest-next/tests/input/simple.out.with-skips.rs +++ b/ctest-next/tests/input/simple.out.with-skips.rs @@ -63,6 +63,73 @@ mod generated_tests { check_same(r_val, c_val, "const A string"); } + + /// Compare the size and alignment of the type in Rust and C, making sure they are the same. + pub fn ctest_size_align_Byte() { + extern "C" { + fn ctest_size_of__Byte() -> u64; + fn ctest_align_of__Byte() -> u64; + } + + let rust_size = size_of::() as u64; + let c_size = unsafe { ctest_size_of__Byte() }; + + let rust_align = align_of::() as u64; + let c_align = unsafe { ctest_align_of__Byte() }; + + check_same(rust_size, c_size, "Byte size"); + check_same(rust_align, c_align, "Byte align"); + } + + /// Compare the size and alignment of the type in Rust and C, making sure they are the same. + pub fn ctest_size_align_Person() { + extern "C" { + fn ctest_size_of__Person() -> u64; + fn ctest_align_of__Person() -> u64; + } + + let rust_size = size_of::() as u64; + let c_size = unsafe { ctest_size_of__Person() }; + + let rust_align = align_of::() as u64; + let c_align = unsafe { ctest_align_of__Person() }; + + check_same(rust_size, c_size, "Person size"); + check_same(rust_align, c_align, "Person align"); + } + + /// Compare the size and alignment of the type in Rust and C, making sure they are the same. + pub fn ctest_size_align_Word() { + extern "C" { + fn ctest_size_of__Word() -> u64; + fn ctest_align_of__Word() -> u64; + } + + let rust_size = size_of::() as u64; + let c_size = unsafe { ctest_size_of__Word() }; + + let rust_align = align_of::() as u64; + let c_align = unsafe { ctest_align_of__Word() }; + + check_same(rust_size, c_size, "Word size"); + check_same(rust_align, c_align, "Word align"); + } + + /// Make sure that the signededness of a type alias in Rust and C is the same. + /// + /// This is done by casting 0 to that type and flipping all of its bits. For unsigned types, + /// this would result in a value larger than zero. For signed types, this results in a value + /// smaller than 0. + pub fn ctest_signededness_Byte() { + extern "C" { + fn ctest_signededness_of__Byte() -> u32; + } + let all_ones = !(0 as Byte); + let all_zeros = 0 as Byte; + let c_is_signed = unsafe { ctest_signededness_of__Byte() }; + + check_same((all_ones < all_zeros) as u32, c_is_signed, "Byte signed"); + } } use generated_tests::*; @@ -83,4 +150,8 @@ fn main() { // Run all tests by calling the functions that define them. fn run_all() { ctest_const_cstr_A(); + ctest_size_align_Byte(); + ctest_size_align_Person(); + ctest_size_align_Word(); + ctest_signededness_Byte(); }