| 1 | //! Template declaration and instantiation related things. |
| 2 | //! |
| 3 | //! The nomenclature surrounding templates is often confusing, so here are a few |
| 4 | //! brief definitions: |
| 5 | //! |
| 6 | //! * "Template definition": a class/struct/alias/function definition that takes |
| 7 | //! generic template parameters. For example: |
| 8 | //! |
| 9 | //! ```c++ |
| 10 | //! template<typename T> |
| 11 | //! class List<T> { |
| 12 | //! // ... |
| 13 | //! }; |
| 14 | //! ``` |
| 15 | //! |
| 16 | //! * "Template instantiation": an instantiation is a use of a template with |
| 17 | //! concrete template arguments. For example, `List<int>`. |
| 18 | //! |
| 19 | //! * "Template specialization": an alternative template definition providing a |
| 20 | //! custom definition for instantiations with the matching template |
| 21 | //! arguments. This C++ feature is unsupported by bindgen. For example: |
| 22 | //! |
| 23 | //! ```c++ |
| 24 | //! template<> |
| 25 | //! class List<int> { |
| 26 | //! // Special layout for int lists... |
| 27 | //! }; |
| 28 | //! ``` |
| 29 | |
| 30 | use super::context::{BindgenContext, ItemId, TypeId}; |
| 31 | use super::item::{IsOpaque, Item, ItemAncestors}; |
| 32 | use super::traversal::{EdgeKind, Trace, Tracer}; |
| 33 | use crate::clang; |
| 34 | |
| 35 | /// Template declaration (and such declaration's template parameters) related |
| 36 | /// methods. |
| 37 | /// |
| 38 | /// This trait's methods distinguish between `None` and `Some([])` for |
| 39 | /// declarations that are not templates and template declarations with zero |
| 40 | /// parameters, in general. |
| 41 | /// |
| 42 | /// Consider this example: |
| 43 | /// |
| 44 | /// ```c++ |
| 45 | /// template <typename T, typename U> |
| 46 | /// class Foo { |
| 47 | /// T use_of_t; |
| 48 | /// U use_of_u; |
| 49 | /// |
| 50 | /// template <typename V> |
| 51 | /// using Bar = V*; |
| 52 | /// |
| 53 | /// class Inner { |
| 54 | /// T x; |
| 55 | /// U y; |
| 56 | /// Bar<int> z; |
| 57 | /// }; |
| 58 | /// |
| 59 | /// template <typename W> |
| 60 | /// class Lol { |
| 61 | /// // No use of W, but here's a use of T. |
| 62 | /// T t; |
| 63 | /// }; |
| 64 | /// |
| 65 | /// template <typename X> |
| 66 | /// class Wtf { |
| 67 | /// // X is not used because W is not used. |
| 68 | /// Lol<X> lololol; |
| 69 | /// }; |
| 70 | /// }; |
| 71 | /// |
| 72 | /// class Qux { |
| 73 | /// int y; |
| 74 | /// }; |
| 75 | /// ``` |
| 76 | /// |
| 77 | /// The following table depicts the results of each trait method when invoked on |
| 78 | /// each of the declarations above: |
| 79 | /// |
| 80 | /// +------+----------------------+--------------------------+-------------------------+---- |
| 81 | /// |Decl. | self_template_params | num_self_template_params | all_template_parameters | ... |
| 82 | /// +------+----------------------+--------------------------+-------------------------+---- |
| 83 | /// |Foo | T, U | 2 | T, U | ... |
| 84 | /// |Bar | V | 1 | T, U, V | ... |
| 85 | /// |Inner | | 0 | T, U | ... |
| 86 | /// |Lol | W | 1 | T, U, W | ... |
| 87 | /// |Wtf | X | 1 | T, U, X | ... |
| 88 | /// |Qux | | 0 | | ... |
| 89 | /// +------+----------------------+--------------------------+------------------------+---- |
| 90 | /// |
| 91 | /// ----+------+-----+----------------------+ |
| 92 | /// ... |Decl. | ... | used_template_params | |
| 93 | /// ----+------+-----+----------------------+ |
| 94 | /// ... |Foo | ... | T, U | |
| 95 | /// ... |Bar | ... | V | |
| 96 | /// ... |Inner | ... | | |
| 97 | /// ... |Lol | ... | T | |
| 98 | /// ... |Wtf | ... | T | |
| 99 | /// ... |Qux | ... | | |
| 100 | /// ----+------+-----+----------------------+ |
| 101 | pub(crate) trait TemplateParameters: Sized { |
| 102 | /// Get the set of `ItemId`s that make up this template declaration's free |
| 103 | /// template parameters. |
| 104 | /// |
| 105 | /// Note that these might *not* all be named types: C++ allows |
| 106 | /// constant-value template parameters as well as template-template |
| 107 | /// parameters. Of course, Rust does not allow generic parameters to be |
| 108 | /// anything but types, so we must treat them as opaque, and avoid |
| 109 | /// instantiating them. |
| 110 | fn self_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId>; |
| 111 | |
| 112 | /// Get the number of free template parameters this template declaration |
| 113 | /// has. |
| 114 | fn num_self_template_params(&self, ctx: &BindgenContext) -> usize { |
| 115 | self.self_template_params(ctx).len() |
| 116 | } |
| 117 | |
| 118 | /// Get the complete set of template parameters that can affect this |
| 119 | /// declaration. |
| 120 | /// |
| 121 | /// Note that this item doesn't need to be a template declaration itself for |
| 122 | /// `Some` to be returned here (in contrast to `self_template_params`). If |
| 123 | /// this item is a member of a template declaration, then the parent's |
| 124 | /// template parameters are included here. |
| 125 | /// |
| 126 | /// In the example above, `Inner` depends on both of the `T` and `U` type |
| 127 | /// parameters, even though it is not itself a template declaration and |
| 128 | /// therefore has no type parameters itself. Perhaps it helps to think about |
| 129 | /// how we would fully reference such a member type in C++: |
| 130 | /// `Foo<int,char>::Inner`. `Foo` *must* be instantiated with template |
| 131 | /// arguments before we can gain access to the `Inner` member type. |
| 132 | fn all_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId> |
| 133 | where |
| 134 | Self: ItemAncestors, |
| 135 | { |
| 136 | let mut ancestors: Vec<_> = self.ancestors(ctx).collect(); |
| 137 | ancestors.reverse(); |
| 138 | ancestors |
| 139 | .into_iter() |
| 140 | .flat_map(|id| id.self_template_params(ctx).into_iter()) |
| 141 | .collect() |
| 142 | } |
| 143 | |
| 144 | /// Get only the set of template parameters that this item uses. This is a |
| 145 | /// subset of `all_template_params` and does not necessarily contain any of |
| 146 | /// `self_template_params`. |
| 147 | fn used_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId> |
| 148 | where |
| 149 | Self: AsRef<ItemId>, |
| 150 | { |
| 151 | assert!( |
| 152 | ctx.in_codegen_phase(), |
| 153 | "template parameter usage is not computed until codegen" |
| 154 | ); |
| 155 | |
| 156 | let id = *self.as_ref(); |
| 157 | ctx.resolve_item(id) |
| 158 | .all_template_params(ctx) |
| 159 | .into_iter() |
| 160 | .filter(|p| ctx.uses_template_parameter(id, *p)) |
| 161 | .collect() |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | /// A trait for things which may or may not be a named template type parameter. |
| 166 | pub(crate) trait AsTemplateParam { |
| 167 | /// Any extra information the implementor might need to make this decision. |
| 168 | type Extra; |
| 169 | |
| 170 | /// Convert this thing to the item ID of a named template type parameter. |
| 171 | fn as_template_param( |
| 172 | &self, |
| 173 | ctx: &BindgenContext, |
| 174 | extra: &Self::Extra, |
| 175 | ) -> Option<TypeId>; |
| 176 | |
| 177 | /// Is this a named template type parameter? |
| 178 | fn is_template_param( |
| 179 | &self, |
| 180 | ctx: &BindgenContext, |
| 181 | extra: &Self::Extra, |
| 182 | ) -> bool { |
| 183 | self.as_template_param(ctx, extra).is_some() |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | /// A concrete instantiation of a generic template. |
| 188 | #[derive (Clone, Debug)] |
| 189 | pub(crate) struct TemplateInstantiation { |
| 190 | /// The template definition which this is instantiating. |
| 191 | definition: TypeId, |
| 192 | /// The concrete template arguments, which will be substituted in the |
| 193 | /// definition for the generic template parameters. |
| 194 | args: Vec<TypeId>, |
| 195 | } |
| 196 | |
| 197 | impl TemplateInstantiation { |
| 198 | /// Construct a new template instantiation from the given parts. |
| 199 | pub(crate) fn new<I>(definition: TypeId, args: I) -> TemplateInstantiation |
| 200 | where |
| 201 | I: IntoIterator<Item = TypeId>, |
| 202 | { |
| 203 | TemplateInstantiation { |
| 204 | definition, |
| 205 | args: args.into_iter().collect(), |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | /// Get the template definition for this instantiation. |
| 210 | pub(crate) fn template_definition(&self) -> TypeId { |
| 211 | self.definition |
| 212 | } |
| 213 | |
| 214 | /// Get the concrete template arguments used in this instantiation. |
| 215 | pub(crate) fn template_arguments(&self) -> &[TypeId] { |
| 216 | &self.args[..] |
| 217 | } |
| 218 | |
| 219 | /// Parse a `TemplateInstantiation` from a clang `Type`. |
| 220 | pub(crate) fn from_ty( |
| 221 | ty: &clang::Type, |
| 222 | ctx: &mut BindgenContext, |
| 223 | ) -> Option<TemplateInstantiation> { |
| 224 | use clang_sys::*; |
| 225 | |
| 226 | let template_args = ty.template_args().map_or(vec![], |args| match ty |
| 227 | .canonical_type() |
| 228 | .template_args() |
| 229 | { |
| 230 | Some(canonical_args) => { |
| 231 | let arg_count = args.len(); |
| 232 | args.chain(canonical_args.skip(arg_count)) |
| 233 | .filter(|t| t.kind() != CXType_Invalid) |
| 234 | .map(|t| { |
| 235 | Item::from_ty_or_ref(t, t.declaration(), None, ctx) |
| 236 | }) |
| 237 | .collect() |
| 238 | } |
| 239 | None => args |
| 240 | .filter(|t| t.kind() != CXType_Invalid) |
| 241 | .map(|t| Item::from_ty_or_ref(t, t.declaration(), None, ctx)) |
| 242 | .collect(), |
| 243 | }); |
| 244 | |
| 245 | let declaration = ty.declaration(); |
| 246 | let definition = if declaration.kind() == CXCursor_TypeAliasTemplateDecl |
| 247 | { |
| 248 | Some(declaration) |
| 249 | } else { |
| 250 | declaration.specialized().or_else(|| { |
| 251 | let mut template_ref = None; |
| 252 | ty.declaration().visit(|child| { |
| 253 | if child.kind() == CXCursor_TemplateRef { |
| 254 | template_ref = Some(child); |
| 255 | return CXVisit_Break; |
| 256 | } |
| 257 | |
| 258 | // Instantiations of template aliases might have the |
| 259 | // TemplateRef to the template alias definition arbitrarily |
| 260 | // deep, so we need to recurse here and not only visit |
| 261 | // direct children. |
| 262 | CXChildVisit_Recurse |
| 263 | }); |
| 264 | |
| 265 | template_ref.and_then(|cur| cur.referenced()) |
| 266 | }) |
| 267 | }; |
| 268 | |
| 269 | let definition = match definition { |
| 270 | Some(def) => def, |
| 271 | None => { |
| 272 | if !ty.declaration().is_builtin() { |
| 273 | warn!( |
| 274 | "Could not find template definition for template \ |
| 275 | instantiation" |
| 276 | ); |
| 277 | } |
| 278 | return None; |
| 279 | } |
| 280 | }; |
| 281 | |
| 282 | let template_definition = |
| 283 | Item::from_ty_or_ref(definition.cur_type(), definition, None, ctx); |
| 284 | |
| 285 | Some(TemplateInstantiation::new( |
| 286 | template_definition, |
| 287 | template_args, |
| 288 | )) |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | impl IsOpaque for TemplateInstantiation { |
| 293 | type Extra = Item; |
| 294 | |
| 295 | /// Is this an opaque template instantiation? |
| 296 | fn is_opaque(&self, ctx: &BindgenContext, item: &Item) -> bool { |
| 297 | if self.template_definition().is_opaque(ctx, &()) { |
| 298 | return true; |
| 299 | } |
| 300 | |
| 301 | // TODO(#774): This doesn't properly handle opaque instantiations where |
| 302 | // an argument is itself an instantiation because `canonical_name` does |
| 303 | // not insert the template arguments into the name, ie it for nested |
| 304 | // template arguments it creates "Foo" instead of "Foo<int>". The fully |
| 305 | // correct fix is to make `canonical_{name,path}` include template |
| 306 | // arguments properly. |
| 307 | |
| 308 | let mut path = item.path_for_allowlisting(ctx).clone(); |
| 309 | let args: Vec<_> = self |
| 310 | .template_arguments() |
| 311 | .iter() |
| 312 | .map(|arg| { |
| 313 | let arg_path = |
| 314 | ctx.resolve_item(*arg).path_for_allowlisting(ctx); |
| 315 | arg_path[1..].join("::" ) |
| 316 | }) |
| 317 | .collect(); |
| 318 | { |
| 319 | let last = path.last_mut().unwrap(); |
| 320 | last.push('<' ); |
| 321 | last.push_str(&args.join(", " )); |
| 322 | last.push('>' ); |
| 323 | } |
| 324 | |
| 325 | ctx.opaque_by_name(&path) |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | impl Trace for TemplateInstantiation { |
| 330 | type Extra = (); |
| 331 | |
| 332 | fn trace<T>(&self, _ctx: &BindgenContext, tracer: &mut T, _: &()) |
| 333 | where |
| 334 | T: Tracer, |
| 335 | { |
| 336 | tracer |
| 337 | .visit_kind(self.definition.into(), kind:EdgeKind::TemplateDeclaration); |
| 338 | for arg: &TypeId in self.template_arguments() { |
| 339 | tracer.visit_kind(item:arg.into(), kind:EdgeKind::TemplateArgument); |
| 340 | } |
| 341 | } |
| 342 | } |
| 343 | |