diff --git a/libbindgen/src/codegen/mod.rs b/libbindgen/src/codegen/mod.rs index 24b862bec5..c6d5fd39f8 100644 --- a/libbindgen/src/codegen/mod.rs +++ b/libbindgen/src/codegen/mod.rs @@ -4,7 +4,7 @@ mod helpers; use aster; use ir::annotations::FieldAccessorKind; -use ir::comp::{CompInfo, CompKind, Field, Method, MethodKind}; +use ir::comp::{Base, CompInfo, CompKind, Field, Method, MethodKind}; use ir::context::{BindgenContext, ItemId}; use ir::derive::{CanDeriveCopy, CanDeriveDebug}; use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; @@ -556,13 +556,13 @@ struct Vtable<'a> { #[allow(dead_code)] methods: &'a [Method], #[allow(dead_code)] - base_classes: &'a [ItemId], + base_classes: &'a [Base], } impl<'a> Vtable<'a> { fn new(item_id: ItemId, methods: &'a [Method], - base_classes: &'a [ItemId]) + base_classes: &'a [Base]) -> Self { Vtable { item_id: item_id, @@ -835,14 +835,18 @@ impl CodeGenerator for CompInfo { } for (i, base) in self.base_members().iter().enumerate() { - let base_ty = ctx.resolve_type(*base); + // Virtual bases are already taken into account by the vtable + // pointer. + // + // FIXME(emilio): Is this always right? + if base.is_virtual() { + continue; + } + + let base_ty = ctx.resolve_type(base.ty); // NB: We won't include unsized types in our base chain because they // would contribute to our size given the dummy field we insert for // unsized types. - // - // NB: Canonical type is here because it could be inheriting from a - // typedef, for example, and the lack of `unwrap()` is because we - // can inherit from a template parameter, yes. if base_ty.is_unsized(ctx) { continue; } @@ -854,7 +858,7 @@ impl CodeGenerator for CompInfo { } } - let inner = base.to_rust_ty(ctx); + let inner = base.ty.to_rust_ty(ctx); let field_name = if i == 0 { "_base".into() } else { diff --git a/libbindgen/src/ir/comp.rs b/libbindgen/src/ir/comp.rs index 0af8a99e8e..70dfd4a640 100644 --- a/libbindgen/src/ir/comp.rs +++ b/libbindgen/src/ir/comp.rs @@ -174,6 +174,40 @@ impl<'a> CanDeriveCopy<'a> for Field { } } + +/// The kind of inheritance a base class is using. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum BaseKind { + /// Normal inheritance, like: + /// + /// ```cpp + /// class A : public B {}; + /// ``` + Normal, + /// Virtual inheritance, like: + /// + /// ```cpp + /// class A: public virtual B {}; + /// ``` + Virtual, +} + +/// A base class. +#[derive(Clone, Debug)] +pub struct Base { + /// The type of this base class. + pub ty: ItemId, + /// The kind of inheritance we're doing. + pub kind: BaseKind, +} + +impl Base { + /// Whether this base class is inheriting virtually. + pub fn is_virtual(&self) -> bool { + self.kind == BaseKind::Virtual + } +} + /// A compound type. /// /// Either a struct or union, a compound type is built up from the combination @@ -199,7 +233,7 @@ pub struct CompInfo { constructors: Vec, /// Vector of classes this one inherits from. - base_members: Vec, + base_members: Vec, /// The parent reference template if any. ref_template: Option, @@ -287,7 +321,7 @@ impl CompInfo { pub fn is_unsized(&self, ctx: &BindgenContext) -> bool { !self.has_vtable(ctx) && self.fields.is_empty() && self.base_members.iter().all(|base| { - ctx.resolve_type(*base).canonical_type(ctx).is_unsized(ctx) + ctx.resolve_type(base.ty).canonical_type(ctx).is_unsized(ctx) }) && self.ref_template .map_or(true, |template| ctx.resolve_type(template).is_unsized(ctx)) @@ -314,12 +348,12 @@ impl CompInfo { self.ref_template.as_ref().map_or(false, |t| { ctx.resolve_type(*t).has_destructor(ctx) }) || - self.template_args - .iter() - .any(|t| ctx.resolve_type(*t).has_destructor(ctx)) || - self.base_members - .iter() - .any(|t| ctx.resolve_type(*t).has_destructor(ctx)) || + self.template_args.iter().any(|t| { + ctx.resolve_type(*t).has_destructor(ctx) + }) || + self.base_members.iter().any(|base| { + ctx.resolve_type(base.ty).has_destructor(ctx) + }) || self.fields.iter().any(|field| { ctx.resolve_type(field.ty) .has_destructor(ctx) @@ -394,7 +428,7 @@ impl CompInfo { pub fn has_vtable(&self, ctx: &BindgenContext) -> bool { self.has_vtable || self.base_members().iter().any(|base| { - ctx.resolve_type(*base) + ctx.resolve_type(base.ty) .has_vtable(ctx) }) || self.ref_template.map_or(false, |template| { @@ -418,7 +452,7 @@ impl CompInfo { } /// The set of types that this one inherits from. - pub fn base_members(&self) -> &[ItemId] { + pub fn base_members(&self) -> &[Base] { &self.base_members } @@ -590,14 +624,23 @@ impl CompInfo { ci.template_args.push(param); } CXCursor_CXXBaseSpecifier => { - if !ci.has_vtable { - ci.has_vtable = cur.is_virtual_base(); - } + let is_virtual_base = cur.is_virtual_base(); + ci.has_vtable |= is_virtual_base; + + let kind = if is_virtual_base { + BaseKind::Virtual + } else { + BaseKind::Normal + }; + let type_id = Item::from_ty_or_ref(cur.cur_type(), Some(cur), None, ctx); - ci.base_members.push(type_id); + ci.base_members.push(Base { + ty: type_id, + kind: kind, + }); } CXCursor_Constructor | CXCursor_Destructor | @@ -794,7 +837,7 @@ impl CompInfo { // Unfortunately, given the way we implement --match-pat, and also // that you can inherit from templated types, we need to handle // other cases here too. - ctx.resolve_type(*base) + ctx.resolve_type(base.ty) .canonical_type(ctx) .as_comp() .map_or(false, |ci| ci.has_vtable(ctx)) @@ -835,7 +878,7 @@ impl CanDeriveDebug for CompInfo { let can_derive_debug = { self.base_members .iter() - .all(|id| id.can_derive_debug(ctx, ())) && + .all(|base| base.ty.can_derive_debug(ctx, ())) && self.template_args .iter() .all(|id| id.can_derive_debug(ctx, ())) && @@ -888,7 +931,7 @@ impl<'a> CanDeriveCopy<'a> for CompInfo { .map_or(true, |t| t.can_derive_copy(ctx, ())) && self.base_members .iter() - .all(|t| t.can_derive_copy(ctx, ())) && + .all(|base| base.ty.can_derive_copy(ctx, ())) && self.fields.iter().all(|field| field.can_derive_copy(ctx, ())) } @@ -916,8 +959,8 @@ impl TypeCollector for CompInfo { types.insert(arg); } - for &base in self.base_members() { - types.insert(base); + for base in self.base_members() { + types.insert(base.ty); } for field in self.fields() { diff --git a/libbindgen/tests/expectations/tests/virtual_inheritance.rs b/libbindgen/tests/expectations/tests/virtual_inheritance.rs new file mode 100644 index 0000000000..f271223f94 --- /dev/null +++ b/libbindgen/tests/expectations/tests/virtual_inheritance.rs @@ -0,0 +1,68 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Copy)] +pub struct A { + pub foo: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_A() { + assert_eq!(::std::mem::size_of::() , 4usize); + assert_eq!(::std::mem::align_of::() , 4usize); +} +impl Clone for A { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +pub struct B__bindgen_vtable { +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct B { + pub vtable_: *const B__bindgen_vtable, + pub bar: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_B() { + assert_eq!(::std::mem::size_of::() , 16usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for B { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +pub struct C__bindgen_vtable { +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct C { + pub vtable_: *const C__bindgen_vtable, + pub baz: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_C() { + assert_eq!(::std::mem::size_of::() , 16usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for C { + fn clone(&self) -> Self { *self } +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct D { + pub _base: C, + pub _base_1: B, + pub bazz: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_D() { + assert_eq!(::std::mem::size_of::() , 40usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for D { + fn clone(&self) -> Self { *self } +} diff --git a/libbindgen/tests/headers/virtual_inheritance.hpp b/libbindgen/tests/headers/virtual_inheritance.hpp new file mode 100644 index 0000000000..5198c51e27 --- /dev/null +++ b/libbindgen/tests/headers/virtual_inheritance.hpp @@ -0,0 +1,16 @@ + +class A { + int foo; +}; + +class B: public virtual A { + int bar; +}; + +class C: public virtual A { + int baz; +}; + +class D: public C, public B { + int bazz; +};