diff --git a/src/clang.rs b/src/clang.rs index 385fd09a2a..7da755eaba 100755 --- a/src/clang.rs +++ b/src/clang.rs @@ -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 { @@ -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. diff --git a/src/ir/context.rs b/src/ir/context.rs index c7949befd7..1b446ace16 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -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 @@ -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 - // class Incomplete { - // T d; - // U p; - // }; - // - // template - // class Foo { - // Incomplete 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 + // class Incomplete { + // T d; + // U p; + // }; + // + // template + // class Foo { + // Incomplete 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); @@ -586,7 +589,9 @@ impl<'ctx> BindgenContext<'ctx> { location: Option) -> Option { use clangll::{CXCursor_ClassTemplate, - CXCursor_ClassTemplatePartialSpecialization}; + CXCursor_ClassTemplatePartialSpecialization, + CXCursor_TypeAliasTemplateDecl, + CXCursor_TypeRef}; debug!("builtin_or_resolved_ty: {:?}, {:?}, {:?}", ty, location, @@ -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) && *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)); diff --git a/tests/expectations/tests/type_alias_partial_template_especialization.rs b/tests/expectations/tests/type_alias_partial_template_especialization.rs new file mode 100644 index 0000000000..70b5f66c50 --- /dev/null +++ b/tests/expectations/tests/type_alias_partial_template_especialization.rs @@ -0,0 +1,12 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +pub type MaybeWrapped = A; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Rooted { + pub ptr: MaybeWrapped, +} diff --git a/tests/expectations/tests/type_alias_template_specialized.rs b/tests/expectations/tests/type_alias_template_specialized.rs new file mode 100644 index 0000000000..989f2015b0 --- /dev/null +++ b/tests/expectations/tests/type_alias_template_specialized.rs @@ -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::() , 4usize); + assert_eq!(::std::mem::align_of::() , 4usize); +} +impl Clone for Rooted { + fn clone(&self) -> Self { *self } +} diff --git a/tests/headers/type_alias_partial_template_especialization.hpp b/tests/headers/type_alias_partial_template_especialization.hpp new file mode 100644 index 0000000000..dfc36786c4 --- /dev/null +++ b/tests/headers/type_alias_partial_template_especialization.hpp @@ -0,0 +1,7 @@ +// bindgen-flags: -- -std=c++14 +template using MaybeWrapped = A; + +template +class Rooted { + MaybeWrapped ptr; +}; diff --git a/tests/headers/type_alias_template_specialized.hpp b/tests/headers/type_alias_template_specialized.hpp new file mode 100644 index 0000000000..a2d32b56a1 --- /dev/null +++ b/tests/headers/type_alias_template_specialized.hpp @@ -0,0 +1,9 @@ +// bindgen-flags: --whitelist-type Rooted -- -std=c++14 + +template using MaybeWrapped = a; +class Rooted { + MaybeWrapped ptr; +}; + +///
+template using replaces_MaybeWrapped = a;