@@ -10,11 +10,16 @@ use rustc_session::config::MirIncludeSpans;
1010
1111use crate :: borrow_set:: BorrowSet ;
1212use crate :: constraints:: OutlivesConstraint ;
13+ use crate :: dataflow:: BorrowIndex ;
1314use crate :: polonius:: { LocalizedConstraintGraphVisitor , LocalizedNode , PoloniusContext } ;
1415use crate :: region_infer:: values:: LivenessValues ;
1516use crate :: type_check:: Locations ;
1617use crate :: { BorrowckInferCtxt , ClosureRegionRequirements , RegionInferenceContext } ;
1718
19+ /// The polonius MIR dump template: a regular HTML file for easy editing, with special dummy
20+ /// sections to be replaced by real contents.
21+ const TEMPLATE : & str = include_str ! ( "./dump/polonius-mir-dump.template.html" ) ;
22+
1823/// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information.
1924pub ( crate ) fn dump_polonius_mir < ' tcx > (
2025 infcx : & BorrowckInferCtxt < ' tcx > ,
@@ -36,7 +41,7 @@ pub(crate) fn dump_polonius_mir<'tcx>(
3641
3742 // If we have a polonius graph to dump along the rest of the MIR and NLL info, we extract its
3843 // constraints here.
39- let mut collector = LocalizedOutlivesConstraintCollector { constraints : Vec :: new ( ) } ;
44+ let mut collector = MirDumpCollector :: default ( ) ;
4045 if let Some ( graph) = & polonius_context. graph {
4146 graph. traverse (
4247 body,
@@ -72,7 +77,7 @@ pub(crate) fn dump_polonius_mir<'tcx>(
7277
7378 let _ = try {
7479 let mut file = dumper. create_dump_file ( "html" , body) ?;
75- emit_polonius_dump ( & dumper, body, regioncx, borrow_set, & collector. constraints , & mut file) ?;
80+ emit_polonius_dump ( & dumper, body, regioncx, borrow_set, & collector, & mut file) ?;
7681 } ;
7782}
7883
@@ -84,12 +89,19 @@ struct LocalizedOutlivesConstraint {
8489 to : PointIndex ,
8590}
8691
87- /// Visitor to record constraints encountered when traversing the localized constraint graph.
88- struct LocalizedOutlivesConstraintCollector {
92+ /// Visitor to record constraints encountered when traversing the localized constraint graph, as
93+ /// well as the reachability of each loan.
94+ #[ derive( Default ) ]
95+ struct MirDumpCollector {
8996 constraints : Vec < LocalizedOutlivesConstraint > ,
97+ reachability : FxIndexMap < BorrowIndex , Vec < LocalizedNode > > ,
9098}
9199
92- impl LocalizedConstraintGraphVisitor for LocalizedOutlivesConstraintCollector {
100+ impl LocalizedConstraintGraphVisitor for MirDumpCollector {
101+ fn on_node_traversed ( & mut self , loan : BorrowIndex , node : LocalizedNode ) {
102+ self . reachability . entry ( loan) . or_default ( ) . push ( node) ;
103+ }
104+
93105 fn on_successor_discovered ( & mut self , current_node : LocalizedNode , successor : LocalizedNode ) {
94106 self . constraints . push ( LocalizedOutlivesConstraint {
95107 source : current_node. region ,
@@ -111,75 +123,77 @@ fn emit_polonius_dump<'tcx>(
111123 body : & Body < ' tcx > ,
112124 regioncx : & RegionInferenceContext < ' tcx > ,
113125 borrow_set : & BorrowSet < ' tcx > ,
114- localized_outlives_constraints : & [ LocalizedOutlivesConstraint ] ,
126+ collector : & MirDumpCollector ,
115127 out : & mut dyn io:: Write ,
116128) -> io:: Result < ( ) > {
117- // Prepare the HTML dump file prologue.
118- writeln ! ( out, "<!DOCTYPE html>" ) ?;
119- writeln ! ( out, "<html>" ) ?;
120- writeln ! ( out, "<head><title>Polonius MIR dump</title></head>" ) ?;
121- writeln ! ( out, "<body>" ) ?;
122-
123- // Section 1: the NLL + Polonius MIR.
124- writeln ! ( out, "<div>" ) ?;
125- writeln ! ( out, "Raw MIR dump" ) ?;
126- writeln ! ( out, "<pre><code>" ) ?;
127- emit_html_mir ( dumper, body, out) ?;
128- writeln ! ( out, "</code></pre>" ) ?;
129- writeln ! ( out, "</div>" ) ?;
130-
131- // Section 2: mermaid visualization of the polonius constraint graph.
132- writeln ! ( out, "<div>" ) ?;
133- writeln ! ( out, "Polonius constraint graph" ) ?;
134- writeln ! ( out, "<pre class='mermaid'>" ) ?;
135- let edge_count = emit_mermaid_constraint_graph (
136- borrow_set,
137- regioncx. liveness_constraints ( ) ,
138- & localized_outlives_constraints,
139- out,
140- ) ?;
141- writeln ! ( out, "</pre>" ) ?;
142- writeln ! ( out, "</div>" ) ?;
143-
144- // Section 3: mermaid visualization of the CFG.
145- writeln ! ( out, "<div>" ) ?;
146- writeln ! ( out, "Control-flow graph" ) ?;
147- writeln ! ( out, "<pre class='mermaid'>" ) ?;
148- emit_mermaid_cfg ( body, out) ?;
149- writeln ! ( out, "</pre>" ) ?;
150- writeln ! ( out, "</div>" ) ?;
151-
152- // Section 4: mermaid visualization of the NLL region graph.
153- writeln ! ( out, "<div>" ) ?;
154- writeln ! ( out, "NLL regions" ) ?;
155- writeln ! ( out, "<pre class='mermaid'>" ) ?;
156- emit_mermaid_nll_regions ( dumper. tcx ( ) , regioncx, out) ?;
157- writeln ! ( out, "</pre>" ) ?;
158- writeln ! ( out, "</div>" ) ?;
159-
160- // Section 5: mermaid visualization of the NLL SCC graph.
161- writeln ! ( out, "<div>" ) ?;
162- writeln ! ( out, "NLL SCCs" ) ?;
163- writeln ! ( out, "<pre class='mermaid'>" ) ?;
164- emit_mermaid_nll_sccs ( dumper. tcx ( ) , regioncx, out) ?;
165- writeln ! ( out, "</pre>" ) ?;
166- writeln ! ( out, "</div>" ) ?;
167-
168- // Finalize the dump with the HTML epilogue.
169- writeln ! (
170- out,
171- "<script src='https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js'></script>"
172- ) ?;
173- writeln ! ( out, "<script>" ) ?;
174- writeln ! (
175- out,
176- "mermaid.initialize({{ startOnLoad: false, maxEdges: {} }});" ,
177- edge_count. max( 100 ) ,
178- ) ?;
179- writeln ! ( out, "mermaid.run({{ querySelector: '.mermaid' }})" ) ?;
180- writeln ! ( out, "</script>" ) ?;
181- writeln ! ( out, "</body>" ) ?;
182- writeln ! ( out, "</html>" ) ?;
129+ let mut edge_count = 0 ;
130+
131+ // We replace the dummy $SECTION tokens from the HTML polonius dump template, and emit the
132+ // result into the given writer.
133+ for chunk in TEMPLATE . split ( "$SECTION" ) {
134+ match chunk. strip_prefix ( "_" ) {
135+ None => {
136+ // We're at the beginning of the template: this is the prologue to emit as-is.
137+ writeln ! ( out, "{}" , chunk) ?;
138+ }
139+ Some ( section) => {
140+ // This is the start of a prefixed section, we look for its identifier.
141+ let dummy_section_end = section
142+ . find ( "<" )
143+ . expect ( "the template section end boundary needs to be present" ) ;
144+ let section_identifier = section[ ..dummy_section_end] . trim ( ) ;
145+
146+ // Emit the real section instead of the dummy token.
147+ match section_identifier {
148+ "MIR" => {
149+ emit_html_mir ( dumper, body, out) ?;
150+ }
151+ "POLONIUS_CONSTRAINTS" => {
152+ edge_count = emit_mermaid_constraint_graph (
153+ borrow_set,
154+ regioncx. liveness_constraints ( ) ,
155+ & collector. constraints ,
156+ out,
157+ ) ?;
158+ }
159+ "POLONIUS_REACHABILITY" => {
160+ emit_loan_reachability (
161+ borrow_set,
162+ regioncx. liveness_constraints ( ) ,
163+ & collector. reachability ,
164+ out,
165+ ) ?;
166+ }
167+ "CFG" => {
168+ emit_mermaid_cfg ( body, out) ?;
169+ }
170+ "NLL_CONSTRAINTS" => {
171+ emit_mermaid_nll_regions ( dumper. tcx ( ) , regioncx, out) ?;
172+ }
173+ "NLL_SCCS" => {
174+ emit_mermaid_nll_sccs ( dumper. tcx ( ) , regioncx, out) ?;
175+ }
176+ "INITIALIZATION" => {
177+ writeln ! ( out, "<script>" ) ?;
178+ writeln ! (
179+ out,
180+ "mermaid.initialize({{ startOnLoad: false, maxEdges: {} }});" ,
181+ edge_count. max( 100 ) ,
182+ ) ?;
183+ writeln ! ( out, "mermaid.run({{ querySelector: '.mermaid' }})" ) ?;
184+ writeln ! ( out, "</script>" ) ?;
185+ }
186+
187+ _ => {
188+ unreachable ! ( "unexpected dummy section identifier {:?}" , section_identifier)
189+ }
190+ }
191+
192+ // And finally, emit the contents that followed the dummy token.
193+ writeln ! ( out, "{}" , & section[ dummy_section_end..] ) ?;
194+ }
195+ }
196+ }
183197
184198 Ok ( ( ) )
185199}
@@ -431,15 +445,9 @@ fn emit_mermaid_constraint_graph<'tcx>(
431445 localized_outlives_constraints : & [ LocalizedOutlivesConstraint ] ,
432446 out : & mut dyn io:: Write ,
433447) -> io:: Result < usize > {
434- let location_name = |location : Location | {
435- // A MIR location looks like `bb5[2]`. As that is not a syntactically valid mermaid node id,
436- // transform it into `BB5_2`.
437- format ! ( "BB{}_{}" , location. block. index( ) , location. statement_index)
438- } ;
439- let region_name = |region : RegionVid | format ! ( "'{}" , region. index( ) ) ;
440- let node_name = |region : RegionVid , point : PointIndex | {
448+ let node_label = |region : RegionVid , point : PointIndex | {
441449 let location = liveness. location_from_point ( point) ;
442- format ! ( "{}_{}" , region_name ( region) , location_name ( location) )
450+ node_name ( region, location)
443451 } ;
444452
445453 // The mermaid chart type: a top-down flowchart, which supports subgraphs.
@@ -474,7 +482,7 @@ fn emit_mermaid_constraint_graph<'tcx>(
474482 for ( region, points) in points_per_region {
475483 writeln ! ( out, " subgraph \" {}\" " , region_name( region) ) ?;
476484 for point in points {
477- writeln ! ( out, " {}" , node_name ( region, point) ) ?;
485+ writeln ! ( out, " {}" , node_label ( region, point) ) ?;
478486 }
479487 writeln ! ( out, " end\n " ) ?;
480488 }
@@ -485,8 +493,8 @@ fn emit_mermaid_constraint_graph<'tcx>(
485493 writeln ! (
486494 out,
487495 " {} --> {}" ,
488- node_name ( constraint. source, constraint. from) ,
489- node_name ( constraint. target, constraint. to) ,
496+ node_label ( constraint. source, constraint. from) ,
497+ node_label ( constraint. target, constraint. to) ,
490498 ) ?;
491499 }
492500
@@ -495,3 +503,70 @@ fn emit_mermaid_constraint_graph<'tcx>(
495503 let edge_count = borrow_set. len ( ) + localized_outlives_constraints. len ( ) ;
496504 Ok ( edge_count)
497505}
506+
507+ /// Emits the reachability of loans: a list of all nodes reached while traversing the polonius
508+ /// constraint graph.
509+ fn emit_loan_reachability (
510+ borrow_set : & BorrowSet < ' _ > ,
511+ liveness : & LivenessValues ,
512+ reachability : & FxIndexMap < BorrowIndex , Vec < LocalizedNode > > ,
513+ out : & mut dyn io:: Write ,
514+ ) -> io:: Result < ( ) > {
515+ for ( loan, _) in borrow_set. iter_enumerated ( ) {
516+ let Some ( reachability) = reachability. get ( & loan) else {
517+ continue ;
518+ } ;
519+ let loan = format ! ( "L{}" , loan. index( ) ) ;
520+
521+ // The button to display the loan trace. The javascript event listener is hooked up in the
522+ // template itself.
523+ writeln ! (
524+ out,
525+ "<div class='trace'><button data-loan='{loan}'>Trace for loan {loan}</button></div>"
526+ ) ?;
527+
528+ // The actual trace contents, hidden by default.
529+ writeln ! ( out, "<div id='trace-{loan}' class='trace hidden'>" ) ?;
530+ writeln ! ( out, "<div>Trace for loan {loan}</div>" ) ?;
531+ writeln ! ( out, "<ul>" ) ?;
532+ for ( idx, node) in reachability. iter ( ) . enumerate ( ) {
533+ writeln ! ( out, "<li>" ) ?;
534+
535+ let location = liveness. location_from_point ( node. point ) ;
536+ let kind = if idx == 0 { "starts in" } else { "reaches" } ;
537+ writeln ! (
538+ out,
539+ "<code>{loan}</code> {kind} <code>{}</code>" ,
540+ node_name( node. region, location) ,
541+ ) ?;
542+
543+ // It's useful to know whether the region we're reaching is live at this point.
544+ let node_liveness =
545+ if liveness. is_live_at ( node. region , location) { "live" } else { "not live" } ;
546+ writeln ! (
547+ out,
548+ "/ at <code>{:?}</code>: <code>'{}</code> is {}" ,
549+ location,
550+ node. region. index( ) ,
551+ node_liveness,
552+ ) ?;
553+ writeln ! ( out, "</li>" ) ?;
554+ }
555+ writeln ! ( out, "</ul>" ) ?;
556+ writeln ! ( out, "</div>" ) ?;
557+ }
558+
559+ Ok ( ( ) )
560+ }
561+
562+ fn region_name ( region : RegionVid ) -> String {
563+ format ! ( "'{}" , region. index( ) )
564+ }
565+ /// A MIR location looks like `bb5[2]`. As that is not a syntactically valid mermaid node id,
566+ /// transform it into `BB5_2`.
567+ fn location_name ( location : Location ) -> String {
568+ format ! ( "BB{}_{}" , location. block. index( ) , location. statement_index)
569+ }
570+ fn node_name ( region : RegionVid , location : Location ) -> String {
571+ format ! ( "{}_{}" , region_name( region) , location_name( location) )
572+ }
0 commit comments