summaryrefslogtreecommitdiffstats
path: root/src/bindgen/ir
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:39:07 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:39:07 +0000
commitaf6b8ed095f88f1df2116cdc7a9d44872cfa6074 (patch)
tree1f2df671c1f8033d5ed83f056167a0911f8d2a57 /src/bindgen/ir
parentInitial commit. (diff)
downloadrust-cbindgen-af6b8ed095f88f1df2116cdc7a9d44872cfa6074.tar.xz
rust-cbindgen-af6b8ed095f88f1df2116cdc7a9d44872cfa6074.zip
Adding upstream version 0.26.0.upstream/0.26.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/bindgen/ir')
-rw-r--r--src/bindgen/ir/annotation.rs217
-rw-r--r--src/bindgen/ir/cfg.rs365
-rw-r--r--src/bindgen/ir/constant.rs813
-rw-r--r--src/bindgen/ir/documentation.rs111
-rw-r--r--src/bindgen/ir/enumeration.rs1556
-rw-r--r--src/bindgen/ir/field.rs80
-rw-r--r--src/bindgen/ir/function.rs390
-rw-r--r--src/bindgen/ir/generic_path.rs303
-rw-r--r--src/bindgen/ir/global.rs121
-rw-r--r--src/bindgen/ir/item.rs250
-rw-r--r--src/bindgen/ir/mod.rs39
-rw-r--r--src/bindgen/ir/opaque.rs176
-rw-r--r--src/bindgen/ir/path.rs50
-rw-r--r--src/bindgen/ir/repr.rs182
-rw-r--r--src/bindgen/ir/structure.rs717
-rw-r--r--src/bindgen/ir/ty.rs1017
-rw-r--r--src/bindgen/ir/typedef.rs208
-rw-r--r--src/bindgen/ir/union.rs336
18 files changed, 6931 insertions, 0 deletions
diff --git a/src/bindgen/ir/annotation.rs b/src/bindgen/ir/annotation.rs
new file mode 100644
index 0000000..48e3e4b
--- /dev/null
+++ b/src/bindgen/ir/annotation.rs
@@ -0,0 +1,217 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::borrow::Cow;
+use std::collections::hash_map::Entry;
+use std::collections::HashMap;
+use std::str::FromStr;
+
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::utilities::SynAttributeHelpers;
+
+// A system for specifying properties on items. Annotations are
+// given through document comments and parsed by this code.
+//
+// An annotation is in the form cbindgen:PROPERTY=VALUE
+// Where PROPERTY depends on the item
+// Where VALUE can be
+// * list - [Item1, Item2, Item3, ...]
+// * atom - Foo
+// * bool - true,false
+// Examples:
+// * cbindgen:field-names=[mHandle, mNamespace]
+// * cbindgen:function-postfix=WR_DESTRUCTOR_SAFE
+
+/// A value specified by an annotation.
+#[derive(Debug, Clone)]
+pub enum AnnotationValue {
+ List(Vec<String>),
+ Atom(Option<String>),
+ Bool(bool),
+}
+
+/// A set of annotations specified by a document comment.
+#[derive(Debug, Default, Clone)]
+pub struct AnnotationSet {
+ annotations: HashMap<String, AnnotationValue>,
+ pub must_use: bool,
+ pub deprecated: Option<String>,
+}
+
+pub enum DeprecatedNoteKind {
+ Function,
+ Struct,
+ Enum,
+}
+
+impl AnnotationSet {
+ pub fn new() -> AnnotationSet {
+ AnnotationSet {
+ annotations: HashMap::new(),
+ must_use: false,
+ deprecated: None,
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.annotations.is_empty() && !self.must_use
+ }
+
+ pub(crate) fn must_use(&self, config: &Config) -> bool {
+ self.must_use && config.language != Language::Cython
+ }
+
+ pub(crate) fn deprecated_note<'c>(
+ &self,
+ config: &'c Config,
+ kind: DeprecatedNoteKind,
+ ) -> Option<Cow<'c, str>> {
+ let note = self.deprecated.as_deref()?;
+
+ if config.language == Language::Cython {
+ return None;
+ }
+
+ if note.is_empty() {
+ return Some(Cow::Borrowed(match kind {
+ DeprecatedNoteKind::Enum => config.enumeration.deprecated.as_deref()?,
+ DeprecatedNoteKind::Function => config.function.deprecated.as_deref()?,
+ DeprecatedNoteKind::Struct => config.structure.deprecated.as_deref()?,
+ }));
+ }
+
+ let format = match kind {
+ DeprecatedNoteKind::Enum => &config.enumeration.deprecated_with_note,
+ DeprecatedNoteKind::Function => &config.function.deprecated_with_note,
+ DeprecatedNoteKind::Struct => &config.structure.deprecated_with_note,
+ }
+ .as_ref()?;
+ Some(Cow::Owned(format.replace("{}", &format!("{:?}", note))))
+ }
+
+ pub fn load(attrs: &[syn::Attribute]) -> Result<AnnotationSet, String> {
+ let lines = attrs.get_comment_lines();
+ let lines: Vec<&str> = lines
+ .iter()
+ .filter_map(|line| {
+ let line = line.trim_start();
+ if !line.starts_with("cbindgen:") {
+ return None;
+ }
+
+ Some(line)
+ })
+ .collect();
+
+ let must_use = attrs.has_attr_word("must_use");
+ let deprecated = attrs.find_deprecated_note();
+ let mut annotations = HashMap::new();
+
+ // Look at each line for an annotation
+ for line in lines {
+ debug_assert!(line.starts_with("cbindgen:"));
+
+ // Remove the "cbindgen:" prefix
+ let annotation = &line[9..];
+
+ // Split the annotation in two
+ let parts: Vec<&str> = annotation.split('=').map(|x| x.trim()).collect();
+
+ if parts.len() > 2 {
+ return Err(format!("Couldn't parse {}.", line));
+ }
+
+ // Grab the name that this annotation is modifying
+ let name = parts[0];
+
+ // If the annotation only has a name, assume it's setting a bool flag
+ if parts.len() == 1 {
+ annotations.insert(name.to_string(), AnnotationValue::Bool(true));
+ continue;
+ }
+
+ // Parse the value we're setting the name to
+ let value = parts[1];
+
+ if let Some(x) = parse_list(value) {
+ annotations.insert(name.to_string(), AnnotationValue::List(x));
+ continue;
+ }
+ if let Ok(x) = value.parse::<bool>() {
+ annotations.insert(name.to_string(), AnnotationValue::Bool(x));
+ continue;
+ }
+ annotations.insert(
+ name.to_string(),
+ if value.is_empty() {
+ AnnotationValue::Atom(None)
+ } else {
+ AnnotationValue::Atom(Some(value.to_string()))
+ },
+ );
+ }
+
+ Ok(AnnotationSet {
+ annotations,
+ must_use,
+ deprecated,
+ })
+ }
+
+ /// Adds an annotation value if none is specified.
+ pub fn add_default(&mut self, name: &str, value: AnnotationValue) {
+ if let Entry::Vacant(e) = self.annotations.entry(name.to_string()) {
+ e.insert(value);
+ }
+ }
+
+ pub fn list(&self, name: &str) -> Option<Vec<String>> {
+ match self.annotations.get(name) {
+ Some(AnnotationValue::List(x)) => Some(x.clone()),
+ _ => None,
+ }
+ }
+ pub fn atom(&self, name: &str) -> Option<Option<String>> {
+ match self.annotations.get(name) {
+ Some(AnnotationValue::Atom(x)) => Some(x.clone()),
+ _ => None,
+ }
+ }
+ pub fn bool(&self, name: &str) -> Option<bool> {
+ match self.annotations.get(name) {
+ Some(AnnotationValue::Bool(x)) => Some(*x),
+ _ => None,
+ }
+ }
+
+ pub fn parse_atom<T>(&self, name: &str) -> Option<T>
+ where
+ T: Default + FromStr,
+ {
+ match self.annotations.get(name) {
+ Some(AnnotationValue::Atom(x)) => Some(
+ x.as_ref()
+ .map_or(T::default(), |y| y.parse::<T>().ok().unwrap()),
+ ),
+ _ => None,
+ }
+ }
+}
+
+/// Parse lists like "[x, y, z]". This is not implemented efficiently or well.
+fn parse_list(list: &str) -> Option<Vec<String>> {
+ if list.len() < 2 {
+ return None;
+ }
+
+ match (list.chars().next(), list.chars().last()) {
+ (Some('['), Some(']')) => Some(
+ list[1..list.len() - 1]
+ .split(',')
+ .map(|x| x.trim().to_string())
+ .collect(),
+ ),
+ _ => None,
+ }
+}
diff --git a/src/bindgen/ir/cfg.rs b/src/bindgen/ir/cfg.rs
new file mode 100644
index 0000000..e8aa412
--- /dev/null
+++ b/src/bindgen/ir/cfg.rs
@@ -0,0 +1,365 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::fmt;
+use std::io::Write;
+
+use crate::bindgen::cargo::cargo_metadata::Dependency;
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::writer::SourceWriter;
+
+#[derive(PartialEq, Eq)]
+enum DefineKey<'a> {
+ Boolean(&'a str),
+ Named(&'a str, &'a str),
+}
+
+impl<'a> DefineKey<'a> {
+ fn load(key: &str) -> DefineKey {
+ // TODO: dirty parser
+ if !key.contains('=') {
+ return DefineKey::Boolean(key);
+ }
+
+ let mut splits = key.trim().split('=');
+
+ let name = match splits.next() {
+ Some(n) => n.trim(),
+ None => return DefineKey::Boolean(key),
+ };
+
+ let value = match splits.next() {
+ Some(v) => v.trim(),
+ None => return DefineKey::Boolean(key),
+ };
+
+ if splits.next().is_some() {
+ return DefineKey::Boolean(key);
+ }
+
+ DefineKey::Named(name, value)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum Cfg {
+ Boolean(String),
+ Named(String, String),
+ Any(Vec<Cfg>),
+ All(Vec<Cfg>),
+ Not(Box<Cfg>),
+}
+
+impl fmt::Display for Cfg {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Cfg::Boolean(key) => write!(f, "{}", key),
+ Cfg::Named(key, value) => write!(f, "{} = {:?}", key, value),
+ Cfg::Any(cfgs) => {
+ write!(f, "any(")?;
+ for (index, cfg) in cfgs.iter().enumerate() {
+ if index > 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "{}", cfg)?;
+ }
+ write!(f, ")")
+ }
+ Cfg::All(cfgs) => {
+ write!(f, "all(")?;
+ for (index, cfg) in cfgs.iter().enumerate() {
+ if index > 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "{}", cfg)?;
+ }
+ write!(f, ")")
+ }
+ Cfg::Not(cfg) => write!(f, "not({})", cfg),
+ }
+ }
+}
+
+impl Cfg {
+ pub fn join(cfgs: &[Cfg]) -> Option<Cfg> {
+ if cfgs.is_empty() {
+ None
+ } else {
+ Some(Cfg::All(cfgs.to_owned()))
+ }
+ }
+
+ pub fn append(parent: Option<&Cfg>, child: Option<Cfg>) -> Option<Cfg> {
+ match (parent, child) {
+ (None, None) => None,
+ (None, Some(child)) => Some(child),
+ (Some(parent), None) => Some(parent.clone()),
+ (Some(parent), Some(child)) => Some(Cfg::All(vec![parent.clone(), child])),
+ }
+ }
+
+ pub fn load(attrs: &[syn::Attribute]) -> Option<Cfg> {
+ let mut configs = Vec::new();
+
+ for attr in attrs {
+ if let Ok(syn::Meta::List(syn::MetaList { path, nested, .. })) = attr.parse_meta() {
+ if !path.is_ident("cfg") || nested.len() != 1 {
+ continue;
+ }
+
+ if let Some(config) = Cfg::load_single(nested.first().unwrap()) {
+ configs.push(config);
+ }
+ }
+ }
+
+ match configs.len() {
+ 0 => None,
+ 1 => Some(configs.pop().unwrap()),
+ _ => Some(Cfg::All(configs)),
+ }
+ }
+
+ pub fn load_metadata(dependency: &Dependency) -> Option<Cfg> {
+ let target = dependency.target.as_ref()?;
+ match syn::parse_str::<syn::Meta>(target) {
+ Ok(target) => {
+ // Parsing succeeded using the #[cfg] syntax
+ if let syn::Meta::List(syn::MetaList { path, nested, .. }) = target {
+ if !path.is_ident("cfg") || nested.len() != 1 {
+ return None;
+ }
+ Cfg::load_single(nested.first().unwrap())
+ } else {
+ None
+ }
+ }
+ Err(_) => {
+ // Parsing failed using #[cfg], this may be a literal target
+ // name
+ Cfg::load_single(&syn::NestedMeta::Lit(syn::Lit::Str(syn::LitStr::new(
+ target,
+ proc_macro2::Span::call_site(),
+ ))))
+ }
+ }
+ }
+
+ fn load_single(item: &syn::NestedMeta) -> Option<Cfg> {
+ Some(match *item {
+ syn::NestedMeta::Meta(syn::Meta::Path(ref path)) => {
+ Cfg::Boolean(format!("{}", path.segments.first().unwrap().ident))
+ }
+ syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
+ ref path,
+ lit: syn::Lit::Str(ref value),
+ ..
+ })) => Cfg::Named(
+ format!("{}", path.segments.first().unwrap().ident),
+ value.value(),
+ ),
+ syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList {
+ ref path,
+ ref nested,
+ ..
+ })) => {
+ if path.is_ident("any") {
+ Cfg::Any(Cfg::load_list(nested.iter())?)
+ } else if path.is_ident("all") {
+ Cfg::All(Cfg::load_list(nested.iter())?)
+ } else if path.is_ident("not") {
+ if nested.len() != 1 {
+ return None;
+ }
+
+ Cfg::Not(Box::new(Cfg::load_single(&nested[0])?))
+ } else {
+ return None;
+ }
+ }
+ _ => return None,
+ })
+ }
+
+ fn load_list<'a, I: Iterator<Item = &'a syn::NestedMeta>>(attrs: I) -> Option<Vec<Cfg>> {
+ let mut configs = Vec::new();
+
+ for attr in attrs {
+ configs.push(Cfg::load_single(attr)?);
+ }
+
+ if configs.is_empty() {
+ None
+ } else {
+ Some(configs)
+ }
+ }
+}
+
+pub trait ToCondition: Sized {
+ fn to_condition(&self, config: &Config) -> Option<Condition>;
+}
+
+impl ToCondition for Option<Cfg> {
+ fn to_condition(&self, config: &Config) -> Option<Condition> {
+ self.as_ref()?.to_condition(config)
+ }
+}
+
+impl ToCondition for Cfg {
+ fn to_condition(&self, config: &Config) -> Option<Condition> {
+ match *self {
+ Cfg::Boolean(ref cfg_name) => {
+ let define = config
+ .defines
+ .iter()
+ .find(|(key, ..)| DefineKey::Boolean(cfg_name) == DefineKey::load(key));
+ if let Some((_, define)) = define {
+ Some(Condition::Define(define.to_owned()))
+ } else {
+ warn!(
+ "Missing `[defines]` entry for `{}` in cbindgen config.",
+ self,
+ );
+ None
+ }
+ }
+ Cfg::Named(ref cfg_name, ref cfg_value) => {
+ let define = config.defines.iter().find(|(key, ..)| {
+ DefineKey::Named(cfg_name, cfg_value) == DefineKey::load(key)
+ });
+ if let Some((_, define)) = define {
+ Some(Condition::Define(define.to_owned()))
+ } else {
+ warn!(
+ "Missing `[defines]` entry for `{}` in cbindgen config.",
+ self,
+ );
+ None
+ }
+ }
+ Cfg::Any(ref children) => {
+ let conditions: Vec<_> = children
+ .iter()
+ .filter_map(|x| x.to_condition(config))
+ .collect();
+ match conditions.len() {
+ 0 => None,
+ 1 => conditions.into_iter().next(),
+ _ => Some(Condition::Any(conditions)),
+ }
+ }
+ Cfg::All(ref children) => {
+ let cfgs: Vec<_> = children
+ .iter()
+ .filter_map(|x| x.to_condition(config))
+ .collect();
+ match cfgs.len() {
+ 0 => None,
+ 1 => cfgs.into_iter().next(),
+ _ => Some(Condition::All(cfgs)),
+ }
+ }
+ Cfg::Not(ref child) => child
+ .to_condition(config)
+ .map(|cfg| Condition::Not(Box::new(cfg))),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum Condition {
+ Define(String),
+ Any(Vec<Condition>),
+ All(Vec<Condition>),
+ Not(Box<Condition>),
+}
+
+impl Condition {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ match *self {
+ Condition::Define(ref define) => {
+ if config.language == Language::Cython {
+ write!(out, "{}", define);
+ } else {
+ out.write("defined(");
+ write!(out, "{}", define);
+ out.write(")");
+ }
+ }
+ Condition::Any(ref conditions) => {
+ out.write("(");
+ for (i, condition) in conditions.iter().enumerate() {
+ if i != 0 {
+ out.write(if config.language == Language::Cython {
+ " or "
+ } else {
+ " || "
+ });
+ }
+ condition.write(config, out);
+ }
+ out.write(")");
+ }
+ Condition::All(ref conditions) => {
+ out.write("(");
+ for (i, condition) in conditions.iter().enumerate() {
+ if i != 0 {
+ out.write(if config.language == Language::Cython {
+ " and "
+ } else {
+ " && "
+ });
+ }
+ condition.write(config, out);
+ }
+ out.write(")");
+ }
+ Condition::Not(ref condition) => {
+ out.write(if config.language == Language::Cython {
+ "not "
+ } else {
+ "!"
+ });
+ condition.write(config, out);
+ }
+ }
+ }
+}
+
+pub trait ConditionWrite {
+ fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
+ fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
+}
+
+impl ConditionWrite for Option<Condition> {
+ fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ if let Some(ref cfg) = *self {
+ if config.language == Language::Cython {
+ out.write("IF ");
+ cfg.write(config, out);
+ out.open_brace();
+ } else {
+ out.push_set_spaces(0);
+ out.write("#if ");
+ cfg.write(config, out);
+ out.pop_set_spaces();
+ out.new_line();
+ }
+ }
+ }
+
+ fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ if self.is_some() {
+ if config.language == Language::Cython {
+ out.close_brace(false);
+ } else {
+ out.new_line();
+ out.push_set_spaces(0);
+ out.write("#endif");
+ out.pop_set_spaces();
+ }
+ }
+ }
+}
diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs
new file mode 100644
index 0000000..d3b9bd4
--- /dev/null
+++ b/src/bindgen/ir/constant.rs
@@ -0,0 +1,813 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::borrow::Cow;
+use std::collections::HashMap;
+use std::io::Write;
+
+use syn::ext::IdentExt;
+use syn::{self, UnOp};
+
+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,
+ Struct, ToCondition, Type,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::writer::{Source, SourceWriter};
+use crate::bindgen::Bindings;
+
+fn member_to_ident(member: &syn::Member) -> String {
+ match member {
+ syn::Member::Named(ref name) => name.unraw().to_string(),
+ syn::Member::Unnamed(ref index) => format!("_{}", index.index),
+ }
+}
+
+// TODO: Maybe add support to more std associated constants.
+fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option<String> {
+ use crate::bindgen::ir::{IntKind, PrimitiveType};
+
+ if name != "MAX" && name != "MIN" {
+ return None;
+ }
+
+ let prim = PrimitiveType::maybe(associated_to.name())?;
+ let prefix = match prim {
+ PrimitiveType::Integer {
+ kind,
+ signed,
+ zeroable: _,
+ } => match kind {
+ IntKind::B8 => {
+ if signed {
+ "INT8"
+ } else {
+ "UINT8"
+ }
+ }
+ IntKind::B16 => {
+ if signed {
+ "INT16"
+ } else {
+ "UINT16"
+ }
+ }
+ IntKind::B32 => {
+ if signed {
+ "INT32"
+ } else {
+ "UINT32"
+ }
+ }
+ IntKind::B64 => {
+ if signed {
+ "INT64"
+ } else {
+ "UINT64"
+ }
+ }
+ _ => return None,
+ },
+ _ => return None,
+ };
+ Some(format!("{}_{}", prefix, name))
+}
+
+#[derive(Debug, Clone)]
+pub enum Literal {
+ Expr(String),
+ Path {
+ associated_to: Option<(Path, String)>,
+ name: String,
+ },
+ PostfixUnaryOp {
+ op: &'static str,
+ value: Box<Literal>,
+ },
+ BinOp {
+ left: Box<Literal>,
+ op: &'static str,
+ right: Box<Literal>,
+ },
+ FieldAccess {
+ base: Box<Literal>,
+ field: String,
+ },
+ Struct {
+ path: Path,
+ export_name: String,
+ fields: HashMap<String, Literal>,
+ },
+ Cast {
+ ty: Type,
+ value: Box<Literal>,
+ },
+}
+
+impl Literal {
+ fn replace_self_with(&mut self, self_ty: &Path) {
+ match *self {
+ Literal::PostfixUnaryOp { ref mut value, .. } => {
+ value.replace_self_with(self_ty);
+ }
+ Literal::BinOp {
+ ref mut left,
+ ref mut right,
+ ..
+ } => {
+ left.replace_self_with(self_ty);
+ right.replace_self_with(self_ty);
+ }
+ Literal::FieldAccess { ref mut base, .. } => {
+ base.replace_self_with(self_ty);
+ }
+ Literal::Struct {
+ ref mut path,
+ ref mut export_name,
+ ref mut fields,
+ } => {
+ if path.replace_self_with(self_ty) {
+ *export_name = self_ty.name().to_owned();
+ }
+ for ref mut expr in fields.values_mut() {
+ expr.replace_self_with(self_ty);
+ }
+ }
+ Literal::Cast {
+ ref mut ty,
+ ref mut value,
+ } => {
+ ty.replace_self_with(self_ty);
+ value.replace_self_with(self_ty);
+ }
+ Literal::Path {
+ ref mut associated_to,
+ ..
+ } => {
+ if let Some((ref mut path, ref mut export_name)) = *associated_to {
+ if path.replace_self_with(self_ty) {
+ *export_name = self_ty.name().to_owned();
+ }
+ }
+ }
+ Literal::Expr(..) => {}
+ }
+ }
+
+ fn is_valid(&self, bindings: &Bindings) -> bool {
+ match *self {
+ Literal::Expr(..) => true,
+ Literal::Path {
+ ref associated_to,
+ ref name,
+ } => {
+ if let Some((ref path, _export_name)) = associated_to {
+ return bindings.struct_exists(path)
+ || to_known_assoc_constant(path, name).is_some();
+ }
+ true
+ }
+ Literal::PostfixUnaryOp { ref value, .. } => value.is_valid(bindings),
+ Literal::BinOp {
+ ref left,
+ ref right,
+ ..
+ } => left.is_valid(bindings) && right.is_valid(bindings),
+ Literal::FieldAccess { ref base, .. } => base.is_valid(bindings),
+ Literal::Struct { ref path, .. } => bindings.struct_exists(path),
+ Literal::Cast { ref value, .. } => value.is_valid(bindings),
+ }
+ }
+
+ fn can_be_constexpr(&self) -> bool {
+ !self.has_pointer_casts()
+ }
+
+ fn visit(&self, visitor: &mut impl FnMut(&Self) -> bool) -> bool {
+ if !visitor(self) {
+ return false;
+ }
+ match self {
+ Literal::Expr(..) | Literal::Path { .. } => true,
+ Literal::PostfixUnaryOp { ref value, .. } => value.visit(visitor),
+ Literal::BinOp {
+ ref left,
+ ref right,
+ ..
+ } => left.visit(visitor) && right.visit(visitor),
+ Literal::FieldAccess { ref base, .. } => base.visit(visitor),
+ Literal::Struct { ref fields, .. } => {
+ for (_name, field) in fields.iter() {
+ if !field.visit(visitor) {
+ return false;
+ }
+ }
+ true
+ }
+ Literal::Cast { ref value, .. } => value.visit(visitor),
+ }
+ }
+
+ fn has_pointer_casts(&self) -> bool {
+ let mut has_pointer_casts = false;
+ self.visit(&mut |lit| {
+ if let Literal::Cast { ref ty, .. } = *lit {
+ has_pointer_casts = has_pointer_casts || ty.is_ptr();
+ }
+ !has_pointer_casts
+ });
+ has_pointer_casts
+ }
+
+ pub fn uses_only_primitive_types(&self) -> bool {
+ let mut uses_only_primitive_types = true;
+ self.visit(&mut |lit| {
+ // XXX This is a bit sketchy, but alas.
+ uses_only_primitive_types = uses_only_primitive_types
+ && match *lit {
+ Literal::Struct { .. } => false,
+ Literal::Cast { ref ty, .. } => ty.is_primitive_or_ptr_primitive(),
+ _ => true,
+ };
+ uses_only_primitive_types
+ });
+ uses_only_primitive_types
+ }
+}
+
+impl Literal {
+ pub fn rename_for_config(&mut self, config: &Config) {
+ match self {
+ Literal::Struct {
+ ref mut export_name,
+ fields,
+ ..
+ } => {
+ config.export.rename(export_name);
+ for lit in fields.values_mut() {
+ lit.rename_for_config(config);
+ }
+ }
+ Literal::FieldAccess { ref mut base, .. } => {
+ base.rename_for_config(config);
+ }
+ Literal::Path {
+ ref mut associated_to,
+ ref mut name,
+ } => {
+ if let Some((_path, ref mut export_name)) = associated_to {
+ config.export.rename(export_name);
+ } else {
+ config.export.rename(name);
+ }
+ }
+ Literal::PostfixUnaryOp { ref mut value, .. } => {
+ value.rename_for_config(config);
+ }
+ Literal::BinOp {
+ ref mut left,
+ ref mut right,
+ ..
+ } => {
+ left.rename_for_config(config);
+ right.rename_for_config(config);
+ }
+ Literal::Expr(_) => {}
+ Literal::Cast {
+ ref mut ty,
+ ref mut value,
+ } => {
+ ty.rename_for_config(config, &GenericParams::default());
+ value.rename_for_config(config);
+ }
+ }
+ }
+
+ // Translate from full blown `syn::Expr` into a simpler `Literal` type
+ pub fn load(expr: &syn::Expr) -> Result<Literal, String> {
+ match *expr {
+ // Match binary expressions of the form `a * b`
+ syn::Expr::Binary(ref bin_expr) => {
+ let l = Self::load(&bin_expr.left)?;
+ let r = Self::load(&bin_expr.right)?;
+ let op = match bin_expr.op {
+ syn::BinOp::Add(..) => "+",
+ syn::BinOp::Sub(..) => "-",
+ syn::BinOp::Mul(..) => "*",
+ syn::BinOp::Div(..) => "/",
+ syn::BinOp::Rem(..) => "%",
+ syn::BinOp::And(..) => "&&",
+ syn::BinOp::Or(..) => "||",
+ syn::BinOp::BitXor(..) => "^",
+ syn::BinOp::BitAnd(..) => "&",
+ syn::BinOp::BitOr(..) => "|",
+ syn::BinOp::Shl(..) => "<<",
+ syn::BinOp::Shr(..) => ">>",
+ syn::BinOp::Eq(..) => "==",
+ syn::BinOp::Lt(..) => "<",
+ syn::BinOp::Le(..) => "<=",
+ syn::BinOp::Ne(..) => "!=",
+ syn::BinOp::Ge(..) => ">=",
+ syn::BinOp::Gt(..) => ">",
+ syn::BinOp::AddEq(..) => "+=",
+ syn::BinOp::SubEq(..) => "-=",
+ syn::BinOp::MulEq(..) => "*=",
+ syn::BinOp::DivEq(..) => "/=",
+ syn::BinOp::RemEq(..) => "%=",
+ syn::BinOp::BitXorEq(..) => "^=",
+ syn::BinOp::BitAndEq(..) => "&=",
+ syn::BinOp::BitOrEq(..) => "|=",
+ syn::BinOp::ShlEq(..) => "<<=",
+ syn::BinOp::ShrEq(..) => ">>=",
+ };
+ Ok(Literal::BinOp {
+ left: Box::new(l),
+ op,
+ right: Box::new(r),
+ })
+ }
+
+ // Match literals like true, 'a', 32 etc
+ syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
+ match lit {
+ syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!("{}", value.value()))),
+ syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 {
+ 0..=255 => format!("'{}'", value.value().escape_default()),
+ other_code => format!(r"U'\U{:08X}'", other_code),
+ })),
+ syn::Lit::Int(ref value) => {
+ let suffix = match value.suffix() {
+ "u64" => "ull",
+ "i64" => "ll",
+ "u32" => "u",
+ _ if value.base10_parse::<i64>().is_err() => "ull",
+ _ => "",
+ };
+ Ok(Literal::Expr(format!(
+ "{}{}",
+ value.base10_digits(),
+ suffix
+ )))
+ }
+ syn::Lit::Float(ref value) => {
+ Ok(Literal::Expr(value.base10_digits().to_string()))
+ }
+ syn::Lit::Bool(ref value) => Ok(Literal::Expr(format!("{}", value.value))),
+ // TODO: Add support for byte string and Verbatim
+ _ => Err(format!("Unsupported literal expression. {:?}", *lit)),
+ }
+ }
+
+ syn::Expr::Field(syn::ExprField {
+ ref base,
+ ref member,
+ ..
+ }) => Ok(Literal::FieldAccess {
+ base: Box::new(Literal::load(base)?),
+ field: member_to_ident(member),
+ }),
+
+ syn::Expr::Call(syn::ExprCall {
+ ref func, ref args, ..
+ }) => {
+ let struct_name = match Literal::load(func)? {
+ Literal::Path {
+ associated_to: None,
+ name,
+ } => name,
+ _ => return Err(format!("Unsupported call expression. {:?}", *expr)),
+ };
+ let mut fields = HashMap::<String, Literal>::default();
+ for (index, arg) in args.iter().enumerate() {
+ let ident =
+ member_to_ident(&syn::Member::Unnamed(syn::Index::from(index))).to_string();
+ let value = Literal::load(arg)?;
+ fields.insert(ident, value);
+ }
+ Ok(Literal::Struct {
+ path: Path::new(struct_name.clone()),
+ export_name: struct_name,
+ fields,
+ })
+ }
+
+ syn::Expr::Struct(syn::ExprStruct {
+ ref path,
+ ref fields,
+ ..
+ }) => {
+ let struct_name = path.segments[0].ident.unraw().to_string();
+ let mut field_map = HashMap::<String, Literal>::default();
+ for field in fields {
+ let ident = member_to_ident(&field.member).to_string();
+ let value = Literal::load(&field.expr)?;
+ field_map.insert(ident, value);
+ }
+ Ok(Literal::Struct {
+ path: Path::new(struct_name.clone()),
+ export_name: struct_name,
+ fields: field_map,
+ })
+ }
+
+ syn::Expr::Unary(syn::ExprUnary {
+ ref op, ref expr, ..
+ }) => match *op {
+ UnOp::Not(_) => {
+ let val = Self::load(expr)?;
+ Ok(Literal::PostfixUnaryOp {
+ op: "~",
+ value: Box::new(val),
+ })
+ }
+ UnOp::Neg(_) => {
+ let val = Self::load(expr)?;
+ Ok(Literal::PostfixUnaryOp {
+ op: "-",
+ value: Box::new(val),
+ })
+ }
+ _ => Err(format!("Unsupported Unary expression. {:?}", *op)),
+ },
+
+ // Match identifiers, like `5 << SHIFT`
+ syn::Expr::Path(syn::ExprPath { ref path, .. }) => {
+ // Handle only the simplest identifiers and Associated::IDENT
+ // kind of syntax.
+ Ok(match path.segments.len() {
+ 1 => Literal::Path {
+ associated_to: None,
+ name: path.segments[0].ident.to_string(),
+ },
+ 2 => {
+ let struct_name = path.segments[0].ident.to_string();
+ Literal::Path {
+ associated_to: Some((Path::new(&struct_name), struct_name)),
+ name: path.segments[1].ident.to_string(),
+ }
+ }
+ _ => return Err(format!("Unsupported path expression. {:?}", path)),
+ })
+ }
+
+ syn::Expr::Paren(syn::ExprParen { ref expr, .. }) => Self::load(expr),
+
+ syn::Expr::Cast(syn::ExprCast {
+ ref expr, ref ty, ..
+ }) => {
+ let val = Self::load(expr)?;
+ match Type::load(ty)? {
+ Some(ty) => Ok(Literal::Cast {
+ ty,
+ value: Box::new(val),
+ }),
+ None => Err("Cannot cast to zero sized type.".to_owned()),
+ }
+ }
+
+ _ => Err(format!("Unsupported expression. {:?}", *expr)),
+ }
+ }
+
+ pub(crate) fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ match self {
+ Literal::Expr(v) => match (&**v, config.language) {
+ ("true", Language::Cython) => write!(out, "True"),
+ ("false", Language::Cython) => write!(out, "False"),
+ (v, _) => write!(out, "{}", v),
+ },
+ Literal::Path {
+ ref associated_to,
+ ref name,
+ } => {
+ if let Some((ref path, ref export_name)) = associated_to {
+ if let Some(known) = to_known_assoc_constant(path, name) {
+ return write!(out, "{}", known);
+ }
+ let path_separator = match config.language {
+ Language::Cython | Language::C => "_",
+ Language::Cxx => {
+ if config.structure.associated_constants_in_body {
+ "::"
+ } else {
+ "_"
+ }
+ }
+ };
+ write!(out, "{}{}", export_name, path_separator)
+ }
+ write!(out, "{}", name)
+ }
+ Literal::FieldAccess {
+ ref base,
+ ref field,
+ } => {
+ write!(out, "(");
+ base.write(config, out);
+ write!(out, ").{}", field);
+ }
+ Literal::PostfixUnaryOp { op, ref value } => {
+ write!(out, "{}", op);
+ value.write(config, out);
+ }
+ Literal::BinOp {
+ ref left,
+ op,
+ ref right,
+ } => {
+ write!(out, "(");
+ left.write(config, out);
+ write!(out, " {} ", op);
+ right.write(config, out);
+ write!(out, ")");
+ }
+ Literal::Cast { ref ty, ref value } => {
+ out.write(if config.language == Language::Cython {
+ "<"
+ } else {
+ "("
+ });
+ ty.write(config, out);
+ out.write(if config.language == Language::Cython {
+ ">"
+ } else {
+ ")"
+ });
+ value.write(config, out);
+ }
+ Literal::Struct {
+ export_name,
+ fields,
+ path,
+ } => {
+ match config.language {
+ Language::C => write!(out, "({})", export_name),
+ Language::Cxx => write!(out, "{}", export_name),
+ Language::Cython => write!(out, "<{}>", export_name),
+ }
+
+ write!(out, "{{ ");
+ let mut is_first_field = true;
+ // In C++, same order as defined is required.
+ let ordered_fields = out.bindings().struct_field_names(path);
+ for ordered_key in ordered_fields.iter() {
+ if let Some(lit) = fields.get(ordered_key) {
+ if !is_first_field {
+ write!(out, ", ");
+ } else {
+ is_first_field = false;
+ }
+ match config.language {
+ Language::Cxx => write!(out, "/* .{} = */ ", ordered_key),
+ Language::C => write!(out, ".{} = ", ordered_key),
+ Language::Cython => {}
+ }
+ lit.write(config, out);
+ }
+ }
+ write!(out, " }}");
+ }
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Constant {
+ pub path: Path,
+ pub export_name: String,
+ pub ty: Type,
+ pub value: Literal,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+ pub associated_to: Option<Path>,
+}
+
+impl Constant {
+ pub fn load(
+ path: Path,
+ mod_cfg: Option<&Cfg>,
+ ty: &syn::Type,
+ expr: &syn::Expr,
+ attrs: &[syn::Attribute],
+ associated_to: Option<Path>,
+ ) -> Result<Constant, String> {
+ let ty = Type::load(ty)?;
+ let mut ty = match ty {
+ Some(ty) => ty,
+ None => {
+ return Err("Cannot have a zero sized const definition.".to_owned());
+ }
+ };
+
+ let mut lit = Literal::load(expr)?;
+
+ if let Some(ref associated_to) = associated_to {
+ ty.replace_self_with(associated_to);
+ lit.replace_self_with(associated_to);
+ }
+
+ Ok(Constant::new(
+ path,
+ ty,
+ lit,
+ Cfg::append(mod_cfg, Cfg::load(attrs)),
+ AnnotationSet::load(attrs)?,
+ Documentation::load(attrs),
+ associated_to,
+ ))
+ }
+
+ pub fn new(
+ path: Path,
+ ty: Type,
+ value: Literal,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ associated_to: Option<Path>,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ ty,
+ value,
+ cfg,
+ annotations,
+ documentation,
+ associated_to,
+ }
+ }
+
+ pub fn uses_only_primitive_types(&self) -> bool {
+ self.value.uses_only_primitive_types() && self.ty.is_primitive_or_ptr_primitive()
+ }
+}
+
+impl Item for Constant {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ self.ty.add_dependencies(library, out);
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Constant(self.clone())
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ if self.associated_to.is_none() {
+ config.export.rename(&mut self.export_name);
+ }
+ self.value.rename_for_config(config);
+ self.ty.rename_for_config(config, &GenericParams::default()); // FIXME: should probably propagate something here
+ }
+
+ fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ self.ty.resolve_declaration_types(resolver);
+ }
+}
+
+impl Constant {
+ pub fn write_declaration<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ associated_to_struct: &Struct,
+ ) {
+ debug_assert!(self.associated_to.is_some());
+ debug_assert!(config.language == Language::Cxx);
+ debug_assert!(!associated_to_struct.is_transparent);
+ debug_assert!(config.structure.associated_constants_in_body);
+ debug_assert!(config.constant.allow_static_const);
+
+ if let Type::Ptr { is_const: true, .. } = self.ty {
+ out.write("static ");
+ } else {
+ out.write("static const ");
+ }
+ self.ty.write(config, out);
+ write!(out, " {};", self.export_name())
+ }
+
+ pub fn write<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ associated_to_struct: Option<&Struct>,
+ ) {
+ if let Some(assoc) = associated_to_struct {
+ if assoc.is_generic() {
+ return; // Not tested / implemented yet, so bail out.
+ }
+ }
+
+ if !self.value.is_valid(out.bindings()) {
+ return;
+ }
+
+ let associated_to_transparent = associated_to_struct.map_or(false, |s| s.is_transparent);
+
+ let in_body = associated_to_struct.is_some()
+ && config.language == Language::Cxx
+ && config.structure.associated_constants_in_body
+ && config.constant.allow_static_const
+ && !associated_to_transparent;
+
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ let name = if in_body {
+ Cow::Owned(format!(
+ "{}::{}",
+ associated_to_struct.unwrap().export_name(),
+ self.export_name(),
+ ))
+ } else if self.associated_to.is_none() {
+ Cow::Borrowed(self.export_name())
+ } else {
+ let associated_name = match associated_to_struct {
+ Some(s) => Cow::Borrowed(s.export_name()),
+ None => {
+ let mut name = self.associated_to.as_ref().unwrap().name().to_owned();
+ config.export.rename(&mut name);
+ Cow::Owned(name)
+ }
+ };
+
+ Cow::Owned(format!("{}_{}", associated_name, self.export_name()))
+ };
+
+ let value = match self.value {
+ Literal::Struct {
+ ref fields,
+ ref path,
+ ..
+ } if out.bindings().struct_is_transparent(path) => fields.iter().next().unwrap().1,
+ _ => &self.value,
+ };
+
+ self.documentation.write(config, out);
+
+ let allow_constexpr = config.constant.allow_constexpr && self.value.can_be_constexpr();
+ match config.language {
+ Language::Cxx if config.constant.allow_static_const || allow_constexpr => {
+ if allow_constexpr {
+ out.write("constexpr ")
+ }
+
+ if config.constant.allow_static_const {
+ out.write(if in_body { "inline " } else { "static " });
+ }
+
+ if let Type::Ptr { is_const: true, .. } = self.ty {
+ // Nothing.
+ } else {
+ out.write("const ");
+ }
+
+ self.ty.write(config, out);
+ write!(out, " {} = ", name);
+ value.write(config, out);
+ write!(out, ";");
+ }
+ Language::Cxx | Language::C => {
+ write!(out, "#define {} ", name);
+ value.write(config, out);
+ }
+ Language::Cython => {
+ out.write("const ");
+ self.ty.write(config, out);
+ // For extern Cython declarations the initializer is ignored,
+ // but still useful as documentation, so we write it as a comment.
+ write!(out, " {} # = ", name);
+ value.write(config, out);
+ }
+ }
+
+ condition.write_after(config, out);
+ }
+}
diff --git a/src/bindgen/ir/documentation.rs b/src/bindgen/ir/documentation.rs
new file mode 100644
index 0000000..6822c0e
--- /dev/null
+++ b/src/bindgen/ir/documentation.rs
@@ -0,0 +1,111 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::io::Write;
+
+use crate::bindgen::config::{Config, DocumentationLength, DocumentationStyle, Language};
+use crate::bindgen::utilities::SynAttributeHelpers;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct Documentation {
+ pub doc_comment: Vec<String>,
+}
+
+impl Documentation {
+ pub fn load(attrs: &[syn::Attribute]) -> Self {
+ let doc = attrs
+ .get_comment_lines()
+ .into_iter()
+ .filter(|x| !x.trim_start().starts_with("cbindgen:"))
+ .collect();
+
+ Documentation { doc_comment: doc }
+ }
+
+ pub fn simple(line: &str) -> Self {
+ Documentation {
+ doc_comment: vec![line.to_owned()],
+ }
+ }
+
+ pub fn none() -> Self {
+ Documentation {
+ doc_comment: Vec::new(),
+ }
+ }
+}
+
+impl Source for Documentation {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ if self.doc_comment.is_empty() || !config.documentation {
+ return;
+ }
+
+ let end = match config.documentation_length {
+ DocumentationLength::Short => 1,
+ DocumentationLength::Full => self.doc_comment.len(),
+ };
+
+ // Cython uses Python-style comments, so `documentation_style` is not relevant.
+ if config.language == Language::Cython {
+ for line in &self.doc_comment[..end] {
+ write!(out, "#{}", line);
+ out.new_line();
+ }
+ return;
+ }
+
+ let style = match config.documentation_style {
+ DocumentationStyle::Auto if config.language == Language::C => DocumentationStyle::Doxy,
+ DocumentationStyle::Auto if config.language == Language::Cxx => DocumentationStyle::Cxx,
+ DocumentationStyle::Auto => DocumentationStyle::C, // Fallback if `Language` gets extended.
+ other => other,
+ };
+
+ // Following these documents for style conventions:
+ // https://en.wikibooks.org/wiki/C++_Programming/Code/Style_Conventions/Comments
+ // https://www.cs.cmu.edu/~410/doc/doxygen.html
+ match style {
+ DocumentationStyle::C => {
+ out.write("/*");
+ out.new_line();
+ }
+
+ DocumentationStyle::Doxy => {
+ out.write("/**");
+ out.new_line();
+ }
+
+ _ => (),
+ }
+
+ for line in &self.doc_comment[..end] {
+ match style {
+ DocumentationStyle::C => out.write(""),
+ DocumentationStyle::Doxy => out.write(" *"),
+ DocumentationStyle::C99 => out.write("//"),
+ DocumentationStyle::Cxx => out.write("///"),
+ DocumentationStyle::Auto => unreachable!(), // Auto case should always be covered
+ }
+
+ write!(out, "{}", line);
+ out.new_line();
+ }
+
+ match style {
+ DocumentationStyle::C => {
+ out.write(" */");
+ out.new_line();
+ }
+
+ DocumentationStyle::Doxy => {
+ out.write(" */");
+ out.new_line();
+ }
+
+ _ => (),
+ }
+ }
+}
diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs
new file mode 100644
index 0000000..a456b76
--- /dev/null
+++ b/src/bindgen/ir/enumeration.rs
@@ -0,0 +1,1556 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::io::Write;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, AnnotationValue, Cfg, ConditionWrite, DeprecatedNoteKind, Documentation, Field,
+ GenericArgument, GenericParams, GenericPath, Item, ItemContainer, Literal, Path, Repr,
+ ReprStyle, Struct, ToCondition, Type,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::mangle;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::rename::{IdentifierType, RenameRule};
+use crate::bindgen::reserved;
+use crate::bindgen::writer::{ListType, Source, SourceWriter};
+
+#[allow(clippy::large_enum_variant)]
+#[derive(Debug, Clone)]
+pub enum VariantBody {
+ Empty(AnnotationSet),
+ Body {
+ /// The variant field / export name.
+ name: String,
+ /// The struct with all the items.
+ body: Struct,
+ /// A separate named struct is not created for this variant,
+ /// an unnamed struct is inlined at the point of use instead.
+ /// This is a reasonable thing to do only for tuple variants with a single field.
+ inline: bool,
+ /// Generated cast methods return the variant's only field instead of the variant itself.
+ /// For backward compatibility casts are inlined in a slightly
+ /// larger set of cases than whole variants.
+ inline_casts: bool,
+ },
+}
+
+impl VariantBody {
+ fn empty() -> Self {
+ Self::Empty(AnnotationSet::new())
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ match *self {
+ Self::Empty(ref anno) => anno,
+ Self::Body { ref body, .. } => &body.annotations,
+ }
+ }
+
+ fn is_empty(&self) -> bool {
+ match *self {
+ Self::Empty(..) => true,
+ Self::Body { .. } => false,
+ }
+ }
+
+ fn specialize(
+ &self,
+ generic_values: &[GenericArgument],
+ mappings: &[(&Path, &GenericArgument)],
+ config: &Config,
+ ) -> Self {
+ match *self {
+ Self::Empty(ref annos) => Self::Empty(annos.clone()),
+ Self::Body {
+ ref name,
+ ref body,
+ inline,
+ inline_casts,
+ } => Self::Body {
+ name: name.clone(),
+ body: body.specialize(generic_values, mappings, config),
+ inline,
+ inline_casts,
+ },
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct EnumVariant {
+ pub name: String,
+ pub export_name: String,
+ pub discriminant: Option<Literal>,
+ pub body: VariantBody,
+ pub cfg: Option<Cfg>,
+ pub documentation: Documentation,
+}
+
+impl EnumVariant {
+ fn load(
+ inline_tag_field: bool,
+ variant: &syn::Variant,
+ generic_params: GenericParams,
+ mod_cfg: Option<&Cfg>,
+ self_path: &Path,
+ enum_annotations: &AnnotationSet,
+ config: &Config,
+ ) -> Result<Self, String> {
+ let discriminant = match variant.discriminant {
+ Some((_, ref expr)) => Some(Literal::load(expr)?),
+ None => None,
+ };
+
+ fn parse_fields(
+ inline_tag_field: bool,
+ fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>,
+ self_path: &Path,
+ inline_name: Option<&str>,
+ ) -> Result<Vec<Field>, String> {
+ let mut res = Vec::new();
+
+ if inline_tag_field {
+ res.push(Field::from_name_and_type(
+ inline_name.map_or_else(|| "tag".to_string(), |name| format!("{}_tag", name)),
+ Type::Path(GenericPath::new(Path::new("Tag"), vec![])),
+ ));
+ }
+
+ for (i, field) in fields.iter().enumerate() {
+ if let Some(mut ty) = Type::load(&field.ty)? {
+ ty.replace_self_with(self_path);
+ res.push(Field {
+ name: inline_name.map_or_else(
+ || match field.ident {
+ Some(ref ident) => ident.unraw().to_string(),
+ None => i.to_string(),
+ },
+ |name| name.to_string(),
+ ),
+ ty,
+ cfg: Cfg::load(&field.attrs),
+ annotations: AnnotationSet::load(&field.attrs)?,
+ documentation: Documentation::load(&field.attrs),
+ });
+ }
+ }
+
+ Ok(res)
+ }
+
+ let variant_cfg = Cfg::append(mod_cfg, Cfg::load(&variant.attrs));
+ let mut annotations = AnnotationSet::load(&variant.attrs)?;
+ if let Some(b) = enum_annotations.bool("derive-ostream") {
+ annotations.add_default("derive-ostream", AnnotationValue::Bool(b));
+ }
+
+ let body_rule = enum_annotations
+ .parse_atom::<RenameRule>("rename-variant-name-fields")
+ .unwrap_or(config.enumeration.rename_variant_name_fields);
+
+ let body = match variant.fields {
+ syn::Fields::Unit => VariantBody::Empty(annotations),
+ syn::Fields::Named(ref fields) => {
+ let path = Path::new(format!("{}_Body", variant.ident));
+ let name = body_rule
+ .apply(
+ &variant.ident.unraw().to_string(),
+ IdentifierType::StructMember,
+ )
+ .into_owned();
+ VariantBody::Body {
+ body: Struct::new(
+ path,
+ generic_params,
+ parse_fields(inline_tag_field, &fields.named, self_path, None)?,
+ inline_tag_field,
+ true,
+ None,
+ false,
+ None,
+ annotations,
+ Documentation::none(),
+ ),
+ name,
+ inline: false,
+ inline_casts: false,
+ }
+ }
+ syn::Fields::Unnamed(ref fields) => {
+ let path = Path::new(format!("{}_Body", variant.ident));
+ let name = body_rule
+ .apply(
+ &variant.ident.unraw().to_string(),
+ IdentifierType::StructMember,
+ )
+ .into_owned();
+ let inline_casts = fields.unnamed.len() == 1;
+ // In C++ types with destructors cannot be put into unnamed structs like the
+ // inlining requires, and it's hard to detect such types.
+ // Besides that for C++ we generate casts/getters that can be used instead of
+ // direct field accesses and also have a benefit of being checked.
+ // As a result we don't currently inline variant definitions in C++ mode at all.
+ let inline = inline_casts && config.language != Language::Cxx;
+ let inline_name = if inline { Some(&*name) } else { None };
+ VariantBody::Body {
+ body: Struct::new(
+ path,
+ generic_params,
+ parse_fields(inline_tag_field, &fields.unnamed, self_path, inline_name)?,
+ inline_tag_field,
+ true,
+ None,
+ false,
+ None,
+ annotations,
+ Documentation::none(),
+ ),
+ name,
+ inline,
+ inline_casts,
+ }
+ }
+ };
+
+ Ok(EnumVariant::new(
+ variant.ident.unraw().to_string(),
+ discriminant,
+ body,
+ variant_cfg,
+ Documentation::load(&variant.attrs),
+ ))
+ }
+
+ pub fn new(
+ name: String,
+ discriminant: Option<Literal>,
+ body: VariantBody,
+ cfg: Option<Cfg>,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = name.clone();
+ Self {
+ name,
+ export_name,
+ discriminant,
+ body,
+ cfg,
+ documentation,
+ }
+ }
+
+ fn simplify_standard_types(&mut self, config: &Config) {
+ if let VariantBody::Body { ref mut body, .. } = self.body {
+ body.simplify_standard_types(config);
+ }
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ if let VariantBody::Body { ref body, .. } = self.body {
+ body.add_dependencies(library, out);
+ }
+ }
+
+ fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ if let VariantBody::Body { ref mut body, .. } = self.body {
+ body.resolve_declaration_types(resolver);
+ }
+ }
+
+ fn specialize(
+ &self,
+ generic_values: &[GenericArgument],
+ mappings: &[(&Path, &GenericArgument)],
+ config: &Config,
+ ) -> Self {
+ Self::new(
+ mangle::mangle_name(&self.name, generic_values, &config.export.mangle),
+ self.discriminant.clone(),
+ self.body.specialize(generic_values, mappings, config),
+ self.cfg.clone(),
+ self.documentation.clone(),
+ )
+ }
+
+ fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ if let VariantBody::Body { ref body, .. } = self.body {
+ body.add_monomorphs(library, out);
+ }
+ }
+
+ fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ if let VariantBody::Body { ref mut body, .. } = self.body {
+ body.mangle_paths(monomorphs);
+ }
+ }
+}
+
+impl Source for EnumVariant {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ let condition = self.cfg.to_condition(config);
+ // Cython doesn't support conditional enum variants.
+ if config.language != Language::Cython {
+ condition.write_before(config, out);
+ }
+ self.documentation.write(config, out);
+ write!(out, "{}", self.export_name);
+ if let Some(discriminant) = &self.discriminant {
+ if config.language == Language::Cython {
+ // For extern Cython declarations the enumerator value is ignored,
+ // but still useful as documentation, so we write it as a comment.
+ out.write(" #")
+ }
+ out.write(" = ");
+ discriminant.write(config, out);
+ }
+ out.write(",");
+ if config.language != Language::Cython {
+ condition.write_after(config, out);
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Enum {
+ pub path: Path,
+ pub export_name: String,
+ pub generic_params: GenericParams,
+ pub repr: Repr,
+ pub variants: Vec<EnumVariant>,
+ pub tag: Option<String>,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl Enum {
+ /// Name of the generated tag enum.
+ fn tag_name(&self) -> &str {
+ self.tag.as_deref().unwrap_or_else(|| self.export_name())
+ }
+
+ /// Enum with data turns into a union of structs with each struct having its own tag field.
+ fn inline_tag_field(repr: &Repr) -> bool {
+ repr.style != ReprStyle::C
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ if self.generic_params.len() > 0 {
+ return;
+ }
+
+ for v in &self.variants {
+ v.add_monomorphs(library, out);
+ }
+ }
+
+ fn can_derive_eq(&self) -> bool {
+ if self.tag.is_none() {
+ return false;
+ }
+
+ self.variants.iter().all(|variant| match variant.body {
+ VariantBody::Empty(..) => true,
+ VariantBody::Body { ref body, .. } => body.can_derive_eq(),
+ })
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ for variant in &mut self.variants {
+ variant.mangle_paths(monomorphs);
+ }
+ }
+
+ pub fn load(
+ item: &syn::ItemEnum,
+ mod_cfg: Option<&Cfg>,
+ config: &Config,
+ ) -> Result<Enum, String> {
+ let repr = Repr::load(&item.attrs)?;
+ if repr.style == ReprStyle::Rust && repr.ty.is_none() {
+ return Err("Enum is not marked with a valid #[repr(prim)] or #[repr(C)].".to_owned());
+ }
+ // TODO: Implement translation of aligned enums.
+ if repr.align.is_some() {
+ return Err("Enum is marked with #[repr(align(...))] or #[repr(packed)].".to_owned());
+ }
+
+ let path = Path::new(item.ident.unraw().to_string());
+ let generic_params = GenericParams::load(&item.generics)?;
+
+ let mut variants = Vec::new();
+ let mut has_data = false;
+
+ let annotations = AnnotationSet::load(&item.attrs)?;
+
+ for variant in item.variants.iter() {
+ let variant = EnumVariant::load(
+ Self::inline_tag_field(&repr),
+ variant,
+ generic_params.clone(),
+ mod_cfg,
+ &path,
+ &annotations,
+ config,
+ )?;
+ has_data = has_data || !variant.body.is_empty();
+ variants.push(variant);
+ }
+
+ if let Some(names) = annotations.list("enum-trailing-values") {
+ for name in names {
+ variants.push(EnumVariant::new(
+ name,
+ None,
+ VariantBody::empty(),
+ None,
+ Documentation::none(),
+ ));
+ }
+ }
+
+ if config.enumeration.add_sentinel(&annotations) {
+ variants.push(EnumVariant::new(
+ "Sentinel".to_owned(),
+ None,
+ VariantBody::empty(),
+ None,
+ Documentation::simple(" Must be last for serialization purposes"),
+ ));
+ }
+
+ let tag = if has_data {
+ Some("Tag".to_string())
+ } else {
+ None
+ };
+
+ Ok(Enum::new(
+ path,
+ generic_params,
+ repr,
+ variants,
+ tag,
+ Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
+ annotations,
+ Documentation::load(&item.attrs),
+ ))
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ path: Path,
+ generic_params: GenericParams,
+ repr: Repr,
+ variants: Vec<EnumVariant>,
+ tag: Option<String>,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ generic_params,
+ repr,
+ variants,
+ tag,
+ cfg,
+ annotations,
+ documentation,
+ }
+ }
+}
+
+impl Item for Enum {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Enum(self.clone())
+ }
+
+ fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
+ if self.tag.is_some() {
+ if self.repr.style == ReprStyle::C {
+ resolver.add_struct(&self.path);
+ } else {
+ resolver.add_union(&self.path);
+ }
+ } else if self.repr.style == ReprStyle::C {
+ resolver.add_enum(&self.path);
+ } else {
+ // This is important to handle conflicting names with opaque items.
+ resolver.add_none(&self.path);
+ }
+ }
+
+ fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ for &mut ref mut var in &mut self.variants {
+ var.resolve_declaration_types(resolver);
+ }
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ config.export.rename(&mut self.export_name);
+
+ if config.language != Language::Cxx && self.tag.is_some() {
+ // it makes sense to always prefix Tag with type name in C
+ let new_tag = format!("{}_Tag", self.export_name);
+ if self.repr.style == ReprStyle::Rust {
+ for variant in &mut self.variants {
+ if let VariantBody::Body { ref mut body, .. } = variant.body {
+ let path = Path::new(new_tag.clone());
+ let generic_path = GenericPath::new(path, vec![]);
+ body.fields[0].ty = Type::Path(generic_path);
+ }
+ }
+ }
+ self.tag = Some(new_tag);
+ }
+
+ for variant in &mut self.variants {
+ reserved::escape(&mut variant.export_name);
+ if let Some(discriminant) = &mut variant.discriminant {
+ discriminant.rename_for_config(config);
+ }
+ if let VariantBody::Body {
+ ref mut name,
+ ref mut body,
+ ..
+ } = variant.body
+ {
+ body.rename_for_config(config);
+ reserved::escape(name);
+ }
+ }
+
+ if config.enumeration.prefix_with_name
+ || self.annotations.bool("prefix-with-name").unwrap_or(false)
+ {
+ let separator = if config.export.mangle.remove_underscores {
+ ""
+ } else {
+ "_"
+ };
+
+ for variant in &mut self.variants {
+ variant.export_name =
+ format!("{}{}{}", self.export_name, separator, variant.export_name);
+ if let VariantBody::Body { ref mut body, .. } = variant.body {
+ body.export_name =
+ format!("{}{}{}", self.export_name, separator, body.export_name());
+ }
+ }
+ }
+
+ let rules = self
+ .annotations
+ .parse_atom::<RenameRule>("rename-all")
+ .unwrap_or(config.enumeration.rename_variants);
+
+ if let Some(r) = rules.not_none() {
+ self.variants = self
+ .variants
+ .iter()
+ .map(|variant| {
+ EnumVariant::new(
+ r.apply(
+ &variant.export_name,
+ IdentifierType::EnumVariant {
+ prefix: &self.export_name,
+ },
+ )
+ .into_owned(),
+ variant.discriminant.clone(),
+ match variant.body {
+ VariantBody::Empty(..) => variant.body.clone(),
+ VariantBody::Body {
+ ref name,
+ ref body,
+ inline,
+ inline_casts,
+ } => VariantBody::Body {
+ name: r.apply(name, IdentifierType::StructMember).into_owned(),
+ body: body.clone(),
+ inline,
+ inline_casts,
+ },
+ },
+ variant.cfg.clone(),
+ variant.documentation.clone(),
+ )
+ })
+ .collect();
+ }
+ }
+
+ fn instantiate_monomorph(
+ &self,
+ generic_values: &[GenericArgument],
+ library: &Library,
+ out: &mut Monomorphs,
+ ) {
+ let mappings = self.generic_params.call(self.path.name(), generic_values);
+
+ for variant in &self.variants {
+ if let VariantBody::Body { ref body, .. } = variant.body {
+ body.instantiate_monomorph(generic_values, library, out);
+ }
+ }
+
+ let mangled_path = mangle::mangle_path(
+ &self.path,
+ generic_values,
+ &library.get_config().export.mangle,
+ );
+
+ let monomorph = Enum::new(
+ mangled_path,
+ GenericParams::default(),
+ self.repr,
+ self.variants
+ .iter()
+ .map(|v| v.specialize(generic_values, &mappings, library.get_config()))
+ .collect(),
+ self.tag.clone(),
+ self.cfg.clone(),
+ self.annotations.clone(),
+ self.documentation.clone(),
+ );
+
+ out.insert_enum(library, self, monomorph, generic_values.to_owned());
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ for variant in &self.variants {
+ variant.add_dependencies(library, out);
+ }
+ }
+}
+
+impl Source for Enum {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ let size = self.repr.ty.map(|ty| ty.to_primitive().to_repr_c(config));
+ let has_data = self.tag.is_some();
+ let inline_tag_field = Self::inline_tag_field(&self.repr);
+ let tag_name = self.tag_name();
+
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ self.documentation.write(config, out);
+ self.generic_params.write(config, out);
+
+ // If the enum has data, we need to emit a struct or union for the data
+ // and enum for the tag. C++ supports nested type definitions, so we open
+ // the struct or union here and define the tag enum inside it (*).
+ if has_data && config.language == Language::Cxx {
+ self.open_struct_or_union(config, out, inline_tag_field);
+ }
+
+ // Emit the tag enum and everything related to it.
+ self.write_tag_enum(config, out, size, has_data, tag_name);
+
+ // If the enum has data, we need to emit structs for the variants and gather them together.
+ if has_data {
+ self.write_variant_defs(config, out);
+ out.new_line();
+ out.new_line();
+
+ // Open the struct or union for the data (**), gathering all the variants with data
+ // together, unless it's C++, then we have already opened that struct/union at (*) and
+ // are currently inside it.
+ if config.language != Language::Cxx {
+ self.open_struct_or_union(config, out, inline_tag_field);
+ }
+
+ // Emit tag field that is separate from all variants.
+ self.write_tag_field(config, out, size, inline_tag_field, tag_name);
+ out.new_line();
+
+ // Open union of all variants with data, only in the non-inline tag scenario.
+ // Cython extern declarations don't manage layouts, layouts are defined entierly by the
+ // corresponding C code. So we can inline the unnamed union into the struct and get the
+ // same observable result. Moreother we have to do it because Cython doesn't support
+ // unnamed unions.
+ if !inline_tag_field && config.language != Language::Cython {
+ out.write("union");
+ out.open_brace();
+ }
+
+ // Emit fields for all variants with data.
+ self.write_variant_fields(config, out, inline_tag_field);
+
+ // Close union of all variants with data, only in the non-inline tag scenario.
+ // See the comment about Cython on `open_brace`.
+ if !inline_tag_field && config.language != Language::Cython {
+ out.close_brace(true);
+ }
+
+ // Emit convenience methods for the struct or enum for the data.
+ self.write_derived_functions_data(config, out, tag_name);
+
+ // Emit the post_body section, if relevant.
+ if let Some(body) = config.export.post_body(&self.path) {
+ out.new_line();
+ out.write_raw_block(body);
+ }
+
+ // Close the struct or union opened either at (*) or at (**).
+ if config.language == Language::C && config.style.generate_typedef() {
+ out.close_brace(false);
+ write!(out, " {};", self.export_name);
+ } else {
+ out.close_brace(true);
+ }
+ }
+
+ condition.write_after(config, out);
+ }
+}
+
+impl Enum {
+ /// Emit the tag enum and convenience methods for it.
+ /// For enums with data this is only a part of the output,
+ /// but for enums without data it's the whole output (modulo doc comments etc.).
+ fn write_tag_enum<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ size: Option<&str>,
+ has_data: bool,
+ tag_name: &str,
+ ) {
+ // Open the tag enum.
+ match config.language {
+ Language::C => {
+ if let Some(prim) = size {
+ // If we need to specify size, then we have no choice but to create a typedef,
+ // so `config.style` is not respected.
+ write!(out, "enum");
+ if let Some(note) = self
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Enum)
+ {
+ write!(out, " {}", note);
+ }
+ write!(out, " {}", tag_name);
+
+ if config.cpp_compatible_c() {
+ out.new_line();
+ out.write("#ifdef __cplusplus");
+ out.new_line();
+ write!(out, " : {}", prim);
+ out.new_line();
+ out.write("#endif // __cplusplus");
+ out.new_line();
+ }
+ } else {
+ if config.style.generate_typedef() {
+ out.write("typedef ");
+ }
+ out.write("enum");
+ if let Some(note) = self
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Enum)
+ {
+ write!(out, " {}", note);
+ }
+ if config.style.generate_tag() {
+ write!(out, " {}", tag_name);
+ }
+ }
+ }
+ Language::Cxx => {
+ if config.enumeration.enum_class(&self.annotations) {
+ out.write("enum class");
+ } else {
+ out.write("enum");
+ }
+
+ if self.annotations.must_use(config) {
+ if let Some(ref anno) = config.enumeration.must_use {
+ write!(out, " {}", anno)
+ }
+ }
+
+ if let Some(note) = self
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Enum)
+ {
+ write!(out, " {}", note);
+ }
+
+ write!(out, " {}", tag_name);
+ if let Some(prim) = size {
+ write!(out, " : {}", prim);
+ }
+ }
+ Language::Cython => {
+ if size.is_some() {
+ // If we need to specify size, then we have no choice but to create a typedef,
+ // so `config.style` is not respected.
+ write!(out, "cdef enum");
+ } else {
+ write!(out, "{}enum {}", config.style.cython_def(), tag_name);
+ }
+ }
+ }
+ out.open_brace();
+
+ // Emit enumerators for the tag enum.
+ for (i, variant) in self.variants.iter().enumerate() {
+ if i != 0 {
+ out.new_line()
+ }
+ variant.write(config, out);
+ }
+
+ // Close the tag enum.
+ if config.language == Language::C && size.is_none() && config.style.generate_typedef() {
+ out.close_brace(false);
+ write!(out, " {};", tag_name);
+ } else {
+ out.close_brace(true);
+ }
+
+ // Emit typedef specifying the tag enum's size if necessary.
+ // In C++ enums can "inherit" from numeric types (`enum E: uint8_t { ... }`),
+ // but in C `typedef uint8_t E` is the only way to give a fixed size to `E`.
+ if let Some(prim) = size {
+ if config.cpp_compatible_c() {
+ out.new_line_if_not_start();
+ out.write("#ifndef __cplusplus");
+ }
+
+ if config.language != Language::Cxx {
+ out.new_line();
+ write!(out, "{} {} {};", config.language.typedef(), prim, tag_name);
+ }
+
+ if config.cpp_compatible_c() {
+ out.new_line_if_not_start();
+ out.write("#endif // __cplusplus");
+ }
+ }
+
+ // Emit convenience methods for the tag enum.
+ self.write_derived_functions_enum(config, out, has_data, tag_name);
+ }
+
+ /// The code here mirrors the beginning of `Struct::write` and `Union::write`.
+ fn open_struct_or_union<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ inline_tag_field: bool,
+ ) {
+ match config.language {
+ Language::C if config.style.generate_typedef() => out.write("typedef "),
+ Language::C | Language::Cxx => {}
+ Language::Cython => out.write(config.style.cython_def()),
+ }
+
+ out.write(if inline_tag_field { "union" } else { "struct" });
+
+ if self.annotations.must_use(config) {
+ if let Some(ref anno) = config.structure.must_use {
+ write!(out, " {}", anno);
+ }
+ }
+
+ if let Some(note) = self
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Struct)
+ {
+ write!(out, " {} ", note);
+ }
+
+ if config.language != Language::C || config.style.generate_tag() {
+ write!(out, " {}", self.export_name());
+ }
+
+ out.open_brace();
+
+ // Emit the pre_body section, if relevant.
+ if let Some(body) = config.export.pre_body(&self.path) {
+ out.write_raw_block(body);
+ out.new_line();
+ }
+ }
+
+ /// Emit struct definitions for variants having data.
+ fn write_variant_defs<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ for variant in &self.variants {
+ if let VariantBody::Body {
+ ref body,
+ inline: false,
+ ..
+ } = variant.body
+ {
+ out.new_line();
+ out.new_line();
+ let condition = variant.cfg.to_condition(config);
+ // Cython doesn't support conditional enum variants.
+ if config.language != Language::Cython {
+ condition.write_before(config, out);
+ }
+ body.write(config, out);
+ if config.language != Language::Cython {
+ condition.write_after(config, out);
+ }
+ }
+ }
+ }
+
+ /// Emit tag field that is separate from all variants.
+ /// For non-inline tag scenario this is *the* tag field, and it does not exist in the variants.
+ /// For the inline tag scenario this is just a convenience and another way
+ /// to refer to the same tag that exist in all the variants.
+ fn write_tag_field<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ size: Option<&str>,
+ inline_tag_field: bool,
+ tag_name: &str,
+ ) {
+ // C++ allows accessing only common initial sequence of union
+ // fields so we have to wrap the tag field into an anonymous struct.
+ let wrap_tag = inline_tag_field && config.language == Language::Cxx;
+
+ if wrap_tag {
+ out.write("struct");
+ out.open_brace();
+ }
+
+ if config.language == Language::C && size.is_none() && !config.style.generate_typedef() {
+ out.write("enum ");
+ }
+
+ write!(out, "{} tag;", tag_name);
+
+ if wrap_tag {
+ out.close_brace(true);
+ }
+ }
+
+ /// Emit fields for all variants with data.
+ fn write_variant_fields<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ inline_tag_field: bool,
+ ) {
+ let mut first = true;
+ for variant in &self.variants {
+ if let VariantBody::Body {
+ name, body, inline, ..
+ } = &variant.body
+ {
+ if !first {
+ out.new_line();
+ }
+ first = false;
+ let condition = variant.cfg.to_condition(config);
+ // Cython doesn't support conditional enum variants.
+ if config.language != Language::Cython {
+ condition.write_before(config, out);
+ }
+ if *inline {
+ // Write definition of an inlined variant with data.
+ // Cython extern declarations don't manage layouts, layouts are defined entierly
+ // by the corresponding C code. So we can inline the unnamed struct and get the
+ // same observable result. Moreother we have to do it because Cython doesn't
+ // support unnamed structs.
+ // For the same reason with Cython we can omit per-variant tags (the first
+ // field) to avoid extra noise, the main `tag` is enough in this case.
+ if config.language != Language::Cython {
+ out.write("struct");
+ out.open_brace();
+ }
+ let start_field =
+ usize::from(inline_tag_field && config.language == Language::Cython);
+ out.write_vertical_source_list(&body.fields[start_field..], ListType::Cap(";"));
+ if config.language != Language::Cython {
+ out.close_brace(true);
+ }
+ } else if config.style.generate_typedef() || config.language == Language::Cython {
+ write!(out, "{} {};", body.export_name(), name);
+ } else {
+ write!(out, "struct {} {};", body.export_name(), name);
+ }
+ if config.language != Language::Cython {
+ condition.write_after(config, out);
+ }
+ }
+ }
+ }
+
+ // Emit convenience methods for enums themselves.
+ fn write_derived_functions_enum<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ has_data: bool,
+ tag_name: &str,
+ ) {
+ if config.language != Language::Cxx {
+ return;
+ }
+
+ // Emit an ostream function if required.
+ if config.enumeration.derive_ostream(&self.annotations) {
+ // For enums without data, this emits the serializer function for the
+ // enum. For enums with data, this emits the serializer function for
+ // the tag enum. In the latter case we need a couple of minor changes
+ // due to the function living inside the top-level struct or enum.
+ let stream = config
+ .function
+ .rename_args
+ .apply("stream", IdentifierType::FunctionArg);
+ let instance = config
+ .function
+ .rename_args
+ .apply("instance", IdentifierType::FunctionArg);
+
+ out.new_line();
+ out.new_line();
+ // For enums without data, we mark the function inline because the
+ // header might get included into multiple compilation units that
+ // get linked together, and not marking it inline would result in
+ // multiply-defined symbol errors. For enums with data we don't have
+ // the same problem, but mark it as a friend function of the
+ // containing union/struct.
+ // Note also that for enums with data, the case labels for switch
+ // statements apparently need to be qualified to the top-level
+ // generated struct or union. This is why the generated case labels
+ // below use the A::B::C format for enums with data, with A being
+ // self.export_name(). Failure to have that qualification results
+ // in a surprising compilation failure for the generated header.
+ write!(
+ out,
+ "{} std::ostream& operator<<(std::ostream& {}, const {}& {})",
+ if has_data { "friend" } else { "inline" },
+ stream,
+ tag_name,
+ instance,
+ );
+
+ out.open_brace();
+ if has_data {
+ // C++ name resolution rules are weird.
+ write!(
+ out,
+ "using {} = {}::{};",
+ tag_name,
+ self.export_name(),
+ tag_name
+ );
+ out.new_line();
+ }
+ write!(out, "switch ({})", instance);
+ out.open_brace();
+ let vec: Vec<_> = self
+ .variants
+ .iter()
+ .map(|x| {
+ format!(
+ "case {}::{}: {} << \"{}\"; break;",
+ tag_name, x.export_name, stream, x.export_name
+ )
+ })
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(""));
+ out.close_brace(false);
+ out.new_line();
+
+ write!(out, "return {};", stream);
+ out.close_brace(false);
+
+ if has_data {
+ // For enums with data, this emits the serializer function for
+ // the top-level union or struct.
+ out.new_line();
+ out.new_line();
+ write!(
+ out,
+ "friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
+ stream,
+ self.export_name(),
+ instance,
+ );
+
+ out.open_brace();
+
+ // C++ name resolution rules are weird.
+ write!(
+ out,
+ "using {} = {}::{};",
+ tag_name,
+ self.export_name(),
+ tag_name
+ );
+ out.new_line();
+
+ write!(out, "switch ({}.tag)", instance);
+ out.open_brace();
+ let vec: Vec<_> = self
+ .variants
+ .iter()
+ .map(|x| {
+ let tag_str = format!("\"{}\"", x.export_name);
+ if let VariantBody::Body {
+ ref name, ref body, ..
+ } = x.body
+ {
+ format!(
+ "case {}::{}: {} << {}{}{}.{}; break;",
+ tag_name,
+ x.export_name,
+ stream,
+ if body.has_tag_field { "" } else { &tag_str },
+ if body.has_tag_field { "" } else { " << " },
+ instance,
+ name,
+ )
+ } else {
+ format!(
+ "case {}::{}: {} << {}; break;",
+ tag_name, x.export_name, stream, tag_str,
+ )
+ }
+ })
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(""));
+ out.close_brace(false);
+ out.new_line();
+
+ write!(out, "return {};", stream);
+ out.close_brace(false);
+ }
+ }
+ }
+
+ // Emit convenience methods for structs or unions produced for enums with data.
+ fn write_derived_functions_data<F: Write>(
+ &self,
+ config: &Config,
+ out: &mut SourceWriter<F>,
+ tag_name: &str,
+ ) {
+ if config.language != Language::Cxx {
+ return;
+ }
+
+ if config.enumeration.derive_helper_methods(&self.annotations) {
+ for variant in &self.variants {
+ out.new_line();
+ out.new_line();
+
+ let condition = variant.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ let arg_renamer = |name: &str| {
+ config
+ .function
+ .rename_args
+ .apply(name, IdentifierType::FunctionArg)
+ .into_owned()
+ };
+
+ macro_rules! write_attrs {
+ ($op:expr) => {{
+ if let Some(Some(attrs)) =
+ variant
+ .body
+ .annotations()
+ .atom(concat!("variant-", $op, "-attributes"))
+ {
+ write!(out, "{} ", attrs);
+ }
+ }};
+ }
+
+ write_attrs!("constructor");
+ write!(out, "static {} {}(", self.export_name, variant.export_name);
+
+ if let VariantBody::Body { ref body, .. } = variant.body {
+ let skip_fields = body.has_tag_field as usize;
+ let vec: Vec<_> = body
+ .fields
+ .iter()
+ .skip(skip_fields)
+ .map(|field| {
+ Field::from_name_and_type(
+ // const-ref args to constructor
+ arg_renamer(&field.name),
+ Type::const_ref_to(&field.ty),
+ )
+ })
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(","));
+ }
+
+ write!(out, ")");
+ out.open_brace();
+
+ write!(out, "{} result;", self.export_name);
+
+ if let VariantBody::Body {
+ name: ref variant_name,
+ ref body,
+ ..
+ } = variant.body
+ {
+ let skip_fields = body.has_tag_field as usize;
+ for field in body.fields.iter().skip(skip_fields) {
+ out.new_line();
+ match field.ty {
+ Type::Array(ref ty, ref length) => {
+ // arrays are not assignable in C++ so we
+ // need to manually copy the elements
+ write!(out, "for (int i = 0; i < {}; i++)", length.as_str());
+ out.open_brace();
+ write!(out, "::new (&result.{}.{}[i]) (", variant_name, field.name);
+ ty.write(config, out);
+ write!(out, ")({}[i]);", arg_renamer(&field.name));
+ out.close_brace(false);
+ }
+ ref ty => {
+ write!(out, "::new (&result.{}.{}) (", variant_name, field.name);
+ ty.write(config, out);
+ write!(out, ")({});", arg_renamer(&field.name));
+ }
+ }
+ }
+ }
+
+ out.new_line();
+ write!(out, "result.tag = {}::{};", tag_name, variant.export_name);
+ out.new_line();
+ write!(out, "return result;");
+ out.close_brace(false);
+
+ out.new_line();
+ out.new_line();
+
+ write_attrs!("is");
+ // FIXME: create a config for method case
+ write!(out, "bool Is{}() const", variant.export_name);
+ out.open_brace();
+ write!(out, "return tag == {}::{};", tag_name, variant.export_name);
+ out.close_brace(false);
+
+ let assert_name = match config.enumeration.cast_assert_name {
+ Some(ref n) => &**n,
+ None => "assert",
+ };
+
+ let mut derive_casts = |const_casts: bool| {
+ let (member_name, body, inline_casts) = match variant.body {
+ VariantBody::Body {
+ ref name,
+ ref body,
+ inline_casts,
+ ..
+ } => (name, body, inline_casts),
+ VariantBody::Empty(..) => return,
+ };
+
+ let skip_fields = body.has_tag_field as usize;
+ let field_count = body.fields.len() - skip_fields;
+ if field_count == 0 {
+ return;
+ }
+
+ out.new_line();
+ out.new_line();
+
+ if const_casts {
+ write_attrs!("const-cast");
+ } else {
+ write_attrs!("mut-cast");
+ }
+ if inline_casts {
+ let field = body.fields.last().unwrap();
+ let return_type = field.ty.clone();
+ let return_type = Type::Ptr {
+ ty: Box::new(return_type),
+ is_const: const_casts,
+ is_ref: true,
+ is_nullable: false,
+ };
+ return_type.write(config, out);
+ } else if const_casts {
+ write!(out, "const {}&", body.export_name());
+ } else {
+ write!(out, "{}&", body.export_name());
+ }
+
+ write!(out, " As{}()", variant.export_name);
+ if const_casts {
+ write!(out, " const");
+ }
+ out.open_brace();
+ write!(out, "{}(Is{}());", assert_name, variant.export_name);
+ out.new_line();
+ write!(out, "return {}", member_name);
+ if inline_casts {
+ write!(out, "._0");
+ }
+ write!(out, ";");
+ out.close_brace(false);
+ };
+
+ if config.enumeration.derive_const_casts(&self.annotations) {
+ derive_casts(true)
+ }
+
+ if config.enumeration.derive_mut_casts(&self.annotations) {
+ derive_casts(false)
+ }
+
+ condition.write_after(config, out);
+ }
+ }
+
+ let other = config
+ .function
+ .rename_args
+ .apply("other", IdentifierType::FunctionArg);
+
+ macro_rules! write_attrs {
+ ($op:expr) => {{
+ if let Some(Some(attrs)) = self.annotations.atom(concat!($op, "-attributes")) {
+ write!(out, "{} ", attrs);
+ }
+ }};
+ }
+
+ if self.can_derive_eq() && config.structure.derive_eq(&self.annotations) {
+ out.new_line();
+ out.new_line();
+ write_attrs!("eq");
+ write!(
+ out,
+ "bool operator==(const {}& {}) const",
+ self.export_name, other
+ );
+ out.open_brace();
+ write!(out, "if (tag != {}.tag)", other);
+ out.open_brace();
+ write!(out, "return false;");
+ out.close_brace(false);
+ out.new_line();
+ write!(out, "switch (tag)");
+ out.open_brace();
+ let mut exhaustive = true;
+ for variant in &self.variants {
+ if let VariantBody::Body {
+ name: ref variant_name,
+ ..
+ } = variant.body
+ {
+ let condition = variant.cfg.to_condition(config);
+ condition.write_before(config, out);
+ write!(
+ out,
+ "case {}::{}: return {} == {}.{};",
+ self.tag.as_ref().unwrap(),
+ variant.export_name,
+ variant_name,
+ other,
+ variant_name
+ );
+ condition.write_after(config, out);
+ out.new_line();
+ } else {
+ exhaustive = false;
+ }
+ }
+ if !exhaustive {
+ write!(out, "default: break;");
+ }
+ out.close_brace(false);
+
+ out.new_line();
+ write!(out, "return true;");
+
+ out.close_brace(false);
+
+ if config.structure.derive_neq(&self.annotations) {
+ out.new_line();
+ out.new_line();
+ write_attrs!("neq");
+ write!(
+ out,
+ "bool operator!=(const {}& {}) const",
+ self.export_name, other
+ );
+ out.open_brace();
+ write!(out, "return !(*this == {});", other);
+ out.close_brace(false);
+ }
+ }
+
+ if config
+ .enumeration
+ .private_default_tagged_enum_constructor(&self.annotations)
+ {
+ out.new_line();
+ out.new_line();
+ write!(out, "private:");
+ out.new_line();
+ write!(out, "{}()", self.export_name);
+ out.open_brace();
+ out.close_brace(false);
+ out.new_line();
+ write!(out, "public:");
+ out.new_line();
+ }
+
+ if config
+ .enumeration
+ .derive_tagged_enum_destructor(&self.annotations)
+ {
+ out.new_line();
+ out.new_line();
+ write_attrs!("destructor");
+ write!(out, "~{}()", self.export_name);
+ out.open_brace();
+ write!(out, "switch (tag)");
+ out.open_brace();
+ let mut exhaustive = true;
+ for variant in &self.variants {
+ if let VariantBody::Body {
+ ref name, ref body, ..
+ } = variant.body
+ {
+ let condition = variant.cfg.to_condition(config);
+ condition.write_before(config, out);
+ write!(
+ out,
+ "case {}::{}: {}.~{}(); break;",
+ self.tag.as_ref().unwrap(),
+ variant.export_name,
+ name,
+ body.export_name(),
+ );
+ condition.write_after(config, out);
+ out.new_line();
+ } else {
+ exhaustive = false;
+ }
+ }
+ if !exhaustive {
+ write!(out, "default: break;");
+ }
+ out.close_brace(false);
+ out.close_brace(false);
+ }
+
+ if config
+ .enumeration
+ .derive_tagged_enum_copy_constructor(&self.annotations)
+ {
+ out.new_line();
+ out.new_line();
+ write_attrs!("copy-constructor");
+ write!(
+ out,
+ "{}(const {}& {})",
+ self.export_name, self.export_name, other
+ );
+ out.new_line();
+ write!(out, " : tag({}.tag)", other);
+ out.open_brace();
+ write!(out, "switch (tag)");
+ out.open_brace();
+ let mut exhaustive = true;
+ for variant in &self.variants {
+ if let VariantBody::Body {
+ ref name, ref body, ..
+ } = variant.body
+ {
+ let condition = variant.cfg.to_condition(config);
+ condition.write_before(config, out);
+ write!(
+ out,
+ "case {}::{}: ::new (&{}) ({})({}.{}); break;",
+ self.tag.as_ref().unwrap(),
+ variant.export_name,
+ name,
+ body.export_name(),
+ other,
+ name,
+ );
+ condition.write_after(config, out);
+ out.new_line();
+ } else {
+ exhaustive = false;
+ }
+ }
+ if !exhaustive {
+ write!(out, "default: break;");
+ }
+ out.close_brace(false);
+ out.close_brace(false);
+
+ if config
+ .enumeration
+ .derive_tagged_enum_copy_assignment(&self.annotations)
+ {
+ out.new_line();
+ write_attrs!("copy-assignment");
+ write!(
+ out,
+ "{}& operator=(const {}& {})",
+ self.export_name, self.export_name, other
+ );
+ out.open_brace();
+ write!(out, "if (this != &{})", other);
+ out.open_brace();
+ write!(out, "this->~{}();", self.export_name);
+ out.new_line();
+ write!(out, "new (this) {}({});", self.export_name, other);
+ out.close_brace(false);
+ out.new_line();
+ write!(out, "return *this;");
+ out.close_brace(false);
+ }
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ for variant in &mut self.variants {
+ variant.simplify_standard_types(config);
+ }
+ }
+}
diff --git a/src/bindgen/ir/field.rs b/src/bindgen/ir/field.rs
new file mode 100644
index 0000000..6e132bf
--- /dev/null
+++ b/src/bindgen/ir/field.rs
@@ -0,0 +1,80 @@
+use std::io::Write;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::cdecl;
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::ir::{AnnotationSet, Cfg, ConditionWrite};
+use crate::bindgen::ir::{Documentation, Path, ToCondition, Type};
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct Field {
+ pub name: String,
+ pub ty: Type,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl Field {
+ pub fn from_name_and_type(name: String, ty: Type) -> Field {
+ Field {
+ name,
+ ty,
+ cfg: None,
+ annotations: AnnotationSet::new(),
+ documentation: Documentation::none(),
+ }
+ }
+
+ pub fn load(field: &syn::Field, self_path: &Path) -> Result<Option<Field>, String> {
+ Ok(if let Some(mut ty) = Type::load(&field.ty)? {
+ ty.replace_self_with(self_path);
+ Some(Field {
+ name: field
+ .ident
+ .as_ref()
+ .ok_or_else(|| "field is missing identifier".to_string())?
+ .unraw()
+ .to_string(),
+ ty,
+ cfg: Cfg::load(&field.attrs),
+ annotations: AnnotationSet::load(&field.attrs)?,
+ documentation: Documentation::load(&field.attrs),
+ })
+ } else {
+ None
+ })
+ }
+}
+
+impl Source for Field {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ // Cython doesn't support conditional fields.
+ let condition = self.cfg.to_condition(config);
+ if config.language != Language::Cython {
+ condition.write_before(config, out);
+ }
+
+ self.documentation.write(config, out);
+ cdecl::write_field(out, &self.ty, &self.name, config);
+ // Cython extern declarations don't manage layouts, layouts are defined entierly by the
+ // corresponding C code. So we can omit bitfield sizes which are not supported by Cython.
+ if config.language != Language::Cython {
+ if let Some(bitfield) = self.annotations.atom("bitfield") {
+ write!(out, ": {}", bitfield.unwrap_or_default());
+ }
+ }
+
+ if config.language != Language::Cython {
+ condition.write_after(config, out);
+ // FIXME(#634): `write_vertical_source_list` should support
+ // configuring list elements natively. For now we print a newline
+ // here to avoid printing `#endif;` with semicolon.
+ if condition.is_some() {
+ out.new_line();
+ }
+ }
+ }
+}
diff --git a/src/bindgen/ir/function.rs b/src/bindgen/ir/function.rs
new file mode 100644
index 0000000..8c65f2f
--- /dev/null
+++ b/src/bindgen/ir/function.rs
@@ -0,0 +1,390 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::collections::HashMap;
+use std::io::Write;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::cdecl;
+use crate::bindgen::config::{Config, Language, Layout};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, Cfg, ConditionWrite, DeprecatedNoteKind, Documentation, GenericPath, Path,
+ ToCondition, Type,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::rename::{IdentifierType, RenameRule};
+use crate::bindgen::reserved;
+use crate::bindgen::utilities::IterHelpers;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct FunctionArgument {
+ pub name: Option<String>,
+ pub ty: Type,
+ pub array_length: Option<String>,
+}
+
+#[derive(Debug, Clone)]
+pub struct Function {
+ pub path: Path,
+ /// Path to the self-type of the function
+ /// If the function is a method, this will contain the path of the type in the impl block
+ pub self_type_path: Option<Path>,
+ pub ret: Type,
+ pub args: Vec<FunctionArgument>,
+ pub extern_decl: bool,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+ pub never_return: bool,
+}
+
+impl Function {
+ pub fn load(
+ path: Path,
+ self_type_path: Option<&Path>,
+ sig: &syn::Signature,
+ extern_decl: bool,
+ attrs: &[syn::Attribute],
+ mod_cfg: Option<&Cfg>,
+ ) -> Result<Function, String> {
+ let mut args = sig.inputs.iter().try_skip_map(|x| x.as_argument())?;
+
+ let (mut ret, never_return) = Type::load_from_output(&sig.output)?;
+
+ if let Some(self_path) = self_type_path {
+ for arg in &mut args {
+ arg.ty.replace_self_with(self_path);
+ }
+ ret.replace_self_with(self_path);
+ }
+
+ Ok(Function {
+ path,
+ self_type_path: self_type_path.cloned(),
+ ret,
+ args,
+ extern_decl,
+ cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
+ annotations: AnnotationSet::load(attrs)?,
+ documentation: Documentation::load(attrs),
+ never_return,
+ })
+ }
+
+ pub fn swift_name(&self, config: &Config) -> Option<String> {
+ if config.language == Language::Cython {
+ return None;
+ }
+ // If the symbol name starts with the type name, separate the two components with '.'
+ // so that Swift recognises the association between the method and the type
+ let (ref type_prefix, ref type_name) = match self.self_type_path {
+ Some(ref type_name) => {
+ let type_name = type_name.to_string();
+ if !self.path.name().starts_with(&type_name) {
+ return Some(self.path.to_string());
+ }
+ (format!("{}.", type_name), type_name)
+ }
+ None => ("".to_string(), "".to_string()),
+ };
+
+ let item_name = self
+ .path
+ .name()
+ .trim_start_matches(type_name)
+ .trim_start_matches('_');
+
+ let item_args = {
+ let mut items = Vec::with_capacity(self.args.len());
+ for arg in self.args.iter() {
+ items.push(format!("{}:", arg.name.as_ref()?.as_str()));
+ }
+ items.join("")
+ };
+ Some(format!("{}{}({})", type_prefix, item_name, item_args))
+ }
+
+ pub fn path(&self) -> &Path {
+ &self.path
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ self.ret.simplify_standard_types(config);
+ for arg in &mut self.args {
+ arg.ty.simplify_standard_types(config);
+ }
+ }
+
+ pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ self.ret.add_dependencies(library, out);
+ for arg in &self.args {
+ arg.ty.add_dependencies(library, out);
+ }
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ self.ret.add_monomorphs(library, out);
+ for arg in &self.args {
+ arg.ty.add_monomorphs(library, out);
+ }
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ self.ret.mangle_paths(monomorphs);
+ for arg in &mut self.args {
+ arg.ty.mangle_paths(monomorphs);
+ }
+ }
+
+ pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ self.ret.resolve_declaration_types(resolver);
+ for arg in &mut self.args {
+ arg.ty.resolve_declaration_types(resolver);
+ }
+ }
+
+ pub fn rename_for_config(&mut self, config: &Config) {
+ // Rename the types used in arguments
+ let generic_params = Default::default();
+ self.ret.rename_for_config(config, &generic_params);
+
+ // Apply rename rules to argument names
+ let rules = self
+ .annotations
+ .parse_atom::<RenameRule>("rename-all")
+ .unwrap_or(config.function.rename_args);
+
+ if let Some(r) = rules.not_none() {
+ let args = std::mem::take(&mut self.args);
+ self.args = args
+ .into_iter()
+ .map(|arg| {
+ let name = arg
+ .name
+ .map(|n| r.apply(&n, IdentifierType::FunctionArg).into_owned());
+ FunctionArgument {
+ name,
+ ty: arg.ty,
+ array_length: None,
+ }
+ })
+ .collect()
+ }
+
+ // Escape C/C++ reserved keywords used in argument names, and
+ // recursively rename argument types.
+ for arg in &mut self.args {
+ arg.ty.rename_for_config(config, &generic_params);
+ if let Some(ref mut name) = arg.name {
+ reserved::escape(name);
+ }
+ }
+
+ // Save the array length of the pointer arguments which need to use
+ // the C-array notation
+ if let Some(tuples) = self.annotations.list("ptrs-as-arrays") {
+ let mut ptrs_as_arrays: HashMap<String, String> = HashMap::new();
+ for str_tuple in tuples {
+ let parts: Vec<&str> = str_tuple[1..str_tuple.len() - 1]
+ .split(';')
+ .map(|x| x.trim())
+ .collect();
+ if parts.len() != 2 {
+ warn!(
+ "{:?} does not follow the correct syntax, so the annotation is being ignored",
+ parts
+ );
+ continue;
+ }
+ ptrs_as_arrays.insert(parts[0].to_string(), parts[1].to_string());
+ }
+
+ for arg in &mut self.args {
+ match arg.ty {
+ Type::Ptr { .. } => {}
+ _ => continue,
+ }
+ let name = match arg.name {
+ Some(ref name) => name,
+ None => continue,
+ };
+ arg.array_length = ptrs_as_arrays.get(name).cloned();
+ }
+ }
+ }
+}
+
+impl Source for Function {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ fn write_1<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
+ let prefix = config.function.prefix(&func.annotations);
+ let postfix = config.function.postfix(&func.annotations);
+
+ let condition = func.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ func.documentation.write(config, out);
+
+ if func.extern_decl {
+ out.write("extern ");
+ } else {
+ if let Some(ref prefix) = prefix {
+ write!(out, "{} ", prefix);
+ }
+ if func.annotations.must_use(config) {
+ if let Some(ref anno) = config.function.must_use {
+ write!(out, "{} ", anno);
+ }
+ }
+ if let Some(note) = func
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Function)
+ {
+ write!(out, "{} ", note);
+ }
+ }
+ cdecl::write_func(out, func, Layout::Horizontal, config);
+
+ if !func.extern_decl {
+ if let Some(ref postfix) = postfix {
+ write!(out, " {}", postfix);
+ }
+ }
+
+ if let Some(ref swift_name_macro) = config.function.swift_name_macro {
+ if let Some(swift_name) = func.swift_name(config) {
+ write!(out, " {}({})", swift_name_macro, swift_name);
+ }
+ }
+
+ out.write(";");
+
+ condition.write_after(config, out);
+ }
+
+ fn write_2<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
+ let prefix = config.function.prefix(&func.annotations);
+ let postfix = config.function.postfix(&func.annotations);
+
+ let condition = func.cfg.to_condition(config);
+
+ condition.write_before(config, out);
+
+ func.documentation.write(config, out);
+
+ if func.extern_decl {
+ out.write("extern ");
+ } else {
+ if let Some(ref prefix) = prefix {
+ write!(out, "{}", prefix);
+ out.new_line();
+ }
+ if func.annotations.must_use(config) {
+ if let Some(ref anno) = config.function.must_use {
+ write!(out, "{}", anno);
+ out.new_line();
+ }
+ }
+ if let Some(note) = func
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Function)
+ {
+ write!(out, "{}", note);
+ out.new_line();
+ }
+ }
+ cdecl::write_func(out, func, Layout::Vertical, config);
+ if !func.extern_decl {
+ if let Some(ref postfix) = postfix {
+ out.new_line();
+ write!(out, "{}", postfix);
+ }
+ }
+
+ if let Some(ref swift_name_macro) = config.function.swift_name_macro {
+ if let Some(swift_name) = func.swift_name(config) {
+ write!(out, " {}({})", swift_name_macro, swift_name);
+ }
+ }
+
+ out.write(";");
+
+ condition.write_after(config, out);
+ }
+
+ match config.function.args {
+ Layout::Horizontal => write_1(self, config, out),
+ Layout::Vertical => write_2(self, config, out),
+ Layout::Auto => {
+ if !out.try_write(|out| write_1(self, config, out), config.line_length) {
+ write_2(self, config, out)
+ }
+ }
+ }
+ }
+}
+
+trait SynFnArgHelpers {
+ fn as_argument(&self) -> Result<Option<FunctionArgument>, String>;
+}
+
+fn gen_self_type(receiver: &syn::Receiver) -> Type {
+ let self_ty = Type::Path(GenericPath::self_path());
+ if receiver.reference.is_none() {
+ return self_ty;
+ }
+
+ let is_const = receiver.mutability.is_none();
+ Type::Ptr {
+ ty: Box::new(self_ty),
+ is_const,
+ is_nullable: false,
+ is_ref: false,
+ }
+}
+
+impl SynFnArgHelpers for syn::FnArg {
+ fn as_argument(&self) -> Result<Option<FunctionArgument>, String> {
+ match *self {
+ syn::FnArg::Typed(syn::PatType {
+ ref pat, ref ty, ..
+ }) => {
+ let name = match **pat {
+ syn::Pat::Wild(..) => None,
+ syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => {
+ Some(ident.unraw().to_string())
+ }
+ _ => {
+ return Err(format!(
+ "Parameter has an unsupported argument name: {:?}",
+ pat
+ ))
+ }
+ };
+ let ty = match Type::load(ty)? {
+ Some(x) => x,
+ None => return Ok(None),
+ };
+ if let Type::Array(..) = ty {
+ return Err("Array as function arguments are not supported".to_owned());
+ }
+ Ok(Some(FunctionArgument {
+ name,
+ ty,
+ array_length: None,
+ }))
+ }
+ syn::FnArg::Receiver(ref receiver) => Ok(Some(FunctionArgument {
+ name: Some("self".to_string()),
+ ty: gen_self_type(receiver),
+ array_length: None,
+ })),
+ }
+ }
+}
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))
+ }
+}
diff --git a/src/bindgen/ir/global.rs b/src/bindgen/ir/global.rs
new file mode 100644
index 0000000..82a1756
--- /dev/null
+++ b/src/bindgen/ir/global.rs
@@ -0,0 +1,121 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::io::Write;
+
+use crate::bindgen::cdecl;
+use crate::bindgen::config::Config;
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{AnnotationSet, Cfg, Documentation, Item, ItemContainer, Path, Type};
+use crate::bindgen::library::Library;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct Static {
+ pub path: Path,
+ pub export_name: String,
+ pub ty: Type,
+ pub mutable: bool,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl Static {
+ pub fn load(
+ path: Path,
+ item: &syn::ItemStatic,
+ mod_cfg: Option<&Cfg>,
+ ) -> Result<Static, String> {
+ let ty = Type::load(&item.ty)?;
+
+ if ty.is_none() {
+ return Err("Cannot have a zero sized static definition.".to_owned());
+ }
+
+ Ok(Static::new(
+ path,
+ ty.unwrap(),
+ item.mutability.is_some(),
+ Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
+ AnnotationSet::load(&item.attrs)?,
+ Documentation::load(&item.attrs),
+ ))
+ }
+
+ pub fn new(
+ path: Path,
+ ty: Type,
+ mutable: bool,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ ty,
+ mutable,
+ cfg,
+ annotations,
+ documentation,
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ self.ty.simplify_standard_types(config);
+ }
+}
+
+impl Item for Static {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Static(self.clone())
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ self.ty.rename_for_config(config, &Default::default());
+ }
+
+ fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ self.ty.resolve_declaration_types(resolver);
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ self.ty.add_dependencies(library, out);
+ }
+}
+
+impl Source for Static {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ out.write("extern ");
+ if let Type::Ptr { is_const: true, .. } = self.ty {
+ } else if !self.mutable {
+ out.write("const ");
+ }
+ cdecl::write_field(out, &self.ty, &self.export_name, config);
+ out.write(";");
+ }
+}
diff --git a/src/bindgen/ir/item.rs b/src/bindgen/ir/item.rs
new file mode 100644
index 0000000..16d98f5
--- /dev/null
+++ b/src/bindgen/ir/item.rs
@@ -0,0 +1,250 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use indexmap::IndexMap;
+use std::mem;
+
+use crate::bindgen::config::Config;
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, Cfg, Constant, Enum, GenericArgument, OpaqueItem, Path, Static, Struct, Typedef,
+ Union,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::monomorph::Monomorphs;
+
+/// An item is any type of rust item besides a function
+pub trait Item {
+ fn path(&self) -> &Path;
+ fn name(&self) -> &str {
+ self.path().name()
+ }
+ fn export_name(&self) -> &str {
+ self.name()
+ }
+ fn cfg(&self) -> Option<&Cfg>;
+ fn annotations(&self) -> &AnnotationSet;
+ fn annotations_mut(&mut self) -> &mut AnnotationSet;
+
+ fn container(&self) -> ItemContainer;
+
+ fn collect_declaration_types(&self, _resolver: &mut DeclarationTypeResolver) {
+ unimplemented!()
+ }
+ fn resolve_declaration_types(&mut self, _resolver: &DeclarationTypeResolver) {
+ unimplemented!()
+ }
+ fn rename_for_config(&mut self, _config: &Config) {}
+ fn add_dependencies(&self, _library: &Library, _out: &mut Dependencies) {}
+ fn instantiate_monomorph(
+ &self,
+ _generics: &[GenericArgument],
+ _library: &Library,
+ _out: &mut Monomorphs,
+ ) {
+ unreachable!("Cannot instantiate {} as a generic.", self.name())
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum ItemContainer {
+ Constant(Constant),
+ Static(Static),
+ OpaqueItem(OpaqueItem),
+ Struct(Struct),
+ Union(Union),
+ Enum(Enum),
+ Typedef(Typedef),
+}
+
+impl ItemContainer {
+ pub fn deref(&self) -> &dyn Item {
+ match *self {
+ ItemContainer::Constant(ref x) => x,
+ ItemContainer::Static(ref x) => x,
+ ItemContainer::OpaqueItem(ref x) => x,
+ ItemContainer::Struct(ref x) => x,
+ ItemContainer::Union(ref x) => x,
+ ItemContainer::Enum(ref x) => x,
+ ItemContainer::Typedef(ref x) => x,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum ItemValue<T: Item> {
+ Cfg(Vec<T>),
+ Single(T),
+}
+
+#[derive(Debug, Clone)]
+pub struct ItemMap<T: Item> {
+ data: IndexMap<Path, ItemValue<T>>,
+}
+
+impl<T: Item> Default for ItemMap<T> {
+ fn default() -> ItemMap<T> {
+ ItemMap {
+ data: Default::default(),
+ }
+ }
+}
+
+impl<T: Item + Clone> ItemMap<T> {
+ pub fn rebuild(&mut self) {
+ let old = mem::take(self);
+ old.for_all_items(|x| {
+ self.try_insert(x.clone());
+ });
+ }
+
+ pub fn try_insert(&mut self, item: T) -> bool {
+ match (item.cfg().is_some(), self.data.get_mut(item.path())) {
+ (true, Some(&mut ItemValue::Cfg(ref mut items))) => {
+ items.push(item);
+ return true;
+ }
+ (false, Some(&mut ItemValue::Cfg(_))) => {
+ return false;
+ }
+ (true, Some(&mut ItemValue::Single(_))) => {
+ return false;
+ }
+ (false, Some(&mut ItemValue::Single(_))) => {
+ return false;
+ }
+ _ => {}
+ }
+
+ let path = item.path().clone();
+ if item.cfg().is_some() {
+ self.data.insert(path, ItemValue::Cfg(vec![item]));
+ } else {
+ self.data.insert(path, ItemValue::Single(item));
+ }
+
+ true
+ }
+
+ pub fn extend_with(&mut self, other: &ItemMap<T>) {
+ other.for_all_items(|x| {
+ self.try_insert(x.clone());
+ });
+ }
+
+ pub fn to_vec(&self) -> Vec<T> {
+ let mut result = Vec::with_capacity(self.data.len());
+ for container in self.data.values() {
+ match *container {
+ ItemValue::Cfg(ref items) => result.extend_from_slice(items),
+ ItemValue::Single(ref item) => {
+ result.push(item.clone());
+ }
+ }
+ }
+ result
+ }
+
+ pub fn get_items(&self, path: &Path) -> Option<Vec<ItemContainer>> {
+ Some(match *self.data.get(path)? {
+ ItemValue::Cfg(ref items) => items.iter().map(|x| x.container()).collect(),
+ ItemValue::Single(ref item) => vec![item.container()],
+ })
+ }
+
+ pub fn filter<F>(&mut self, callback: F)
+ where
+ F: Fn(&T) -> bool,
+ {
+ let data = mem::take(&mut self.data);
+
+ for (name, container) in data {
+ match container {
+ ItemValue::Cfg(items) => {
+ let mut new_items = Vec::new();
+ for item in items {
+ if !callback(&item) {
+ new_items.push(item);
+ }
+ }
+ if !new_items.is_empty() {
+ self.data.insert(name, ItemValue::Cfg(new_items));
+ }
+ }
+ ItemValue::Single(item) => {
+ if !callback(&item) {
+ self.data.insert(name, ItemValue::Single(item));
+ }
+ }
+ }
+ }
+ }
+
+ pub fn for_all_items<F>(&self, mut callback: F)
+ where
+ F: FnMut(&T),
+ {
+ for container in self.data.values() {
+ match *container {
+ ItemValue::Cfg(ref items) => {
+ for item in items {
+ callback(item);
+ }
+ }
+ ItemValue::Single(ref item) => callback(item),
+ }
+ }
+ }
+
+ pub fn for_all_items_mut<F>(&mut self, mut callback: F)
+ where
+ F: FnMut(&mut T),
+ {
+ for container in self.data.values_mut() {
+ match *container {
+ ItemValue::Cfg(ref mut items) => {
+ for item in items {
+ callback(item);
+ }
+ }
+ ItemValue::Single(ref mut item) => callback(item),
+ }
+ }
+ }
+
+ pub fn for_items<F>(&self, path: &Path, mut callback: F)
+ where
+ F: FnMut(&T),
+ {
+ match self.data.get(path) {
+ Some(ItemValue::Cfg(items)) => {
+ for item in items {
+ callback(item);
+ }
+ }
+ Some(ItemValue::Single(item)) => {
+ callback(item);
+ }
+ None => {}
+ }
+ }
+
+ pub fn for_items_mut<F>(&mut self, path: &Path, mut callback: F)
+ where
+ F: FnMut(&mut T),
+ {
+ match self.data.get_mut(path) {
+ Some(&mut ItemValue::Cfg(ref mut items)) => {
+ for item in items {
+ callback(item);
+ }
+ }
+ Some(&mut ItemValue::Single(ref mut item)) => {
+ callback(item);
+ }
+ None => {}
+ }
+ }
+}
diff --git a/src/bindgen/ir/mod.rs b/src/bindgen/ir/mod.rs
new file mode 100644
index 0000000..3566f0d
--- /dev/null
+++ b/src/bindgen/ir/mod.rs
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+pub mod annotation;
+pub mod cfg;
+pub mod constant;
+pub mod documentation;
+pub mod enumeration;
+pub mod field;
+pub mod function;
+pub mod generic_path;
+pub mod global;
+pub mod item;
+pub mod opaque;
+pub mod path;
+pub mod repr;
+pub mod structure;
+pub mod ty;
+pub mod typedef;
+pub mod union;
+
+pub use self::annotation::{AnnotationSet, AnnotationValue, DeprecatedNoteKind};
+pub use self::cfg::*;
+pub use self::constant::*;
+pub use self::documentation::Documentation;
+pub use self::enumeration::*;
+pub use self::field::*;
+pub use self::function::*;
+pub use self::generic_path::*;
+pub use self::global::*;
+pub use self::item::*;
+pub use self::opaque::*;
+pub use self::path::*;
+pub use self::repr::*;
+pub use self::structure::*;
+pub use self::ty::*;
+pub use self::typedef::*;
+pub use self::union::*;
diff --git a/src/bindgen/ir/opaque.rs b/src/bindgen/ir/opaque.rs
new file mode 100644
index 0000000..4451d4a
--- /dev/null
+++ b/src/bindgen/ir/opaque.rs
@@ -0,0 +1,176 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::io::Write;
+
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{
+ AnnotationSet, Cfg, ConditionWrite, Documentation, GenericArgument, GenericParams, Item,
+ ItemContainer, Path, ToCondition,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::mangle;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct OpaqueItem {
+ pub path: Path,
+ pub export_name: String,
+ pub generic_params: GenericParams,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl OpaqueItem {
+ pub fn load(
+ path: Path,
+ generics: &syn::Generics,
+ attrs: &[syn::Attribute],
+ mod_cfg: Option<&Cfg>,
+ ) -> Result<OpaqueItem, String> {
+ Ok(Self::new(
+ path,
+ GenericParams::load(generics)?,
+ Cfg::append(mod_cfg, Cfg::load(attrs)),
+ AnnotationSet::load(attrs).unwrap_or_else(|_| AnnotationSet::new()),
+ Documentation::load(attrs),
+ ))
+ }
+
+ pub fn new(
+ path: Path,
+ generic_params: GenericParams,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> OpaqueItem {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ generic_params,
+ cfg,
+ annotations,
+ documentation,
+ }
+ }
+}
+
+impl Item for OpaqueItem {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::OpaqueItem(self.clone())
+ }
+
+ fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
+ resolver.add_struct(&self.path);
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ config.export.rename(&mut self.export_name);
+ }
+
+ fn add_dependencies(&self, _: &Library, _: &mut Dependencies) {}
+
+ fn instantiate_monomorph(
+ &self,
+ generic_values: &[GenericArgument],
+ library: &Library,
+ out: &mut Monomorphs,
+ ) {
+ assert!(
+ !self.generic_params.is_empty(),
+ "{} is not generic",
+ self.path
+ );
+
+ // We can be instantiated with less generic params because of default
+ // template parameters, or because of empty types that we remove during
+ // parsing (`()`).
+ 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 mangled_path = mangle::mangle_path(
+ &self.path,
+ generic_values,
+ &library.get_config().export.mangle,
+ );
+
+ let monomorph = OpaqueItem::new(
+ mangled_path,
+ GenericParams::default(),
+ self.cfg.clone(),
+ self.annotations.clone(),
+ self.documentation.clone(),
+ );
+
+ out.insert_opaque(self, monomorph, generic_values.to_owned());
+ }
+}
+
+impl Source for OpaqueItem {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ self.documentation.write(config, out);
+
+ self.generic_params.write_with_default(config, out);
+
+ match config.language {
+ Language::C if config.style.generate_typedef() => {
+ write!(
+ out,
+ "typedef struct {} {};",
+ self.export_name(),
+ self.export_name()
+ );
+ }
+ Language::C | Language::Cxx => {
+ write!(out, "struct {};", self.export_name());
+ }
+ Language::Cython => {
+ write!(
+ out,
+ "{}struct {}",
+ config.style.cython_def(),
+ self.export_name()
+ );
+ out.open_brace();
+ out.write("pass");
+ out.close_brace(false);
+ }
+ }
+
+ condition.write_after(config, out);
+ }
+}
diff --git a/src/bindgen/ir/path.rs b/src/bindgen/ir/path.rs
new file mode 100644
index 0000000..480c31b
--- /dev/null
+++ b/src/bindgen/ir/path.rs
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cmp::Ordering;
+use std::fmt;
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Path {
+ name: String,
+}
+
+impl Path {
+ pub fn new<T>(name: T) -> Self
+ where
+ String: From<T>,
+ {
+ Self { name: name.into() }
+ }
+
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn replace_self_with(&mut self, path: &Self) -> bool {
+ if self.name() != "Self" {
+ return false;
+ }
+ *self = path.clone();
+ true
+ }
+}
+
+impl PartialOrd for Path {
+ fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Path {
+ fn cmp(&self, other: &Path) -> Ordering {
+ self.name.cmp(&other.name)
+ }
+}
+
+impl fmt::Display for Path {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.name)
+ }
+}
diff --git a/src/bindgen/ir/repr.rs b/src/bindgen/ir/repr.rs
new file mode 100644
index 0000000..c40cd7f
--- /dev/null
+++ b/src/bindgen/ir/repr.rs
@@ -0,0 +1,182 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::ir::ty::{IntKind, PrimitiveType};
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
+pub enum ReprStyle {
+ #[default]
+ Rust,
+ C,
+ Transparent,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct ReprType {
+ kind: IntKind,
+ signed: bool,
+}
+
+impl ReprType {
+ pub(crate) fn to_primitive(self) -> PrimitiveType {
+ PrimitiveType::Integer {
+ kind: self.kind,
+ signed: self.signed,
+ zeroable: true,
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum ReprAlign {
+ Packed,
+ Align(u64),
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
+pub struct Repr {
+ pub style: ReprStyle,
+ pub ty: Option<ReprType>,
+ pub align: Option<ReprAlign>,
+}
+
+impl Repr {
+ pub fn load(attrs: &[syn::Attribute]) -> Result<Repr, String> {
+ let ids = attrs
+ .iter()
+ .filter_map(|attr| {
+ if let syn::Meta::List(syn::MetaList { path, nested, .. }) =
+ attr.parse_meta().ok()?
+ {
+ if path.is_ident("repr") {
+ return Some(nested.into_iter().collect::<Vec<_>>());
+ }
+ }
+ None
+ })
+ .flatten()
+ .filter_map(|meta| match meta {
+ syn::NestedMeta::Meta(syn::Meta::Path(path)) => Some((
+ path.segments.first().unwrap().ident.unraw().to_string(),
+ None,
+ )),
+ syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList { path, nested, .. })) => {
+ Some((
+ path.segments.first().unwrap().ident.unraw().to_string(),
+ Some(
+ nested
+ .iter()
+ .filter_map(|meta| match meta {
+ // Only used for #[repr(align(...))].
+ syn::NestedMeta::Lit(syn::Lit::Int(literal)) => {
+ Some(literal.base10_digits().to_string())
+ }
+ // Only single levels of nesting supported at the moment.
+ _ => None,
+ })
+ .collect::<Vec<_>>(),
+ ),
+ ))
+ }
+ _ => None,
+ });
+
+ let mut repr = Repr::default();
+ for id in ids {
+ let (int_kind, signed) = match (id.0.as_ref(), id.1) {
+ ("u8", None) => (IntKind::B8, false),
+ ("u16", None) => (IntKind::B16, false),
+ ("u32", None) => (IntKind::B32, false),
+ ("u64", None) => (IntKind::B64, false),
+ ("usize", None) => (IntKind::Size, false),
+ ("i8", None) => (IntKind::B8, true),
+ ("i16", None) => (IntKind::B16, true),
+ ("i32", None) => (IntKind::B32, true),
+ ("i64", None) => (IntKind::B64, true),
+ ("isize", None) => (IntKind::Size, true),
+ ("C", None) => {
+ repr.style = ReprStyle::C;
+ continue;
+ }
+ ("transparent", None) => {
+ repr.style = ReprStyle::Transparent;
+ continue;
+ }
+ ("packed", args) => {
+ // #[repr(packed(n))] not supported because of some open questions about how
+ // to calculate the native alignment of types. See mozilla/cbindgen#433.
+ if args.is_some() {
+ return Err(
+ "Not-yet-implemented #[repr(packed(...))] encountered.".to_string()
+ );
+ }
+ let align = ReprAlign::Packed;
+ // Only permit a single alignment-setting repr.
+ if let Some(old_align) = repr.align {
+ return Err(format!(
+ "Conflicting #[repr(align(...))] type hints {:?} and {:?}.",
+ old_align, align
+ ));
+ }
+ repr.align = Some(align);
+ continue;
+ }
+ ("align", Some(args)) => {
+ // #[repr(align(...))] only allows a single argument.
+ if args.len() != 1 {
+ return Err(format!(
+ "Unsupported #[repr(align({}))], align must have exactly one argument.",
+ args.join(", ")
+ ));
+ }
+ // Must be a positive integer.
+ let align = match args.first().unwrap().parse::<u64>() {
+ Ok(align) => align,
+ Err(_) => {
+ return Err(format!("Non-numeric #[repr(align({}))].", args.join(", ")))
+ }
+ };
+ // Must be a power of 2.
+ if !align.is_power_of_two() || align == 0 {
+ return Err(format!("Invalid alignment to #[repr(align({}))].", align));
+ }
+ // Only permit a single alignment-setting repr.
+ if let Some(old_align) = repr.align {
+ return Err(format!(
+ "Conflicting #[repr(align(...))] type hints {:?} and {:?}.",
+ old_align,
+ ReprAlign::Align(align)
+ ));
+ }
+ repr.align = Some(ReprAlign::Align(align));
+ continue;
+ }
+ (path, args) => match args {
+ None => return Err(format!("Unsupported #[repr({})].", path)),
+ Some(args) => {
+ return Err(format!(
+ "Unsupported #[repr({}({}))].",
+ path,
+ args.join(", ")
+ ));
+ }
+ },
+ };
+ let ty = ReprType {
+ kind: int_kind,
+ signed,
+ };
+ if let Some(old_ty) = repr.ty {
+ return Err(format!(
+ "Conflicting #[repr(...)] type hints {:?} and {:?}.",
+ old_ty, ty
+ ));
+ }
+ repr.ty = Some(ty);
+ }
+ Ok(repr)
+ }
+}
diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs
new file mode 100644
index 0000000..2eefa95
--- /dev/null
+++ b/src/bindgen/ir/structure.rs
@@ -0,0 +1,717 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::io::Write;
+
+use syn::ext::IdentExt;
+
+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, DeprecatedNoteKind, Documentation, Field,
+ GenericArgument, GenericParams, Item, ItemContainer, Path, Repr, ReprAlign, ReprStyle,
+ ToCondition, Type, Typedef,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::mangle;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::rename::{IdentifierType, RenameRule};
+use crate::bindgen::reserved;
+use crate::bindgen::utilities::IterHelpers;
+use crate::bindgen::writer::{ListType, Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct Struct {
+ pub path: Path,
+ pub export_name: String,
+ pub generic_params: GenericParams,
+ pub fields: Vec<Field>,
+ /// Whether there's a tag field on the body of this struct. When this is
+ /// true, is_enum_variant_body is also guaranteed to be true.
+ pub has_tag_field: bool,
+ /// Whether this is an enum variant body.
+ pub is_enum_variant_body: bool,
+ pub alignment: Option<ReprAlign>,
+ pub is_transparent: bool,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+ pub associated_constants: Vec<Constant>,
+}
+
+impl Struct {
+ /// Whether this struct can derive operator== / operator!=.
+ pub fn can_derive_eq(&self) -> bool {
+ !self.fields.is_empty() && self.fields.iter().all(|x| x.ty.can_cmp_eq())
+ }
+
+ pub fn add_associated_constant(&mut self, c: Constant) {
+ self.associated_constants.push(c);
+ }
+
+ pub fn load(
+ layout_config: &LayoutConfig,
+ item: &syn::ItemStruct,
+ mod_cfg: Option<&Cfg>,
+ ) -> Result<Self, String> {
+ let repr = Repr::load(&item.attrs)?;
+ let is_transparent = match repr.style {
+ ReprStyle::C => false,
+ ReprStyle::Transparent => true,
+ _ => {
+ return Err("Struct is not marked #[repr(C)] or #[repr(transparent)].".to_owned());
+ }
+ };
+
+ let path = Path::new(item.ident.unraw().to_string());
+
+ // Ensure we can safely represent the struct given the configuration.
+ if let Some(align) = repr.align {
+ layout_config.ensure_safe_to_represent(&align)?;
+ }
+
+ let fields = match item.fields {
+ syn::Fields::Unit => Vec::new(),
+ syn::Fields::Named(ref fields) => fields
+ .named
+ .iter()
+ .try_skip_map(|field| Field::load(field, &path))?,
+ syn::Fields::Unnamed(ref fields) => {
+ let mut out = Vec::new();
+ let mut current = 0;
+ for field in fields.unnamed.iter() {
+ if let Some(mut ty) = Type::load(&field.ty)? {
+ ty.replace_self_with(&path);
+ out.push(Field {
+ name: format!("{}", current),
+ ty,
+ cfg: Cfg::load(&field.attrs),
+ annotations: AnnotationSet::load(&field.attrs)?,
+ documentation: Documentation::load(&field.attrs),
+ });
+ current += 1;
+ }
+ }
+ out
+ }
+ };
+
+ let has_tag_field = false;
+ let is_enum_variant_body = false;
+
+ Ok(Struct::new(
+ path,
+ GenericParams::load(&item.generics)?,
+ fields,
+ has_tag_field,
+ is_enum_variant_body,
+ repr.align,
+ is_transparent,
+ Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
+ AnnotationSet::load(&item.attrs)?,
+ Documentation::load(&item.attrs),
+ ))
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ path: Path,
+ generic_params: GenericParams,
+ fields: Vec<Field>,
+ has_tag_field: bool,
+ is_enum_variant_body: bool,
+ alignment: Option<ReprAlign>,
+ is_transparent: bool,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ generic_params,
+ fields,
+ has_tag_field,
+ is_enum_variant_body,
+ alignment,
+ is_transparent,
+ cfg,
+ annotations,
+ documentation,
+ associated_constants: vec![],
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ for field in &mut self.fields {
+ field.ty.simplify_standard_types(config);
+ }
+ }
+
+ pub fn is_generic(&self) -> bool {
+ self.generic_params.len() > 0
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ // Generic structs can instantiate monomorphs only once they've been
+ // instantiated. See `instantiate_monomorph` for more details.
+ if self.is_generic() {
+ return;
+ }
+
+ for field in &self.fields {
+ field.ty.add_monomorphs(library, out);
+ }
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ for field in &mut self.fields {
+ field.ty.mangle_paths(monomorphs);
+ }
+ }
+
+ pub fn specialize(
+ &self,
+ generic_values: &[GenericArgument],
+ mappings: &[(&Path, &GenericArgument)],
+ config: &Config,
+ ) -> Self {
+ let mangled_path = mangle::mangle_path(&self.path, generic_values, &config.export.mangle);
+ Struct::new(
+ mangled_path,
+ GenericParams::default(),
+ self.fields
+ .iter()
+ .map(|field| Field {
+ name: field.name.clone(),
+ ty: field.ty.specialize(mappings),
+ cfg: field.cfg.clone(),
+ annotations: field.annotations.clone(),
+ documentation: field.documentation.clone(),
+ })
+ .collect(),
+ self.has_tag_field,
+ self.is_enum_variant_body,
+ self.alignment,
+ self.is_transparent,
+ self.cfg.clone(),
+ self.annotations.clone(),
+ self.documentation.clone(),
+ )
+ }
+
+ fn emit_bitflags_binop<F: Write>(
+ &self,
+ constexpr_prefix: &str,
+ operator: char,
+ other: &str,
+ out: &mut SourceWriter<F>,
+ ) {
+ let bits = &self.fields[0].name;
+ out.new_line();
+ write!(
+ out,
+ "{}{} operator{}(const {}& {}) const",
+ constexpr_prefix,
+ self.export_name(),
+ operator,
+ self.export_name(),
+ other
+ );
+ out.open_brace();
+ write!(
+ out,
+ "return {} {{ static_cast<decltype({bits})>(this->{bits} {operator} {other}.{bits}) }};",
+ self.export_name()
+ );
+ out.close_brace(false);
+
+ out.new_line();
+ write!(
+ out,
+ "{}& operator{}=(const {}& {})",
+ self.export_name(),
+ operator,
+ self.export_name(),
+ other
+ );
+ out.open_brace();
+ write!(out, "*this = (*this {} {});", operator, other);
+ out.new_line();
+ write!(out, "return *this;");
+ out.close_brace(false);
+ }
+}
+
+impl Item for Struct {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Struct(self.clone())
+ }
+
+ fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
+ if self.is_transparent {
+ resolver.add_none(&self.path);
+ } else {
+ resolver.add_struct(&self.path);
+ }
+ }
+
+ fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ for field in &mut self.fields {
+ field.ty.resolve_declaration_types(resolver);
+ }
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ // Rename the name of the struct
+ if !(self.has_tag_field && config.language == Language::Cxx) {
+ config.export.rename(&mut self.export_name);
+ }
+
+ // Rename the types used in fields
+ {
+ let fields = self.fields.iter_mut().skip(self.has_tag_field as usize);
+ for field in fields {
+ field.ty.rename_for_config(config, &self.generic_params);
+ }
+ }
+
+ // Apply renaming rules to fields in the following order
+ // 1. `cbindgen::field-names` annotation
+ // 2. `cbindgen::rename-all` annotation
+ // 3. config struct rename rule
+ // If the struct is a tuple struct and we have not renamed the
+ // fields, then prefix each of them with an underscore.
+ // If any field is a reserved keyword, then postfix it with an
+ // underscore.
+
+ // Scope for mutable borrow of fields
+ {
+ let names = self.fields.iter_mut().map(|field| &mut field.name);
+
+ let field_rules = self
+ .annotations
+ .parse_atom::<RenameRule>("rename-all")
+ .unwrap_or(config.structure.rename_fields);
+
+ if let Some(o) = self.annotations.list("field-names") {
+ for (dest, src) in names.zip(o) {
+ *dest = src;
+ }
+ } else if let Some(r) = field_rules.not_none() {
+ for name in names {
+ *name = r.apply(name, IdentifierType::StructMember).into_owned();
+ }
+ } else {
+ // If we don't have any rules for a tuple struct, prefix them with
+ // an underscore so it still compiles.
+ for name in names {
+ if name.starts_with(|c: char| c.is_ascii_digit()) {
+ name.insert(0, '_');
+ }
+ }
+ }
+ }
+
+ for field in &mut self.fields {
+ reserved::escape(&mut field.name);
+ }
+
+ for c in self.associated_constants.iter_mut() {
+ c.rename_for_config(config);
+ }
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ let mut fields = self.fields.iter();
+
+ // If there is a tag field, skip it
+ if self.has_tag_field {
+ fields.next();
+ }
+
+ for field in fields {
+ field
+ .ty
+ .add_dependencies_ignoring_generics(&self.generic_params, library, out);
+ }
+
+ for c in &self.associated_constants {
+ c.add_dependencies(library, out);
+ }
+ }
+
+ fn instantiate_monomorph(
+ &self,
+ generic_values: &[GenericArgument],
+ library: &Library,
+ out: &mut Monomorphs,
+ ) {
+ 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());
+ }
+}
+
+impl Source for Struct {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ if self.is_transparent {
+ let typedef = Typedef {
+ path: self.path.clone(),
+ export_name: self.export_name.to_owned(),
+ generic_params: self.generic_params.clone(),
+ aliased: self.fields[0].ty.clone(),
+ cfg: self.cfg.clone(),
+ annotations: self.annotations.clone(),
+ documentation: self.documentation.clone(),
+ };
+ typedef.write(config, out);
+ for constant in &self.associated_constants {
+ out.new_line();
+ constant.write(config, out, Some(self));
+ }
+ return;
+ }
+
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ self.documentation.write(config, out);
+
+ if !self.is_enum_variant_body {
+ self.generic_params.write(config, out);
+ }
+
+ // The following results in
+ // C++ or C with Tag as style:
+ // struct Name {
+ // C with Type only style:
+ // typedef struct {
+ // C with Both as style:
+ // typedef struct Name {
+ match config.language {
+ Language::C if config.style.generate_typedef() => out.write("typedef "),
+ Language::C | Language::Cxx => {}
+ Language::Cython => out.write(config.style.cython_def()),
+ }
+
+ // Cython extern declarations don't manage layouts, layouts are defined entierly by the
+ // corresponding C code. So this `packed` is only for documentation, and missing
+ // `aligned(n)` is also not a problem.
+ if config.language == Language::Cython {
+ if let Some(align) = self.alignment {
+ match align {
+ ReprAlign::Packed => out.write("packed "),
+ ReprAlign::Align(_) => {} // Not supported
+ }
+ }
+ }
+
+ out.write("struct");
+
+ if config.language != Language::Cython {
+ if let Some(align) = self.alignment {
+ match align {
+ ReprAlign::Packed => {
+ if let Some(ref anno) = config.layout.packed {
+ write!(out, " {}", anno);
+ }
+ }
+ ReprAlign::Align(n) => {
+ if let Some(ref anno) = config.layout.aligned_n {
+ write!(out, " {}({})", anno, n);
+ }
+ }
+ }
+ }
+ }
+
+ if self.annotations.must_use(config) {
+ if let Some(ref anno) = config.structure.must_use {
+ write!(out, " {}", anno);
+ }
+ }
+ if let Some(note) = self
+ .annotations
+ .deprecated_note(config, DeprecatedNoteKind::Struct)
+ {
+ write!(out, " {}", note);
+ }
+
+ if config.language != Language::C || config.style.generate_tag() {
+ write!(out, " {}", self.export_name());
+ }
+
+ out.open_brace();
+
+ // Emit the pre_body section, if relevant
+ if let Some(body) = config.export.pre_body(&self.path) {
+ out.write_raw_block(body);
+ out.new_line();
+ }
+
+ out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
+ if config.language == Language::Cython && self.fields.is_empty() {
+ out.write("pass");
+ }
+
+ if config.language == Language::Cxx {
+ let mut wrote_start_newline = false;
+
+ if config.structure.derive_constructor(&self.annotations) && !self.fields.is_empty() {
+ if !wrote_start_newline {
+ wrote_start_newline = true;
+ out.new_line();
+ }
+
+ out.new_line();
+
+ let arg_renamer = |name: &str| {
+ config
+ .function
+ .rename_args
+ .apply(name, IdentifierType::FunctionArg)
+ .into_owned()
+ };
+ write!(out, "{}(", self.export_name());
+ let vec: Vec<_> = self
+ .fields
+ .iter()
+ .map(|field| {
+ Field::from_name_and_type(
+ // const-ref args to constructor
+ format!("const& {}", arg_renamer(&field.name)),
+ field.ty.clone(),
+ )
+ })
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(","));
+ write!(out, ")");
+ out.new_line();
+ write!(out, " : ");
+ let vec: Vec<_> = self
+ .fields
+ .iter()
+ .map(|field| format!("{}({})", field.name, arg_renamer(&field.name)))
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(","));
+ out.new_line();
+ write!(out, "{{}}");
+ out.new_line();
+ }
+
+ let other = config
+ .function
+ .rename_args
+ .apply("other", IdentifierType::FunctionArg);
+
+ if self
+ .annotations
+ .bool("internal-derive-bitflags")
+ .unwrap_or(false)
+ {
+ assert_eq!(self.fields.len(), 1);
+ let bits = &self.fields[0].name;
+ if !wrote_start_newline {
+ wrote_start_newline = true;
+ out.new_line();
+ }
+ let constexpr_prefix = if config.constant.allow_constexpr {
+ "constexpr "
+ } else {
+ ""
+ };
+
+ out.new_line();
+ write!(out, "{}explicit operator bool() const", constexpr_prefix);
+ out.open_brace();
+ write!(out, "return !!{bits};");
+ out.close_brace(false);
+
+ out.new_line();
+ write!(
+ out,
+ "{}{} operator~() const",
+ constexpr_prefix,
+ self.export_name()
+ );
+ out.open_brace();
+ write!(
+ out,
+ "return {} {{ static_cast<decltype({bits})>(~{bits}) }};",
+ self.export_name()
+ );
+ out.close_brace(false);
+ self.emit_bitflags_binop(constexpr_prefix, '|', &other, out);
+ self.emit_bitflags_binop(constexpr_prefix, '&', &other, out);
+ self.emit_bitflags_binop(constexpr_prefix, '^', &other, out);
+ }
+
+ // Generate a serializer function that allows dumping this struct
+ // to an std::ostream. It's defined as a friend function inside the
+ // struct definition, and doesn't need the `inline` keyword even
+ // though it's implemented right in the generated header file.
+ if config.structure.derive_ostream(&self.annotations) {
+ if !wrote_start_newline {
+ wrote_start_newline = true;
+ out.new_line();
+ }
+
+ out.new_line();
+ let stream = config
+ .function
+ .rename_args
+ .apply("stream", IdentifierType::FunctionArg);
+ let instance = config
+ .function
+ .rename_args
+ .apply("instance", IdentifierType::FunctionArg);
+ write!(
+ out,
+ "friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
+ stream,
+ self.export_name(),
+ instance,
+ );
+ out.open_brace();
+ write!(out, "return {} << \"{{ \"", stream);
+ let vec: Vec<_> = self
+ .fields
+ .iter()
+ .map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name))
+ .collect();
+ out.write_vertical_source_list(&vec[..], ListType::Join(" << \", \""));
+ out.write(" << \" }\";");
+ out.close_brace(false);
+ }
+
+ let skip_fields = self.has_tag_field as usize;
+
+ macro_rules! emit_op {
+ ($op_name:expr, $op:expr, $conjuc:expr) => {{
+ if !wrote_start_newline {
+ #[allow(unused_assignments)]
+ {
+ wrote_start_newline = true;
+ }
+ out.new_line();
+ }
+
+ out.new_line();
+
+ if let Some(Some(attrs)) =
+ self.annotations.atom(concat!($op_name, "-attributes"))
+ {
+ write!(out, "{} ", attrs);
+ }
+
+ write!(
+ out,
+ "bool operator{}(const {}& {}) const",
+ $op,
+ self.export_name(),
+ other
+ );
+ out.open_brace();
+ out.write("return ");
+ let vec: Vec<_> = self
+ .fields
+ .iter()
+ .skip(skip_fields)
+ .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name))
+ .collect();
+ out.write_vertical_source_list(
+ &vec[..],
+ ListType::Join(&format!(" {}", $conjuc)),
+ );
+ out.write(";");
+ out.close_brace(false);
+ }};
+ }
+
+ if config.structure.derive_eq(&self.annotations) && self.can_derive_eq() {
+ emit_op!("eq", "==", "&&");
+ }
+ if config.structure.derive_neq(&self.annotations) && self.can_derive_eq() {
+ emit_op!("neq", "!=", "||");
+ }
+ if config.structure.derive_lt(&self.annotations)
+ && self.fields.len() == 1
+ && self.fields[0].ty.can_cmp_order()
+ {
+ emit_op!("lt", "<", "&&");
+ }
+ if config.structure.derive_lte(&self.annotations)
+ && self.fields.len() == 1
+ && self.fields[0].ty.can_cmp_order()
+ {
+ emit_op!("lte", "<=", "&&");
+ }
+ if config.structure.derive_gt(&self.annotations)
+ && self.fields.len() == 1
+ && self.fields[0].ty.can_cmp_order()
+ {
+ emit_op!("gt", ">", "&&");
+ }
+ if config.structure.derive_gte(&self.annotations)
+ && self.fields.len() == 1
+ && self.fields[0].ty.can_cmp_order()
+ {
+ emit_op!("gte", ">=", "&&");
+ }
+ }
+
+ // Emit the post_body section, if relevant
+ if let Some(body) = config.export.post_body(&self.path) {
+ out.new_line();
+ out.write_raw_block(body);
+ }
+
+ if config.language == Language::Cxx
+ && config.structure.associated_constants_in_body
+ && config.constant.allow_static_const
+ {
+ for constant in &self.associated_constants {
+ out.new_line();
+ constant.write_declaration(config, out, self);
+ }
+ }
+
+ if config.language == Language::C && config.style.generate_typedef() {
+ out.close_brace(false);
+ write!(out, " {};", self.export_name());
+ } else {
+ out.close_brace(true);
+ }
+
+ for constant in &self.associated_constants {
+ out.new_line();
+ constant.write(config, out, Some(self));
+ }
+
+ condition.write_after(config, out);
+ }
+}
diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs
new file mode 100644
index 0000000..5a31fb6
--- /dev/null
+++ b/src/bindgen/ir/ty.rs
@@ -0,0 +1,1017 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::borrow::Cow;
+use std::io::Write;
+
+use syn::ext::IdentExt;
+
+use crate::bindgen::cdecl;
+use crate::bindgen::config::{Config, Language};
+use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
+use crate::bindgen::dependencies::Dependencies;
+use crate::bindgen::ir::{GenericArgument, GenericParams, GenericPath, Path};
+use crate::bindgen::library::Library;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::utilities::IterHelpers;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum PrimitiveType {
+ Void,
+ Bool,
+ Char,
+ SChar,
+ UChar,
+ Char32,
+ Float,
+ Double,
+ VaList,
+ PtrDiffT,
+ Integer {
+ zeroable: bool,
+ signed: bool,
+ kind: IntKind,
+ },
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum IntKind {
+ Short,
+ Int,
+ Long,
+ LongLong,
+ SizeT,
+ Size,
+ B8,
+ B16,
+ B32,
+ B64,
+}
+
+impl PrimitiveType {
+ pub fn maybe(path: &str) -> Option<PrimitiveType> {
+ Some(match path {
+ "c_void" => PrimitiveType::Void,
+ "c_char" => PrimitiveType::Char,
+ "c_schar" => PrimitiveType::SChar,
+ "c_uchar" => PrimitiveType::UChar,
+ "c_float" => PrimitiveType::Float,
+ "c_double" => PrimitiveType::Double,
+ "ptrdiff_t" => PrimitiveType::PtrDiffT,
+ "VaList" => PrimitiveType::VaList,
+ "bool" => PrimitiveType::Bool,
+ "char" => PrimitiveType::Char32,
+
+ "f32" => PrimitiveType::Float,
+ "f64" => PrimitiveType::Double,
+
+ _ => {
+ let (kind, signed) = match path {
+ "c_short" => (IntKind::Short, true),
+ "c_int" => (IntKind::Int, true),
+ "c_long" => (IntKind::Long, true),
+ "c_longlong" => (IntKind::LongLong, true),
+ "ssize_t" => (IntKind::SizeT, true),
+ "c_ushort" => (IntKind::Short, false),
+ "c_uint" => (IntKind::Int, false),
+ "c_ulong" => (IntKind::Long, false),
+ "c_ulonglong" => (IntKind::LongLong, false),
+ "size_t" => (IntKind::SizeT, false),
+ "RawFd" => (IntKind::Int, true),
+
+ "isize" | "intptr_t" => (IntKind::Size, true),
+ "usize" | "uintptr_t" => (IntKind::Size, false),
+
+ "u8" | "uint8_t" => (IntKind::B8, false),
+ "u16" | "uint16_t" => (IntKind::B16, false),
+ "u32" | "uint32_t" => (IntKind::B32, false),
+ "u64" | "uint64_t" => (IntKind::B64, false),
+ "i8" | "int8_t" => (IntKind::B8, true),
+ "i16" | "int16_t" => (IntKind::B16, true),
+ "i32" | "int32_t" => (IntKind::B32, true),
+ "i64" | "int64_t" => (IntKind::B64, true),
+ _ => return None,
+ };
+ PrimitiveType::Integer {
+ zeroable: true,
+ signed,
+ kind,
+ }
+ }
+ })
+ }
+
+ pub fn to_repr_rust(&self) -> &'static str {
+ match *self {
+ PrimitiveType::Bool => "bool",
+ PrimitiveType::Void => "c_void",
+ PrimitiveType::Char => "c_char",
+ PrimitiveType::SChar => "c_schar",
+ PrimitiveType::UChar => "c_uchar",
+ PrimitiveType::Char32 => "char",
+ PrimitiveType::Integer {
+ kind,
+ signed,
+ zeroable: _,
+ } => match kind {
+ IntKind::Short => {
+ if signed {
+ "c_short"
+ } else {
+ "c_ushort"
+ }
+ }
+ IntKind::Int => {
+ if signed {
+ "c_int"
+ } else {
+ "c_uint"
+ }
+ }
+ IntKind::Long => {
+ if signed {
+ "c_long"
+ } else {
+ "c_ulong"
+ }
+ }
+ IntKind::LongLong => {
+ if signed {
+ "c_longlong"
+ } else {
+ "c_ulonglong"
+ }
+ }
+ IntKind::SizeT => {
+ if signed {
+ "ssize_t"
+ } else {
+ "size_t"
+ }
+ }
+ IntKind::Size => {
+ if signed {
+ "isize"
+ } else {
+ "usize"
+ }
+ }
+ IntKind::B8 => {
+ if signed {
+ "i8"
+ } else {
+ "u8"
+ }
+ }
+ IntKind::B16 => {
+ if signed {
+ "i16"
+ } else {
+ "u16"
+ }
+ }
+ IntKind::B32 => {
+ if signed {
+ "i32"
+ } else {
+ "u32"
+ }
+ }
+ IntKind::B64 => {
+ if signed {
+ "i64"
+ } else {
+ "u64"
+ }
+ }
+ },
+ PrimitiveType::Float => "f32",
+ PrimitiveType::Double => "f64",
+ PrimitiveType::PtrDiffT => "ptrdiff_t",
+ PrimitiveType::VaList => "va_list",
+ }
+ }
+
+ pub fn to_repr_c(&self, config: &Config) -> &'static str {
+ match *self {
+ PrimitiveType::Void => "void",
+ PrimitiveType::Bool => "bool",
+ PrimitiveType::Char => "char",
+ PrimitiveType::SChar => "signed char",
+ PrimitiveType::UChar => "unsigned char",
+ // NOTE: It'd be nice to use a char32_t, but:
+ //
+ // * uchar.h is not present on mac (see #423).
+ //
+ // * char32_t isn't required to be compatible with Rust's char, as
+ // the C++ spec only requires it to be the same size as
+ // uint_least32_t, which is _not_ guaranteed to be 4-bytes.
+ //
+ PrimitiveType::Char32 => "uint32_t",
+ PrimitiveType::Integer {
+ kind,
+ signed,
+ zeroable: _,
+ } => match kind {
+ IntKind::Short => {
+ if signed {
+ "short"
+ } else {
+ "unsigned short"
+ }
+ }
+ IntKind::Int => {
+ if signed {
+ "int"
+ } else {
+ "unsigned int"
+ }
+ }
+ IntKind::Long => {
+ if signed {
+ "long"
+ } else {
+ "unsigned long"
+ }
+ }
+ IntKind::LongLong => {
+ if signed {
+ "long long"
+ } else {
+ "unsigned long long"
+ }
+ }
+ IntKind::SizeT => {
+ if signed {
+ "ssize_t"
+ } else {
+ "size_t"
+ }
+ }
+ IntKind::Size => {
+ if config.usize_is_size_t {
+ if signed {
+ "ptrdiff_t"
+ } else {
+ "size_t"
+ }
+ } else if signed {
+ "intptr_t"
+ } else {
+ "uintptr_t"
+ }
+ }
+ IntKind::B8 => {
+ if signed {
+ "int8_t"
+ } else {
+ "uint8_t"
+ }
+ }
+ IntKind::B16 => {
+ if signed {
+ "int16_t"
+ } else {
+ "uint16_t"
+ }
+ }
+ IntKind::B32 => {
+ if signed {
+ "int32_t"
+ } else {
+ "uint32_t"
+ }
+ }
+ IntKind::B64 => {
+ if signed {
+ "int64_t"
+ } else {
+ "uint64_t"
+ }
+ }
+ },
+ PrimitiveType::Float => "float",
+ PrimitiveType::Double => "double",
+ PrimitiveType::PtrDiffT => "ptrdiff_t",
+ PrimitiveType::VaList => "va_list",
+ }
+ }
+
+ fn can_cmp_order(&self) -> bool {
+ !matches!(*self, PrimitiveType::Bool)
+ }
+
+ fn can_cmp_eq(&self) -> bool {
+ true
+ }
+}
+
+/// Constant expressions.
+///
+/// Used for the `U` part of `[T; U]` and const generics. We support a very
+/// limited vocabulary here: only identifiers and literals.
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum ConstExpr {
+ Name(String),
+ Value(String),
+}
+
+impl ConstExpr {
+ pub fn as_str(&self) -> &str {
+ match *self {
+ ConstExpr::Name(ref string) | ConstExpr::Value(ref string) => string,
+ }
+ }
+
+ pub fn rename_for_config(&mut self, config: &Config) {
+ if let ConstExpr::Name(ref mut name) = self {
+ config.export.rename(name);
+ }
+ }
+
+ pub fn load(expr: &syn::Expr) -> Result<Self, String> {
+ match *expr {
+ syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
+ let val = match *lit {
+ syn::Lit::Bool(syn::LitBool { value, .. }) => value.to_string(),
+ syn::Lit::Int(ref len) => len.base10_digits().to_string(),
+ syn::Lit::Byte(ref byte) => u8::to_string(&byte.value()),
+ syn::Lit::Char(ref ch) => u32::to_string(&ch.value().into()),
+ _ => return Err(format!("can't handle const expression {:?}", lit)),
+ };
+ Ok(ConstExpr::Value(val))
+ }
+ syn::Expr::Path(ref path) => {
+ let generic_path = GenericPath::load(&path.path)?;
+ Ok(ConstExpr::Name(generic_path.export_name().to_owned()))
+ }
+ _ => Err(format!("can't handle const expression {:?}", expr)),
+ }
+ }
+
+ pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> ConstExpr {
+ match *self {
+ ConstExpr::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 ConstExpr::Name(path.name().to_string());
+ }
+ GenericArgument::Const(ref expr) => {
+ return expr.clone();
+ }
+ _ => {
+ // unsupported argument type - really should be an error
+ }
+ }
+ }
+ }
+ }
+ ConstExpr::Value(_) => {}
+ }
+ self.clone()
+ }
+}
+
+impl Source for ConstExpr {
+ fn write<F: Write>(&self, _config: &Config, out: &mut SourceWriter<F>) {
+ write!(out, "{}", self.as_str());
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum Type {
+ Ptr {
+ ty: Box<Type>,
+ is_const: bool,
+ is_nullable: bool,
+ // FIXME: This is a bit of a hack, this is only to get us to codegen
+ // `T&` / `const T&`, but we should probably pass that down as an option
+ // to code generation or something.
+ is_ref: bool,
+ },
+ Path(GenericPath),
+ Primitive(PrimitiveType),
+ Array(Box<Type>, ConstExpr),
+ FuncPtr {
+ ret: Box<Type>,
+ args: Vec<(Option<String>, Type)>,
+ is_nullable: bool,
+ never_return: bool,
+ },
+}
+
+impl Type {
+ pub fn const_ref_to(ty: &Self) -> Self {
+ Type::Ptr {
+ ty: Box::new(ty.clone()),
+ is_const: true,
+ is_nullable: false,
+ is_ref: true,
+ }
+ }
+
+ pub fn load_from_output(output: &syn::ReturnType) -> Result<(Type, bool), String> {
+ let mut never_return = false;
+ let ty = match output {
+ syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
+ syn::ReturnType::Type(_, ref ty) => {
+ if let syn::Type::Never(_) = ty.as_ref() {
+ never_return = true;
+ Type::Primitive(PrimitiveType::Void)
+ } else {
+ Type::load(ty)?.unwrap_or(Type::Primitive(PrimitiveType::Void))
+ }
+ }
+ };
+ Ok((ty, never_return))
+ }
+
+ pub fn load(ty: &syn::Type) -> Result<Option<Type>, String> {
+ let converted = match *ty {
+ syn::Type::Reference(ref reference) => {
+ let converted = Type::load(&reference.elem)?;
+
+ let converted = match converted {
+ Some(converted) => converted,
+ None => Type::Primitive(PrimitiveType::Void),
+ };
+
+ // TODO(emilio): we could make these use is_ref: true.
+ let is_const = reference.mutability.is_none();
+ Type::Ptr {
+ ty: Box::new(converted),
+ is_const,
+ is_nullable: false,
+ is_ref: false,
+ }
+ }
+ syn::Type::Ptr(ref pointer) => {
+ let converted = Type::load(&pointer.elem)?;
+
+ let converted = match converted {
+ Some(converted) => converted,
+ None => Type::Primitive(PrimitiveType::Void),
+ };
+
+ let is_const = pointer.mutability.is_none();
+ Type::Ptr {
+ ty: Box::new(converted),
+ is_const,
+ is_nullable: true,
+ is_ref: false,
+ }
+ }
+ syn::Type::Path(ref path) => {
+ let generic_path = GenericPath::load(&path.path)?;
+
+ if generic_path.name() == "PhantomData" || generic_path.name() == "PhantomPinned" {
+ return Ok(None);
+ }
+
+ if let Some(prim) = PrimitiveType::maybe(generic_path.name()) {
+ if !generic_path.generics().is_empty() {
+ return Err("Primitive has generics.".to_owned());
+ }
+ Type::Primitive(prim)
+ } else {
+ Type::Path(generic_path)
+ }
+ }
+ syn::Type::Array(syn::TypeArray {
+ ref elem, 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 = ConstExpr::load(len)?;
+ Type::Array(Box::new(converted), len)
+ }
+ syn::Type::BareFn(ref function) => {
+ let mut wildcard_counter = 0;
+ let args = function.inputs.iter().try_skip_map(|x| {
+ Type::load(&x.ty).map(|opt_ty| {
+ opt_ty.map(|ty| {
+ (
+ x.name.as_ref().map(|(ref ident, _)| {
+ if ident == "_" {
+ wildcard_counter += 1;
+ if wildcard_counter == 1 {
+ "_".to_owned()
+ } else {
+ format!("_{}", wildcard_counter - 1)
+ }
+ } else {
+ ident.unraw().to_string()
+ }
+ }),
+ ty,
+ )
+ })
+ })
+ })?;
+ let (ret, never_return) = Type::load_from_output(&function.output)?;
+ Type::FuncPtr {
+ ret: Box::new(ret),
+ args,
+ is_nullable: false,
+ never_return,
+ }
+ }
+ syn::Type::Tuple(ref tuple) => {
+ if tuple.elems.is_empty() {
+ return Ok(None);
+ }
+ return Err("Tuples are not supported types.".to_owned());
+ }
+ syn::Type::Verbatim(ref tokens) if tokens.to_string() == "..." => {
+ Type::Primitive(PrimitiveType::VaList)
+ }
+ _ => return Err(format!("Unsupported type: {:?}", ty)),
+ };
+
+ Ok(Some(converted))
+ }
+
+ pub fn is_ptr(&self) -> bool {
+ matches!(*self, Type::Ptr { .. } | Type::FuncPtr { .. })
+ }
+
+ pub fn is_primitive_or_ptr_primitive(&self) -> bool {
+ match *self {
+ Type::Primitive(..) => true,
+ Type::Ptr { ref ty, .. } => matches!(ty.as_ref(), Type::Primitive(..)),
+ _ => false,
+ }
+ }
+
+ pub fn make_zeroable(&self) -> Option<Self> {
+ let (kind, signed) = match *self {
+ Type::Primitive(PrimitiveType::Integer {
+ zeroable: false,
+ kind,
+ signed,
+ }) => (kind, signed),
+ _ => return None,
+ };
+
+ Some(Type::Primitive(PrimitiveType::Integer {
+ kind,
+ signed,
+ zeroable: true,
+ }))
+ }
+
+ pub fn make_nullable(&self) -> Option<Self> {
+ match *self {
+ Type::Ptr {
+ ref ty,
+ is_const,
+ is_ref,
+ is_nullable: false,
+ } => Some(Type::Ptr {
+ ty: ty.clone(),
+ is_const,
+ is_ref,
+ is_nullable: true,
+ }),
+ Type::FuncPtr {
+ ref ret,
+ ref args,
+ is_nullable: false,
+ never_return,
+ } => Some(Type::FuncPtr {
+ ret: ret.clone(),
+ args: args.clone(),
+ is_nullable: true,
+ never_return,
+ }),
+ _ => None,
+ }
+ }
+
+ fn nonzero_to_primitive(&self) -> Option<Self> {
+ let path = match *self {
+ Type::Path(ref p) => p,
+ _ => return None,
+ };
+
+ if !path.generics().is_empty() {
+ return None;
+ }
+
+ let name = path.name();
+ if !name.starts_with("NonZero") {
+ return None;
+ }
+
+ let (kind, signed) = match path.name() {
+ "NonZeroU8" => (IntKind::B8, false),
+ "NonZeroU16" => (IntKind::B16, false),
+ "NonZeroU32" => (IntKind::B32, false),
+ "NonZeroU64" => (IntKind::B64, false),
+ "NonZeroUSize" => (IntKind::Size, false),
+ "NonZeroI8" => (IntKind::B8, true),
+ "NonZeroI16" => (IntKind::B16, true),
+ "NonZeroI32" => (IntKind::B32, true),
+ "NonZeroI64" => (IntKind::B64, true),
+ "NonZeroISize" => (IntKind::Size, true),
+ _ => return None,
+ };
+
+ Some(Type::Primitive(PrimitiveType::Integer {
+ zeroable: false,
+ signed,
+ kind,
+ }))
+ }
+
+ fn simplified_type(&self, config: &Config) -> Option<Self> {
+ let path = match *self {
+ Type::Path(ref p) => p,
+ _ => return None,
+ };
+
+ if path.generics().is_empty() {
+ return self.nonzero_to_primitive();
+ }
+
+ if path.generics().len() != 1 {
+ return None;
+ }
+
+ 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),
+ };
+ match path.name() {
+ "Option" => {
+ if let Some(nullable) = generic.make_nullable() {
+ return Some(nullable);
+ }
+ if let Some(zeroable) = generic.make_zeroable() {
+ return Some(zeroable);
+ }
+ None
+ }
+ "NonNull" => Some(Type::Ptr {
+ ty: Box::new(generic.into_owned()),
+ is_const: false,
+ is_nullable: false,
+ is_ref: false,
+ }),
+ "Box" if config.language != Language::Cxx => Some(Type::Ptr {
+ ty: Box::new(generic.into_owned()),
+ is_const: false,
+ is_nullable: false,
+ is_ref: false,
+ }),
+ "Cell" => Some(generic.into_owned()),
+ "ManuallyDrop" | "MaybeUninit" | "Pin" if config.language != Language::Cxx => {
+ Some(generic.into_owned())
+ }
+ _ => None,
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ self.visit_types(|ty| ty.simplify_standard_types(config));
+ if let Some(ty) = self.simplified_type(config) {
+ *self = ty;
+ }
+ }
+
+ pub fn replace_self_with(&mut self, self_ty: &Path) {
+ if let Type::Path(ref mut generic_path) = *self {
+ generic_path.replace_self_with(self_ty);
+ }
+ self.visit_types(|ty| ty.replace_self_with(self_ty))
+ }
+
+ fn visit_types(&mut self, mut visitor: impl FnMut(&mut Type)) {
+ match *self {
+ Type::Array(ref mut ty, ..) | Type::Ptr { ref mut ty, .. } => visitor(ty),
+ Type::Path(ref mut path) => {
+ for generic in path.generics_mut() {
+ match *generic {
+ GenericArgument::Type(ref mut ty) => visitor(ty),
+ GenericArgument::Const(_) => {}
+ }
+ }
+ }
+ Type::Primitive(..) => {}
+ Type::FuncPtr {
+ ref mut ret,
+ ref mut args,
+ ..
+ } => {
+ visitor(ret);
+ for arg in args {
+ visitor(&mut arg.1)
+ }
+ }
+ }
+ }
+
+ pub fn get_root_path(&self) -> Option<Path> {
+ let mut current = self;
+ loop {
+ match *current {
+ Type::Ptr { ref ty, .. } => current = ty,
+ Type::Path(ref generic) => {
+ return Some(generic.path().clone());
+ }
+ Type::Primitive(..) => {
+ return None;
+ }
+ Type::Array(..) => {
+ return None;
+ }
+ Type::FuncPtr { .. } => {
+ return None;
+ }
+ };
+ }
+ }
+
+ pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> Type {
+ match *self {
+ Type::Ptr {
+ ref ty,
+ is_const,
+ is_nullable,
+ is_ref,
+ } => Type::Ptr {
+ ty: Box::new(ty.specialize(mappings)),
+ is_const,
+ is_nullable,
+ is_ref,
+ },
+ Type::Path(ref generic_path) => {
+ for &(param, value) in mappings {
+ if generic_path.path() == param {
+ if let GenericArgument::Type(ref ty) = *value {
+ return ty.clone();
+ }
+ }
+ }
+
+ let specialized = GenericPath::new(
+ generic_path.path().clone(),
+ generic_path
+ .generics()
+ .iter()
+ .map(|x| x.specialize(mappings))
+ .collect(),
+ );
+ 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.specialize(mappings),
+ ),
+ Type::FuncPtr {
+ ref ret,
+ ref args,
+ is_nullable,
+ never_return,
+ } => Type::FuncPtr {
+ ret: Box::new(ret.specialize(mappings)),
+ args: args
+ .iter()
+ .cloned()
+ .map(|(name, ty)| (name, ty.specialize(mappings)))
+ .collect(),
+ is_nullable,
+ never_return,
+ },
+ }
+ }
+
+ pub fn add_dependencies_ignoring_generics(
+ &self,
+ generic_params: &GenericParams,
+ library: &Library,
+ out: &mut Dependencies,
+ ) {
+ match *self {
+ Type::Ptr { ref ty, .. } => {
+ ty.add_dependencies_ignoring_generics(generic_params, library, out);
+ }
+ Type::Path(ref generic) => {
+ for generic_value in generic.generics() {
+ if let GenericArgument::Type(ref ty) = *generic_value {
+ ty.add_dependencies_ignoring_generics(generic_params, library, out);
+ }
+ }
+ let path = generic.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());
+
+ for item in &items {
+ item.deref().add_dependencies(library, out);
+ }
+ for item in items {
+ out.order.push(item);
+ }
+ }
+ } else {
+ warn!(
+ "Can't find {}. This usually means that this type was incompatible or \
+ not found.",
+ path
+ );
+ }
+ }
+ }
+ Type::Primitive(_) => {}
+ Type::Array(ref ty, _) => {
+ ty.add_dependencies_ignoring_generics(generic_params, library, out);
+ }
+ Type::FuncPtr {
+ ref ret, ref args, ..
+ } => {
+ ret.add_dependencies_ignoring_generics(generic_params, library, out);
+ for (_, ref arg) in args {
+ arg.add_dependencies_ignoring_generics(generic_params, library, out);
+ }
+ }
+ }
+ }
+
+ pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ self.add_dependencies_ignoring_generics(&GenericParams::default(), library, out)
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ match *self {
+ Type::Ptr { ref ty, .. } => {
+ ty.add_monomorphs(library, out);
+ }
+ Type::Path(ref generic) => {
+ if generic.generics().is_empty() || out.contains(generic) {
+ return;
+ }
+ let path = generic.path();
+ if let Some(items) = library.get_items(path) {
+ for item in items {
+ item.deref()
+ .instantiate_monomorph(generic.generics(), library, out);
+ }
+ }
+ }
+ Type::Primitive(_) => {}
+ Type::Array(ref ty, _) => {
+ ty.add_monomorphs(library, out);
+ }
+ Type::FuncPtr {
+ ref ret, ref args, ..
+ } => {
+ ret.add_monomorphs(library, out);
+ for (_, ref arg) in args {
+ arg.add_monomorphs(library, out);
+ }
+ }
+ }
+ }
+
+ pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) {
+ match *self {
+ Type::Ptr { ref mut ty, .. } => {
+ ty.rename_for_config(config, generic_params);
+ }
+ Type::Path(ref mut ty) => {
+ ty.rename_for_config(config, generic_params);
+ }
+ Type::Primitive(_) => {}
+ Type::Array(ref mut ty, ref mut len) => {
+ ty.rename_for_config(config, generic_params);
+ len.rename_for_config(config);
+ }
+ Type::FuncPtr {
+ ref mut ret,
+ ref mut args,
+ ..
+ } => {
+ ret.rename_for_config(config, generic_params);
+ for (_, arg) in args {
+ arg.rename_for_config(config, generic_params);
+ }
+ }
+ }
+ }
+
+ pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ match *self {
+ Type::Ptr { ref mut ty, .. } => {
+ ty.resolve_declaration_types(resolver);
+ }
+ Type::Path(ref mut generic_path) => {
+ generic_path.resolve_declaration_types(resolver);
+ }
+ Type::Primitive(_) => {}
+ Type::Array(ref mut ty, _) => {
+ ty.resolve_declaration_types(resolver);
+ }
+ Type::FuncPtr {
+ ref mut ret,
+ ref mut args,
+ ..
+ } => {
+ ret.resolve_declaration_types(resolver);
+ for (_, ref mut arg) in args {
+ arg.resolve_declaration_types(resolver);
+ }
+ }
+ }
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ match *self {
+ Type::Ptr { ref mut ty, .. } => {
+ ty.mangle_paths(monomorphs);
+ }
+ Type::Path(ref mut generic_path) => {
+ if generic_path.generics().is_empty() {
+ return;
+ }
+
+ if let Some(mangled_path) = monomorphs.mangle_path(generic_path) {
+ *generic_path = GenericPath::new(mangled_path.clone(), vec![]);
+ } else {
+ warn!(
+ "Cannot find a mangling for generic path {:?}. This usually means that a \
+ type referenced by this generic was incompatible or not found.",
+ generic_path
+ );
+ }
+ }
+ Type::Primitive(_) => {}
+ Type::Array(ref mut ty, _) => {
+ ty.mangle_paths(monomorphs);
+ }
+ Type::FuncPtr {
+ ref mut ret,
+ ref mut args,
+ ..
+ } => {
+ ret.mangle_paths(monomorphs);
+ for (_, ref mut arg) in args {
+ arg.mangle_paths(monomorphs);
+ }
+ }
+ }
+ }
+
+ pub fn can_cmp_order(&self) -> bool {
+ match *self {
+ // FIXME: Shouldn't this look at ty.can_cmp_order() as well?
+ Type::Ptr { is_ref, .. } => !is_ref,
+ Type::Path(..) => true,
+ Type::Primitive(ref p) => p.can_cmp_order(),
+ Type::Array(..) => false,
+ Type::FuncPtr { .. } => false,
+ }
+ }
+
+ pub fn can_cmp_eq(&self) -> bool {
+ match *self {
+ Type::Ptr { ref ty, is_ref, .. } => !is_ref || ty.can_cmp_eq(),
+ Type::Path(..) => true,
+ Type::Primitive(ref p) => p.can_cmp_eq(),
+ Type::Array(..) => false,
+ Type::FuncPtr { .. } => true,
+ }
+ }
+}
+
+impl Source for String {
+ fn write<F: Write>(&self, _config: &Config, out: &mut SourceWriter<F>) {
+ write!(out, "{}", self);
+ }
+}
+
+impl Source for Type {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ cdecl::write_type(out, self, config);
+ }
+}
diff --git a/src/bindgen/ir/typedef.rs b/src/bindgen/ir/typedef.rs
new file mode 100644
index 0000000..626732e
--- /dev/null
+++ b/src/bindgen/ir/typedef.rs
@@ -0,0 +1,208 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::collections::HashMap;
+use std::io::Write;
+
+use syn::ext::IdentExt;
+
+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, GenericArgument, GenericParams, Item,
+ ItemContainer, Path, ToCondition, Type,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::mangle;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::writer::{Source, SourceWriter};
+
+/// A type alias that is represented as a C typedef
+#[derive(Debug, Clone)]
+pub struct Typedef {
+ pub path: Path,
+ pub export_name: String,
+ pub generic_params: GenericParams,
+ pub aliased: Type,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl Typedef {
+ pub fn load(item: &syn::ItemType, mod_cfg: Option<&Cfg>) -> Result<Typedef, String> {
+ if let Some(x) = Type::load(&item.ty)? {
+ let path = Path::new(item.ident.unraw().to_string());
+ Ok(Typedef::new(
+ path,
+ GenericParams::load(&item.generics)?,
+ x,
+ Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
+ AnnotationSet::load(&item.attrs)?,
+ Documentation::load(&item.attrs),
+ ))
+ } else {
+ Err("Cannot have a typedef of a zero sized type.".to_owned())
+ }
+ }
+
+ pub fn new(
+ path: Path,
+ generic_params: GenericParams,
+ aliased: Type,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ generic_params,
+ aliased,
+ cfg,
+ annotations,
+ documentation,
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ self.aliased.simplify_standard_types(config);
+ }
+
+ pub fn transfer_annotations(&mut self, out: &mut HashMap<Path, AnnotationSet>) {
+ if self.annotations.is_empty() {
+ return;
+ }
+
+ if let Some(alias_path) = self.aliased.get_root_path() {
+ if out.contains_key(&alias_path) {
+ warn!(
+ "Multiple typedef's with annotations for {}. Ignoring annotations from {}.",
+ alias_path, self.path
+ );
+ return;
+ }
+
+ out.insert(alias_path, self.annotations.clone());
+ self.annotations = AnnotationSet::new();
+ }
+ }
+
+ pub fn is_generic(&self) -> bool {
+ self.generic_params.len() > 0
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ // Generic structs can instantiate monomorphs only once they've been
+ // instantiated. See `instantiate_monomorph` for more details.
+ if self.is_generic() {
+ return;
+ }
+
+ self.aliased.add_monomorphs(library, out);
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ self.aliased.mangle_paths(monomorphs);
+ }
+}
+
+impl Item for Typedef {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Typedef(self.clone())
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ config.export.rename(&mut self.export_name);
+ self.aliased.rename_for_config(config, &self.generic_params);
+ }
+
+ fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
+ resolver.add_none(&self.path);
+ }
+
+ fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ self.aliased.resolve_declaration_types(resolver);
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ self.aliased
+ .add_dependencies_ignoring_generics(&self.generic_params, library, out);
+ }
+
+ fn instantiate_monomorph(
+ &self,
+ generic_values: &[GenericArgument],
+ library: &Library,
+ out: &mut Monomorphs,
+ ) {
+ let mappings = self.generic_params.call(self.path.name(), generic_values);
+
+ let mangled_path = mangle::mangle_path(
+ &self.path,
+ generic_values,
+ &library.get_config().export.mangle,
+ );
+
+ let monomorph = Typedef::new(
+ mangled_path,
+ GenericParams::default(),
+ self.aliased.specialize(&mappings),
+ self.cfg.clone(),
+ self.annotations.clone(),
+ self.documentation.clone(),
+ );
+
+ out.insert_typedef(library, self, monomorph, generic_values.to_owned());
+ }
+}
+
+impl Source for Typedef {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ self.documentation.write(config, out);
+
+ self.generic_params.write(config, out);
+
+ match config.language {
+ Language::Cxx => {
+ write!(out, "using {} = ", self.export_name());
+ self.aliased.write(config, out);
+ }
+ Language::C | Language::Cython => {
+ write!(out, "{} ", config.language.typedef());
+ Field::from_name_and_type(self.export_name().to_owned(), self.aliased.clone())
+ .write(config, out);
+ }
+ }
+
+ out.write(";");
+
+ condition.write_after(config, out);
+ }
+}
diff --git a/src/bindgen/ir/union.rs b/src/bindgen/ir/union.rs
new file mode 100644
index 0000000..07bd16c
--- /dev/null
+++ b/src/bindgen/ir/union.rs
@@ -0,0 +1,336 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::io::Write;
+
+use syn::ext::IdentExt;
+
+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, GenericArgument, GenericParams, Item,
+ ItemContainer, Path, Repr, ReprAlign, ReprStyle, ToCondition,
+};
+use crate::bindgen::library::Library;
+use crate::bindgen::mangle;
+use crate::bindgen::monomorph::Monomorphs;
+use crate::bindgen::rename::{IdentifierType, RenameRule};
+use crate::bindgen::utilities::IterHelpers;
+use crate::bindgen::writer::{ListType, Source, SourceWriter};
+
+#[derive(Debug, Clone)]
+pub struct Union {
+ pub path: Path,
+ pub export_name: String,
+ pub generic_params: GenericParams,
+ pub fields: Vec<Field>,
+ pub tuple_union: bool,
+ pub alignment: Option<ReprAlign>,
+ pub cfg: Option<Cfg>,
+ pub annotations: AnnotationSet,
+ pub documentation: Documentation,
+}
+
+impl Union {
+ pub fn load(
+ layout_config: &LayoutConfig,
+ item: &syn::ItemUnion,
+ mod_cfg: Option<&Cfg>,
+ ) -> Result<Union, String> {
+ let repr = Repr::load(&item.attrs)?;
+ if repr.style != ReprStyle::C {
+ return Err("Union is not marked #[repr(C)].".to_owned());
+ }
+
+ // Ensure we can safely represent the union given the configuration.
+ if let Some(align) = repr.align {
+ layout_config.ensure_safe_to_represent(&align)?;
+ }
+
+ let path = Path::new(item.ident.unraw().to_string());
+
+ let (fields, tuple_union) = {
+ let out = item
+ .fields
+ .named
+ .iter()
+ .try_skip_map(|field| Field::load(field, &path))?;
+ (out, false)
+ };
+
+ Ok(Union::new(
+ path,
+ GenericParams::load(&item.generics)?,
+ fields,
+ repr.align,
+ tuple_union,
+ Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
+ AnnotationSet::load(&item.attrs)?,
+ Documentation::load(&item.attrs),
+ ))
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ path: Path,
+ generic_params: GenericParams,
+ fields: Vec<Field>,
+ alignment: Option<ReprAlign>,
+ tuple_union: bool,
+ cfg: Option<Cfg>,
+ annotations: AnnotationSet,
+ documentation: Documentation,
+ ) -> Self {
+ let export_name = path.name().to_owned();
+ Self {
+ path,
+ export_name,
+ generic_params,
+ fields,
+ tuple_union,
+ alignment,
+ cfg,
+ annotations,
+ documentation,
+ }
+ }
+
+ pub fn simplify_standard_types(&mut self, config: &Config) {
+ for field in &mut self.fields {
+ field.ty.simplify_standard_types(config);
+ }
+ }
+
+ pub fn is_generic(&self) -> bool {
+ self.generic_params.len() > 0
+ }
+
+ pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
+ // Generic unions can instantiate monomorphs only once they've been
+ // instantiated. See `instantiate_monomorph` for more details.
+ if self.is_generic() {
+ return;
+ }
+
+ for field in &self.fields {
+ field.ty.add_monomorphs(library, out);
+ }
+ }
+
+ pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
+ for field in &mut self.fields {
+ field.ty.mangle_paths(monomorphs);
+ }
+ }
+}
+
+impl Item for Union {
+ fn path(&self) -> &Path {
+ &self.path
+ }
+
+ fn export_name(&self) -> &str {
+ &self.export_name
+ }
+
+ fn cfg(&self) -> Option<&Cfg> {
+ self.cfg.as_ref()
+ }
+
+ fn annotations(&self) -> &AnnotationSet {
+ &self.annotations
+ }
+
+ fn annotations_mut(&mut self) -> &mut AnnotationSet {
+ &mut self.annotations
+ }
+
+ fn container(&self) -> ItemContainer {
+ ItemContainer::Union(self.clone())
+ }
+
+ fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
+ resolver.add_union(&self.path);
+ }
+
+ fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
+ for field in &mut self.fields {
+ field.ty.resolve_declaration_types(resolver);
+ }
+ }
+
+ fn rename_for_config(&mut self, config: &Config) {
+ config.export.rename(&mut self.export_name);
+ for field in &mut self.fields {
+ field.ty.rename_for_config(config, &self.generic_params);
+ }
+
+ let rules = self
+ .annotations
+ .parse_atom::<RenameRule>("rename-all")
+ .unwrap_or(config.structure.rename_fields);
+
+ if let Some(o) = self.annotations.list("field-names") {
+ let mut overriden_fields = Vec::new();
+
+ for (i, field) in self.fields.iter().enumerate() {
+ if i >= o.len() {
+ overriden_fields.push(field.clone());
+ } else {
+ overriden_fields.push(Field {
+ name: o[i].clone(),
+ ty: field.ty.clone(),
+ cfg: field.cfg.clone(),
+ annotations: field.annotations.clone(),
+ documentation: field.documentation.clone(),
+ });
+ }
+ }
+
+ self.fields = overriden_fields;
+ } else if let Some(r) = rules.not_none() {
+ self.fields = self
+ .fields
+ .iter()
+ .map(|field| Field {
+ name: r
+ .apply(&field.name, IdentifierType::StructMember)
+ .into_owned(),
+ ty: field.ty.clone(),
+ cfg: field.cfg.clone(),
+ annotations: field.annotations.clone(),
+ documentation: field.documentation.clone(),
+ })
+ .collect();
+ } else if self.tuple_union {
+ // If we don't have any rules for a tuple union, prefix them with
+ // an underscore so it still compiles
+ for field in &mut self.fields {
+ field.name.insert(0, '_');
+ }
+ }
+ }
+
+ fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
+ for field in &self.fields {
+ field
+ .ty
+ .add_dependencies_ignoring_generics(&self.generic_params, library, out);
+ }
+ }
+
+ fn instantiate_monomorph(
+ &self,
+ generic_values: &[GenericArgument],
+ library: &Library,
+ out: &mut Monomorphs,
+ ) {
+ let mappings = self.generic_params.call(self.path.name(), generic_values);
+
+ let mangled_path = mangle::mangle_path(
+ &self.path,
+ generic_values,
+ &library.get_config().export.mangle,
+ );
+
+ let monomorph = Union::new(
+ mangled_path,
+ GenericParams::default(),
+ self.fields
+ .iter()
+ .map(|field| Field {
+ name: field.name.clone(),
+ ty: field.ty.specialize(&mappings),
+ cfg: field.cfg.clone(),
+ annotations: field.annotations.clone(),
+ documentation: field.documentation.clone(),
+ })
+ .collect(),
+ self.alignment,
+ self.tuple_union,
+ self.cfg.clone(),
+ self.annotations.clone(),
+ self.documentation.clone(),
+ );
+
+ out.insert_union(library, self, monomorph, generic_values.to_owned());
+ }
+}
+
+impl Source for Union {
+ fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
+ let condition = self.cfg.to_condition(config);
+ condition.write_before(config, out);
+
+ self.documentation.write(config, out);
+
+ self.generic_params.write(config, out);
+
+ // The following results in
+ // C++ or C with Tag as style:
+ // union Name {
+ // C with Type only style:
+ // typedef union {
+ // C with Both as style:
+ // typedef union Name {
+ match config.language {
+ Language::C if config.style.generate_typedef() => out.write("typedef "),
+ Language::C | Language::Cxx => {}
+ Language::Cython => out.write(config.style.cython_def()),
+ }
+
+ out.write("union");
+
+ // Cython supports `packed` on structs (see comments there), but not on unions.
+ if config.language != Language::Cython {
+ if let Some(align) = self.alignment {
+ match align {
+ ReprAlign::Packed => {
+ if let Some(ref anno) = config.layout.packed {
+ write!(out, " {}", anno);
+ }
+ }
+ ReprAlign::Align(n) => {
+ if let Some(ref anno) = config.layout.aligned_n {
+ write!(out, " {}({})", anno, n);
+ }
+ }
+ }
+ }
+ }
+
+ if config.language != Language::C || config.style.generate_tag() {
+ write!(out, " {}", self.export_name);
+ }
+
+ out.open_brace();
+
+ // Emit the pre_body section, if relevant
+ if let Some(body) = config.export.pre_body(&self.path) {
+ out.write_raw_block(body);
+ out.new_line();
+ }
+
+ out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
+ if config.language == Language::Cython && self.fields.is_empty() {
+ out.write("pass");
+ }
+
+ // Emit the post_body section, if relevant
+ if let Some(body) = config.export.post_body(&self.path) {
+ out.new_line();
+ out.write_raw_block(body);
+ }
+
+ if config.language == Language::C && config.style.generate_typedef() {
+ out.close_brace(false);
+ write!(out, " {};", self.export_name);
+ } else {
+ out.close_brace(true);
+ }
+
+ condition.write_after(config, out);
+ }
+}