diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs index 8075f97e5..1018e09d8 100644 --- a/src/bindgen/cdecl.rs +++ b/src/bindgen/cdecl.rs @@ -5,7 +5,7 @@ use std::io::Write; use crate::bindgen::declarationtyperesolver::DeclarationType; -use crate::bindgen::ir::{ArrayLength, Function, Type}; +use crate::bindgen::ir::{ArrayLength, Function, GenericArgument, Type}; use crate::bindgen::writer::{ListType, SourceWriter}; use crate::bindgen::{Config, Language}; @@ -35,7 +35,7 @@ impl CDeclarator { struct CDecl { type_qualifers: String, type_name: String, - type_generic_args: Vec, + type_generic_args: Vec, declarators: Vec, type_ctype: Option, } diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs index 413ba7223..6409a7913 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -10,8 +10,9 @@ use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, AnnotationValue, Cfg, ConditionWrite, Documentation, Field, GenericParams, - GenericPath, Item, ItemContainer, Literal, Path, Repr, ReprStyle, Struct, ToCondition, Type, + AnnotationSet, AnnotationValue, Cfg, ConditionWrite, Documentation, Field, GenericArgument, + GenericParams, GenericPath, Item, ItemContainer, Literal, Path, Repr, ReprStyle, Struct, + ToCondition, Type, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; @@ -61,8 +62,8 @@ impl VariantBody { fn specialize( &self, - generic_values: &[Type], - mappings: &[(&Path, &Type)], + generic_values: &[GenericArgument], + mappings: &[(&Path, &GenericArgument)], config: &Config, ) -> Self { match *self { @@ -265,8 +266,8 @@ impl EnumVariant { fn specialize( &self, - generic_values: &[Type], - mappings: &[(&Path, &Type)], + generic_values: &[GenericArgument], + mappings: &[(&Path, &GenericArgument)], config: &Config, ) -> Self { Self::new( @@ -382,7 +383,7 @@ impl Enum { } let path = Path::new(item.ident.unraw().to_string()); - let generic_params = GenericParams::new(&item.generics); + let generic_params = GenericParams::load(&item.generics)?; let mut variants = Vec::new(); let mut has_data = false; @@ -611,28 +612,11 @@ impl Item for Enum { fn instantiate_monomorph( &self, - generic_values: &[Type], + generic_values: &[GenericArgument], library: &Library, out: &mut Monomorphs, ) { - assert!( - self.generic_params.len() > 0, - "{} is not generic", - self.path.name() - ); - assert!( - self.generic_params.len() == generic_values.len(), - "{} has {} params but is being instantiated with {} values", - self.path.name(), - self.generic_params.len(), - generic_values.len(), - ); - - let mappings = self - .generic_params - .iter() - .zip(generic_values.iter()) - .collect::>(); + let mappings = self.generic_params.call(self.path.name(), generic_values); for variant in &self.variants { if let VariantBody::Body { ref body, .. } = variant.body { diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs index bb4522fbd..e9f362097 100644 --- a/src/bindgen/ir/generic_path.rs +++ b/src/bindgen/ir/generic_path.rs @@ -3,29 +3,95 @@ use std::ops::Deref; use syn::ext::IdentExt; +use crate::bindgen::cdecl; use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::{DeclarationType, DeclarationTypeResolver}; -use crate::bindgen::ir::{Path, Type}; +use crate::bindgen::ir::{ArrayLength, Path, Type}; use crate::bindgen::utilities::IterHelpers; use crate::bindgen::writer::{Source, SourceWriter}; +#[derive(Debug, Clone)] +pub enum GenericParamType { + Type, + Const(Type), +} + +#[derive(Debug, Clone)] +pub struct GenericParam { + name: Path, + ty: GenericParamType, +} + +impl GenericParam { + pub fn new_type_param(name: &str) -> Self { + GenericParam { + name: Path::new(name), + ty: GenericParamType::Type, + } + } + + 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::Lifetime(_) => Ok(None), + + syn::GenericParam::Const(syn::ConstParam { + ref ident, ref ty, .. + }) => match Type::load(ty)? { + None => { + // A type that evaporates, like PhantomData. + Err(format!("unsupported const generic type: {:?}", ty)) + } + Some(ty) => Ok(Some(GenericParam { + name: Path::new(ident.unraw().to_string()), + ty: GenericParamType::Const(ty), + })), + }, + } + } + + pub fn name(&self) -> &Path { + &self.name + } +} + #[derive(Default, Debug, Clone)] -pub struct GenericParams(pub Vec); +pub struct GenericParams(pub Vec); impl GenericParams { - pub fn new(generics: &syn::Generics) -> Self { - GenericParams( - generics - .params - .iter() - .filter_map(|x| match *x { - syn::GenericParam::Type(syn::TypeParam { ref ident, .. }) => { - Some(Path::new(ident.unraw().to_string())) - } - _ => None, - }) - .collect(), - ) + pub fn load(generics: &syn::Generics) -> Result { + let mut params = vec![]; + for param in &generics.params { + if let Some(p) = GenericParam::load(param)? { + params.push(p); + } + } + + Ok(GenericParams(params)) + } + + /// Associate each parameter with an argument. + pub fn call<'out>( + &'out self, + 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(), + "{} has {} params but is being instantiated with {} values", + item_name, + self.len(), + arguments.len(), + ); + self.iter() + .map(|param| param.name()) + .zip(arguments.iter()) + .collect() } fn write_internal( @@ -40,9 +106,19 @@ impl GenericParams { if i != 0 { out.write(", "); } - write!(out, "typename {}", item); - if with_default { - write!(out, " = void"); + match item.ty { + GenericParamType::Type => { + write!(out, "typename {}", item.name); + if with_default { + write!(out, " = void"); + } + } + GenericParamType::Const(ref ty) => { + cdecl::write_field(out, ty, item.name.name(), config); + if with_default { + write!(out, " = 0"); + } + } } } out.write(">"); @@ -56,9 +132,9 @@ impl GenericParams { } impl Deref for GenericParams { - type Target = [Path]; + type Target = [GenericParam]; - fn deref(&self) -> &[Path] { + fn deref(&self) -> &[GenericParam] { &self.0 } } @@ -69,16 +145,52 @@ impl Source for GenericParams { } } +/// A (non-lifetime) argument passed to a generic, either a type or a constant expression. +/// +/// Note: Both arguments in a type like `Array` are represented as +/// `GenericArgument::Type`s, even if `N` is actually the name of a const. This +/// is a consequence of `syn::GenericArgument` doing the same thing. +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum GenericArgument { + Type(Type), + Const(ArrayLength), +} + +impl GenericArgument { + pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> GenericArgument { + match *self { + GenericArgument::Type(ref ty) => GenericArgument::Type(ty.specialize(mappings)), + GenericArgument::Const(ref expr) => GenericArgument::Const(expr.clone()), + } + } + + pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) { + match *self { + GenericArgument::Type(ref mut ty) => ty.rename_for_config(config, generic_params), + GenericArgument::Const(ref mut expr) => expr.rename_for_config(config), + } + } +} + +impl Source for GenericArgument { + fn write(&self, config: &Config, out: &mut SourceWriter) { + match *self { + GenericArgument::Type(ref ty) => ty.write(config, out), + GenericArgument::Const(ref expr) => expr.write(config, out), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct GenericPath { path: Path, export_name: String, - generics: Vec, + generics: Vec, ctype: Option, } impl GenericPath { - pub fn new(path: Path, generics: Vec) -> Self { + pub fn new(path: Path, generics: Vec) -> Self { let export_name = path.name().to_owned(); Self { path, @@ -103,11 +215,11 @@ impl GenericPath { &self.path } - pub fn generics(&self) -> &[Type] { + pub fn generics(&self) -> &[GenericArgument] { &self.generics } - pub fn generics_mut(&mut self) -> &mut [Type] { + pub fn generics_mut(&mut self) -> &mut [GenericArgument] { &mut self.generics } @@ -123,11 +235,15 @@ impl GenericPath { &self.export_name } + pub fn is_single_identifier(&self) -> bool { + self.generics.is_empty() + } + pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) { for generic in &mut self.generics { generic.rename_for_config(config, generic_params); } - if !generic_params.contains(&self.path) { + if !generic_params.iter().any(|param| param.name == self.path) { config.export.rename(&mut self.export_name); } } @@ -156,8 +272,11 @@ impl GenericPath { ref args, .. }) => args.iter().try_skip_map(|x| match *x { - syn::GenericArgument::Type(ref x) => Type::load(x), + syn::GenericArgument::Type(ref x) => Ok(Type::load(x)?.map(GenericArgument::Type)), syn::GenericArgument::Lifetime(_) => Ok(None), + syn::GenericArgument::Const(ref x) => { + Ok(Some(GenericArgument::Const(ArrayLength::load(x)?))) + } _ => Err(format!("can't handle generic argument {:?}", x)), })?, syn::PathArguments::Parenthesized(_) => { diff --git a/src/bindgen/ir/item.rs b/src/bindgen/ir/item.rs index c009e64ea..a1c3a7521 100644 --- a/src/bindgen/ir/item.rs +++ b/src/bindgen/ir/item.rs @@ -9,7 +9,8 @@ use crate::bindgen::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, Constant, Enum, OpaqueItem, Path, Static, Struct, Type, Typedef, Union, + AnnotationSet, Cfg, Constant, Enum, GenericArgument, OpaqueItem, Path, Static, Struct, Typedef, + Union, }; use crate::bindgen::library::Library; use crate::bindgen::monomorph::Monomorphs; @@ -37,7 +38,12 @@ pub trait Item { } fn rename_for_config(&mut self, _config: &Config) {} fn add_dependencies(&self, _library: &Library, _out: &mut Dependencies) {} - fn instantiate_monomorph(&self, _generics: &[Type], _library: &Library, _out: &mut Monomorphs) { + fn instantiate_monomorph( + &self, + _generics: &[GenericArgument], + _library: &Library, + _out: &mut Monomorphs, + ) { unreachable!("Cannot instantiate {} as a generic.", self.name()) } } diff --git a/src/bindgen/ir/opaque.rs b/src/bindgen/ir/opaque.rs index a2bcf5cab..4451d4a16 100644 --- a/src/bindgen/ir/opaque.rs +++ b/src/bindgen/ir/opaque.rs @@ -8,8 +8,8 @@ use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path, - ToCondition, Type, + AnnotationSet, Cfg, ConditionWrite, Documentation, GenericArgument, GenericParams, Item, + ItemContainer, Path, ToCondition, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; @@ -35,7 +35,7 @@ impl OpaqueItem { ) -> Result { Ok(Self::new( path, - GenericParams::new(generics), + GenericParams::load(generics)?, Cfg::append(mod_cfg, Cfg::load(attrs)), AnnotationSet::load(attrs).unwrap_or_else(|_| AnnotationSet::new()), Documentation::load(attrs), @@ -98,7 +98,7 @@ impl Item for OpaqueItem { fn instantiate_monomorph( &self, - generic_values: &[Type], + generic_values: &[GenericArgument], library: &Library, out: &mut Monomorphs, ) { diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs index 12f43a216..be199739d 100644 --- a/src/bindgen/ir/structure.rs +++ b/src/bindgen/ir/structure.rs @@ -10,8 +10,9 @@ use crate::bindgen::config::{Config, Language, LayoutConfig}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, ConditionWrite, Constant, Documentation, Field, GenericParams, Item, - ItemContainer, Path, Repr, ReprAlign, ReprStyle, ToCondition, Type, Typedef, + AnnotationSet, Cfg, ConditionWrite, Constant, Documentation, Field, GenericArgument, + GenericParams, Item, ItemContainer, Path, Repr, ReprAlign, ReprStyle, ToCondition, Type, + Typedef, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; @@ -102,7 +103,7 @@ impl Struct { Ok(Struct::new( path, - GenericParams::new(&item.generics), + GenericParams::load(&item.generics)?, fields, has_tag_field, is_enum_variant_body, @@ -174,8 +175,8 @@ impl Struct { pub fn specialize( &self, - generic_values: &[Type], - mappings: &[(&Path, &Type)], + generic_values: &[GenericArgument], + mappings: &[(&Path, &GenericArgument)], config: &Config, ) -> Self { let mangled_path = mangle::mangle_path(&self.path, generic_values, &config.export.mangle); @@ -365,29 +366,11 @@ impl Item for Struct { fn instantiate_monomorph( &self, - generic_values: &[Type], + generic_values: &[GenericArgument], library: &Library, out: &mut Monomorphs, ) { - assert!( - self.generic_params.len() > 0, - "{} is not generic", - self.path - ); - assert!( - self.generic_params.len() == generic_values.len(), - "{} has {} params but is being instantiated with {} values", - self.path, - self.generic_params.len(), - generic_values.len(), - ); - - let mappings = self - .generic_params - .iter() - .zip(generic_values.iter()) - .collect::>(); - + let mappings = self.generic_params.call(self.path.name(), generic_values); let monomorph = self.specialize(generic_values, &mappings, library.get_config()); out.insert_struct(library, self, monomorph, generic_values.to_owned()); } diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs index 2edfd73f4..cd52fec72 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -11,7 +11,7 @@ use crate::bindgen::cdecl; use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; -use crate::bindgen::ir::{GenericParams, GenericPath, Path}; +use crate::bindgen::ir::{GenericArgument, GenericParams, GenericPath, Path}; use crate::bindgen::library::Library; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::utilities::IterHelpers; @@ -320,16 +320,64 @@ pub enum ArrayLength { impl ArrayLength { pub fn as_str(&self) -> &str { - match self { + match *self { ArrayLength::Name(ref string) | ArrayLength::Value(ref string) => string, } } - fn rename_for_config(&mut self, config: &Config) { + pub fn rename_for_config(&mut self, config: &Config) { if let ArrayLength::Name(ref mut name) = self { config.export.rename(name); } } + + pub fn load(expr: &syn::Expr) -> Result { + match *expr { + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Int(ref len), + .. + }) => Ok(ArrayLength::Value(len.base10_digits().to_string())), + syn::Expr::Path(ref path) => { + let generic_path = GenericPath::load(&path.path)?; + Ok(ArrayLength::Name(generic_path.export_name().to_owned())) + } + _ => Err(format!("can't handle const expression {:?}", expr)), + } + } + + pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> ArrayLength { + match *self { + ArrayLength::Name(ref name) => { + let path = Path::new(name); + for &(param, value) in mappings { + if path == *param { + match *value { + GenericArgument::Type(Type::Path(ref path)) + if path.is_single_identifier() => + { + // This happens when the generic argument is a path. + return ArrayLength::Name(path.name().to_string()); + } + GenericArgument::Const(ref expr) => { + return expr.clone(); + } + _ => { + // unsupported argument type - really should be an error + } + } + } + } + } + ArrayLength::Value(_) => {} + } + self.clone() + } +} + +impl Source for ArrayLength { + fn write(&self, _config: &Config, out: &mut SourceWriter) { + write!(out, "{}", self.as_str()); + } } #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -415,9 +463,7 @@ impl Type { } } syn::Type::Array(syn::TypeArray { - ref elem, - len: syn::Expr::Path(ref path), - .. + ref elem, ref len, .. }) => { let converted = Type::load(elem)?; @@ -425,28 +471,8 @@ impl Type { Some(converted) => converted, None => return Err("Cannot have an array of zero sized types.".to_owned()), }; - let generic_path = GenericPath::load(&path.path)?; - let len = ArrayLength::Name(generic_path.export_name().to_owned()); - Type::Array(Box::new(converted), len) - } - syn::Type::Array(syn::TypeArray { - ref elem, - len: - syn::Expr::Lit(syn::ExprLit { - lit: syn::Lit::Int(ref len), - .. - }), - .. - }) => { - let converted = Type::load(elem)?; - let converted = match converted { - Some(converted) => converted, - None => return Err("Cannot have an array of zero sized types.".to_owned()), - }; - - let len = ArrayLength::Value(len.base10_digits().to_string()); - // panic!("panic -> value: {:?}", len); + let len = ArrayLength::load(len)?; Type::Array(Box::new(converted), len) } syn::Type::BareFn(ref function) => { @@ -609,7 +635,11 @@ impl Type { return None; } - let unsimplified_generic = &path.generics()[0]; + let unsimplified_generic = match path.generics()[0] { + GenericArgument::Type(ref ty) => ty, + GenericArgument::Const(_) => return None, + }; + let generic = match unsimplified_generic.simplified_type(config) { Some(generic) => Cow::Owned(generic), None => Cow::Borrowed(unsimplified_generic), @@ -663,7 +693,10 @@ impl Type { Type::Array(ref mut ty, ..) | Type::Ptr { ref mut ty, .. } => visitor(ty), Type::Path(ref mut path) => { for generic in path.generics_mut() { - visitor(generic); + match *generic { + GenericArgument::Type(ref mut ty) => visitor(ty), + GenericArgument::Const(_) => {} + } } } Type::Primitive(..) => {} @@ -701,7 +734,7 @@ impl Type { } } - pub fn specialize(&self, mappings: &[(&Path, &Type)]) -> Type { + pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> Type { match *self { Type::Ptr { ref ty, @@ -717,7 +750,9 @@ impl Type { Type::Path(ref generic_path) => { for &(param, value) in mappings { if generic_path.path() == param { - return value.clone(); + if let GenericArgument::Type(ref ty) = *value { + return ty.clone(); + } } } @@ -732,9 +767,10 @@ impl Type { Type::Path(specialized) } Type::Primitive(ref primitive) => Type::Primitive(primitive.clone()), - Type::Array(ref ty, ref constant) => { - Type::Array(Box::new(ty.specialize(mappings)), constant.clone()) - } + Type::Array(ref ty, ref constant) => Type::Array( + Box::new(ty.specialize(mappings)), + constant.specialize(mappings), + ), Type::FuncPtr { ref ret, ref args, @@ -763,10 +799,12 @@ impl Type { } Type::Path(ref generic) => { for generic_value in generic.generics() { - generic_value.add_dependencies_ignoring_generics(generic_params, library, out); + if let GenericArgument::Type(ref ty) = *generic_value { + ty.add_dependencies_ignoring_generics(generic_params, library, out); + } } let path = generic.path(); - if !generic_params.contains(path) { + if !generic_params.iter().any(|param| param.name() == path) { if let Some(items) = library.get_items(path) { if !out.items.contains(path) { out.items.insert(path.clone()); diff --git a/src/bindgen/ir/typedef.rs b/src/bindgen/ir/typedef.rs index da7cf737c..626732e2a 100644 --- a/src/bindgen/ir/typedef.rs +++ b/src/bindgen/ir/typedef.rs @@ -11,8 +11,8 @@ use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericParams, Item, ItemContainer, - Path, ToCondition, Type, + AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericArgument, GenericParams, Item, + ItemContainer, Path, ToCondition, Type, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; @@ -37,7 +37,7 @@ impl Typedef { let path = Path::new(item.ident.unraw().to_string()); Ok(Typedef::new( path, - GenericParams::new(&item.generics), + GenericParams::load(&item.generics)?, x, Cfg::append(mod_cfg, Cfg::load(&item.attrs)), AnnotationSet::load(&item.attrs)?, @@ -155,28 +155,11 @@ impl Item for Typedef { fn instantiate_monomorph( &self, - generic_values: &[Type], + generic_values: &[GenericArgument], library: &Library, out: &mut Monomorphs, ) { - assert!( - self.generic_params.len() > 0, - "{} is not generic", - self.path - ); - assert!( - self.generic_params.len() == generic_values.len(), - "{} has {} params but is being instantiated with {} values", - self.path, - self.generic_params.len(), - generic_values.len(), - ); - - let mappings = self - .generic_params - .iter() - .zip(generic_values.iter()) - .collect::>(); + let mappings = self.generic_params.call(self.path.name(), generic_values); let mangled_path = mangle::mangle_path( &self.path, diff --git a/src/bindgen/ir/union.rs b/src/bindgen/ir/union.rs index ed77962c2..07bd16c58 100644 --- a/src/bindgen/ir/union.rs +++ b/src/bindgen/ir/union.rs @@ -10,8 +10,8 @@ use crate::bindgen::config::{Config, Language, LayoutConfig}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericParams, Item, ItemContainer, - Path, Repr, ReprAlign, ReprStyle, ToCondition, Type, + AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericArgument, GenericParams, Item, + ItemContainer, Path, Repr, ReprAlign, ReprStyle, ToCondition, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; @@ -62,7 +62,7 @@ impl Union { Ok(Union::new( path, - GenericParams::new(&item.generics), + GenericParams::load(&item.generics)?, fields, repr.align, tuple_union, @@ -223,28 +223,11 @@ impl Item for Union { fn instantiate_monomorph( &self, - generic_values: &[Type], + generic_values: &[GenericArgument], library: &Library, out: &mut Monomorphs, ) { - assert!( - self.generic_params.len() > 0, - "{} is not generic", - self.path - ); - assert!( - self.generic_params.len() == generic_values.len(), - "{} has {} params but is being instantiated with {} values", - self.path, - self.generic_params.len(), - generic_values.len(), - ); - - let mappings = self - .generic_params - .iter() - .zip(generic_values.iter()) - .collect::>(); + let mappings = self.generic_params.call(self.path.name(), generic_values); let mangled_path = mangle::mangle_path( &self.path, diff --git a/src/bindgen/mangle.rs b/src/bindgen/mangle.rs index b84622aa5..c61255e6c 100644 --- a/src/bindgen/mangle.rs +++ b/src/bindgen/mangle.rs @@ -3,14 +3,18 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::bindgen::config::MangleConfig; -use crate::bindgen::ir::{Path, Type}; +use crate::bindgen::ir::{ArrayLength, GenericArgument, GenericPath, Path, Type}; use crate::bindgen::rename::IdentifierType; -pub fn mangle_path(path: &Path, generic_values: &[Type], config: &MangleConfig) -> Path { +pub fn mangle_path(path: &Path, generic_values: &[GenericArgument], config: &MangleConfig) -> Path { Path::new(mangle_name(path.name(), generic_values, config)) } -pub fn mangle_name(name: &str, generic_values: &[Type], config: &MangleConfig) -> String { +pub fn mangle_name( + name: &str, + generic_values: &[GenericArgument], + config: &MangleConfig, +) -> String { Mangler::new(name, generic_values, /* last = */ true, config).mangle() } @@ -27,7 +31,7 @@ enum Separator { struct Mangler<'a> { input: &'a str, - generic_values: &'a [Type], + generic_values: &'a [GenericArgument], output: String, last: bool, config: &'a MangleConfig, @@ -36,7 +40,7 @@ struct Mangler<'a> { impl<'a> Mangler<'a> { fn new( input: &'a str, - generic_values: &'a [Type], + generic_values: &'a [GenericArgument], last: bool, config: &'a MangleConfig, ) -> Self { @@ -64,6 +68,20 @@ impl<'a> Mangler<'a> { self.output.extend(std::iter::repeat(separator).take(count)); } + fn append_mangled_argument(&mut self, arg: &GenericArgument, last: bool) { + match *arg { + GenericArgument::Type(ref ty) => self.append_mangled_type(ty, last), + GenericArgument::Const(ArrayLength::Name(ref name)) => { + // This must behave the same as a GenericArgument::Type, + // because const arguments are commonly represented as Types; + // see the comment on `enum GenericArgument`. + let fake_ty = Type::Path(GenericPath::new(Path::new(name), vec![])); + self.append_mangled_type(&fake_ty, last); + } + GenericArgument::Const(ArrayLength::Value(ref val)) => self.output.push_str(val), + } + } + fn append_mangled_type(&mut self, ty: &Type, last: bool) { match *ty { Type::Path(ref generic) => { @@ -128,12 +146,12 @@ impl<'a> Mangler<'a> { } self.push(Separator::OpeningAngleBracket); - for (i, ty) in self.generic_values.iter().enumerate() { + for (i, arg) in self.generic_values.iter().enumerate() { if i != 0 { self.push(Separator::Comma); } let last = self.last && i == self.generic_values.len() - 1; - self.append_mangled_type(ty, last); + self.append_mangled_argument(arg, last); } // Skip writing the trailing '>' mangling when possible @@ -148,22 +166,22 @@ fn generics() { use crate::bindgen::ir::{GenericPath, PrimitiveType}; use crate::bindgen::rename::RenameRule::{self, PascalCase}; - fn float() -> Type { - Type::Primitive(PrimitiveType::Float) + fn float() -> GenericArgument { + GenericArgument::Type(Type::Primitive(PrimitiveType::Float)) } - fn c_char() -> Type { - Type::Primitive(PrimitiveType::Char) + fn c_char() -> GenericArgument { + GenericArgument::Type(Type::Primitive(PrimitiveType::Char)) } - fn path(path: &str) -> Type { + fn path(path: &str) -> GenericArgument { generic_path(path, &[]) } - fn generic_path(path: &str, generics: &[Type]) -> Type { + fn generic_path(path: &str, arguments: &[GenericArgument]) -> GenericArgument { let path = Path::new(path); - let generic_path = GenericPath::new(path, generics.to_owned()); - Type::Path(generic_path) + let generic_path = GenericPath::new(path, arguments.to_owned()); + GenericArgument::Type(Type::Path(generic_path)) } // Foo => Foo_f32 @@ -288,4 +306,43 @@ fn generics() { ), Path::new("FooBarTBarE") ); + + assert_eq!( + mangle_path( + &Path::new("Top"), + &[GenericArgument::Const(ArrayLength::Value("40".to_string()))], + &MangleConfig::default(), + ), + Path::new("Top_40") + ); + + assert_eq!( + mangle_path( + &Path::new("Top"), + &[GenericArgument::Const(ArrayLength::Name("N".to_string()))], + &MangleConfig::default(), + ), + Path::new("Top_N") + ); + + assert_eq!( + mangle_path( + &Path::new("Top"), + &[generic_path("N", &[])], + &MangleConfig::default(), + ), + Path::new("Top_N") + ); + + assert_eq!( + mangle_path( + &Path::new("Foo"), + &[ + float(), + GenericArgument::Const(ArrayLength::Value("40".to_string())) + ], + &MangleConfig::default(), + ), + Path::new("Foo_f32__40") + ); } diff --git a/src/bindgen/monomorph.rs b/src/bindgen/monomorph.rs index 8f910d9a9..db6dce63e 100644 --- a/src/bindgen/monomorph.rs +++ b/src/bindgen/monomorph.rs @@ -5,7 +5,9 @@ use std::collections::HashMap; use std::mem; -use crate::bindgen::ir::{Enum, GenericPath, OpaqueItem, Path, Struct, Type, Typedef, Union}; +use crate::bindgen::ir::{ + Enum, GenericArgument, GenericPath, OpaqueItem, Path, Struct, Typedef, Union, +}; use crate::bindgen::library::Library; #[derive(Default, Clone, Debug)] @@ -28,9 +30,9 @@ impl Monomorphs { library: &Library, generic: &Struct, monomorph: Struct, - parameters: Vec, + arguments: Vec, ) { - let replacement_path = GenericPath::new(generic.path.clone(), parameters); + let replacement_path = GenericPath::new(generic.path.clone(), arguments); debug_assert!(generic.generic_params.len() > 0); debug_assert!(!self.contains(&replacement_path)); @@ -48,9 +50,9 @@ impl Monomorphs { library: &Library, generic: &Enum, monomorph: Enum, - parameters: Vec, + arguments: Vec, ) { - let replacement_path = GenericPath::new(generic.path.clone(), parameters); + let replacement_path = GenericPath::new(generic.path.clone(), arguments); debug_assert!(generic.generic_params.len() > 0); debug_assert!(!self.contains(&replacement_path)); @@ -68,9 +70,9 @@ impl Monomorphs { library: &Library, generic: &Union, monomorph: Union, - parameters: Vec, + arguments: Vec, ) { - let replacement_path = GenericPath::new(generic.path.clone(), parameters); + let replacement_path = GenericPath::new(generic.path.clone(), arguments); debug_assert!(generic.generic_params.len() > 0); debug_assert!(!self.contains(&replacement_path)); @@ -87,9 +89,9 @@ impl Monomorphs { &mut self, generic: &OpaqueItem, monomorph: OpaqueItem, - parameters: Vec, + arguments: Vec, ) { - let replacement_path = GenericPath::new(generic.path.clone(), parameters); + let replacement_path = GenericPath::new(generic.path.clone(), arguments); debug_assert!(generic.generic_params.len() > 0); debug_assert!(!self.contains(&replacement_path)); @@ -104,9 +106,9 @@ impl Monomorphs { library: &Library, generic: &Typedef, monomorph: Typedef, - parameters: Vec, + arguments: Vec, ) { - let replacement_path = GenericPath::new(generic.path.clone(), parameters); + let replacement_path = GenericPath::new(generic.path.clone(), arguments); debug_assert!(generic.generic_params.len() > 0); debug_assert!(!self.contains(&replacement_path)); diff --git a/src/bindgen/parser.rs b/src/bindgen/parser.rs index 804ead033..81681ba17 100644 --- a/src/bindgen/parser.rs +++ b/src/bindgen/parser.rs @@ -15,8 +15,8 @@ use crate::bindgen::cargo::{Cargo, PackageRef}; use crate::bindgen::config::{Config, ParseConfig}; use crate::bindgen::error::Error; use crate::bindgen::ir::{ - AnnotationSet, Cfg, Constant, Documentation, Enum, Function, GenericParams, ItemMap, - OpaqueItem, Path, Static, Struct, Type, Typedef, Union, + AnnotationSet, Cfg, Constant, Documentation, Enum, Function, GenericParam, GenericParams, + ItemMap, OpaqueItem, Path, Static, Struct, Type, Typedef, Union, }; use crate::bindgen::utilities::{SynAbiHelpers, SynAttributeHelpers, SynItemFnHelpers}; @@ -425,7 +425,10 @@ impl Parse { pub fn add_std_types(&mut self) { let mut add_opaque = |path: &str, generic_params: Vec<&str>| { let path = Path::new(path); - let generic_params: Vec<_> = generic_params.into_iter().map(Path::new).collect(); + let generic_params: Vec<_> = generic_params + .into_iter() + .map(GenericParam::new_type_param) + .collect(); self.opaque_items.try_insert(OpaqueItem::new( path, GenericParams(generic_params), diff --git a/tests/expectations/const_generics.both.c b/tests/expectations/const_generics.both.c new file mode 100644 index 000000000..78399bc52 --- /dev/null +++ b/tests/expectations/const_generics.both.c @@ -0,0 +1,17 @@ +#include +#include +#include +#include + +#define TITLE_SIZE 80 + +typedef int8_t CArrayString_TITLE_SIZE[TITLE_SIZE]; + +typedef int8_t CArrayString_40[40]; + +typedef struct Book { + CArrayString_TITLE_SIZE title; + CArrayString_40 author; +} Book; + +void root(struct Book *a); diff --git a/tests/expectations/const_generics.both.compat.c b/tests/expectations/const_generics.both.compat.c new file mode 100644 index 000000000..3274454af --- /dev/null +++ b/tests/expectations/const_generics.both.compat.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +#define TITLE_SIZE 80 + +typedef int8_t CArrayString_TITLE_SIZE[TITLE_SIZE]; + +typedef int8_t CArrayString_40[40]; + +typedef struct Book { + CArrayString_TITLE_SIZE title; + CArrayString_40 author; +} Book; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(struct Book *a); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/const_generics.c b/tests/expectations/const_generics.c new file mode 100644 index 000000000..eeea09a78 --- /dev/null +++ b/tests/expectations/const_generics.c @@ -0,0 +1,17 @@ +#include +#include +#include +#include + +#define TITLE_SIZE 80 + +typedef int8_t CArrayString_TITLE_SIZE[TITLE_SIZE]; + +typedef int8_t CArrayString_40[40]; + +typedef struct { + CArrayString_TITLE_SIZE title; + CArrayString_40 author; +} Book; + +void root(Book *a); diff --git a/tests/expectations/const_generics.compat.c b/tests/expectations/const_generics.compat.c new file mode 100644 index 000000000..40ded76cb --- /dev/null +++ b/tests/expectations/const_generics.compat.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +#define TITLE_SIZE 80 + +typedef int8_t CArrayString_TITLE_SIZE[TITLE_SIZE]; + +typedef int8_t CArrayString_40[40]; + +typedef struct { + CArrayString_TITLE_SIZE title; + CArrayString_40 author; +} Book; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(Book *a); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/const_generics.cpp b/tests/expectations/const_generics.cpp new file mode 100644 index 000000000..e5157e622 --- /dev/null +++ b/tests/expectations/const_generics.cpp @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include + +constexpr static const uintptr_t TITLE_SIZE = 80; + +template +using CArrayString = int8_t[CAP]; + +struct Book { + CArrayString title; + CArrayString<40> author; +}; + +extern "C" { + +void root(Book *a); + +} // extern "C" diff --git a/tests/expectations/const_generics.pyx b/tests/expectations/const_generics.pyx new file mode 100644 index 000000000..873918610 --- /dev/null +++ b/tests/expectations/const_generics.pyx @@ -0,0 +1,19 @@ +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 *: + + const uintptr_t TITLE_SIZE # = 80 + + ctypedef int8_t CArrayString_TITLE_SIZE[TITLE_SIZE]; + + ctypedef int8_t CArrayString_40[40]; + + ctypedef struct Book: + CArrayString_TITLE_SIZE title; + CArrayString_40 author; + + void root(Book *a); diff --git a/tests/expectations/const_generics.tag.c b/tests/expectations/const_generics.tag.c new file mode 100644 index 000000000..09e47666d --- /dev/null +++ b/tests/expectations/const_generics.tag.c @@ -0,0 +1,17 @@ +#include +#include +#include +#include + +#define TITLE_SIZE 80 + +typedef int8_t CArrayString_TITLE_SIZE[TITLE_SIZE]; + +typedef int8_t CArrayString_40[40]; + +struct Book { + CArrayString_TITLE_SIZE title; + CArrayString_40 author; +}; + +void root(struct Book *a); diff --git a/tests/expectations/const_generics.tag.compat.c b/tests/expectations/const_generics.tag.compat.c new file mode 100644 index 000000000..f764e5b3c --- /dev/null +++ b/tests/expectations/const_generics.tag.compat.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +#define TITLE_SIZE 80 + +typedef int8_t CArrayString_TITLE_SIZE[TITLE_SIZE]; + +typedef int8_t CArrayString_40[40]; + +struct Book { + CArrayString_TITLE_SIZE title; + CArrayString_40 author; +}; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(struct Book *a); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/const_generics.tag.pyx b/tests/expectations/const_generics.tag.pyx new file mode 100644 index 000000000..f2c9d7a41 --- /dev/null +++ b/tests/expectations/const_generics.tag.pyx @@ -0,0 +1,19 @@ +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 *: + + const uintptr_t TITLE_SIZE # = 80 + + ctypedef int8_t CArrayString_TITLE_SIZE[TITLE_SIZE]; + + ctypedef int8_t CArrayString_40[40]; + + cdef struct Book: + CArrayString_TITLE_SIZE title; + CArrayString_40 author; + + void root(Book *a); diff --git a/tests/expectations/const_generics_arrayvec.both.c b/tests/expectations/const_generics_arrayvec.both.c new file mode 100644 index 000000000..e3cc9a076 --- /dev/null +++ b/tests/expectations/const_generics_arrayvec.both.c @@ -0,0 +1,11 @@ +#include +#include +#include +#include + +typedef struct ArrayVec_____u8__100 { + uint8_t *xs[100]; + uint32_t len; +} ArrayVec_____u8__100; + +int32_t push(struct ArrayVec_____u8__100 *v, uint8_t *elem); diff --git a/tests/expectations/const_generics_arrayvec.both.compat.c b/tests/expectations/const_generics_arrayvec.both.compat.c new file mode 100644 index 000000000..3c382bac5 --- /dev/null +++ b/tests/expectations/const_generics_arrayvec.both.compat.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +typedef struct ArrayVec_____u8__100 { + uint8_t *xs[100]; + uint32_t len; +} ArrayVec_____u8__100; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +int32_t push(struct ArrayVec_____u8__100 *v, uint8_t *elem); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/const_generics_arrayvec.c b/tests/expectations/const_generics_arrayvec.c new file mode 100644 index 000000000..f7deb7545 --- /dev/null +++ b/tests/expectations/const_generics_arrayvec.c @@ -0,0 +1,11 @@ +#include +#include +#include +#include + +typedef struct { + uint8_t *xs[100]; + uint32_t len; +} ArrayVec_____u8__100; + +int32_t push(ArrayVec_____u8__100 *v, uint8_t *elem); diff --git a/tests/expectations/const_generics_arrayvec.compat.c b/tests/expectations/const_generics_arrayvec.compat.c new file mode 100644 index 000000000..3ed7a89d9 --- /dev/null +++ b/tests/expectations/const_generics_arrayvec.compat.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +typedef struct { + uint8_t *xs[100]; + uint32_t len; +} ArrayVec_____u8__100; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +int32_t push(ArrayVec_____u8__100 *v, uint8_t *elem); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/const_generics_arrayvec.cpp b/tests/expectations/const_generics_arrayvec.cpp new file mode 100644 index 000000000..5e63c744b --- /dev/null +++ b/tests/expectations/const_generics_arrayvec.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include +#include + +template +struct ArrayVec { + T xs[CAP]; + uint32_t len; +}; + +extern "C" { + +int32_t push(ArrayVec *v, uint8_t *elem); + +} // extern "C" diff --git a/tests/expectations/const_generics_arrayvec.pyx b/tests/expectations/const_generics_arrayvec.pyx new file mode 100644 index 000000000..7e80d25d3 --- /dev/null +++ b/tests/expectations/const_generics_arrayvec.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 struct ArrayVec_____u8__100: + uint8_t *xs[100]; + uint32_t len; + + int32_t push(ArrayVec_____u8__100 *v, uint8_t *elem); diff --git a/tests/expectations/const_generics_arrayvec.tag.c b/tests/expectations/const_generics_arrayvec.tag.c new file mode 100644 index 000000000..1f45babca --- /dev/null +++ b/tests/expectations/const_generics_arrayvec.tag.c @@ -0,0 +1,11 @@ +#include +#include +#include +#include + +struct ArrayVec_____u8__100 { + uint8_t *xs[100]; + uint32_t len; +}; + +int32_t push(struct ArrayVec_____u8__100 *v, uint8_t *elem); diff --git a/tests/expectations/const_generics_arrayvec.tag.compat.c b/tests/expectations/const_generics_arrayvec.tag.compat.c new file mode 100644 index 000000000..cf45ebcca --- /dev/null +++ b/tests/expectations/const_generics_arrayvec.tag.compat.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +struct ArrayVec_____u8__100 { + uint8_t *xs[100]; + uint32_t len; +}; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +int32_t push(struct ArrayVec_____u8__100 *v, uint8_t *elem); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/const_generics_arrayvec.tag.pyx b/tests/expectations/const_generics_arrayvec.tag.pyx new file mode 100644 index 000000000..0b6299087 --- /dev/null +++ b/tests/expectations/const_generics_arrayvec.tag.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 *: + + cdef struct ArrayVec_____u8__100: + uint8_t *xs[100]; + uint32_t len; + + int32_t push(ArrayVec_____u8__100 *v, uint8_t *elem); diff --git a/tests/rust/const_generics.rs b/tests/rust/const_generics.rs new file mode 100644 index 000000000..081b38e24 --- /dev/null +++ b/tests/rust/const_generics.rs @@ -0,0 +1,15 @@ +#[repr(transparent)] +pub struct CArrayString { + pub chars: [i8; CAP], +} + +pub const TITLE_SIZE: usize = 80; + +#[repr(C)] +pub struct Book { + pub title: CArrayString, + pub author: CArrayString<40>, +} + +#[no_mangle] +pub extern "C" fn root(a: *mut Book) {} diff --git a/tests/rust/const_generics_arrayvec.rs b/tests/rust/const_generics_arrayvec.rs new file mode 100644 index 000000000..459ddbcbb --- /dev/null +++ b/tests/rust/const_generics_arrayvec.rs @@ -0,0 +1,17 @@ +#[repr(C)] +pub struct ArrayVec { + // the `len` first elements of the array are initialized + xs: [T; CAP], + len: u32, +} + +#[no_mangle] +pub unsafe extern "C" fn push(v: *mut ArrayVec<*mut u8, 100>, elem: *mut u8) -> i32 { + if (*v).len < 100 { + (*v).xs[(*v).len] = elem; + (*v).len += 1; + 1 + } else { + 0 + } +}