From f7716a5bdb56d36fae3d1bffc9185e989af0f193 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Tue, 11 Oct 2022 10:49:59 -0500 Subject: [PATCH] Handle the `const struct *` and `struct *` patterns. Given that C keeps a different namespace for `struct`s and aliases. The following patterns ```c typedef const struct foo { void *inner; } *foo; typedef struct bar { void *inner; } *bar; ``` are valid C code and produces both a `struct` and a pointer called `foo` and `bar` in different namespaces. Given that Rust does not make this distinction, we add the `_ptr` prefix to the pointer type aliases to avoid any name collisions. --- .../tests/expectations/tests/struct_ptr.rs | 83 +++++++++++++++++++ bindgen-tests/tests/headers/struct_ptr.h | 12 +++ bindgen/ir/ty.rs | 26 +++++- 3 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 bindgen-tests/tests/expectations/tests/struct_ptr.rs create mode 100644 bindgen-tests/tests/headers/struct_ptr.h diff --git a/bindgen-tests/tests/expectations/tests/struct_ptr.rs b/bindgen-tests/tests/expectations/tests/struct_ptr.rs new file mode 100644 index 0000000000..80e757f4d5 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/struct_ptr.rs @@ -0,0 +1,83 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct foo { + pub inner: ::std::os::raw::c_char, +} +#[test] +fn bindgen_test_layout_foo() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 1usize, + concat!("Size of: ", stringify!(foo)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(foo)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).inner) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(foo), + "::", + stringify!(inner) + ) + ); +} +pub type foo_ptr = *const foo; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct bar { + pub inner: ::std::os::raw::c_char, +} +#[test] +fn bindgen_test_layout_bar() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 1usize, + concat!("Size of: ", stringify!(bar)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(bar)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).inner) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(bar), + "::", + stringify!(inner) + ) + ); +} +pub type bar_ptr = *mut bar; +extern "C" { + pub fn takes_foo_ptr(arg1: foo_ptr); +} +extern "C" { + pub fn takes_foo_struct(arg1: foo); +} +extern "C" { + pub fn takes_bar_ptr(arg1: bar_ptr); +} +extern "C" { + pub fn takes_bar_struct(arg1: bar); +} diff --git a/bindgen-tests/tests/headers/struct_ptr.h b/bindgen-tests/tests/headers/struct_ptr.h new file mode 100644 index 0000000000..656f2aece5 --- /dev/null +++ b/bindgen-tests/tests/headers/struct_ptr.h @@ -0,0 +1,12 @@ +typedef const struct foo { + char inner; +} *foo; + +typedef struct bar { + char inner; +} *bar; + +void takes_foo_ptr(foo); +void takes_foo_struct(struct foo); +void takes_bar_ptr(bar); +void takes_bar_struct(struct bar); diff --git a/bindgen/ir/ty.rs b/bindgen/ir/ty.rs index 9edc43d419..0cd86f8985 100644 --- a/bindgen/ir/ty.rs +++ b/bindgen/ir/ty.rs @@ -1094,9 +1094,9 @@ impl Type { } CXType_Typedef => { let inner = cursor.typedef_type().expect("Not valid Type?"); - let inner = + let inner_id = Item::from_ty_or_ref(inner, location, None, ctx); - if inner == potential_id { + if inner_id == potential_id { warn!( "Generating oqaque type instead of self-referential \ typedef"); @@ -1104,7 +1104,27 @@ impl Type { // within the clang parsing. TypeKind::Opaque } else { - TypeKind::Alias(inner) + // Check if this type definition is an alias to a pointer of a `const + // struct` with the same name and add the `_ptr` suffix to it to avoid name + // collisions. + if !ctx.options().c_naming { + if let Some(pointee_spelling) = + inner.pointee_type().map(|ty| ty.spelling()) + { + if let Some(pointee_name) = pointee_spelling + .strip_prefix("const struct ") + .or_else(|| { + pointee_spelling.strip_prefix("struct ") + }) + { + if pointee_name == name { + name += "_ptr"; + } + } + } + } + + TypeKind::Alias(inner_id) } } CXType_Enum => {