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, 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); impl GenericParams { 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( &self, config: &Config, out: &mut SourceWriter, 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(&self, config: &Config, out: &mut SourceWriter) { 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(&self, config: &Config, out: &mut SourceWriter) { 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` 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(&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, ctype: Option, } impl GenericPath { pub fn new(path: Path, generics: Vec) -> 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 { 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)) } }