summaryrefslogtreecommitdiffstats
path: root/third_party/rust/bindgen/ir/template.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/bindgen/ir/template.rs
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/bindgen/ir/template.rs')
-rw-r--r--third_party/rust/bindgen/ir/template.rs342
1 files changed, 342 insertions, 0 deletions
diff --git a/third_party/rust/bindgen/ir/template.rs b/third_party/rust/bindgen/ir/template.rs
new file mode 100644
index 0000000000..e3ef6a9c96
--- /dev/null
+++ b/third_party/rust/bindgen/ir/template.rs
@@ -0,0 +1,342 @@
+//! Template declaration and instantiation related things.
+//!
+//! The nomenclature surrounding templates is often confusing, so here are a few
+//! brief definitions:
+//!
+//! * "Template definition": a class/struct/alias/function definition that takes
+//! generic template parameters. For example:
+//!
+//! ```c++
+//! template<typename T>
+//! class List<T> {
+//! // ...
+//! };
+//! ```
+//!
+//! * "Template instantiation": an instantiation is a use of a template with
+//! concrete template arguments. For example, `List<int>`.
+//!
+//! * "Template specialization": an alternative template definition providing a
+//! custom definition for instantiations with the matching template
+//! arguments. This C++ feature is unsupported by bindgen. For example:
+//!
+//! ```c++
+//! template<>
+//! class List<int> {
+//! // Special layout for int lists...
+//! };
+//! ```
+
+use super::context::{BindgenContext, ItemId, TypeId};
+use super::item::{IsOpaque, Item, ItemAncestors};
+use super::traversal::{EdgeKind, Trace, Tracer};
+use crate::clang;
+
+/// Template declaration (and such declaration's template parameters) related
+/// methods.
+///
+/// This trait's methods distinguish between `None` and `Some([])` for
+/// declarations that are not templates and template declarations with zero
+/// parameters, in general.
+///
+/// Consider this example:
+///
+/// ```c++
+/// template <typename T, typename U>
+/// class Foo {
+/// T use_of_t;
+/// U use_of_u;
+///
+/// template <typename V>
+/// using Bar = V*;
+///
+/// class Inner {
+/// T x;
+/// U y;
+/// Bar<int> z;
+/// };
+///
+/// template <typename W>
+/// class Lol {
+/// // No use of W, but here's a use of T.
+/// T t;
+/// };
+///
+/// template <typename X>
+/// class Wtf {
+/// // X is not used because W is not used.
+/// Lol<X> lololol;
+/// };
+/// };
+///
+/// class Qux {
+/// int y;
+/// };
+/// ```
+///
+/// The following table depicts the results of each trait method when invoked on
+/// each of the declarations above:
+///
+/// +------+----------------------+--------------------------+------------------------+----
+/// |Decl. | self_template_params | num_self_template_params | all_template_parameters| ...
+/// +------+----------------------+--------------------------+------------------------+----
+/// |Foo | [T, U] | 2 | [T, U] | ...
+/// |Bar | [V] | 1 | [T, U, V] | ...
+/// |Inner | [] | 0 | [T, U] | ...
+/// |Lol | [W] | 1 | [T, U, W] | ...
+/// |Wtf | [X] | 1 | [T, U, X] | ...
+/// |Qux | [] | 0 | [] | ...
+/// +------+----------------------+--------------------------+------------------------+----
+///
+/// ----+------+-----+----------------------+
+/// ... |Decl. | ... | used_template_params |
+/// ----+------+-----+----------------------+
+/// ... |Foo | ... | [T, U] |
+/// ... |Bar | ... | [V] |
+/// ... |Inner | ... | [] |
+/// ... |Lol | ... | [T] |
+/// ... |Wtf | ... | [T] |
+/// ... |Qux | ... | [] |
+/// ----+------+-----+----------------------+
+pub trait TemplateParameters: Sized {
+ /// Get the set of `ItemId`s that make up this template declaration's free
+ /// template parameters.
+ ///
+ /// Note that these might *not* all be named types: C++ allows
+ /// constant-value template parameters as well as template-template
+ /// parameters. Of course, Rust does not allow generic parameters to be
+ /// anything but types, so we must treat them as opaque, and avoid
+ /// instantiating them.
+ fn self_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId>;
+
+ /// Get the number of free template parameters this template declaration
+ /// has.
+ fn num_self_template_params(&self, ctx: &BindgenContext) -> usize {
+ self.self_template_params(ctx).len()
+ }
+
+ /// Get the complete set of template parameters that can affect this
+ /// declaration.
+ ///
+ /// Note that this item doesn't need to be a template declaration itself for
+ /// `Some` to be returned here (in contrast to `self_template_params`). If
+ /// this item is a member of a template declaration, then the parent's
+ /// template parameters are included here.
+ ///
+ /// In the example above, `Inner` depends on both of the `T` and `U` type
+ /// parameters, even though it is not itself a template declaration and
+ /// therefore has no type parameters itself. Perhaps it helps to think about
+ /// how we would fully reference such a member type in C++:
+ /// `Foo<int,char>::Inner`. `Foo` *must* be instantiated with template
+ /// arguments before we can gain access to the `Inner` member type.
+ fn all_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId>
+ where
+ Self: ItemAncestors,
+ {
+ let mut ancestors: Vec<_> = self.ancestors(ctx).collect();
+ ancestors.reverse();
+ ancestors
+ .into_iter()
+ .flat_map(|id| id.self_template_params(ctx).into_iter())
+ .collect()
+ }
+
+ /// Get only the set of template parameters that this item uses. This is a
+ /// subset of `all_template_params` and does not necessarily contain any of
+ /// `self_template_params`.
+ fn used_template_params(&self, ctx: &BindgenContext) -> Vec<TypeId>
+ where
+ Self: AsRef<ItemId>,
+ {
+ assert!(
+ ctx.in_codegen_phase(),
+ "template parameter usage is not computed until codegen"
+ );
+
+ let id = *self.as_ref();
+ ctx.resolve_item(id)
+ .all_template_params(ctx)
+ .into_iter()
+ .filter(|p| ctx.uses_template_parameter(id, *p))
+ .collect()
+ }
+}
+
+/// A trait for things which may or may not be a named template type parameter.
+pub trait AsTemplateParam {
+ /// Any extra information the implementor might need to make this decision.
+ type Extra;
+
+ /// Convert this thing to the item id of a named template type parameter.
+ fn as_template_param(
+ &self,
+ ctx: &BindgenContext,
+ extra: &Self::Extra,
+ ) -> Option<TypeId>;
+
+ /// Is this a named template type parameter?
+ fn is_template_param(
+ &self,
+ ctx: &BindgenContext,
+ extra: &Self::Extra,
+ ) -> bool {
+ self.as_template_param(ctx, extra).is_some()
+ }
+}
+
+/// A concrete instantiation of a generic template.
+#[derive(Clone, Debug)]
+pub struct TemplateInstantiation {
+ /// The template definition which this is instantiating.
+ definition: TypeId,
+ /// The concrete template arguments, which will be substituted in the
+ /// definition for the generic template parameters.
+ args: Vec<TypeId>,
+}
+
+impl TemplateInstantiation {
+ /// Construct a new template instantiation from the given parts.
+ pub fn new<I>(definition: TypeId, args: I) -> TemplateInstantiation
+ where
+ I: IntoIterator<Item = TypeId>,
+ {
+ TemplateInstantiation {
+ definition,
+ args: args.into_iter().collect(),
+ }
+ }
+
+ /// Get the template definition for this instantiation.
+ pub fn template_definition(&self) -> TypeId {
+ self.definition
+ }
+
+ /// Get the concrete template arguments used in this instantiation.
+ pub fn template_arguments(&self) -> &[TypeId] {
+ &self.args[..]
+ }
+
+ /// Parse a `TemplateInstantiation` from a clang `Type`.
+ pub fn from_ty(
+ ty: &clang::Type,
+ ctx: &mut BindgenContext,
+ ) -> Option<TemplateInstantiation> {
+ use clang_sys::*;
+
+ let template_args = ty.template_args().map_or(vec![], |args| match ty
+ .canonical_type()
+ .template_args()
+ {
+ Some(canonical_args) => {
+ let arg_count = args.len();
+ args.chain(canonical_args.skip(arg_count))
+ .filter(|t| t.kind() != CXType_Invalid)
+ .map(|t| {
+ Item::from_ty_or_ref(t, t.declaration(), None, ctx)
+ })
+ .collect()
+ }
+ None => args
+ .filter(|t| t.kind() != CXType_Invalid)
+ .map(|t| Item::from_ty_or_ref(t, t.declaration(), None, ctx))
+ .collect(),
+ });
+
+ let declaration = ty.declaration();
+ let definition = if declaration.kind() == CXCursor_TypeAliasTemplateDecl
+ {
+ Some(declaration)
+ } else {
+ declaration.specialized().or_else(|| {
+ let mut template_ref = None;
+ ty.declaration().visit(|child| {
+ if child.kind() == CXCursor_TemplateRef {
+ template_ref = Some(child);
+ return CXVisit_Break;
+ }
+
+ // Instantiations of template aliases might have the
+ // TemplateRef to the template alias definition arbitrarily
+ // deep, so we need to recurse here and not only visit
+ // direct children.
+ CXChildVisit_Recurse
+ });
+
+ template_ref.and_then(|cur| cur.referenced())
+ })
+ };
+
+ let definition = match definition {
+ Some(def) => def,
+ None => {
+ if !ty.declaration().is_builtin() {
+ warn!(
+ "Could not find template definition for template \
+ instantiation"
+ );
+ }
+ return None;
+ }
+ };
+
+ let template_definition =
+ Item::from_ty_or_ref(definition.cur_type(), definition, None, ctx);
+
+ Some(TemplateInstantiation::new(
+ template_definition,
+ template_args,
+ ))
+ }
+}
+
+impl IsOpaque for TemplateInstantiation {
+ type Extra = Item;
+
+ /// Is this an opaque template instantiation?
+ fn is_opaque(&self, ctx: &BindgenContext, item: &Item) -> bool {
+ if self.template_definition().is_opaque(ctx, &()) {
+ return true;
+ }
+
+ // TODO(#774): This doesn't properly handle opaque instantiations where
+ // an argument is itself an instantiation because `canonical_name` does
+ // not insert the template arguments into the name, ie it for nested
+ // template arguments it creates "Foo" instead of "Foo<int>". The fully
+ // correct fix is to make `canonical_{name,path}` include template
+ // arguments properly.
+
+ let mut path = item.path_for_allowlisting(ctx).clone();
+ let args: Vec<_> = self
+ .template_arguments()
+ .iter()
+ .map(|arg| {
+ let arg_path =
+ ctx.resolve_item(*arg).path_for_allowlisting(ctx);
+ arg_path[1..].join("::")
+ })
+ .collect();
+ {
+ let last = path.last_mut().unwrap();
+ last.push('<');
+ last.push_str(&args.join(", "));
+ last.push('>');
+ }
+
+ ctx.opaque_by_name(&path)
+ }
+}
+
+impl Trace for TemplateInstantiation {
+ type Extra = ();
+
+ fn trace<T>(&self, _ctx: &BindgenContext, tracer: &mut T, _: &())
+ where
+ T: Tracer,
+ {
+ tracer
+ .visit_kind(self.definition.into(), EdgeKind::TemplateDeclaration);
+ for arg in self.template_arguments() {
+ tracer.visit_kind(arg.into(), EdgeKind::TemplateArgument);
+ }
+ }
+}