summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/clang.rs122
-rw-r--r--src/codegen/helpers.rs4
-rw-r--r--src/codegen/mod.rs15
-rw-r--r--src/ir/comp.rs18
-rw-r--r--src/ir/context.rs491
-rw-r--r--src/ir/derive.rs2
-rw-r--r--src/ir/function.rs5
-rw-r--r--src/ir/item.rs60
-rw-r--r--src/ir/objc.rs4
-rw-r--r--src/ir/ty.rs87
-rw-r--r--src/main.rs7
-rw-r--r--src/options.rs5
-rw-r--r--tests/expectations/tests/issue-446.rs22
-rw-r--r--tests/headers/issue-446.hpp11
14 files changed, 638 insertions, 215 deletions
diff --git a/src/clang.rs b/src/clang.rs
index e74f1a61..bcb22e06 100644
--- a/src/clang.rs
+++ b/src/clang.rs
@@ -136,11 +136,11 @@ impl Cursor {
// `clang_Cursor_getNumTemplateArguments` is totally unreliable.
// Therefore, try former first, and only fallback to the latter if we
// have to.
- self.cur_type().num_template_args()
+ self.cur_type()
+ .num_template_args()
.or_else(|| {
- let n: c_int = unsafe {
- clang_Cursor_getNumTemplateArguments(self.x)
- };
+ let n: c_int =
+ unsafe { clang_Cursor_getNumTemplateArguments(self.x) };
if n >= 0 {
Some(n as u32)
@@ -360,6 +360,41 @@ impl Cursor {
}
}
+ /// Collect all of this cursor's children into a vec and return them.
+ pub fn collect_children(&self) -> Vec<Cursor> {
+ let mut children = vec![];
+ self.visit(|c| {
+ children.push(c);
+ CXChildVisit_Continue
+ });
+ children
+ }
+
+ /// Does this cursor have any children?
+ pub fn has_children(&self) -> bool {
+ let mut has_children = false;
+ self.visit(|_| {
+ has_children = true;
+ CXChildVisit_Break
+ });
+ has_children
+ }
+
+ /// Does this cursor have at least `n` children?
+ pub fn has_at_least_num_children(&self, n: usize) -> bool {
+ assert!(n > 0);
+ let mut num_left = n;
+ self.visit(|_| {
+ num_left -= 1;
+ if num_left == 0 {
+ CXChildVisit_Break
+ } else {
+ CXChildVisit_Continue
+ }
+ });
+ num_left == 0
+ }
+
/// Returns whether the given location contains a cursor with the given
/// kind in the first level of nesting underneath (doesn't look
/// recursively).
@@ -581,7 +616,7 @@ impl Hash for Cursor {
}
/// The type of a node in clang's AST.
-#[derive(Clone)]
+#[derive(Clone, Copy)]
pub struct Type {
x: CXType,
}
@@ -657,6 +692,31 @@ impl Type {
}
}
+ /// Get the canonical declaration of this type, if it is available.
+ pub fn canonical_declaration(&self,
+ location: Option<&Cursor>)
+ -> Option<CanonicalTypeDeclaration> {
+ let mut declaration = self.declaration();
+ if !declaration.is_valid() {
+ if let Some(location) = location {
+ let mut location = *location;
+ if let Some(referenced) = location.referenced() {
+ location = referenced;
+ }
+ if location.is_template_like() {
+ declaration = location;
+ }
+ }
+ }
+
+ let canonical = declaration.canonical();
+ if canonical.is_valid() && canonical.kind() != CXCursor_NoDeclFound {
+ Some(CanonicalTypeDeclaration(*self, canonical))
+ } else {
+ None
+ }
+ }
+
/// Get a raw display name for this type.
pub fn spelling(&self) -> String {
unsafe { cxstring_into_string(clang_getTypeSpelling(self.x)) }
@@ -731,10 +791,12 @@ impl Type {
/// If this type is a class template specialization, return its
/// template arguments. Otherwise, return None.
pub fn template_args(&self) -> Option<TypeTemplateArgIterator> {
- self.num_template_args().map(|n| TypeTemplateArgIterator {
- x: self.x,
- length: n,
- index: 0,
+ self.num_template_args().map(|n| {
+ TypeTemplateArgIterator {
+ x: self.x,
+ length: n,
+ index: 0,
+ }
})
}
@@ -836,9 +898,8 @@ impl Type {
// Yep, the spelling of this containing type-parameter is extremely
// nasty... But can happen in <type_traits>. Unfortunately I couldn't
// reduce it enough :(
- self.template_args().map_or(false, |args| {
- args.len() > 0
- }) && match self.declaration().kind() {
+ self.template_args().map_or(false, |args| args.len() > 0) &&
+ match self.declaration().kind() {
CXCursor_ClassTemplatePartialSpecialization |
CXCursor_TypeAliasTemplateDecl |
CXCursor_TemplateTemplateParameter => false,
@@ -847,6 +908,26 @@ impl Type {
}
}
+/// The `CanonicalTypeDeclaration` type exists as proof-by-construction that its
+/// cursor is the canonical declaration for its type. If you have a
+/// `CanonicalTypeDeclaration` instance, you know for sure that the type and
+/// cursor match up in a canonical declaration relationship, and it simply
+/// cannot be otherwise.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct CanonicalTypeDeclaration(Type, Cursor);
+
+impl CanonicalTypeDeclaration {
+ /// Get the type.
+ pub fn ty(&self) -> &Type {
+ &self.0
+ }
+
+ /// Get the type's canonical declaration cursor.
+ pub fn cursor(&self) -> &Cursor {
+ &self.1
+ }
+}
+
/// An iterator for a type's template arguments.
pub struct TypeTemplateArgIterator {
x: CXType,
@@ -1389,7 +1470,10 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult {
type_to_str(ty.kind())));
}
if let Some(ty) = c.ret_type() {
- print_indent(depth, format!(" {}ret-type = {}", prefix, type_to_str(ty.kind())));
+ print_indent(depth,
+ format!(" {}ret-type = {}",
+ prefix,
+ type_to_str(ty.kind())));
}
if let Some(refd) = c.referenced() {
@@ -1398,7 +1482,9 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult {
print_cursor(depth,
String::from(prefix) + "referenced.",
&refd);
- print_cursor(depth, String::from(prefix) + "referenced.", &refd);
+ print_cursor(depth,
+ String::from(prefix) + "referenced.",
+ &refd);
}
}
@@ -1408,7 +1494,9 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult {
print_cursor(depth,
String::from(prefix) + "canonical.",
&canonical);
- print_cursor(depth, String::from(prefix) + "canonical.", &canonical);
+ print_cursor(depth,
+ String::from(prefix) + "canonical.",
+ &canonical);
}
if let Some(specialized) = c.specialized() {
@@ -1417,7 +1505,9 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult {
print_cursor(depth,
String::from(prefix) + "specialized.",
&specialized);
- print_cursor(depth, String::from(prefix) + "specialized.", &specialized);
+ print_cursor(depth,
+ String::from(prefix) + "specialized.",
+ &specialized);
}
}
}
diff --git a/src/codegen/helpers.rs b/src/codegen/helpers.rs
index 9e19637c..06dadab0 100644
--- a/src/codegen/helpers.rs
+++ b/src/codegen/helpers.rs
@@ -89,9 +89,7 @@ pub mod ast_ty {
let prefix = ctx.rust_ident_raw(prefix);
quote_ty!(ctx.ext_cx(), $prefix::$ident)
}
- None => {
- quote_ty!(ctx.ext_cx(), ::std::os::raw::$ident)
- }
+ None => quote_ty!(ctx.ext_cx(), ::std::os::raw::$ident),
}
}
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index d3e204ce..7469abb9 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -521,7 +521,7 @@ impl CodeGenerator for Type {
TypeKind::Pointer(..) |
TypeKind::BlockPointer |
TypeKind::Reference(..) |
- TypeKind::TemplateRef(..) |
+ TypeKind::TemplateInstantiation(..) |
TypeKind::Function(..) |
TypeKind::ResolvedTypeRef(..) |
TypeKind::Named => {
@@ -1377,9 +1377,10 @@ impl CodeGenerator for CompInfo {
// FIXME when [issue #465](https://github.com/servo/rust-bindgen/issues/465) ready
let too_many_base_vtables = self.base_members()
.iter()
- .filter(|base| ctx.resolve_type(base.ty).has_vtable(ctx))
- .count() >
- 1;
+ .filter(|base| {
+ ctx.resolve_type(base.ty).has_vtable(ctx)
+ })
+ .count() > 1;
let should_skip_field_offset_checks = item.is_opaque(ctx) ||
too_many_base_vtables;
@@ -2180,7 +2181,7 @@ impl ToRustTy for Type {
let path = item.namespace_aware_canonical_path(ctx);
aster::AstBuilder::new().ty().path().ids(path).build()
}
- TypeKind::TemplateRef(inner, ref template_args) => {
+ TypeKind::TemplateInstantiation(inner, ref template_args) => {
// PS: Sorry for the duplication here.
let mut inner_ty = inner.to_rust_ty(ctx).unwrap();
@@ -2189,7 +2190,7 @@ impl ToRustTy for Type {
.map(|arg| arg.to_rust_ty(ctx))
.collect::<Vec<_>>();
- path.segments.last_mut().unwrap().parameters = if
+ path.segments.last_mut().unwrap().parameters = if
template_args.is_empty() {
None
} else {
@@ -2509,8 +2510,8 @@ mod utils {
use super::ItemToRustTy;
use aster;
use ir::context::{BindgenContext, ItemId};
- use ir::item::{Item, ItemCanonicalPath};
use ir::function::FunctionSig;
+ use ir::item::{Item, ItemCanonicalPath};
use ir::ty::TypeKind;
use std::mem;
use syntax::ast;
diff --git a/src/ir/comp.rs b/src/ir/comp.rs
index 69569efa..b02cd342 100644
--- a/src/ir/comp.rs
+++ b/src/ir/comp.rs
@@ -5,7 +5,7 @@ use super::context::{BindgenContext, ItemId};
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault};
use super::item::Item;
use super::layout::Layout;
-use super::ty::Type;
+use super::ty::{TemplateDeclaration, Type};
use super::type_collector::{ItemSet, TypeCollector};
use clang;
use parse::{ClangItemParser, ParseError};
@@ -559,10 +559,8 @@ impl CompInfo {
_ => false,
});
ci.template_args = match ty.template_args() {
- // In forward declarations and not specializations,
- // etc, they are in
- // the ast, we'll meet them in
- // CXCursor_TemplateTypeParameter
+ // In forward declarations and not specializations, etc, they are in
+ // the ast, we'll meet them in CXCursor_TemplateTypeParameter
None => vec![],
Some(arg_types) => {
let num_arg_types = arg_types.len();
@@ -911,6 +909,16 @@ impl CompInfo {
}
}
+impl TemplateDeclaration for CompInfo {
+ fn template_params(&self, _ctx: &BindgenContext) -> Option<Vec<ItemId>> {
+ if self.template_args.is_empty() {
+ None
+ } else {
+ Some(self.template_args.clone())
+ }
+ }
+}
+
impl CanDeriveDebug for CompInfo {
type Extra = Option<Layout>;
diff --git a/src/ir/context.rs b/src/ir/context.rs
index a7482394..91ce579c 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -5,18 +5,20 @@ use super::int::IntKind;
use super::item::{Item, ItemCanonicalPath};
use super::item_kind::ItemKind;
use super::module::{Module, ModuleKind};
-use super::ty::{FloatKind, Type, TypeKind};
+use super::ty::{FloatKind, TemplateDeclaration, Type, TypeKind};
use super::type_collector::{ItemSet, TypeCollector};
use BindgenOptions;
use cexpr;
use chooser::TypeChooser;
use clang::{self, Cursor};
+use clang_sys;
use parse::ClangItemParser;
use std::borrow::Cow;
use std::cell::Cell;
use std::collections::{HashMap, VecDeque, hash_map};
use std::collections::btree_map::{self, BTreeMap};
use std::fmt;
+use std::iter::IntoIterator;
use syntax::ast::Ident;
use syntax::codemap::{DUMMY_SP, Span};
use syntax::ext::base::ExtCtxt;
@@ -120,9 +122,7 @@ pub struct BindgenContext<'ctx> {
/// We could also use the `types` HashMap, but my intention with it is that
/// only valid types and declarations end up there, and this could
/// potentially break that assumption.
- ///
- /// FIXME: Should not be public, though... meh.
- pub currently_parsed_types: Vec<(Cursor, ItemId)>,
+ currently_parsed_types: Vec<PartialType>,
/// A HashSet with all the already parsed macro names. This is done to avoid
/// hard errors while parsing duplicated macros, as well to allow macro
@@ -193,6 +193,26 @@ impl<'ctx> BindgenContext<'ctx> {
me
}
+ /// Get the stack of partially parsed types that we are in the middle of
+ /// parsing.
+ pub fn currently_parsed_types(&self) -> &[PartialType] {
+ &self.currently_parsed_types[..]
+ }
+
+ /// Begin parsing the given partial type, and push it onto the
+ /// `currently_parsed_types` stack so that we won't infinite recurse if we
+ /// run into a reference to it while parsing it.
+ pub fn begin_parsing(&mut self, partial_ty: PartialType) {
+ self.currently_parsed_types.push(partial_ty);
+ }
+
+ /// Finish parsing the current partial type, pop it off the
+ /// `currently_parsed_types` stack, and return it.
+ pub fn finish_parsing(&mut self) -> PartialType {
+ self.currently_parsed_types.pop()
+ .expect("should have been parsing a type, if we finished parsing a type")
+ }
+
/// Get the user-provided type chooser by reference, if any.
pub fn type_chooser(&self) -> Option<&TypeChooser> {
self.options().type_chooser.as_ref().map(|t| &**t)
@@ -600,117 +620,262 @@ impl<'ctx> BindgenContext<'ctx> {
self.current_module
}
- /// This is one of the hackiest methods in all the parsing code. This method
- /// is used to allow having templates with another argument names instead of
- /// the canonical ones.
+ /// Given a cursor pointing to the location of a template instantiation,
+ /// return a tuple of the form `(declaration_cursor, declaration_id,
+ /// num_expected_template_args)`.
+ ///
+ /// Note that `declaration_id` is not guaranteed to be in the context's item
+ /// set! It is possible that it is a partial type that we are still in the
+ /// middle of parsign.
+ fn get_declaration_info_for_template_instantiation
+ (&self,
+ instantiation: &Cursor)
+ -> Option<(Cursor, ItemId, usize)> {
+ instantiation.cur_type()
+ .canonical_declaration(Some(instantiation))
+ .and_then(|canon_decl| {
+ self.get_resolved_type(&canon_decl)
+ .and_then(|template_decl_id| {
+ template_decl_id.num_template_params(self)
+ .map(|num_params| {
+ (*canon_decl.cursor(),
+ template_decl_id,
+ num_params)
+ })
+ })
+ })
+ .or_else(|| {
+ // If we haven't already parsed the declaration of
+ // the template being instantiated, then it *must*
+ // be on the stack of types we are currently
+ // parsing. If it wasn't then clang would have
+ // already errored out before we started
+ // constructing our IR because you can't instantiate
+ // a template until it is fully defined.
+ instantiation.referenced()
+ .and_then(|referenced| {
+ self.currently_parsed_types()
+ .iter()
+ .find(|partial_ty| *partial_ty.decl() == referenced)
+ .cloned()
+ })
+ .and_then(|template_decl| {
+ template_decl.num_template_params(self)
+ .map(|num_template_params| {
+ (*template_decl.decl(),
+ template_decl.id(),
+ num_template_params)
+ })
+ })
+ })
+ }
+
+ /// Parse a template instantiation, eg `Foo<int>`.
///
/// This is surprisingly difficult to do with libclang, due to the fact that
- /// partial template specializations don't provide explicit template
- /// argument information.
+ /// it doesn't provide explicit template argument information, except for
+ /// function template declarations(!?!??!).
///
- /// The only way to do this as far as I know, is inspecting manually the
- /// AST, looking for TypeRefs inside. This, unfortunately, doesn't work for
+ /// The only way to do this is manually inspecting the AST and looking for
+ /// TypeRefs and TemplateRefs inside. This, unfortunately, doesn't work for
/// more complex cases, see the comment on the assertion below.
///
- /// To see an example of what this handles:
+ /// To add insult to injury, the AST itself has structure that doesn't make
+ /// sense. Sometimes `Foo<Bar<int>>` has an AST with nesting like you might
+ /// expect: `(Foo (Bar (int)))`. Other times, the AST we get is completely
+ /// flat: `(Foo Bar int)`.
+ ///
+ /// To see an example of what this method handles:
///
/// ```c++
- /// template<typename T>
- /// class Incomplete {
- /// T p;
- /// };
+ /// template<typename T>
+ /// class Incomplete {
+ /// T p;
+ /// };
///
- /// template<typename U>
- /// class Foo {
- /// Incomplete<U> bar;
- /// };
+ /// template<typename U>
+ /// class Foo {
+ /// Incomplete<U> bar;
+ /// };
/// ```
- fn build_template_wrapper(&mut self,
- with_id: ItemId,
- wrapping: ItemId,
- parent_id: ItemId,
- ty: &clang::Type,
- location: clang::Cursor,
- declaration: clang::Cursor)
- -> ItemId {
- use clang_sys::*;
+ fn instantiate_template(&mut self,
+ with_id: ItemId,
+ template: ItemId,
+ parent_id: ItemId,
+ ty: &clang::Type,
+ location: clang::Cursor)
+ -> Option<ItemId> {
+ use clang_sys;
+
+ let num_expected_args = match self.resolve_type(template)
+ .num_template_params(self) {
+ Some(n) => n,
+ None => {
+ warn!("Tried to instantiate a template for which we could not \
+ determine any template parameters");
+ return None;
+ }
+ };
+
let mut args = vec![];
- location.visit(|c| {
- 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
- // specialization), so if we pass `with_id` as the parent, it is
- // potentially a dangling reference. Instead, use the canonical
- // template declaration as the parent. It is already parsed and
- // has a known-resolvable `ItemId`.
- let new_ty = Item::from_ty_or_ref(c.cur_type(),
- Some(c),
- Some(wrapping),
- self);
- args.push(new_ty);
+ let mut found_const_arg = false;
+ let mut children = location.collect_children();
+
+ if children.iter().all(|c| !c.has_children()) {
+ // This is insanity... If clang isn't giving us a properly nested
+ // AST for which template arguments belong to which template we are
+ // instantiating, we'll need to construct it ourselves. However,
+ // there is an extra `NamespaceRef, NamespaceRef, ..., TemplateRef`
+ // representing a reference to the outermost template declaration
+ // that we need to filter out of the children. We need to do this
+ // filtering because we already know which template declaration is
+ // being specialized via the `location`'s type, and if we do not
+ // filter it out, we'll add an extra layer of template instantiation
+ // on accident.
+ let idx = children.iter()
+ .position(|c| c.kind() == clang_sys::CXCursor_TemplateRef);
+ if let Some(idx) = idx {
+ if children.iter()
+ .take(idx)
+ .all(|c| c.kind() == clang_sys::CXCursor_NamespaceRef) {
+ children = children.into_iter().skip(idx + 1).collect();
+ }
}
- CXChildVisit_Continue
- });
+ }
- let item = {
- let wrapping_type = self.resolve_type(wrapping);
- if let TypeKind::Comp(ref ci) = *wrapping_type.kind() {
- let old_args = ci.template_args();
+ for child in children.iter().rev() {
+ match child.kind() {
+ clang_sys::CXCursor_TypeRef |
+ clang_sys::CXCursor_TypedefDecl |
+ clang_sys::CXCursor_TypeAliasDecl => {
+ // The `with_id` id will potentially end up unused if we give up
+ // on this type (for example, because it has const value
+ // template args), so if we pass `with_id` as the parent, it is
+ // potentially a dangling reference. Instead, use the canonical
+ // template declaration as the parent. It is already parsed and
+ // has a known-resolvable `ItemId`.
+ let ty = Item::from_ty_or_ref(child.cur_type(),
+ Some(*child),
+ Some(template),
+ self);
+ args.push(ty);
+ }
+ clang_sys::CXCursor_TemplateRef => {
+ let (template_decl_cursor, template_decl_id, num_expected_template_args) =
+ match self.get_declaration_info_for_template_instantiation(child) {
+ Some(info) => info,
+ None => return None,
+ };
+
+ if num_expected_template_args == 0 ||
+ child.has_at_least_num_children(num_expected_template_args) {
+ // Do a happy little parse. See comment in the TypeRef
+ // match arm about parent IDs.
+ let ty = Item::from_ty_or_ref(child.cur_type(),
+ Some(*child),
+ Some(template),
+ self);
+ args.push(ty);
+ } else {
+ // This is the case mentioned in the doc comment where
+ // clang gives us a flattened AST and we have to
+ // reconstruct which template arguments go to which
+ // instantiation :(
+ let args_len = args.len();
+ if args_len < num_expected_template_args {
+ warn!("Found a template instantiation without \
+ enough template arguments");
+ return None;
+ }
- // 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;
+ let mut sub_args: Vec<_> =
+ args.drain(args_len - num_expected_template_args..)
+ .collect();
+ sub_args.reverse();
+
+ let sub_name = Some(template_decl_cursor.spelling());
+ let sub_kind =
+ TypeKind::TemplateInstantiation(template_decl_id,
+ sub_args);
+ let sub_ty = Type::new(sub_name,
+ template_decl_cursor.cur_type()
+ .fallible_layout()
+ .ok(),
+ sub_kind,
+ false);
+ let sub_id = self.next_item_id();
+ let sub_item = Item::new(sub_id,
+ None,
+ None,
+ template_decl_id,
+ ItemKind::Type(sub_ty));
+
+ // Bypass all the validations in add_item explicitly.
+ debug!("instantiate_template: inserting nested \
+ instantiation item: {:?}",
+ sub_item);
+ debug_assert!(sub_id == sub_item.id());
+ self.items.insert(sub_id, sub_item);
+ args.push(sub_id);
+ }
+ }
+ _ => {
+ warn!("Found template arg cursor we can't handle: {:?}",
+ child);
+ found_const_arg = true;
}
- } else {
- assert_eq!(declaration.kind(),
- ::clang_sys::CXCursor_TypeAliasTemplateDecl,
- "Expected wrappable type");
}
+ }
- let type_kind = TypeKind::TemplateRef(wrapping, args);
- let name = ty.spelling();
- let name = if name.is_empty() { None } else { Some(name) };
- let ty = Type::new(name,
- ty.fallible_layout().ok(),
- type_kind,
- ty.is_const());
- Item::new(with_id, None, None, parent_id, ItemKind::Type(ty))
- };
+ if found_const_arg {
+ // This is a dependently typed template instantiation. That is, an
+ // instantiation of a template with one or more const values as
+ // template arguments, rather than only types as template
+ // arguments. For example, `Foo<true, 5>` versus `Bar<bool, int>`.
+ // We can't handle these instantiations, so just punt in this
+ // situation...
+ warn!("Found template instantiated with a const value; \
+ bindgen can't handle this kind of template instantiation!");
+ return None;
+ }
+
+ if args.len() != num_expected_args {
+ warn!("Found a template with an unexpected number of template \
+ arguments");
+ return None;
+ }
+
+ args.reverse();
+ let type_kind = TypeKind::TemplateInstantiation(template, args);
+ let name = ty.spelling();
+ let name = if name.is_empty() { None } else { Some(name) };
+ let ty = Type::new(name,
+ ty.fallible_layout().ok(),
+ type_kind,
+ ty.is_const());
+ let item =
+ Item::new(with_id, None, None, parent_id, ItemKind::Type(ty));
// Bypass all the validations in add_item explicitly.
- debug!("build_template_wrapper: inserting item: {:?}", item);
+ debug!("instantiate_template: inserting item: {:?}", item);
debug_assert!(with_id == item.id());
self.items.insert(with_id, item);
- with_id
+ Some(with_id)
+ }
+
+ /// If we have already resolved the type for the given type declaration,
+ /// return its `ItemId`. Otherwise, return `None`.
+ fn get_resolved_type(&self,
+ decl: &clang::CanonicalTypeDeclaration)
+ -> Option<ItemId> {
+ self.types
+ .get(&TypeKey::Declaration(*decl.cursor()))
+ .or_else(|| {
+ decl.cursor()
+ .usr()
+ .and_then(|usr| self.types.get(&TypeKey::USR(usr)))
+ })
+ .cloned()
}
/// Looks up for an already resolved type, either because it's builtin, or
@@ -726,45 +891,32 @@ impl<'ctx> BindgenContext<'ctx> {
ty,
location,
parent_id);
- let mut declaration = ty.declaration();
- if !declaration.is_valid() {
- if let Some(location) = location {
- if location.is_template_like() {
- declaration = location;
- }
- }
- }
- let canonical_declaration = declaration.canonical();
- if canonical_declaration.is_valid() {
- let id = self.types
- .get(&TypeKey::Declaration(canonical_declaration))
- .map(|id| *id)
- .or_else(|| {
- canonical_declaration.usr()
- .and_then(|usr| self.types.get(&TypeKey::USR(usr)))
- .map(|id| *id)
- });
- if let Some(id) = id {
+
+ if let Some(decl) = ty.canonical_declaration(location.as_ref()) {
+ if let Some(id) = self.get_resolved_type(&decl) {
debug!("Already resolved ty {:?}, {:?}, {:?} {:?}",
id,
- declaration,
+ decl,
ty,
location);
-
- // If the declaration existed, we *might* be done, but it's not
- // the case for class templates, where the template arguments
- // may vary.
+ // If the declaration already exists, then either:
//
- // In this case, we create a TemplateRef with the new template
- // arguments, pointing to the canonical template.
+ // * the declaration is a template declaration of some sort,
+ // and we are looking at an instantiation or specialization
+ // of it, or
+ // * we have already parsed and resolved this type, and
+ // there's nothing left to do.
//
- // Note that we only do it if parent_id is some, and we have a
- // location for building the new arguments, the template
- // argument names don't matter in the global context.
- if declaration.is_template_like() &&
- *ty != canonical_declaration.cur_type() &&
+ // Note that we only do the former if the `parent_id` exists,
+ // and we have a location for building the new arguments. The
+ // template argument names don't matter in the global context.
+ if decl.cursor().is_template_like() &&
+ *ty != decl.cursor().cur_type() &&
location.is_some() &&
parent_id.is_some() {
+ let location = location.unwrap();
+ let parent_id = parent_id.unwrap();
+
// 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).
@@ -775,18 +927,18 @@ impl<'ctx> BindgenContext<'ctx> {
// exposed.
//
// This is _tricky_, I know :(
- if declaration.kind() == CXCursor_TypeAliasTemplateDecl &&
- !location.unwrap().contains_cursor(CXCursor_TypeRef) &&
+ if decl.cursor().kind() == CXCursor_TypeAliasTemplateDecl &&
+ !location.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(),
- declaration));
+ return self.instantiate_template(with_id,
+ id,
+ parent_id,
+ ty,
+ location)
+ .or_else(|| Some(id));
}
return Some(self.build_ty_wrapper(with_id, id, parent_id, ty));
@@ -794,9 +946,7 @@ impl<'ctx> BindgenContext<'ctx> {
}
debug!("Not resolved, maybe builtin?");
-
- // Else, build it.
- self.build_builtin_ty(ty, declaration)
+ self.build_builtin_ty(ty)
}
// This is unfortunately a lot of bloat, but is needed to properly track
@@ -831,10 +981,7 @@ impl<'ctx> BindgenContext<'ctx> {
ret
}
- fn build_builtin_ty(&mut self,
- ty: &clang::Type,
- _declaration: Cursor)
- -> Option<ItemId> {
+ fn build_builtin_ty(&mut self, ty: &clang::Type) -> Option<ItemId> {
use clang_sys::*;
let type_kind = match ty.kind() {
CXType_NullPtr => TypeKind::NullPtr,
@@ -1156,6 +1303,68 @@ impl<'ctx> BindgenContext<'ctx> {
}
}
+/// A type that we are in the middle of parsing.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct PartialType {
+ decl: Cursor,
+ id: ItemId,
+}
+
+impl PartialType {
+ /// Construct a new `PartialType`.
+ pub fn new(decl: Cursor, id: ItemId) -> PartialType {
+ // assert!(decl == decl.canonical());
+ PartialType {
+ decl: decl,
+ id: id,
+ }
+ }
+
+ /// The cursor pointing to this partial type's declaration location.
+ pub fn decl(&self) -> &Cursor {
+ &self.decl
+ }
+
+ /// The item ID allocated for this type. This is *NOT* a key for an entry in
+ /// the context's item set yet!
+ pub fn id(&self) -> ItemId {
+ self.id
+ }
+}
+
+impl TemplateDeclaration for PartialType {
+ fn template_params(&self, _ctx: &BindgenContext) -> Option<Vec<ItemId>> {
+ // Maybe at some point we will eagerly parse named types, but for now we
+ // don't and this information is unavailable.
+ None
+ }
+
+ fn num_template_params(&self, _ctx: &BindgenContext) -> Option<usize> {
+ // Wouldn't it be nice if libclang would reliably give us this
+ // information‽
+ match self.decl().kind() {
+ clang_sys::CXCursor_ClassTemplate |
+ clang_sys::CXCursor_FunctionTemplate |
+ clang_sys::CXCursor_TypeAliasTemplateDecl => {
+ let mut num_params = 0;
+ self.decl().visit(|c| {
+ match c.kind() {
+ clang_sys::CXCursor_TemplateTypeParameter |
+ clang_sys::CXCursor_TemplateTemplateParameter |
+ clang_sys::CXCursor_NonTypeTemplateParameter => {
+ num_params += 1;
+ }
+ _ => {}
+ };
+ clang_sys::CXChildVisit_Continue
+ });
+ Some(num_params)
+ }
+ _ => None,
+ }
+ }
+}
+
/// An iterator over whitelisted items.
///
/// See `BindgenContext::whitelisted_items` for more information.
diff --git a/src/ir/derive.rs b/src/ir/derive.rs
index 6d9f368b..0fc8debf 100644
--- a/src/ir/derive.rs
+++ b/src/ir/derive.rs
@@ -85,4 +85,4 @@ pub trait CanDeriveDefault {
ctx: &BindgenContext,
extra: Self::Extra)
-> bool;
-} \ No newline at end of file
+}
diff --git a/src/ir/function.rs b/src/ir/function.rs
index 9ef9c3a7..6273de28 100644
--- a/src/ir/function.rs
+++ b/src/ir/function.rs
@@ -229,8 +229,9 @@ impl FunctionSig {
let abi = get_abi(ty.call_conv());
if abi.is_none() {
- assert_eq!(cursor.kind(), CXCursor_ObjCInstanceMethodDecl,
- "Invalid ABI for function signature")
+ assert_eq!(cursor.kind(),
+ CXCursor_ObjCInstanceMethodDecl,
+ "Invalid ABI for function signature")
}
Ok(Self::new(ret, args, ty.is_variadic(), abi))
diff --git a/src/ir/item.rs b/src/ir/item.rs
index c8de95c0..83e2a41f 100644
--- a/src/ir/item.rs
+++ b/src/ir/item.rs
@@ -1,12 +1,12 @@
//! Bindgen's core intermediate representation type.
use super::annotations::Annotations;
-use super::context::{BindgenContext, ItemId};
+use super::context::{BindgenContext, ItemId, PartialType};
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault};
use super::function::Function;
use super::item_kind::ItemKind;
use super::module::Module;
-use super::ty::{Type, TypeKind};
+use super::ty::{TemplateDeclaration, Type, TypeKind};
use super::type_collector::{ItemSet, TypeCollector};
use clang;
use clang_sys;
@@ -619,7 +619,7 @@ impl Item {
// XXX Is this completely correct? Partial template specialization
// is hard anyways, sigh...
TypeKind::TemplateAlias(_, ref args) |
- TypeKind::TemplateRef(_, ref args) => args.clone(),
+ TypeKind::TemplateInstantiation(_, ref args) => args.clone(),
// In a template specialization we've got all we want.
TypeKind::Comp(ref ci) if ci.is_template_specialization() => {
ci.template_args().iter().cloned().collect()
@@ -718,7 +718,7 @@ impl Item {
}
// Same as above.
TypeKind::ResolvedTypeRef(inner) |
- TypeKind::TemplateRef(inner, _) => {
+ TypeKind::TemplateInstantiation(inner, _) => {
item = ctx.resolve_item(inner);
}
_ => return item.id(),
@@ -902,6 +902,32 @@ impl Item {
}
}
+impl TemplateDeclaration for ItemId {
+ fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> {
+ ctx.resolve_item_fallible(*self)
+ .and_then(|item| item.template_params(ctx))
+ }
+}
+
+impl TemplateDeclaration for Item {
+ fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> {
+ self.kind.template_params(ctx)
+ }
+}
+
+impl TemplateDeclaration for ItemKind {
+ fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> {
+ match *self {
+ ItemKind::Type(ref ty) => ty.template_params(ctx),
+ // TODO FITZGEN: shouldn't functions be able to have free template
+ // params?
+ ItemKind::Module(_) |
+ ItemKind::Function(_) |
+ ItemKind::Var(_) => None,
+ }
+ }
+}
+
// An utility function to handle recursing inside nested types.
fn visit_child(cur: clang::Cursor,
id: ItemId,
@@ -1176,18 +1202,18 @@ impl ClangItemParser for Item {
};
if valid_decl {
- if let Some(&(_, item_id)) =
- ctx.currently_parsed_types
- .iter()
- .find(|&&(d, _)| d == declaration_to_look_for) {
+ if let Some(partial) = ctx.currently_parsed_types()
+ .iter()
+ .find(|ty| *ty.decl() == declaration_to_look_for) {
debug!("Avoiding recursion parsing type: {:?}", ty);
- return Ok(item_id);
+ return Ok(partial.id());
}
}
let current_module = ctx.current_module();
+ let partial_ty = PartialType::new(declaration_to_look_for, id);
if valid_decl {
- ctx.currently_parsed_types.push((declaration_to_look_for, id));
+ ctx.begin_parsing(partial_ty);
}
let result = Type::from_clang_ty(id, ty, location, parent_id, ctx);
@@ -1215,9 +1241,8 @@ impl ClangItemParser for Item {
// declaration_to_look_for suspiciously shares a lot of
// logic with ir::context, so we should refactor that.
if valid_decl {
- let (popped_decl, _) =
- ctx.currently_parsed_types.pop().unwrap();
- assert_eq!(popped_decl, declaration_to_look_for);
+ let finished = ctx.finish_parsing();
+ assert_eq!(*finished.decl(), declaration_to_look_for);
}
location.visit(|cur| {
@@ -1225,8 +1250,9 @@ impl ClangItemParser for Item {
});
if valid_decl {
- ctx.currently_parsed_types
- .push((declaration_to_look_for, id));
+ let partial_ty =
+ PartialType::new(declaration_to_look_for, id);
+ ctx.begin_parsing(partial_ty);
}
}
// If we have recursed into the AST all we know, and we still
@@ -1253,8 +1279,8 @@ impl ClangItemParser for Item {
};
if valid_decl {
- let (popped_decl, _) = ctx.currently_parsed_types.pop().unwrap();
- assert_eq!(popped_decl, declaration_to_look_for);
+ let partial_ty = ctx.finish_parsing();
+ assert_eq!(*partial_ty.decl(), declaration_to_look_for);
}
ret
diff --git a/src/ir/objc.rs b/src/ir/objc.rs
index ea2cc0c8..b3c3688b 100644
--- a/src/ir/objc.rs
+++ b/src/ir/objc.rs
@@ -1,10 +1,10 @@
//! Objective C types
+use super::context::BindgenContext;
+use super::function::FunctionSig;
use clang;
use clang_sys::CXChildVisit_Continue;
use clang_sys::CXCursor_ObjCInstanceMethodDecl;
-use super::context::BindgenContext;
-use super::function::FunctionSig;
/// Objective C interface as used in TypeKind
///
diff --git a/src/ir/ty.rs b/src/ir/ty.rs
index be108cc0..a98ca446 100644
--- a/src/ir/ty.rs
+++ b/src/ir/ty.rs
@@ -14,6 +14,29 @@ use clang::{self, Cursor};
use parse::{ClangItemParser, ParseError, ParseResult};
use std::mem;
+/// Template declaration related methods.
+pub trait TemplateDeclaration {
+ /// Get the set of `ItemId`s that make up this template declaration's free
+ /// template parameters.
+ ///
+ /// Note that these might *not* all be named types: C++ allows
+ /// constant-value template parameters. Of course, Rust does not allow
+ /// generic parameters to be anything but types, so we must treat them as
+ /// opaque, and avoid instantiating them.
+ fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>>;
+
+ /// Get the number of free template parameters this template declaration
+ /// has.
+ ///
+ /// Implementations *may* return `Some` from this method when
+ /// `template_params` returns `None`. This is useful when we only have
+ /// partial information about the template declaration, such as when we are
+ /// in the middle of parsing it.
+ fn num_template_params(&self, ctx: &BindgenContext) -> Option<usize> {
+ self.template_params(ctx).map(|params| params.len())
+ }
+}
+
/// The base representation of a type in bindgen.
///
/// A type has an optional name, which if present cannot be empty, a `layout`
@@ -217,7 +240,7 @@ impl Type {
pub fn has_vtable(&self, ctx: &BindgenContext) -> bool {
// FIXME: Can we do something about template parameters? Huh...
match self.kind {
- TypeKind::TemplateRef(t, _) |
+ TypeKind::TemplateInstantiation(t, _) |
TypeKind::TemplateAlias(t, _) |
TypeKind::Alias(t) |
TypeKind::ResolvedTypeRef(t) => ctx.resolve_type(t).has_vtable(ctx),
@@ -230,7 +253,7 @@ impl Type {
/// Returns whether this type has a destructor.
pub fn has_destructor(&self, ctx: &BindgenContext) -> bool {
match self.kind {
- TypeKind::TemplateRef(t, _) |
+ TypeKind::TemplateInstantiation(t, _) |
TypeKind::TemplateAlias(t, _) |
TypeKind::Alias(t) |
TypeKind::ResolvedTypeRef(t) => {
@@ -269,7 +292,7 @@ impl Type {
.signature_contains_named_type(ctx, ty)
}
TypeKind::TemplateAlias(_, ref template_args) |
- TypeKind::TemplateRef(_, ref template_args) => {
+ TypeKind::TemplateInstantiation(_, ref template_args) => {
template_args.iter().any(|arg| {
ctx.resolve_type(*arg)
.signature_contains_named_type(ctx, ty)
@@ -336,7 +359,7 @@ impl Type {
TypeKind::ResolvedTypeRef(inner) |
TypeKind::Alias(inner) |
TypeKind::TemplateAlias(inner, _) |
- TypeKind::TemplateRef(inner, _) => {
+ TypeKind::TemplateInstantiation(inner, _) => {
ctx.resolve_type(inner).safe_canonical_type(ctx)
}
@@ -352,7 +375,7 @@ impl Type {
TypeKind::Pointer(..) |
TypeKind::Array(..) |
TypeKind::Reference(..) |
- TypeKind::TemplateRef(..) |
+ TypeKind::TemplateInstantiation(..) |
TypeKind::ResolvedTypeRef(..) => true,
_ => false,
}
@@ -379,7 +402,7 @@ impl Type {
TypeKind::ResolvedTypeRef(inner) |
TypeKind::Alias(inner) |
TypeKind::TemplateAlias(inner, _) |
- TypeKind::TemplateRef(inner, _) => {
+ TypeKind::TemplateInstantiation(inner, _) => {
ctx.resolve_type(inner).calc_size(ctx)
}
TypeKind::Array(inner, len) => {
@@ -438,6 +461,41 @@ fn is_invalid_named_type_empty_name() {
}
+impl TemplateDeclaration for Type {
+ fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> {
+ self.kind.template_params(ctx)
+ }
+}
+
+impl TemplateDeclaration for TypeKind {
+ fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> {
+ match *self {
+ TypeKind::ResolvedTypeRef(id) => {
+ ctx.resolve_type(id).template_params(ctx)
+ }
+ TypeKind::Comp(ref comp) => comp.template_params(ctx),
+ TypeKind::TemplateAlias(_, ref args) => Some(args.clone()),
+
+ TypeKind::TemplateInstantiation(..) |
+ TypeKind::Void |
+ TypeKind::NullPtr |
+ TypeKind::Int(_) |
+ TypeKind::Float(_) |
+ TypeKind::Complex(_) |
+ TypeKind::Array(..) |
+ TypeKind::Function(_) |
+ TypeKind::Enum(_) |
+ TypeKind::Pointer(_) |
+ TypeKind::BlockPointer |
+ TypeKind::Reference(_) |
+ TypeKind::UnresolvedTypeRef(..) |
+ TypeKind::Named |
+ TypeKind::Alias(_) |
+ TypeKind::ObjCInterface(_) => None,
+ }
+ }
+}
+
impl CanDeriveDebug for Type {
type Extra = ();
@@ -474,7 +532,7 @@ impl CanDeriveDefault for Type {
}
TypeKind::Void |
TypeKind::Named |
- TypeKind::TemplateRef(..) |
+ TypeKind::TemplateInstantiation(..) |
TypeKind::Reference(..) |
TypeKind::NullPtr |
TypeKind::Pointer(..) |
@@ -501,7 +559,7 @@ impl<'a> CanDeriveCopy<'a> for Type {
}
TypeKind::ResolvedTypeRef(t) |
TypeKind::TemplateAlias(t, _) |
- TypeKind::TemplateRef(t, _) |
+ TypeKind::TemplateInstantiation(t, _) |
TypeKind::Alias(t) => t.can_derive_copy(ctx, ()),
TypeKind::Comp(ref info) => {
info.can_derive_copy(ctx, (item, self.layout(ctx)))
@@ -597,10 +655,9 @@ pub enum TypeKind {
/// A reference to a type, as in: int& foo().
Reference(ItemId),
- /// A reference to a template, with different template parameter names. To
- /// see why this is needed, check out the creation of this variant in
- /// `Type::from_clang_ty`.
- TemplateRef(ItemId, Vec<ItemId>),
+ /// An instantiation of an abstract template declaration (first tuple
+ /// member) with a set of concrete template arguments (second tuple member).
+ TemplateInstantiation(ItemId, Vec<ItemId>),
/// A reference to a yet-to-resolve type. This stores the clang cursor
/// itself, and postpones its resolution.
@@ -644,7 +701,7 @@ impl Type {
TypeKind::ResolvedTypeRef(inner) |
TypeKind::Alias(inner) |
TypeKind::TemplateAlias(inner, _) |
- TypeKind::TemplateRef(inner, _) => {
+ TypeKind::TemplateInstantiation(inner, _) => {
ctx.resolve_type(inner).is_unsized(ctx)
}
TypeKind::Named |
@@ -698,7 +755,7 @@ impl Type {
potential_id,
ty,
location);
- debug!("currently_parsed_types: {:?}", ctx.currently_parsed_types);
+ debug!("currently_parsed_types: {:?}", ctx.currently_parsed_types());
let canonical_ty = ty.canonical_type();
@@ -1087,7 +1144,7 @@ impl TypeCollector for Type {
}
TypeKind::TemplateAlias(inner, ref template_args) |
- TypeKind::TemplateRef(inner, ref template_args) => {
+ TypeKind::TemplateInstantiation(inner, ref template_args) => {
types.insert(inner);
for &item in template_args {
types.insert(item);
diff --git a/src/main.rs b/src/main.rs
index 68f3d0a8..fc1ef8e2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -44,10 +44,9 @@ pub fn main() {
match builder_from_flags(bind_args.into_iter()) {
Ok((builder, output, verbose)) => {
- let builder_result =
- panic::catch_unwind(||
- builder.generate().expect("Unable to generate bindings")
- );
+ let builder_result = panic::catch_unwind(|| {
+ builder.generate().expect("Unable to generate bindings")
+ });
if builder_result.is_err() {
if verbose {
diff --git a/src/options.rs b/src/options.rs
index 7a6fde96..49bad841 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -4,8 +4,9 @@ use std::fs::File;
use std::io::{self, Error, ErrorKind};
/// Construct a new [`Builder`](./struct.Builder.html) from command line flags.
-pub fn builder_from_flags<I>(args: I)
- -> Result<(Builder, Box<io::Write>, bool), io::Error>
+pub fn builder_from_flags<I>
+ (args: I)
+ -> Result<(Builder, Box<io::Write>, bool), io::Error>
where I: Iterator<Item = String>,
{
let matches = App::new("bindgen")
diff --git a/tests/expectations/tests/issue-446.rs b/tests/expectations/tests/issue-446.rs
new file mode 100644
index 00000000..fa736bcc
--- /dev/null
+++ b/tests/expectations/tests/issue-446.rs
@@ -0,0 +1,22 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct List<Elem> {
+ pub next: *mut List<Elem>,
+}
+impl <Elem> Default for List<Elem> {
+ fn default() -> Self { unsafe { ::std::mem::zeroed() } }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct PersistentRooted<GcThing> {
+ pub root_list: List<PersistentRooted<GcThing>>,
+}
+impl <GcThing> Default for PersistentRooted<GcThing> {
+ fn default() -> Self { unsafe { ::std::mem::zeroed() } }
+}
diff --git a/tests/headers/issue-446.hpp b/tests/headers/issue-446.hpp
new file mode 100644
index 00000000..2e09c274
--- /dev/null
+++ b/tests/headers/issue-446.hpp
@@ -0,0 +1,11 @@
+// bindgen-flags: -- -std=c++14
+
+template <typename Elem>
+class List {
+ List<Elem> *next;
+};
+
+template <typename GcThing>
+class PersistentRooted {
+ List<PersistentRooted<GcThing>> root_list;
+};