1
1
use std:: io;
2
2
3
+ use rustc_data_structures:: fx:: FxHashSet ;
4
+ use rustc_index:: IndexVec ;
3
5
use rustc_middle:: mir:: pretty:: {
4
6
PassWhere , PrettyPrintMirOptions , create_dump_file, dump_enabled, dump_mir_to_writer,
5
7
} ;
6
8
use rustc_middle:: mir:: { Body , ClosureRegionRequirements } ;
7
- use rustc_middle:: ty:: TyCtxt ;
9
+ use rustc_middle:: ty:: { RegionVid , TyCtxt } ;
8
10
use rustc_session:: config:: MirIncludeSpans ;
9
11
10
12
use crate :: borrow_set:: BorrowSet ;
13
+ use crate :: constraints:: OutlivesConstraint ;
11
14
use crate :: polonius:: { LocalizedOutlivesConstraint , LocalizedOutlivesConstraintSet } ;
15
+ use crate :: type_check:: Locations ;
12
16
use crate :: { BorrowckInferCtxt , RegionInferenceContext } ;
13
17
14
18
/// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information.
@@ -50,6 +54,8 @@ pub(crate) fn dump_polonius_mir<'tcx>(
50
54
/// - the NLL MIR
51
55
/// - the list of polonius localized constraints
52
56
/// - a mermaid graph of the CFG
57
+ /// - a mermaid graph of the NLL regions and the constraints between them
58
+ /// - a mermaid graph of the NLL SCCs and the constraints between them
53
59
fn emit_polonius_dump < ' tcx > (
54
60
tcx : TyCtxt < ' tcx > ,
55
61
body : & Body < ' tcx > ,
@@ -68,7 +74,7 @@ fn emit_polonius_dump<'tcx>(
68
74
// Section 1: the NLL + Polonius MIR.
69
75
writeln ! ( out, "<div>" ) ?;
70
76
writeln ! ( out, "Raw MIR dump" ) ?;
71
- writeln ! ( out, "<code><pre >" ) ?;
77
+ writeln ! ( out, "<pre><code >" ) ?;
72
78
emit_html_mir (
73
79
tcx,
74
80
body,
@@ -78,15 +84,31 @@ fn emit_polonius_dump<'tcx>(
78
84
closure_region_requirements,
79
85
out,
80
86
) ?;
81
- writeln ! ( out, "</pre ></code >" ) ?;
87
+ writeln ! ( out, "</code ></pre >" ) ?;
82
88
writeln ! ( out, "</div>" ) ?;
83
89
84
90
// Section 2: mermaid visualization of the CFG.
85
91
writeln ! ( out, "<div>" ) ?;
86
92
writeln ! ( out, "Control-flow graph" ) ?;
87
- writeln ! ( out, "<code>< pre class='mermaid'>" ) ?;
93
+ writeln ! ( out, "<pre class='mermaid'>" ) ?;
88
94
emit_mermaid_cfg ( body, out) ?;
89
- writeln ! ( out, "</pre></code>" ) ?;
95
+ writeln ! ( out, "</pre>" ) ?;
96
+ writeln ! ( out, "</div>" ) ?;
97
+
98
+ // Section 3: mermaid visualization of the NLL region graph.
99
+ writeln ! ( out, "<div>" ) ?;
100
+ writeln ! ( out, "NLL regions" ) ?;
101
+ writeln ! ( out, "<pre class='mermaid'>" ) ?;
102
+ emit_mermaid_nll_regions ( regioncx, out) ?;
103
+ writeln ! ( out, "</pre>" ) ?;
104
+ writeln ! ( out, "</div>" ) ?;
105
+
106
+ // Section 4: mermaid visualization of the NLL SCC graph.
107
+ writeln ! ( out, "<div>" ) ?;
108
+ writeln ! ( out, "NLL SCCs" ) ?;
109
+ writeln ! ( out, "<pre class='mermaid'>" ) ?;
110
+ emit_mermaid_nll_sccs ( regioncx, out) ?;
111
+ writeln ! ( out, "</pre>" ) ?;
90
112
writeln ! ( out, "</div>" ) ?;
91
113
92
114
// Finalize the dump with the HTML epilogue.
@@ -261,3 +283,112 @@ fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()>
261
283
262
284
Ok ( ( ) )
263
285
}
286
+
287
+ /// Emits a region's label: index, universe, external name.
288
+ fn render_region (
289
+ region : RegionVid ,
290
+ regioncx : & RegionInferenceContext < ' _ > ,
291
+ out : & mut dyn io:: Write ,
292
+ ) -> io:: Result < ( ) > {
293
+ let def = regioncx. region_definition ( region) ;
294
+ let universe = def. universe ;
295
+
296
+ write ! ( out, "'{}" , region. as_usize( ) ) ?;
297
+ if !universe. is_root ( ) {
298
+ write ! ( out, "/{universe:?}" ) ?;
299
+ }
300
+ if let Some ( name) = def. external_name . and_then ( |e| e. get_name ( ) ) {
301
+ write ! ( out, " ({name})" ) ?;
302
+ }
303
+ Ok ( ( ) )
304
+ }
305
+
306
+ /// Emits a mermaid flowchart of the NLL regions and the outlives constraints between them, similar
307
+ /// to the graphviz version.
308
+ fn emit_mermaid_nll_regions < ' tcx > (
309
+ regioncx : & RegionInferenceContext < ' tcx > ,
310
+ out : & mut dyn io:: Write ,
311
+ ) -> io:: Result < ( ) > {
312
+ // The mermaid chart type: a top-down flowchart.
313
+ writeln ! ( out, "flowchart TD" ) ?;
314
+
315
+ // Emit the region nodes.
316
+ for region in regioncx. var_infos . indices ( ) {
317
+ write ! ( out, "{}[\" " , region. as_usize( ) ) ?;
318
+ render_region ( region, regioncx, out) ?;
319
+ writeln ! ( out, "\" ]" ) ?;
320
+ }
321
+
322
+ // Get a set of edges to check for the reverse edge being present.
323
+ let edges: FxHashSet < _ > = regioncx. outlives_constraints ( ) . map ( |c| ( c. sup , c. sub ) ) . collect ( ) ;
324
+
325
+ // Order (and deduplicate) edges for traversal, to display them in a generally increasing order.
326
+ let constraint_key = |c : & OutlivesConstraint < ' _ > | {
327
+ let min = c. sup . min ( c. sub ) ;
328
+ let max = c. sup . max ( c. sub ) ;
329
+ ( min, max)
330
+ } ;
331
+ let mut ordered_edges: Vec < _ > = regioncx. outlives_constraints ( ) . collect ( ) ;
332
+ ordered_edges. sort_by_key ( |c| constraint_key ( c) ) ;
333
+ ordered_edges. dedup_by_key ( |c| constraint_key ( c) ) ;
334
+
335
+ for outlives in ordered_edges {
336
+ // Source node.
337
+ write ! ( out, "{} " , outlives. sup. as_usize( ) ) ?;
338
+
339
+ // The kind of arrow: bidirectional if the opposite edge exists in the set.
340
+ if edges. contains ( & ( outlives. sub , outlives. sup ) ) {
341
+ write ! ( out, "<" ) ?;
342
+ }
343
+ write ! ( out, "-- " ) ?;
344
+
345
+ // Edge label from its `Locations`.
346
+ match outlives. locations {
347
+ Locations :: All ( _) => write ! ( out, "All" ) ?,
348
+ Locations :: Single ( location) => write ! ( out, "{:?}" , location) ?,
349
+ }
350
+
351
+ // Target node.
352
+ writeln ! ( out, " --> {}" , outlives. sub. as_usize( ) ) ?;
353
+ }
354
+ Ok ( ( ) )
355
+ }
356
+
357
+ /// Emits a mermaid flowchart of the NLL SCCs and the outlives constraints between them, similar
358
+ /// to the graphviz version.
359
+ fn emit_mermaid_nll_sccs < ' tcx > (
360
+ regioncx : & RegionInferenceContext < ' tcx > ,
361
+ out : & mut dyn io:: Write ,
362
+ ) -> io:: Result < ( ) > {
363
+ // The mermaid chart type: a top-down flowchart.
364
+ writeln ! ( out, "flowchart TD" ) ?;
365
+
366
+ // Gather and emit the SCC nodes.
367
+ let mut nodes_per_scc: IndexVec < _ , _ > =
368
+ regioncx. constraint_sccs ( ) . all_sccs ( ) . map ( |_| Vec :: new ( ) ) . collect ( ) ;
369
+ for region in regioncx. var_infos . indices ( ) {
370
+ let scc = regioncx. constraint_sccs ( ) . scc ( region) ;
371
+ nodes_per_scc[ scc] . push ( region) ;
372
+ }
373
+ for ( scc, regions) in nodes_per_scc. iter_enumerated ( ) {
374
+ // The node label: the regions contained in the SCC.
375
+ write ! ( out, "{scc}[\" SCC({scc}) = {{" , scc = scc. as_usize( ) ) ?;
376
+ for ( idx, & region) in regions. iter ( ) . enumerate ( ) {
377
+ render_region ( region, regioncx, out) ?;
378
+ if idx < regions. len ( ) - 1 {
379
+ write ! ( out, "," ) ?;
380
+ }
381
+ }
382
+ writeln ! ( out, "}}\" ]" ) ?;
383
+ }
384
+
385
+ // Emit the edges between SCCs.
386
+ let edges = regioncx. constraint_sccs ( ) . all_sccs ( ) . flat_map ( |source| {
387
+ regioncx. constraint_sccs ( ) . successors ( source) . iter ( ) . map ( move |& target| ( source, target) )
388
+ } ) ;
389
+ for ( source, target) in edges {
390
+ writeln ! ( out, "{} --> {}" , source. as_usize( ) , target. as_usize( ) ) ?;
391
+ }
392
+
393
+ Ok ( ( ) )
394
+ }
0 commit comments