diff options
-rw-r--r-- | src/codegen/mod.rs | 10 | ||||
-rw-r--r-- | src/ir/comp.rs | 4 | ||||
-rw-r--r-- | src/ir/context.rs | 88 | ||||
-rw-r--r-- | src/ir/item.rs | 10 | ||||
-rw-r--r-- | src/ir/named.rs | 257 | ||||
-rw-r--r-- | src/ir/template.rs | 142 | ||||
-rw-r--r-- | src/ir/ty.rs | 148 | ||||
-rw-r--r-- | src/lib.rs | 29 | ||||
-rw-r--r-- | src/uses.rs | 2 | ||||
-rw-r--r-- | tests/expectations/tests/issue-638-stylo-cannot-find-T-in-this-scope.rs | 23 | ||||
-rw-r--r-- | tests/headers/issue-638-stylo-cannot-find-T-in-this-scope.hpp | 12 | ||||
-rwxr-xr-x | tests/test-one.sh | 8 |
12 files changed, 452 insertions, 281 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 20151fe3..a5df8945 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -21,8 +21,8 @@ use ir::item_kind::ItemKind; use ir::layout::Layout; use ir::module::Module; use ir::objc::{ObjCInterface, ObjCMethod}; -use ir::template::{AsNamed, TemplateInstantiation}; -use ir::ty::{TemplateDeclaration, Type, TypeKind}; +use ir::template::{AsNamed, TemplateInstantiation, TemplateParameters}; +use ir::ty::{Type, TypeKind}; use ir::var::Var; use std::borrow::Cow; @@ -2647,8 +2647,10 @@ impl TryToRustTy for TemplateInstantiation { // This can happen if we generated an opaque type for a partial // template specialization, and we've hit an instantiation of // that partial specialization. - extra_assert!(ctx.resolve_type_through_type_refs(decl) - .is_opaque()); + extra_assert!(decl.into_resolver() + .through_type_refs() + .resolve(ctx) + .is_opaque(ctx)); return Err(error::Error::InstantiationOfOpaqueType); } }; diff --git a/src/ir/comp.rs b/src/ir/comp.rs index 800a8c19..7a85794b 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -6,7 +6,7 @@ use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault}; use super::item::Item; use super::layout::Layout; use super::traversal::{EdgeKind, Trace, Tracer}; -use super::ty::TemplateDeclaration; +use super::template::TemplateParameters; use clang; use parse::{ClangItemParser, ParseError}; use std::cell::Cell; @@ -821,7 +821,7 @@ impl CompInfo { } } -impl TemplateDeclaration for CompInfo { +impl TemplateParameters for CompInfo { fn self_template_params(&self, _ctx: &BindgenContext) -> Option<Vec<ItemId>> { diff --git a/src/ir/context.rs b/src/ir/context.rs index d00e4899..60ea90c6 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -6,9 +6,9 @@ use super::item::{Item, ItemCanonicalPath, ItemSet}; use super::item_kind::ItemKind; use super::module::{Module, ModuleKind}; use super::named::{UsedTemplateParameters, analyze}; -use super::template::TemplateInstantiation; +use super::template::{TemplateInstantiation, TemplateParameters}; use super::traversal::{self, Edge, ItemTraversal}; -use super::ty::{FloatKind, TemplateDeclaration, Type, TypeKind}; +use super::ty::{FloatKind, Type, TypeKind}; use BindgenOptions; use cexpr; use callbacks::ParseCallbacks; @@ -712,21 +712,6 @@ 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 @@ -1420,6 +1405,73 @@ impl<'ctx> BindgenContext<'ctx> { } } +/// A builder struct for configuring item resolution options. +#[derive(Debug, Copy, Clone)] +pub struct ItemResolver { + id: ItemId, + through_type_refs: bool, + through_type_aliases: bool, +} + +impl ItemId { + /// Create an `ItemResolver` from this item id. + pub fn into_resolver(self) -> ItemResolver { + self.into() + } +} + +impl From<ItemId> for ItemResolver { + fn from(id: ItemId) -> ItemResolver { + ItemResolver::new(id) + } +} + +impl ItemResolver { + /// Construct a new `ItemResolver` from the given id. + pub fn new(id: ItemId) -> ItemResolver { + ItemResolver { + id: id, + through_type_refs: false, + through_type_aliases: false, + } + } + + /// Keep resolving through `Type::TypeRef` items. + pub fn through_type_refs(mut self) -> ItemResolver { + self.through_type_refs = true; + self + } + + /// Keep resolving through `Type::Alias` items. + pub fn through_type_aliases(mut self) -> ItemResolver { + self.through_type_aliases = true; + self + } + + /// Finish configuring and perform the actual item resolution. + pub fn resolve<'a, 'b>(self, ctx: &'a BindgenContext<'b>) -> &'a Item { + assert!(ctx.collected_typerefs()); + + let mut id = self.id; + loop { + let item = ctx.resolve_item(id); + let ty_kind = item.as_type().map(|t| t.kind()); + match ty_kind { + Some(&TypeKind::ResolvedTypeRef(next_id)) if self.through_type_refs => { + id = next_id; + } + // We intentionally ignore template aliases here, as they are + // more complicated, and don't represent a simple renaming of + // some type. + Some(&TypeKind::Alias(next_id)) if self.through_type_aliases => { + id = next_id; + } + _ => return item, + } + } + } +} + /// A type that we are in the middle of parsing. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct PartialType { @@ -1449,7 +1501,7 @@ impl PartialType { } } -impl TemplateDeclaration for PartialType { +impl TemplateParameters for PartialType { fn self_template_params(&self, _ctx: &BindgenContext) -> Option<Vec<ItemId>> { diff --git a/src/ir/item.rs b/src/ir/item.rs index 5e806de9..20451bbc 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -8,9 +8,9 @@ use super::function::Function; use super::item_kind::ItemKind; use super::layout::Opaque; use super::module::Module; -use super::template::AsNamed; +use super::template::{AsNamed, TemplateParameters}; use super::traversal::{EdgeKind, Trace, Tracer}; -use super::ty::{TemplateDeclaration, Type, TypeKind}; +use super::ty::{Type, TypeKind}; use clang; use clang_sys; use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult}; @@ -830,7 +830,7 @@ impl DotAttributes for Item { } } -impl TemplateDeclaration for ItemId { +impl TemplateParameters for ItemId { fn self_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { @@ -839,7 +839,7 @@ impl TemplateDeclaration for ItemId { } } -impl TemplateDeclaration for Item { +impl TemplateParameters for Item { fn self_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { @@ -847,7 +847,7 @@ impl TemplateDeclaration for Item { } } -impl TemplateDeclaration for ItemKind { +impl TemplateParameters for ItemKind { fn self_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { diff --git a/src/ir/named.rs b/src/ir/named.rs index 1af98a26..84a63379 100644 --- a/src/ir/named.rs +++ b/src/ir/named.rs @@ -127,10 +127,10 @@ //! [spa]: https://cs.au.dk/~amoeller/spa/spa.pdf use super::context::{BindgenContext, ItemId}; -use super::item::ItemSet; -use super::template::AsNamed; +use super::item::{Item, ItemSet}; +use super::template::{AsNamed, TemplateInstantiation, TemplateParameters}; use super::traversal::{EdgeKind, Trace}; -use super::ty::{TemplateDeclaration, TypeKind}; +use super::ty::TypeKind; use std::collections::{HashMap, HashSet}; use std::fmt; @@ -212,46 +212,37 @@ pub fn analyze<Analysis>(extra: Analysis::Extra) -> Analysis::Output /// An analysis that finds for each IR item its set of template parameters that /// it uses. /// -/// We use the following monotone constraint function: +/// We use the monotone constraint function `template_param_usage`, defined as +/// follows: /// -/// ```ignore -/// template_param_usage(v) = -/// self_template_param_usage(v) union -/// template_param_usage(w_0) union -/// template_param_usage(w_1) union -/// ... -/// template_param_usage(w_n) -/// ``` -/// -/// Where `v` has direct edges in the IR graph to each of `w_0`, `w_1`, -/// ..., `w_n` (for example, if `v` were a struct type and `x` and `y` -/// were the types of two of `v`'s fields). We ignore certain edges, such -/// as edges from a template declaration to its template parameters' -/// definitions for this analysis. If we didn't, then we would mistakenly -/// determine that ever template parameter is always used. -/// -/// Finally, `self_template_param_usage` is defined with the following cases: -/// -/// * If `T` is a template parameter: +/// * If `T` is a named template type parameter, it trivially uses itself: /// /// ```ignore -/// self_template_param_usage(T) = { T } +/// template_param_usage(T) = { T } /// ``` /// /// * If `inst` is a template instantiation, `inst.args` are the template -/// instantiation's template arguments, and `inst.decl` is the template -/// declaration being instantiated: +/// instantiation's template arguments, and `inst.def` is the template +/// definition being instantiated: /// /// ```ignore -/// self_template_param_usage(inst) = -/// { T: for T in inst.args, if T in template_param_usage(inst.decl) } +/// template_param_usage(inst) = +/// { T: for T in inst.args, if T in template_param_usage(inst.def) } /// ``` /// -/// * And for all other IR items, the result is the empty set: +/// * Finally, for all other IR item kinds, we use our lattice's `join` +/// operation: set union with each successor of the given item's template +/// parameter usage: /// /// ```ignore -/// self_template_param_usage(_) = { } +/// template_param_usage(v) = +/// union(template_param_usage(w) for w in successors(v)) /// ``` +/// +/// Note that we ignore certain edges in the graph, such as edges from a +/// template declaration to its template parameters' definitions for this +/// analysis. If we didn't, then we would mistakenly determine that ever +/// template parameter is always used. #[derive(Debug, Clone)] pub struct UsedTemplateParameters<'ctx, 'gen> where 'gen: 'ctx, @@ -307,6 +298,103 @@ impl<'ctx, 'gen> UsedTemplateParameters<'ctx, 'gen> { EdgeKind::Generic => false, } } + + fn take_this_id_usage_set(&mut self, this_id: ItemId) -> ItemSet { + self.used + .get_mut(&this_id) + .expect("Should have a set of used template params for every item \ + id") + .take() + .expect("Should maintain the invariant that all used template param \ + sets are `Some` upon entry of `constrain`") + } + + /// We say that blacklisted items use all of their template parameters. The + /// blacklisted type is most likely implemented explicitly by the user, + /// since it won't be in the generated bindings, and we don't know exactly + /// what they'll to with template parameters, but we can push the issue down + /// the line to them. + fn constrain_instantiation_of_blacklisted_template(&self, + used_by_this_id: &mut ItemSet, + instantiation: &TemplateInstantiation) { + trace!(" instantiation of blacklisted template, uses all template \ + arguments"); + + let args = instantiation.template_arguments() + .iter() + .filter_map(|a| a.as_named(self.ctx, &())); + used_by_this_id.extend(args); + } + + /// A template instantiation's concrete template argument is only used if + /// the template definition uses the corresponding template parameter. + fn constrain_instantiation(&self, + used_by_this_id: &mut ItemSet, + instantiation: &TemplateInstantiation) { + trace!(" template instantiation"); + + let decl = self.ctx.resolve_type(instantiation.template_definition()); + let args = instantiation.template_arguments(); + + let params = decl.self_template_params(self.ctx) + .unwrap_or(vec![]); + + let used_by_def = self.used[&instantiation.template_definition()] + .as_ref() + .unwrap(); + + for (arg, param) in args.iter().zip(params.iter()) { + trace!(" instantiation's argument {:?} is used if definition's \ + parameter {:?} is used", + arg, + param); + + if used_by_def.contains(param) { + trace!(" param is used by template definition"); + + let arg = arg.into_resolver() + .through_type_refs() + .through_type_aliases() + .resolve(self.ctx); + if let Some(named) = arg.as_named(self.ctx, &()) { + trace!(" arg is a type parameter, marking used"); + used_by_this_id.insert(named); + } + } + } + } + + /// The join operation on our lattice: the set union of all of this id's + /// successors. + fn constrain_join(&self, used_by_this_id: &mut ItemSet, item: &Item) { + trace!(" other item: join with successors' usage"); + + item.trace(self.ctx, &mut |sub_id, edge_kind| { + // Ignore ourselves, since union with ourself is a + // no-op. Ignore edges that aren't relevant to the + // analysis. Ignore edges to blacklisted items. + if sub_id == item.id() || + !Self::consider_edge(edge_kind) || + !self.whitelisted_items.contains(&sub_id) { + return; + } + + let used_by_sub_id = self.used[&sub_id] + .as_ref() + .expect("Because sub_id != id, and all used template \ + param sets other than id's are `Some`, \ + sub_id's used template param set should be \ + `Some`") + .iter() + .cloned(); + + trace!(" union with {:?}'s usage: {:?}", + sub_id, + used_by_sub_id.clone().collect::<Vec<_>>()); + + used_by_this_id.extend(used_by_sub_id); + }, &()); + } } impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> { @@ -322,12 +410,14 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> { for item in whitelisted_items.iter().cloned() { dependencies.entry(item).or_insert(vec![]); - used.insert(item, Some(ItemSet::new())); + used.entry(item).or_insert(Some(ItemSet::new())); { // We reverse our natural IR graph edges to find dependencies // between nodes. item.trace(ctx, &mut |sub_item, _| { + used.entry(sub_item).or_insert(Some(ItemSet::new())); + // We won't be generating code for items that aren't // whitelisted, so don't bother keeping track of their // template parameters. But isn't whitelisting the @@ -353,12 +443,17 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> { &TypeKind::TemplateInstantiation(ref inst) => { let decl = ctx.resolve_type(inst.template_definition()); let args = inst.template_arguments(); + // Although template definitions should always have // template parameters, there is a single exception: // opaque templates. Hence the unwrap_or. let params = decl.self_template_params(ctx) .unwrap_or(vec![]); + for (arg, param) in args.iter().zip(params.iter()) { + used.entry(*arg).or_insert(Some(ItemSet::new())); + used.entry(*param).or_insert(Some(ItemSet::new())); + dependencies.entry(*arg) .or_insert(vec![]) .push(*param); @@ -368,6 +463,28 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> { }); } + if cfg!(feature = "testing_only_extra_assertions") { + // Invariant: The `used` map has an entry for every whitelisted + // item, as well as all explicitly blacklisted items that are + // reachable from whitelisted items. + // + // (This is so that every item we call `constrain` on is guaranteed + // to have a set of template parameters, and we can allow + // blacklisted templates to use all of their parameters). + for item in whitelisted_items.iter() { + extra_assert!(used.contains_key(item)); + item.trace(ctx, &mut |sub_item, _| { + extra_assert!(used.contains_key(&sub_item)); + }, &()) + } + + // Invariant: the `dependencies` map has an entry for every + // whitelisted item. + for item in whitelisted_items.iter() { + extra_assert!(dependencies.contains_key(item)); + } + } + UsedTemplateParameters { ctx: ctx, used: used, @@ -389,13 +506,10 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> { // on other hash map entries. We *must* put it back into the hash map at // the end of this method. This allows us to side-step HashMap's lack of // an analog to slice::split_at_mut. - let mut used_by_this_id = self.used - .get_mut(&id) - .expect("Should have a set of used template params for every item \ - id") - .take() - .expect("Should maintain the invariant that all used template param \ - sets are `Some` upon entry of `constrain`"); + let mut used_by_this_id = self.take_this_id_usage_set(id); + + trace!("constrain {:?}", id); + trace!(" initially, used set is {:?}", used_by_this_id); let original_len = used_by_this_id.len(); @@ -404,70 +518,26 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> { match ty_kind { // Named template type parameters trivially use themselves. Some(&TypeKind::Named) => { + trace!(" named type, trivially uses itself"); used_by_this_id.insert(id); } - - // We say that blacklisted items use all of their template - // parameters. The blacklisted type is most likely implemented - // explicitly by the user, since it won't be in the generated - // bindings, and we don't know exactly what they'll to with template - // parameters, but we can push the issue down the line to them. - Some(&TypeKind::TemplateInstantiation(ref inst)) - if !self.whitelisted_items.contains(&inst.template_definition()) => { - let args = inst.template_arguments() - .iter() - .filter_map(|a| a.as_named(self.ctx, &())); - used_by_this_id.extend(args); - } - - // A template instantiation's concrete template argument is - // only used if the template declaration uses the - // corresponding template parameter. + // Template instantiations only use their template arguments if the + // template definition uses the corresponding template parameter. Some(&TypeKind::TemplateInstantiation(ref inst)) => { - let decl = self.ctx.resolve_type(inst.template_definition()); - let args = inst.template_arguments(); - - let params = decl.self_template_params(self.ctx) - .unwrap_or(vec![]); - - for (arg, param) in args.iter().zip(params.iter()) { - let used_by_def = self.used[&inst.template_definition()] - .as_ref() - .unwrap(); - if used_by_def.contains(param) { - if let Some(named) = arg.as_named(self.ctx, &()) { - used_by_this_id.insert(named); - } - } + if self.whitelisted_items.contains(&inst.template_definition()) { + self.constrain_instantiation(&mut used_by_this_id, inst); + } else { + self.constrain_instantiation_of_blacklisted_template(&mut used_by_this_id, + inst); } } - // Otherwise, add the union of each of its referent item's template // parameter usage. - _ => { - item.trace(self.ctx, &mut |sub_id, edge_kind| { - // Ignore ourselves, since union with ourself is a - // no-op. Ignore edges that aren't relevant to the - // analysis. Ignore edges to blacklisted items. - if sub_id == id || - !Self::consider_edge(edge_kind) || - !self.whitelisted_items.contains(&sub_id) { - return; - } - - let used_by_sub_id = self.used[&sub_id] - .as_ref() - .expect("Because sub_id != id, and all used template \ - param sets other than id's are `Some`, \ - sub_id's used template param set should be \ - `Some`") - .iter() - .cloned(); - used_by_this_id.extend(used_by_sub_id); - }, &()); - } + _ => self.constrain_join(&mut used_by_this_id, item), } + trace!(" finally, used set is {:?}", used_by_this_id); + let new_len = used_by_this_id.len(); assert!(new_len >= original_len, "This is the property that ensures this function is monotone -- \ @@ -486,6 +556,7 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> { { if let Some(edges) = self.dependencies.get(&item) { for item in edges { + trace!("enqueue {:?} into worklist", item); f(*item); } } diff --git a/src/ir/template.rs b/src/ir/template.rs index 3484a9c6..d2996330 100644 --- a/src/ir/template.rs +++ b/src/ir/template.rs @@ -29,12 +29,152 @@ use super::context::{BindgenContext, ItemId}; use super::derive::{CanDeriveCopy, CanDeriveDebug}; -use super::item::Item; +use super::item::{Item, ItemAncestors}; use super::layout::Layout; use super::traversal::{EdgeKind, Trace, Tracer}; use clang; use parse::ClangItemParser; +/// Template declaration (and such declaration's template parameters) related +/// methods. +/// +/// This trait's methods distinguish between `None` and `Some([])` for +/// declarations that are not templates and template declarations with zero +/// parameters, in general. +/// +/// Consider this example: +/// +/// ```c++ +/// template <typename T, typename U> +/// class Foo { +/// T use_of_t; +/// U use_of_u; +/// +/// template <typename V> +/// using Bar = V*; +/// +/// class Inner { +/// T x; +/// U y; +/// Bar<int> z; +/// }; +/// +/// template <typename W> +/// class Lol { +/// // No use of W, but here's a use of T. +/// T t; +/// }; +/// +/// template <typename X> +/// class Wtf { +/// // X is not used because W is not used. +/// Lol<X> lololol; +/// }; +/// }; +/// +/// class Qux { +/// int y; +/// }; +/// ``` +/// +/// The following table depicts the results of each trait method when invoked on +/// each of the declarations above: +/// +/// +------+----------------------+--------------------------+------------------------+---- +/// |Decl. | self_template_params | num_self_template_params | all_template_parameters| ... +/// +------+----------------------+--------------------------+------------------------+---- +/// |Foo | Some([T, U]) | Some(2) | Some([T, U]) | ... +/// |Bar | Some([V]) | Some(1) | Some([T, U, V]) | ... +/// |Inner | None | None | Some([T, U]) | ... +/// |Lol | Some([W]) | Some(1) | Some([T, U, W]) | ... +/// |Wtf | Some([X]) | Some(1) | Some([T, U, X]) | ... +/// |Qux | None | None | None | ... +/// +------+----------------------+--------------------------+------------------------+---- +/// +/// ----+------+-----+----------------------+ +/// ... |Decl. | ... | used_template_params | +/// ----+------+-----+----------------------+ +/// ... |Foo | ... | Some([T, U]) | +/// ... |Bar | ... | Some([V]) | +/// ... |Inner | ... | None | +/// ... |Lol | ... | Some([T]) | +/// ... |Wtf | ... | Some([T]) | +/// ... |Qux | ... | None | +/// ----+------+-----+----------------------+ +pub trait TemplateParameters { + /// 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 as well as template-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 self_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_self_template_params(&self, ctx: &BindgenContext) -> Option<usize> { + self.self_template_params(ctx).map(|params| params.len()) + } + + /// Get the complete set of template parameters that can affect this + /// declaration. + /// + /// Note that this item doesn't need to be a template declaration itself for + /// `Some` to be returned here (in contrast to `self_template_params`). If + /// this item is a member of a template declaration, then the parent's + /// template parameters are included here. + /// + /// In the example above, `Inner` depends on both of the `T` and `U` type + /// parameters, even though it is not itself a template declaration and + /// therefore has no type parameters itself. Perhaps it helps to think about + /// how we would fully reference such a member type in C++: + /// `Foo<int,char>::Inner`. `Foo` *must* be instantiated with template + /// arguments before we can gain access to the `Inner` member type. + fn all_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> + where Self: ItemAncestors, + { + let each_self_params: Vec<Vec<_>> = self.ancestors(ctx) + .filter_map(|id| id.self_template_params(ctx)) + .collect(); + if each_self_params.is_empty() { + None + } else { + Some(each_self_params.into_iter() + .rev() + .flat_map(|params| params) + .collect()) + } + } + + /// Get only the set of template parameters that this item uses. This is a + /// subset of `all_template_params` and does not necessarily contain any of + /// `self_template_params`. + fn used_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> + where Self: AsRef<ItemId>, + { + assert!(ctx.in_codegen_phase(), + "template parameter usage is not computed until codegen"); + + let id = *self.as_ref(); + ctx.resolve_item(id) + .all_template_params(ctx) + .map(|all_params| { + all_params.into_iter() + .filter(|p| ctx.uses_template_parameter(id, *p)) + .collect() + }) + } +} + /// A trait for things which may or may not be a named template type parameter. pub trait AsNamed { /// Any extra information the implementor might need to make this decision. diff --git a/src/ir/ty.rs b/src/ir/ty.rs index b47e2bb2..9fabf980 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -7,10 +7,10 @@ use super::dot::DotAttributes; use super::enum_ty::Enum; use super::function::FunctionSig; use super::int::IntKind; -use super::item::{Item, ItemAncestors}; +use super::item::Item; use super::layout::{Layout, Opaque}; use super::objc::ObjCInterface; -use super::template::{AsNamed, TemplateInstantiation}; +use super::template::{AsNamed, TemplateInstantiation, TemplateParameters}; use super::traversal::{EdgeKind, Trace, Tracer}; use clang::{self, Cursor}; use parse::{ClangItemParser, ParseError, ParseResult}; @@ -18,146 +18,6 @@ use std::cell::Cell; use std::io; use std::mem; -/// Template declaration (and such declaration's template parameters) related -/// methods. -/// -/// This trait's methods distinguish between `None` and `Some([])` for -/// declarations that are not templates and template declarations with zero -/// parameters, in general. -/// -/// Consider this example: -/// -/// ```c++ -/// template <typename T, typename U> -/// class Foo { -/// T use_of_t; -/// U use_of_u; -/// -/// template <typename V> -/// using Bar = V*; -/// -/// class Inner { -/// T x; -/// U y; -/// Bar<int> z; -/// }; -/// -/// template <typename W> -/// class Lol { -/// // No use of W, but here's a use of T. -/// T t; -/// }; -/// -/// template <typename X> -/// class Wtf { -/// // X is not used because W is not used. -/// Lol<X> lololol; -/// }; -/// }; -/// -/// class Qux { -/// int y; -/// }; -/// ``` -/// -/// The following table depicts the results of each trait method when invoked on -/// each of the declarations above: -/// -/// +------+----------------------+--------------------------+------------------------+---- -/// |Decl. | self_template_params | num_self_template_params | all_template_parameters| ... -/// +------+----------------------+--------------------------+------------------------+---- -/// |Foo | Some([T, U]) | Some(2) | Some([T, U]) | ... -/// |Bar | Some([V]) | Some(1) | Some([T, U, V]) | ... -/// |Inner | None | None | Some([T, U]) | ... -/// |Lol | Some([W]) | Some(1) | Some([T, U, W]) | ... -/// |Wtf | Some([X]) | Some(1) | Some([T, U, X]) | ... -/// |Qux | None | None | None | ... -/// +------+----------------------+--------------------------+------------------------+---- -/// -/// ----+------+-----+----------------------+ -/// ... |Decl. | ... | used_template_params | -/// ----+------+-----+----------------------+ -/// ... |Foo | ... | Some([T, U]) | -/// ... |Bar | ... | Some([V]) | -/// ... |Inner | ... | None | -/// ... |Lol | ... | Some([T]) | -/// ... |Wtf | ... | Some([T]) | -/// ... |Qux | ... | None | -/// ----+------+-----+----------------------+ -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 as well as template-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 self_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_self_template_params(&self, ctx: &BindgenContext) -> Option<usize> { - self.self_template_params(ctx).map(|params| params.len()) - } - - /// Get the complete set of template parameters that can affect this - /// declaration. - /// - /// Note that this item doesn't need to be a template declaration itself for - /// `Some` to be returned here (in contrast to `self_template_params`). If - /// this item is a member of a template declaration, then the parent's - /// template parameters are included here. - /// - /// In the example above, `Inner` depends on both of the `T` and `U` type - /// parameters, even though it is not itself a template declaration and - /// therefore has no type parameters itself. Perhaps it helps to think about - /// how we would fully reference such a member type in C++: - /// `Foo<int,char>::Inner`. `Foo` *must* be instantiated with template - /// arguments before we can gain access to the `Inner` member type. - fn all_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> - where Self: ItemAncestors, - { - let each_self_params: Vec<Vec<_>> = self.ancestors(ctx) - .filter_map(|id| id.self_template_params(ctx)) - .collect(); - if each_self_params.is_empty() { - None - } else { - Some(each_self_params.into_iter() - .rev() - .flat_map(|params| params) - .collect()) - } - } - - /// Get only the set of template parameters that this item uses. This is a - /// subset of `all_template_params` and does not necessarily contain any of - /// `self_template_params`. - fn used_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> - where Self: AsRef<ItemId>, - { - assert!(ctx.in_codegen_phase(), - "template parameter usage is not computed until codegen"); - - let id = *self.as_ref(); - ctx.resolve_item(id) - .all_template_params(ctx) - .map(|all_params| { - all_params.into_iter() - .filter(|p| ctx.uses_template_parameter(id, *p)) - .collect() - }) - } -} - /// The base representation of a type in bindgen. /// /// A type has an optional name, which if present cannot be empty, a `layout` @@ -623,7 +483,7 @@ fn is_invalid_named_type_empty_name() { } -impl TemplateDeclaration for Type { +impl TemplateParameters for Type { fn self_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { @@ -631,7 +491,7 @@ impl TemplateDeclaration for Type { } } -impl TemplateDeclaration for TypeKind { +impl TemplateParameters for TypeKind { fn self_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { @@ -235,11 +235,11 @@ impl Builder { if !self.options.whitelist_recursively { output_vector.push("--no-recursive-whitelist".into()); } - + if self.options.objc_extern_crate { output_vector.push("--objc-extern-crate".into()); } - + if self.options.builtins { output_vector.push("--builtins".into()); } @@ -249,15 +249,6 @@ impl Builder { output_vector.push(prefix.clone()); } - self.options - .clang_args - .iter() - .map(|item| { - output_vector.push("--clang-args".into()); - output_vector.push(item.trim_left_matches("^").trim_right_matches("$").into()); - }) - .count(); - if let Some(ref dummy) = self.options.dummy_uses { output_vector.push("--dummy-uses".into()); output_vector.push(dummy.clone()); @@ -316,7 +307,7 @@ impl Builder { if self.options.codegen_config.destructors { options.push("destructors".into()); } - + output_vector.push(options.join(",")); if !self.options.codegen_config.methods { @@ -410,6 +401,18 @@ impl Builder { }) .count(); + if !self.options.clang_args.is_empty() { + output_vector.push("--".into()); + self.options + .clang_args + .iter() + .cloned() + .map(|item| { + output_vector.push(item); + }) + .count(); + } + output_vector } @@ -1243,7 +1246,7 @@ fn commandline_flag_unit_test_function() { assert!(test_cases.iter().all(|ref x| command_line_flags.contains(x)) ); - //Test 2 + //Test 2 let bindings = ::builder().header("input_header") .whitelisted_type("Distinct_Type") .whitelisted_function("safe_function"); diff --git a/src/uses.rs b/src/uses.rs index 0799011b..fee2be24 100644 --- a/src/uses.rs +++ b/src/uses.rs @@ -37,7 +37,7 @@ use ir::context::BindgenContext; use ir::item::{Item, ItemAncestors, ItemCanonicalName}; -use ir::ty::TemplateDeclaration; +use ir::template::TemplateParameters; use std::io; // Like `canonical_path`, except we always take namespaces into account, ignore diff --git a/tests/expectations/tests/issue-638-stylo-cannot-find-T-in-this-scope.rs b/tests/expectations/tests/issue-638-stylo-cannot-find-T-in-this-scope.rs new file mode 100644 index 00000000..4ff0b5bc --- /dev/null +++ b/tests/expectations/tests/issue-638-stylo-cannot-find-T-in-this-scope.rs @@ -0,0 +1,23 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct RefPtr<T> { + pub use_of_t: T, +} +impl <T> Default for RefPtr<T> { + fn default() -> Self { unsafe { ::std::mem::zeroed() } } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct UsesRefPtrWithAliasedTypeParam<U> { + pub member: RefPtr<UsesRefPtrWithAliasedTypeParam_V<U>>, +} +pub type UsesRefPtrWithAliasedTypeParam_V<U> = U; +impl <U> Default for UsesRefPtrWithAliasedTypeParam<U> { + fn default() -> Self { unsafe { ::std::mem::zeroed() } } +} diff --git a/tests/headers/issue-638-stylo-cannot-find-T-in-this-scope.hpp b/tests/headers/issue-638-stylo-cannot-find-T-in-this-scope.hpp new file mode 100644 index 00000000..13b656e1 --- /dev/null +++ b/tests/headers/issue-638-stylo-cannot-find-T-in-this-scope.hpp @@ -0,0 +1,12 @@ +// bindgen-flags: -- -std=c++14 + +template <class T> +class RefPtr { + T use_of_t; +}; + +template <typename U> +class UsesRefPtrWithAliasedTypeParam { + using V = U; + RefPtr<V> member; +}; diff --git a/tests/test-one.sh b/tests/test-one.sh index 6475df7f..d1950eb9 100755 --- a/tests/test-one.sh +++ b/tests/test-one.sh @@ -31,6 +31,14 @@ TEST_BINDINGS_BINARY=$(mktemp -t bindings.XXXXX) dot -Tpng ir.dot -o ir.png +echo "=== Input header ========================================================" +cat "$TEST" + +echo "=== Generated bindings ==================================================" +cat "$BINDINGS" + +echo "=== Building bindings ===================================================" rustc --test -o "$TEST_BINDINGS_BINARY" "$BINDINGS" +echo "=== Testing bindings ====================================================" "$TEST_BINDINGS_BINARY" |