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 | |