Skip to content
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

Support generics with defaulted args #959

Merged
merged 1 commit into from
May 26, 2024
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
64 changes: 53 additions & 11 deletions src/bindgen/ir/generic_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,44 @@ pub enum GenericParamType {
pub struct GenericParam {
name: Path,
ty: GenericParamType,
default: Option<GenericArgument>,
}

impl GenericParam {
pub fn new_type_param(name: &str) -> Self {
GenericParam {
name: Path::new(name),
ty: GenericParamType::Type,
default: None,
}
}

pub fn load(param: &syn::GenericParam) -> Result<Option<Self>, 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.
Expand All @@ -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),
})),
},
}
Expand Down Expand Up @@ -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()
}

Expand All @@ -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");
}
}
Expand Down
19 changes: 19 additions & 0 deletions tests/expectations/generic_defaults.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

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);
27 changes: 27 additions & 0 deletions tests/expectations/generic_defaults.compat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

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
23 changes: 23 additions & 0 deletions tests/expectations/generic_defaults.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>

template<typename T, typename P = void>
using Foo = T;

template<typename T, typename P>
struct Bar {
Foo<T> f;
P p;
};

template<typename T>
using Baz = Foo<T>;

extern "C" {

void foo_root(Foo<int16_t> f, Bar<int32_t, uint32_t> b, Baz<int64_t> z);

} // extern "C"
21 changes: 21 additions & 0 deletions tests/expectations/generic_defaults.pyx
Original file line number Diff line number Diff line change
@@ -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);
19 changes: 19 additions & 0 deletions tests/expectations/generic_defaults_both.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

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);
27 changes: 27 additions & 0 deletions tests/expectations/generic_defaults_both.compat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

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
19 changes: 19 additions & 0 deletions tests/expectations/generic_defaults_tag.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

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);
27 changes: 27 additions & 0 deletions tests/expectations/generic_defaults_tag.compat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

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
21 changes: 21 additions & 0 deletions tests/expectations/generic_defaults_tag.pyx
Original file line number Diff line number Diff line change
@@ -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);
16 changes: 16 additions & 0 deletions tests/rust/generic_defaults.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#[repr(transparent)]
pub struct Foo<T, P = c_void> {
field: T,
_phantom: std::marker::PhantomData<P>,
}

#[repr(C)]
pub struct Bar<T, P> {
f: Foo<T>,
p: P,
}

pub type Baz<T> = Foo<T>;

#[no_mangle]
pub extern "C" fn foo_root(f: Foo<i16>, b: Bar<i32, u32>, z: Baz<i64>) {}
Loading