diff options
-rw-r--r-- | src/ir/context.rs | 377 |
1 files changed, 240 insertions, 137 deletions
diff --git a/src/ir/context.rs b/src/ir/context.rs index a2277b39..f25eaf3f 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -11,12 +11,14 @@ 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; @@ -618,119 +620,239 @@ 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) + -> (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) + }) + }) + }) + .unwrap_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. + let referenced = instantiation.referenced() + .expect("TemplateRefs should reference a template declaration"); + let template_decl = self.currently_parsed_types() + .iter() + .find(|partial_ty| *partial_ty.decl() == referenced) + .cloned() + .expect("template decl must be on the parse stack"); + let num_template_params = template_decl + .num_template_params(self) + .expect("and it had better be a template declaration"); + (*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) + -> ItemId { + use clang_sys; + + let num_expected_args = self.resolve_type(template) + .num_template_params(self) + .expect("template types should have template params"); + 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(); + } - // 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; + for child in children.iter().rev() { + match child.kind() { + clang_sys::CXCursor_TypeRef => { + // 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) = + self.get_declaration_info_for_template_instantiation(child); + + 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(); + assert!(args_len >= num_expected_template_args); + 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)) - }; + assert!(args.len() <= num_expected_args); + if found_const_arg || args.len() < num_expected_args { + // 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 template; + } + + 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 } + /// 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 /// because we already have it in the map. pub fn builtin_or_resolved_ty(&mut self, @@ -744,45 +866,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). @@ -793,18 +902,17 @@ 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 Some(self.instantiate_template(with_id, + id, + parent_id, + ty, + location)); } return Some(self.build_ty_wrapper(with_id, id, parent_id, ty)); @@ -812,9 +920,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 @@ -849,10 +955,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, |