diff --git a/ctest/src/template.rs b/ctest/src/template.rs index f518200ea6be6..538ab18eee1f0 100644 --- a/ctest/src/template.rs +++ b/ctest/src/template.rs @@ -576,7 +576,6 @@ fn static_test_ident(ident: &str) -> BoxStr { /// Wrap methods that depend on both ffi items and the generator. pub(crate) struct TranslateHelper<'a> { filtered_ffi_items: FfiItems, - ffi_items: &'a FfiItems, generator: &'a TestGenerator, translator: Translator<'a>, } @@ -584,10 +583,8 @@ pub(crate) struct TranslateHelper<'a> { impl<'a> TranslateHelper<'a> { /// Create a new translation helper. pub(crate) fn new(ffi_items: &'a FfiItems, generator: &'a TestGenerator) -> Self { - let filtered_ffi_items = ffi_items.clone(); let mut helper = Self { - filtered_ffi_items, - ffi_items, + filtered_ffi_items: ffi_items.clone(), generator, translator: Translator::new(ffi_items, generator), }; @@ -697,15 +694,7 @@ impl<'a> TranslateHelper<'a> { ) })?; - let item = if self.ffi_items.contains_struct(&ty) { - MapInput::StructType(&ty) - } else if self.ffi_items.contains_union(ident) { - MapInput::UnionType(&ty) - } else if self.generator.c_enums.iter().any(|f| f(&ty)) { - MapInput::CEnumType(&ty) - } else { - MapInput::Type(&ty) - }; + let item = self.translator.map_rust_name_to_c(&ty); Ok(self.generator.rty_to_cty(item)) } diff --git a/ctest/src/translator.rs b/ctest/src/translator.rs index 2fad59e6cbc0d..ac9599d1b06c7 100644 --- a/ctest/src/translator.rs +++ b/ctest/src/translator.rs @@ -234,13 +234,7 @@ impl<'a> Translator<'a> { } let name = last.ident.to_string(); - let item = if self.ffi_items.contains_struct(&name) { - MapInput::StructType(&name) - } else if self.ffi_items.contains_union(&name) { - MapInput::UnionType(&name) - } else { - MapInput::Type(&name) - }; + let item = self.map_rust_name_to_c(&name); Ok(cdecl::named( &self.generator.rty_to_cty(item), @@ -287,6 +281,18 @@ impl<'a> Translator<'a> { _ => false, } } + + pub(crate) fn map_rust_name_to_c<'name>(&self, name: &'name str) -> MapInput<'name> { + if self.ffi_items.contains_struct(name) { + MapInput::StructType(name) + } else if self.ffi_items.contains_union(name) { + MapInput::UnionType(name) + } else if self.generator.c_enums.iter().any(|f| f(name)) { + MapInput::CEnumType(name) + } else { + MapInput::Type(name) + } + } } /// Translate mutability from Rust to C. diff --git a/ctest/tests/input/simple.h b/ctest/tests/input/simple.h index bb706ad38a98b..137908f0453ff 100644 --- a/ctest/tests/input/simple.h +++ b/ctest/tests/input/simple.h @@ -6,11 +6,19 @@ typedef unsigned long gregset_t[32]; Byte byte = 0x42; +enum Color +{ + RED, + BLUE, + GREEN +}; + struct Person { const char *name; uint8_t age; void (*job)(uint8_t, const char *); + enum Color favorite_color; }; union Word @@ -22,12 +30,5 @@ union Word #define A "abc" #define C_B "bac" -enum Color -{ - RED, - BLUE, - GREEN -}; - extern void *calloc(size_t num, size_t size); extern Byte byte; diff --git a/ctest/tests/input/simple.out.with-renames.c b/ctest/tests/input/simple.out.with-renames.c index 223c1ac205295..233a1395a91e4 100644 --- a/ctest/tests/input/simple.out.with-renames.c +++ b/ctest/tests/input/simple.out.with-renames.c @@ -123,6 +123,16 @@ CTEST_EXTERN uint64_t ctest_size_of__Person__job(void) { return sizeof(((struct Person){}).job); } +// Return the offset of a struct/union field. +CTEST_EXTERN uint64_t ctest_offset_of__Person__favorite_color(void) { + return offsetof(struct Person, favorite_color); +} + +// Return the size of a struct/union field. +CTEST_EXTERN uint64_t ctest_size_of__Person__favorite_color(void) { + return sizeof(((struct Person){}).favorite_color); +} + // Return the offset of a struct/union field. CTEST_EXTERN uint64_t ctest_offset_of__Word__word(void) { return offsetof(union Word, word); @@ -170,6 +180,15 @@ ctest_field_ptr__Person__job(struct Person *b) { return &b->job; } +// Return a pointer to a struct/union field. +// This field can have a normal data type, or it could be a function pointer or an array, which +// have different syntax. A typedef is used for convenience, but the syntax must be precomputed. +typedef enum Color *ctest_field_ty__Person__favorite_color; +CTEST_EXTERN ctest_field_ty__Person__favorite_color +ctest_field_ptr__Person__favorite_color(struct Person *b) { + return &b->favorite_color; +} + // Return a pointer to a struct/union field. // This field can have a normal data type, or it could be a function pointer or an array, which // have different syntax. A typedef is used for convenience, but the syntax must be precomputed. diff --git a/ctest/tests/input/simple.out.with-renames.rs b/ctest/tests/input/simple.out.with-renames.rs index a4d66ac524ce0..985b43be36601 100644 --- a/ctest/tests/input/simple.out.with-renames.rs +++ b/ctest/tests/input/simple.out.with-renames.rs @@ -348,6 +348,32 @@ mod generated_tests { "field size job of Person"); } + /// Make sure that the offset and size of a field in a struct/union is the same. + pub fn ctest_field_size_offset_Person_favorite_color() { + extern "C" { + fn ctest_offset_of__Person__favorite_color() -> u64; + fn ctest_size_of__Person__favorite_color() -> u64; + } + + let uninit_ty = MaybeUninit::::zeroed(); + let uninit_ty = uninit_ty.as_ptr(); + + // SAFETY: we assume the field access doesn't wrap + let ty_ptr = unsafe { &raw const (*uninit_ty).favorite_color }; + // SAFETY: we assume that all zeros is a valid bitpattern for `ty_ptr`, otherwise the + // test should be skipped. + let val = unsafe { ty_ptr.read_unaligned() }; + + // SAFETY: FFI call with no preconditions + let ctest_field_offset = unsafe { ctest_offset_of__Person__favorite_color() }; + check_same(offset_of!(Person, favorite_color) as u64, ctest_field_offset, + "field offset favorite_color of Person"); + // SAFETY: FFI call with no preconditions + let ctest_field_size = unsafe { ctest_size_of__Person__favorite_color() }; + check_same(size_of_val(&val) as u64, ctest_field_size, + "field size favorite_color of Person"); + } + /// Make sure that the offset and size of a field in a struct/union is the same. pub fn ctest_field_size_offset_Word_word() { extern "C" { @@ -454,6 +480,24 @@ mod generated_tests { "field type job of Person"); } + /// Tests if the pointer to the field is the same in Rust and C. + pub fn ctest_field_ptr_Person_favorite_color() { + extern "C" { + fn ctest_field_ptr__Person__favorite_color(a: *const Person) -> *mut u8; + } + + let uninit_ty = MaybeUninit::::zeroed(); + let ty_ptr = uninit_ty.as_ptr(); + // SAFETY: We don't read `field_ptr`, only compare the pointer itself. + // The assumption is made that this does not wrap the address space. + let field_ptr = unsafe { &raw const ((*ty_ptr).favorite_color) }; + + // SAFETY: FFI call with no preconditions + let ctest_field_ptr = unsafe { ctest_field_ptr__Person__favorite_color(ty_ptr) }; + check_same(field_ptr.cast(), ctest_field_ptr, + "field type favorite_color of Person"); + } + /// Tests if the pointer to the field is the same in Rust and C. pub fn ctest_field_ptr_Word_word() { extern "C" { @@ -720,7 +764,7 @@ mod generated_tests { /// if there are no fields, then everything is padding, if there are fields, then we have to /// go through each field and figure out the padding. fn roundtrip_padding__Person() -> Vec { - if 3 == 0 { + if 4 == 0 { // FIXME(ctest): What if it's an alias to a struct/union? return vec![!false; size_of::()] } @@ -753,6 +797,13 @@ mod generated_tests { let size = size_of_val(&val); let off = offset_of!(Person, job); v.push((off, size)); + + let ty_ptr = unsafe { &raw const ((*bar).favorite_color) }; + let val = unsafe { ty_ptr.read_unaligned() }; + + let size = size_of_val(&val); + let off = offset_of!(Person, favorite_color); + v.push((off, size)); // This vector contains `true` if the byte is padding and `false` if the byte is not // padding. Initialize all bytes as: // - padding if we have fields, this means that only the fields will be checked @@ -1007,11 +1058,11 @@ fn main() { // FIXME(ctest): Maybe consider running the tests in parallel, since everything is independent // and we already use atomics. fn run_all() { - ctest_const_cstr_A(); - ctest_const_cstr_B(); ctest_const_RED(); ctest_const_BLUE(); ctest_const_GREEN(); + ctest_const_cstr_A(); + ctest_const_cstr_B(); ctest_size_align_Byte(); ctest_size_align_gregset_t(); ctest_size_align_Color(); @@ -1021,11 +1072,13 @@ fn run_all() { ctest_field_size_offset_Person_name(); ctest_field_size_offset_Person_age(); ctest_field_size_offset_Person_job(); + ctest_field_size_offset_Person_favorite_color(); ctest_field_size_offset_Word_word(); ctest_field_size_offset_Word_byte(); ctest_field_ptr_Person_name(); ctest_field_ptr_Person_age(); ctest_field_ptr_Person_job(); + ctest_field_ptr_Person_favorite_color(); ctest_field_ptr_Word_word(); ctest_field_ptr_Word_byte(); ctest_roundtrip_Byte(); diff --git a/ctest/tests/input/simple.rs b/ctest/tests/input/simple.rs index 8abef29aeef85..5ff7db41ab5f8 100644 --- a/ctest/tests/input/simple.rs +++ b/ctest/tests/input/simple.rs @@ -5,11 +5,17 @@ pub type Byte = u8; // This should be automatically skipped for roundtripping. pub type gregset_t = [c_ulong; 32]; +pub type Color = c_int; +pub const RED: Color = 0; +pub const BLUE: Color = RED + 1; +pub const GREEN: Color = BLUE + 1; + #[repr(C)] pub struct Person { pub name: *const c_char, pub age: u8, pub job: extern "C" fn(u8, *const c_char), + pub favorite_color: Color, } #[repr(C)] @@ -21,11 +27,6 @@ pub union Word { const A: *const c_char = c"abc".as_ptr(); const B: *const c_char = c"bac".as_ptr(); -pub type Color = c_int; -pub const RED: Color = 0; -pub const BLUE: Color = RED + 1; -pub const GREEN: Color = BLUE + 1; - unsafe extern "C" { pub fn calloc(num: usize, size: usize) -> *mut c_void;