Rollup merge of #129711 - lqd:nll-mir-dumps, r=compiler-errors

Expand NLL MIR dumps

This PR is a first step to clean up and expand NLL MIR dumps:
- by restoring the "mir-include-spans" comments which are useful for `-Zdump-mir=nll`
- by adding the list of borrows to NLL MIR dumps, where they are introduced in the CFG and in which region

Comments in MIR dumps were turned off in #112346, but as shown in #114652 they were still useful for us working with NLL MIR dumps. So this PR pulls `-Z mir-include-spans` into its own options struct, so that passes dumping MIR can override them if need be. The rest of the compiler is not affected, only the "nll" pass dumps have these comments enabled again. The CLI still has priority when specifying the flag, so that we can explicitly turn them off in the `mir-opt` tests to keep blessed dumps easier to work with (which was one of the points of #112346).

Then, as part of a couple steps to improve NLL/polonius MIR dumps and `.dot` visualizations, I've also added the list of borrows and where they're introduced. I'm doing all this to help debug some polonius scope issues in my prototype location-sensitive analysis :3. I'll probably add member constraints soon.
This commit is contained in:
Matthias Krüger 2024-08-31 14:46:07 +02:00 committed by GitHub
commit 1c51e5b110
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 216 additions and 66 deletions

View File

@ -229,7 +229,7 @@ fn do_mir_borrowck<'tcx>(
// Dump MIR results into a file, if that is enabled. This let us // Dump MIR results into a file, if that is enabled. This let us
// write unit-tests, as well as helping with debugging. // write unit-tests, as well as helping with debugging.
nll::dump_mir_results(&infcx, body, &regioncx, &opt_closure_req); nll::dump_nll_mir(&infcx, body, &regioncx, &opt_closure_req, &borrow_set);
// We also have a `#[rustc_regions]` annotation that causes us to dump // We also have a `#[rustc_regions]` annotation that causes us to dump
// information. // information.

View File

@ -9,6 +9,7 @@ use polonius_engine::{Algorithm, Output};
use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_index::IndexSlice; use rustc_index::IndexSlice;
use rustc_middle::mir::pretty::{dump_mir_with_options, PrettyPrintMirOptions};
use rustc_middle::mir::{ use rustc_middle::mir::{
create_dump_file, dump_enabled, dump_mir, Body, ClosureOutlivesSubject, create_dump_file, dump_enabled, dump_mir, Body, ClosureOutlivesSubject,
ClosureRegionRequirements, PassWhere, Promoted, ClosureRegionRequirements, PassWhere, Promoted,
@ -19,6 +20,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::move_paths::MoveData;
use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_mir_dataflow::ResultsCursor; use rustc_mir_dataflow::ResultsCursor;
use rustc_session::config::MirIncludeSpans;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use crate::borrow_set::BorrowSet; use crate::borrow_set::BorrowSet;
@ -208,52 +210,90 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
} }
} }
pub(super) fn dump_mir_results<'tcx>( /// `-Zdump-mir=nll` dumps MIR annotated with NLL specific information:
/// - free regions
/// - inferred region values
/// - region liveness
/// - inference constraints and their causes
///
/// As well as graphviz `.dot` visualizations of:
/// - the region constraints graph
/// - the region SCC graph
pub(super) fn dump_nll_mir<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>, body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>, regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>, closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
borrow_set: &BorrowSet<'tcx>,
) { ) {
if !dump_enabled(infcx.tcx, "nll", body.source.def_id()) { let tcx = infcx.tcx;
if !dump_enabled(tcx, "nll", body.source.def_id()) {
return; return;
} }
dump_mir(infcx.tcx, false, "nll", &0, body, |pass_where, out| { // We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in
match pass_where { // #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example,
// Before the CFG, dump out the values for each region variable. // they're always disabled in mir-opt tests to make working with blessed dumps easier.
PassWhere::BeforeCFG => { let options = PrettyPrintMirOptions {
regioncx.dump_mir(infcx.tcx, out)?; include_extra_comments: matches!(
writeln!(out, "|")?; infcx.tcx.sess.opts.unstable_opts.mir_include_spans,
MirIncludeSpans::On | MirIncludeSpans::Nll
if let Some(closure_region_requirements) = closure_region_requirements { ),
writeln!(out, "| Free Region Constraints")?; };
for_each_region_constraint( dump_mir_with_options(
infcx.tcx, tcx,
closure_region_requirements, false,
&mut |msg| writeln!(out, "| {msg}"), "nll",
)?; &0,
body,
|pass_where, out| {
match pass_where {
// Before the CFG, dump out the values for each region variable.
PassWhere::BeforeCFG => {
regioncx.dump_mir(tcx, out)?;
writeln!(out, "|")?; writeln!(out, "|")?;
if let Some(closure_region_requirements) = closure_region_requirements {
writeln!(out, "| Free Region Constraints")?;
for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| {
writeln!(out, "| {msg}")
})?;
writeln!(out, "|")?;
}
if borrow_set.len() > 0 {
writeln!(out, "| Borrows")?;
for (borrow_idx, borrow_data) in borrow_set.iter_enumerated() {
writeln!(
out,
"| {:?}: issued at {:?} in {:?}",
borrow_idx, borrow_data.reserve_location, borrow_data.region
)?;
}
writeln!(out, "|")?;
}
} }
PassWhere::BeforeLocation(_) => {}
PassWhere::AfterTerminator(_) => {}
PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
} }
Ok(())
},
options,
);
PassWhere::BeforeLocation(_) => {} // Also dump the region constraint graph as a graphviz file.
PassWhere::AfterTerminator(_) => {}
PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
}
Ok(())
});
// Also dump the inference graph constraints as a graphviz file.
let _: io::Result<()> = try { let _: io::Result<()> = try {
let mut file = create_dump_file(infcx.tcx, "regioncx.all.dot", false, "nll", &0, body)?; let mut file = create_dump_file(tcx, "regioncx.all.dot", false, "nll", &0, body)?;
regioncx.dump_graphviz_raw_constraints(&mut file)?; regioncx.dump_graphviz_raw_constraints(&mut file)?;
}; };
// Also dump the inference graph constraints as a graphviz file. // Also dump the region constraint SCC graph as a graphviz file.
let _: io::Result<()> = try { let _: io::Result<()> = try {
let mut file = create_dump_file(infcx.tcx, "regioncx.scc.dot", false, "nll", &0, body)?; let mut file = create_dump_file(tcx, "regioncx.scc.dot", false, "nll", &0, body)?;
regioncx.dump_graphviz_scc_constraints(&mut file)?; regioncx.dump_graphviz_scc_constraints(&mut file)?;
}; };
} }

View File

@ -46,7 +46,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
dot::render(&RawConstraints { regioncx: self }, &mut w) dot::render(&RawConstraints { regioncx: self }, &mut w)
} }
/// Write out the region constraint graph. /// Write out the region constraint SCC graph.
pub(crate) fn dump_graphviz_scc_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> { pub(crate) fn dump_graphviz_scc_constraints(&self, mut w: &mut dyn Write) -> io::Result<()> {
let mut nodes_per_scc: IndexVec<ConstraintSccIndex, _> = let mut nodes_per_scc: IndexVec<ConstraintSccIndex, _> =
self.constraint_sccs.all_sccs().map(|_| Vec::new()).collect(); self.constraint_sccs.all_sccs().map(|_| Vec::new()).collect();

View File

@ -44,8 +44,9 @@ pub(crate) fn codegen_fn<'tcx>(
let _mir_guard = crate::PrintOnPanic(|| { let _mir_guard = crate::PrintOnPanic(|| {
let mut buf = Vec::new(); let mut buf = Vec::new();
with_no_trimmed_paths!({ with_no_trimmed_paths!({
rustc_middle::mir::pretty::write_mir_fn(tcx, mir, &mut |_, _| Ok(()), &mut buf) use rustc_middle::mir::pretty;
.unwrap(); let options = pretty::PrettyPrintMirOptions::from_cli(tcx);
pretty::write_mir_fn(tcx, mir, &mut |_, _| Ok(()), &mut buf, options).unwrap();
}); });
String::from_utf8_lossy(&buf).into_owned() String::from_utf8_lossy(&buf).into_owned()
}); });

View File

@ -12,9 +12,10 @@ use rustc_session::config::{
CollapseMacroDebuginfo, CoverageLevel, CoverageOptions, DebugInfo, DumpMonoStatsFormat, CollapseMacroDebuginfo, CoverageLevel, CoverageOptions, DebugInfo, DumpMonoStatsFormat,
ErrorOutputType, ExternEntry, ExternLocation, Externs, FmtDebug, FunctionReturn, ErrorOutputType, ExternEntry, ExternLocation, Externs, FmtDebug, FunctionReturn,
InliningThreshold, Input, InstrumentCoverage, InstrumentXRay, LinkSelfContained, InliningThreshold, Input, InstrumentCoverage, InstrumentXRay, LinkSelfContained,
LinkerPluginLto, LocationDetail, LtoCli, NextSolverConfig, OomStrategy, Options, OutFileName, LinkerPluginLto, LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, OomStrategy,
OutputType, OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, Polonius, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes,
ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, PatchableFunctionEntry, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath,
SymbolManglingVersion, WasiExecModel,
}; };
use rustc_session::lint::Level; use rustc_session::lint::Level;
use rustc_session::search_paths::SearchPath; use rustc_session::search_paths::SearchPath;
@ -705,7 +706,7 @@ fn test_unstable_options_tracking_hash() {
untracked!(ls, vec!["all".to_owned()]); untracked!(ls, vec!["all".to_owned()]);
untracked!(macro_backtrace, true); untracked!(macro_backtrace, true);
untracked!(meta_stats, true); untracked!(meta_stats, true);
untracked!(mir_include_spans, true); untracked!(mir_include_spans, MirIncludeSpans::On);
untracked!(nll_facts, true); untracked!(nll_facts, true);
untracked!(no_analysis, true); untracked!(no_analysis, true);
untracked!(no_leak_check, true); untracked!(no_leak_check, true);

View File

@ -43,8 +43,23 @@ pub enum PassWhere {
AfterTerminator(BasicBlock), AfterTerminator(BasicBlock),
} }
/// If the session is properly configured, dumps a human-readable /// Cosmetic options for pretty-printing the MIR contents, gathered from the CLI. Each pass can
/// representation of the mir into: /// override these when dumping its own specific MIR information with [`dump_mir_with_options`].
#[derive(Copy, Clone)]
pub struct PrettyPrintMirOptions {
/// Whether to include extra comments, like span info. From `-Z mir-include-spans`.
pub include_extra_comments: bool,
}
impl PrettyPrintMirOptions {
/// Create the default set of MIR pretty-printing options from the CLI flags.
pub fn from_cli(tcx: TyCtxt<'_>) -> Self {
Self { include_extra_comments: tcx.sess.opts.unstable_opts.mir_include_spans.is_enabled() }
}
}
/// If the session is properly configured, dumps a human-readable representation of the MIR (with
/// default pretty-printing options) into:
/// ///
/// ```text /// ```text
/// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator> /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
@ -77,12 +92,40 @@ pub fn dump_mir<'tcx, F>(
extra_data: F, extra_data: F,
) where ) where
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
dump_mir_with_options(
tcx,
pass_num,
pass_name,
disambiguator,
body,
extra_data,
PrettyPrintMirOptions::from_cli(tcx),
);
}
/// If the session is properly configured, dumps a human-readable representation of the MIR, with
/// the given [pretty-printing options][PrettyPrintMirOptions].
///
/// See [`dump_mir`] for more details.
///
#[inline]
pub fn dump_mir_with_options<'tcx, F>(
tcx: TyCtxt<'tcx>,
pass_num: bool,
pass_name: &str,
disambiguator: &dyn Display,
body: &Body<'tcx>,
extra_data: F,
options: PrettyPrintMirOptions,
) where
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{ {
if !dump_enabled(tcx, pass_name, body.source.def_id()) { if !dump_enabled(tcx, pass_name, body.source.def_id()) {
return; return;
} }
dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data); dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data, options);
} }
pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool { pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool {
@ -112,6 +155,7 @@ fn dump_matched_mir_node<'tcx, F>(
disambiguator: &dyn Display, disambiguator: &dyn Display,
body: &Body<'tcx>, body: &Body<'tcx>,
mut extra_data: F, mut extra_data: F,
options: PrettyPrintMirOptions,
) where ) where
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{ {
@ -133,7 +177,7 @@ fn dump_matched_mir_node<'tcx, F>(
writeln!(file)?; writeln!(file)?;
extra_data(PassWhere::BeforeCFG, &mut file)?; extra_data(PassWhere::BeforeCFG, &mut file)?;
write_user_type_annotations(tcx, body, &mut file)?; write_user_type_annotations(tcx, body, &mut file)?;
write_mir_fn(tcx, body, &mut extra_data, &mut file)?; write_mir_fn(tcx, body, &mut extra_data, &mut file, options)?;
extra_data(PassWhere::AfterCFG, &mut file)?; extra_data(PassWhere::AfterCFG, &mut file)?;
}; };
@ -243,12 +287,15 @@ pub fn create_dump_file<'tcx>(
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Whole MIR bodies // Whole MIR bodies
/// Write out a human-readable textual representation for the given MIR. /// Write out a human-readable textual representation for the given MIR, with the default
/// [PrettyPrintMirOptions].
pub fn write_mir_pretty<'tcx>( pub fn write_mir_pretty<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
single: Option<DefId>, single: Option<DefId>,
w: &mut dyn io::Write, w: &mut dyn io::Write,
) -> io::Result<()> { ) -> io::Result<()> {
let options = PrettyPrintMirOptions::from_cli(tcx);
writeln!(w, "// WARNING: This output format is intended for human consumers only")?; writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
writeln!(w, "// and is subject to change without notice. Knock yourself out.")?; writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
@ -262,11 +309,11 @@ pub fn write_mir_pretty<'tcx>(
} }
let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> { let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?; write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
for body in tcx.promoted_mir(def_id) { for body in tcx.promoted_mir(def_id) {
writeln!(w)?; writeln!(w)?;
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?; write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
} }
Ok(()) Ok(())
}; };
@ -278,7 +325,7 @@ pub fn write_mir_pretty<'tcx>(
writeln!(w, "// MIR FOR CTFE")?; writeln!(w, "// MIR FOR CTFE")?;
// Do not use `render_body`, as that would render the promoteds again, but these // Do not use `render_body`, as that would render the promoteds again, but these
// are shared between mir_for_ctfe and optimized_mir // are shared between mir_for_ctfe and optimized_mir
write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w)?; write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w, options)?;
} else { } else {
let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id)); let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));
render_body(w, instance_mir)?; render_body(w, instance_mir)?;
@ -293,14 +340,15 @@ pub fn write_mir_fn<'tcx, F>(
body: &Body<'tcx>, body: &Body<'tcx>,
extra_data: &mut F, extra_data: &mut F,
w: &mut dyn io::Write, w: &mut dyn io::Write,
options: PrettyPrintMirOptions,
) -> io::Result<()> ) -> io::Result<()>
where where
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{ {
write_mir_intro(tcx, body, w)?; write_mir_intro(tcx, body, w, options)?;
for block in body.basic_blocks.indices() { for block in body.basic_blocks.indices() {
extra_data(PassWhere::BeforeBlock(block), w)?; extra_data(PassWhere::BeforeBlock(block), w)?;
write_basic_block(tcx, block, body, extra_data, w)?; write_basic_block(tcx, block, body, extra_data, w, options)?;
if block.index() + 1 != body.basic_blocks.len() { if block.index() + 1 != body.basic_blocks.len() {
writeln!(w)?; writeln!(w)?;
} }
@ -321,6 +369,7 @@ fn write_scope_tree(
w: &mut dyn io::Write, w: &mut dyn io::Write,
parent: SourceScope, parent: SourceScope,
depth: usize, depth: usize,
options: PrettyPrintMirOptions,
) -> io::Result<()> { ) -> io::Result<()> {
let indent = depth * INDENT.len(); let indent = depth * INDENT.len();
@ -333,7 +382,7 @@ fn write_scope_tree(
let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info); let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);
if tcx.sess.opts.unstable_opts.mir_include_spans { if options.include_extra_comments {
writeln!( writeln!(
w, w,
"{0:1$} // in {2}", "{0:1$} // in {2}",
@ -373,7 +422,7 @@ fn write_scope_tree(
let local_name = if local == RETURN_PLACE { " return place" } else { "" }; let local_name = if local == RETURN_PLACE { " return place" } else { "" };
if tcx.sess.opts.unstable_opts.mir_include_spans { if options.include_extra_comments {
writeln!( writeln!(
w, w,
"{0:1$} //{2} in {3}", "{0:1$} //{2} in {3}",
@ -410,7 +459,7 @@ fn write_scope_tree(
let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special); let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
if tcx.sess.opts.unstable_opts.mir_include_spans { if options.include_extra_comments {
if let Some(span) = span { if let Some(span) = span {
writeln!( writeln!(
w, w,
@ -426,7 +475,7 @@ fn write_scope_tree(
writeln!(w, "{indented_header}")?; writeln!(w, "{indented_header}")?;
} }
write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?; write_scope_tree(tcx, body, scope_tree, w, child, depth + 1, options)?;
writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?; writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
} }
@ -449,10 +498,11 @@ impl Debug for VarDebugInfo<'_> {
/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
/// local variables (both user-defined bindings and compiler temporaries). /// local variables (both user-defined bindings and compiler temporaries).
pub fn write_mir_intro<'tcx>( fn write_mir_intro<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
body: &Body<'_>, body: &Body<'_>,
w: &mut dyn io::Write, w: &mut dyn io::Write,
options: PrettyPrintMirOptions,
) -> io::Result<()> { ) -> io::Result<()> {
write_mir_sig(tcx, body, w)?; write_mir_sig(tcx, body, w)?;
writeln!(w, "{{")?; writeln!(w, "{{")?;
@ -468,7 +518,7 @@ pub fn write_mir_intro<'tcx>(
} }
} }
write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?; write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1, options)?;
// Add an empty line before the first block is printed. // Add an empty line before the first block is printed.
writeln!(w)?; writeln!(w)?;
@ -651,12 +701,13 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
// Basic blocks and their parts (statements, terminators, ...) // Basic blocks and their parts (statements, terminators, ...)
/// Write out a human-readable textual representation for the given basic block. /// Write out a human-readable textual representation for the given basic block.
pub fn write_basic_block<'tcx, F>( fn write_basic_block<'tcx, F>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
block: BasicBlock, block: BasicBlock,
body: &Body<'tcx>, body: &Body<'tcx>,
extra_data: &mut F, extra_data: &mut F,
w: &mut dyn io::Write, w: &mut dyn io::Write,
options: PrettyPrintMirOptions,
) -> io::Result<()> ) -> io::Result<()>
where where
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
@ -672,7 +723,7 @@ where
for statement in &data.statements { for statement in &data.statements {
extra_data(PassWhere::BeforeLocation(current_location), w)?; extra_data(PassWhere::BeforeLocation(current_location), w)?;
let indented_body = format!("{INDENT}{INDENT}{statement:?};"); let indented_body = format!("{INDENT}{INDENT}{statement:?};");
if tcx.sess.opts.unstable_opts.mir_include_spans { if options.include_extra_comments {
writeln!( writeln!(
w, w,
"{:A$} // {}{}", "{:A$} // {}{}",
@ -689,9 +740,14 @@ where
writeln!(w, "{indented_body}")?; writeln!(w, "{indented_body}")?;
} }
write_extra(tcx, w, |visitor| { write_extra(
visitor.visit_statement(statement, current_location); tcx,
})?; w,
|visitor| {
visitor.visit_statement(statement, current_location);
},
options,
)?;
extra_data(PassWhere::AfterLocation(current_location), w)?; extra_data(PassWhere::AfterLocation(current_location), w)?;
@ -701,7 +757,7 @@ where
// Terminator at the bottom. // Terminator at the bottom.
extra_data(PassWhere::BeforeLocation(current_location), w)?; extra_data(PassWhere::BeforeLocation(current_location), w)?;
let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind); let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
if tcx.sess.opts.unstable_opts.mir_include_spans { if options.include_extra_comments {
writeln!( writeln!(
w, w,
"{:A$} // {}{}", "{:A$} // {}{}",
@ -718,9 +774,14 @@ where
writeln!(w, "{indented_terminator}")?; writeln!(w, "{indented_terminator}")?;
} }
write_extra(tcx, w, |visitor| { write_extra(
visitor.visit_terminator(data.terminator(), current_location); tcx,
})?; w,
|visitor| {
visitor.visit_terminator(data.terminator(), current_location);
},
options,
)?;
extra_data(PassWhere::AfterLocation(current_location), w)?; extra_data(PassWhere::AfterLocation(current_location), w)?;
extra_data(PassWhere::AfterTerminator(block), w)?; extra_data(PassWhere::AfterTerminator(block), w)?;
@ -1271,11 +1332,12 @@ fn write_extra<'tcx, F>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
write: &mut dyn io::Write, write: &mut dyn io::Write,
mut visit_op: F, mut visit_op: F,
options: PrettyPrintMirOptions,
) -> io::Result<()> ) -> io::Result<()>
where where
F: FnMut(&mut ExtraComments<'tcx>), F: FnMut(&mut ExtraComments<'tcx>),
{ {
if tcx.sess.opts.unstable_opts.mir_include_spans { if options.include_extra_comments {
let mut extra_comments = ExtraComments { tcx, comments: vec![] }; let mut extra_comments = ExtraComments { tcx, comments: vec![] };
visit_op(&mut extra_comments); visit_op(&mut extra_comments);
for comment in extra_comments.comments { for comment in extra_comments.comments {
@ -1890,7 +1952,7 @@ pub(crate) fn pretty_print_const_value<'tcx>(
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Miscellaneous // Miscellaneous
/// Calc converted u64 decimal into hex and return it's length in chars /// Calc converted u64 decimal into hex and return its length in chars.
/// ///
/// ```ignore (cannot-test-private-function) /// ```ignore (cannot-test-private-function)
/// assert_eq!(1, hex_number_length(0)); /// assert_eq!(1, hex_number_length(0));

View File

@ -3369,3 +3369,25 @@ pub enum FunctionReturn {
/// Replace returns with jumps to thunk, without emitting the thunk. /// Replace returns with jumps to thunk, without emitting the thunk.
ThunkExtern, ThunkExtern,
} }
/// Whether extra span comments are included when dumping MIR, via the `-Z mir-include-spans` flag.
/// By default, only enabled in the NLL MIR dumps, and disabled in all other passes.
#[derive(Clone, Copy, Default, PartialEq, Debug)]
pub enum MirIncludeSpans {
Off,
On,
/// Default: include extra comments in NLL MIR dumps only. Can be ignored and considered as
/// `Off` in all other cases.
#[default]
Nll,
}
impl MirIncludeSpans {
/// Unless opting into extra comments for all passes, they can be considered disabled.
/// The cases where a distinction between on/off and a per-pass value can exist will be handled
/// in the passes themselves: i.e. the `Nll` value is considered off for all intents and
/// purposes, except for the NLL MIR dump pass.
pub fn is_enabled(self) -> bool {
self == MirIncludeSpans::On
}
}

View File

@ -445,6 +445,8 @@ mod desc {
pub const parse_llvm_module_flag: &str = "<key>:<type>:<value>:<behavior>. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)"; pub const parse_llvm_module_flag: &str = "<key>:<type>:<value>:<behavior>. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)";
pub const parse_function_return: &str = "`keep` or `thunk-extern`"; pub const parse_function_return: &str = "`keep` or `thunk-extern`";
pub const parse_wasm_c_abi: &str = "`legacy` or `spec`"; pub const parse_wasm_c_abi: &str = "`legacy` or `spec`";
pub const parse_mir_include_spans: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), or `nll` (default: `nll`)";
} }
mod parse { mod parse {
@ -1488,6 +1490,17 @@ mod parse {
} }
true true
} }
pub(crate) fn parse_mir_include_spans(slot: &mut MirIncludeSpans, v: Option<&str>) -> bool {
*slot = match v {
Some("on" | "yes" | "y" | "true") | None => MirIncludeSpans::On,
Some("off" | "no" | "n" | "false") => MirIncludeSpans::Off,
Some("nll") => MirIncludeSpans::Nll,
_ => return false,
};
true
}
} }
options! { options! {
@ -1848,8 +1861,9 @@ options! {
specified passes to be enabled, overriding all other checks. In particular, this will \ specified passes to be enabled, overriding all other checks. In particular, this will \
enable unsound (known-buggy and hence usually disabled) passes without further warning! \ enable unsound (known-buggy and hence usually disabled) passes without further warning! \
Passes that are not specified are enabled or disabled by other flags as usual."), Passes that are not specified are enabled or disabled by other flags as usual."),
mir_include_spans: bool = (false, parse_bool, [UNTRACKED], mir_include_spans: MirIncludeSpans = (MirIncludeSpans::default(), parse_mir_include_spans, [UNTRACKED],
"use line numbers relative to the function in mir pretty printing"), "include extra comments in mir pretty printing, like line numbers and statement indices, \
details about types, etc. (boolean for all passes, 'nll' to enable in NLL MIR only, default: 'nll')"),
mir_keep_place_mention: bool = (false, parse_bool, [TRACKED], mir_keep_place_mention: bool = (false, parse_bool, [TRACKED],
"keep place mention MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ "keep place mention MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
(default: no)"), (default: no)"),

View File

@ -2175,6 +2175,7 @@ impl<'test> TestCx<'test> {
"-Zvalidate-mir", "-Zvalidate-mir",
"-Zlint-mir", "-Zlint-mir",
"-Zdump-mir-exclude-pass-number", "-Zdump-mir-exclude-pass-number",
"-Zmir-include-spans=false", // remove span comments from NLL MIR dumps
"--crate-type=rlib", "--crate-type=rlib",
]); ]);
if let Some(pass) = &self.props.mir_unit_test { if let Some(pass) = &self.props.mir_unit_test {

View File

@ -20,6 +20,9 @@
| '?2: '?3 due to Assignment at Single(bb1[0]) ($DIR/region_subtyping_basic.rs:19:13: 19:18 (#0) | '?2: '?3 due to Assignment at Single(bb1[0]) ($DIR/region_subtyping_basic.rs:19:13: 19:18 (#0)
| '?3: '?4 due to Assignment at Single(bb1[3]) ($DIR/region_subtyping_basic.rs:20:13: 20:14 (#0) | '?3: '?4 due to Assignment at Single(bb1[3]) ($DIR/region_subtyping_basic.rs:20:13: 20:14 (#0)
| |
| Borrows
| bw0: issued at bb1[0] in '?2
|
fn main() -> () { fn main() -> () {
let mut _0: (); let mut _0: ();
let mut _1: [usize; ValTree(Leaf(0x00000003): usize)]; let mut _1: [usize; ValTree(Leaf(0x00000003): usize)];

View File

@ -20,6 +20,9 @@
| '?2: '?3 due to Assignment at Single(bb1[0]) ($DIR/region_subtyping_basic.rs:19:13: 19:18 (#0) | '?2: '?3 due to Assignment at Single(bb1[0]) ($DIR/region_subtyping_basic.rs:19:13: 19:18 (#0)
| '?3: '?4 due to Assignment at Single(bb1[3]) ($DIR/region_subtyping_basic.rs:20:13: 20:14 (#0) | '?3: '?4 due to Assignment at Single(bb1[3]) ($DIR/region_subtyping_basic.rs:20:13: 20:14 (#0)
| |
| Borrows
| bw0: issued at bb1[0] in '?2
|
fn main() -> () { fn main() -> () {
let mut _0: (); let mut _0: ();
let mut _1: [usize; ValTree(Leaf(0x0000000000000003): usize)]; let mut _1: [usize; ValTree(Leaf(0x0000000000000003): usize)];

View File

@ -17,6 +17,9 @@
| '?3 live at {bb0[11]} | '?3 live at {bb0[11]}
| '?2: '?3 due to Assignment at Single(bb0[10]) ($DIR/storage_ranges.rs:7:17: 7:25 (#0) | '?2: '?3 due to Assignment at Single(bb0[10]) ($DIR/storage_ranges.rs:7:17: 7:25 (#0)
| |
| Borrows
| bw0: issued at bb0[10] in '?2
|
fn main() -> () { fn main() -> () {
let mut _0: (); let mut _0: ();
let _1: i32; let _1: i32;