Skip to content

Commit 1c31f29

Browse files
JRF63emilio
authored andcommitted
Option to use #[repr(transparent)] structs instead of type aliasing.
1 parent 3609bd6 commit 1c31f29

9 files changed

+387
-6
lines changed

src/codegen/mod.rs

+102-4
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,16 @@ impl CodeGenerator for Type {
749749
quote! {}
750750
};
751751

752+
let alias_style = if ctx.options().type_alias.matches(&name) {
753+
AliasVariation::TypeAlias
754+
} else if ctx.options().new_type_alias.matches(&name) {
755+
AliasVariation::NewType
756+
} else if ctx.options().new_type_alias_deref.matches(&name) {
757+
AliasVariation::NewTypeDeref
758+
} else {
759+
ctx.options().default_alias_style
760+
};
761+
752762
// We prefer using `pub use` over `pub type` because of:
753763
// https://github.com/rust-lang/rust/issues/26264
754764
if inner_rust_type.to_string().chars().all(|c| match c {
@@ -758,6 +768,7 @@ impl CodeGenerator for Type {
758768
_ => false,
759769
}) && outer_params.is_empty() &&
760770
!is_opaque &&
771+
alias_style == AliasVariation::TypeAlias &&
761772
inner_item.expect_type().canonical_type(ctx).is_enum()
762773
{
763774
tokens.append_all(quote! {
@@ -772,8 +783,21 @@ impl CodeGenerator for Type {
772783
return;
773784
}
774785

775-
tokens.append_all(quote! {
776-
pub type #rust_name
786+
tokens.append_all(match alias_style {
787+
AliasVariation::TypeAlias => quote! {
788+
pub type #rust_name
789+
},
790+
AliasVariation::NewType | AliasVariation::NewTypeDeref => {
791+
assert!(
792+
ctx.options().rust_features().repr_transparent,
793+
"repr_transparent feature is required to use {:?}",
794+
alias_style
795+
);
796+
quote! {
797+
#[repr(transparent)]
798+
pub struct #rust_name
799+
}
800+
}
777801
});
778802

779803
let params: Vec<_> = outer_params
@@ -806,10 +830,36 @@ impl CodeGenerator for Type {
806830
});
807831
}
808832

809-
tokens.append_all(quote! {
810-
= #inner_rust_type ;
833+
tokens.append_all(match alias_style {
834+
AliasVariation::TypeAlias => quote! {
835+
= #inner_rust_type ;
836+
},
837+
AliasVariation::NewType | AliasVariation::NewTypeDeref => {
838+
quote! {
839+
(pub #inner_rust_type) ;
840+
}
841+
}
811842
});
812843

844+
if alias_style == AliasVariation::NewTypeDeref {
845+
let prefix = ctx.trait_prefix();
846+
tokens.append_all(quote! {
847+
impl ::#prefix::ops::Deref for #rust_name {
848+
type Target = #inner_rust_type;
849+
#[inline]
850+
fn deref(&self) -> &Self::Target {
851+
&self.0
852+
}
853+
}
854+
impl ::#prefix::ops::DerefMut for #rust_name {
855+
#[inline]
856+
fn deref_mut(&mut self) -> &mut Self::Target {
857+
&mut self.0
858+
}
859+
}
860+
});
861+
}
862+
813863
result.push(tokens);
814864
}
815865
TypeKind::Enum(ref ei) => ei.codegen(ctx, result, item),
@@ -2870,6 +2920,54 @@ impl CodeGenerator for Enum {
28702920
}
28712921
}
28722922

2923+
/// Enum for how aliases should be translated.
2924+
#[derive(Copy, Clone, PartialEq, Debug)]
2925+
pub enum AliasVariation {
2926+
/// Convert to regular Rust alias
2927+
TypeAlias,
2928+
/// Create a new type by wrapping the old type in a struct and using #[repr(transparent)]
2929+
NewType,
2930+
/// Same as NewStruct but also impl Deref to be able to use the methods of the wrapped type
2931+
NewTypeDeref,
2932+
}
2933+
2934+
impl AliasVariation {
2935+
/// Convert an `AliasVariation` to its str representation.
2936+
pub fn as_str(&self) -> &str {
2937+
match self {
2938+
AliasVariation::TypeAlias => "type_alias",
2939+
AliasVariation::NewType => "new_type",
2940+
AliasVariation::NewTypeDeref => "new_type_deref",
2941+
}
2942+
}
2943+
}
2944+
2945+
impl Default for AliasVariation {
2946+
fn default() -> AliasVariation {
2947+
AliasVariation::TypeAlias
2948+
}
2949+
}
2950+
2951+
impl std::str::FromStr for AliasVariation {
2952+
type Err = std::io::Error;
2953+
2954+
/// Create an `AliasVariation` from a string.
2955+
fn from_str(s: &str) -> Result<Self, Self::Err> {
2956+
match s {
2957+
"type_alias" => Ok(AliasVariation::TypeAlias),
2958+
"new_type" => Ok(AliasVariation::NewType),
2959+
"new_type_deref" => Ok(AliasVariation::NewTypeDeref),
2960+
_ => Err(std::io::Error::new(
2961+
std::io::ErrorKind::InvalidInput,
2962+
concat!(
2963+
"Got an invalid AliasVariation. Accepted values ",
2964+
"are 'type_alias', 'new_type', and 'new_type_deref'"
2965+
),
2966+
)),
2967+
}
2968+
}
2969+
}
2970+
28732971
/// Fallible conversion to an opaque blob.
28742972
///
28752973
/// Implementors of this trait should provide the `try_get_layout` method to

src/lib.rs

+96-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ doc_mod!(ir, ir_docs);
8383
doc_mod!(parse, parse_docs);
8484
doc_mod!(regex_set, regex_set_docs);
8585

86-
pub use codegen::EnumVariation;
86+
pub use codegen::{AliasVariation, EnumVariation};
8787
use features::RustFeatures;
8888
pub use features::{RustTarget, LATEST_STABLE_RUST, RUST_TARGET_STRINGS};
8989
use ir::context::{BindgenContext, ItemId};
@@ -292,6 +292,42 @@ impl Builder {
292292
})
293293
.count();
294294

295+
if self.options.default_alias_style != Default::default() {
296+
output_vector.push("--default-alias-style=".into());
297+
output_vector
298+
.push(self.options.default_alias_style.as_str().into());
299+
}
300+
301+
self.options
302+
.type_alias
303+
.get_items()
304+
.iter()
305+
.map(|item| {
306+
output_vector.push("--type-alias".into());
307+
output_vector.push(item.to_owned());
308+
})
309+
.count();
310+
311+
self.options
312+
.new_type_alias
313+
.get_items()
314+
.iter()
315+
.map(|item| {
316+
output_vector.push("--new-type-alias".into());
317+
output_vector.push(item.to_owned());
318+
})
319+
.count();
320+
321+
self.options
322+
.new_type_alias_deref
323+
.get_items()
324+
.iter()
325+
.map(|item| {
326+
output_vector.push("--new-type-alias-deref".into());
327+
output_vector.push(item.to_owned());
328+
})
329+
.count();
330+
295331
self.options
296332
.blacklisted_types
297333
.get_items()
@@ -873,6 +909,45 @@ impl Builder {
873909
self
874910
}
875911

912+
/// Set the default style of code to generate for typedefs
913+
pub fn default_alias_style(
914+
mut self,
915+
arg: codegen::AliasVariation,
916+
) -> Builder {
917+
self.options.default_alias_style = arg;
918+
self
919+
}
920+
921+
/// Mark the given typedef alias (or set of aliases, if using a pattern) to
922+
/// use regular Rust type aliasing.
923+
///
924+
/// This is the default behavior and should be used if `default_alias_style`
925+
/// was set to NewType or NewTypeDeref and you want to override it for a
926+
/// set of typedefs.
927+
pub fn type_alias<T: AsRef<str>>(mut self, arg: T) -> Builder {
928+
self.options.type_alias.insert(arg);
929+
self
930+
}
931+
932+
/// Mark the given typedef alias (or set of aliases, if using a pattern) to
933+
/// be generated as a new type by having the aliased type be wrapped in a
934+
/// #[repr(transparent)] struct.
935+
///
936+
/// Used to enforce stricter type checking.
937+
pub fn new_type_alias<T: AsRef<str>>(mut self, arg: T) -> Builder {
938+
self.options.new_type_alias.insert(arg);
939+
self
940+
}
941+
942+
/// Mark the given typedef alias (or set of aliases, if using a pattern) to
943+
/// be generated as a new type by having the aliased type be wrapped in a
944+
/// #[repr(transparent)] struct and also have an automatically generated
945+
/// impl's of `Deref` and `DerefMut` to their aliased type.
946+
pub fn new_type_alias_deref<T: AsRef<str>>(mut self, arg: T) -> Builder {
947+
self.options.new_type_alias_deref.insert(arg);
948+
self
949+
}
950+
876951
/// Add a string to prepend to the generated bindings. The string is passed
877952
/// through without any modification.
878953
pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Self {
@@ -1443,6 +1518,19 @@ struct BindgenOptions {
14431518
/// The enum patterns to mark an enum as a set of constants.
14441519
constified_enums: RegexSet,
14451520

1521+
/// The default style of code to generate for typedefs.
1522+
default_alias_style: codegen::AliasVariation,
1523+
1524+
/// Typedef patterns that will use regular type aliasing.
1525+
type_alias: RegexSet,
1526+
1527+
/// Typedef patterns that will be aliased by creating a new struct.
1528+
new_type_alias: RegexSet,
1529+
1530+
/// Typedef patterns that will be wrapped in a new struct and have
1531+
/// Deref and Deref to their aliased type.
1532+
new_type_alias_deref: RegexSet,
1533+
14461534
/// Whether we should generate builtins or not.
14471535
builtins: bool,
14481536

@@ -1651,6 +1739,9 @@ impl BindgenOptions {
16511739
&mut self.constified_enum_modules,
16521740
&mut self.rustified_enums,
16531741
&mut self.rustified_non_exhaustive_enums,
1742+
&mut self.type_alias,
1743+
&mut self.new_type_alias,
1744+
&mut self.new_type_alias_deref,
16541745
&mut self.no_partialeq_types,
16551746
&mut self.no_copy_types,
16561747
&mut self.no_hash_types,
@@ -1696,6 +1787,10 @@ impl Default for BindgenOptions {
16961787
rustified_non_exhaustive_enums: Default::default(),
16971788
constified_enums: Default::default(),
16981789
constified_enum_modules: Default::default(),
1790+
default_alias_style: Default::default(),
1791+
type_alias: Default::default(),
1792+
new_type_alias: Default::default(),
1793+
new_type_alias_deref: Default::default(),
16991794
builtins: false,
17001795
emit_ast: false,
17011796
emit_ir: false,

src/options.rs

+66-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use bindgen::{
2-
builder, Builder, CodegenConfig, EnumVariation, RustTarget,
2+
builder, AliasVariation, Builder, CodegenConfig, EnumVariation, RustTarget,
33
RUST_TARGET_STRINGS,
44
};
55
use clap::{App, Arg};
@@ -79,6 +79,47 @@ where
7979
.takes_value(true)
8080
.multiple(true)
8181
.number_of_values(1),
82+
Arg::with_name("default-alias-style")
83+
.long("default-alias-style")
84+
.help("The default style of code used to generate typedefs.")
85+
.value_name("variant")
86+
.default_value("type_alias")
87+
.possible_values(&[
88+
"type_alias",
89+
"new_type",
90+
"new_type_deref",
91+
])
92+
.multiple(false),
93+
Arg::with_name("normal-alias")
94+
.long("normal-alias")
95+
.help(
96+
"Mark any typedef alias whose name matches <regex> to use \
97+
normal type aliasing.",
98+
)
99+
.value_name("regex")
100+
.takes_value(true)
101+
.multiple(true)
102+
.number_of_values(1),
103+
Arg::with_name("new-type-alias")
104+
.long("new-type-alias")
105+
.help(
106+
"Mark any typedef alias whose name matches <regex> to have \
107+
a new type generated for it.",
108+
)
109+
.value_name("regex")
110+
.takes_value(true)
111+
.multiple(true)
112+
.number_of_values(1),
113+
Arg::with_name("new-type-alias-deref")
114+
.long("new-type-alias-deref")
115+
.help(
116+
"Mark any typedef alias whose name matches <regex> to have \
117+
a new type with Deref and DerefMut to the inner type.",
118+
)
119+
.value_name("regex")
120+
.takes_value(true)
121+
.multiple(true)
122+
.number_of_values(1),
82123
Arg::with_name("blacklist-type")
83124
.long("blacklist-type")
84125
.help("Mark <type> as hidden.")
@@ -436,6 +477,30 @@ where
436477
builder = builder.constified_enum_module(regex);
437478
}
438479
}
480+
481+
if let Some(variant) = matches.value_of("default-alias-style") {
482+
builder =
483+
builder.default_alias_style(AliasVariation::from_str(variant)?);
484+
}
485+
486+
if let Some(type_alias) = matches.values_of("normal-alias") {
487+
for regex in type_alias {
488+
builder = builder.type_alias(regex);
489+
}
490+
}
491+
492+
if let Some(new_type) = matches.values_of("new-type-alias") {
493+
for regex in new_type {
494+
builder = builder.new_type_alias(regex);
495+
}
496+
}
497+
498+
if let Some(new_type_deref) = matches.values_of("new-type-alias-deref") {
499+
for regex in new_type_deref {
500+
builder = builder.new_type_alias_deref(regex);
501+
}
502+
}
503+
439504
if let Some(hidden_types) = matches.values_of("blacklist-type") {
440505
for ty in hidden_types {
441506
builder = builder.blacklist_type(ty);

0 commit comments

Comments
 (0)