summaryrefslogtreecommitdiffstats
path: root/src/bindgen/ir/generic_path.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bindgen/ir/generic_path.rs')
-rw-r--r--src/bindgen/ir/generic_path.rs303
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))
+ }
+}