diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs index 53b6bdec..e97488d4 100644 --- a/src/bindgen/ir/generic_path.rs +++ b/src/bindgen/ir/generic_path.rs @@ -21,6 +21,7 @@ pub enum GenericParamType { pub struct GenericParam { name: Path, ty: GenericParamType, + default: Option, } impl GenericParam { @@ -28,20 +29,36 @@ impl GenericParam { GenericParam { name: Path::new(name), ty: GenericParamType::Type, + default: None, } } pub fn load(param: &syn::GenericParam) -> Result, String> { match *param { - syn::GenericParam::Type(syn::TypeParam { ref ident, .. }) => Ok(Some(GenericParam { - name: Path::new(ident.unraw().to_string()), - ty: GenericParamType::Type, - })), + syn::GenericParam::Type(syn::TypeParam { + ref ident, + ref default, + .. + }) => { + let default = match default.as_ref().map(Type::load).transpose()? { + None => None, + Some(None) => Err(format!("unsupported generic type default: {:?}", default))?, + Some(Some(ty)) => Some(GenericArgument::Type(ty)), + }; + Ok(Some(GenericParam { + name: Path::new(ident.unraw().to_string()), + ty: GenericParamType::Type, + default, + })) + } syn::GenericParam::Lifetime(_) => Ok(None), syn::GenericParam::Const(syn::ConstParam { - ref ident, ref ty, .. + ref ident, + ref ty, + ref default, + .. }) => match Type::load(ty)? { None => { // A type that evaporates, like PhantomData. @@ -50,6 +67,11 @@ impl GenericParam { Some(ty) => Ok(Some(GenericParam { name: Path::new(ident.unraw().to_string()), ty: GenericParamType::Const(ty), + default: default + .as_ref() + .map(ConstExpr::load) + .transpose()? + .map(GenericArgument::Const), })), }, } @@ -81,17 +103,32 @@ impl GenericParams { item_name: &str, arguments: &'out [GenericArgument], ) -> Vec<(&'out Path, &'out GenericArgument)> { - assert!(self.len() > 0, "{} is not generic", item_name); assert!( - self.len() == arguments.len(), + self.len() >= arguments.len(), "{} has {} params but is being instantiated with {} values", item_name, self.len(), arguments.len(), ); self.iter() - .map(|param| param.name()) - .zip(arguments.iter()) + .enumerate() + .map(|(i, param)| { + // Fall back to the GenericParam default if no GenericArgument is available. + let arg = arguments + .get(i) + .or(param.default.as_ref()) + .unwrap_or_else(|| { + panic!( + "{} with {} params is being instantiated with only {} values, \ + and param {} lacks a default value", + item_name, + self.len(), + arguments.len(), + i + ) + }); + (param.name(), arg) + }) .collect() } @@ -111,13 +148,18 @@ impl GenericParams { match item.ty { GenericParamType::Type => { write!(out, "typename {}", item.name); - if with_default { + if let Some(GenericArgument::Type(ref ty)) = item.default { + write!(out, " = "); + cdecl::write_type(language_backend, out, ty, config); + } else if with_default { write!(out, " = void"); } } GenericParamType::Const(ref ty) => { cdecl::write_field(language_backend, out, ty, item.name.name(), config); - if with_default { + if let Some(GenericArgument::Const(ref expr)) = item.default { + write!(out, " = {}", expr.as_str()); + } else if with_default { write!(out, " = 0"); } } diff --git a/tests/expectations/generic_defaults.c b/tests/expectations/generic_defaults.c new file mode 100644 index 00000000..72fbb417 --- /dev/null +++ b/tests/expectations/generic_defaults.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +typedef int16_t Foo_i16; + +typedef int32_t Foo_i32; + +typedef struct { + Foo_i32 f; + uint32_t p; +} Bar_i32__u32; + +typedef int64_t Foo_i64; + +typedef Foo_i64 Baz_i64; + +void foo_root(Foo_i16 f, Bar_i32__u32 b, Baz_i64 z); diff --git a/tests/expectations/generic_defaults.compat.c b/tests/expectations/generic_defaults.compat.c new file mode 100644 index 00000000..d08662a3 --- /dev/null +++ b/tests/expectations/generic_defaults.compat.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +typedef int16_t Foo_i16; + +typedef int32_t Foo_i32; + +typedef struct { + Foo_i32 f; + uint32_t p; +} Bar_i32__u32; + +typedef int64_t Foo_i64; + +typedef Foo_i64 Baz_i64; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void foo_root(Foo_i16 f, Bar_i32__u32 b, Baz_i64 z); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/generic_defaults.cpp b/tests/expectations/generic_defaults.cpp new file mode 100644 index 00000000..afc28fb8 --- /dev/null +++ b/tests/expectations/generic_defaults.cpp @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include + +template +using Foo = T; + +template +struct Bar { + Foo f; + P p; +}; + +template +using Baz = Foo; + +extern "C" { + +void foo_root(Foo f, Bar b, Baz z); + +} // extern "C" diff --git a/tests/expectations/generic_defaults.pyx b/tests/expectations/generic_defaults.pyx new file mode 100644 index 00000000..0c17c1b6 --- /dev/null +++ b/tests/expectations/generic_defaults.pyx @@ -0,0 +1,21 @@ +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 int16_t Foo_i16; + + ctypedef int32_t Foo_i32; + + ctypedef struct Bar_i32__u32: + Foo_i32 f; + uint32_t p; + + ctypedef int64_t Foo_i64; + + ctypedef Foo_i64 Baz_i64; + + void foo_root(Foo_i16 f, Bar_i32__u32 b, Baz_i64 z); diff --git a/tests/expectations/generic_defaults_both.c b/tests/expectations/generic_defaults_both.c new file mode 100644 index 00000000..1e7e7c5f --- /dev/null +++ b/tests/expectations/generic_defaults_both.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +typedef int16_t Foo_i16; + +typedef int32_t Foo_i32; + +typedef struct Bar_i32__u32 { + Foo_i32 f; + uint32_t p; +} Bar_i32__u32; + +typedef int64_t Foo_i64; + +typedef Foo_i64 Baz_i64; + +void foo_root(Foo_i16 f, struct Bar_i32__u32 b, Baz_i64 z); diff --git a/tests/expectations/generic_defaults_both.compat.c b/tests/expectations/generic_defaults_both.compat.c new file mode 100644 index 00000000..e7aa9d44 --- /dev/null +++ b/tests/expectations/generic_defaults_both.compat.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +typedef int16_t Foo_i16; + +typedef int32_t Foo_i32; + +typedef struct Bar_i32__u32 { + Foo_i32 f; + uint32_t p; +} Bar_i32__u32; + +typedef int64_t Foo_i64; + +typedef Foo_i64 Baz_i64; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void foo_root(Foo_i16 f, struct Bar_i32__u32 b, Baz_i64 z); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/generic_defaults_tag.c b/tests/expectations/generic_defaults_tag.c new file mode 100644 index 00000000..b3fa4988 --- /dev/null +++ b/tests/expectations/generic_defaults_tag.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +typedef int16_t Foo_i16; + +typedef int32_t Foo_i32; + +struct Bar_i32__u32 { + Foo_i32 f; + uint32_t p; +}; + +typedef int64_t Foo_i64; + +typedef Foo_i64 Baz_i64; + +void foo_root(Foo_i16 f, struct Bar_i32__u32 b, Baz_i64 z); diff --git a/tests/expectations/generic_defaults_tag.compat.c b/tests/expectations/generic_defaults_tag.compat.c new file mode 100644 index 00000000..9fcc7e3c --- /dev/null +++ b/tests/expectations/generic_defaults_tag.compat.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +typedef int16_t Foo_i16; + +typedef int32_t Foo_i32; + +struct Bar_i32__u32 { + Foo_i32 f; + uint32_t p; +}; + +typedef int64_t Foo_i64; + +typedef Foo_i64 Baz_i64; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void foo_root(Foo_i16 f, struct Bar_i32__u32 b, Baz_i64 z); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/generic_defaults_tag.pyx b/tests/expectations/generic_defaults_tag.pyx new file mode 100644 index 00000000..9b880749 --- /dev/null +++ b/tests/expectations/generic_defaults_tag.pyx @@ -0,0 +1,21 @@ +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 int16_t Foo_i16; + + ctypedef int32_t Foo_i32; + + cdef struct Bar_i32__u32: + Foo_i32 f; + uint32_t p; + + ctypedef int64_t Foo_i64; + + ctypedef Foo_i64 Baz_i64; + + void foo_root(Foo_i16 f, Bar_i32__u32 b, Baz_i64 z); diff --git a/tests/rust/generic_defaults.rs b/tests/rust/generic_defaults.rs new file mode 100644 index 00000000..7b7a8357 --- /dev/null +++ b/tests/rust/generic_defaults.rs @@ -0,0 +1,16 @@ +#[repr(transparent)] +pub struct Foo { + field: T, + _phantom: std::marker::PhantomData

, +} + +#[repr(C)] +pub struct Bar { + f: Foo, + p: P, +} + +pub type Baz = Foo; + +#[no_mangle] +pub extern "C" fn foo_root(f: Foo, b: Bar, z: Baz) {}