76
76
//! fixed-point.
77
77
//!
78
78
//! We use the "monotone framework" for this fix-point analysis where our
79
- //! lattice is the powerset of the template parameters that appear in the input
80
- //! C++ header, our join function is set union, and we use the
81
- //! `ir::traversal::Trace` trait to implement the work-list optimization so we
82
- //! don't have to revisit every node in the graph when for every iteration
83
- //! towards the fix-point.
79
+ //! lattice is the mapping from each IR item to the powerset of the template
80
+ //! parameters that appear in the input C++ header, our join function is set
81
+ //! union, and we use the `ir::traversal::Trace` trait to implement the
82
+ //! work-list optimization so we don't have to revisit every node in the graph
83
+ //! when for every iteration towards the fix-point.
84
+ //!
85
+ //! A lattice is a set with a partial ordering between elements, where there is
86
+ //! a single least upper bound and a single greatest least bound for every
87
+ //! subset. We are dealing with finite lattices, which means that it has a
88
+ //! finite number of elements, and it follows that there exists a single top and
89
+ //! a single bottom member of the lattice. For example, the power set of a
90
+ //! finite set forms a finite lattice where partial ordering is defined by set
91
+ //! inclusion, that is `a <= b` if `a` is a subset of `b`. Here is the finite
92
+ //! lattice constructed from the set {0,1,2}:
93
+ //!
94
+ //! ```text
95
+ //! .----- Top = {0,1,2} -----.
96
+ //! / | \
97
+ //! / | \
98
+ //! / | \
99
+ //! {0,1} -------. {0,2} .--------- {1,2}
100
+ //! | \ / \ / |
101
+ //! | / \ |
102
+ //! | / \ / \ |
103
+ //! {0} --------' {1} `---------- {2}
104
+ //! \ | /
105
+ //! \ | /
106
+ //! \ | /
107
+ //! `------ Bottom = {} ------'
108
+ //! ```
109
+ //!
110
+ //! A monotone function `f` is a function where if `x <= y`, then it holds that
111
+ //! `f(x) <= f(y)`. It should be clear that running a monotone function to a
112
+ //! fix-point on a finite lattice will always terminate: `f` can only "move"
113
+ //! along the lattice in a single direction, and therefore can only either find
114
+ //! a fix-point in the middle of the lattice or continue to the top or bottom
115
+ //! depending if it is ascending or descending the lattice respectively.
116
+ //!
117
+ //! For our analysis, we are collecting the set of template parameters used by
118
+ //! any given IR node. The set of template parameters appearing in the program
119
+ //! is finite. Our lattice is their powerset. We start at the bottom element,
120
+ //! the empty set. Our analysis only adds members to the set of used template
121
+ //! parameters, never removes them, so it is monotone, and therefore iteration
122
+ //! to a fix-point will terminate.
84
123
//!
85
124
//! For a deeper introduction to the general form of this kind of analysis, see
86
125
//! [Static Program Analysis by Anders Møller and Michael I. Schwartzbach][spa].
@@ -173,38 +212,119 @@ pub fn analyze<Analysis>(extra: Analysis::Extra) -> Analysis::Output
173
212
analysis. into ( )
174
213
}
175
214
176
- /// An analysis that finds the set of template parameters that actually end up
177
- /// used in our generated bindings.
215
+ /// An analysis that finds for each IR item its set of template parameters that
216
+ /// it uses.
217
+ ///
218
+ /// We use the following monotone constraint function:
219
+ ///
220
+ /// ```ignore
221
+ /// template_param_usage(v) =
222
+ /// self_template_param_usage(v) union
223
+ /// template_param_usage(w_0) union
224
+ /// template_param_usage(w_1) union
225
+ /// ...
226
+ /// template_param_usage(w_n)
227
+ /// ```
228
+ ///
229
+ /// Where `v` has direct edges in the IR graph to each of `w_0`, `w_1`,
230
+ /// ..., `w_n` (for example, if `v` were a struct type and `x` and `y`
231
+ /// were the types of two of `v`'s fields). We ignore certain edges, such
232
+ /// as edges from a template declaration to its template parameters'
233
+ /// definitions for this analysis. If we didn't, then we would mistakenly
234
+ /// determine that ever template parameter is always used.
235
+ ///
236
+ /// Finally, `self_template_param_usage` is defined with the following cases:
237
+ ///
238
+ /// * If `T` is a template parameter:
239
+ ///
240
+ /// ```ignore
241
+ /// self_template_param_usage(T) = { T }
242
+ /// ```
243
+ ///
244
+ /// * If `inst` is a template instantiation, `inst.args` are the template
245
+ /// instantiation's template arguments, and `inst.decl` is the template
246
+ /// declaration being instantiated:
247
+ ///
248
+ /// ```ignore
249
+ /// self_template_param_usage(inst) =
250
+ /// { T: for T in inst.args, if T in template_param_usage(inst.decl) }
251
+ /// ```
252
+ ///
253
+ /// * And for all other IR items, the result is the empty set:
254
+ ///
255
+ /// ```ignore
256
+ /// self_template_param_usage(_) = { }
257
+ /// ```
178
258
#[ derive( Debug , Clone ) ]
179
259
pub struct UsedTemplateParameters < ' a > {
180
260
ctx : & ' a BindgenContext < ' a > ,
181
- used : ItemSet ,
261
+ used : HashMap < ItemId , ItemSet > ,
182
262
dependencies : HashMap < ItemId , Vec < ItemId > > ,
183
263
}
184
264
265
+ impl < ' a > UsedTemplateParameters < ' a > {
266
+ fn consider_edge ( kind : EdgeKind ) -> bool {
267
+ match kind {
268
+ // For each of these kinds of edges, if the referent uses a template
269
+ // parameter, then it should be considered that the origin of the
270
+ // edge also uses the template parameter.
271
+ EdgeKind :: TemplateArgument |
272
+ EdgeKind :: BaseMember |
273
+ EdgeKind :: Field |
274
+ EdgeKind :: InnerType |
275
+ EdgeKind :: InnerVar |
276
+ EdgeKind :: Constructor |
277
+ EdgeKind :: VarType |
278
+ EdgeKind :: TypeReference => true ,
279
+
280
+ // We can't emit machine code for new instantiations of function
281
+ // templates and class templates' methods (and don't detect explicit
282
+ // instantiations) so we must ignore template parameters that are
283
+ // only used by functions.
284
+ EdgeKind :: Method |
285
+ EdgeKind :: FunctionReturn |
286
+ EdgeKind :: FunctionParameter => false ,
287
+
288
+ // If we considered these edges, we would end up mistakenly claiming
289
+ // that every template parameter always used.
290
+ EdgeKind :: TemplateDeclaration |
291
+ EdgeKind :: TemplateParameterDefinition => false ,
292
+
293
+ // Since we have to be careful about which edges we consider for
294
+ // this analysis to be correct, we ignore generic edges. We also
295
+ // avoid a `_` wild card to force authors of new edge kinds to
296
+ // determine whether they need to be considered by this analysis.
297
+ EdgeKind :: Generic => false ,
298
+ }
299
+ }
300
+ }
301
+
185
302
impl < ' a > MonotoneFramework for UsedTemplateParameters < ' a > {
186
303
type Node = ItemId ;
187
304
type Extra = & ' a BindgenContext < ' a > ;
188
- type Output = ItemSet ;
305
+ type Output = HashMap < ItemId , ItemSet > ;
189
306
190
307
fn new ( ctx : & ' a BindgenContext < ' a > ) -> UsedTemplateParameters < ' a > {
308
+ let mut used = HashMap :: new ( ) ;
191
309
let mut dependencies = HashMap :: new ( ) ;
192
310
193
311
for item in ctx. whitelisted_items ( ) {
312
+ dependencies. entry ( item) . or_insert ( vec ! [ ] ) ;
313
+ used. insert ( item, ItemSet :: new ( ) ) ;
314
+
194
315
{
195
316
// We reverse our natural IR graph edges to find dependencies
196
317
// between nodes.
197
- let mut add_reverse_edge = |sub_item, _| {
318
+ item . trace ( ctx , & mut |sub_item, _| {
198
319
dependencies. entry ( sub_item) . or_insert ( vec ! [ ] ) . push ( item) ;
199
- } ;
200
- item. trace ( ctx, & mut add_reverse_edge, & ( ) ) ;
320
+ } , & ( ) ) ;
201
321
}
202
322
203
323
// Additionally, whether a template instantiation's template
204
324
// arguments are used depends on whether the template declaration's
205
325
// generic template parameters are used.
206
- ctx. resolve_item_fallible ( item)
207
- . and_then ( |item| item . as_type ( ) )
326
+ ctx. resolve_item ( item)
327
+ . as_type ( )
208
328
. map ( |ty| match ty. kind ( ) {
209
329
& TypeKind :: TemplateInstantiation ( decl, ref args) => {
210
330
let decl = ctx. resolve_type ( decl) ;
@@ -222,57 +342,65 @@ impl<'a> MonotoneFramework for UsedTemplateParameters<'a> {
222
342
223
343
UsedTemplateParameters {
224
344
ctx : ctx,
225
- used : ItemSet :: new ( ) ,
345
+ used : used ,
226
346
dependencies : dependencies,
227
347
}
228
348
}
229
349
230
- fn initial_worklist ( & self ) -> Vec < Self :: Node > {
350
+ fn initial_worklist ( & self ) -> Vec < ItemId > {
231
351
self . ctx . whitelisted_items ( ) . collect ( )
232
352
}
233
353
234
- fn constrain ( & mut self , item : ItemId ) -> bool {
235
- let original_size = self . used . len ( ) ;
354
+ fn constrain ( & mut self , id : ItemId ) -> bool {
355
+ let original_len = self . used [ & id ] . len ( ) ;
236
356
237
- item. trace ( self . ctx , & mut |item, edge_kind| {
238
- if edge_kind == EdgeKind :: TemplateParameterDefinition {
239
- // The definition of a template parameter is not considered a
240
- // use of said template parameter. Ignore this edge.
241
- return ;
357
+ // First, add this item's self template parameter usage.
358
+ let item = self . ctx . resolve_item ( id) ;
359
+ let ty_kind = item. as_type ( ) . map ( |ty| ty. kind ( ) ) ;
360
+ match ty_kind {
361
+ Some ( & TypeKind :: Named ) => {
362
+ // This is a trivial use of the template type parameter.
363
+ self . used . get_mut ( & id) . unwrap ( ) . insert ( id) ;
242
364
}
243
-
244
- let ty_kind = self . ctx . resolve_item ( item)
245
- . as_type ( )
246
- . map ( |ty| ty. kind ( ) ) ;
247
-
248
- match ty_kind {
249
- Some ( & TypeKind :: Named ) => {
250
- // This is a "trivial" use of the template type parameter.
251
- self . used . insert ( item) ;
252
- } ,
253
- Some ( & TypeKind :: TemplateInstantiation ( decl, ref args) ) => {
254
- // A template instantiation's concrete template argument is
255
- // only used if the template declaration uses the
256
- // corresponding template parameter.
257
- let decl = self . ctx . resolve_type ( decl) ;
258
- let params = decl. self_template_params ( self . ctx )
259
- . expect ( "a template instantiation's referenced \
260
- template declaration should have template \
261
- parameters") ;
262
- for ( arg, param) in args. iter ( ) . zip ( params. iter ( ) ) {
263
- if self . used . contains ( param) {
264
- if self . ctx . resolve_item ( * arg) . is_named ( ) {
265
- self . used . insert ( * arg) ;
266
- }
365
+ Some ( & TypeKind :: TemplateInstantiation ( decl, ref args) ) => {
366
+ // A template instantiation's concrete template argument is
367
+ // only used if the template declaration uses the
368
+ // corresponding template parameter.
369
+ let params = decl. self_template_params ( self . ctx )
370
+ . expect ( "a template instantiation's referenced \
371
+ template declaration should have template \
372
+ parameters") ;
373
+ for ( arg, param) in args. iter ( ) . zip ( params. iter ( ) ) {
374
+ if self . used [ & decl] . contains ( param) {
375
+ if self . ctx . resolve_item ( * arg) . is_named ( ) {
376
+ self . used . get_mut ( & id) . unwrap ( ) . insert ( * arg) ;
267
377
}
268
378
}
269
- } ,
270
- _ => return ,
379
+ }
271
380
}
381
+ _ => { }
382
+ }
383
+
384
+ // Second, add the union of each of its referent item's template
385
+ // parameter usage.
386
+ item. trace ( self . ctx , & mut |sub_id, edge_kind| {
387
+ if sub_id == id || !Self :: consider_edge ( edge_kind) {
388
+ return ;
389
+ }
390
+
391
+ // This clone is unfortunate because we are potentially thrashing
392
+ // malloc. We could investigate replacing the ItemSet values with
393
+ // Rc<RefCell<ItemSet>> to make the borrow checker happy, but it
394
+ // isn't clear that the added indirection wouldn't outweigh the cost
395
+ // of malloc'ing a new ItemSet here. Ideally, `HashMap` would have a
396
+ // `split_entries` method analogous to `slice::split_at_mut`...
397
+ let to_add = self . used [ & sub_id] . clone ( ) ;
398
+ self . used . get_mut ( & id) . unwrap ( ) . extend ( to_add) ;
272
399
} , & ( ) ) ;
273
400
274
- let new_size = self . used . len ( ) ;
275
- new_size != original_size
401
+ let new_len = self . used [ & id] . len ( ) ;
402
+ assert ! ( new_len >= original_len) ;
403
+ new_len != original_len
276
404
}
277
405
278
406
fn each_depending_on < F > ( & self , item : ItemId , mut f : F )
@@ -286,8 +414,8 @@ impl<'a> MonotoneFramework for UsedTemplateParameters<'a> {
286
414
}
287
415
}
288
416
289
- impl < ' a > From < UsedTemplateParameters < ' a > > for ItemSet {
290
- fn from ( used_templ_params : UsedTemplateParameters ) -> ItemSet {
417
+ impl < ' a > From < UsedTemplateParameters < ' a > > for HashMap < ItemId , ItemSet > {
418
+ fn from ( used_templ_params : UsedTemplateParameters ) -> Self {
291
419
used_templ_params. used
292
420
}
293
421
}
0 commit comments