Skip to content

Template alias full and partial specialization improvements. #255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/clang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,24 @@ impl Cursor {
}
}

/// Returns whether the given location contains a cursor with the given
/// kind in the first level of nesting underneath (doesn't look
/// recursively).
pub fn contains_cursor(&self, kind: Enum_CXCursorKind) -> bool {
let mut found = false;

self.visit(|c| {
if c.kind() == kind {
found = true;
CXChildVisit_Break
} else {
CXChildVisit_Continue
}
});

found
}

/// Is the referent an inlined function?
#[cfg(not(feature="llvm_stable"))]
pub fn is_inlined_function(&self) -> bool {
Expand Down Expand Up @@ -721,6 +739,11 @@ impl Type {
pub fn is_valid(&self) -> bool {
self.kind() != CXType_Invalid
}

/// Is this a valid and exposed type?
pub fn is_valid_and_exposed(&self) -> bool {
self.is_valid() && self.kind() != CXType_Unexposed
}
}

/// An iterator for a type's template arguments.
Expand Down
107 changes: 65 additions & 42 deletions src/ir/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,16 +499,12 @@ impl<'ctx> BindgenContext<'ctx> {
wrapping: ItemId,
parent_id: ItemId,
ty: &clang::Type,
location: clang::Cursor)
location: clang::Cursor,
declaration: clang::Cursor)
-> ItemId {
use clangll::*;
let mut args = vec![];
let mut found_invalid_template_ref = false;
location.visit(|c| {
if c.kind() == CXCursor_TemplateRef &&
c.cur_type().kind() == CXType_Invalid {
found_invalid_template_ref = true;
}
if c.kind() == CXCursor_TypeRef {
// The `with_id` id will potentially end up unused if we give up
// on this type (for example, its a tricky partial template
Expand All @@ -527,39 +523,46 @@ impl<'ctx> BindgenContext<'ctx> {

let item = {
let wrapping_type = self.resolve_type(wrapping);
let old_args = match *wrapping_type.kind() {
TypeKind::Comp(ref ci) => ci.template_args(),
_ => panic!("how?"),
};
// The following assertion actually fails with partial template
// specialization. But as far as I know there's no way at all to
// grab the specialized types from neither the AST or libclang.
//
// This flaw was already on the old parser, but I now think it has
// no clear solution.
//
// For an easy example in which there's no way at all of getting the
// `int` type, except manually parsing the spelling:
//
// template<typename T, typename U>
// class Incomplete {
// T d;
// U p;
// };
//
// template<typename U>
// class Foo {
// Incomplete<U, int> bar;
// };
//
// debug_assert_eq!(old_args.len(), args.len());
//
// That being said, this is not so common, so just error! and hope
// for the best, returning the previous type, who knows.
if old_args.len() != args.len() {
error!("Found partial template specialization, \
expect dragons!");
return wrapping;
if let TypeKind::Comp(ref ci) = *wrapping_type.kind() {
let old_args = ci.template_args();

// The following assertion actually fails with partial template
// specialization. But as far as I know there's no way at all to
// grab the specialized types from neither the AST or libclang,
// which sucks. The same happens for specialized type alias
// template declarations, where we have that ugly hack up there.
//
// This flaw was already on the old parser, but I now think it
// has no clear solution (apart from patching libclang to
// somehow expose them, of course).
//
// For an easy example in which there's no way at all of getting
// the `int` type, except manually parsing the spelling:
//
// template<typename T, typename U>
// class Incomplete {
// T d;
// U p;
// };
//
// template<typename U>
// class Foo {
// Incomplete<U, int> bar;
// };
//
// debug_assert_eq!(old_args.len(), args.len());
//
// That being said, this is not so common, so just error! and
// hope for the best, returning the previous type, who knows.
if old_args.len() != args.len() {
error!("Found partial template specialization, \
expect dragons!");
return wrapping;
}
} else {
assert_eq!(declaration.kind(),
::clangll::CXCursor_TypeAliasTemplateDecl,
"Expected wrappable type");
}

let type_kind = TypeKind::TemplateRef(wrapping, args);
Expand All @@ -586,7 +589,9 @@ impl<'ctx> BindgenContext<'ctx> {
location: Option<clang::Cursor>)
-> Option<ItemId> {
use clangll::{CXCursor_ClassTemplate,
CXCursor_ClassTemplatePartialSpecialization};
CXCursor_ClassTemplatePartialSpecialization,
CXCursor_TypeAliasTemplateDecl,
CXCursor_TypeRef};
debug!("builtin_or_resolved_ty: {:?}, {:?}, {:?}",
ty,
location,
Expand Down Expand Up @@ -630,15 +635,33 @@ impl<'ctx> BindgenContext<'ctx> {
// argument names don't matter in the global context.
if (declaration.kind() == CXCursor_ClassTemplate ||
declaration.kind() ==
CXCursor_ClassTemplatePartialSpecialization) &&
CXCursor_ClassTemplatePartialSpecialization ||
declaration.kind() == CXCursor_TypeAliasTemplateDecl) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems we would benefit from a Cursor::is_template() method here, that we would use like declaration.is_template() and replace all these kind comparisons.

As it is right now, this condition is super intense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I agree, but we special-case ClassTemplate/ClassTemplatePartialSpecialization in a few places, but TypeAliasTemplateDecl only here. I'll try to organize it better.

*ty != canonical_declaration.cur_type() &&
location.is_some() &&
parent_id.is_some() {
// For specialized type aliases, there's no way to get the
// template parameters as of this writing (for a struct
// specialization we wouldn't be in this branch anyway).
//
// Explicitly return `None` if there aren't any
// unspecialized parameters (contains any `TypeRef`) so we
// resolve the canonical type if there is one and it's
// exposed.
//
// This is _tricky_, I know :(
if declaration.kind() == CXCursor_TypeAliasTemplateDecl &&
!location.unwrap().contains_cursor(CXCursor_TypeRef) &&
ty.canonical_type().is_valid_and_exposed() {
return None;
}

return Some(self.build_template_wrapper(with_id,
id,
parent_id.unwrap(),
ty,
location.unwrap()));
location.unwrap(),
declaration));
}

return Some(self.build_ty_wrapper(with_id, id, parent_id, ty));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* automatically generated by rust-bindgen */


#![allow(non_snake_case)]


pub type MaybeWrapped<A> = A;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Rooted<T> {
pub ptr: MaybeWrapped<T>,
}
19 changes: 19 additions & 0 deletions tests/expectations/tests/type_alias_template_specialized.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* automatically generated by rust-bindgen */


#![allow(non_snake_case)]


#[repr(C)]
#[derive(Debug, Copy)]
pub struct Rooted {
pub ptr: ::std::os::raw::c_int,
}
#[test]
fn bindgen_test_layout_Rooted() {
assert_eq!(::std::mem::size_of::<Rooted>() , 4usize);
assert_eq!(::std::mem::align_of::<Rooted>() , 4usize);
}
impl Clone for Rooted {
fn clone(&self) -> Self { *self }
}
7 changes: 7 additions & 0 deletions tests/headers/type_alias_partial_template_especialization.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// bindgen-flags: -- -std=c++14
template <typename A> using MaybeWrapped = A;

template<class T>
class Rooted {
MaybeWrapped<T> ptr;
};
9 changes: 9 additions & 0 deletions tests/headers/type_alias_template_specialized.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// bindgen-flags: --whitelist-type Rooted -- -std=c++14

template <typename a> using MaybeWrapped = a;
class Rooted {
MaybeWrapped<int> ptr;
};

/// <div rustbindgen replaces="MaybeWrapped"></div>
template <typename a> using replaces_MaybeWrapped = a;