diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs index f0b9b8a5..accb3dbf 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -526,8 +526,16 @@ impl Type { _ => return None, }; - if path.generics().is_empty() { - return None; + // Special-case: when a ZST like `()` is used as the generic parameter of `NonNull`, it + // evaporates during parsing and `NonNull` ends up with zero generics. In that case, treat + // it as `void*` so aliases like `type Id = NonNull<()>;` lower correctly. + if path.generics().is_empty() && path.name() == "NonNull" { + return Some(Type::Ptr { + ty: Box::new(Type::Primitive(PrimitiveType::Void)), + is_const: false, + is_nullable: false, + is_ref: false, + }); } if path.generics().len() != 1 { diff --git a/tests/expectations-symbols/nonnull_unit.c.sym b/tests/expectations-symbols/nonnull_unit.c.sym new file mode 100644 index 00000000..091d1b12 --- /dev/null +++ b/tests/expectations-symbols/nonnull_unit.c.sym @@ -0,0 +1,4 @@ +{ +takes_id; +takes_unit_ptr; +}; \ No newline at end of file diff --git a/tests/expectations/nonnull_unit.c b/tests/expectations/nonnull_unit.c new file mode 100644 index 00000000..dd99c107 --- /dev/null +++ b/tests/expectations/nonnull_unit.c @@ -0,0 +1,10 @@ +#include +#include +#include +#include + +typedef void *MyId; + +void takes_id(MyId id); + +void takes_unit_ptr(void *id); diff --git a/tests/expectations/nonnull_unit.compat.c b/tests/expectations/nonnull_unit.compat.c new file mode 100644 index 00000000..60e5b660 --- /dev/null +++ b/tests/expectations/nonnull_unit.compat.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +typedef void *MyId; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void takes_id(MyId id); + +void takes_unit_ptr(void *id); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/nonnull_unit.cpp b/tests/expectations/nonnull_unit.cpp new file mode 100644 index 00000000..74f6fb68 --- /dev/null +++ b/tests/expectations/nonnull_unit.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include + +using MyId = void*; + +extern "C" { + +void takes_id(MyId id); + +void takes_unit_ptr(void *id); + +} // extern "C" diff --git a/tests/expectations/nonnull_unit.pyx b/tests/expectations/nonnull_unit.pyx new file mode 100644 index 00000000..5a15872d --- /dev/null +++ b/tests/expectations/nonnull_unit.pyx @@ -0,0 +1,13 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + ctypedef void *MyId; + + void takes_id(MyId id); + + void takes_unit_ptr(void *id); diff --git a/tests/rust/nonnull_unit.rs b/tests/rust/nonnull_unit.rs new file mode 100644 index 00000000..3ddf8f20 --- /dev/null +++ b/tests/rust/nonnull_unit.rs @@ -0,0 +1,16 @@ +use core::ptr::NonNull; + +// Reproducer: generic argument is a ZST `()` which evaporates in parsing. +pub type MyId = NonNull<()>; + +#[no_mangle] +pub extern "C" fn takes_id(id: MyId) { + let _ = id; +} + +#[no_mangle] +pub extern "C" fn takes_unit_ptr(id: *mut ()) { + let _ = id; +} + +