diff options
Diffstat (limited to 'src/bindgen/ir/generic_path.rs')
-rw-r--r-- | src/bindgen/ir/generic_path.rs | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs new file mode 100644 index 0000000..ef14890 --- /dev/null +++ b/src/bindgen/ir/generic_path.rs @@ -0,0 +1,303 @@ +use std::io::Write; +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::{ConstExpr, 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<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::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<GenericParam>); + +impl GenericParams { + pub fn load(generics: &syn::Generics) -> Result<Self, String> { + 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<F: Write>( + &self, + config: &Config, + out: &mut SourceWriter<F>, + with_default: bool, + ) { + if !self.0.is_empty() && config.language == Language::Cxx { + out.write("template<"); + for (i, item) in self.0.iter().enumerate() { + if i != 0 { + out.write(", "); + } + 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(">"); + out.new_line(); + } + } + + pub fn write_with_default<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) { + self.write_internal(config, out, true); + } +} + +impl Deref for GenericParams { + type Target = [GenericParam]; + + fn deref(&self) -> &[GenericParam] { + &self.0 + } +} + +impl Source for GenericParams { + fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) { + self.write_internal(config, out, false); + } +} + +/// A (non-lifetime) argument passed to a generic, either a type or a constant expression. +/// +/// Note: Both arguments in a type like `Array<T, N>` 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(ConstExpr), +} + +impl GenericArgument { + pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> GenericArgument { + match *self { + GenericArgument::Type(ref ty) => { + if let Type::Path(ref path) = *ty { + if path.is_single_identifier() { + // See note on `GenericArgument` above: `ty` may + // actually be the name of a const. Check for that now. + for &(name, value) in mappings { + if *name == path.path { + return value.clone(); + } + } + } + } + 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<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) { + 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<GenericArgument>, + ctype: Option<DeclarationType>, +} + +impl GenericPath { + pub fn new(path: Path, generics: Vec<GenericArgument>) -> Self { + let export_name = path.name().to_owned(); + Self { + path, + export_name, + generics, + ctype: None, + } + } + + pub fn self_path() -> Self { + Self::new(Path::new("Self"), vec![]) + } + + pub fn replace_self_with(&mut self, self_ty: &Path) { + if self.path.replace_self_with(self_ty) { + self.export_name = self_ty.name().to_owned(); + } + // Caller deals with generics. + } + + pub fn path(&self) -> &Path { + &self.path + } + + pub fn generics(&self) -> &[GenericArgument] { + &self.generics + } + + pub fn generics_mut(&mut self) -> &mut [GenericArgument] { + &mut self.generics + } + + pub fn ctype(&self) -> Option<&DeclarationType> { + self.ctype.as_ref() + } + + pub fn name(&self) -> &str { + self.path.name() + } + + pub fn export_name(&self) -> &str { + &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.iter().any(|param| param.name == self.path) { + config.export.rename(&mut self.export_name); + } + } + + pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) { + self.ctype = resolver.type_for(&self.path); + } + + pub fn load(path: &syn::Path) -> Result<Self, String> { + assert!( + !path.segments.is_empty(), + "{:?} doesn't have any segments", + path + ); + let last_segment = path.segments.last().unwrap(); + let name = last_segment.ident.unraw().to_string(); + + let path = Path::new(name); + let phantom_data_path = Path::new("PhantomData"); + if path == phantom_data_path { + return Ok(Self::new(path, Vec::new())); + } + + let generics = match last_segment.arguments { + syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + ref args, + .. + }) => args.iter().try_skip_map(|x| match *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(ConstExpr::load(x)?))) + } + _ => Err(format!("can't handle generic argument {:?}", x)), + })?, + syn::PathArguments::Parenthesized(_) => { + return Err("Path contains parentheses.".to_owned()); + } + _ => Vec::new(), + }; + + Ok(Self::new(path, generics)) + } +} |