1 //! Determining which types has float.
2 
3 use super::{generate_dependencies, ConstrainResult, MonotoneFramework};
4 use crate::ir::comp::Field;
5 use crate::ir::comp::FieldMethods;
6 use crate::ir::context::{BindgenContext, ItemId};
7 use crate::ir::traversal::EdgeKind;
8 use crate::ir::ty::TypeKind;
9 use 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)]
25 pub(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 
42 impl<'ctx> HasFloat<'ctx> {
consider_edge(kind: EdgeKind) -> bool43     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 
insert<Id: Into<ItemId>>(&mut self, id: Id) -> ConstrainResult64     fn insert<Id: Into<ItemId>>(&mut self, id: Id) -> ConstrainResult {
65         let id = id.into();
66         trace!("inserting {:?} into the has_float set", id);
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 {:?} twice because if it was \
72              already in the set, `constrain` should have exited early.",
73             id
74         );
75 
76         ConstrainResult::Changed
77     }
78 }
79 
80 impl<'ctx> MonotoneFramework for HasFloat<'ctx> {
81     type Node = ItemId;
82     type Extra = &'ctx BindgenContext;
83     type Output = HashSet<ItemId>;
84 
new(ctx: &'ctx BindgenContext) -> HasFloat<'ctx>85     fn new(ctx: &'ctx BindgenContext) -> HasFloat<'ctx> {
86         let has_float = HashSet::default();
87         let dependencies = generate_dependencies(ctx, Self::consider_edge);
88 
89         HasFloat {
90             ctx,
91             has_float,
92             dependencies,
93         }
94     }
95 
initial_worklist(&self) -> Vec<ItemId>96     fn initial_worklist(&self) -> Vec<ItemId> {
97         self.ctx.allowlisted_items().iter().cloned().collect()
98     }
99 
constrain(&mut self, id: ItemId) -> ConstrainResult100     fn constrain(&mut self, id: ItemId) -> ConstrainResult {
101         trace!("constrain: {:?}", id);
102 
103         if self.has_float.contains(&id) {
104             trace!("    already know it do not have float");
105             return ConstrainResult::Same;
106         }
107 
108         let item = self.ctx.resolve_item(id);
109         let ty = match item.as_type() {
110             Some(ty) => ty,
111             None => {
112                 trace!("    not a type; ignoring");
113                 return ConstrainResult::Same;
114             }
115         };
116 
117         match *ty.kind() {
118             TypeKind::Void |
119             TypeKind::NullPtr |
120             TypeKind::Int(..) |
121             TypeKind::Function(..) |
122             TypeKind::Enum(..) |
123             TypeKind::Reference(..) |
124             TypeKind::TypeParam |
125             TypeKind::Opaque |
126             TypeKind::Pointer(..) |
127             TypeKind::UnresolvedTypeRef(..) |
128             TypeKind::ObjCInterface(..) |
129             TypeKind::ObjCId |
130             TypeKind::ObjCSel => {
131                 trace!("    simple type that do not have float");
132                 ConstrainResult::Same
133             }
134 
135             TypeKind::Float(..) | TypeKind::Complex(..) => {
136                 trace!("    float type has float");
137                 self.insert(id)
138             }
139 
140             TypeKind::Array(t, _) => {
141                 if self.has_float.contains(&t.into()) {
142                     trace!(
143                         "    Array with type T that has float also has float"
144                     );
145                     return self.insert(id);
146                 }
147                 trace!("    Array with type T that do not have float also do not have float");
148                 ConstrainResult::Same
149             }
150             TypeKind::Vector(t, _) => {
151                 if self.has_float.contains(&t.into()) {
152                     trace!(
153                         "    Vector with type T that has float also has float"
154                     );
155                     return self.insert(id);
156                 }
157                 trace!("    Vector with type T that do not have float also do not have float");
158                 ConstrainResult::Same
159             }
160 
161             TypeKind::ResolvedTypeRef(t) |
162             TypeKind::TemplateAlias(t, _) |
163             TypeKind::Alias(t) |
164             TypeKind::BlockPointer(t) => {
165                 if self.has_float.contains(&t.into()) {
166                     trace!(
167                         "    aliases and type refs to T which have float \
168                          also have float"
169                     );
170                     self.insert(id)
171                 } else {
172                     trace!("    aliases and type refs to T which do not have float \
173                             also do not have floaarrayt");
174                     ConstrainResult::Same
175                 }
176             }
177 
178             TypeKind::Comp(ref info) => {
179                 let bases_have = info
180                     .base_members()
181                     .iter()
182                     .any(|base| self.has_float.contains(&base.ty.into()));
183                 if bases_have {
184                     trace!("    bases have float, so we also have");
185                     return self.insert(id);
186                 }
187                 let fields_have = info.fields().iter().any(|f| match *f {
188                     Field::DataMember(ref data) => {
189                         self.has_float.contains(&data.ty().into())
190                     }
191                     Field::Bitfields(ref bfu) => bfu
192                         .bitfields()
193                         .iter()
194                         .any(|b| self.has_float.contains(&b.ty().into())),
195                 });
196                 if fields_have {
197                     trace!("    fields have float, so we also have");
198                     return self.insert(id);
199                 }
200 
201                 trace!("    comp doesn't have float");
202                 ConstrainResult::Same
203             }
204 
205             TypeKind::TemplateInstantiation(ref template) => {
206                 let args_have = template
207                     .template_arguments()
208                     .iter()
209                     .any(|arg| self.has_float.contains(&arg.into()));
210                 if args_have {
211                     trace!(
212                         "    template args have float, so \
213                          insantiation also has float"
214                     );
215                     return self.insert(id);
216                 }
217 
218                 let def_has = self
219                     .has_float
220                     .contains(&template.template_definition().into());
221                 if def_has {
222                     trace!(
223                         "    template definition has float, so \
224                          insantiation also has"
225                     );
226                     return self.insert(id);
227                 }
228 
229                 trace!("    template instantiation do not have float");
230                 ConstrainResult::Same
231             }
232         }
233     }
234 
each_depending_on<F>(&self, id: ItemId, mut f: F) where F: FnMut(ItemId),235     fn each_depending_on<F>(&self, id: ItemId, mut f: F)
236     where
237         F: FnMut(ItemId),
238     {
239         if let Some(edges) = self.dependencies.get(&id) {
240             for item in edges {
241                 trace!("enqueue {:?} into worklist", item);
242                 f(*item);
243             }
244         }
245     }
246 }
247 
248 impl<'ctx> From<HasFloat<'ctx>> for HashSet<ItemId> {
from(analysis: HasFloat<'ctx>) -> Self249     fn from(analysis: HasFloat<'ctx>) -> Self {
250         analysis.has_float
251     }
252 }
253