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