|
1 | 1 | use std::io;
|
2 | 2 |
|
| 3 | +use rustc_data_structures::fx::FxHashSet; |
3 | 4 | use rustc_middle::mir::pretty::{
|
4 | 5 | PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer,
|
5 | 6 | };
|
6 | 7 | use rustc_middle::mir::{Body, ClosureRegionRequirements};
|
7 |
| -use rustc_middle::ty::TyCtxt; |
| 8 | +use rustc_middle::ty::{RegionVid, TyCtxt}; |
8 | 9 | use rustc_session::config::MirIncludeSpans;
|
9 | 10 |
|
10 | 11 | use crate::borrow_set::BorrowSet;
|
| 12 | +use crate::constraints::OutlivesConstraint; |
11 | 13 | use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet};
|
| 14 | +use crate::type_check::Locations; |
12 | 15 | use crate::{BorrowckInferCtxt, RegionInferenceContext};
|
13 | 16 |
|
14 | 17 | /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information.
|
@@ -50,6 +53,7 @@ pub(crate) fn dump_polonius_mir<'tcx>(
|
50 | 53 | /// - the NLL MIR
|
51 | 54 | /// - the list of polonius localized constraints
|
52 | 55 | /// - a mermaid graph of the CFG
|
| 56 | +/// - a mermaid graph of the NLL regions and the constraints between them |
53 | 57 | fn emit_polonius_dump<'tcx>(
|
54 | 58 | tcx: TyCtxt<'tcx>,
|
55 | 59 | body: &Body<'tcx>,
|
@@ -89,6 +93,14 @@ fn emit_polonius_dump<'tcx>(
|
89 | 93 | writeln!(out, "</pre></code>")?;
|
90 | 94 | writeln!(out, "</div>")?;
|
91 | 95 |
|
| 96 | + // Section 3: mermaid visualization of the NLL region graph. |
| 97 | + writeln!(out, "<div>")?; |
| 98 | + writeln!(out, "NLL regions")?; |
| 99 | + writeln!(out, "<pre class='mermaid'>")?; |
| 100 | + emit_mermaid_nll_regions(regioncx, out)?; |
| 101 | + writeln!(out, "</pre>")?; |
| 102 | + writeln!(out, "</div>")?; |
| 103 | + |
92 | 104 | // Finalize the dump with the HTML epilogue.
|
93 | 105 | writeln!(
|
94 | 106 | out,
|
@@ -261,3 +273,73 @@ fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()>
|
261 | 273 |
|
262 | 274 | Ok(())
|
263 | 275 | }
|
| 276 | + |
| 277 | +/// Emits a region's label: index, universe, external name. |
| 278 | +fn render_region( |
| 279 | + region: RegionVid, |
| 280 | + regioncx: &RegionInferenceContext<'_>, |
| 281 | + out: &mut dyn io::Write, |
| 282 | +) -> io::Result<()> { |
| 283 | + let def = regioncx.region_definition(region); |
| 284 | + let universe = def.universe; |
| 285 | + |
| 286 | + write!(out, "'{}", region.as_usize())?; |
| 287 | + if !universe.is_root() { |
| 288 | + write!(out, "/{universe:?}")?; |
| 289 | + } |
| 290 | + if let Some(name) = def.external_name.and_then(|e| e.get_name()) { |
| 291 | + write!(out, " ({name})")?; |
| 292 | + } |
| 293 | + Ok(()) |
| 294 | +} |
| 295 | + |
| 296 | +/// Emits a mermaid flowchart of the NLL regions and the outlives constraints between them, similar |
| 297 | +/// to the graphviz version. |
| 298 | +fn emit_mermaid_nll_regions<'tcx>( |
| 299 | + regioncx: &RegionInferenceContext<'tcx>, |
| 300 | + out: &mut dyn io::Write, |
| 301 | +) -> io::Result<()> { |
| 302 | + // The mermaid chart type: a top-down flowchart. |
| 303 | + writeln!(out, "flowchart TD")?; |
| 304 | + |
| 305 | + // Emit the region nodes. |
| 306 | + for region in regioncx.var_infos.indices() { |
| 307 | + write!(out, "{}[\"", region.as_usize())?; |
| 308 | + render_region(region, regioncx, out)?; |
| 309 | + writeln!(out, "\"]")?; |
| 310 | + } |
| 311 | + |
| 312 | + // Get a set of edges to check for the reverse edge being present. |
| 313 | + let edges: FxHashSet<_> = regioncx.outlives_constraints().map(|c| (c.sup, c.sub)).collect(); |
| 314 | + |
| 315 | + // Order (and deduplicate) edges for traversal, to display them in a generally increasing order. |
| 316 | + let constraint_key = |c: &OutlivesConstraint<'_>| { |
| 317 | + let min = c.sup.min(c.sub); |
| 318 | + let max = c.sup.max(c.sub); |
| 319 | + (min, max) |
| 320 | + }; |
| 321 | + let mut ordered_edges: Vec<_> = regioncx.outlives_constraints().collect(); |
| 322 | + ordered_edges.sort_by_key(|c| constraint_key(c)); |
| 323 | + ordered_edges.dedup_by_key(|c| constraint_key(c)); |
| 324 | + |
| 325 | + for outlives in ordered_edges { |
| 326 | + // Source node. |
| 327 | + write!(out, "{} ", outlives.sup.as_usize())?; |
| 328 | + |
| 329 | + // The kind of arrow: bidirectional if the opposite edge exists in the set. |
| 330 | + if edges.contains(&(outlives.sub, outlives.sup)) { |
| 331 | + write!(out, "<")?; |
| 332 | + } |
| 333 | + write!(out, "-- ")?; |
| 334 | + |
| 335 | + // Edge label from its `Locations`. |
| 336 | + match outlives.locations { |
| 337 | + Locations::All(_) => write!(out, "All")?, |
| 338 | + Locations::Single(location) => write!(out, "{:?}", location)?, |
| 339 | + } |
| 340 | + |
| 341 | + // Target node. |
| 342 | + writeln!(out, " --> {}", outlives.sub.as_usize())?; |
| 343 | + } |
| 344 | + Ok(()) |
| 345 | +} |
0 commit comments