bindgen/ir/analysis/
has_float.rs

1//! Determining which types has float.
2
3use super::{generate_dependencies, ConstrainResult, MonotoneFramework};
4use crate::ir::comp::Field;
5use crate::ir::comp::FieldMethods;
6use crate::ir::context::{BindgenContext, ItemId};
7use crate::ir::traversal::EdgeKind;
8use crate::ir::ty::TypeKind;
9use crate::{HashMap, HashSet};
10
11/// An analysis that finds for each IR item whether it has float or not.
12///
13/// We use the monotone constraint function `has_float`,
14/// defined as follows:
15///
16/// * If T is float or complex float, T trivially has.
17/// * If T is a type alias, a templated alias or an indirection to another type,
18///   it has float if the type T refers to has.
19/// * If T is a compound type, it has float if any of base memter or field
20///   has.
21/// * If T is an instantiation of an abstract template definition, T has
22///   float if any of the template arguments or template definition
23///   has.
24#[derive(Debug, Clone)]
25pub(crate) struct HasFloat<'ctx> {
26    ctx: &'ctx BindgenContext,
27
28    // The incremental result of this analysis's computation. Everything in this
29    // set has float.
30    has_float: HashSet<ItemId>,
31
32    // Dependencies saying that if a key ItemId has been inserted into the
33    // `has_float` set, then each of the ids in Vec<ItemId> need to be
34    // considered again.
35    //
36    // This is a subset of the natural IR graph with reversed edges, where we
37    // only include the edges from the IR graph that can affect whether a type
38    // has float or not.
39    dependencies: HashMap<ItemId, Vec<ItemId>>,
40}
41
42impl HasFloat<'_> {
43    fn consider_edge(kind: EdgeKind) -> bool {
44        match kind {
45            EdgeKind::BaseMember |
46            EdgeKind::Field |
47            EdgeKind::TypeReference |
48            EdgeKind::VarType |
49            EdgeKind::TemplateArgument |
50            EdgeKind::TemplateDeclaration |
51            EdgeKind::TemplateParameterDefinition => true,
52
53            EdgeKind::Constructor |
54            EdgeKind::Destructor |
55            EdgeKind::FunctionReturn |
56            EdgeKind::FunctionParameter |
57            EdgeKind::InnerType |
58            EdgeKind::InnerVar |
59            EdgeKind::Method => false,
60            EdgeKind::Generic => false,
61        }
62    }
63
64    fn insert<Id: Into<ItemId>>(&mut self, id: Id) -> ConstrainResult {
65        let id = id.into();
66        trace!("inserting {id:?} into the has_float set");
67
68        let was_not_already_in_set = self.has_float.insert(id);
69        assert!(
70            was_not_already_in_set,
71            "We shouldn't try and insert {id:?} twice because if it was \
72             already in the set, `constrain` should have exited early."
73        );
74
75        ConstrainResult::Changed
76    }
77}
78
79impl<'ctx> MonotoneFramework for HasFloat<'ctx> {
80    type Node = ItemId;
81    type Extra = &'ctx BindgenContext;
82    type Output = HashSet<ItemId>;
83
84    fn new(ctx: &'ctx BindgenContext) -> HasFloat<'ctx> {
85        let has_float = HashSet::default();
86        let dependencies = generate_dependencies(ctx, Self::consider_edge);
87
88        HasFloat {
89            ctx,
90            has_float,
91            dependencies,
92        }
93    }
94
95    fn initial_worklist(&self) -> Vec<ItemId> {
96        self.ctx.allowlisted_items().iter().copied().collect()
97    }
98
99    fn constrain(&mut self, id: ItemId) -> ConstrainResult {
100        trace!("constrain: {id:?}");
101
102        if self.has_float.contains(&id) {
103            trace!("    already know it do not have float");
104            return ConstrainResult::Same;
105        }
106
107        let item = self.ctx.resolve_item(id);
108        let Some(ty) = item.as_type() else {
109            trace!("    not a type; ignoring");
110            return ConstrainResult::Same;
111        };
112
113        match *ty.kind() {
114            TypeKind::Void |
115            TypeKind::NullPtr |
116            TypeKind::Int(..) |
117            TypeKind::Function(..) |
118            TypeKind::Enum(..) |
119            TypeKind::Reference(..) |
120            TypeKind::TypeParam |
121            TypeKind::Opaque |
122            TypeKind::Pointer(..) |
123            TypeKind::UnresolvedTypeRef(..) |
124            TypeKind::ObjCInterface(..) |
125            TypeKind::ObjCId |
126            TypeKind::ObjCSel => {
127                trace!("    simple type that do not have float");
128                ConstrainResult::Same
129            }
130
131            TypeKind::Float(..) | TypeKind::Complex(..) => {
132                trace!("    float type has float");
133                self.insert(id)
134            }
135
136            TypeKind::Array(t, _) => {
137                if self.has_float.contains(&t.into()) {
138                    trace!(
139                        "    Array with type T that has float also has float"
140                    );
141                    return self.insert(id);
142                }
143                trace!("    Array with type T that do not have float also do not have float");
144                ConstrainResult::Same
145            }
146            TypeKind::Vector(t, _) => {
147                if self.has_float.contains(&t.into()) {
148                    trace!(
149                        "    Vector with type T that has float also has float"
150                    );
151                    return self.insert(id);
152                }
153                trace!("    Vector with type T that do not have float also do not have float");
154                ConstrainResult::Same
155            }
156
157            TypeKind::ResolvedTypeRef(t) |
158            TypeKind::TemplateAlias(t, _) |
159            TypeKind::Alias(t) |
160            TypeKind::BlockPointer(t) => {
161                if self.has_float.contains(&t.into()) {
162                    trace!(
163                        "    aliases and type refs to T which have float \
164                         also have float"
165                    );
166                    self.insert(id)
167                } else {
168                    trace!("    aliases and type refs to T which do not have float \
169                            also do not have floaarrayt");
170                    ConstrainResult::Same
171                }
172            }
173
174            TypeKind::Comp(ref info) => {
175                let bases_have = info
176                    .base_members()
177                    .iter()
178                    .any(|base| self.has_float.contains(&base.ty.into()));
179                if bases_have {
180                    trace!("    bases have float, so we also have");
181                    return self.insert(id);
182                }
183                let fields_have = info.fields().iter().any(|f| match *f {
184                    Field::DataMember(ref data) => {
185                        self.has_float.contains(&data.ty().into())
186                    }
187                    Field::Bitfields(ref bfu) => bfu
188                        .bitfields()
189                        .iter()
190                        .any(|b| self.has_float.contains(&b.ty().into())),
191                });
192                if fields_have {
193                    trace!("    fields have float, so we also have");
194                    return self.insert(id);
195                }
196
197                trace!("    comp doesn't have float");
198                ConstrainResult::Same
199            }
200
201            TypeKind::TemplateInstantiation(ref template) => {
202                let args_have = template
203                    .template_arguments()
204                    .iter()
205                    .any(|arg| self.has_float.contains(&arg.into()));
206                if args_have {
207                    trace!(
208                        "    template args have float, so \
209                         instantiation also has float"
210                    );
211                    return self.insert(id);
212                }
213
214                let def_has = self
215                    .has_float
216                    .contains(&template.template_definition().into());
217                if def_has {
218                    trace!(
219                        "    template definition has float, so \
220                         instantiation also has"
221                    );
222                    return self.insert(id);
223                }
224
225                trace!("    template instantiation do not have float");
226                ConstrainResult::Same
227            }
228        }
229    }
230
231    fn each_depending_on<F>(&self, id: ItemId, mut f: F)
232    where
233        F: FnMut(ItemId),
234    {
235        if let Some(edges) = self.dependencies.get(&id) {
236            for item in edges {
237                trace!("enqueue {item:?} into worklist");
238                f(*item);
239            }
240        }
241    }
242}
243
244impl<'ctx> From<HasFloat<'ctx>> for HashSet<ItemId> {
245    fn from(analysis: HasFloat<'ctx>) -> Self {
246        analysis.has_float
247    }
248}