| //! Template declaration and instantiation related things. |
| //! |
| //! The nomenclature surrounding templates is often confusing, so here are a few |
| //! brief definitions: |
| //! |
| //! * "Template definition": a class/struct/alias/function definition that takes |
| //! generic template parameters. For example: |
| //! |
| //! ```c++ |
| //! template<typename T> |
| //! class List<T> { |
| //! // ... |
| //! }; |
| //! ``` |
| //! |
| //! * "Template instantiation": an instantiation is a use of a template with |
| //! concrete template arguments. For example, `List<int>`. |
| //! |
| //! * "Template specialization": an alternative template definition providing a |
| //! custom definition for instantiations with the matching template |
| //! arguments. This C++ feature is unsupported by bindgen. For example: |
| //! |
| //! ```c++ |
| //! template<> |
| //! class List<int> { |
| //! // Special layout for int lists... |
| //! }; |
| //! ``` |
| |
| use super::context::{BindgenContext, ItemId, TypeId}; |
| use super::item::{IsOpaque, Item, ItemAncestors}; |
| use super::traversal::{EdgeKind, Trace, Tracer}; |
| use crate::clang; |
| use crate::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 | [T, U] | 2 | [T, U] | ... |
| /// |Bar | [V] | 1 | [T, U, V] | ... |
| /// |Inner | [] | 0 | [T, U] | ... |
| /// |Lol | [W] | 1 | [T, U, W] | ... |
| /// |Wtf | [X] | 1 | [T, U, X] | ... |
| /// |Qux | [] | 0 | [] | ... |
| /// +------+----------------------+--------------------------+------------------------+---- |
| /// |
| /// ----+------+-----+----------------------+ |
| /// ... |Decl. | ... | used_template_params | |
| /// ----+------+-----+----------------------+ |
| /// ... |Foo | ... | [T, U] | |
| /// ... |Bar | ... | [V] | |
| /// ... |Inner | ... | [] | |
| /// ... |Lol | ... | [T] | |
| /// ... |Wtf | ... | [T] | |
| /// ... |Qux | ... | [] | |
| /// ----+------+-----+----------------------+ |
| pub trait TemplateParameters: Sized { |
| /// 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) -> Vec<TypeId>; |
| |
| /// Get the number of free template parameters this template declaration |
| /// has. |
| fn num_self_template_params(&self, ctx: &BindgenContext) -> usize { |
| self.self_template_params(ctx).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) -> Vec<TypeId> |
| where |
| Self: ItemAncestors, |
| { |
| let mut ancestors: Vec<_> = self.ancestors(ctx).collect(); |
| ancestors.reverse(); |
| ancestors |
| .into_iter() |
| .flat_map(|id| id.self_template_params(ctx).into_iter()) |
| .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) -> Vec<TypeId> |
| 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) |
| .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 AsTemplateParam { |
| /// Any extra information the implementor might need to make this decision. |
| type Extra; |
| |
| /// Convert this thing to the item id of a named template type parameter. |
| fn as_template_param( |
| &self, |
| ctx: &BindgenContext, |
| extra: &Self::Extra, |
| ) -> Option<TypeId>; |
| |
| /// Is this a named template type parameter? |
| fn is_template_param( |
| &self, |
| ctx: &BindgenContext, |
| extra: &Self::Extra, |
| ) -> bool { |
| self.as_template_param(ctx, extra).is_some() |
| } |
| } |
| |
| /// A concrete instantiation of a generic template. |
| #[derive(Clone, Debug)] |
| pub struct TemplateInstantiation { |
| /// The template definition which this is instantiating. |
| definition: TypeId, |
| /// The concrete template arguments, which will be substituted in the |
| /// definition for the generic template parameters. |
| args: Vec<TypeId>, |
| } |
| |
| impl TemplateInstantiation { |
| /// Construct a new template instantiation from the given parts. |
| pub fn new<I>(definition: TypeId, args: I) -> TemplateInstantiation |
| where |
| I: IntoIterator<Item = TypeId>, |
| { |
| TemplateInstantiation { |
| definition, |
| args: args.into_iter().collect(), |
| } |
| } |
| |
| /// Get the template definition for this instantiation. |
| pub fn template_definition(&self) -> TypeId { |
| self.definition |
| } |
| |
| /// Get the concrete template arguments used in this instantiation. |
| pub fn template_arguments(&self) -> &[TypeId] { |
| &self.args[..] |
| } |
| |
| /// Parse a `TemplateInstantiation` from a clang `Type`. |
| pub fn from_ty( |
| ty: &clang::Type, |
| ctx: &mut BindgenContext, |
| ) -> Option<TemplateInstantiation> { |
| use clang_sys::*; |
| |
| let template_args = ty.template_args().map_or(vec![], |args| match ty |
| .canonical_type() |
| .template_args() |
| { |
| Some(canonical_args) => { |
| let arg_count = args.len(); |
| args.chain(canonical_args.skip(arg_count)) |
| .filter(|t| t.kind() != CXType_Invalid) |
| .map(|t| { |
| Item::from_ty_or_ref(t, t.declaration(), None, ctx) |
| }) |
| .collect() |
| } |
| None => args |
| .filter(|t| t.kind() != CXType_Invalid) |
| .map(|t| Item::from_ty_or_ref(t, t.declaration(), None, ctx)) |
| .collect(), |
| }); |
| |
| let declaration = ty.declaration(); |
| let definition = if declaration.kind() == CXCursor_TypeAliasTemplateDecl |
| { |
| Some(declaration) |
| } else { |
| declaration.specialized().or_else(|| { |
| let mut template_ref = None; |
| ty.declaration().visit(|child| { |
| if child.kind() == CXCursor_TemplateRef { |
| template_ref = Some(child); |
| return CXVisit_Break; |
| } |
| |
| // Instantiations of template aliases might have the |
| // TemplateRef to the template alias definition arbitrarily |
| // deep, so we need to recurse here and not only visit |
| // direct children. |
| CXChildVisit_Recurse |
| }); |
| |
| template_ref.and_then(|cur| cur.referenced()) |
| }) |
| }; |
| |
| let definition = match definition { |
| Some(def) => def, |
| None => { |
| if !ty.declaration().is_builtin() { |
| warn!( |
| "Could not find template definition for template \ |
| instantiation" |
| ); |
| } |
| return None; |
| } |
| }; |
| |
| let template_definition = |
| Item::from_ty_or_ref(definition.cur_type(), definition, None, ctx); |
| |
| Some(TemplateInstantiation::new( |
| template_definition, |
| template_args, |
| )) |
| } |
| } |
| |
| impl IsOpaque for TemplateInstantiation { |
| type Extra = Item; |
| |
| /// Is this an opaque template instantiation? |
| fn is_opaque(&self, ctx: &BindgenContext, item: &Item) -> bool { |
| if self.template_definition().is_opaque(ctx, &()) { |
| return true; |
| } |
| |
| // TODO(#774): This doesn't properly handle opaque instantiations where |
| // an argument is itself an instantiation because `canonical_name` does |
| // not insert the template arguments into the name, ie it for nested |
| // template arguments it creates "Foo" instead of "Foo<int>". The fully |
| // correct fix is to make `canonical_{name,path}` include template |
| // arguments properly. |
| |
| let mut path = item.path_for_allowlisting(ctx).clone(); |
| let args: Vec<_> = self |
| .template_arguments() |
| .iter() |
| .map(|arg| { |
| let arg_path = |
| ctx.resolve_item(*arg).path_for_allowlisting(ctx); |
| arg_path[1..].join("::") |
| }) |
| .collect(); |
| { |
| let last = path.last_mut().unwrap(); |
| last.push('<'); |
| last.push_str(&args.join(", ")); |
| last.push('>'); |
| } |
| |
| ctx.opaque_by_name(&path) |
| } |
| } |
| |
| impl Trace for TemplateInstantiation { |
| type Extra = (); |
| |
| fn trace<T>(&self, _ctx: &BindgenContext, tracer: &mut T, _: &()) |
| where |
| T: Tracer, |
| { |
| tracer |
| .visit_kind(self.definition.into(), EdgeKind::TemplateDeclaration); |
| for arg in self.template_arguments() { |
| tracer.visit_kind(arg.into(), EdgeKind::TemplateArgument); |
| } |
| } |
| } |