Commit 644a717f authored by Ryan Hunt's avatar Ryan Hunt
Browse files

Add support for unions

parent e6ad0354
#[repr(C)]
union Foo<T> {
data: *const T
}
union Bar<T> {
data: *const T
}
#[repr(C)]
union Tuple<T, E> {
a: *const T,
b: *const E,
}
type Indirection<T> = Tuple<T, f32>;
#[no_mangle]
extern "C" fn root(a: Foo<i32>,
b: Foo<f32>,
c: Bar<f32>,
d: Foo<Bar<f32>>,
e: Bar<Foo<f32>>,
f: Bar<Bar<f32>>,
g: Tuple<Foo<f32>, f32>,
h: Indirection<f32>) {
}
[struct]
generic_template_specialization = true
use std::marker::PhantomData;
union Opaque {
x: i32,
y: f32,
}
#[repr(C)]
union Normal {
x: i32,
y: f32,
}
#[repr(C)]
union NormalWithZST {
x: i32,
y: f32,
z: (),
w: PhantomData<i32>,
}
#[no_mangle]
extern "C" fn root(a: *mut Opaque,
b: Normal,
c: NormalWithZST)
{ }
......@@ -112,6 +112,7 @@ impl Bindings {
&ItemContainer::Static(..) => unreachable!(),
&ItemContainer::Enum(ref x) => x.write(&self.config, &mut out),
&ItemContainer::Struct(ref x) => x.write(&self.config, &mut out),
&ItemContainer::Union(ref x) => x.write(&self.config, &mut out),
&ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out),
&ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out),
&ItemContainer::Specialization(_) => {
......
......@@ -9,7 +9,7 @@ use syn;
use bindgen::cargo::Cargo;
use bindgen::config::Config;
use bindgen::ir::{AnnotationSet, Cfg, Constant, Documentation, Enum, Function};
use bindgen::ir::{ItemMap, OpaqueItem, Specialization, Static, Struct, Typedef};
use bindgen::ir::{ItemMap, OpaqueItem, Specialization, Static, Struct, Typedef, Union};
use bindgen::library::Library;
use bindgen::rust_lib;
use bindgen::utilities::{SynAbiHelpers, SynItemHelpers};
......@@ -84,6 +84,7 @@ impl LibraryBuilder {
result.globals,
result.enums,
result.structs,
result.unions,
result.opaque_items,
result.typedefs,
result.specializations,
......@@ -97,6 +98,7 @@ struct LibraryParseResult {
globals: ItemMap<Static>,
enums: ItemMap<Enum>,
structs: ItemMap<Struct>,
unions: ItemMap<Union>,
opaque_items: ItemMap<OpaqueItem>,
typedefs: ItemMap<Typedef>,
specializations: ItemMap<Specialization>,
......@@ -110,6 +112,7 @@ impl LibraryParseResult {
constants: ItemMap::new(),
globals: ItemMap::new(),
structs: ItemMap::new(),
unions: ItemMap::new(),
opaque_items: ItemMap::new(),
typedefs: ItemMap::new(),
specializations: ItemMap::new(),
......@@ -193,6 +196,9 @@ impl LibraryParseResult {
syn::ItemKind::Struct(ref variant, ref generics) => {
self.load_syn_struct(crate_name, mod_cfg, item, variant, generics);
}
syn::ItemKind::Union(ref variant, ref generics) => {
self.load_syn_union(crate_name, mod_cfg, item, variant, generics);
}
syn::ItemKind::Enum(ref variants, ref generics) => {
self.load_syn_enum(crate_name, mod_cfg, item, variants, generics);
}
......@@ -401,6 +407,38 @@ impl LibraryParseResult {
}
}
/// Loads a `union` declaration
fn load_syn_union(&mut self,
crate_name: &str,
mod_cfg: &Option<Cfg>,
item: &syn::Item,
variant: &syn::VariantData,
generics: &syn::Generics) {
let union_name = item.ident.to_string();
match Union::load(union_name.clone(),
variant,
generics,
&item.attrs,
mod_cfg) {
Ok(st) => {
info!("Take {}::{}.", crate_name, &item.ident);
self.unions.try_insert(st);
}
Err(msg) => {
info!("Take {}::{} - opaque ({}).",
crate_name,
&item.ident,
msg);
self.opaque_items.try_insert(OpaqueItem::new(union_name,
generics,
&item.attrs,
mod_cfg));
}
}
}
/// Loads a `enum` declaration
fn load_syn_enum(&mut self,
crate_name: &str,
......
......@@ -7,7 +7,7 @@ use std::mem;
use bindgen::config::Config;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{AnnotationSet, Cfg, Constant, Enum, OpaqueItem, Specialization, Static, Struct, Type, Typedef};
use bindgen::ir::{AnnotationSet, Cfg, Constant, Enum, OpaqueItem, Specialization, Static, Struct, Type, Typedef, Union};
use bindgen::library::Library;
use bindgen::monomorph::Monomorphs;
......@@ -33,6 +33,7 @@ pub enum ItemContainer {
Static(Static),
OpaqueItem(OpaqueItem),
Struct(Struct),
Union(Union),
Enum(Enum),
Typedef(Typedef),
Specialization(Specialization),
......@@ -45,6 +46,7 @@ impl ItemContainer {
&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,
&ItemContainer::Specialization(ref x) => x,
......
......@@ -16,6 +16,7 @@ pub mod specialization;
pub mod structure;
pub mod ty;
pub mod typedef;
pub mod union;
pub mod documentation;
pub use self::annotation::{AnnotationSet, AnnotationValue};
......@@ -32,4 +33,5 @@ pub use self::specialization::*;
pub use self::structure::*;
pub use self::ty::*;
pub use self::typedef::*;
pub use self::union::*;
pub use self::documentation::Documentation;
......@@ -77,6 +77,9 @@ impl Specialization {
ItemContainer::Struct(ref aliased) => {
aliased.specialize(library, self)
}
ItemContainer::Union(ref aliased) => {
aliased.specialize(library, self)
}
ItemContainer::Enum(ref aliased) => {
aliased.specialize(library, self)
}
......
......@@ -404,6 +404,9 @@ impl Type {
ItemContainer::OpaqueItem(ref x) => {
x.instantiate_monomorph(&path.generics, out);
},
ItemContainer::Union(ref x) => {
x.instantiate_monomorph(&path.generics, library, out);
},
ItemContainer::Struct(ref x) => {
x.instantiate_monomorph(&path.generics, library, out);
},
......
/* 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;
use bindgen::config::{Config, Language};
use bindgen::dependencies::Dependencies;
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, ItemContainer, Item, Repr, Specialization, Type};
use bindgen::ir::SynFieldHelpers;
use bindgen::library::Library;
use bindgen::mangle;
use bindgen::monomorph::Monomorphs;
use bindgen::rename::{IdentifierType, RenameRule};
use bindgen::utilities::{find_first_some, IterHelpers};
use bindgen::writer::{ListType, Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Union {
pub name: String,
pub generic_params: Vec<String>,
pub fields: Vec<(String, Type, Documentation)>,
pub tuple_union: bool,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
}
impl Union {
pub fn load(name: String,
decl: &syn::VariantData,
generics: &syn::Generics,
attrs: &Vec<syn::Attribute>,
mod_cfg: &Option<Cfg>) -> Result<Union, String>
{
if Repr::load(attrs) != Repr::C {
return Err("Union is not marked #[repr(C)].".to_owned());
}
let (fields, tuple_union) = match decl {
&syn::VariantData::Struct(ref fields) => {
let out = fields.iter()
.try_skip_map(|x| x.as_ident_and_type())?;
(out, false)
}
&syn::VariantData::Tuple(ref fields) => {
let mut out = Vec::new();
let mut current = 0;
for field in fields {
if let Some(x) = Type::load(&field.ty)? {
out.push((format!("{}", current), x, Documentation::load(&field.attrs)));
current += 1;
}
}
(out, true)
}
&syn::VariantData::Unit => {
(vec![], false)
}
};
let generic_params = generics.ty_params.iter()
.map(|x| x.ident.to_string())
.collect::<Vec<_>>();
Ok(Union {
name: name,
generic_params: generic_params,
fields: fields,
tuple_union: tuple_union,
cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
annotations: AnnotationSet::load(attrs)?,
documentation: Documentation::load(attrs),
})
}
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 &(_, ref ty, _) in &self.fields {
ty.add_monomorphs(library, out);
}
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
for &mut (_, ref mut ty, _) in &mut self.fields {
ty.mangle_paths(monomorphs);
}
}
}
impl Item for Union {
fn name(&self) -> &str {
&self.name
}
fn cfg(&self) -> &Option<Cfg> {
&self.cfg
}
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 rename_for_config(&mut self, config: &Config) {
let rules = [self.annotations.parse_atom::<RenameRule>("rename-all"),
config.structure.rename_fields];
if let Some(o) = self.annotations.list("field-names") {
let mut overriden_fields = Vec::new();
for (i, &(ref name, ref ty, ref doc)) in self.fields.iter().enumerate() {
if i >= o.len() {
overriden_fields.push((name.clone(), ty.clone(), doc.clone()));
} else {
overriden_fields.push((o[i].clone(), ty.clone(), doc.clone()));
}
}
self.fields = overriden_fields;
} else if let Some(r) = find_first_some(&rules) {
self.fields = self.fields.iter()
.map(|x| (r.apply_to_snake_case(&x.0,
IdentifierType::StructMember),
x.1.clone(),
x.2.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 &mut (ref mut name, ..) in &mut self.fields {
name.insert(0, '_');
}
}
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
for &(_, ref ty, _) in &self.fields {
ty.add_dependencies_ignoring_generics(&self.generic_params, library, out);
}
}
fn instantiate_monomorph(&self, generic_values: &Vec<Type>, library: &Library, out: &mut Monomorphs) {
assert!(self.generic_params.len() > 0 &&
self.generic_params.len() == generic_values.len());
let mappings = self.generic_params.iter()
.zip(generic_values.iter())
.collect::<Vec<_>>();
let monomorph = Union {
name: mangle::mangle_path(&self.name, generic_values),
generic_params: vec![],
fields: self.fields.iter()
.map(|x| (x.0.clone(), x.1.specialize(&mappings), x.2.clone()))
.collect(),
tuple_union: self.tuple_union,
cfg: self.cfg.clone(),
annotations: self.annotations.clone(),
documentation: self.documentation.clone(),
};
// Instantiate any monomorphs for any generic paths we may have just created.
monomorph.add_monomorphs(library, out);
out.insert_union(self, monomorph, generic_values.clone());
}
fn specialize(&self, _: &Library, aliasee: &Specialization) -> Result<Box<Item>, String> {
if aliasee.aliased.generics.len() !=
self.generic_params.len() {
return Err("Incomplete specialization, the amount of generics in the path doesn't match the amount of generics in the item.".to_owned());
}
let mappings = self.generic_params.iter()
.zip(aliasee.aliased.generics.iter())
.collect::<Vec<_>>();
Ok(Box::new(Union {
name: aliasee.name.clone(),
generic_params: aliasee.generic_params.clone(),
fields: self.fields.iter()
.map(|x| (x.0.clone(), x.1.specialize(&mappings), x.2.clone()))
.collect(),
tuple_union: self.tuple_union,
cfg: aliasee.cfg.clone(),
annotations: aliasee.annotations.clone(),
documentation: aliasee.documentation.clone(),
}))
}
}
impl Source for Union {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
assert!(self.generic_params.is_empty());
self.cfg.write_before(config, out);
self.documentation.write(config, out);
if config.language == Language::C {
out.write("typedef union");
} else {
out.write(&format!("union {}", self.name));
}
out.open_brace();
if config.documentation {
out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
} else {
out.write_vertical_source_list(&self.fields.iter()
.map(|&(ref name, ref ty, _)| (name.clone(), ty.clone()))
.collect(),
ListType::Cap(";"));
}
if config.language == Language::C {
out.close_brace(false);
out.write(&format!(" {};", self.name));
} else {
out.close_brace(true);
}
self.cfg.write_after(config, out);
}
}
......@@ -9,7 +9,7 @@ use bindgen::bindings::Bindings;
use bindgen::config::{Config, Language};
use bindgen::dependencies::Dependencies;
use bindgen::ir::{Constant, Enum, Function, ItemContainer, ItemMap, Item};
use bindgen::ir::{OpaqueItem, Path, Specialization, Static, Struct, Typedef};
use bindgen::ir::{OpaqueItem, Path, Specialization, Static, Struct, Typedef, Union};
use bindgen::monomorph::{Monomorphs, TemplateSpecialization};
#[derive(Debug, Clone)]
......@@ -19,6 +19,7 @@ pub struct Library {
globals: ItemMap<Static>,
enums: ItemMap<Enum>,
structs: ItemMap<Struct>,
unions: ItemMap<Union>,
opaque_items: ItemMap<OpaqueItem>,
typedefs: ItemMap<Typedef>,
specializations: ItemMap<Specialization>,
......@@ -32,6 +33,7 @@ impl Library {
globals: ItemMap<Static>,
enums: ItemMap<Enum>,
structs: ItemMap<Struct>,
unions: ItemMap<Union>,
opaque_items: ItemMap<OpaqueItem>,
typedefs: ItemMap<Typedef>,
specializations: ItemMap<Specialization>,
......@@ -42,6 +44,7 @@ impl Library {
globals: globals,
enums: enums,
structs: structs,
unions: unions,
opaque_items: opaque_items,
typedefs: typedefs,
specializations: specializations,
......@@ -95,6 +98,9 @@ impl Library {
if let Some(x) = self.structs.get_items(p) {
return Some(x);
}
if let Some(x) = self.unions.get_items(p) {
return Some(x);
}
if let Some(x) = self.opaque_items.get_items(p) {
return Some(x);
}
......@@ -122,6 +128,9 @@ impl Library {
ItemContainer::Struct(x) => {
self.structs.try_insert(x);
},
ItemContainer::Union(x) => {
self.unions.try_insert(x);
},
ItemContainer::Enum(x) => {
self.enums.try_insert(x);
},
......@@ -169,6 +178,18 @@ impl Library {
if transferred {
continue;
}
self.unions.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
transferred = true;
} else {
warn!("Can't transfer annotations from typedef to alias ({}) that already has annotations.",
alias_path);
}
});
if transferred {
continue;
}
self.opaque_items.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
......@@ -211,6 +232,7 @@ impl Library {
fn rename_items(&mut self) {
let config = &self.config;
self.structs.for_all_items_mut(|x| x.rename_for_config(config));
self.unions.for_all_items_mut(|x| x.rename_for_config(config));
self.enums.for_all_items_mut(|x| x.rename_for_config(config));
for item in &mut self.functions {
......@@ -248,6 +270,9 @@ impl Library {
self.structs.for_all_items(|x| {
x.add_monomorphs(self, &mut monomorphs);
});
self.unions.for_all_items(|x| {
x.add_monomorphs(self, &mut monomorphs);
});
self.typedefs.for_all_items(|x| {
x.add_monomorphs(self, &mut monomorphs);
});
......@@ -259,6 +284,9 @@ impl Library {
for monomorph in monomorphs.drain_structs() {
self.structs.try_insert(monomorph);
}
for monomorph in monomorphs.drain_unions() {
self.unions.try_insert(monomorph);
}
for monomorph in monomorphs.drain_opaques() {
self.opaque_items.try_insert(monomorph);
}
......@@ -266,8 +294,10 @@ impl Library {
// Remove structs and opaque items that are generic
self.opaque_items.filter(|x| x.generic_params.len() > 0);
self.structs.filter(|x| x.generic_params.len() > 0);
self.unions.filter(|x| x.generic_params.len() > 0);
// Mangle the paths that remain
self.unions.for_all_items_mut(|x| x.mangle_paths(&monomorphs));
self.structs.for_all_items_mut(|x| x.mangle_paths(&monomorphs));
self.typedefs.for_all_items_mut(|x| x.mangle_paths(&monomorphs));
for x in &mut self.functions {
......
......@@ -6,7 +6,7 @@ use std::collections::HashMap;
use std::mem;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{GenericPath, OpaqueItem, Path, Struct, Type};
use bindgen::ir::{GenericPath, OpaqueItem, Path, Struct, Type, Union};
use bindgen::library::Library;
#[derive(Clone, Debug)]
......@@ -37,6 +37,7 @@ pub struct Monomorphs {
replacements: HashMap<GenericPath, Path>,
opaques: Vec<OpaqueItem>,
structs: Vec<Struct>,
unions: Vec<Union>,
templates: HashMap<Path, TemplateSpecialization>,
}
......@@ -46,6 +47,7 @@ impl Monomorphs {
replacements: HashMap::new(),
opaques: Vec::new(),
structs: Vec::new(),
unions: Vec::new(),
templates: HashMap::new(),
}
}
......@@ -55,9 +57,9 @@ impl Monomorphs {
}
pub fn insert_struct(&mut self,
generic: &Struct,
monomorph: Struct,
parameters: Vec<Type>) {
generic: &Struct,
monomorph: Struct,
parameters: Vec<Type>) {
// Add extra information for struct instantiations so we can use template
// specialization to make using the type more ergonomic.
self.templates.entry(generic.name.clone())
......@@ -73,6 +75,19 @@ impl Monomorphs {
self.structs.push(monomorph);
}
pub fn insert_union(&mut self,
generic: &Union,
monomorph: Union,
parameters: Vec<Type>) {
let replacement_path = GenericPath::new(generic.name.clone(), parameters);
debug_assert!(generic.generic_params.len() > 0);