Skip to content

impl_debug(true) and default_alias_style(NewTypeDeref) broken for C unions with typedef aliases #3268

@ivmaykov

Description

@ivmaykov

When using impl_debug(true) together with default_alias_style(bindgen::AliasVariation::NewTypeDeref) and generating bindings to a C union which has a typedef alias, the generated Rust code has a Debug implementation for the C union but does NOT have a Debug implementation for the newtype alias of the C union. If there are any structs defined which contain the aliased type, then the generated bindings won't even compile! I know it's a weird case, but this is actually preventing me from using default_alias_style(bindgen::AliasVariation::NewTypeDeref) in a real project.

Very simple example:

// foo.h
union Union {
  uint8_t bytes[4];
  uint32_t word;
};

typedef union Union UnionAlias;

struct StructContainingUnionAlias {
  UnionAlias ua;
};
// build.rs
use std::env;
use std::path::PathBuf;

fn main() {
    let crate_dir = &env::var("CARGO_MANIFEST_DIR").unwrap();
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    let bindings = bindgen::Builder::default()
        .derive_default(true)
        .impl_debug(true)
        .default_alias_style(bindgen::AliasVariation::NewTypeDeref)
        .header(format!("{crate_dir}/src/foo.h"))
        .allowlist_file(".*foo.h.*")
        .generate()
        .expect("Unable to generate bindings");

    bindings
        .write_to_file(out_path.join("foo_bindings.rs"))
        .expect("Couldn't write bindings");
}
// foo_bindings.rs, generated by bindgen
/* automatically generated by rust-bindgen 0.72.0 */

#[repr(C)]
#[derive(Copy, Clone)]
pub union Union {
    pub bytes: [u8; 4usize],
    pub word: u32,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
    ["Size of Union"][::std::mem::size_of::<Union>() - 4usize];
    ["Alignment of Union"][::std::mem::align_of::<Union>() - 4usize];
    ["Offset of field: Union::bytes"][::std::mem::offset_of!(Union, bytes) - 0usize];
    ["Offset of field: Union::word"][::std::mem::offset_of!(Union, word) - 0usize];
};
impl Default for Union {
    fn default() -> Self {
        let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
        unsafe {
            ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
            s.assume_init()
        }
    }
}
impl ::std::fmt::Debug for Union {
    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
        write!(f, "Union {{ union }}")
    }
}
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct UnionAlias(pub Union);
impl ::std::ops::Deref for UnionAlias {
    type Target = Union;
    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl ::std::ops::DerefMut for UnionAlias {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct StructContainingUnionAlias {
    pub ua: UnionAlias,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
    ["Size of StructContainingUnionAlias"]
        [::std::mem::size_of::<StructContainingUnionAlias>() - 4usize];
    ["Alignment of StructContainingUnionAlias"]
        [::std::mem::align_of::<StructContainingUnionAlias>() - 4usize];
    ["Offset of field: StructContainingUnionAlias::ua"]
        [::std::mem::offset_of!(StructContainingUnionAlias, ua) - 0usize];
};
impl Default for StructContainingUnionAlias {
    fn default() -> Self {
        let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
        unsafe {
            ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
            s.assume_init()
        }
    }
}
impl ::std::fmt::Debug for StructContainingUnionAlias {
    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
        write!(f, "StructContainingUnionAlias {{ ua: {:?} }}", self.ua)
    }
}

The generated file does not compile, since it's trying to derive Debug for StructContainingUnionAlias, but the field self.ua does not derive or implement Debug. Compilation error is:

$ cargo build
   Compiling bindgen-bug v0.1.0 (/Users/ivmaykov/Development/experiments/bindgen-bug)
error[E0277]: `UnionAlias` doesn't implement `Debug`
  --> /Users/ivmaykov/Development/experiments/bindgen-bug/target/debug/build/bindgen-bug-d43f6c4a8531d2b1/out/foo_bindings.rs:71:64
   |
71 |         write!(f, "StructContainingUnionAlias {{ ua: {:?} }}", self.ua)
   |                                                      ----      ^^^^^^^ `UnionAlias` cannot be formatted using `{:?}` because it doesn't implement `Debug`
   |                                                      |
   |                                                      required by this formatting parameter
   |
   = help: the trait `Debug` is not implemented for `UnionAlias`
   = note: add `#[derive(Debug)]` to `UnionAlias` or manually `impl Debug for UnionAlias`
   = note: this error originates in the macro `$crate::format_args` which comes from the expansion of the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `UnionAlias` with `#[derive(Debug)]`
   |
32 + #[derive(Debug)]
33 | pub struct UnionAlias(pub Union);
   |

For more information about this error, try `rustc --explain E0277`.
error: could not compile `bindgen-bug` (lib) due to 1 previous error

I'm using bindgen 0.72.0 and rustc 1.90.0-nightly on MacOS.

See https://github.com/ivmaykov/bindgen-bug for a minimal project which reproduces the issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions