11use std:: io;
22
3+ use rustc_data_structures:: fx:: FxHashSet ;
4+ use rustc_index:: IndexVec ;
35use rustc_middle:: mir:: pretty:: {
46 PassWhere , PrettyPrintMirOptions , create_dump_file, dump_enabled, dump_mir_to_writer,
57} ;
68use rustc_middle:: mir:: { Body , ClosureRegionRequirements } ;
7- use rustc_middle:: ty:: TyCtxt ;
9+ use rustc_middle:: ty:: { RegionVid , TyCtxt } ;
810use rustc_session:: config:: MirIncludeSpans ;
911
1012use crate :: borrow_set:: BorrowSet ;
13+ use crate :: constraints:: OutlivesConstraint ;
1114use crate :: polonius:: { LocalizedOutlivesConstraint , LocalizedOutlivesConstraintSet } ;
15+ use crate :: type_check:: Locations ;
1216use crate :: { BorrowckInferCtxt , RegionInferenceContext } ;
1317
1418/// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information.
@@ -50,6 +54,8 @@ pub(crate) fn dump_polonius_mir<'tcx>(
5054/// - the NLL MIR
5155/// - the list of polonius localized constraints
5256/// - 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
5359fn emit_polonius_dump < ' tcx > (
5460 tcx : TyCtxt < ' tcx > ,
5561 body : & Body < ' tcx > ,
@@ -68,7 +74,7 @@ fn emit_polonius_dump<'tcx>(
6874 // Section 1: the NLL + Polonius MIR.
6975 writeln ! ( out, "<div>" ) ?;
7076 writeln ! ( out, "Raw MIR dump" ) ?;
71- writeln ! ( out, "<code><pre >" ) ?;
77+ writeln ! ( out, "<pre><code >" ) ?;
7278 emit_html_mir (
7379 tcx,
7480 body,
@@ -78,15 +84,31 @@ fn emit_polonius_dump<'tcx>(
7884 closure_region_requirements,
7985 out,
8086 ) ?;
81- writeln ! ( out, "</pre ></code >" ) ?;
87+ writeln ! ( out, "</code ></pre >" ) ?;
8288 writeln ! ( out, "</div>" ) ?;
8389
8490 // Section 2: mermaid visualization of the CFG.
8591 writeln ! ( out, "<div>" ) ?;
8692 writeln ! ( out, "Control-flow graph" ) ?;
87- writeln ! ( out, "<code>< pre class='mermaid'>" ) ?;
93+ writeln ! ( out, "<pre class='mermaid'>" ) ?;
8894 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>" ) ?;
90112 writeln ! ( out, "</div>" ) ?;
91113
92114 // Finalize the dump with the HTML epilogue.
@@ -261,3 +283,112 @@ fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()>
261283
262284 Ok ( ( ) )
263285}
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