Skip to content

treat incomplete array as zero length array #456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ struct CodegenResult<'a> {
/// Whether an union has been generated at least once.
saw_union: bool,

/// Whether an incomplete array has been generated at least once.
saw_incomplete_array: bool,

items_seen: HashSet<ItemId>,
/// The set of generated function/var names, needed because in C/C++ is
/// legal to do something like:
Expand Down Expand Up @@ -115,6 +118,7 @@ impl<'a> CodegenResult<'a> {
CodegenResult {
items: vec![],
saw_union: false,
saw_incomplete_array: false,
codegen_id: codegen_id,
items_seen: Default::default(),
functions_seen: Default::default(),
Expand All @@ -132,6 +136,10 @@ impl<'a> CodegenResult<'a> {
self.saw_union = true;
}

fn saw_incomplete_array(&mut self) {
self.saw_incomplete_array = true;
}

fn seen(&self, item: ItemId) -> bool {
self.items_seen.contains(&item)
}
Expand Down Expand Up @@ -175,6 +183,7 @@ impl<'a> CodegenResult<'a> {
cb(&mut new);

self.saw_union |= new.saw_union;
self.saw_incomplete_array |= new.saw_incomplete_array;

new.items
}
Expand Down Expand Up @@ -344,6 +353,9 @@ impl CodeGenerator for Module {
if saw_union && !ctx.options().unstable_rust {
utils::prepend_union_types(ctx, &mut *result);
}
if result.saw_incomplete_array {
utils::prepend_incomplete_array_types(ctx, &mut *result);
}
if ctx.need_bindegen_complex_type() {
utils::prepend_complex_type(ctx, &mut *result);
}
Expand Down Expand Up @@ -1013,6 +1025,16 @@ impl CodeGenerator for CompInfo {
} else {
quote_ty!(ctx.ext_cx(), __BindgenUnionField<$ty>)
}
} else if let Some(item) = field_ty.is_incomplete_array(ctx) {
result.saw_incomplete_array();

let inner = item.to_rust_ty(ctx);

if ctx.options().enable_cxx_namespaces {
quote_ty!(ctx.ext_cx(), root::__IncompleteArrayField<$inner>)
} else {
quote_ty!(ctx.ext_cx(), __IncompleteArrayField<$inner>)
}
} else {
ty
};
Expand Down Expand Up @@ -2333,6 +2355,67 @@ mod utils {
result.extend(old_items.into_iter());
}

pub fn prepend_incomplete_array_types(ctx: &BindgenContext,
result: &mut Vec<P<ast::Item>>) {
let prefix = ctx.trait_prefix();

let incomplete_array_decl = quote_item!(ctx.ext_cx(),
#[repr(C)]
pub struct __IncompleteArrayField<T>(
::$prefix::marker::PhantomData<T>);
)
.unwrap();

let incomplete_array_impl = quote_item!(&ctx.ext_cx(),
impl<T> __IncompleteArrayField<T> {
#[inline]
pub fn new() -> Self {
__IncompleteArrayField(::$prefix::marker::PhantomData)
}

#[inline]
pub unsafe fn as_ptr(&self) -> *const T {
::$prefix::mem::transmute(self)
}

#[inline]
pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
::$prefix::mem::transmute(self)
}

#[inline]
pub unsafe fn as_slice(&self, len: usize) -> &[T] {
::std::slice::from_raw_parts(self.as_ptr(), len)
}

#[inline]
pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
}
}
)
.unwrap();

let incomplete_array_debug_impl = quote_item!(ctx.ext_cx(),
impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
-> ::std::fmt::Result {
fmt.write_str("__IncompleteArrayField")
}
}
)
.unwrap();

let items = vec![
incomplete_array_decl,
incomplete_array_impl,
incomplete_array_debug_impl,
];

let old_items = mem::replace(result, items);
result.extend(old_items.into_iter());
}

pub fn prepend_complex_type(ctx: &BindgenContext,
result: &mut Vec<P<ast::Item>>) {
let complex_type = quote_item!(ctx.ext_cx(),
Expand Down
20 changes: 18 additions & 2 deletions src/ir/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@ impl Type {
}
}

/// Is this a incomplete array type?
pub fn is_incomplete_array(&self, ctx: &BindgenContext) -> Option<ItemId> {
match self.kind {
TypeKind::Array(item, len) => if len == 0 { Some(item) } else { None },
TypeKind::ResolvedTypeRef(inner) => ctx.resolve_type(inner).is_incomplete_array(ctx),
_ => None,
}
}

/// What is the layout of this type?
pub fn layout(&self, ctx: &BindgenContext) -> Option<Layout> {
use std::mem;
Expand Down Expand Up @@ -816,15 +825,22 @@ impl Type {
}
// XXX DependentSizedArray is wrong
CXType_VariableArray |
CXType_DependentSizedArray |
CXType_IncompleteArray => {
CXType_DependentSizedArray => {
let inner = Item::from_ty(ty.elem_type().as_ref().unwrap(),
location,
parent_id,
ctx)
.expect("Not able to resolve array element?");
TypeKind::Pointer(inner)
}
CXType_IncompleteArray => {
let inner = Item::from_ty(ty.elem_type().as_ref().unwrap(),
location,
parent_id,
ctx)
.expect("Not able to resolve array element?");
TypeKind::Array(inner, 0)
}
CXType_FunctionNoProto |
CXType_FunctionProto => {
let signature = try!(FunctionSig::from_ty(ty,
Expand Down
63 changes: 63 additions & 0 deletions tests/expectations/tests/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,33 @@
#![allow(non_snake_case)]


#[repr(C)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
impl <T> __IncompleteArrayField<T> {
#[inline]
pub fn new() -> Self {
__IncompleteArrayField(::std::marker::PhantomData)
}
#[inline]
pub unsafe fn as_ptr(&self) -> *const T { ::std::mem::transmute(self) }
#[inline]
pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
::std::mem::transmute(self)
}
#[inline]
pub unsafe fn as_slice(&self, len: usize) -> &[T] {
::std::slice::from_raw_parts(self.as_ptr(), len)
}
#[inline]
pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
}
}
impl <T> ::std::fmt::Debug for __IncompleteArrayField<T> {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
fmt.write_str("__IncompleteArrayField")
}
}
#[repr(C)]
pub struct __BindgenUnionField<T>(::std::marker::PhantomData<T>);
impl <T> __BindgenUnionField<T> {
Expand Down Expand Up @@ -39,6 +66,42 @@ fn bindgen_test_layout_C() {
assert_eq!(::std::mem::align_of::<C>() , 4usize);
}
#[repr(C)]
pub struct C_with_zero_length_array {
pub a: ::std::os::raw::c_int,
pub big_array: [::std::os::raw::c_char; 33usize],
pub zero_length_array: __IncompleteArrayField<::std::os::raw::c_char>,
}
#[test]
fn bindgen_test_layout_C_with_zero_length_array() {
assert_eq!(::std::mem::size_of::<C_with_zero_length_array>() , 40usize);
assert_eq!(::std::mem::align_of::<C_with_zero_length_array>() , 4usize);
}
#[repr(C)]
pub struct C_with_incomplete_array {
pub a: ::std::os::raw::c_int,
pub big_array: [::std::os::raw::c_char; 33usize],
pub incomplete_array: __IncompleteArrayField<::std::os::raw::c_char>,
}
#[test]
fn bindgen_test_layout_C_with_incomplete_array() {
assert_eq!(::std::mem::size_of::<C_with_incomplete_array>() , 40usize);
assert_eq!(::std::mem::align_of::<C_with_incomplete_array>() , 4usize);
}
#[repr(C)]
pub struct C_with_zero_length_array_and_incomplete_array {
pub a: ::std::os::raw::c_int,
pub big_array: [::std::os::raw::c_char; 33usize],
pub zero_length_array: __IncompleteArrayField<::std::os::raw::c_char>,
pub incomplete_array: __IncompleteArrayField<::std::os::raw::c_char>,
}
#[test]
fn bindgen_test_layout_C_with_zero_length_array_and_incomplete_array() {
assert_eq!(::std::mem::size_of::<C_with_zero_length_array_and_incomplete_array>()
, 40usize);
assert_eq!(::std::mem::align_of::<C_with_zero_length_array_and_incomplete_array>()
, 4usize);
}
#[repr(C)]
#[derive(Debug)]
pub struct WithDtor {
pub b: ::std::os::raw::c_int,
Expand Down
2 changes: 1 addition & 1 deletion tests/expectations/tests/var-tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub struct Baz {
}
extern "C" {
#[link_name = "_ZN3Baz3FOOE"]
pub static mut Baz_FOO: *const Bar;
pub static mut Baz_FOO: [Bar; 0usize];
}
#[test]
fn bindgen_test_layout_Baz() {
Expand Down
22 changes: 22 additions & 0 deletions tests/headers/class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@ class C {
char big_array[33];
};

class C_with_zero_length_array {
int a;
// More than rust limits (32)
char big_array[33];
char zero_length_array[0];
};

class C_with_incomplete_array {
int a;
// More than rust limits (32)
char big_array[33];
char incomplete_array[];
};

class C_with_zero_length_array_and_incomplete_array {
int a;
// More than rust limits (32)
char big_array[33];
char zero_length_array[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is legal C/C++? (the zero_length_array and incomplete_array fields would have the same address).

Could you remove the zero_length_array field from the test?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or better, test them in two different structs)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, some times, people use zero length array as a mark (another story should be solved), and use incomplete array as the last field

/* define a set of marker types that can be used to refer to set points in the
 * mbuf */
__extension__
typedef void    *MARKER[0];   /**< generic marker for a point in a structure */
__extension__
typedef uint8_t  MARKER8[0];  /**< generic marker with 1B alignment */
__extension__
typedef uint64_t MARKER64[0]; /**< marker that allows us to overwrite 8 bytes
                               * with a single assignment */

/**
 * The generic rte_mbuf, containing a packet mbuf.
 */
struct rte_mbuf {
	MARKER cacheline0;

	void *buf_addr;           /**< Virtual address of segment buffer. */
	phys_addr_t buf_physaddr; /**< Physical address of segment buffer. */

	uint16_t buf_len;         /**< Length of segment buffer. */

	/* next 6 bytes are initialised on RX descriptor rearm */
	MARKER8 rearm_data;
	uint16_t data_off;
struct cmdline_inst {
	/* f(parsed_struct, data) */
	void (*f)(void *, struct cmdline *, void *);
	void *data;
	const char *help_str;
	cmdline_parse_token_hdr_t *tokens[];
};

I have submitted another patch base on a helper class like __BindgenUnionField did

#[repr(C)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
impl <T> __IncompleteArrayField<T> {
    #[inline]
    pub fn new() -> Self {
        __IncompleteArrayField(::std::marker::PhantomData)
    }
    #[inline]
    pub unsafe fn as_slice(&self, len: usize) -> &[T] {
        ::std::slice::from_raw_parts(::std::mem::transmute(self), len)
    }
    #[inline]
    pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
        ::std::slice::from_raw_parts_mut(::std::mem::transmute(self), len)
    }
}
impl <T> ::std::fmt::Debug for __IncompleteArrayField<T> {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        fmt.write_str("__IncompleteArrayField")
    }
}
#[repr(C)]
pub struct C {
    pub a: ::std::os::raw::c_int,
    pub big_array: [::std::os::raw::c_char; 33usize],
    pub zero_length_array: __IncompleteArrayField<::std::os::raw::c_char>,
    pub incomplete_array: __IncompleteArrayField<::std::os::raw::c_char>,
}
#[test]
fn bindgen_test_layout_C() {
    assert_eq!(::std::mem::size_of::<C>() , 40usize);
    assert_eq!(::std::mem::align_of::<C>() , 4usize);
}

char incomplete_array[];
};

class WithDtor {
int b;

Expand Down