Skip to content

Commit

Permalink
Option to use #[repr(transparent)] structs instead of type aliasing.
Browse files Browse the repository at this point in the history
  • Loading branch information
JRF63 authored and emilio committed Nov 8, 2019
1 parent 3609bd6 commit 1c31f29
Show file tree
Hide file tree
Showing 9 changed files with 387 additions and 6 deletions.
106 changes: 102 additions & 4 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,16 @@ impl CodeGenerator for Type {
quote! {}
};

let alias_style = if ctx.options().type_alias.matches(&name) {
AliasVariation::TypeAlias
} else if ctx.options().new_type_alias.matches(&name) {
AliasVariation::NewType
} else if ctx.options().new_type_alias_deref.matches(&name) {
AliasVariation::NewTypeDeref
} else {
ctx.options().default_alias_style
};

// We prefer using `pub use` over `pub type` because of:
// https://github.com/rust-lang/rust/issues/26264
if inner_rust_type.to_string().chars().all(|c| match c {
Expand All @@ -758,6 +768,7 @@ impl CodeGenerator for Type {
_ => false,
}) && outer_params.is_empty() &&
!is_opaque &&
alias_style == AliasVariation::TypeAlias &&
inner_item.expect_type().canonical_type(ctx).is_enum()
{
tokens.append_all(quote! {
Expand All @@ -772,8 +783,21 @@ impl CodeGenerator for Type {
return;
}

tokens.append_all(quote! {
pub type #rust_name
tokens.append_all(match alias_style {
AliasVariation::TypeAlias => quote! {
pub type #rust_name
},
AliasVariation::NewType | AliasVariation::NewTypeDeref => {
assert!(
ctx.options().rust_features().repr_transparent,
"repr_transparent feature is required to use {:?}",
alias_style
);
quote! {
#[repr(transparent)]
pub struct #rust_name
}
}
});

let params: Vec<_> = outer_params
Expand Down Expand Up @@ -806,10 +830,36 @@ impl CodeGenerator for Type {
});
}

tokens.append_all(quote! {
= #inner_rust_type ;
tokens.append_all(match alias_style {
AliasVariation::TypeAlias => quote! {
= #inner_rust_type ;
},
AliasVariation::NewType | AliasVariation::NewTypeDeref => {
quote! {
(pub #inner_rust_type) ;
}
}
});

if alias_style == AliasVariation::NewTypeDeref {
let prefix = ctx.trait_prefix();
tokens.append_all(quote! {
impl ::#prefix::ops::Deref for #rust_name {
type Target = #inner_rust_type;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ::#prefix::ops::DerefMut for #rust_name {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
});
}

result.push(tokens);
}
TypeKind::Enum(ref ei) => ei.codegen(ctx, result, item),
Expand Down Expand Up @@ -2870,6 +2920,54 @@ impl CodeGenerator for Enum {
}
}

/// Enum for how aliases should be translated.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum AliasVariation {
/// Convert to regular Rust alias
TypeAlias,
/// Create a new type by wrapping the old type in a struct and using #[repr(transparent)]
NewType,
/// Same as NewStruct but also impl Deref to be able to use the methods of the wrapped type
NewTypeDeref,
}

impl AliasVariation {
/// Convert an `AliasVariation` to its str representation.
pub fn as_str(&self) -> &str {
match self {
AliasVariation::TypeAlias => "type_alias",
AliasVariation::NewType => "new_type",
AliasVariation::NewTypeDeref => "new_type_deref",
}
}
}

impl Default for AliasVariation {
fn default() -> AliasVariation {
AliasVariation::TypeAlias
}
}

impl std::str::FromStr for AliasVariation {
type Err = std::io::Error;

/// Create an `AliasVariation` from a string.
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"type_alias" => Ok(AliasVariation::TypeAlias),
"new_type" => Ok(AliasVariation::NewType),
"new_type_deref" => Ok(AliasVariation::NewTypeDeref),
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
concat!(
"Got an invalid AliasVariation. Accepted values ",
"are 'type_alias', 'new_type', and 'new_type_deref'"
),
)),
}
}
}

/// Fallible conversion to an opaque blob.
///
/// Implementors of this trait should provide the `try_get_layout` method to
Expand Down
97 changes: 96 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ doc_mod!(ir, ir_docs);
doc_mod!(parse, parse_docs);
doc_mod!(regex_set, regex_set_docs);

pub use codegen::EnumVariation;
pub use codegen::{AliasVariation, EnumVariation};
use features::RustFeatures;
pub use features::{RustTarget, LATEST_STABLE_RUST, RUST_TARGET_STRINGS};
use ir::context::{BindgenContext, ItemId};
Expand Down Expand Up @@ -292,6 +292,42 @@ impl Builder {
})
.count();

if self.options.default_alias_style != Default::default() {
output_vector.push("--default-alias-style=".into());
output_vector
.push(self.options.default_alias_style.as_str().into());
}

self.options
.type_alias
.get_items()
.iter()
.map(|item| {
output_vector.push("--type-alias".into());
output_vector.push(item.to_owned());
})
.count();

self.options
.new_type_alias
.get_items()
.iter()
.map(|item| {
output_vector.push("--new-type-alias".into());
output_vector.push(item.to_owned());
})
.count();

self.options
.new_type_alias_deref
.get_items()
.iter()
.map(|item| {
output_vector.push("--new-type-alias-deref".into());
output_vector.push(item.to_owned());
})
.count();

self.options
.blacklisted_types
.get_items()
Expand Down Expand Up @@ -873,6 +909,45 @@ impl Builder {
self
}

/// Set the default style of code to generate for typedefs
pub fn default_alias_style(
mut self,
arg: codegen::AliasVariation,
) -> Builder {
self.options.default_alias_style = arg;
self
}

/// Mark the given typedef alias (or set of aliases, if using a pattern) to
/// use regular Rust type aliasing.
///
/// This is the default behavior and should be used if `default_alias_style`
/// was set to NewType or NewTypeDeref and you want to override it for a
/// set of typedefs.
pub fn type_alias<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.type_alias.insert(arg);
self
}

/// Mark the given typedef alias (or set of aliases, if using a pattern) to
/// be generated as a new type by having the aliased type be wrapped in a
/// #[repr(transparent)] struct.
///
/// Used to enforce stricter type checking.
pub fn new_type_alias<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.new_type_alias.insert(arg);
self
}

/// Mark the given typedef alias (or set of aliases, if using a pattern) to
/// be generated as a new type by having the aliased type be wrapped in a
/// #[repr(transparent)] struct and also have an automatically generated
/// impl's of `Deref` and `DerefMut` to their aliased type.
pub fn new_type_alias_deref<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.new_type_alias_deref.insert(arg);
self
}

/// Add a string to prepend to the generated bindings. The string is passed
/// through without any modification.
pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Self {
Expand Down Expand Up @@ -1443,6 +1518,19 @@ struct BindgenOptions {
/// The enum patterns to mark an enum as a set of constants.
constified_enums: RegexSet,

/// The default style of code to generate for typedefs.
default_alias_style: codegen::AliasVariation,

/// Typedef patterns that will use regular type aliasing.
type_alias: RegexSet,

/// Typedef patterns that will be aliased by creating a new struct.
new_type_alias: RegexSet,

/// Typedef patterns that will be wrapped in a new struct and have
/// Deref and Deref to their aliased type.
new_type_alias_deref: RegexSet,

/// Whether we should generate builtins or not.
builtins: bool,

Expand Down Expand Up @@ -1651,6 +1739,9 @@ impl BindgenOptions {
&mut self.constified_enum_modules,
&mut self.rustified_enums,
&mut self.rustified_non_exhaustive_enums,
&mut self.type_alias,
&mut self.new_type_alias,
&mut self.new_type_alias_deref,
&mut self.no_partialeq_types,
&mut self.no_copy_types,
&mut self.no_hash_types,
Expand Down Expand Up @@ -1696,6 +1787,10 @@ impl Default for BindgenOptions {
rustified_non_exhaustive_enums: Default::default(),
constified_enums: Default::default(),
constified_enum_modules: Default::default(),
default_alias_style: Default::default(),
type_alias: Default::default(),
new_type_alias: Default::default(),
new_type_alias_deref: Default::default(),
builtins: false,
emit_ast: false,
emit_ir: false,
Expand Down
67 changes: 66 additions & 1 deletion src/options.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bindgen::{
builder, Builder, CodegenConfig, EnumVariation, RustTarget,
builder, AliasVariation, Builder, CodegenConfig, EnumVariation, RustTarget,
RUST_TARGET_STRINGS,
};
use clap::{App, Arg};
Expand Down Expand Up @@ -79,6 +79,47 @@ where
.takes_value(true)
.multiple(true)
.number_of_values(1),
Arg::with_name("default-alias-style")
.long("default-alias-style")
.help("The default style of code used to generate typedefs.")
.value_name("variant")
.default_value("type_alias")
.possible_values(&[
"type_alias",
"new_type",
"new_type_deref",
])
.multiple(false),
Arg::with_name("normal-alias")
.long("normal-alias")
.help(
"Mark any typedef alias whose name matches <regex> to use \
normal type aliasing.",
)
.value_name("regex")
.takes_value(true)
.multiple(true)
.number_of_values(1),
Arg::with_name("new-type-alias")
.long("new-type-alias")
.help(
"Mark any typedef alias whose name matches <regex> to have \
a new type generated for it.",
)
.value_name("regex")
.takes_value(true)
.multiple(true)
.number_of_values(1),
Arg::with_name("new-type-alias-deref")
.long("new-type-alias-deref")
.help(
"Mark any typedef alias whose name matches <regex> to have \
a new type with Deref and DerefMut to the inner type.",
)
.value_name("regex")
.takes_value(true)
.multiple(true)
.number_of_values(1),
Arg::with_name("blacklist-type")
.long("blacklist-type")
.help("Mark <type> as hidden.")
Expand Down Expand Up @@ -436,6 +477,30 @@ where
builder = builder.constified_enum_module(regex);
}
}

if let Some(variant) = matches.value_of("default-alias-style") {
builder =
builder.default_alias_style(AliasVariation::from_str(variant)?);
}

if let Some(type_alias) = matches.values_of("normal-alias") {
for regex in type_alias {
builder = builder.type_alias(regex);
}
}

if let Some(new_type) = matches.values_of("new-type-alias") {
for regex in new_type {
builder = builder.new_type_alias(regex);
}
}

if let Some(new_type_deref) = matches.values_of("new-type-alias-deref") {
for regex in new_type_deref {
builder = builder.new_type_alias_deref(regex);
}
}

if let Some(hidden_types) = matches.values_of("blacklist-type") {
for ty in hidden_types {
builder = builder.blacklist_type(ty);
Expand Down
Loading

0 comments on commit 1c31f29

Please sign in to comment.