Rust-bindgen: Generate sensible code for long double / stuff that has more than 64-bit alignment.

Created on 1 Mar 2017  路  20Comments  路  Source: rust-lang/rust-bindgen

Input C/C++ Header

typedef struct {
    long double ld;
} max_align;

Bindgen Invokation

$ bindgen input.h > lib.rs

This is with both bindgen 0.22 and HEAD, on both of these:

  • Linux stewie 4.4.0-57-generic #78-Ubuntu SMP Fri Dec 9 23:50:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
  • Darwin sublevel 16.4.0 Darwin Kernel Version 16.4.0: Thu Dec 22 22:53:21 PST 2016; root:xnu-3789.41.3~3/RELEASE_X86_64 x86_64

Actual Results

/* automatically generated by rust-bindgen */

#[repr(C)]
#[derive(Debug, Copy)]
pub struct max_align {
    pub ld: f64,
}
#[test]
fn bindgen_test_layout_max_align() {
    assert_eq!(::std::mem::size_of::<max_align>() , 16usize , concat ! (
               "Size of: " , stringify ! ( max_align ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const max_align ) ) . ld as * const _ as usize
                } , 0usize , concat ! (
                "Alignment of field: " , stringify ! ( max_align ) , "::" ,
                stringify ! ( ld ) ));
}
impl Clone for max_align {
    fn clone(&self) -> Self { *self }
}

The generated test is that sizeof(max_align) == 16. This may be true for the original C struct, but it's not true for the generated Rust struct.

Expected Results

I鈥檓 not sure how the struct should be translated, since Rust doesn鈥檛 have f80 or whatever, but even without a sane translation, the generated test should pass, right?

Does this have something to do with #468? That shows the same code (from <stddef.h>) that led me to discover this problem. The translation that it shows includes padding that I'm not seeing.

RUST_LOG=bindgen Output

INFO:bindgen: Clang Version: clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
WARN:bindgen: Using clang (3, 8), expected (3, 9)
DEBUG:bindgen::ir::context: BindgenContext::add_item(Item { id: ItemId(0), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Module(Module { name: Some("root"), kind: Normal, children_ids: [] }) }, declaration: None, loc: None
DEBUG:bindgen::ir::context: builtin_or_resolved_ty: Type(max_align, kind: Record, decl: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align")), canon: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align"))), Some(Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align"))), None
DEBUG:bindgen::ir::context: Not resolved, maybe builtin?
DEBUG:bindgen::ir::context: builtin_or_resolved_ty: Type(max_align, kind: Record, decl: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align")), canon: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align"))), Some(Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align"))), None
DEBUG:bindgen::ir::context: Not resolved, maybe builtin?
DEBUG:bindgen::ir::ty: from_clang_ty: ItemId(1), ty: Type(max_align, kind: Record, decl: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align")), canon: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align"))), loc: Some(Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align")))
DEBUG:bindgen::ir::ty: currently_parsed_types: [PartialType { decl: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align")), id: ItemId(1) }]
DEBUG:bindgen::ir::comp: CompInfo::from_ty(Struct, Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align")))
DEBUG:bindgen::ir::item: from_ty_or_ref_with_id: ItemId(2) Type(long double, kind: LongDouble, decl: Cursor( kind: NoDeclFound, loc: builtin definitions, usr: None), canon: Cursor( kind: NoDeclFound, loc: builtin definitions, usr: None)), Some(Cursor(ld kind: FieldDecl, loc: ffi.h:2:17, usr: Some("c:@SA@max_align@FI@ld"))), Some(ItemId(1))
DEBUG:bindgen::ir::context: builtin_or_resolved_ty: Type(long double, kind: LongDouble, decl: Cursor( kind: NoDeclFound, loc: builtin definitions, usr: None), canon: Cursor( kind: NoDeclFound, loc: builtin definitions, usr: None)), Some(Cursor(ld kind: FieldDecl, loc: ffi.h:2:17, usr: Some("c:@SA@max_align@FI@ld"))), Some(ItemId(1))
DEBUG:bindgen::ir::context: Not resolved, maybe builtin?
DEBUG:bindgen::ir::context: add_builtin_item: item = Item { id: ItemId(3), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Type(Type { name: Some("long double"), layout: Some(Layout { size: 16, align: 16, packed: false }), kind: Float(LongDouble), is_const: false }) }
DEBUG:bindgen::ir::item: ItemId(3) already resolved: Some(Cursor(ld kind: FieldDecl, loc: ffi.h:2:17, usr: Some("c:@SA@max_align@FI@ld")))
DEBUG:bindgen::ir::context: BindgenContext::add_item(Item { id: ItemId(1), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Type(Type { name: Some("max_align"), layout: Some(Layout { size: 16, align: 16, packed: false }), kind: Comp(CompInfo { kind: Struct, fields: [Field { name: Some("ld"), ty: ItemId(3), comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, bitfield: None, mutable: false, offset: Some(0) }], template_args: [], methods: [], constructors: [], base_members: [], ref_template: None, inner_types: [], inner_vars: [], has_vtable: false, has_destructor: false, has_nonempty_base: false, has_non_type_template_params: false, packed: false, found_unknown_attr: false, detect_derive_debug_cycle: Cell { value: false }, detect_derive_default_cycle: Cell { value: false }, detect_has_destructor_cycle: Cell { value: false }, is_forward_declaration: false }), is_const: false }) }, declaration: Some(Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align"))), loc: Some(Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align")))
DEBUG:bindgen::ir::context: builtin_or_resolved_ty: Type(max_align, kind: Typedef, decl: Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align")), canon: Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align"))), Some(Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align"))), None
DEBUG:bindgen::ir::context: Not resolved, maybe builtin?
DEBUG:bindgen::ir::context: builtin_or_resolved_ty: Type(max_align, kind: Typedef, decl: Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align")), canon: Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align"))), Some(Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align"))), None
DEBUG:bindgen::ir::context: Not resolved, maybe builtin?
DEBUG:bindgen::ir::ty: from_clang_ty: ItemId(4), ty: Type(max_align, kind: Typedef, decl: Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align")), canon: Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align"))), loc: Some(Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align")))
DEBUG:bindgen::ir::ty: currently_parsed_types: [PartialType { decl: Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align")), id: ItemId(4) }]
DEBUG:bindgen::ir::item: from_ty_or_ref_with_id: ItemId(5) Type(struct max_align, kind: Unexposed, decl: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align")), canon: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align"))), Some(Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align"))), None
DEBUG:bindgen::ir::context: builtin_or_resolved_ty: Type(struct max_align, kind: Unexposed, decl: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align")), canon: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align"))), Some(Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align"))), None
DEBUG:bindgen::ir::context: Already resolved ty ItemId(1), CanonicalTypeDeclaration(Type(struct max_align, kind: Unexposed, decl: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align")), canon: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align"))), Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align"))), Type(struct max_align, kind: Unexposed, decl: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align")), canon: Cursor( kind: StructDecl, loc: ffi.h:1:9, usr: Some("c:@SA@max_align"))) Some(Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align")))
DEBUG:bindgen::ir::context: add_builtin_item: item = Item { id: ItemId(5), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Type(Type { name: Some("struct max_align"), layout: Some(Layout { size: 16, align: 16, packed: false }), kind: ResolvedTypeRef(ItemId(1)), is_const: false }) }
DEBUG:bindgen::ir::item: ItemId(5) already resolved: Some(Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align")))
DEBUG:bindgen::ir::context: BindgenContext::add_item(Item { id: ItemId(4), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Type(Type { name: Some("max_align"), layout: Some(Layout { size: 16, align: 16, packed: false }), kind: Alias(ItemId(5)), is_const: false }) }, declaration: Some(Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align"))), loc: Some(Cursor(max_align kind: TypedefDecl, loc: ffi.h:3:3, usr: Some("c:ffi.h@T@max_align")))
DEBUG:bindgen::ir::context: No replacements to process
DEBUG:bindgen::codegen: codegen: BindgenOptions { hidden_types: RegexSet { items: [], set: Some(RegexSet([])) }, opaque_types: RegexSet { items: [], set: Some(RegexSet([])) }, whitelisted_types: RegexSet { items: [], set: Some(RegexSet([])) }, whitelisted_functions: RegexSet { items: [], set: Some(RegexSet([])) }, whitelisted_vars: RegexSet { items: [], set: Some(RegexSet([])) }, bitfield_enums: RegexSet { items: [], set: Some(RegexSet([])) }, constified_enums: RegexSet { items: [], set: Some(RegexSet([])) }, builtins: false, links: [], emit_ast: false, emit_ir: false, emit_ir_graphviz: None, enable_cxx_namespaces: false, disable_name_namespacing: false, derive_debug: true, derive_default: false, unstable_rust: true, use_core: false, ctypes_prefix: None, namespaced_constants: true, msvc_mangling: false, convert_floats: true, raw_lines: [], clang_args: ["-isystem", "/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0", "-isystem", "/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/x86_64-linux-gnu/c++/5.4.0", "-isystem", "/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/backward", "-isystem", "/usr/local/include", "-isystem", "/usr/lib/llvm-3.8/bin/../lib/clang/3.8.0/include", "-isystem", "/usr/include/x86_64-linux-gnu", "-isystem", "/usr/include", "ffi.h"], input_header: Some("ffi.h"), dummy_uses: None, type_chooser: None, codegen_config: CodegenConfig { functions: true, types: true, vars: true, methods: true, constructors: true }, conservative_inline_namespaces: false, generate_comments: true, whitelist_recursively: true, objc_extern_crate: false, enable_mangling: true, prepend_enum_name: true }
DEBUG:bindgen::codegen: <Item as CodeGenerator>::codegen: self = Item { id: ItemId(0), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Module(Module { name: Some("root"), kind: Normal, children_ids: [ItemId(1), ItemId(4)] }) }
DEBUG:bindgen::codegen: <Module as CodeGenerator>::codegen: item = Item { id: ItemId(0), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Module(Module { name: Some("root"), kind: Normal, children_ids: [ItemId(1), ItemId(4)] }) }
DEBUG:bindgen::codegen: <Item as CodeGenerator>::codegen: self = Item { id: ItemId(1), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Type(Type { name: Some("max_align"), layout: Some(Layout { size: 16, align: 16, packed: false }), kind: Comp(CompInfo { kind: Struct, fields: [Field { name: Some("ld"), ty: ItemId(3), comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, bitfield: None, mutable: false, offset: Some(0) }], template_args: [], methods: [], constructors: [], base_members: [], ref_template: None, inner_types: [], inner_vars: [], has_vtable: false, has_destructor: false, has_nonempty_base: false, has_non_type_template_params: false, packed: false, found_unknown_attr: false, detect_derive_debug_cycle: Cell { value: false }, detect_derive_default_cycle: Cell { value: false }, detect_has_destructor_cycle: Cell { value: false }, is_forward_declaration: false }), is_const: false }) }
DEBUG:bindgen::codegen: <Type as CodeGenerator>::codegen: item = Item { id: ItemId(1), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Type(Type { name: Some("max_align"), layout: Some(Layout { size: 16, align: 16, packed: false }), kind: Comp(CompInfo { kind: Struct, fields: [Field { name: Some("ld"), ty: ItemId(3), comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, bitfield: None, mutable: false, offset: Some(0) }], template_args: [], methods: [], constructors: [], base_members: [], ref_template: None, inner_types: [], inner_vars: [], has_vtable: false, has_destructor: false, has_nonempty_base: false, has_non_type_template_params: false, packed: false, found_unknown_attr: false, detect_derive_debug_cycle: Cell { value: false }, detect_derive_default_cycle: Cell { value: false }, detect_has_destructor_cycle: Cell { value: false }, is_forward_declaration: false }), is_const: false }) }
DEBUG:bindgen::codegen: <CompInfo as CodeGenerator>::codegen: item = Item { id: ItemId(1), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Type(Type { name: Some("max_align"), layout: Some(Layout { size: 16, align: 16, packed: false }), kind: Comp(CompInfo { kind: Struct, fields: [Field { name: Some("ld"), ty: ItemId(3), comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, bitfield: None, mutable: false, offset: Some(0) }], template_args: [], methods: [], constructors: [], base_members: [], ref_template: None, inner_types: [], inner_vars: [], has_vtable: false, has_destructor: false, has_nonempty_base: false, has_non_type_template_params: false, packed: false, found_unknown_attr: false, detect_derive_debug_cycle: Cell { value: false }, detect_derive_default_cycle: Cell { value: false }, detect_has_destructor_cycle: Cell { value: false }, is_forward_declaration: false }), is_const: false }) }
DEBUG:bindgen::codegen::struct_layout: Offset: <padding>: 0 -> 0
DEBUG:bindgen::codegen::struct_layout: align field ld to 0/0 with 0 padding bytes Layout { size: 16, align: 16, packed: false }
DEBUG:bindgen::codegen::struct_layout: Offset: ld: 0 -> 16
DEBUG:bindgen::codegen: <Item as CodeGenerator>::codegen: self = Item { id: ItemId(4), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Type(Type { name: Some("max_align"), layout: Some(Layout { size: 16, align: 16, packed: false }), kind: Alias(ItemId(5)), is_const: false }) }
DEBUG:bindgen::codegen: <Type as CodeGenerator>::codegen: item = Item { id: ItemId(4), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None, constify_enum_variant: false }, parent_id: ItemId(0), kind: Type(Type { name: Some("max_align"), layout: Some(Layout { size: 16, align: 16, packed: false }), kind: Alias(ItemId(5)), is_const: false }) }
I-failing-layout-test

Most helpful comment

#[repr(align(...))] has been available in nightly for a while

All 20 comments

Yeah, this is known. We don't have any way of generating larger floats in rust though, so I don't know what could we do here :(. The test seems to be doing its job.

Does this have something to do with #468? That shows the same code (from ) that led me to discover this problem. The translation that it shows includes padding that I'm not seeing.

The padding in #468 was there, though incorrectly (and the code that generated the padding caused a few other bugs). I cleaned that up in #512.

We could generate [u64; 2] or something similar for long double if we detect it's more than 64-bit, if that works for you.

I suppose the test is doing its job, yes :) But because Clang鈥檚 <stddef.h> has a struct with a long double, it makes any header that includes <stddef.h> fail the generated tests.

I think that [u64; 2] would be a more appropriate translation of long double, or even better, [u8; sizeof(long double)]. Though for my purposes, being able to suppress the tests is just as good.

Note that you should be able to just do --blacklist-type max_align_t (or the equivalent with the builder API) to do that.

Oh, that should work. Thanks!

To clarify, @emilio is referring to bindgen::Builder::hide_type.

This currently prevents anything that does include <stddef.h> from building.

@lukaslueg see the workarounds above: https://github.com/servo/rust-bindgen/issues/550#issuecomment-289631540 and https://github.com/servo/rust-bindgen/issues/550#issuecomment-283438998

Rust has no way to set these alignments, so our hands are tied for the moment.

Any workaround? I currently have following output:

PS C:\Users\SOLO\Documents\Github\ocv\vc15\install> bindgen opencv/include/opencv2/core/core_c.h -o core.rs -- "-Iopencv
/include" -target stable-x86_64-pc-windows-gnu --blacklist-type max_align_t
error: unsupported option '--blacklist-type'
warning: max_align_t: 'linker' input unused [-Wunused-command-line-argument]
opencv/include\opencv2/core/hal/interface.h:18:10: fatal error: 'stdbool.h' file not found
error: unsupported option '--blacklist-type', err: true
warning: max_align_t: 'linker' input unused [-Wunused-command-line-argument], err: false
opencv/include\opencv2/core/hal/interface.h:18:10: fatal error: 'stdbool.h' file not found, err: true
thread 'main' panicked at 'Unable to generate bindings: ()', src\libcore\result.rs:916:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.


PS C:\Users\SOLO\Documents\Github\ocv\vc15\install> bindgen opencv/include/opencv2/core/core_c.h -o core.rs -- "-Iopencv
/include" -target stable-x86_64-pc-windows-gnu
opencv/include\opencv2/core/types_c.h:929:13: warning: implicit declaration of function 'cvRound' is invalid in C99 [-Wimplicit-function-declaration], err: false

PS C:\Users\SOLO\Documents\Github\ocv\vc15\install>

I.E. when I blacklist type it just doesn't compile.

@Pzixel That error has nothing to do with this bug, that looks like it's failing to find <stdbool.h>.

You're not passing the --blacklist-type flag to bindgen, but to clang, which probably accounts for most of the error.

@emilio it fails to find stdbool only when I blacklist some type? Sounds weird. Yes, I see that it cannot find it, but the only difference in two successful and failed calls are in additional --blacklist-type.

However, I just compiled it without blacklisting anything and adding additional _padding: u64 field as shown in https://github.com/rust-lang-nursery/rust-bindgen/pull/468.

#[repr(align(...))] has been available in nightly for a while

Just got this error myself, here's a case for you:

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct max_align_t {
    pub __clang_max_align_nonce1: ::std::os::raw::c_longlong,
    pub __bindgen_padding_0: u64,
    pub __clang_max_align_nonce2: f64,
}
#[test]
fn bindgen_test_layout_max_align_t() {
    assert_eq!(
        ::std::mem::size_of::<max_align_t>(),
        32usize,
        concat!("Size of: ", stringify!(max_align_t))
    );
    assert_eq!(
        unsafe {
            &(*(::std::ptr::null::<max_align_t>())).__clang_max_align_nonce1 as *const _ as usize
        },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(max_align_t),
            "::",
            stringify!(__clang_max_align_nonce1)
        )
    );
    assert_eq!(
        unsafe {
            &(*(::std::ptr::null::<max_align_t>())).__clang_max_align_nonce2 as *const _ as usize
        },
        16usize,
        concat!(
            "Offset of field: ",
            stringify!(max_align_t),
            "::",
            stringify!(__clang_max_align_nonce2)
        )
    );
}
test bindgen_test_layout_max_align_t ... FAILED

failures:

---- bindgen_test_layout_max_align_t stdout ----
thread 'bindgen_test_layout_max_align_t' panicked at 'assertion failed: `(left == right)`
  left: `24`,
 right: `32`: Size of: max_align_t', /home/nico/Projects/itadinanta/ofx-rs/target/debug/build/ofx_sys-0b6feeb523d786fb/out/bindings.rs:1181:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
   1: std::sys_common::backtrace::print
             at libstd/sys_common/backtrace.rs:71
             at libstd/sys_common/backtrace.rs:59
   2: std::panicking::default_hook::{{closure}}
             at libstd/panicking.rs:211
   3: std::panicking::default_hook
             at libstd/panicking.rs:221
   4: std::panicking::rust_panic_with_hook
             at libstd/panicking.rs:476
   5: std::panicking::continue_panic_fmt
             at libstd/panicking.rs:390
   6: std::panicking::begin_panic_fmt
             at libstd/panicking.rs:345
   7: ofx_sys::bindgen_test_layout_max_align_t
             at /home/nico/Projects/itadinanta/ofx-rs/target/debug/build/ofx_sys-0b6feeb523d786fb/out/bindings.rs:1181
   8: ofx_sys::bindgen_test_layout_max_align_t::{{closure}}
             at /home/nico/Projects/itadinanta/ofx-rs/target/debug/build/ofx_sys-0b6feeb523d786fb/out/bindings.rs:1180
   9: core::ops::function::FnOnce::call_once
             at libcore/ops/function.rs:238
  10: <F as alloc::boxed::FnBox<A>>::call_box
             at libtest/lib.rs:1468
             at libcore/ops/function.rs:238
             at liballoc/boxed.rs:672
  11: __rust_maybe_catch_panic
             at libpanic_unwind/lib.rs:102


failures:
    bindgen_test_layout_max_align_t

test result: FAILED. 40 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

I've temporarily resolved blacklisting max_align_t as suggested.

This was properly fixed recently with .rust_target(<a version that supports repr(align)>), like 1.26.

See #1391 / #1394 / etc.

@emilio This issue still isn't fixed as of bindgen 0.42.2. Those patches only deal with 128 bit ints, but not 80 bit floats. In fact, output is completely unchanged from the original post. Specifically, the struct:

typedef struct {
    long double ld;
} max_align;

Is converted to:

#[repr(C)]
#[derive(Debug, Copy)]
pub struct max_align {
    pub ld: f64,
}

When it instead should be something like:

#[repr(C, align(16))]
#[derive(Debug, Copy)]
pub struct f80([u8; 10]);

#[repr(C)]
#[derive(Debug, Copy)]
pub struct max_align {
    pub ld: f80,
}

In master:

$ cat > t.h                                                                                                                                                                                
typedef struct {
    long double ld;
} max_align;
$ ./target/debug/bindgen --no-layout-tests t.h
/* automatically generated by rust-bindgen */

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct max_align {
    pub ld: f64,
}
$ ./target/debug/bindgen --no-layout-tests --rust-target 1.26 t.h
/* automatically generated by rust-bindgen */

#[repr(C)]
#[repr(align(16))]
#[derive(Debug, Copy, Clone)]
pub struct max_align {
    pub ld: u128,
}

(--no-layout-tests has been added just to avoid clobbering the output, but in master the layout tests should in fact pass).

Note the --rust-target 1.26. That's needed to tell bindgen that it can use repr(align) and u128.

(If for some reason that output can be improved or what not, let's continue that discussion in another issue)

@emilio maybe rust-bindgen should start warning / erroring on long double if --rust-target 1.26 is not used, the repr(C) struct using f64 will introduce undefined behavior when used in C FFI. Users should either not generate bindings for long double types for older rust targets (e.g. by ignoring them), or use a newer rust target.

Yeah, I agree. FWIW tests should fail when that happens, so this already kind-of happens.

But should be trivial to add a warning to the place where we generate the long double, want to submit a patch?

Same problem with bindgen --rust-target "1.21"
Maybe not generating max_align_t when it's not possible is the better way?

Maybe not generating max_align_t when it's not possible is the better way?

Seems very special-casey (if it's just about max_align_t and not long-double in general), and this relies on max_align_t not actually being used for anything else.

Given it produces test failures and that default rust target is newer now iirc, I don't personally think it's worth it, but patch welcome of course :)

Was this page helpful?
0 / 5 - 0 ratings