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