Skip to content

Commit

Permalink
Generate opaque blobs for uses of partially specialized templates
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
fitzgen committed Feb 27, 2017
1 parent 187df63 commit d702d31
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 11 deletions.
24 changes: 19 additions & 5 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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();

Expand All @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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 = */
Expand Down
21 changes: 19 additions & 2 deletions src/ir/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
11 changes: 10 additions & 1 deletion src/ir/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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> {
Expand Down
23 changes: 20 additions & 3 deletions src/ir/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -427,6 +427,7 @@ impl Type {
TypeKind::Named |
TypeKind::Array(..) |
TypeKind::Comp(..) |
TypeKind::Opaque |
TypeKind::Int(..) |
TypeKind::Float(..) |
TypeKind::Complex(..) |
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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(..) |
Expand Down Expand Up @@ -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,
}
}
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -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 |
Expand Down
44 changes: 44 additions & 0 deletions tests/expectations/tests/partial-specialization-and-inheritance.rs
Original file line number Diff line number Diff line change
@@ -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::<Usage>() , 1usize , concat ! (
"Size of: " , stringify ! ( Usage ) ));
assert_eq! (::std::mem::align_of::<Usage>() , 1usize , concat ! (
"Alignment of " , stringify ! ( Usage ) ));
}
impl Clone for Usage {
fn clone(&self) -> Self { *self }
}
38 changes: 38 additions & 0 deletions tests/headers/partial-specialization-and-inheritance.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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 <typename T>
class Base {
public:
void some_method(T, T);
};

// A template with a default representation.
template <typename U>
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 <typename U>
class Derived<U*> : public Base<U*> {
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<Usage*> static_member;
};

0 comments on commit d702d31

Please sign in to comment.