Auto merge of #146072 - Zalathar:rollup-0svnrfe, r=Zalathar

Rollup of 6 pull requests

Successful merges:

 - rust-lang/rust#145421 (`dump_mir` cleanups)
 - rust-lang/rust#145968 (Add `Bound::copied`)
 - rust-lang/rust#146004 (resolve: Refactor `struct ExternPreludeEntry`)
 - rust-lang/rust#146042 (Detect negative literal inferred to unsigned integer)
 - rust-lang/rust#146046 (Suggest method name with maybe ty mismatch)
 - rust-lang/rust#146051 (Change std f32 test to pass under Miri)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-09-01 04:37:39 +00:00
commit be4e9b77ab
31 changed files with 705 additions and 605 deletions

View file

@ -8,8 +8,8 @@ use std::str::FromStr;
use polonius_engine::{Algorithm, AllFacts, Output};
use rustc_data_structures::frozen::Frozen;
use rustc_index::IndexSlice;
use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir};
use rustc_middle::mir::pretty::PrettyPrintMirOptions;
use rustc_middle::mir::{Body, MirDumper, PassWhere, Promoted};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, TyCtxt};
use rustc_mir_dataflow::move_paths::MoveData;
@ -68,7 +68,9 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
// Replace all remaining regions with fresh inference variables.
renumber::renumber_mir(infcx, body, promoted);
dump_mir(infcx.tcx, false, "renumber", &0, body, |_, _| Ok(()));
if let Some(dumper) = MirDumper::new(infcx.tcx, "renumber", body) {
dumper.dump_mir(body);
}
universal_regions
}
@ -175,9 +177,7 @@ pub(super) fn dump_nll_mir<'tcx>(
borrow_set: &BorrowSet<'tcx>,
) {
let tcx = infcx.tcx;
if !dump_enabled(tcx, "nll", body.source.def_id()) {
return;
}
let Some(dumper) = MirDumper::new(tcx, "nll", body) else { return };
// We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in
// #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example,
@ -188,27 +188,24 @@ pub(super) fn dump_nll_mir<'tcx>(
MirIncludeSpans::On | MirIncludeSpans::Nll
),
};
dump_mir_with_options(
tcx,
false,
"nll",
&0,
body,
|pass_where, out| {
emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
},
options,
);
let extra_data = &|pass_where, out: &mut dyn std::io::Write| {
emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
};
let dumper = dumper.set_extra_data(extra_data).set_options(options);
dumper.dump_mir(body);
// Also dump the region constraint graph as a graphviz file.
let _: io::Result<()> = try {
let mut file = create_dump_file(tcx, "regioncx.all.dot", false, "nll", &0, body)?;
let mut file = dumper.create_dump_file("regioncx.all.dot", body)?;
regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?;
};
// Also dump the region constraint SCC graph as a graphviz file.
let _: io::Result<()> = try {
let mut file = create_dump_file(tcx, "regioncx.scc.dot", false, "nll", &0, body)?;
let mut file = dumper.create_dump_file("regioncx.scc.dot", body)?;
regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?;
};
}

View file

@ -2,9 +2,7 @@ use std::io;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_index::IndexVec;
use rustc_middle::mir::pretty::{
PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer,
};
use rustc_middle::mir::pretty::{MirDumper, PassWhere, PrettyPrintMirOptions};
use rustc_middle::mir::{Body, Location};
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_mir_dataflow::points::PointIndex;
@ -33,22 +31,41 @@ pub(crate) fn dump_polonius_mir<'tcx>(
return;
}
if !dump_enabled(tcx, "polonius", body.source.def_id()) {
return;
}
let Some(dumper) = MirDumper::new(tcx, "polonius", body) else { return };
let polonius_diagnostics =
polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`");
let _: io::Result<()> = try {
let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?;
emit_polonius_dump(
let extra_data = &|pass_where, out: &mut dyn io::Write| {
emit_polonius_mir(
tcx,
regioncx,
closure_region_requirements,
borrow_set,
&polonius_diagnostics.localized_outlives_constraints,
pass_where,
out,
)
};
// We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
// mir-include-spans` on the CLI still has priority.
let options = PrettyPrintMirOptions {
include_extra_comments: matches!(
tcx.sess.opts.unstable_opts.mir_include_spans,
MirIncludeSpans::On | MirIncludeSpans::Nll
),
};
let dumper = dumper.set_extra_data(extra_data).set_options(options);
let _: io::Result<()> = try {
let mut file = dumper.create_dump_file("html", body)?;
emit_polonius_dump(
&dumper,
body,
regioncx,
borrow_set,
&polonius_diagnostics.localized_outlives_constraints,
closure_region_requirements,
&mut file,
)?;
};
@ -61,12 +78,11 @@ pub(crate) fn dump_polonius_mir<'tcx>(
/// - a mermaid graph of the NLL regions and the constraints between them
/// - a mermaid graph of the NLL SCCs and the constraints between them
fn emit_polonius_dump<'tcx>(
tcx: TyCtxt<'tcx>,
dumper: &MirDumper<'_, '_, 'tcx>,
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
borrow_set: &BorrowSet<'tcx>,
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
out: &mut dyn io::Write,
) -> io::Result<()> {
// Prepare the HTML dump file prologue.
@ -79,15 +95,7 @@ fn emit_polonius_dump<'tcx>(
writeln!(out, "<div>")?;
writeln!(out, "Raw MIR dump")?;
writeln!(out, "<pre><code>")?;
emit_html_mir(
tcx,
body,
regioncx,
borrow_set,
&localized_outlives_constraints,
closure_region_requirements,
out,
)?;
emit_html_mir(dumper, body, out)?;
writeln!(out, "</code></pre>")?;
writeln!(out, "</div>")?;
@ -116,7 +124,7 @@ fn emit_polonius_dump<'tcx>(
writeln!(out, "<div>")?;
writeln!(out, "NLL regions")?;
writeln!(out, "<pre class='mermaid'>")?;
emit_mermaid_nll_regions(tcx, regioncx, out)?;
emit_mermaid_nll_regions(dumper.tcx(), regioncx, out)?;
writeln!(out, "</pre>")?;
writeln!(out, "</div>")?;
@ -124,7 +132,7 @@ fn emit_polonius_dump<'tcx>(
writeln!(out, "<div>")?;
writeln!(out, "NLL SCCs")?;
writeln!(out, "<pre class='mermaid'>")?;
emit_mermaid_nll_sccs(tcx, regioncx, out)?;
emit_mermaid_nll_sccs(dumper.tcx(), regioncx, out)?;
writeln!(out, "</pre>")?;
writeln!(out, "</div>")?;
@ -149,45 +157,14 @@ fn emit_polonius_dump<'tcx>(
/// Emits the polonius MIR, as escaped HTML.
fn emit_html_mir<'tcx>(
tcx: TyCtxt<'tcx>,
dumper: &MirDumper<'_, '_, 'tcx>,
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
borrow_set: &BorrowSet<'tcx>,
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
out: &mut dyn io::Write,
) -> io::Result<()> {
// Buffer the regular MIR dump to be able to escape it.
let mut buffer = Vec::new();
// We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
// mir-include-spans` on the CLI still has priority.
let options = PrettyPrintMirOptions {
include_extra_comments: matches!(
tcx.sess.opts.unstable_opts.mir_include_spans,
MirIncludeSpans::On | MirIncludeSpans::Nll
),
};
dump_mir_to_writer(
tcx,
"polonius",
&0,
body,
&mut buffer,
|pass_where, out| {
emit_polonius_mir(
tcx,
regioncx,
closure_region_requirements,
borrow_set,
localized_outlives_constraints,
pass_where,
out,
)
},
options,
)?;
dumper.dump_mir_to_writer(body, &mut buffer)?;
// Escape the handful of characters that need it. We don't need to be particularly efficient:
// we're actually writing into a buffered writer already. Note that MIR dumps are valid UTF-8.

View file

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

View file

@ -117,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Err(Ambiguity(..)) => true,
Err(PrivateMatch(..)) => false,
Err(IllegalSizedBound { .. }) => true,
Err(BadReturnType) => false,
Err(BadReturnType) => true,
Err(ErrorReported(_)) => false,
}
}

View file

@ -962,13 +962,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip();
let cause = self.cause(
span,
ObligationCauseCode::BinOp {
lhs_hir_id: lhs_expr.hir_id,
rhs_hir_id: opt_rhs_expr.map(|expr| expr.hir_id),
rhs_span: opt_rhs_expr.map(|expr| expr.span),
rhs_is_lit: opt_rhs_expr
.is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
output_ty: expected.only_has_type(self),
match opt_rhs_expr {
Some(rhs) => ObligationCauseCode::BinOp {
lhs_hir_id: lhs_expr.hir_id,
rhs_hir_id: rhs.hir_id,
rhs_span: rhs.span,
rhs_is_lit: matches!(rhs.kind, hir::ExprKind::Lit(_)),
output_ty: expected.only_has_type(self),
},
None => ObligationCauseCode::UnOp { hir_id: lhs_expr.hir_id },
},
);

View file

@ -62,9 +62,7 @@ pub use terminator::*;
pub use self::generic_graph::graphviz_safe_def_name;
pub use self::graphviz::write_mir_graphviz;
pub use self::pretty::{
PassWhere, create_dump_file, display_allocation, dump_enabled, dump_mir, write_mir_pretty,
};
pub use self::pretty::{MirDumper, PassWhere, display_allocation, write_mir_pretty};
/// Types for locals
pub type LocalDecls<'tcx> = IndexSlice<Local, LocalDecl<'tcx>>;

View file

@ -44,7 +44,7 @@ pub enum PassWhere {
}
/// Cosmetic options for pretty-printing the MIR contents, gathered from the CLI. Each pass can
/// override these when dumping its own specific MIR information with [`dump_mir_with_options`].
/// override these when dumping its own specific MIR information with `dump_mir`.
#[derive(Copy, Clone)]
pub struct PrettyPrintMirOptions {
/// Whether to include extra comments, like span info. From `-Z mir-include-spans`.
@ -58,277 +58,253 @@ impl PrettyPrintMirOptions {
}
}
/// If the session is properly configured, dumps a human-readable representation of the MIR (with
/// default pretty-printing options) into:
///
/// ```text
/// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
/// ```
///
/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
/// where `<filter>` takes the following forms:
///
/// - `all` -- dump MIR for all fns, all passes, all everything
/// - a filter defined by a set of substrings combined with `&` and `|`
/// (`&` has higher precedence). At least one of the `|`-separated groups
/// must match; an `|`-separated group matches if all of its `&`-separated
/// substrings are matched.
///
/// Example:
///
/// - `nll` == match if `nll` appears in the name
/// - `foo & nll` == match if `foo` and `nll` both appear in the name
/// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
/// or `typeck` appears in the name.
/// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
/// or `typeck` and `bar` both appear in the name.
#[inline]
pub fn dump_mir<'tcx, F>(
tcx: TyCtxt<'tcx>,
pass_num: bool,
pass_name: &str,
disambiguator: &dyn Display,
body: &Body<'tcx>,
extra_data: F,
) where
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),
);
/// Manages MIR dumping, which is MIR writing done to a file with a specific name. In particular,
/// it makes it impossible to dump MIR to one of these files when it hasn't been requested from the
/// command line. Layered on top of `MirWriter`, which does the actual writing.
pub struct MirDumper<'dis, 'de, 'tcx> {
show_pass_num: bool,
pass_name: &'static str,
disambiguator: &'dis dyn Display,
writer: MirWriter<'de, '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()) {
return;
}
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 {
let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir else {
return false;
};
// see notes on #41697 below
let node_path = ty::print::with_forced_impl_filename_line!(tcx.def_path_str(def_id));
filters.split('|').any(|or_filter| {
or_filter.split('&').all(|and_filter| {
let and_filter_trimmed = and_filter.trim();
and_filter_trimmed == "all"
|| pass_name.contains(and_filter_trimmed)
|| node_path.contains(and_filter_trimmed)
})
})
}
// #41697 -- we use `with_forced_impl_filename_line()` because
// `def_path_str()` would otherwise trigger `type_of`, and this can
// run while we are already attempting to evaluate `type_of`.
/// Most use-cases of dumping MIR should use the [dump_mir] entrypoint instead, which will also
/// check if dumping MIR is enabled, and if this body matches the filters passed on the CLI.
///
/// That being said, if the above requirements have been validated already, this function is where
/// most of the MIR dumping occurs, if one needs to export it to a file they have created with
/// [create_dump_file], rather than to a new file created as part of [dump_mir], or to stdout/stderr
/// for debugging purposes.
pub fn dump_mir_to_writer<'tcx, F>(
tcx: TyCtxt<'tcx>,
pass_name: &str,
disambiguator: &dyn Display,
body: &Body<'tcx>,
w: &mut dyn io::Write,
mut extra_data: F,
options: PrettyPrintMirOptions,
) -> io::Result<()>
where
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
// see notes on #41697 above
let def_path =
ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
// ignore-tidy-odd-backticks the literal below is fine
write!(w, "// MIR for `{def_path}")?;
match body.source.promoted {
None => write!(w, "`")?,
Some(promoted) => write!(w, "::{promoted:?}`")?,
}
writeln!(w, " {disambiguator} {pass_name}")?;
if let Some(ref layout) = body.coroutine_layout_raw() {
writeln!(w, "/* coroutine_layout = {layout:#?} */")?;
}
writeln!(w)?;
extra_data(PassWhere::BeforeCFG, w)?;
write_user_type_annotations(tcx, body, w)?;
write_mir_fn(tcx, body, &mut extra_data, w, options)?;
extra_data(PassWhere::AfterCFG, w)
}
fn dump_matched_mir_node<'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<()>,
{
let _: io::Result<()> = try {
let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?;
dump_mir_to_writer(tcx, pass_name, disambiguator, body, &mut file, extra_data, options)?;
};
if tcx.sess.opts.unstable_opts.dump_mir_graphviz {
let _: io::Result<()> = try {
let mut file = create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body)?;
write_mir_fn_graphviz(tcx, body, false, &mut file)?;
impl<'dis, 'de, 'tcx> MirDumper<'dis, 'de, 'tcx> {
// If dumping should be performed (e.g. because it was requested on the
// CLI), returns a `MirDumper` with default values for the following fields:
// - `show_pass_num`: `false`
// - `disambiguator`: `&0`
// - `writer.extra_data`: a no-op
// - `writer.options`: default options derived from CLI flags
pub fn new(tcx: TyCtxt<'tcx>, pass_name: &'static str, body: &Body<'tcx>) -> Option<Self> {
let dump_enabled = if let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir {
// see notes on #41697 below
let node_path =
ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
filters.split('|').any(|or_filter| {
or_filter.split('&').all(|and_filter| {
let and_filter_trimmed = and_filter.trim();
and_filter_trimmed == "all"
|| pass_name.contains(and_filter_trimmed)
|| node_path.contains(and_filter_trimmed)
})
})
} else {
false
};
dump_enabled.then_some(MirDumper {
show_pass_num: false,
pass_name,
disambiguator: &0,
writer: MirWriter::new(tcx),
})
}
}
/// Returns the path to the filename where we should dump a given MIR.
/// Also used by other bits of code (e.g., NLL inference) that dump
/// graphviz data or other things.
fn dump_path<'tcx>(
tcx: TyCtxt<'tcx>,
extension: &str,
pass_num: bool,
pass_name: &str,
disambiguator: &dyn Display,
body: &Body<'tcx>,
) -> PathBuf {
let source = body.source;
let promotion_id = match source.promoted {
Some(id) => format!("-{id:?}"),
None => String::new(),
};
pub fn tcx(&self) -> TyCtxt<'tcx> {
self.writer.tcx
}
let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
String::new()
} else if pass_num {
let (dialect_index, phase_index) = body.phase.index();
format!(".{}-{}-{:03}", dialect_index, phase_index, body.pass_count)
} else {
".-------".to_string()
};
#[must_use]
pub fn set_show_pass_num(mut self) -> Self {
self.show_pass_num = true;
self
}
let crate_name = tcx.crate_name(source.def_id().krate);
let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
// All drop shims have the same DefId, so we have to add the type
// to get unique file names.
let shim_disambiguator = match source.instance {
ty::InstanceKind::DropGlue(_, Some(ty)) => {
// Unfortunately, pretty-printed typed are not very filename-friendly.
// We dome some filtering.
let mut s = ".".to_owned();
s.extend(ty.to_string().chars().filter_map(|c| match c {
' ' => None,
':' | '<' | '>' => Some('_'),
c => Some(c),
}));
s
}
ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => {
let mut s = ".".to_owned();
s.extend(ty.to_string().chars().filter_map(|c| match c {
' ' => None,
':' | '<' | '>' => Some('_'),
c => Some(c),
}));
s
}
ty::InstanceKind::AsyncDropGlue(_, ty) => {
let ty::Coroutine(_, args) = ty.kind() else {
bug!();
#[must_use]
pub fn set_disambiguator(mut self, disambiguator: &'dis dyn Display) -> Self {
self.disambiguator = disambiguator;
self
}
#[must_use]
pub fn set_extra_data(
mut self,
extra_data: &'de dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,
) -> Self {
self.writer.extra_data = extra_data;
self
}
#[must_use]
pub fn set_options(mut self, options: PrettyPrintMirOptions) -> Self {
self.writer.options = options;
self
}
/// If the session is properly configured, dumps a human-readable representation of the MIR
/// (with default pretty-printing options) into:
///
/// ```text
/// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
/// ```
///
/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
/// where `<filter>` takes the following forms:
///
/// - `all` -- dump MIR for all fns, all passes, all everything
/// - a filter defined by a set of substrings combined with `&` and `|`
/// (`&` has higher precedence). At least one of the `|`-separated groups
/// must match; an `|`-separated group matches if all of its `&`-separated
/// substrings are matched.
///
/// Example:
///
/// - `nll` == match if `nll` appears in the name
/// - `foo & nll` == match if `foo` and `nll` both appear in the name
/// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
/// or `typeck` appears in the name.
/// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
/// or `typeck` and `bar` both appear in the name.
pub fn dump_mir(&self, body: &Body<'tcx>) {
let _: io::Result<()> = try {
let mut file = self.create_dump_file("mir", body)?;
self.dump_mir_to_writer(body, &mut file)?;
};
if self.tcx().sess.opts.unstable_opts.dump_mir_graphviz {
let _: io::Result<()> = try {
let mut file = self.create_dump_file("dot", body)?;
write_mir_fn_graphviz(self.tcx(), body, false, &mut file)?;
};
let ty = args.first().unwrap().expect_ty();
let mut s = ".".to_owned();
s.extend(ty.to_string().chars().filter_map(|c| match c {
' ' => None,
':' | '<' | '>' => Some('_'),
c => Some(c),
}));
s
}
ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => {
let mut s = ".".to_owned();
s.extend(proxy_cor.to_string().chars().filter_map(|c| match c {
' ' => None,
':' | '<' | '>' => Some('_'),
c => Some(c),
}));
s.push('.');
s.extend(impl_cor.to_string().chars().filter_map(|c| match c {
' ' => None,
':' | '<' | '>' => Some('_'),
c => Some(c),
}));
s
}
_ => String::new(),
};
let mut file_path = PathBuf::new();
file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
let file_name = format!(
"{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}.{extension}",
);
file_path.push(&file_name);
file_path
}
/// Attempts to open a file where we should dump a given MIR or other
/// bit of MIR-related data. Used by `mir-dump`, but also by other
/// bits of code (e.g., NLL inference) that dump graphviz data or
/// other things, and hence takes the extension as an argument.
pub fn create_dump_file<'tcx>(
tcx: TyCtxt<'tcx>,
extension: &str,
pass_num: bool,
pass_name: &str,
disambiguator: &dyn Display,
body: &Body<'tcx>,
) -> io::Result<io::BufWriter<fs::File>> {
let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, body);
if let Some(parent) = file_path.parent() {
fs::create_dir_all(parent).map_err(|e| {
io::Error::new(
e.kind(),
format!("IO error creating MIR dump directory: {parent:?}; {e}"),
)
})?;
}
fs::File::create_buffered(&file_path).map_err(|e| {
io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))
})
// #41697 -- we use `with_forced_impl_filename_line()` because `def_path_str()` would otherwise
// trigger `type_of`, and this can run while we are already attempting to evaluate `type_of`.
pub fn dump_mir_to_writer(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {
// see notes on #41697 above
let def_path = ty::print::with_forced_impl_filename_line!(
self.tcx().def_path_str(body.source.def_id())
);
// ignore-tidy-odd-backticks the literal below is fine
write!(w, "// MIR for `{def_path}")?;
match body.source.promoted {
None => write!(w, "`")?,
Some(promoted) => write!(w, "::{promoted:?}`")?,
}
writeln!(w, " {} {}", self.disambiguator, self.pass_name)?;
if let Some(ref layout) = body.coroutine_layout_raw() {
writeln!(w, "/* coroutine_layout = {layout:#?} */")?;
}
writeln!(w)?;
(self.writer.extra_data)(PassWhere::BeforeCFG, w)?;
write_user_type_annotations(self.tcx(), body, w)?;
self.writer.write_mir_fn(body, w)?;
(self.writer.extra_data)(PassWhere::AfterCFG, w)
}
/// Returns the path to the filename where we should dump a given MIR.
/// Also used by other bits of code (e.g., NLL inference) that dump
/// graphviz data or other things.
fn dump_path(&self, extension: &str, body: &Body<'tcx>) -> PathBuf {
let tcx = self.tcx();
let source = body.source;
let promotion_id = match source.promoted {
Some(id) => format!("-{id:?}"),
None => String::new(),
};
let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
String::new()
} else if self.show_pass_num {
let (dialect_index, phase_index) = body.phase.index();
format!(".{}-{}-{:03}", dialect_index, phase_index, body.pass_count)
} else {
".-------".to_string()
};
let crate_name = tcx.crate_name(source.def_id().krate);
let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
// All drop shims have the same DefId, so we have to add the type
// to get unique file names.
let shim_disambiguator = match source.instance {
ty::InstanceKind::DropGlue(_, Some(ty)) => {
// Unfortunately, pretty-printed types are not very filename-friendly.
// We do some filtering.
let mut s = ".".to_owned();
s.extend(ty.to_string().chars().filter_map(|c| match c {
' ' => None,
':' | '<' | '>' => Some('_'),
c => Some(c),
}));
s
}
ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => {
let mut s = ".".to_owned();
s.extend(ty.to_string().chars().filter_map(|c| match c {
' ' => None,
':' | '<' | '>' => Some('_'),
c => Some(c),
}));
s
}
ty::InstanceKind::AsyncDropGlue(_, ty) => {
let ty::Coroutine(_, args) = ty.kind() else {
bug!();
};
let ty = args.first().unwrap().expect_ty();
let mut s = ".".to_owned();
s.extend(ty.to_string().chars().filter_map(|c| match c {
' ' => None,
':' | '<' | '>' => Some('_'),
c => Some(c),
}));
s
}
ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => {
let mut s = ".".to_owned();
s.extend(proxy_cor.to_string().chars().filter_map(|c| match c {
' ' => None,
':' | '<' | '>' => Some('_'),
c => Some(c),
}));
s.push('.');
s.extend(impl_cor.to_string().chars().filter_map(|c| match c {
' ' => None,
':' | '<' | '>' => Some('_'),
c => Some(c),
}));
s
}
_ => String::new(),
};
let mut file_path = PathBuf::new();
file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
let pass_name = self.pass_name;
let disambiguator = self.disambiguator;
let file_name = format!(
"{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}.{extension}",
);
file_path.push(&file_name);
file_path
}
/// Attempts to open a file where we should dump a given MIR or other
/// bit of MIR-related data. Used by `mir-dump`, but also by other
/// bits of code (e.g., NLL inference) that dump graphviz data or
/// other things, and hence takes the extension as an argument.
pub fn create_dump_file(
&self,
extension: &str,
body: &Body<'tcx>,
) -> io::Result<io::BufWriter<fs::File>> {
let file_path = self.dump_path(extension, body);
if let Some(parent) = file_path.parent() {
fs::create_dir_all(parent).map_err(|e| {
io::Error::new(
e.kind(),
format!("IO error creating MIR dump directory: {parent:?}; {e}"),
)
})?;
}
fs::File::create_buffered(&file_path).map_err(|e| {
io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))
})
}
}
///////////////////////////////////////////////////////////////////////////
@ -341,7 +317,7 @@ pub fn write_mir_pretty<'tcx>(
single: Option<DefId>,
w: &mut dyn io::Write,
) -> io::Result<()> {
let options = PrettyPrintMirOptions::from_cli(tcx);
let writer = MirWriter::new(tcx);
writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
@ -357,11 +333,11 @@ pub fn write_mir_pretty<'tcx>(
}
let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
writer.write_mir_fn(body, w)?;
for body in tcx.promoted_mir(def_id) {
writeln!(w)?;
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
writer.write_mir_fn(body, w)?;
}
Ok(())
};
@ -373,7 +349,7 @@ pub fn write_mir_pretty<'tcx>(
writeln!(w, "// MIR FOR CTFE")?;
// Do not use `render_body`, as that would render the promoteds again, but these
// are shared between mir_for_ctfe and optimized_mir
write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w, options)?;
writer.write_mir_fn(tcx.mir_for_ctfe(def_id), w)?;
} else {
let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));
render_body(w, instance_mir)?;
@ -382,31 +358,35 @@ pub fn write_mir_pretty<'tcx>(
Ok(())
}
/// Write out a human-readable textual representation for the given function.
pub fn write_mir_fn<'tcx, F>(
/// Does the writing of MIR to output, e.g. a file.
pub struct MirWriter<'de, 'tcx> {
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
extra_data: &mut F,
w: &mut dyn io::Write,
extra_data: &'de dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,
options: PrettyPrintMirOptions,
) -> io::Result<()>
where
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
write_mir_intro(tcx, body, w, options)?;
for block in body.basic_blocks.indices() {
extra_data(PassWhere::BeforeBlock(block), w)?;
write_basic_block(tcx, block, body, extra_data, w, options)?;
if block.index() + 1 != body.basic_blocks.len() {
writeln!(w)?;
}
}
impl<'de, 'tcx> MirWriter<'de, 'tcx> {
pub fn new(tcx: TyCtxt<'tcx>) -> Self {
MirWriter { tcx, extra_data: &|_, _| Ok(()), options: PrettyPrintMirOptions::from_cli(tcx) }
}
writeln!(w, "}}")?;
/// Write out a human-readable textual representation for the given function.
pub fn write_mir_fn(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {
write_mir_intro(self.tcx, body, w, self.options)?;
for block in body.basic_blocks.indices() {
(self.extra_data)(PassWhere::BeforeBlock(block), w)?;
self.write_basic_block(block, body, w)?;
if block.index() + 1 != body.basic_blocks.len() {
writeln!(w)?;
}
}
write_allocations(tcx, body, w)?;
writeln!(w, "}}")?;
Ok(())
write_allocations(self.tcx, body, w)?;
Ok(())
}
}
/// Prints local variables in a scope tree.
@ -719,95 +699,88 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
///////////////////////////////////////////////////////////////////////////
// Basic blocks and their parts (statements, terminators, ...)
/// Write out a human-readable textual representation for the given basic block.
fn write_basic_block<'tcx, F>(
tcx: TyCtxt<'tcx>,
block: BasicBlock,
body: &Body<'tcx>,
extra_data: &mut F,
w: &mut dyn io::Write,
options: PrettyPrintMirOptions,
) -> io::Result<()>
where
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
let data = &body[block];
impl<'de, 'tcx> MirWriter<'de, 'tcx> {
/// Write out a human-readable textual representation for the given basic block.
fn write_basic_block(
&self,
block: BasicBlock,
body: &Body<'tcx>,
w: &mut dyn io::Write,
) -> io::Result<()> {
let data = &body[block];
// Basic block label at the top.
let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
// Basic block label at the top.
let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
// List of statements in the middle.
let mut current_location = Location { block, statement_index: 0 };
for statement in &data.statements {
extra_data(PassWhere::BeforeLocation(current_location), w)?;
let indented_body = format!("{INDENT}{INDENT}{statement:?};");
if options.include_extra_comments {
writeln!(
// List of statements in the middle.
let mut current_location = Location { block, statement_index: 0 };
for statement in &data.statements {
(self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
let indented_body = format!("{INDENT}{INDENT}{statement:?};");
if self.options.include_extra_comments {
writeln!(
w,
"{:A$} // {}{}",
indented_body,
if self.tcx.sess.verbose_internals() {
format!("{current_location:?}: ")
} else {
String::new()
},
comment(self.tcx, statement.source_info),
A = ALIGN,
)?;
} else {
writeln!(w, "{indented_body}")?;
}
write_extra(
self.tcx,
w,
"{:A$} // {}{}",
indented_body,
if tcx.sess.verbose_internals() {
format!("{current_location:?}: ")
} else {
String::new()
},
comment(tcx, statement.source_info),
A = ALIGN,
&|visitor| visitor.visit_statement(statement, current_location),
self.options,
)?;
} else {
writeln!(w, "{indented_body}")?;
(self.extra_data)(PassWhere::AfterLocation(current_location), w)?;
current_location.statement_index += 1;
}
write_extra(
tcx,
w,
|visitor| {
visitor.visit_statement(statement, current_location);
},
options,
)?;
// Terminator at the bottom.
(self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
if data.terminator.is_some() {
let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
if self.options.include_extra_comments {
writeln!(
w,
"{:A$} // {}{}",
indented_terminator,
if self.tcx.sess.verbose_internals() {
format!("{current_location:?}: ")
} else {
String::new()
},
comment(self.tcx, data.terminator().source_info),
A = ALIGN,
)?;
} else {
writeln!(w, "{indented_terminator}")?;
}
extra_data(PassWhere::AfterLocation(current_location), w)?;
current_location.statement_index += 1;
}
// Terminator at the bottom.
extra_data(PassWhere::BeforeLocation(current_location), w)?;
if data.terminator.is_some() {
let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
if options.include_extra_comments {
writeln!(
write_extra(
self.tcx,
w,
"{:A$} // {}{}",
indented_terminator,
if tcx.sess.verbose_internals() {
format!("{current_location:?}: ")
} else {
String::new()
},
comment(tcx, data.terminator().source_info),
A = ALIGN,
&|visitor| visitor.visit_terminator(data.terminator(), current_location),
self.options,
)?;
} else {
writeln!(w, "{indented_terminator}")?;
}
write_extra(
tcx,
w,
|visitor| {
visitor.visit_terminator(data.terminator(), current_location);
},
options,
)?;
(self.extra_data)(PassWhere::AfterLocation(current_location), w)?;
(self.extra_data)(PassWhere::AfterTerminator(block), w)?;
writeln!(w, "{INDENT}}}")
}
extra_data(PassWhere::AfterLocation(current_location), w)?;
extra_data(PassWhere::AfterTerminator(block), w)?;
writeln!(w, "{INDENT}}}")
}
impl Debug for Statement<'_> {
@ -1374,15 +1347,12 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
/// After we print the main statement, we sometimes dump extra
/// information. There's often a lot of little things "nuzzled up" in
/// a statement.
fn write_extra<'tcx, F>(
fn write_extra<'tcx>(
tcx: TyCtxt<'tcx>,
write: &mut dyn io::Write,
mut visit_op: F,
visit_op: &dyn Fn(&mut ExtraComments<'tcx>),
options: PrettyPrintMirOptions,
) -> io::Result<()>
where
F: FnMut(&mut ExtraComments<'tcx>),
{
) -> io::Result<()> {
if options.include_extra_comments {
let mut extra_comments = ExtraComments { tcx, comments: vec![] };
visit_op(&mut extra_comments);

View file

@ -389,10 +389,14 @@ pub enum ObligationCauseCode<'tcx> {
/// against.
MatchImpl(ObligationCause<'tcx>, DefId),
UnOp {
hir_id: HirId,
},
BinOp {
lhs_hir_id: HirId,
rhs_hir_id: Option<HirId>,
rhs_span: Option<Span>,
rhs_hir_id: HirId,
rhs_span: Span,
rhs_is_lit: bool,
output_ty: Option<Ty<'tcx>>,
},

View file

@ -806,10 +806,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
body.coverage_info_hi = self.coverage_info.as_ref().map(|b| b.as_done());
use rustc_middle::mir::pretty;
let options = pretty::PrettyPrintMirOptions::from_cli(self.tcx);
pretty::write_mir_fn(self.tcx, &body, &mut |_, _| Ok(()), &mut std::io::stdout(), options)
.unwrap();
let writer = pretty::MirWriter::new(self.tcx);
writer.write_mir_fn(&body, &mut std::io::stdout()).unwrap();
}
fn finish(self) -> Body<'tcx> {
@ -827,18 +825,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
body.coverage_info_hi = self.coverage_info.map(|b| b.into_done());
let writer = pretty::MirWriter::new(self.tcx);
for (index, block) in body.basic_blocks.iter().enumerate() {
if block.terminator.is_none() {
use rustc_middle::mir::pretty;
let options = pretty::PrettyPrintMirOptions::from_cli(self.tcx);
pretty::write_mir_fn(
self.tcx,
&body,
&mut |_, _| Ok(()),
&mut std::io::stdout(),
options,
)
.unwrap();
writer.write_mir_fn(&body, &mut std::io::stdout()).unwrap();
span_bug!(self.fn_span, "no terminator on block {:?}", index);
}
}

View file

@ -10,8 +10,7 @@ use std::{io, ops, str};
use regex::Regex;
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::mir::{
self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name,
traversal,
self, BasicBlock, Body, Location, MirDumper, graphviz_safe_def_name, traversal,
};
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::print::with_no_trimmed_paths;
@ -61,11 +60,13 @@ where
fs::File::create_buffered(&path)?
}
None if dump_enabled(tcx, A::NAME, def_id) => {
create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
None => {
let Some(dumper) = MirDumper::new(tcx, A::NAME, body) else {
return Ok(());
};
let disambiguator = &pass_name.unwrap_or("-----");
dumper.set_disambiguator(disambiguator).create_dump_file("dot", body)?
}
_ => return Ok(()),
}
};
let mut file = match file {

View file

@ -1294,7 +1294,9 @@ fn create_coroutine_resume_function<'tcx>(
pm::run_passes_no_validate(tcx, body, &[&abort_unwinding_calls::AbortUnwindingCalls], None);
dump_mir(tcx, false, "coroutine_resume", &0, body, |_, _| Ok(()));
if let Some(dumper) = MirDumper::new(tcx, "coroutine_resume", body) {
dumper.dump_mir(body);
}
}
/// An operation that can be performed on a coroutine.
@ -1446,7 +1448,9 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
assert!(body.coroutine_drop().is_none() && body.coroutine_drop_async().is_none());
dump_mir(tcx, false, "coroutine_before", &0, body, |_, _| Ok(()));
if let Some(dumper) = MirDumper::new(tcx, "coroutine_before", body) {
dumper.dump_mir(body);
}
// The first argument is the coroutine type passed by value
let coroutine_ty = body.local_decls.raw[1].ty;
@ -1506,7 +1510,10 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
) {
let context_mut_ref = transform_async_context(tcx, body);
expand_async_drops(tcx, body, context_mut_ref, coroutine_kind, coroutine_ty);
dump_mir(tcx, false, "coroutine_async_drop_expand", &0, body, |_, _| Ok(()));
if let Some(dumper) = MirDumper::new(tcx, "coroutine_async_drop_expand", body) {
dumper.dump_mir(body);
}
} else {
cleanup_async_drops(body);
}
@ -1605,14 +1612,18 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
// This is expanded to a drop ladder in `elaborate_coroutine_drops`.
let drop_clean = insert_clean_drop(tcx, body, has_async_drops);
dump_mir(tcx, false, "coroutine_pre-elab", &0, body, |_, _| Ok(()));
if let Some(dumper) = MirDumper::new(tcx, "coroutine_pre-elab", body) {
dumper.dump_mir(body);
}
// Expand `drop(coroutine_struct)` to a drop ladder which destroys upvars.
// If any upvars are moved out of, drop elaboration will handle upvar destruction.
// However we need to also elaborate the code generated by `insert_clean_drop`.
elaborate_coroutine_drops(tcx, body);
dump_mir(tcx, false, "coroutine_post-transform", &0, body, |_, _| Ok(()));
if let Some(dumper) = MirDumper::new(tcx, "coroutine_post-transform", body) {
dumper.dump_mir(body);
}
let can_unwind = can_unwind(tcx, body);

View file

@ -77,7 +77,7 @@ use rustc_hir::definitions::DisambiguatorState;
use rustc_middle::bug;
use rustc_middle::hir::place::{Projection, ProjectionKind};
use rustc_middle::mir::visit::MutVisitor;
use rustc_middle::mir::{self, dump_mir};
use rustc_middle::mir::{self, MirDumper};
use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, TypeVisitableExt};
pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
@ -225,7 +225,10 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
);
by_move_body.source =
mir::MirSource::from_instance(InstanceKind::Item(body_def.def_id().to_def_id()));
dump_mir(tcx, false, "built", &"after", &by_move_body, |_, _| Ok(()));
if let Some(dumper) = MirDumper::new(tcx, "built", &by_move_body) {
dumper.set_disambiguator(&"after").dump_mir(&by_move_body);
}
// Feed HIR because we try to access this body's attrs in the inliner.
body_def.feed_hir();

View file

@ -605,7 +605,9 @@ pub(super) fn create_coroutine_drop_shim<'tcx>(
// Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible
// filename.
body.source.instance = coroutine_instance;
dump_mir(tcx, false, "coroutine_drop", &0, &body, |_, _| Ok(()));
if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop", &body) {
dumper.dump_mir(&body);
}
body.source.instance = drop_instance;
// Creating a coroutine drop shim happens on `Analysis(PostCleanup) -> Runtime(Initial)`
@ -696,7 +698,9 @@ pub(super) fn create_coroutine_drop_shim_async<'tcx>(
None,
);
dump_mir(tcx, false, "coroutine_drop_async", &0, &body, |_, _| Ok(()));
if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_async", &body) {
dumper.dump_mir(&body);
}
body
}
@ -741,7 +745,9 @@ pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>(
};
body.basic_blocks_mut()[call_bb].terminator = Some(Terminator { source_info, kind });
dump_mir(tcx, false, "coroutine_drop_proxy_async", &0, &body, |_, _| Ok(()));
if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_proxy_async", &body) {
dumper.dump_mir(&body);
}
body
}

View file

@ -137,8 +137,8 @@ use rustc_index::interval::SparseIntervalMatrix;
use rustc_middle::bug;
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
use rustc_middle::mir::{
Body, HasLocalDecls, InlineAsmOperand, Local, LocalKind, Location, Operand, PassWhere, Place,
Rvalue, Statement, StatementKind, TerminatorKind, dump_mir, traversal,
Body, HasLocalDecls, InlineAsmOperand, Local, LocalKind, Location, MirDumper, Operand,
PassWhere, Place, Rvalue, Statement, StatementKind, TerminatorKind, traversal,
};
use rustc_middle::ty::TyCtxt;
use rustc_mir_dataflow::Analysis;
@ -810,11 +810,15 @@ fn dest_prop_mir_dump<'tcx>(
let location = points.point_from_location(location);
live.rows().filter(|&r| live.contains(r, location)).collect::<Vec<_>>()
};
dump_mir(tcx, false, "DestinationPropagation-dataflow", &round, body, |pass_where, w| {
if let PassWhere::BeforeLocation(loc) = pass_where {
writeln!(w, " // live: {:?}", locals_live_at(loc))?;
}
Ok(())
});
if let Some(dumper) = MirDumper::new(tcx, "DestinationPropagation-dataflow", body) {
let extra_data = &|pass_where, w: &mut dyn std::io::Write| {
if let PassWhere::BeforeLocation(loc) = pass_where {
writeln!(w, " // live: {:?}", locals_live_at(loc))?;
}
Ok(())
};
dumper.set_disambiguator(&round).set_extra_data(extra_data).dump_mir(body)
}
}

View file

@ -12,8 +12,8 @@ use rustc_index::{IndexSlice, IndexVec};
use rustc_macros::{LintDiagnostic, Subdiagnostic};
use rustc_middle::bug;
use rustc_middle::mir::{
self, BasicBlock, Body, ClearCrossCrate, Local, Location, Place, StatementKind, TerminatorKind,
dump_mir,
self, BasicBlock, Body, ClearCrossCrate, Local, Location, MirDumper, Place, StatementKind,
TerminatorKind,
};
use rustc_middle::ty::significant_drop_order::{
extract_component_with_significant_dtor, ty_dtor_span,
@ -227,7 +227,10 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
return;
}
dump_mir(tcx, false, "lint_tail_expr_drop_order", &0 as _, body, |_, _| Ok(()));
if let Some(dumper) = MirDumper::new(tcx, "lint_tail_expr_drop_order", body) {
dumper.dump_mir(body);
}
let locals_with_user_names = collect_user_names(body);
let is_closure_like = tcx.is_closure_like(def_id.to_def_id());

View file

@ -2,7 +2,7 @@ use std::cell::RefCell;
use std::collections::hash_map::Entry;
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_middle::mir::{self, Body, MirPhase, RuntimePhase};
use rustc_middle::mir::{Body, MirDumper, MirPhase, RuntimePhase};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use tracing::trace;
@ -281,16 +281,22 @@ fn run_passes_inner<'tcx>(
let lint = tcx.sess.opts.unstable_opts.lint_mir;
for pass in passes {
let name = pass.name();
let pass_name = pass.name();
if !should_run_pass(tcx, *pass, optimizations) {
continue;
};
let dump_enabled = pass.is_mir_dump_enabled();
let dumper = if pass.is_mir_dump_enabled()
&& let Some(dumper) = MirDumper::new(tcx, pass_name, body)
{
Some(dumper.set_show_pass_num().set_disambiguator(&"before"))
} else {
None
};
if dump_enabled {
dump_mir_for_pass(tcx, body, name, false);
if let Some(dumper) = dumper.as_ref() {
dumper.dump_mir(body);
}
if let Some(prof_arg) = &prof_arg {
@ -302,14 +308,15 @@ fn run_passes_inner<'tcx>(
pass.run_pass(tcx, body);
}
if dump_enabled {
dump_mir_for_pass(tcx, body, name, true);
if let Some(dumper) = dumper {
dumper.set_disambiguator(&"after").dump_mir(body);
}
if validate {
validate_body(tcx, body, format!("after pass {name}"));
validate_body(tcx, body, format!("after pass {pass_name}"));
}
if lint {
lint_body(tcx, body, format!("after pass {name}"));
lint_body(tcx, body, format!("after pass {pass_name}"));
}
body.pass_count += 1;
@ -345,18 +352,9 @@ pub(super) fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when
validate::Validator { when }.run_pass(tcx, body);
}
fn dump_mir_for_pass<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, pass_name: &str, is_after: bool) {
mir::dump_mir(
tcx,
true,
pass_name,
if is_after { &"after" } else { &"before" },
body,
|_, _| Ok(()),
);
}
pub(super) fn dump_mir_for_phase_change<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
assert_eq!(body.pass_count, 0);
mir::dump_mir(tcx, true, body.phase.name(), &"after", body, |_, _| Ok(()))
if let Some(dumper) = MirDumper::new(tcx, body.phase.name(), body) {
dumper.set_show_pass_num().set_disambiguator(&"after").dump_mir(body)
}
}

View file

@ -1242,14 +1242,12 @@ fn build_construct_coroutine_by_move_shim<'tcx>(
let body =
new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span);
dump_mir(
tcx,
false,
if receiver_by_ref { "coroutine_closure_by_ref" } else { "coroutine_closure_by_move" },
&0,
&body,
|_, _| Ok(()),
);
let pass_name =
if receiver_by_ref { "coroutine_closure_by_ref" } else { "coroutine_closure_by_move" };
if let Some(dumper) = MirDumper::new(tcx, pass_name, &body) {
dumper.dump_mir(&body);
}
body
}

View file

@ -1008,16 +1008,13 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let msg = format!("extern crate `{ident}` already in extern prelude");
self.r.tcx.dcx().span_delayed_bug(item.span, msg);
} else {
entry.item_binding = Some(imported_binding);
entry.introduced_by_item = orig_name.is_some();
entry.item_binding = Some((imported_binding, orig_name.is_some()));
}
entry
}
Entry::Vacant(vacant) => vacant.insert(ExternPreludeEntry {
item_binding: Some(imported_binding),
flag_binding: Cell::new(None),
only_item: true,
introduced_by_item: true,
item_binding: Some((imported_binding, true)),
flag_binding: None,
}),
};
}

View file

@ -204,7 +204,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> {
.r
.extern_prelude
.get(&Macros20NormalizedIdent::new(extern_crate.ident))
.is_none_or(|entry| entry.introduced_by_item)
.is_none_or(|entry| entry.introduced_by_item())
{
continue;
}

View file

@ -322,7 +322,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let from_item = self
.extern_prelude
.get(&Macros20NormalizedIdent::new(ident))
.is_none_or(|entry| entry.introduced_by_item);
.is_none_or(|entry| entry.introduced_by_item());
// Only suggest removing an import if both bindings are to the same def, if both spans
// aren't dummy spans. Further, if both bindings are imports, then the ident must have
// been introduced by an item.
@ -1845,7 +1845,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let AmbiguityError { kind, ident, b1, b2, misc1, misc2, .. } = *ambiguity_error;
let extern_prelude_ambiguity = || {
self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)).is_some_and(|entry| {
entry.item_binding == Some(b1) && entry.flag_binding.get() == Some(b2)
entry.item_binding.map(|(b, _)| b) == Some(b1)
&& entry.flag_binding.as_ref().and_then(|pb| pb.get().binding()) == Some(b2)
})
};
let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() {

View file

@ -32,7 +32,7 @@ use std::sync::Arc;
use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
use effective_visibilities::EffectiveVisibilitiesVisitor;
use errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst};
use imports::{Import, ImportData, ImportKind, NameResolution};
use imports::{Import, ImportData, ImportKind, NameResolution, PendingBinding};
use late::{
ForwardGenericParamBanReason, HasGenericParams, PathSource, PatternSource,
UnnecessaryQualification,
@ -1025,18 +1025,26 @@ impl<'ra> NameBindingData<'ra> {
}
}
#[derive(Default, Clone)]
struct ExternPreludeEntry<'ra> {
/// Binding from an `extern crate` item.
item_binding: Option<NameBinding<'ra>>,
/// The boolean flag is true is `item_binding` is non-redundant, happens either when
/// `flag_binding` is `None`, or when `extern crate` introducing `item_binding` used renaming.
item_binding: Option<(NameBinding<'ra>, /* introduced by item */ bool)>,
/// Binding from an `--extern` flag, lazily populated on first use.
flag_binding: Cell<Option<NameBinding<'ra>>>,
/// There was no `--extern` flag introducing this name,
/// `flag_binding` doesn't need to be populated.
only_item: bool,
/// `item_binding` is non-redundant, happens either when `only_item` is true,
/// or when `extern crate` introducing `item_binding` used renaming.
introduced_by_item: bool,
flag_binding: Option<Cell<PendingBinding<'ra>>>,
}
impl ExternPreludeEntry<'_> {
fn introduced_by_item(&self) -> bool {
matches!(self.item_binding, Some((_, true)))
}
fn flag() -> Self {
ExternPreludeEntry {
item_binding: None,
flag_binding: Some(Cell::new(PendingBinding::Pending)),
}
}
}
struct DeriveData {
@ -1528,7 +1536,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
&& let name = Symbol::intern(name)
&& name.can_be_raw()
{
Some((Macros20NormalizedIdent::with_dummy_span(name), Default::default()))
let ident = Macros20NormalizedIdent::with_dummy_span(name);
Some((ident, ExternPreludeEntry::flag()))
} else {
None
}
@ -1536,11 +1545,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
.collect();
if !attr::contains_name(attrs, sym::no_core) {
extern_prelude
.insert(Macros20NormalizedIdent::with_dummy_span(sym::core), Default::default());
let ident = Macros20NormalizedIdent::with_dummy_span(sym::core);
extern_prelude.insert(ident, ExternPreludeEntry::flag());
if !attr::contains_name(attrs, sym::no_std) {
extern_prelude
.insert(Macros20NormalizedIdent::with_dummy_span(sym::std), Default::default());
let ident = Macros20NormalizedIdent::with_dummy_span(sym::std);
extern_prelude.insert(ident, ExternPreludeEntry::flag());
}
}
@ -2062,12 +2071,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
// Avoid marking `extern crate` items that refer to a name from extern prelude,
// but not introduce it, as used if they are accessed from lexical scope.
if used == Used::Scope {
if let Some(entry) = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)) {
if !entry.introduced_by_item && entry.item_binding == Some(used_binding) {
return;
}
}
if used == Used::Scope
&& let Some(entry) = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident))
&& entry.item_binding == Some((used_binding, false))
{
return;
}
let old_used = self.import_use_map.entry(import).or_insert(used);
if *old_used < used {
@ -2226,7 +2234,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
finalize: bool,
) -> Option<NameBinding<'ra>> {
let entry = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident));
entry.and_then(|entry| entry.item_binding).map(|binding| {
entry.and_then(|entry| entry.item_binding).map(|(binding, _)| {
if finalize {
self.get_mut().record_use(ident, binding, Used::Scope);
}
@ -2236,31 +2244,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
fn extern_prelude_get_flag(&self, ident: Ident, finalize: bool) -> Option<NameBinding<'ra>> {
let entry = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident));
entry.and_then(|entry| match entry.flag_binding.get() {
Some(binding) => {
if finalize {
self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span);
}
Some(binding)
}
None if entry.only_item => None,
None => {
let crate_id = if finalize {
self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span)
} else {
self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name)
};
match crate_id {
Some(crate_id) => {
let res = Res::Def(DefKind::Mod, crate_id.as_def_id());
let binding =
self.arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT);
entry.flag_binding.set(Some(binding));
Some(binding)
entry.and_then(|entry| entry.flag_binding.as_ref()).and_then(|flag_binding| {
let binding = match flag_binding.get() {
PendingBinding::Ready(binding) => {
if finalize {
self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span);
}
None => finalize.then_some(self.dummy_binding),
binding
}
}
PendingBinding::Pending => {
let crate_id = if finalize {
self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span)
} else {
self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name)
};
crate_id.map(|crate_id| {
let res = Res::Def(DefKind::Mod, crate_id.as_def_id());
self.arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT)
})
}
};
flag_binding.set(PendingBinding::Ready(binding));
binding.or_else(|| finalize.then_some(self.dummy_binding))
})
}

View file

@ -3,7 +3,8 @@ use std::borrow::Cow;
use std::path::PathBuf;
use rustc_abi::ExternAbi;
use rustc_ast::TraitObjectSyntax;
use rustc_ast::ast::LitKind;
use rustc_ast::{LitIntType, TraitObjectSyntax};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unord::UnordSet;
use rustc_errors::codes::*;
@ -280,6 +281,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
(suggested, noted_missing_impl) = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
}
suggested |= self.detect_negative_literal(
&obligation,
main_trait_predicate,
&mut err,
);
if let Some(ret_span) = self.return_type_span(&obligation) {
if is_try_conversion {
let ty = self.tcx.short_string(
@ -950,6 +957,38 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
Ok(())
}
fn detect_negative_literal(
&self,
obligation: &PredicateObligation<'tcx>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
err: &mut Diag<'_>,
) -> bool {
if let ObligationCauseCode::UnOp { hir_id, .. } = obligation.cause.code()
&& let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id)
&& let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = expr.kind
&& let hir::ExprKind::Lit(lit) = inner.kind
&& let LitKind::Int(_, LitIntType::Unsuffixed) = lit.node
{
err.span_suggestion_verbose(
lit.span.shrink_to_hi(),
"consider specifying an integer type that can be negative",
match trait_pred.skip_binder().self_ty().kind() {
ty::Uint(ty::UintTy::Usize) => "isize",
ty::Uint(ty::UintTy::U8) => "i8",
ty::Uint(ty::UintTy::U16) => "i16",
ty::Uint(ty::UintTy::U32) => "i32",
ty::Uint(ty::UintTy::U64) => "i64",
ty::Uint(ty::UintTy::U128) => "i128",
_ => "i64",
}
.to_string(),
Applicability::MaybeIncorrect,
);
return true;
}
false
}
/// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`,
/// identify those method chain sub-expressions that could or could not have been annotated
/// with `?`.
@ -2730,9 +2769,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
suggested: bool,
) {
let body_def_id = obligation.cause.body_id;
let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } =
obligation.cause.code()
{
let span = if let ObligationCauseCode::BinOp { rhs_span, .. } = obligation.cause.code() {
*rhs_span
} else {
span

View file

@ -554,7 +554,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
return true;
}
} else if let (
ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id: Some(rhs_hir_id), .. },
ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. },
predicate,
) = code.peel_derives_with_predicate()
&& let Some(typeck_results) = &self.typeck_results
@ -2801,6 +2801,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
| ObligationCauseCode::QuestionMark
| ObligationCauseCode::CheckAssociatedTypeBounds { .. }
| ObligationCauseCode::LetElse
| ObligationCauseCode::UnOp { .. }
| ObligationCauseCode::BinOp { .. }
| ObligationCauseCode::AscribeUserTypeProvePredicate(..)
| ObligationCauseCode::AlwaysApplicableImpl
@ -3839,9 +3840,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
let rhs_span = match obligation.cause.code() {
ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => {
span
}
ObligationCauseCode::BinOp { rhs_span, rhs_is_lit, .. } if *rhs_is_lit => rhs_span,
_ => return,
};
if let ty::Float(_) = trait_pred.skip_binder().self_ty().kind()
@ -5108,16 +5107,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let tcx = self.tcx;
let predicate = predicate.upcast(tcx);
match *cause_code {
ObligationCauseCode::BinOp {
lhs_hir_id,
rhs_hir_id: Some(rhs_hir_id),
rhs_span: Some(rhs_span),
..
} if let Some(typeck_results) = &self.typeck_results
&& let hir::Node::Expr(lhs) = tcx.hir_node(lhs_hir_id)
&& let hir::Node::Expr(rhs) = tcx.hir_node(rhs_hir_id)
&& let Some(lhs_ty) = typeck_results.expr_ty_opt(lhs)
&& let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) =>
ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, rhs_span, .. }
if let Some(typeck_results) = &self.typeck_results
&& let hir::Node::Expr(lhs) = tcx.hir_node(lhs_hir_id)
&& let hir::Node::Expr(rhs) = tcx.hir_node(rhs_hir_id)
&& let Some(lhs_ty) = typeck_results.expr_ty_opt(lhs)
&& let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) =>
{
if let Some(pred) = predicate.as_trait_clause()
&& tcx.is_lang_item(pred.def_id(), LangItem::PartialEq)

View file

@ -736,6 +736,31 @@ impl<T> Bound<T> {
}
}
impl<T: Copy> Bound<&T> {
/// Map a `Bound<&T>` to a `Bound<T>` by copying the contents of the bound.
///
/// # Examples
///
/// ```
/// #![feature(bound_copied)]
///
/// use std::ops::Bound::*;
/// use std::ops::RangeBounds;
///
/// assert_eq!((1..12).start_bound(), Included(&1));
/// assert_eq!((1..12).start_bound().copied(), Included(1));
/// ```
#[unstable(feature = "bound_copied", issue = "145966")]
#[must_use]
pub fn copied(self) -> Bound<T> {
match self {
Bound::Unbounded => Bound::Unbounded,
Bound::Included(x) => Bound::Included(*x),
Bound::Excluded(x) => Bound::Excluded(*x),
}
}
}
impl<T: Clone> Bound<&T> {
/// Map a `Bound<&T>` to a `Bound<T>` by cloning the contents of the bound.
///
@ -745,8 +770,11 @@ impl<T: Clone> Bound<&T> {
/// use std::ops::Bound::*;
/// use std::ops::RangeBounds;
///
/// assert_eq!((1..12).start_bound(), Included(&1));
/// assert_eq!((1..12).start_bound().cloned(), Included(1));
/// let a1 = String::from("a");
/// let (a2, a3, a4) = (a1.clone(), a1.clone(), a1.clone());
///
/// assert_eq!(Included(&a1), (a2..).start_bound());
/// assert_eq!(Included(a3), (a4..).start_bound().cloned());
/// ```
#[must_use = "`self` will be dropped if the result is not used"]
#[stable(feature = "bound_cloned", since = "1.55.0")]

View file

@ -79,8 +79,8 @@ fn test_log() {
let nan: f32 = f32::NAN;
let inf: f32 = f32::INFINITY;
let neg_inf: f32 = f32::NEG_INFINITY;
assert_approx_eq!(10.0f32.log(10.0), 1.0);
assert_approx_eq!(2.3f32.log(3.5), 0.664858);
assert_approx_eq!(10.0f32.log(10.0), 1.0, APPROX_DELTA);
assert_approx_eq!(2.3f32.log(3.5), 0.664858, APPROX_DELTA);
assert_approx_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0, APPROX_DELTA);
assert!(1.0f32.log(1.0).is_nan());
assert!(1.0f32.log(-13.9).is_nan());

View file

@ -3,6 +3,11 @@ error[E0599]: no method named `test` found for opaque type `impl Future<Output =
|
LL | let x: u32 = foo().test();
| ^^^^ method not found in `impl Future<Output = A>`
|
help: consider `await`ing on the `Future` and calling the method on its `Output`
|
LL | let x: u32 = foo().await.test();
| ++++++
error: aborting due to 1 previous error

View file

@ -3,6 +3,11 @@ error[E0616]: field `len` of struct `Foo` is private
|
LL | if x.len {
| ^^^ private field
|
help: a method `len` also exists, call it with parentheses
|
LL | if x.len() {
| ++
error: aborting due to 1 previous error

View file

@ -0,0 +1,13 @@
fn main() {
for x in -5..5 {
//~^ ERROR: the trait bound `usize: Neg` is not satisfied
//~| HELP: consider specifying an integer type that can be negative
do_something(x);
}
let x = -5;
//~^ ERROR: the trait bound `usize: Neg` is not satisfied
//~| HELP: consider specifying an integer type that can be negative
do_something(x);
}
fn do_something(_val: usize) {}

View file

@ -0,0 +1,25 @@
error[E0277]: the trait bound `usize: Neg` is not satisfied
--> $DIR/negative-literal-infered-to-unsigned.rs:2:14
|
LL | for x in -5..5 {
| ^^ the trait `Neg` is not implemented for `usize`
|
help: consider specifying an integer type that can be negative
|
LL | for x in -5isize..5 {
| +++++
error[E0277]: the trait bound `usize: Neg` is not satisfied
--> $DIR/negative-literal-infered-to-unsigned.rs:7:13
|
LL | let x = -5;
| ^^ the trait `Neg` is not implemented for `usize`
|
help: consider specifying an integer type that can be negative
|
LL | let x = -5isize;
| +++++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,14 @@
struct LlamaModel;
impl LlamaModel {
fn chat_template(&self) -> Result<&str, ()> {
todo!()
}
}
fn template_from_str(_x: &str) {}
fn main() {
let model = LlamaModel;
template_from_str(&model.chat_template); //~ ERROR attempted to take value of method `chat_template` on type `LlamaModel`
}

View file

@ -0,0 +1,14 @@
error[E0615]: attempted to take value of method `chat_template` on type `LlamaModel`
--> $DIR/suggest-method-name-with-maybe-ty-mismatch-146008.rs:13:30
|
LL | template_from_str(&model.chat_template);
| ^^^^^^^^^^^^^ method, not a field
|
help: use parentheses to call the method
|
LL | template_from_str(&model.chat_template());
| ++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0615`.