Skip to content

Commit 8bf4a40

Browse files
michaelwunox
authored andcommitted
Translate C enums to Rust enums
Duplicate values end up as constants of the same enum type. Most enums are repr(u32) as they should, except for those with attribute((packed)), which are of the smallest representation possible.
1 parent 6e33fbe commit 8bf4a40

File tree

9 files changed

+188
-20
lines changed

9 files changed

+188
-20
lines changed

src/gen.rs

+86-20
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use syntax::ext::quote::rt::ToTokens;
1414
use syntax::feature_gate::Features;
1515
use syntax::owned_slice::OwnedSlice;
1616
use syntax::parse;
17+
use syntax::parse::token::InternedString;
1718
use syntax::attr::mk_attr_id;
1819
use syntax::ptr::P;
1920
use syntax::print::pprust::tts_to_string;
@@ -211,8 +212,8 @@ pub fn gen_mod(links: &[(String, LinkType)], globs: Vec<Global>, span: Span) ->
211212
let mut e = ei.borrow_mut();
212213
e.name = unnamed_name(&mut ctx, e.name.clone());
213214
}
214-
let e = ei.borrow().clone();
215-
defs.extend(cenum_to_rs(&mut ctx, enum_name(&e.name), e.kind, e.items).into_iter())
215+
let e = ei.borrow();
216+
defs.extend(cenum_to_rs(&mut ctx, enum_name(&e.name), e.kind, &e.items).into_iter())
216217
},
217218
GVar(vi) => {
218219
let v = vi.borrow();
@@ -476,8 +477,8 @@ fn ctypedef_to_rs(ctx: &mut GenCtx, name: String, ty: &Type) -> Vec<P<ast::Item>
476477
let is_empty = ei.borrow().name.is_empty();
477478
if is_empty {
478479
ei.borrow_mut().name = name.clone();
479-
let e = ei.borrow().clone();
480-
cenum_to_rs(ctx, name, e.kind, e.items)
480+
let e = ei.borrow();
481+
cenum_to_rs(ctx, name, e.kind, &e.items)
481482
} else {
482483
vec!(mk_item(ctx, name, ty))
483484
}
@@ -555,7 +556,7 @@ fn cstruct_to_rs(ctx: &mut GenCtx, name: String,
555556

556557
let id = rust_type_id(ctx, name.clone());
557558
let struct_def = P(ast::Item { ident: ctx.ext_cx.ident_of(&id[..]),
558-
attrs: vec!(mk_repr_attr(ctx, layout), mk_deriving_copy_attr(ctx)),
559+
attrs: vec!(mk_repr_attr(ctx, layout), mk_deriving_copy_attr(ctx, false)),
559560
id: ast::DUMMY_NODE_ID,
560561
node: def,
561562
vis: ast::Public,
@@ -637,7 +638,7 @@ fn cunion_to_rs(ctx: &mut GenCtx, name: String, layout: Layout, members: Vec<Com
637638
empty_generics()
638639
);
639640
let union_id = rust_type_id(ctx, name.clone());
640-
let union_attrs = vec!(mk_repr_attr(ctx, layout), mk_deriving_copy_attr(ctx));
641+
let union_attrs = vec!(mk_repr_attr(ctx, layout), mk_deriving_copy_attr(ctx, false));
641642
let union_def = mk_item(ctx, union_id, def, ast::Public, union_attrs);
642643

643644
let union_impl = ast::ItemImpl(
@@ -682,18 +683,80 @@ fn const_to_rs(ctx: &mut GenCtx, name: String, val: i64, val_ty: ast::Ty) -> P<a
682683
})
683684
}
684685

685-
fn cenum_to_rs(ctx: &mut GenCtx, name: String, kind: IKind, items: Vec<EnumItem>) -> Vec<P<ast::Item>> {
686-
let ty = TInt(kind, Layout::zero());
687-
let ty_id = rust_type_id(ctx, name);
688-
let ty_def = ctypedef_to_rs(ctx, ty_id, &ty);
689-
let val_ty = cty_to_rs(ctx, &ty);
690-
let mut def = ty_def;
686+
fn enum_kind_to_rust_type_name(kind: IKind) -> &'static str {
687+
match kind {
688+
ISChar => "i8",
689+
IUChar => "u8",
690+
IShort => "i16",
691+
IUShort => "u16",
692+
IInt => "i32",
693+
IUInt => "u32",
694+
ILong => "i64",
695+
IULong => "u64",
696+
_ => unreachable!(),
697+
}
698+
}
699+
700+
fn cenum_to_rs(ctx: &mut GenCtx, name: String, kind: IKind, enum_items: &[EnumItem])
701+
-> Vec<P<ast::Item>> {
702+
let enum_name = ctx.ext_cx.ident_of(&name);
703+
let enum_ty = ctx.ext_cx.ty_ident(ctx.span, enum_name);
704+
705+
let mut variants = vec![];
706+
let mut found_values = HashMap::new();
707+
let mut items = vec![];
708+
709+
for item in enum_items {
710+
let name = ctx.ext_cx.ident_of(&item.name);
711+
712+
if let Some(orig) = found_values.get(&item.val) {
713+
let value = ctx.ext_cx.expr_path(
714+
ctx.ext_cx.path(ctx.span, vec![enum_name, *orig]));
715+
items.push(P(ast::Item {
716+
ident: name,
717+
attrs: vec![],
718+
id: ast::DUMMY_NODE_ID,
719+
node: ast::ItemConst(enum_ty.clone(), value),
720+
vis: ast::Public,
721+
span: ctx.span,
722+
}));
723+
continue;
724+
}
725+
726+
found_values.insert(item.val, name);
727+
728+
let sign = ast::UnsuffixedIntLit(if item.val < 0 { ast::Minus } else { ast::Plus });
729+
let value = ctx.ext_cx.expr_lit(ctx.span, ast::LitInt(item.val.abs() as u64, sign));
691730

692-
for it in items.iter() {
693-
def.push(const_to_rs(ctx, it.name.clone(), it.val, val_ty.clone()));
731+
variants.push(P(respan(ctx.span, ast::Variant_ {
732+
name: name,
733+
attrs: vec![],
734+
data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
735+
disr_expr: Some(value),
736+
})));
694737
}
695738

696-
return def;
739+
let enum_repr = InternedString::new(enum_kind_to_rust_type_name(kind));
740+
741+
let repr_arg = ctx.ext_cx.meta_word(ctx.span, enum_repr);
742+
let repr_list = ctx.ext_cx.meta_list(ctx.span, InternedString::new("repr"), vec![repr_arg]);
743+
let repr_attr = respan(ctx.span, ast::Attribute_ {
744+
id: mk_attr_id(),
745+
style: ast::AttrStyle::Outer,
746+
value: repr_list,
747+
is_sugared_doc: false,
748+
});
749+
750+
items.push(P(ast::Item {
751+
ident: enum_name,
752+
attrs: vec![mk_deriving_copy_attr(ctx, true), repr_attr],
753+
id: ast::DUMMY_NODE_ID,
754+
node: ast::ItemEnum(ast::EnumDef { variants: variants }, empty_generics()),
755+
vis: ast::Public,
756+
span: ctx.span,
757+
}));
758+
759+
items
697760
}
698761

699762
/// Generates accessors for fields in nested structs and unions which must be
@@ -848,11 +911,14 @@ fn mk_repr_attr(ctx: &mut GenCtx, layout: Layout) -> ast::Attribute {
848911
})
849912
}
850913

851-
fn mk_deriving_copy_attr(ctx: &mut GenCtx) -> ast::Attribute {
852-
let attr_val = P(respan(ctx.span, ast::MetaList(
853-
to_intern_str(ctx, "derive".to_string()),
854-
vec!(P(respan(ctx.span, ast::MetaWord(to_intern_str(ctx, "Copy".to_string())))))
855-
)));
914+
fn mk_deriving_copy_attr(ctx: &mut GenCtx, clone: bool) -> ast::Attribute {
915+
let mut words = vec!();
916+
if clone {
917+
words.push(ctx.ext_cx.meta_word(ctx.span, InternedString::new("Clone")));
918+
}
919+
words.push(ctx.ext_cx.meta_word(ctx.span, InternedString::new("Copy")));
920+
921+
let attr_val = ctx.ext_cx.meta_list(ctx.span, InternedString::new("derive"), words);
856922

857923
respan(ctx.span, ast::Attribute_ {
858924
id: mk_attr_id(),

tests/headers/enum.h

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
enum Foo {
2+
Bar = 0,
3+
Qux
4+
};
5+
6+
enum Neg {
7+
MinusOne = -1,
8+
One = 1,
9+
};

tests/headers/enum_dupe.h

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
enum Foo {
2+
Bar = 1,
3+
Dupe = 1
4+
};

tests/headers/enum_explicit_type.hpp

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
enum Foo: unsigned char {
2+
Bar = 0,
3+
Qux
4+
};
5+
6+
enum Neg: char {
7+
MinusOne = -1,
8+
One = 1,
9+
};
10+
11+
enum Bigger: unsigned short {
12+
Much = 255,
13+
Larger
14+
};

tests/headers/enum_negative.h

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
enum Foo {
2+
Bar = -2,
3+
Qux = 1,
4+
};

tests/headers/enum_packed.h

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
enum __attribute__((packed)) Foo {
2+
Bar = 0,
3+
Qux
4+
};
5+
6+
enum __attribute__((packed)) Neg {
7+
MinusOne = -1,
8+
One = 1,
9+
};
10+
11+
enum __attribute__((packed)) Bigger {
12+
Much = 255,
13+
Larger
14+
};

tests/support.rs

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ impl Logger for TestLogger {
2525

2626
pub fn generate_bindings(filename: &str) -> Result<Vec<P<ast::Item>>, ()> {
2727
let mut options:BindgenOptions = Default::default();
28+
if filename.ends_with("hpp") {
29+
options.clang_args.push("-std=c++11".to_string());
30+
}
2831
options.clang_args.push(filename.to_string());
2932

3033
let logger = TestLogger;

tests/test_enum.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use support::assert_bind_eq;
2+
3+
#[test]
4+
fn with_simple_enum() {
5+
assert_bind_eq("headers/enum.h", "
6+
#[derive(Clone, Copy)]
7+
#[repr(u32)]
8+
pub enum Enum_Foo { Bar = 0, Qux = 1, }
9+
#[derive(Clone, Copy)]
10+
#[repr(i32)]
11+
pub enum Enum_Neg { MinusOne = -1, One = 1, }
12+
");
13+
}
14+
15+
#[test]
16+
fn with_packed_enums() {
17+
assert_bind_eq("headers/enum_packed.h", "
18+
#[derive(Clone, Copy)]
19+
#[repr(u8)]
20+
pub enum Enum_Foo { Bar = 0, Qux = 1, }
21+
#[derive(Clone, Copy)]
22+
#[repr(i8)]
23+
pub enum Enum_Neg { MinusOne = -1, One = 1, }
24+
#[derive(Clone, Copy)]
25+
#[repr(u16)]
26+
pub enum Enum_Bigger { Much = 255, Larger = 256, }
27+
");
28+
}
29+
30+
#[test]
31+
fn with_duplicate_enum_value() {
32+
assert_bind_eq("headers/enum_dupe.h", "
33+
pub const Dupe: Enum_Foo = Enum_Foo::Bar;
34+
#[derive(Clone, Copy)]
35+
#[repr(u32)]
36+
pub enum Enum_Foo { Bar = 1, }
37+
");
38+
}
39+
40+
#[test]
41+
fn with_explicitly_typed_cxx_enum() {
42+
assert_bind_eq("headers/enum_explicit_type.hpp", "
43+
#[derive(Clone, Copy)]
44+
#[repr(u8)]
45+
pub enum Enum_Foo { Bar = 0, Qux = 1, }
46+
#[derive(Clone, Copy)]
47+
#[repr(i8)]
48+
pub enum Enum_Neg { MinusOne = -1, One = 1, }
49+
#[derive(Clone, Copy)]
50+
#[repr(u16)]
51+
pub enum Enum_Bigger { Much = 255, Larger = 256, }
52+
");
53+
}

tests/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod support;
88

99
// Unused until we can generate code for tests
1010
//mod test_cmath;
11+
mod test_enum;
1112
mod test_decl;
1213
mod test_func;
1314
mod test_struct;

0 commit comments

Comments
 (0)