summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Ramine <n.oxyde@gmail.com>2016-03-05 15:01:30 +0100
committerAnthony Ramine <n.oxyde@gmail.com>2016-03-10 17:14:08 +0100
commit621a81aee108c3af8b59e8ec9e161c56d506dbba (patch)
tree285ed5a242037b7fd3a15c8cca30c02c986f9b18
parent6b07fb06e4bb9916030a4aedfef3b9d96a8b386b (diff)
Properly determine which integer type to use for enums (fixes #232)
We use the kind for the sign and the layout's size for the width, and we wrap unsigned values that are too large to fit inside the determined type.
-rw-r--r--src/gen.rs94
-rw-r--r--src/types.rs2
-rw-r--r--tests/headers/enum_explicit_type.hpp8
-rw-r--r--tests/headers/overflowed_enum.hpp10
-rw-r--r--tests/support.rs1
-rw-r--r--tests/test_enum.rs22
6 files changed, 116 insertions, 21 deletions
diff --git a/src/gen.rs b/src/gen.rs
index 25e48b37..e26fbffb 100644
--- a/src/gen.rs
+++ b/src/gen.rs
@@ -1,3 +1,4 @@
+use std;
use std::cell::RefCell;
use std::vec::Vec;
use std::rc::Rc;
@@ -213,7 +214,7 @@ pub fn gen_mod(links: &[(String, LinkType)], globs: Vec<Global>, span: Span) ->
e.name = unnamed_name(&mut ctx, e.name.clone());
}
let e = ei.borrow();
- defs.extend(cenum_to_rs(&mut ctx, enum_name(&e.name), e.kind, &e.items).into_iter())
+ defs.extend(cenum_to_rs(&mut ctx, enum_name(&e.name), e.kind, e.layout, &e.items).into_iter())
},
GVar(vi) => {
let v = vi.borrow();
@@ -477,7 +478,7 @@ fn ctypedef_to_rs(ctx: &mut GenCtx, name: String, ty: &Type) -> Vec<P<ast::Item>
if is_empty {
ei.borrow_mut().name = name.clone();
let e = ei.borrow();
- cenum_to_rs(ctx, name, e.kind, &e.items)
+ cenum_to_rs(ctx, name, e.kind, e.layout, &e.items)
} else {
vec!(mk_item(ctx, name, ty))
}
@@ -699,24 +700,81 @@ fn const_to_rs(ctx: &mut GenCtx, name: String, val: i64, val_ty: ast::Ty) -> P<a
})
}
-fn enum_kind_to_rust_type_name(kind: IKind) -> &'static str {
+fn enum_kind_is_signed(kind: IKind) -> bool {
match kind {
- ISChar => "i8",
- IUChar => "u8",
- IShort => "i16",
- IUShort => "u16",
- IInt => "i32",
- IUInt => "u32",
- ILong => "i64",
- IULong => "u64",
- _ => unreachable!(),
+ IBool => false,
+ ISChar => true,
+ IUChar => false,
+ IShort => true,
+ IUShort => false,
+ IInt => true,
+ IUInt => false,
+ ILong => true,
+ IULong => false,
+ ILongLong => true,
+ IULongLong => false,
}
}
-fn cenum_to_rs(ctx: &mut GenCtx, name: String, kind: IKind, enum_items: &[EnumItem])
+fn enum_size_to_rust_type_name(signed: bool, size: usize) -> &'static str {
+ match (signed, size) {
+ (true, 1) => "i8",
+ (false, 1) => "u8",
+ (true, 2) => "i16",
+ (false, 2) => "u16",
+ (true, 4) => "i32",
+ (false, 4) => "u32",
+ (true, 8) => "i64",
+ (false, 8) => "u64",
+ _ => unreachable!("invalid enum decl: signed: {}, size: {}", signed, size),
+ }
+}
+
+fn enum_size_to_unsigned_max_value(size: usize) -> u64 {
+ match size {
+ 1 => std::u8::MAX as u64,
+ 2 => std::u16::MAX as u64,
+ 4 => std::u32::MAX as u64,
+ 8 => std::u64::MAX,
+ _ => unreachable!("invalid enum size: {}", size)
+ }
+}
+
+fn cenum_value_to_int_lit(
+ ctx: &mut GenCtx,
+ enum_is_signed: bool,
+ size: usize,
+ value: i64)
+ -> P<ast::Expr> {
+ if enum_is_signed {
+ let int_lit =
+ ast::LitKind::Int(value.abs() as u64, ast::LitIntType::Unsuffixed);
+ let expr = ctx.ext_cx.expr_lit(ctx.span, int_lit);
+ if value < 0 {
+ ctx.ext_cx.expr(
+ ctx.span, ast::ExprKind::Unary(ast::UnOp::Neg, expr))
+ } else {
+ expr
+ }
+ } else {
+ let u64_value =
+ value as u64 & enum_size_to_unsigned_max_value(size);
+ let int_lit =
+ ast::LitKind::Int(u64_value, ast::LitIntType::Unsuffixed);
+ ctx.ext_cx.expr_lit(ctx.span, int_lit)
+ }
+}
+
+
+fn cenum_to_rs(ctx: &mut GenCtx,
+ name: String,
+ kind: IKind,
+ layout: Layout,
+ enum_items: &[EnumItem])
-> Vec<P<ast::Item>> {
let enum_name = ctx.ext_cx.ident_of(&name);
let enum_ty = ctx.ext_cx.ty_ident(ctx.span, enum_name);
+ let enum_is_signed = enum_kind_is_signed(kind);
let mut variants = vec![];
let mut found_values = HashMap::new();
@@ -741,12 +799,8 @@ fn cenum_to_rs(ctx: &mut GenCtx, name: String, kind: IKind, enum_items: &[EnumIt
found_values.insert(item.val, name);
- let int_lit = ast::LitKind::Int(item.val.abs() as u64, ast::LitIntType::Unsuffixed);
- let mut value = ctx.ext_cx.expr_lit(ctx.span, int_lit);
- if item.val < 0 {
- let negated = ast::ExprKind::Unary(ast::UnOp::Neg, value);
- value = ctx.ext_cx.expr(ctx.span, negated);
- }
+ let value = cenum_value_to_int_lit(
+ ctx, enum_is_signed, layout.size, item.val);
variants.push(respan(ctx.span, ast::Variant_ {
name: name,
@@ -756,7 +810,7 @@ fn cenum_to_rs(ctx: &mut GenCtx, name: String, kind: IKind, enum_items: &[EnumIt
}));
}
- let enum_repr = InternedString::new(enum_kind_to_rust_type_name(kind));
+ let enum_repr = InternedString::new(enum_size_to_rust_type_name(enum_is_signed, layout.size));
let repr_arg = ctx.ext_cx.meta_word(ctx.span, enum_repr);
let repr_list = ctx.ext_cx.meta_list(ctx.span, InternedString::new("repr"), vec![repr_arg]);
diff --git a/src/types.rs b/src/types.rs
index 3673df36..14526fd0 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -142,7 +142,7 @@ impl Type {
}
}
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Layout {
pub size: usize,
pub align: usize,
diff --git a/tests/headers/enum_explicit_type.hpp b/tests/headers/enum_explicit_type.hpp
index 6aa5103d..1fcb4880 100644
--- a/tests/headers/enum_explicit_type.hpp
+++ b/tests/headers/enum_explicit_type.hpp
@@ -12,3 +12,11 @@ enum Bigger: unsigned short {
Much = 255,
Larger
};
+
+enum MuchLong: long {
+ MuchLow = -4294967296,
+};
+
+enum MuchLongLong: unsigned long long {
+ MuchHigh = 4294967296,
+};
diff --git a/tests/headers/overflowed_enum.hpp b/tests/headers/overflowed_enum.hpp
new file mode 100644
index 00000000..22fe78ff
--- /dev/null
+++ b/tests/headers/overflowed_enum.hpp
@@ -0,0 +1,10 @@
+enum Foo {
+ BAP_ARM = 0x93fcb9,
+ BAP_X86 = 0xb67eed,
+ BAP_X86_64 = 0xba7b274f,
+};
+
+enum Bar: unsigned short {
+ One = 1,
+ Big = 65538,
+};
diff --git a/tests/support.rs b/tests/support.rs
index 178e8cd2..620a8429 100644
--- a/tests/support.rs
+++ b/tests/support.rs
@@ -27,6 +27,7 @@ pub fn generate_bindings(filename: &str) -> Result<Vec<P<ast::Item>>, ()> {
let mut options:BindgenOptions = Default::default();
if filename.ends_with("hpp") {
options.clang_args.push("-std=c++11".to_string());
+ options.clang_args.push("-Wno-narrowing".to_string());
}
options.clang_args.push(filename.to_string());
diff --git a/tests/test_enum.rs b/tests/test_enum.rs
index 2b94f3b4..66cde668 100644
--- a/tests/test_enum.rs
+++ b/tests/test_enum.rs
@@ -49,5 +49,27 @@ fn with_explicitly_typed_cxx_enum() {
#[derive(Clone, Copy)]
#[repr(u16)]
pub enum Enum_Bigger { Much = 255, Larger = 256, }
+ #[derive(Clone, Copy)]
+ #[repr(i64)]
+ pub enum Enum_MuchLong { MuchLow = -4294967296, }
+ #[derive(Clone, Copy)]
+ #[repr(u64)]
+ pub enum Enum_MuchLongLong { MuchHigh = 4294967296, }
+ ");
+}
+
+#[test]
+fn with_overflowed_enum_value() {
+ assert_bind_eq("headers/overflowed_enum.hpp", "
+ #[derive(Clone, Copy)]
+ #[repr(u32)]
+ pub enum Enum_Foo {
+ BAP_ARM = 9698489,
+ BAP_X86 = 11960045,
+ BAP_X86_64 = 3128633167,
+ }
+ #[derive(Clone, Copy)]
+ #[repr(u16)]
+ pub enum Enum_Bar { One = 1, Big = 2, }
");
}