From 9b8f6e3da22416d6de0f929906496223e70a150f Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Mon, 27 Feb 2017 09:52:29 -0800 Subject: [PATCH] Generate opaque blobs for uses of partially specialized templates This adds `TypeKind::Opaque` which signifies that we do not understand anything about the given type and that we should just generate an opaque blob based on the type's layout. It explicitly uses the opaque type kind for partially specialized templates. --- src/codegen/mod.rs | 24 +++++++--- src/ir/context.rs | 21 ++++++++- src/ir/layout.rs | 11 ++++- src/ir/ty.rs | 23 ++++++++-- .../partial-specialization-and-inheritance.rs | 44 +++++++++++++++++++ ...partial-specialization-and-inheritance.hpp | 40 +++++++++++++++++ 6 files changed, 152 insertions(+), 11 deletions(-) create mode 100644 tests/expectations/tests/partial-specialization-and-inheritance.rs create mode 100644 tests/headers/partial-specialization-and-inheritance.hpp diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 1802934376..1411f80134 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -520,6 +520,7 @@ impl CodeGenerator for Type { TypeKind::Reference(..) | TypeKind::Function(..) | TypeKind::ResolvedTypeRef(..) | + TypeKind::Opaque | TypeKind::Named => { // These items don't need code generation, they only need to be // converted to rust types in fields, arguments, and such. @@ -2170,7 +2171,6 @@ impl ToRustTy for Type { aster::AstBuilder::new().ty().path().ids(path).build() } TypeKind::TemplateInstantiation(ref inst) => { - // PS: Sorry for the duplication here. let decl = inst.template_definition(); let mut ty = decl.to_rust_ty(ctx).unwrap(); @@ -2183,6 +2183,21 @@ impl ToRustTy for Type { } } + let decl_params = if let Some(params) = decl.self_template_params(ctx) { + params + } else { + // This can happen if we generated an opaque type for a + // partial template specialization, in which case we just + // use the opaque type's layout. If we don't have a layout, + // we cross our fingers and hope for the best :-/ + debug_assert_eq!(*ctx.resolve_type_through_type_refs(decl).kind(), + TypeKind::Opaque); + let layout = self.layout(ctx).unwrap_or(Layout::zero()); + ty = BlobTyBuilder::new(layout).build().unwrap(); + + vec![] + }; + // TODO: If the decl type is a template class/struct // declaration's member template declaration, it could rely on // generic template parameters from its outer template @@ -2191,10 +2206,6 @@ impl ToRustTy for Type { // reconstruct them somehow. We don't have any means of doing // that reconstruction at this time. - let decl_params = decl.self_template_params(ctx) - .expect("instantiation's referenced template declaration \ - should be a template declaration"); - if let ast::TyKind::Path(_, ref mut path) = ty.node { let template_args = inst.template_arguments() .iter() @@ -2262,6 +2273,9 @@ impl ToRustTy for Type { utils::build_templated_path(item, ctx, template_params.unwrap_or(vec![])) } + TypeKind::Opaque => { + BlobTyBuilder::new(self.layout(ctx).unwrap_or(Layout::zero())).build() + } TypeKind::BlockPointer => { let void = raw_type(ctx, "c_void"); void.to_ptr(/* is_const = */ diff --git a/src/ir/context.rs b/src/ir/context.rs index 9b12a60dab..ab2130458b 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -250,8 +250,10 @@ impl<'ctx> BindgenContext<'ctx> { item, declaration, location); - debug_assert!(declaration.is_some() || !item.kind().is_type() || - item.kind().expect_type().is_builtin_or_named(), + debug_assert!(declaration.is_some() || + !item.kind().is_type() || + item.kind().expect_type().is_builtin_or_named() || + *item.kind().expect_type().kind() == TypeKind::Opaque, "Adding a type without declaration?"); let id = item.id(); @@ -692,6 +694,21 @@ impl<'ctx> BindgenContext<'ctx> { } } + /// Resolve the given `ItemId` into a `Type`, and keep doing so while we see + /// `ResolvedTypeRef`s to other items until we get to the final `Type`. + pub fn resolve_type_through_type_refs(&self, item_id: ItemId) -> &Type { + assert!(self.collected_typerefs()); + + let mut id = item_id; + loop { + let ty = self.resolve_type(id); + match *ty.kind() { + TypeKind::ResolvedTypeRef(next_id) => id = next_id, + _ => return ty, + } + } + } + /// Get the current module. pub fn current_module(&self) -> ItemId { self.current_module diff --git a/src/ir/layout.rs b/src/ir/layout.rs index f21a501caf..2c387792f1 100644 --- a/src/ir/layout.rs +++ b/src/ir/layout.rs @@ -2,7 +2,8 @@ use super::context::BindgenContext; use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault}; -use super::ty::RUST_DERIVE_IN_ARRAY_LIMIT; +use super::ty::{Type, TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT}; +use clang; use std::{cmp, mem}; /// A type that represents the struct layout of a type. @@ -65,9 +66,17 @@ impl Layout { } /// When we are treating a type as opaque, it is just a blob with a `Layout`. +#[derive(Clone, Debug, PartialEq)] pub struct Opaque(pub Layout); impl Opaque { + /// Construct a new opaque type from the given clang type. + pub fn from_clang_ty(ty: &clang::Type) -> Type { + let layout = Layout::new(ty.size(), ty.align()); + let ty_kind = TypeKind::Opaque; + Type::new(None, Some(layout), ty_kind, false) + } + /// Return the known rust type we should use to create a correctly-aligned /// field with this layout. pub fn known_rust_type_for_array(&self) -> Option<&'static str> { diff --git a/src/ir/ty.rs b/src/ir/ty.rs index caa6e243ce..f0ae5329e3 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -8,7 +8,7 @@ use super::enum_ty::Enum; use super::function::FunctionSig; use super::int::IntKind; use super::item::{Item, ItemAncestors}; -use super::layout::Layout; +use super::layout::{Layout, Opaque}; use super::objc::ObjCInterface; use super::template::{AsNamed, TemplateInstantiation}; use super::traversal::{EdgeKind, Trace, Tracer}; @@ -427,6 +427,7 @@ impl Type { TypeKind::Named | TypeKind::Array(..) | TypeKind::Comp(..) | + TypeKind::Opaque | TypeKind::Int(..) | TypeKind::Float(..) | TypeKind::Complex(..) | @@ -522,6 +523,7 @@ impl DotAttributes for TypeKind { TypeKind::Void => "Void", TypeKind::NullPtr => "NullPtr", TypeKind::Comp(..) => "Comp", + TypeKind::Opaque => "Opaque", TypeKind::Int(..) => "Int", TypeKind::Float(..) => "Float", TypeKind::Complex(..) => "Complex", @@ -606,6 +608,7 @@ impl TemplateDeclaration for TypeKind { TypeKind::Comp(ref comp) => comp.self_template_params(ctx), TypeKind::TemplateAlias(_, ref args) => Some(args.clone()), + TypeKind::Opaque | TypeKind::TemplateInstantiation(..) | TypeKind::Void | TypeKind::NullPtr | @@ -665,6 +668,9 @@ impl CanDeriveDefault for Type { TypeKind::Comp(ref info) => { info.can_derive_default(ctx, self.layout(ctx)) } + TypeKind::Opaque => { + self.layout.map_or(true, |l| l.opaque().can_derive_default(ctx, ())) + } TypeKind::Void | TypeKind::Named | TypeKind::TemplateInstantiation(..) | @@ -703,6 +709,9 @@ impl<'a> CanDeriveCopy<'a> for Type { TypeKind::Comp(ref info) => { info.can_derive_copy(ctx, (item, self.layout(ctx))) } + TypeKind::Opaque => { + self.layout.map_or(true, |l| l.opaque().can_derive_copy(ctx, ())) + } _ => true, } } @@ -758,6 +767,11 @@ pub enum TypeKind { /// A compound type, that is, a class, struct, or union. Comp(CompInfo), + /// An opaque type that we just don't understand. All usage of this shoulf + /// result in an opaque blob of bytes generated from the containing type's + /// layout. + Opaque, + /// An integer type, of a given kind. `bool` and `char` are also considered /// integers. Int(IntKind), @@ -840,6 +854,7 @@ impl Type { match self.kind { TypeKind::Void => true, TypeKind::Comp(ref ci) => ci.is_unsized(ctx), + TypeKind::Opaque => self.layout.map_or(true, |l| l.size == 0), TypeKind::Array(inner, size) => { size == 0 || ctx.resolve_type(inner).is_unsized(ctx) } @@ -919,8 +934,9 @@ impl Type { if location.kind() == CXCursor_ClassTemplatePartialSpecialization { // Sorry! (Not sorry) warn!("Found a partial template specialization; bindgen does not \ - support partial template specialization"); - return Err(ParseError::Continue); + support partial template specialization! Constructing \ + opaque type instead."); + return Ok(ParseResult::New(Opaque::from_clang_ty(&canonical_ty), None)); } let kind = if location.kind() == CXCursor_TemplateRef || @@ -1337,6 +1353,7 @@ impl Trace for Type { } // None of these variants have edges to other items and types. + TypeKind::Opaque | TypeKind::UnresolvedTypeRef(_, _, None) | TypeKind::Named | TypeKind::Void | diff --git a/tests/expectations/tests/partial-specialization-and-inheritance.rs b/tests/expectations/tests/partial-specialization-and-inheritance.rs new file mode 100644 index 0000000000..2422593465 --- /dev/null +++ b/tests/expectations/tests/partial-specialization-and-inheritance.rs @@ -0,0 +1,44 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct Base { + pub _address: u8, +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct Derived { + pub b: bool, +} +#[test] +fn __bindgen_test_layout__bindgen_ty_id_20_instantiation_14() { + assert_eq!(::std::mem::size_of::<[u32; 2usize]>() , 8usize , concat ! ( + "Size of template specialization: " , stringify ! ( + [u32; 2usize] ) )); + assert_eq!(::std::mem::align_of::<[u32; 2usize]>() , 4usize , concat ! ( + "Alignment of template specialization: " , stringify ! ( + [u32; 2usize] ) )); +} +#[repr(C)] +#[derive(Debug, Default, Copy)] +pub struct Usage { + pub _address: u8, +} +extern "C" { + #[link_name = "_ZN5Usage13static_memberE"] + pub static mut Usage_static_member: [u32; 2usize]; +} +#[test] +fn bindgen_test_layout_Usage() { + assert_eq!(::std::mem::size_of::() , 1usize , concat ! ( + "Size of: " , stringify ! ( Usage ) )); + assert_eq! (::std::mem::align_of::() , 1usize , concat ! ( + "Alignment of " , stringify ! ( Usage ) )); +} +impl Clone for Usage { + fn clone(&self) -> Self { *self } +} diff --git a/tests/headers/partial-specialization-and-inheritance.hpp b/tests/headers/partial-specialization-and-inheritance.hpp new file mode 100644 index 0000000000..4eb8f54583 --- /dev/null +++ b/tests/headers/partial-specialization-and-inheritance.hpp @@ -0,0 +1,40 @@ +// bindgen-unstable + +// This was originally a test case generated by creducing errors in SpiderMonkey +// bindings generation. I've tried to make it understandable by giving more +// meaningful names to everything, and a couple comments. +// +// We don't support partial template specialization, but we *should* +// successfully parse this header, and generate bindings for it, but the usage +// of the partial template specialization should result in opaque blobs. + +// A base class providing a method. +template +class Base { +public: + void some_method(T, T); +}; + +// A template with a default representation. +template +class Derived { + bool b; +}; + +// A partial specialization for pointers. Note that this should have a different +// and larger layout than the template it is specializing. +template +class Derived : public Base { + int x; + int y; +}; + +// A struct that uses the partial specialization and method from the partial +// specialization's base class. +struct Usage { + Usage() { + static_member.some_method(this, this); + } + + static Derived static_member; +};