Skip to content

Commit f521263

Browse files
committed
Auto merge of #162866 - JonathanBrouwer:rollup-SxA0kHb, r=<try>
Rollup of 9 pull requests try-job: dist-various-1 try-job: test-various try-job: test-x86_64-gnu-aux try-job: test-x86_64-gnu-llvm-21-3 try-job: test-x86_64-msvc-1 try-job: test-aarch64-apple-1 try-job: test-aarch64-apple-2 try-job: test-x86_64-mingw-1 try-job: test-i686-msvc try-job: test-armhf-gnu
2 parents 92801ed + 77e3e31 commit f521263

18 files changed

Lines changed: 594 additions & 159 deletions

File tree

compiler/rustc_borrowck/src/polonius/constraints.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use rustc_index::interval::SparseIntervalMatrix;
33
use rustc_middle::mir::{Body, Location};
44
use rustc_middle::ty::RegionVid;
55
use rustc_mir_dataflow::points::PointIndex;
6+
use tracing::debug;
67

78
use crate::BorrowSet;
89
use crate::constraints::OutlivesConstraint;
@@ -253,6 +254,7 @@ fn compute_forward_successor(
253254

254255
// 2. Otherwise, gather the edges due to explicit region liveness, when applicable.
255256
if !live_regions.contains(region, next_point) {
257+
debug!(?region, ?next_point, "region isn't live at successor");
256258
return None;
257259
}
258260

@@ -272,6 +274,8 @@ fn compute_forward_successor(
272274
.flatten()
273275
.unwrap_or(ConstraintDirection::Bidirectional);
274276

277+
debug!(?direction);
278+
275279
match direction {
276280
ConstraintDirection::Backward => {
277281
// Contravariant cases: loans flow in the inverse direction, but we're only interested
@@ -300,6 +304,7 @@ fn compute_backward_successor(
300304
// Liveness flows into the regions live at the next point. So, in a backwards view, we'll link
301305
// the region from the current point, if it's live there, to the previous point.
302306
if !live_regions.contains(region, current_point) {
307+
debug!(?region, ?current_point, "region isn't live at current point");
303308
return None;
304309
}
305310

compiler/rustc_borrowck/src/polonius/dump.rs

Lines changed: 158 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@ use rustc_session::config::MirIncludeSpans;
1010

1111
use crate::borrow_set::BorrowSet;
1212
use crate::constraints::OutlivesConstraint;
13+
use crate::dataflow::BorrowIndex;
1314
use crate::polonius::{LocalizedConstraintGraphVisitor, LocalizedNode, PoloniusContext};
1415
use crate::region_infer::values::LivenessValues;
1516
use crate::type_check::Locations;
1617
use 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.
1924
pub(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

Comments
 (0)