Skip to content

Commit

Permalink
support generics with defaulted args
Browse files Browse the repository at this point in the history
  • Loading branch information
scovich authored and emilio committed May 26, 2024
1 parent 9c10488 commit c6012a0
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 11 deletions.
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>) {}

0 comments on commit c6012a0

Please sign in to comment.