diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2016-08-16 02:21:01 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-08-16 02:21:01 -0500 |
commit | eab08be8164bb37ff52bfcb5e739dec58ce9434b (patch) | |
tree | 8297a69b9203879ea9a3cb94ad3e83de9a16f121 | |
parent | 704a6f999904a31d37c1bdc99a66686e65c85cde (diff) | |
parent | 52fd1771877b6f8a81b47edc08313f56e50c5d96 (diff) |
Auto merge of #29 - emilio:template-unions, r=nox
Add support for basic template unions.
Fixes #28.
r? @nox
cc @bholley
-rw-r--r-- | src/clang.rs | 4 | ||||
-rw-r--r-- | src/gen.rs | 390 | ||||
-rw-r--r-- | src/parser.rs | 45 | ||||
-rw-r--r-- | src/types.rs | 106 | ||||
-rw-r--r-- | tests/expectations/ref_argument_array.rs | 27 | ||||
-rw-r--r-- | tests/expectations/union_template.rs | 86 | ||||
-rw-r--r-- | tests/expectations/vtable_recursive_sig.rs | 36 | ||||
-rw-r--r-- | tests/headers/ref_argument_array.hpp | 6 | ||||
-rw-r--r-- | tests/headers/union_template.hpp | 19 | ||||
-rw-r--r-- | tests/headers/vtable_recursive_sig.hpp | 11 |
10 files changed, 508 insertions, 222 deletions
diff --git a/src/clang.rs b/src/clang.rs index c230460d..f8a68e12 100644 --- a/src/clang.rs +++ b/src/clang.rs @@ -119,6 +119,10 @@ impl Cursor { } } + pub fn template_kind(&self) -> Enum_CXCursorKind { + unsafe { clang_getTemplateCursorKind(self.x) } + } + pub fn visit<F>(&self, func:F) where F: for<'a, 'b> FnMut(&'a Cursor, &'b Cursor) -> Enum_CXChildVisitResult { @@ -69,7 +69,7 @@ fn ref_eq<T>(thing: &T, other: &T) -> bool { (thing as *const T) == (other as *const T) } -fn rust_id(ctx: &mut GenCtx, name: &str) -> (String, bool) { +fn rust_id(ctx: &GenCtx, name: &str) -> (String, bool) { let token = parse::token::Ident(ctx.ext_cx.ident_of(name)); if token.is_any_keyword() || name.contains("@") || @@ -88,7 +88,7 @@ fn rust_id(ctx: &mut GenCtx, name: &str) -> (String, bool) { } } -fn rust_type_id(ctx: &mut GenCtx, name: &str) -> String { +fn rust_type_id(ctx: &GenCtx, name: &str) -> String { match name { "bool" | "uint" | "u8" | "u16" | "u32" | "f32" | "f64" | "i8" | @@ -411,7 +411,7 @@ fn gen_global(mut ctx: &mut GenCtx, !c.args.iter().any(|a| a.name().map(|name| name.is_empty()).unwrap_or(true)) { defs.extend(comp_to_rs(&mut ctx, &name, c).into_iter()); } else { - defs.push(opaque_to_rs(&mut ctx, &name, c.layout)); + defs.push(opaque_to_rs(&mut ctx, &name, c.layout())); } }, GComp(ci) => { @@ -558,7 +558,7 @@ fn gen_globals(mut ctx: &mut GenCtx, defs } -fn mk_extern(ctx: &mut GenCtx, links: &[(String, LinkType)], +fn mk_extern(ctx: &GenCtx, links: &[(String, LinkType)], foreign_items: Vec<ast::ForeignItem>, abi: Abi) -> P<ast::Item> { let attrs: Vec<_> = links.iter().map(|&(ref l, ref k)| { @@ -606,7 +606,7 @@ fn mk_extern(ctx: &mut GenCtx, links: &[(String, LinkType)], }) } -fn mk_impl(_ctx: &mut GenCtx, ty: P<ast::Ty>, +fn mk_impl(_ctx: &GenCtx, ty: P<ast::Ty>, items: Vec<ast::ImplItem>) -> P<ast::Item> { aster::AstBuilder::new().item().impl_().with_items(items).build_ty(ty) @@ -758,7 +758,7 @@ fn tag_dup_decl(gs: &[Global]) -> Vec<Global> { } fn ctypedef_to_rs(ctx: &mut GenCtx, ty: TypeInfo) -> Vec<P<ast::Item>> { - fn mk_item(ctx: &mut GenCtx, name: &str, comment: &str, ty: &Type) -> P<ast::Item> { + fn mk_item(ctx: &GenCtx, name: &str, comment: &str, ty: &Type) -> P<ast::Item> { let rust_name = rust_type_id(ctx, name); let rust_ty = if cty_is_translatable(ty) { cty_to_rs(ctx, ty, true, true) @@ -797,18 +797,25 @@ fn comp_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo) if ci.opaque { let name = first(rust_id(ctx, &ci.name)); - return mk_opaque_struct(ctx, &name, &ci.layout); + return mk_opaque_struct(ctx, &name, &ci.layout()); } + if ci.has_non_type_template_params || + ci.args.iter().any(|f| f == &TVoid) { + return vec![]; + } + + let mut template_args_used = vec![false; ci.args.len()]; + match ci.kind { - CompKind::Struct => cstruct_to_rs(ctx, name, ci), - CompKind::Union => cunion_to_rs(ctx, name, ci), + CompKind::Struct => cstruct_to_rs(ctx, name, ci, &mut template_args_used), + CompKind::Union => cunion_to_rs(ctx, name, ci, &mut template_args_used), } } fn comp_attrs(ctx: &GenCtx, ci: &CompInfo, name: &str, extra: &mut Vec<P<ast::Item>>) -> Vec<ast::Attribute> { let mut attrs = mk_doc_attr(ctx, &ci.comment); - attrs.push(mk_repr_attr(ctx, &ci.layout)); + attrs.push(mk_repr_attr(ctx, &ci.layout())); let mut derives = vec![]; if ci.can_derive_debug() && ctx.options.derive_debug { @@ -888,8 +895,34 @@ fn gen_accessors(ctx: &mut GenCtx, name: &str, ty: &ast::Ty, accessor: Accessor, } } -fn cstruct_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo) -> Vec<P<ast::Item>> { - let layout = ci.layout; +fn add_extra_template_fields_if_needed(ctx: &GenCtx, + template_args: &[Type], + template_args_used: &[bool], + fields: &mut Vec<ast::StructField>) { + let mut phantom_count = 0; + for (i, arg) in template_args.iter().enumerate() { + if template_args_used[i] { + continue; + } + + let f_name = format!("_phantom{}", phantom_count); + phantom_count += 1; + let inner_type = P(cty_to_rs(ctx, &arg, true, false)); + + fields.push(ast::StructField { + span: ctx.span, + ident: Some(ctx.ext_cx.ident_of(&f_name)), + vis: ast::Visibility::Public, + id: ast::DUMMY_NODE_ID, + ty: quote_ty!(&ctx.ext_cx, ::std::marker::PhantomData<$inner_type>), + attrs: vec![], + }); + } +} + +fn cstruct_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo, + template_args_used: &mut [bool]) -> Vec<P<ast::Item>> { + let layout = ci.layout(); let members = &ci.members; let template_args = &ci.args; let methodlist = &ci.methods; @@ -902,11 +935,6 @@ fn cstruct_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo) -> Vec<P<ast::Item> let mut unnamed: u32 = 0; let mut bitfields: u32 = 0; - if ci.has_non_type_template_params || - template_args.iter().any(|f| f == &TVoid) { - return vec![]; - } - let id = rust_type_id(ctx, name); let id_ty = P(mk_ty(ctx, false, &[id.clone()])); @@ -989,173 +1017,121 @@ fn cstruct_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo) -> Vec<P<ast::Item> let mut anon_enum_count = 0; let mut setters = vec![]; - let mut template_args_used = vec![false; template_args.len()]; for m in members.iter() { - if let CompMember::Enum(ref ei) = *m { - let empty_name = ei.borrow().name.is_empty(); - if empty_name { - ei.borrow_mut().name = format!("{}_enum{}", name, anon_enum_count); - anon_enum_count += 1; - } - - let e = ei.borrow().clone(); - extra.extend(cenum_to_rs(ctx, &e.name, e.kind, e.comment, &e.items, e.layout).into_iter()); - continue; - } + match *m { + CompMember::Enum(ref ei) => { + let empty_name = ei.borrow().name.is_empty(); + if empty_name { + ei.borrow_mut().name = format!("{}_enum{}", name, anon_enum_count); + anon_enum_count += 1; + } - fn comp_fields(m: &CompMember) - -> (Option<Rc<RefCell<CompInfo>>>, Option<FieldInfo>) { - match *m { - CompMember::Field(ref f) => { (None, Some(f.clone())) } - CompMember::Comp(ref rc_c) => { (Some(rc_c.clone()), None) } - CompMember::CompField(ref rc_c, ref f) => { (Some(rc_c.clone()), Some(f.clone())) } - _ => unreachable!() + let e = ei.borrow().clone(); + extra.extend(cenum_to_rs(ctx, &e.name, e.kind, e.comment, &e.items, e.layout).into_iter()); } - } - - let (opt_rc_c, opt_f) = comp_fields(m); + CompMember::Field(ref f) => { + let f_name = match f.bitfields { + Some(_) => { + bitfields += 1; + format!("_bitfield_{}", bitfields) + } + None => rust_type_id(ctx, &f.name) + }; - if let Some(f) = opt_f { - let f_name = match f.bitfields { - Some(_) => { - bitfields += 1; - format!("_bitfield_{}", bitfields) + let is_translatable = cty_is_translatable(&f.ty); + if !is_translatable || f.ty.is_opaque() { + if !is_translatable { + warn!("{}::{} not translatable, void: {}", ci.name, f.name, f.ty == TVoid); + } + if let Some(layout) = f.ty.layout() { + fields.push(mk_blob_field(ctx, &f_name, &layout)); + } + continue; } - None => rust_type_id(ctx, &f.name) - }; - let f_ty = f.ty; - - let is_translatable = cty_is_translatable(&f_ty); - if !is_translatable || f_ty.is_opaque() { - if !is_translatable { - warn!("{}::{} not translatable, void: {}", ci.name, f.name, f_ty == TVoid); - } - if let Some(layout) = f_ty.layout() { - fields.push(mk_blob_field(ctx, &f_name, &layout)); + if ctx.options.gen_bitfield_methods { + let mut offset: u32 = 0; + if let Some(ref bitfields) = f.bitfields { + for &(ref bf_name, bf_size) in bitfields.iter() { + setters.extend(gen_bitfield_methods(ctx, &f_name, bf_name, &f.ty, offset as usize, bf_size).into_iter()); + offset += bf_size; + } + setters.push(gen_fullbitfield_method(ctx, &f_name, &f.ty, bitfields)) + } } - continue; - } - if ctx.options.gen_bitfield_methods { - let mut offset: u32 = 0; - if let Some(ref bitfields) = f.bitfields { - for &(ref bf_name, bf_size) in bitfields.iter() { - setters.extend(gen_bitfield_methods(ctx, &f_name, bf_name, &f_ty, offset as usize, bf_size).into_iter()); - offset += bf_size; + // If the member is not a template argument, it needs the full path. + let mut needs_full_path = true; + for (index, arg) in template_args.iter().enumerate() { + let used = f.ty.signature_contains_type(arg); + + if used { + template_args_used[index] = true; + needs_full_path = *arg == f.ty || match f.ty { + TPtr(ref t, _, _, _) => **t != *arg, + TArray(ref t, _, _) => **t != *arg, + _ => true, + }; + break; } - setters.push(gen_fullbitfield_method(ctx, &f_name, &f_ty, bitfields)) } - } - let mut bypass = false; - let f_ty = if let Some(ref rc_c) = opt_rc_c { - if rc_c.borrow().members.len() == 1 { - if let CompMember::Field(ref inner_f) = rc_c.borrow().members[0] { - bypass = true; - inner_f.ty.clone() + let rust_ty = P(cty_to_rs(ctx, &f.ty, f.bitfields.is_none(), needs_full_path)); + + // Wrap mutable fields in a Cell/UnsafeCell + let rust_ty = if f.mutable { + if !f.ty.can_derive_copy() { + quote_ty!(&ctx.ext_cx, ::std::cell::UnsafeCell<$rust_ty>) + // We can only wrap in a cell for non-copiable types, since + // Cell<T>: Clone, but not Copy. + // + // It's fine though, since mutating copiable types is trivial + // and doesn't make a lot of sense marking fields as `mutable`. + } else if !ci.can_derive_copy() { + quote_ty!(&ctx.ext_cx, ::std::cell::Cell<$rust_ty>) } else { - f_ty + rust_ty } } else { - f_ty - } - } else { - f_ty - }; - - // If the member is not a template argument, it needs the full path. - let mut needs_full_path = true; - for (index, arg) in template_args.iter().enumerate() { - let used = f_ty.signature_contains_type(arg); - - if used { - template_args_used[index] = true; - needs_full_path = *arg == f_ty || match f_ty { - TPtr(ref t, _, _, _) => **t != *arg, - TArray(ref t, _, _) => **t != *arg, - _ => true, - }; - break; - } + rust_ty + }; + let vis = if f.private { + ast::Visibility::Inherited + } else { + ast::Visibility::Public + }; + gen_accessors(ctx, &f_name, &rust_ty, f.accessor, &mut methods); + let field = ast::StructField { + span: ctx.span, + ident: Some(ctx.ext_cx.ident_of(&f_name)), + vis: vis, + id: ast::DUMMY_NODE_ID, + ty: rust_ty, + attrs: mk_doc_attr(ctx, &f.comment) + }; + fields.push(field); } + CompMember::Comp(ref rc_c) => { + let name_is_empty = rc_c.borrow().name.is_empty(); - let rust_ty = P(cty_to_rs(ctx, &f_ty, f.bitfields.is_none(), needs_full_path)); - - // Wrap mutable fields in a Cell/UnsafeCell - let rust_ty = if f.mutable { - if !f_ty.can_derive_copy() { - quote_ty!(&ctx.ext_cx, ::std::cell::UnsafeCell<$rust_ty>) - // We can only wrap in a cell for non-copiable types, since - // Cell<T>: Clone, but not Copy. - // - // It's fine though, since mutating copiable types is trivial - // and doesn't make a lot of sense marking fields as `mutable`. - } else if !ci.can_derive_copy() { - quote_ty!(&ctx.ext_cx, ::std::cell::Cell<$rust_ty>) + if name_is_empty { + let c = rc_c.borrow(); + unnamed += 1; + let field_name = format!("_bindgen_data_{}_", unnamed); + fields.push(mk_blob_field(ctx, &field_name, &c.layout())); + methods.extend(gen_comp_methods(ctx, &field_name, 0, c.kind, &c.members, &mut extra).into_iter()); } else { - rust_ty + let name = comp_name(&ctx, rc_c.borrow().kind, &rc_c.borrow().name); + extra.extend(comp_to_rs(ctx, &name, rc_c.borrow().clone()).into_iter()); } - } else { - rust_ty - }; - let vis = if f.private { - ast::Visibility::Inherited - } else { - ast::Visibility::Public - }; - gen_accessors(ctx, &f_name, &rust_ty, f.accessor, &mut methods); - let field = ast::StructField { - span: ctx.span, - ident: Some(ctx.ext_cx.ident_of(&f_name)), - vis: vis, - id: ast::DUMMY_NODE_ID, - ty: rust_ty, - attrs: mk_doc_attr(ctx, &f.comment) - }; - fields.push(field); - - if bypass { - continue; - } - } - - if let Some(rc_c) = opt_rc_c { - let name_is_empty = rc_c.borrow().name.is_empty(); - - if name_is_empty { - let c = rc_c.borrow(); - unnamed += 1; - let field_name = format!("_bindgen_data_{}_", unnamed); - fields.push(mk_blob_field(ctx, &field_name, &c.layout)); - methods.extend(gen_comp_methods(ctx, &field_name, 0, c.kind, &c.members, &mut extra).into_iter()); - } else { - let name = comp_name(&ctx, rc_c.borrow().kind, &rc_c.borrow().name); - extra.extend(comp_to_rs(ctx, &name, rc_c.borrow().clone()).into_iter()); } } } - let mut phantom_count = 0; - for (i, arg) in template_args.iter().enumerate() { - if template_args_used[i] { - continue; - } - - let f_name = format!("_phantom{}", phantom_count); - phantom_count += 1; - let inner_type = P(cty_to_rs(ctx, &arg, true, false)); - - fields.push(ast::StructField { - span: ctx.span, - ident: Some(ctx.ext_cx.ident_of(&f_name)), - vis: ast::Visibility::Public, - id: ast::DUMMY_NODE_ID, - ty: quote_ty!(&ctx.ext_cx, ::std::marker::PhantomData<$inner_type>), - attrs: vec![], - }); - } + add_extra_template_fields_if_needed(ctx, template_args, + template_args_used, + &mut fields); if !setters.is_empty() { extra.push(mk_impl(ctx, id_ty.clone(), setters)); @@ -1266,15 +1242,16 @@ fn opaque_to_rs(ctx: &mut GenCtx, name: &str, _layout: Layout) -> P<ast::Item> { quote_item!(&ctx.ext_cx, pub enum $ident {}).unwrap() } -fn cunion_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo) -> Vec<P<ast::Item>> { +fn cunion_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo, + template_args_used: &mut [bool]) -> Vec<P<ast::Item>> { const UNION_DATA_FIELD_NAME: &'static str = "_bindgen_data_"; ctx.saw_union = true; let members = &ci.members; - let layout = &ci.layout; + let layout = ci.layout(); - fn mk_item(ctx: &mut GenCtx, name: &str, item: ast::ItemKind, vis: + fn mk_item(ctx: &GenCtx, name: &str, item: ast::ItemKind, vis: ast::Visibility, attrs: Vec<ast::Attribute>) -> P<ast::Item> { P(ast::Item { ident: ctx.ext_cx.ident_of(name), @@ -1287,7 +1264,8 @@ fn cunion_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo) -> Vec<P<ast::Item>> } let tmp_ci = Rc::new(RefCell::new(ci.clone())); - let union = TNamed(Rc::new(RefCell::new(TypeInfo::new(name.to_owned(), ROOT_MODULE_ID, TComp(tmp_ci), layout.clone())))); + let union = TComp(tmp_ci); + // Nested composites may need to emit declarations and implementations as // they are encountered. The declarations end up in 'extra' and are emitted @@ -1312,33 +1290,67 @@ fn cunion_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo) -> Vec<P<ast::Item>> } } - let mut fields = members.iter() - .flat_map(|member| match *member { - CompMember::Field(ref f) => { - let cty = cty_to_rs(ctx, &f.ty, false, true); - Some(mk_union_field(ctx, &f.name, cty)) - } - _ => None, - }).collect::<Vec<_>>(); - fields.push(mk_blob_field(ctx, UNION_DATA_FIELD_NAME, layout)); + let mut fields = + members.iter().flat_map(|member| { + if let CompMember::Field(ref f) = *member { + let mut needs_full_path = true; + for (index, arg) in ci.args.iter().enumerate() { + let used = f.ty.signature_contains_type(arg); + + if used { + template_args_used[index] = true; + needs_full_path = *arg == f.ty || match f.ty { + TPtr(ref t, _, _, _) => **t != *arg, + TArray(ref t, _, _) => **t != *arg, + _ => true, + }; + break; + } + } + + let cty = cty_to_rs(ctx, &f.ty, false, needs_full_path); + Some(mk_union_field(ctx, &f.name, cty)) + } else { + None + } + }).collect::<Vec<_>>(); + + fields.push(mk_blob_field(ctx, UNION_DATA_FIELD_NAME, &layout)); + + add_extra_template_fields_if_needed(ctx, &ci.args, + template_args_used, + &mut fields); + + let ty_params = mk_ty_params(ctx, &ci.args); + + let generics = ast::Generics { + lifetimes: vec![], + ty_params: P::from_vec(ty_params), + where_clause: ast::WhereClause { + id: ast::DUMMY_NODE_ID, + predicates: vec![] + } + }; // TODO: use aster here. let def = ast::ItemKind::Struct( ast::VariantData::Struct(fields, ast::DUMMY_NODE_ID), - ast::Generics::default() + generics.clone() ); let union_id = rust_type_id(ctx, name); let union_attrs = comp_attrs(&ctx, &ci, name, &mut extra); - extra.push(mk_test_fn(ctx, &name, &layout)); + if ci.args.is_empty() { + extra.push(mk_test_fn(ctx, &name, &layout)); + } let union_def = mk_item(ctx, &union_id, def, ast::Visibility::Public, union_attrs); let union_impl = ast::ItemKind::Impl( ast::Unsafety::Normal, ast::ImplPolarity::Positive, - ast::Generics::default(), + generics, None, P(cty_to_rs(ctx, &union, true, true)), gen_comp_methods(ctx, UNION_DATA_FIELD_NAME, 0, CompKind::Union, &members, &mut extra), @@ -1551,17 +1563,7 @@ fn gen_comp_methods(ctx: &mut GenCtx, data_field: &str, data_offset: usize, let c = rc_c.borrow(); let name = comp_name(&ctx, c.kind, &c.name); extra.extend(comp_to_rs(ctx, &name, c.clone()).into_iter()); - c.layout.size - } - CompMember::CompField(ref rc_c, ref f) => { - if ctx.options.gen_bitfield_methods { - methods.extend(mk_field_method(ctx, f, offset).into_iter()); - } - - let c = rc_c.borrow(); - let name = comp_name(&ctx, c.kind, &c.name); - extra.extend(comp_to_rs(ctx, &name, c.clone()).into_iter()); - f.ty.size() + c.layout().size } CompMember::Enum(_) => 0 }; @@ -1751,7 +1753,7 @@ fn mk_blob_field(ctx: &GenCtx, name: &str, layout: &Layout) -> ast::StructField } } -fn mk_link_name_attr(ctx: &mut GenCtx, name: String) -> ast::Attribute { +fn mk_link_name_attr(ctx: &GenCtx, name: String) -> ast::Attribute { let lit = respan(ctx.span, ast::LitKind::Str(intern(&name).as_str(), ast::StrStyle::Cooked)); let attr_val = P(respan(ctx.span, ast::MetaItemKind::NameValue( InternedString::new("link_name"), lit @@ -1859,7 +1861,7 @@ fn cvar_to_rs(ctx: &mut GenCtx, name: String, } } -fn cfuncty_to_rs(ctx: &mut GenCtx, +fn cfuncty_to_rs(ctx: &GenCtx, rty: &Type, aty: &[(String, Type)], var: bool) -> ast::FnDecl { @@ -1949,7 +1951,7 @@ fn cfunc_to_rs(ctx: &mut GenCtx, } } -fn cty_to_rs(ctx: &mut GenCtx, ty: &Type, allow_bool: bool, use_full_path: bool) -> ast::Ty { +fn cty_to_rs(ctx: &GenCtx, ty: &Type, allow_bool: bool, use_full_path: bool) -> ast::Ty { let prefix = vec!["std".to_owned(), "os".to_owned(), "raw".to_owned()]; let raw = |fragment: &str| { let mut path = prefix.clone(); @@ -2088,7 +2090,7 @@ fn mk_ty_args(ctx: &GenCtx, global: bool, segments: &[String], args: Vec<P<ast:: } } -fn mk_ptrty(ctx: &mut GenCtx, base: &ast::Ty, is_const: bool) -> ast::Ty { +fn mk_ptrty(ctx: &GenCtx, base: &ast::Ty, is_const: bool) -> ast::Ty { let ty = ast::TyKind::Ptr(ast::MutTy { ty: P(base.clone()), mutbl: if is_const { ast::Mutability::Immutable } else { ast::Mutability::Mutable } @@ -2138,7 +2140,7 @@ fn mk_arrty(ctx: &GenCtx, base: &ast::Ty, n: usize) -> ast::Ty { } } -fn mk_fn_proto_ty(ctx: &mut GenCtx, +fn mk_fn_proto_ty(ctx: &GenCtx, decl: &ast::FnDecl, abi: Abi) -> ast::Ty { let fnty = ast::TyKind::BareFn(P(ast::BareFnTy { @@ -2155,7 +2157,7 @@ fn mk_fn_proto_ty(ctx: &mut GenCtx, } } -fn mk_fnty(ctx: &mut GenCtx, decl: &ast::FnDecl, abi: Abi) -> ast::Ty { +fn mk_fnty(ctx: &GenCtx, decl: &ast::FnDecl, abi: Abi) -> ast::Ty { let fnty = ast::TyKind::BareFn(P(ast::BareFnTy { unsafety: ast::Unsafety::Unsafe, abi: abi, diff --git a/src/parser.rs b/src/parser.rs index df6dbf55..49d62b81 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -113,7 +113,7 @@ fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global { let comment = cursor.raw_comment(); let (file, _, _, _) = cursor.location().location(); let ty = cursor.cur_type(); - let layout = Layout::new(ty.size(), ty.align()); + let layout = Layout::from_ty(&ty); let filename = match Path::new(&file.name().unwrap_or("".to_owned())).file_name() { Some(name) => name.to_string_lossy().replace(".", "_"), _ => "".to_string() @@ -124,10 +124,16 @@ fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global { CXCursor_ClassDecl | CXCursor_StructDecl => { let anno = Annotations::new(&cursor); - let kind = if cursor.kind() == CXCursor_UnionDecl { - CompKind::Union - } else { - CompKind::Struct + + let kind = match cursor.kind() { + CXCursor_UnionDecl => CompKind::Union, + CXCursor_ClassTemplate => { + match cursor.template_kind() { + CXCursor_UnionDecl => CompKind::Union, + _ => CompKind::Struct, + } + } + _ => CompKind::Struct, }; let opaque = ctx.options.opaque_types.iter().any(|name| *name == spelling); @@ -161,7 +167,9 @@ fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global { } }; - let mut ci = CompInfo::new(spelling, ctx.current_module_id, filename, comment, kind, vec![], layout, anno); + let mut ci = CompInfo::new(spelling, ctx.current_module_id, + filename, comment, kind, vec![], + layout, anno); ci.parser_cursor = Some(cursor); // If it's an instantiation of another template, @@ -332,8 +340,9 @@ fn mk_fn_sig_resolving_typedefs(ctx: &mut ClangParserCtx, // get parameter names and types. cursor.args().iter().map(|arg| { let arg_name = arg.spelling(); - let is_class_typedef = arg.cur_type().sanitized_spelling_in(typedefs); - (arg_name, conv_ty_resolving_typedefs(ctx, &arg.cur_type(), arg, is_class_typedef)) + let arg_ty = arg.cur_type(); + let is_class_typedef = arg_ty.sanitized_spelling_in(typedefs); + (arg_name, conv_ty_resolving_typedefs(ctx, &arg_ty, arg, is_class_typedef)) }).collect() } _ => { @@ -387,7 +396,7 @@ fn conv_decl_ty_resolving_typedefs(ctx: &mut ClangParserCtx, // If the cursor kind is CXCursor_ClassTemplate, this will still return -1 // and we'll have to keep traversing the cursor. let args = match ty.num_template_args() { - -1 => vec!(), + -1 => vec![], len => { let mut list = Vec::with_capacity(len as usize); for i in 0..len { @@ -442,8 +451,14 @@ fn conv_decl_ty_resolving_typedefs(ctx: &mut ClangParserCtx, TNamed(ti) } CXCursor_NoDeclFound => { - let layout = Layout::new(ty.size(), ty.align()); - TNamed(Rc::new(RefCell::new(TypeInfo::new(ty.spelling().replace("const ", ""), ctx.current_module_id, TVoid, layout)))) + let canonical = ty.canonical_type(); + let kind = canonical.kind(); + if kind != CXType_Invalid && kind != CXType_Unexposed { + conv_ty_resolving_typedefs(ctx, &canonical, &ty_decl, resolve_typedefs) + } else { + let layout = Layout::from_ty(ty); + TNamed(Rc::new(RefCell::new(TypeInfo::new(ty.spelling().replace("const ", ""), ctx.current_module_id, TVoid, layout)))) + } } _ => { let fail = ctx.options.fail_on_unknown_type; @@ -468,7 +483,7 @@ fn conv_ty_resolving_typedefs(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor, resolve_typedefs: bool) -> il::Type { - let layout = Layout::new(ty.size(), ty.align()); + let layout = Layout::from_ty(&ty); // println!("conv_ty: `{}` layout: {:?}, kind {}: {}", cursor.spelling(), layout, ty.kind(), type_to_str(ty.kind())); match ty.kind() { @@ -877,7 +892,7 @@ fn visit_composite(cursor: &Cursor, parent: &Cursor, }); } CXCursor_PackedAttr => { - ci.layout.packed = true; + ci.set_packed(true); } CXCursor_TemplateTypeParameter => { ci.args.push(conv_template_type_parameter(ctx, cursor)); @@ -979,8 +994,8 @@ fn visit_composite(cursor: &Cursor, parent: &Cursor, } } for base in ci.members[..ci.base_members].iter() { - let base = match base { - &CompMember::Field(ref fi) => { + let base = match *base { + CompMember::Field(ref fi) => { match fi.ty { TComp(ref ci) => ci.clone(), _ => continue, diff --git a/src/types.rs b/src/types.rs index 09ce4cb2..30a4f454 100644 --- a/src/types.rs +++ b/src/types.rs @@ -11,7 +11,7 @@ pub use self::Global::*; pub use self::Type::*; pub use self::IKind::*; pub use self::FKind::*; -use clang::Cursor; +use clang::{self, Cursor}; use parser::{Annotations, Accessor}; @@ -167,7 +167,8 @@ pub struct FuncSig { pub abi: abi::Abi, } -#[derive(Clone, PartialEq, Debug)] +// NOTE: Remember to add your new variant to the PartialEq implementation below! +#[derive(Clone, Debug)] pub enum Type { TVoid, TInt(IKind, Layout), @@ -181,6 +182,41 @@ pub enum Type { TEnum(Rc<RefCell<EnumInfo>>) } +/// Compares to Rc<T> types looking first at the value they point to. +/// +/// This is needed to avoid infinite recursion in things like virtual function +/// signatures. +fn ref_ptr_aware_eq<T: PartialEq>(one: &Rc<T>, other: &Rc<T>) -> bool { + &**one as *const T == &**other as *const T || + **one == **other +} + +impl PartialEq for Type { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (&TVoid, &TVoid) + => true, + (&TInt(ref kind, ref l), &TInt(ref o_kind, ref o_l)) + => kind == o_kind && l == o_l, + (&TFloat(ref kind, ref l), &TFloat(ref o_kind, ref o_l)) + => kind == o_kind && l == o_l, + (&TPtr(ref ty, is_const, is_ref, ref l), &TPtr(ref o_ty, o_is_const, o_is_ref, ref o_l)) + => is_const == o_is_const && is_ref == o_is_ref && l == o_l && ty == o_ty, + (&TArray(ref ty, count, ref l), &TArray(ref o_ty, o_count, ref o_l)) + => count == o_count && l == o_l && ty == o_ty, + (&TFuncProto(ref sig), &TFuncProto(ref o_sig)) + => sig == o_sig, + (&TNamed(ref ti), &TNamed(ref o_ti)) + => ref_ptr_aware_eq(ti, o_ti), + (&TComp(ref ci), &TComp(ref o_ci)) + => ref_ptr_aware_eq(ci, o_ci), + (&TEnum(ref ei), &TEnum(ref o_ei)) + => ref_ptr_aware_eq(ei, o_ei), + _ => false, + } + } +} + impl Type { #[allow(dead_code)] pub fn name(&self) -> Option<String> { @@ -226,7 +262,6 @@ impl Type { self.layout().map(|l| l.size).unwrap_or(0) } - #[allow(dead_code)] pub fn align(&self) -> usize { self.layout().map(|l| l.align).unwrap_or(0) } @@ -344,13 +379,22 @@ pub struct Layout { } impl Layout { - pub fn new(size: usize, align: usize) -> Layout { + pub fn new(size: usize, align: usize) -> Self { Layout { size: size, align: align, packed: false } } + // TODO: make this fallible using fallible_size(). + pub fn from_ty(ty: &clang::Type) -> Self { + Self::new(ty.size(), ty.align()) + } + pub fn zero() -> Layout { Layout { size: 0, align: 0, packed: false } } + + pub fn is_zero(&self) -> bool { + *self == Self::zero() + } } #[derive(Debug, Copy, Clone, PartialEq)] @@ -397,8 +441,6 @@ pub enum FKind { pub enum CompMember { Field(FieldInfo), Comp(Rc<RefCell<CompInfo>>), - #[allow(dead_code)] - CompField(Rc<RefCell<CompInfo>>, FieldInfo), Enum(Rc<RefCell<EnumInfo>>), } @@ -431,7 +473,7 @@ pub struct CompInfo { /// the correct layout. pub opaque: bool, pub base_members: usize, - pub layout: Layout, + layout: Layout, /// If this struct is explicitely marked as non-copiable. pub no_copy: bool, /// Typedef'd types names, that we'll resolve early to avoid name conflicts @@ -504,6 +546,47 @@ impl CompInfo { } } + // Gets or computes the layout as appropriately. + pub fn layout(&self) -> Layout { + use std::cmp; + // The returned layout from clang is zero as of right now, but we should + // change it to be fallible to distinguish correctly between zero-sized + // types and unknown layout. + if !self.layout.is_zero() { + return self.layout.clone(); + } + + if self.args.is_empty() { + return self.layout.clone(); + } + + if self.kind == CompKind::Struct { + return self.layout.clone(); + } + + // If we're a union without known layout, we try to compute it from our + // members. This is not ideal, but clang fails to report the size for + // these kind of unions, see test/headers/template_union.hpp + let mut max_size = 0; + let mut max_align = 0; + for member in &self.members { + let layout = match *member { + CompMember::Field(ref f) => f.ty.layout().unwrap_or(Layout::zero()), + CompMember::Comp(ref ci) => ci.borrow().layout(), + CompMember::Enum(ref ei) => ei.borrow().layout.clone(), + }; + + max_size = cmp::max(max_size, layout.size); + max_align = cmp::max(max_align, layout.align); + } + + Layout::new(max_size, max_align) + } + + pub fn set_packed(&mut self, packed: bool) { + self.layout.packed = packed + } + // Return the module id or the class declaration module id. pub fn module_id(&self) -> ModuleId { self.ref_template.as_ref().and_then(|t| if let TComp(ref ci) = *t { @@ -538,8 +621,7 @@ impl CompInfo { let can_derive_debug = self.args.iter().all(|ty| ty.can_derive_debug()) && self.members.iter() .all(|member| match *member { - CompMember::Field(ref f) | - CompMember::CompField(_, ref f) => f.ty.can_derive_debug(), + CompMember::Field(ref f) => f.ty.can_derive_debug(), _ => true, }); self.detect_derive_debug_cycle.set(false); @@ -577,8 +659,7 @@ impl CompInfo { self.ref_template.as_ref().map_or(false, |t| t.has_destructor()) || self.args.iter().any(|t| t.has_destructor()) || self.members.iter().enumerate().any(|(index, m)| match *m { - CompMember::Field(ref f) | - CompMember::CompField(_, ref f) => { + CompMember::Field(ref f) => { // Base members may not be resolved yet if index < self.base_members { f.ty.has_destructor() @@ -611,8 +692,7 @@ impl CompInfo { // since copyability depends on the types itself. self.ref_template.as_ref().map_or(true, |t| t.can_derive_copy()) && self.members.iter().all(|m| match *m { - CompMember::Field(ref f) | - CompMember::CompField(_, ref f) => f.ty.can_derive_copy(), + CompMember::Field(ref f) => f.ty.can_derive_copy(), _ => true, }) } diff --git a/tests/expectations/ref_argument_array.rs b/tests/expectations/ref_argument_array.rs new file mode 100644 index 00000000..9660fbf2 --- /dev/null +++ b/tests/expectations/ref_argument_array.rs @@ -0,0 +1,27 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +pub const NSID_LENGTH: ::std::os::raw::c_uint = 10; +#[repr(C)] +#[derive(Debug, Copy)] +pub struct Struct_nsID { + pub _vftable: *const _vftable_Struct_nsID, +} +#[repr(C)] +pub struct _vftable_Struct_nsID { + pub ToProvidedString: unsafe extern "C" fn(this: + *mut ::std::os::raw::c_void, + aDest: + *mut [::std::os::raw::c_char; 10usize]), +} +impl ::std::clone::Clone for Struct_nsID { + fn clone(&self) -> Self { *self } +} +#[test] +fn bindgen_test_layout_Struct_nsID() { + assert_eq!(::std::mem::size_of::<Struct_nsID>() , 8usize); + assert_eq!(::std::mem::align_of::<Struct_nsID>() , 8usize); +} diff --git a/tests/expectations/union_template.rs b/tests/expectations/union_template.rs new file mode 100644 index 00000000..eb6705f0 --- /dev/null +++ b/tests/expectations/union_template.rs @@ -0,0 +1,86 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[derive(Copy, Debug)] +#[repr(C)] +pub struct __BindgenUnionField<T>(::std::marker::PhantomData<T>); +impl <T> __BindgenUnionField<T> { + #[inline] + pub fn new() -> Self { __BindgenUnionField(::std::marker::PhantomData) } + #[inline] + pub unsafe fn as_ref(&self) -> &T { ::std::mem::transmute(self) } + #[inline] + pub unsafe fn as_mut(&mut self) -> &mut T { ::std::mem::transmute(self) } +} +impl <T> ::std::default::Default for __BindgenUnionField<T> { + #[inline] + fn default() -> Self { Self::new() } +} +impl <T> ::std::clone::Clone for __BindgenUnionField<T> { + #[inline] + fn clone(&self) -> Self { Self::new() } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Struct_NastyStruct<T> { + pub mIsSome: bool, + pub mStorage: Union_NastyStruct_union_template_hpp_unnamed_1<T>, + pub NastyStruct_union_template_hpp_unnamed_2: Union_NastyStruct_union_template_hpp_unnamed_2<T>, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Union_NastyStruct_union_template_hpp_unnamed_1<T> { + pub mFoo: __BindgenUnionField<*mut ::std::os::raw::c_void>, + pub mDummy: __BindgenUnionField<::std::os::raw::c_ulong>, + pub _bindgen_data_: u64, + pub _phantom0: ::std::marker::PhantomData<T>, +} +impl <T> Union_NastyStruct_union_template_hpp_unnamed_1<T> { + pub unsafe fn mFoo(&mut self) -> *mut *mut ::std::os::raw::c_void { + let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_); + ::std::mem::transmute(raw.offset(0)) + } + pub unsafe fn mDummy(&mut self) -> *mut ::std::os::raw::c_ulong { + let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_); + ::std::mem::transmute(raw.offset(0)) + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Union_NastyStruct_union_template_hpp_unnamed_2<T> { + pub wat: __BindgenUnionField<::std::os::raw::c_short>, + pub wut: __BindgenUnionField<*mut ::std::os::raw::c_int>, + pub _bindgen_data_: u64, + pub _phantom0: ::std::marker::PhantomData<T>, +} +impl <T> Union_NastyStruct_union_template_hpp_unnamed_2<T> { + pub unsafe fn wat(&mut self) -> *mut ::std::os::raw::c_short { + let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_); + ::std::mem::transmute(raw.offset(0)) + } + pub unsafe fn wut(&mut self) -> *mut *mut ::std::os::raw::c_int { + let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_); + ::std::mem::transmute(raw.offset(0)) + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Union_Whatever<T> { + pub mTPtr: __BindgenUnionField<*mut ::std::os::raw::c_void>, + pub mInt: __BindgenUnionField<::std::os::raw::c_int>, + pub _bindgen_data_: u64, + pub _phantom0: ::std::marker::PhantomData<T>, +} +impl <T> Union_Whatever<T> { + pub unsafe fn mTPtr(&mut self) -> *mut *mut ::std::os::raw::c_void { + let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_); + ::std::mem::transmute(raw.offset(0)) + } + pub unsafe fn mInt(&mut self) -> *mut ::std::os::raw::c_int { + let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_); + ::std::mem::transmute(raw.offset(0)) + } +} diff --git a/tests/expectations/vtable_recursive_sig.rs b/tests/expectations/vtable_recursive_sig.rs new file mode 100644 index 00000000..8f972e92 --- /dev/null +++ b/tests/expectations/vtable_recursive_sig.rs @@ -0,0 +1,36 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Copy)] +pub struct Base { + pub _vftable: *const _vftable_Base, +} +#[repr(C)] +pub struct _vftable_Base { + pub AsDerived: unsafe extern "C" fn(this: *mut ::std::os::raw::c_void) + -> *mut Derived, +} +impl ::std::clone::Clone for Base { + fn clone(&self) -> Self { *self } +} +#[test] +fn bindgen_test_layout_Base() { + assert_eq!(::std::mem::size_of::<Base>() , 8usize); + assert_eq!(::std::mem::align_of::<Base>() , 8usize); +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct Derived { + pub _base: Base, +} +#[repr(C)] +pub struct _vftable_Derived { + pub _base: _vftable_Base, +} +impl ::std::clone::Clone for Derived { + fn clone(&self) -> Self { *self } +} diff --git a/tests/headers/ref_argument_array.hpp b/tests/headers/ref_argument_array.hpp new file mode 100644 index 00000000..dc73fd62 --- /dev/null +++ b/tests/headers/ref_argument_array.hpp @@ -0,0 +1,6 @@ + +#define NSID_LENGTH 10 +class nsID { +public: + virtual void ToProvidedString(char (&aDest)[NSID_LENGTH]) = 0; +}; diff --git a/tests/headers/union_template.hpp b/tests/headers/union_template.hpp new file mode 100644 index 00000000..0d0a9bb3 --- /dev/null +++ b/tests/headers/union_template.hpp @@ -0,0 +1,19 @@ +template<typename T> +struct NastyStruct { + bool mIsSome; + union { + void* mFoo; + unsigned long mDummy; + } mStorage; + + union { + short wat; + int* wut; + }; +}; + +template<typename T> +union Whatever { + void* mTPtr; + int mInt; +}; diff --git a/tests/headers/vtable_recursive_sig.hpp b/tests/headers/vtable_recursive_sig.hpp new file mode 100644 index 00000000..4e5af806 --- /dev/null +++ b/tests/headers/vtable_recursive_sig.hpp @@ -0,0 +1,11 @@ +// bindgen-flags: -std=c++11 -no-type-renaming + +class Derived; +class Base { +public: + virtual Derived* AsDerived() { return nullptr; } +}; + +class Derived final : public Base { + virtual Derived* AsDerived() override { return this; } +}; |