• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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.
self_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId>110     fn self_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId>;
111 
112     /// Get the number of free template parameters this template declaration
113     /// has.
num_self_template_params(&self, ctx: &BindgenContext) -> usize114     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.
all_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId> where Self: ItemAncestors,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`.
used_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId> where Self: AsRef<ItemId>,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 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.
as_template_param( &self, ctx: &BindgenContext, extra: &Self::Extra, ) -> Option<TypeId>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?
is_template_param( &self, ctx: &BindgenContext, extra: &Self::Extra, ) -> bool178     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 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.
new<I>(definition: TypeId, args: I) -> TemplateInstantiation where I: IntoIterator<Item = TypeId>,199     pub 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.
template_definition(&self) -> TypeId210     pub fn template_definition(&self) -> TypeId {
211         self.definition
212     }
213 
214     /// Get the concrete template arguments used in this instantiation.
template_arguments(&self) -> &[TypeId]215     pub fn template_arguments(&self) -> &[TypeId] {
216         &self.args[..]
217     }
218 
219     /// Parse a `TemplateInstantiation` from a clang `Type`.
from_ty( ty: &clang::Type, ctx: &mut BindgenContext, ) -> Option<TemplateInstantiation>220     pub 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?
is_opaque(&self, ctx: &BindgenContext, item: &Item) -> bool296     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 
trace<T>(&self, _ctx: &BindgenContext, tracer: &mut T, _: &()) where T: Tracer,332     fn trace<T>(&self, _ctx: &BindgenContext, tracer: &mut T, _: &())
333     where
334         T: Tracer,
335     {
336         tracer
337             .visit_kind(self.definition.into(), EdgeKind::TemplateDeclaration);
338         for arg in self.template_arguments() {
339             tracer.visit_kind(arg.into(), EdgeKind::TemplateArgument);
340         }
341     }
342 }
343