@@ -32,8 +32,11 @@ pub(crate) struct Formatter<'mir, 'tcx, A>
32
32
where
33
33
A : Analysis < ' tcx > ,
34
34
{
35
- body : & ' mir Body < ' tcx > ,
36
- results : RefCell < Option < Results < ' tcx , A > > > ,
35
+ // The `RefCell` is used because `<Formatter as Labeller>::node_label`
36
+ // takes `&self`, but it needs to modify the cursor. This is also the
37
+ // reason for the `Formatter`/`BlockFormatter` split; `BlockFormatter` has
38
+ // the operations that involve the mutation, i.e. within the `borrow_mut`.
39
+ cursor : RefCell < ResultsCursor < ' mir , ' tcx , A > > ,
37
40
style : OutputStyle ,
38
41
reachable : BitSet < BasicBlock > ,
39
42
}
@@ -48,11 +51,15 @@ where
48
51
style : OutputStyle ,
49
52
) -> Self {
50
53
let reachable = mir:: traversal:: reachable_as_bitset ( body) ;
51
- Formatter { body, results : Some ( results) . into ( ) , style, reachable }
54
+ Formatter { cursor : results. into_results_cursor ( body) . into ( ) , style, reachable }
55
+ }
56
+
57
+ fn body ( & self ) -> & ' mir Body < ' tcx > {
58
+ self . cursor . borrow ( ) . body ( )
52
59
}
53
60
54
61
pub ( crate ) fn into_results ( self ) -> Results < ' tcx , A > {
55
- self . results . into_inner ( ) . unwrap ( )
62
+ self . cursor . into_inner ( ) . into_results ( )
56
63
}
57
64
}
58
65
81
88
type Edge = CfgEdge ;
82
89
83
90
fn graph_id ( & self ) -> dot:: Id < ' _ > {
84
- let name = graphviz_safe_def_name ( self . body . source . def_id ( ) ) ;
91
+ let name = graphviz_safe_def_name ( self . body ( ) . source . def_id ( ) ) ;
85
92
dot:: Id :: new ( format ! ( "graph_for_def_id_{name}" ) ) . unwrap ( )
86
93
}
87
94
@@ -90,20 +97,11 @@ where
90
97
}
91
98
92
99
fn node_label ( & self , block : & Self :: Node ) -> dot:: LabelText < ' _ > {
93
- let mut label = Vec :: new ( ) ;
94
- self . results . replace_with ( |results| {
95
- // `Formatter::result` is a `RefCell<Option<_>>` so we can replace
96
- // the value with `None`, move it into the results cursor, move it
97
- // back out, and return it to the refcell wrapped in `Some`.
98
- let mut fmt = BlockFormatter {
99
- results : results. take ( ) . unwrap ( ) . into_results_cursor ( self . body ) ,
100
- style : self . style ,
101
- bg : Background :: Light ,
102
- } ;
100
+ let mut cursor = self . cursor . borrow_mut ( ) ;
101
+ let mut fmt =
102
+ BlockFormatter { cursor : & mut cursor, style : self . style , bg : Background :: Light } ;
103
+ let label = fmt. write_node_label ( * block) . unwrap ( ) ;
103
104
104
- fmt. write_node_label ( & mut label, * block) . unwrap ( ) ;
105
- Some ( fmt. results . into_results ( ) )
106
- } ) ;
107
105
dot:: LabelText :: html ( String :: from_utf8 ( label) . unwrap ( ) )
108
106
}
109
107
@@ -112,20 +110,20 @@ where
112
110
}
113
111
114
112
fn edge_label ( & self , e : & Self :: Edge ) -> dot:: LabelText < ' _ > {
115
- let label = & self . body [ e. source ] . terminator ( ) . kind . fmt_successor_labels ( ) [ e. index ] ;
113
+ let label = & self . body ( ) [ e. source ] . terminator ( ) . kind . fmt_successor_labels ( ) [ e. index ] ;
116
114
dot:: LabelText :: label ( label. clone ( ) )
117
115
}
118
116
}
119
117
120
- impl < ' mir , ' tcx , A > dot:: GraphWalk < ' mir > for Formatter < ' mir , ' tcx , A >
118
+ impl < ' tcx , A > dot:: GraphWalk < ' _ > for Formatter < ' _ , ' tcx , A >
121
119
where
122
120
A : Analysis < ' tcx > ,
123
121
{
124
122
type Node = BasicBlock ;
125
123
type Edge = CfgEdge ;
126
124
127
125
fn nodes ( & self ) -> dot:: Nodes < ' _ , Self :: Node > {
128
- self . body
126
+ self . body ( )
129
127
. basic_blocks
130
128
. indices ( )
131
129
. filter ( |& idx| self . reachable . contains ( idx) )
@@ -134,10 +132,10 @@ where
134
132
}
135
133
136
134
fn edges ( & self ) -> dot:: Edges < ' _ , Self :: Edge > {
137
- self . body
138
- . basic_blocks
135
+ let body = self . body ( ) ;
136
+ body . basic_blocks
139
137
. indices ( )
140
- . flat_map ( |bb| dataflow_successors ( self . body , bb) )
138
+ . flat_map ( |bb| dataflow_successors ( body, bb) )
141
139
. collect :: < Vec < _ > > ( )
142
140
. into ( )
143
141
}
@@ -147,20 +145,20 @@ where
147
145
}
148
146
149
147
fn target ( & self , edge : & Self :: Edge ) -> Self :: Node {
150
- self . body [ edge. source ] . terminator ( ) . successors ( ) . nth ( edge. index ) . unwrap ( )
148
+ self . body ( ) [ edge. source ] . terminator ( ) . successors ( ) . nth ( edge. index ) . unwrap ( )
151
149
}
152
150
}
153
151
154
- struct BlockFormatter < ' mir , ' tcx , A >
152
+ struct BlockFormatter < ' a , ' mir , ' tcx , A >
155
153
where
156
154
A : Analysis < ' tcx > ,
157
155
{
158
- results : ResultsCursor < ' mir , ' tcx , A > ,
156
+ cursor : & ' a mut ResultsCursor < ' mir , ' tcx , A > ,
159
157
bg : Background ,
160
158
style : OutputStyle ,
161
159
}
162
160
163
- impl < ' mir , ' tcx , A > BlockFormatter < ' mir , ' tcx , A >
161
+ impl < ' tcx , A > BlockFormatter < ' _ , ' _ , ' tcx , A >
164
162
where
165
163
A : Analysis < ' tcx > ,
166
164
A :: Domain : DebugWithContext < A > ,
@@ -173,7 +171,9 @@ where
173
171
bg
174
172
}
175
173
176
- fn write_node_label ( & mut self , w : & mut impl io:: Write , block : BasicBlock ) -> io:: Result < ( ) > {
174
+ fn write_node_label ( & mut self , block : BasicBlock ) -> io:: Result < Vec < u8 > > {
175
+ use std:: io:: Write ;
176
+
177
177
// Sample output:
178
178
// +-+-----------------------------------------------+
179
179
// A | bb4 |
@@ -200,6 +200,9 @@ where
200
200
// attributes. Make sure to test the output before trying to remove the redundancy.
201
201
// Notably, `align` was found to have no effect when applied only to <table>.
202
202
203
+ let mut v = vec ! [ ] ;
204
+ let w = & mut v;
205
+
203
206
let table_fmt = concat ! (
204
207
" border=\" 1\" " ,
205
208
" cellborder=\" 1\" " ,
@@ -219,21 +222,21 @@ where
219
222
220
223
// C: State at start of block
221
224
self . bg = Background :: Light ;
222
- self . results . seek_to_block_start ( block) ;
223
- let block_start_state = self . results . get ( ) . clone ( ) ;
225
+ self . cursor . seek_to_block_start ( block) ;
226
+ let block_start_state = self . cursor . get ( ) . clone ( ) ;
224
227
self . write_row_with_full_state ( w, "" , "(on start)" ) ?;
225
228
226
229
// D + E: Statement and terminator transfer functions
227
230
self . write_statements_and_terminator ( w, block) ?;
228
231
229
232
// F: State at end of block
230
233
231
- let terminator = self . results . body ( ) [ block] . terminator ( ) ;
234
+ let terminator = self . cursor . body ( ) [ block] . terminator ( ) ;
232
235
233
236
// Write the full dataflow state immediately after the terminator if it differs from the
234
237
// state at block entry.
235
- self . results . seek_to_block_end ( block) ;
236
- if self . results . get ( ) != & block_start_state || A :: Direction :: IS_BACKWARD {
238
+ self . cursor . seek_to_block_end ( block) ;
239
+ if self . cursor . get ( ) != & block_start_state || A :: Direction :: IS_BACKWARD {
237
240
let after_terminator_name = match terminator. kind {
238
241
mir:: TerminatorKind :: Call { target : Some ( _) , .. } => "(on unwind)" ,
239
242
_ => "(on end)" ,
@@ -250,8 +253,8 @@ where
250
253
match terminator. kind {
251
254
mir:: TerminatorKind :: Call { destination, .. } => {
252
255
self . write_row ( w, "" , "(on successful return)" , |this, w, fmt| {
253
- let state_on_unwind = this. results . get ( ) . clone ( ) ;
254
- this. results . apply_custom_effect ( |analysis, state| {
256
+ let state_on_unwind = this. cursor . get ( ) . clone ( ) ;
257
+ this. cursor . apply_custom_effect ( |analysis, state| {
255
258
analysis. apply_call_return_effect (
256
259
state,
257
260
block,
@@ -265,18 +268,18 @@ where
265
268
colspan = this. style. num_state_columns( ) ,
266
269
fmt = fmt,
267
270
diff = diff_pretty(
268
- this. results . get( ) ,
271
+ this. cursor . get( ) ,
269
272
& state_on_unwind,
270
- this. results . analysis( )
273
+ this. cursor . analysis( )
271
274
) ,
272
275
)
273
276
} ) ?;
274
277
}
275
278
276
279
mir:: TerminatorKind :: Yield { resume, resume_arg, .. } => {
277
280
self . write_row ( w, "" , "(on yield resume)" , |this, w, fmt| {
278
- let state_on_coroutine_drop = this. results . get ( ) . clone ( ) ;
279
- this. results . apply_custom_effect ( |analysis, state| {
281
+ let state_on_coroutine_drop = this. cursor . get ( ) . clone ( ) ;
282
+ this. cursor . apply_custom_effect ( |analysis, state| {
280
283
analysis. apply_call_return_effect (
281
284
state,
282
285
resume,
@@ -290,9 +293,9 @@ where
290
293
colspan = this. style. num_state_columns( ) ,
291
294
fmt = fmt,
292
295
diff = diff_pretty(
293
- this. results . get( ) ,
296
+ this. cursor . get( ) ,
294
297
& state_on_coroutine_drop,
295
- this. results . analysis( )
298
+ this. cursor . analysis( )
296
299
) ,
297
300
)
298
301
} ) ?;
@@ -302,8 +305,8 @@ where
302
305
if !targets. is_empty ( ) =>
303
306
{
304
307
self . write_row ( w, "" , "(on successful return)" , |this, w, fmt| {
305
- let state_on_unwind = this. results . get ( ) . clone ( ) ;
306
- this. results . apply_custom_effect ( |analysis, state| {
308
+ let state_on_unwind = this. cursor . get ( ) . clone ( ) ;
309
+ this. cursor . apply_custom_effect ( |analysis, state| {
307
310
analysis. apply_call_return_effect (
308
311
state,
309
312
block,
@@ -317,9 +320,9 @@ where
317
320
colspan = this. style. num_state_columns( ) ,
318
321
fmt = fmt,
319
322
diff = diff_pretty(
320
- this. results . get( ) ,
323
+ this. cursor . get( ) ,
321
324
& state_on_unwind,
322
- this. results . analysis( )
325
+ this. cursor . analysis( )
323
326
) ,
324
327
)
325
328
} ) ?;
@@ -328,7 +331,9 @@ where
328
331
_ => { }
329
332
} ;
330
333
331
- write ! ( w, "</table>" )
334
+ write ! ( w, "</table>" ) ?;
335
+
336
+ Ok ( v)
332
337
}
333
338
334
339
fn write_block_header_simple (
@@ -407,9 +412,9 @@ where
407
412
block : BasicBlock ,
408
413
) -> io:: Result < ( ) > {
409
414
let diffs = StateDiffCollector :: run (
410
- self . results . body ( ) ,
415
+ self . cursor . body ( ) ,
411
416
block,
412
- self . results . mut_results ( ) ,
417
+ self . cursor . mut_results ( ) ,
413
418
self . style ,
414
419
) ;
415
420
@@ -420,7 +425,7 @@ where
420
425
if A :: Direction :: IS_FORWARD { it. next ( ) . unwrap ( ) } else { it. next_back ( ) . unwrap ( ) }
421
426
} ;
422
427
423
- for ( i, statement) in self . results . body ( ) [ block] . statements . iter ( ) . enumerate ( ) {
428
+ for ( i, statement) in self . cursor . body ( ) [ block] . statements . iter ( ) . enumerate ( ) {
424
429
let statement_str = format ! ( "{statement:?}" ) ;
425
430
let index_str = format ! ( "{i}" ) ;
426
431
@@ -442,7 +447,7 @@ where
442
447
assert ! ( diffs_after. is_empty( ) ) ;
443
448
assert ! ( diffs_before. as_ref( ) . map_or( true , ExactSizeIterator :: is_empty) ) ;
444
449
445
- let terminator = self . results . body ( ) [ block] . terminator ( ) ;
450
+ let terminator = self . cursor . body ( ) [ block] . terminator ( ) ;
446
451
let mut terminator_str = String :: new ( ) ;
447
452
terminator. kind . fmt_head ( & mut terminator_str) . unwrap ( ) ;
448
453
@@ -492,8 +497,8 @@ where
492
497
mir : & str ,
493
498
) -> io:: Result < ( ) > {
494
499
self . write_row ( w, i, mir, |this, w, fmt| {
495
- let state = this. results . get ( ) ;
496
- let analysis = this. results . analysis ( ) ;
500
+ let state = this. cursor . get ( ) ;
501
+ let analysis = this. cursor . analysis ( ) ;
497
502
498
503
// FIXME: The full state vector can be quite long. It would be nice to split on commas
499
504
// and use some text wrapping algorithm.
0 commit comments