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}