Skip to content

Commit b40786a

Browse files
committed
Merge pull request rust-lang#247 from nox/proper-enum
Translate C enums to Rust enums
2 parents 6e33fbe + 8bf4a40 commit b40786a

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)