From de6193450bd36ada3ea995ca455399c8bfb8a298 Mon Sep 17 00:00:00 2001 From: Vladimir Petrzhikovskii Date: Tue, 5 Aug 2025 16:34:11 +0200 Subject: [PATCH 01/14] std: clarify `OpenOptions` error for create without write access Previously, attempting to create/truncate a file without write/append access would result in platform-specific error messages: - Unix: "Invalid argument" - Windows: raw OS error code 87 These error codes look like system errors, which could waste hours of debugging for what is actually an API misuse issue. --- library/std/src/fs.rs | 7 +++++- library/std/src/fs/tests.rs | 38 ++++++++++++++++++++++++++----- library/std/src/sys/fs/unix.rs | 26 ++++++++++++++++++--- library/std/src/sys/fs/windows.rs | 24 ++++++++++++++++--- 4 files changed, 82 insertions(+), 13 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 72ad7c244eeba..0015d1659aeb5 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1609,6 +1609,10 @@ impl OpenOptions { /// See also [`std::fs::write()`][self::write] for a simple function to /// create a file with some given data. /// + /// # Errors + /// + /// If `.create(true)` is set without `.write(true)` or `.append(true)`, + /// calling [`open`](Self::open) will fail with [`InvalidInput`](io::ErrorKind::InvalidInput) error. /// # Examples /// /// ```no_run @@ -1680,7 +1684,8 @@ impl OpenOptions { /// * [`AlreadyExists`]: `create_new` was specified and the file already /// exists. /// * [`InvalidInput`]: Invalid combinations of open options (truncate - /// without write access, no access mode set, etc.). + /// without write access, create without write or append access, + /// no access mode set, etc.). /// /// The following errors don't match any existing [`io::ErrorKind`] at the moment: /// * One of the directory components of the specified file path diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index c81e3af2f0d4c..f39dd638cd557 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1265,12 +1265,7 @@ fn open_flavors() { let mut ra = OO::new(); ra.read(true).append(true); - #[cfg(windows)] - let invalid_options = 87; // ERROR_INVALID_PARAMETER - #[cfg(all(unix, not(target_os = "vxworks")))] - let invalid_options = "Invalid argument"; - #[cfg(target_os = "vxworks")] - let invalid_options = "invalid argument"; + let invalid_options = "creating or truncating a file requires write or append access"; // Test various combinations of creation modes and access modes. // @@ -2084,3 +2079,34 @@ fn test_rename_junction() { // Junction links are always absolute so we just check the file name is correct. assert_eq!(fs::read_link(&dest).unwrap().file_name(), Some(not_exist.as_os_str())); } + +#[test] +fn test_open_options_invalid_combinations() { + use crate::fs::OpenOptions as OO; + + let test_cases: &[(fn() -> OO, &str)] = &[ + (|| OO::new().create(true).read(true).clone(), "create without write"), + (|| OO::new().create_new(true).read(true).clone(), "create_new without write"), + (|| OO::new().truncate(true).read(true).clone(), "truncate without write"), + (|| OO::new().truncate(true).append(true).clone(), "truncate with append"), + ]; + + for (make_opts, desc) in test_cases { + let opts = make_opts(); + let result = opts.open("nonexistent.txt"); + assert!(result.is_err(), "{desc} should fail"); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput, "{desc} - wrong error kind"); + assert_eq!( + err.to_string(), + "creating or truncating a file requires write or append access", + "{desc} - wrong error message" + ); + } + + let result = OO::new().open("nonexistent.txt"); + assert!(result.is_err(), "no access mode should fail"); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + assert_eq!(err.to_string(), "must specify at least one of read, write, or append access"); +} diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index b310db2dac485..14524abc4ab09 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1122,7 +1122,21 @@ impl OpenOptions { (true, true, false) => Ok(libc::O_RDWR), (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), - (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)), + (false, false, false) => { + // If no access mode is set, check if any creation flags are set + // to provide a more descriptive error message + if self.create || self.create_new || self.truncate { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "must specify at least one of read, write, or append access", + )) + } + } } } @@ -1131,12 +1145,18 @@ impl OpenOptions { (true, false) => {} (false, false) => { if self.truncate || self.create || self.create_new { - return Err(Error::from_raw_os_error(libc::EINVAL)); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )); } } (_, true) => { if self.truncate && !self.create_new { - return Err(Error::from_raw_os_error(libc::EINVAL)); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )); } } } diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 9b674a251652b..943195ac7d0f7 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -258,7 +258,19 @@ impl OpenOptions { Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA)) } (false, false, false, None) => { - Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)) + // If no access mode is set, check if any creation flags are set + // to provide a more descriptive error message + if self.create || self.create_new || self.truncate { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "must specify at least one of read, write, or append access", + )) + } } } } @@ -268,12 +280,18 @@ impl OpenOptions { (true, false) => {} (false, false) => { if self.truncate || self.create || self.create_new { - return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )); } } (_, true) => { if self.truncate && !self.create_new { - return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )); } } } From 6e6dfa6119197161e945c89bf0e1299f1b0c5564 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 Aug 2025 10:56:18 +1000 Subject: [PATCH 02/14] Inline and remove `dump_matched_mir_node`. It has a single call site. --- compiler/rustc_middle/src/mir/pretty.rs | 36 ++++++++----------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index d4d925d20574e..e390a2bc3a521 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -125,7 +125,17 @@ pub fn dump_mir_with_options<'tcx, F>( return; } - dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data, options); + 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)?; + }; + } } pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool { @@ -187,30 +197,6 @@ where 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)?; - }; - } -} - /// 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. From 36333f4b42f58cd3c8488a62babd95d25c38b137 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 Aug 2025 11:13:24 +1000 Subject: [PATCH 03/14] Inline and remove `dump_mir_for_pass`. The code is more readable without it. --- .../rustc_mir_transform/src/pass_manager.rs | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 7a8d3ba1ff1fa..0e286a45e2aa4 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -264,13 +264,14 @@ fn run_passes_inner<'tcx>( } let prof_arg = tcx.sess.prof.enabled().then(|| format!("{:?}", body.source.def_id())); + let pass_num = true; if !body.should_skip() { let validate = validate_each & tcx.sess.opts.unstable_opts.validate_mir; 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; @@ -279,7 +280,7 @@ fn run_passes_inner<'tcx>( let dump_enabled = pass.is_mir_dump_enabled(); if dump_enabled { - dump_mir_for_pass(tcx, body, name, false); + mir::dump_mir(tcx, pass_num, pass_name, &"before", body, |_, _| Ok(())); } if let Some(prof_arg) = &prof_arg { @@ -292,13 +293,13 @@ fn run_passes_inner<'tcx>( } if dump_enabled { - dump_mir_for_pass(tcx, body, name, true); + mir::dump_mir(tcx, pass_num, pass_name, &"after", body, |_, _| Ok(())); } 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; @@ -334,17 +335,6 @@ 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(())) From f5694598b25c5dc7be18b3225a2dfa5fbcb585a2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 15 Aug 2025 09:13:23 +1000 Subject: [PATCH 04/14] Avoid unnecessary `mut`-ness for various closures. --- compiler/rustc_codegen_cranelift/src/base.rs | 2 +- compiler/rustc_middle/src/mir/pretty.rs | 28 ++++++++++---------- compiler/rustc_mir_build/src/builder/mod.rs | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index bc0a0f034b236..aefb9c9a11564 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -46,7 +46,7 @@ pub(crate) fn codegen_fn<'tcx>( 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(); + pretty::write_mir_fn(tcx, mir, &|_, _| Ok(()), &mut buf, options).unwrap(); }); String::from_utf8_lossy(&buf).into_owned() }); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index e390a2bc3a521..4deb9e6b016c8 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -91,7 +91,7 @@ pub fn dump_mir<'tcx, F>( body: &Body<'tcx>, extra_data: F, ) where - F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, + F: Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, { dump_mir_with_options( tcx, @@ -119,7 +119,7 @@ pub fn dump_mir_with_options<'tcx, F>( extra_data: F, options: PrettyPrintMirOptions, ) where - F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, + F: Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, { if !dump_enabled(tcx, pass_name, body.source.def_id()) { return; @@ -171,11 +171,11 @@ pub fn dump_mir_to_writer<'tcx, F>( disambiguator: &dyn Display, body: &Body<'tcx>, w: &mut dyn io::Write, - mut extra_data: F, + extra_data: F, options: PrettyPrintMirOptions, ) -> io::Result<()> where - F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, + F: Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, { // see notes on #41697 above let def_path = @@ -193,7 +193,7 @@ where writeln!(w)?; extra_data(PassWhere::BeforeCFG, w)?; write_user_type_annotations(tcx, body, w)?; - write_mir_fn(tcx, body, &mut extra_data, w, options)?; + write_mir_fn(tcx, body, &extra_data, w, options)?; extra_data(PassWhere::AfterCFG, w) } @@ -343,11 +343,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)?; + write_mir_fn(tcx, body, &|_, _| Ok(()), w, options)?; for body in tcx.promoted_mir(def_id) { writeln!(w)?; - write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?; + write_mir_fn(tcx, body, &|_, _| Ok(()), w, options)?; } Ok(()) }; @@ -359,7 +359,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)?; + write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &|_, _| Ok(()), w, options)?; } else { let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id)); render_body(w, instance_mir)?; @@ -372,12 +372,12 @@ pub fn write_mir_pretty<'tcx>( pub fn write_mir_fn<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - extra_data: &mut F, + extra_data: &F, w: &mut dyn io::Write, options: PrettyPrintMirOptions, ) -> io::Result<()> where - F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, + F: Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, { write_mir_intro(tcx, body, w, options)?; for block in body.basic_blocks.indices() { @@ -710,12 +710,12 @@ fn write_basic_block<'tcx, F>( tcx: TyCtxt<'tcx>, block: BasicBlock, body: &Body<'tcx>, - extra_data: &mut F, + extra_data: &F, w: &mut dyn io::Write, options: PrettyPrintMirOptions, ) -> io::Result<()> where - F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, + F: Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, { let data = &body[block]; @@ -1360,11 +1360,11 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fn write_extra<'tcx, F>( tcx: TyCtxt<'tcx>, write: &mut dyn io::Write, - mut visit_op: F, + visit_op: F, options: PrettyPrintMirOptions, ) -> io::Result<()> where - F: FnMut(&mut ExtraComments<'tcx>), + F: Fn(&mut ExtraComments<'tcx>), { if options.include_extra_comments { let mut extra_comments = ExtraComments { tcx, comments: vec![] }; diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 855cd2f3bc0a6..3d7b9feefabc1 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -811,7 +811,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pretty::write_mir_fn( self.tcx, &body, - &mut |_, _| Ok(()), + &|_, _| Ok(()), &mut std::io::stdout(), options, ) From 34c9ff2056c4048184c36cbcc983799e0aeb217b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 15 Aug 2025 13:13:14 +1000 Subject: [PATCH 05/14] Use trait object references for closures. The dynamic dispatch cost doesn't matter for MIR dumping, which is perf-insensitive. And it's necessary for the next commit, which will store some `extra_data` closures in a struct. --- compiler/rustc_borrowck/src/nll.rs | 4 +- compiler/rustc_borrowck/src/polonius/dump.rs | 2 +- compiler/rustc_middle/src/mir/pretty.rs | 62 +++++++------------ compiler/rustc_mir_transform/src/coroutine.rs | 10 +-- .../src/coroutine/by_move_body.rs | 2 +- .../rustc_mir_transform/src/coroutine/drop.rs | 6 +- compiler/rustc_mir_transform/src/dest_prop.rs | 2 +- .../src/lint_tail_expr_drop_order.rs | 2 +- .../rustc_mir_transform/src/pass_manager.rs | 6 +- compiler/rustc_mir_transform/src/shim.rs | 2 +- 10 files changed, 39 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 8608a8a3a66f3..021dba9c9a922 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -68,7 +68,7 @@ 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(())); + dump_mir(infcx.tcx, false, "renumber", &0, body, &|_, _| Ok(())); universal_regions } @@ -194,7 +194,7 @@ pub(super) fn dump_nll_mir<'tcx>( "nll", &0, body, - |pass_where, out| { + &|pass_where, out| { emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out) }, options, diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 6b13b5ad0814a..173fb5969769d 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -175,7 +175,7 @@ fn emit_html_mir<'tcx>( &0, body, &mut buffer, - |pass_where, out| { + &|pass_where, out| { emit_polonius_mir( tcx, regioncx, diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 4deb9e6b016c8..8527fc9dc500c 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -83,16 +83,14 @@ impl PrettyPrintMirOptions { /// - `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>( +pub fn dump_mir<'tcx>( tcx: TyCtxt<'tcx>, pass_num: bool, pass_name: &str, disambiguator: &dyn Display, body: &Body<'tcx>, - extra_data: F, -) where - F: Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, -{ + extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, +) { dump_mir_with_options( tcx, pass_num, @@ -110,17 +108,15 @@ pub fn dump_mir<'tcx, F>( /// See [`dump_mir`] for more details. /// #[inline] -pub fn dump_mir_with_options<'tcx, F>( +pub fn dump_mir_with_options<'tcx>( tcx: TyCtxt<'tcx>, pass_num: bool, pass_name: &str, disambiguator: &dyn Display, body: &Body<'tcx>, - extra_data: F, + extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, options: PrettyPrintMirOptions, -) where - F: Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, -{ +) { if !dump_enabled(tcx, pass_name, body.source.def_id()) { return; } @@ -165,18 +161,15 @@ pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool { /// 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>( +pub fn dump_mir_to_writer<'tcx>( tcx: TyCtxt<'tcx>, pass_name: &str, disambiguator: &dyn Display, body: &Body<'tcx>, w: &mut dyn io::Write, - extra_data: F, + extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, options: PrettyPrintMirOptions, -) -> io::Result<()> -where - F: Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, -{ +) -> 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())); @@ -193,7 +186,7 @@ where writeln!(w)?; extra_data(PassWhere::BeforeCFG, w)?; write_user_type_annotations(tcx, body, w)?; - write_mir_fn(tcx, body, &extra_data, w, options)?; + write_mir_fn(tcx, body, extra_data, w, options)?; extra_data(PassWhere::AfterCFG, w) } @@ -369,16 +362,13 @@ pub fn write_mir_pretty<'tcx>( } /// Write out a human-readable textual representation for the given function. -pub fn write_mir_fn<'tcx, F>( +pub fn write_mir_fn<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - extra_data: &F, + extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, w: &mut dyn io::Write, options: PrettyPrintMirOptions, -) -> io::Result<()> -where - F: Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, -{ +) -> io::Result<()> { write_mir_intro(tcx, body, w, options)?; for block in body.basic_blocks.indices() { extra_data(PassWhere::BeforeBlock(block), w)?; @@ -706,17 +696,14 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { // 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>( +fn write_basic_block<'tcx>( tcx: TyCtxt<'tcx>, block: BasicBlock, body: &Body<'tcx>, - extra_data: &F, + extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, w: &mut dyn io::Write, options: PrettyPrintMirOptions, -) -> io::Result<()> -where - F: Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, -{ +) -> io::Result<()> { let data = &body[block]; // Basic block label at the top. @@ -748,9 +735,7 @@ where write_extra( tcx, w, - |visitor| { - visitor.visit_statement(statement, current_location); - }, + &|visitor| visitor.visit_statement(statement, current_location), options, )?; @@ -783,9 +768,7 @@ where write_extra( tcx, w, - |visitor| { - visitor.visit_terminator(data.terminator(), current_location); - }, + &|visitor| visitor.visit_terminator(data.terminator(), current_location), options, )?; } @@ -1357,15 +1340,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, - visit_op: F, + visit_op: &dyn Fn(&mut ExtraComments<'tcx>), options: PrettyPrintMirOptions, -) -> io::Result<()> -where - F: Fn(&mut ExtraComments<'tcx>), -{ +) -> io::Result<()> { if options.include_extra_comments { let mut extra_comments = ExtraComments { tcx, comments: vec![] }; visit_op(&mut extra_comments); diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 761d5461a996f..592192944d2b8 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1294,7 +1294,7 @@ 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(())); + dump_mir(tcx, false, "coroutine_resume", &0, body, &|_, _| Ok(())); } /// An operation that can be performed on a coroutine. @@ -1446,7 +1446,7 @@ 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(())); + dump_mir(tcx, false, "coroutine_before", &0, body, &|_, _| Ok(())); // The first argument is the coroutine type passed by value let coroutine_ty = body.local_decls.raw[1].ty; @@ -1506,7 +1506,7 @@ 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(())); + dump_mir(tcx, false, "coroutine_async_drop_expand", &0, body, &|_, _| Ok(())); } else { cleanup_async_drops(body); } @@ -1605,14 +1605,14 @@ 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(())); + dump_mir(tcx, false, "coroutine_pre-elab", &0, body, &|_, _| Ok(())); // 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(())); + dump_mir(tcx, false, "coroutine_post-transform", &0, body, &|_, _| Ok(())); let can_unwind = can_unwind(tcx, body); diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 81d7b7ba02c2c..5ba6fea9fafb0 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -225,7 +225,7 @@ 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(())); + dump_mir(tcx, false, "built", &"after", &by_move_body, &|_, _| Ok(())); // Feed HIR because we try to access this body's attrs in the inliner. body_def.feed_hir(); diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index 1a314e029f4a7..6dffbc866278d 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -605,7 +605,7 @@ 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(())); + dump_mir(tcx, false, "coroutine_drop", &0, &body, &|_, _| Ok(())); body.source.instance = drop_instance; // Creating a coroutine drop shim happens on `Analysis(PostCleanup) -> Runtime(Initial)` @@ -696,7 +696,7 @@ pub(super) fn create_coroutine_drop_shim_async<'tcx>( None, ); - dump_mir(tcx, false, "coroutine_drop_async", &0, &body, |_, _| Ok(())); + dump_mir(tcx, false, "coroutine_drop_async", &0, &body, &|_, _| Ok(())); body } @@ -741,7 +741,7 @@ 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(())); + dump_mir(tcx, false, "coroutine_drop_proxy_async", &0, &body, &|_, _| Ok(())); body } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 4c94a6c524e00..bb68d1a065957 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -810,7 +810,7 @@ fn dest_prop_mir_dump<'tcx>( let location = points.point_from_location(location); live.rows().filter(|&r| live.contains(r, location)).collect::>() }; - dump_mir(tcx, false, "DestinationPropagation-dataflow", &round, body, |pass_where, w| { + 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))?; } diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs index 2f0edf31162d2..0d0a71bc6c7e9 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -227,7 +227,7 @@ 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(())); + dump_mir(tcx, false, "lint_tail_expr_drop_order", &0 as _, body, &|_, _| Ok(())); let locals_with_user_names = collect_user_names(body); let is_closure_like = tcx.is_closure_like(def_id.to_def_id()); diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 0e286a45e2aa4..77231f730d82f 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -280,7 +280,7 @@ fn run_passes_inner<'tcx>( let dump_enabled = pass.is_mir_dump_enabled(); if dump_enabled { - mir::dump_mir(tcx, pass_num, pass_name, &"before", body, |_, _| Ok(())); + mir::dump_mir(tcx, pass_num, pass_name, &"before", body, &|_, _| Ok(())); } if let Some(prof_arg) = &prof_arg { @@ -293,7 +293,7 @@ fn run_passes_inner<'tcx>( } if dump_enabled { - mir::dump_mir(tcx, pass_num, pass_name, &"after", body, |_, _| Ok(())); + mir::dump_mir(tcx, pass_num, pass_name, &"after", body, &|_, _| Ok(())); } if validate { validate_body(tcx, body, format!("after pass {pass_name}")); @@ -337,5 +337,5 @@ pub(super) fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when 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(())) + mir::dump_mir(tcx, true, body.phase.name(), &"after", body, &|_, _| Ok(())) } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index c687036f544dc..a78b32ea4c976 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -1248,7 +1248,7 @@ fn build_construct_coroutine_by_move_shim<'tcx>( if receiver_by_ref { "coroutine_closure_by_ref" } else { "coroutine_closure_by_move" }, &0, &body, - |_, _| Ok(()), + &|_, _| Ok(()), ); body From e0e75337b8cada56ddc496b940280947ca89b26b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 15 Aug 2025 14:38:03 +1000 Subject: [PATCH 06/14] Indent some functions. This commit exists purely to simplify reviewing: these functions will become methods in the next commit. This commit indents them so that the next commit is more readable. --- compiler/rustc_middle/src/mir/pretty.rs | 644 ++++++++++++------------ 1 file changed, 322 insertions(+), 322 deletions(-) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 8527fc9dc500c..85ac793b016d1 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -58,257 +58,257 @@ 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... -/// ``` -/// -/// Output from this function is controlled by passing `-Z dump-mir=`, -/// where `` 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>( - tcx: TyCtxt<'tcx>, - pass_num: bool, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, - extra_data: &dyn Fn(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>( - tcx: TyCtxt<'tcx>, - pass_num: bool, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, - options: PrettyPrintMirOptions, -) { - if !dump_enabled(tcx, pass_name, body.source.def_id()) { - return; + /// If the session is properly configured, dumps a human-readable representation of the MIR + /// (with default pretty-printing options) into: + /// + /// ```text + /// rustc.node... + /// ``` + /// + /// Output from this function is controlled by passing `-Z dump-mir=`, + /// where `` 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>( + tcx: TyCtxt<'tcx>, + pass_num: bool, + pass_name: &str, + disambiguator: &dyn Display, + body: &Body<'tcx>, + extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, + ) { + dump_mir_with_options( + tcx, + pass_num, + pass_name, + disambiguator, + body, + extra_data, + PrettyPrintMirOptions::from_cli(tcx), + ); } - 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 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>( + tcx: TyCtxt<'tcx>, + pass_num: bool, + pass_name: &str, + disambiguator: &dyn Display, + body: &Body<'tcx>, + extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, + options: PrettyPrintMirOptions, + ) { + if !dump_enabled(tcx, pass_name, body.source.def_id()) { + return; + } - 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)?; + 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)?; + }; + } } -} -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) + 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>( - tcx: TyCtxt<'tcx>, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, - w: &mut dyn io::Write, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, - options: PrettyPrintMirOptions, -) -> 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:#?} */")?; + + // #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>( + tcx: TyCtxt<'tcx>, + pass_name: &str, + disambiguator: &dyn Display, + body: &Body<'tcx>, + w: &mut dyn io::Write, + extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, + options: PrettyPrintMirOptions, + ) -> 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, extra_data, w, options)?; + extra_data(PassWhere::AfterCFG, w) } - writeln!(w)?; - extra_data(PassWhere::BeforeCFG, w)?; - write_user_type_annotations(tcx, body, w)?; - write_mir_fn(tcx, body, extra_data, w, options)?; - 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<'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(), - }; + /// 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(), + }; - 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() - }; + 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() + }; - 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!(); - }; - 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 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!(); + }; + 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 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}", - ); + 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.push(&file_name); - file_path -} + 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> { - 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}"), - ) - })?; + /// 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> { + 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}")) + }) } - fs::File::create_buffered(&file_path).map_err(|e| { - io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}")) - }) -} /////////////////////////////////////////////////////////////////////////// // Whole MIR bodies @@ -361,29 +361,29 @@ pub fn write_mir_pretty<'tcx>( Ok(()) } -/// Write out a human-readable textual representation for the given function. -pub fn write_mir_fn<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, - w: &mut dyn io::Write, - options: PrettyPrintMirOptions, -) -> 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)?; + /// Write out a human-readable textual representation for the given function. + pub fn write_mir_fn<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, + w: &mut dyn io::Write, + options: PrettyPrintMirOptions, + ) -> 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)?; + } } - } - writeln!(w, "}}")?; + writeln!(w, "}}")?; - write_allocations(tcx, body, w)?; + write_allocations(tcx, body, w)?; - Ok(()) -} + Ok(()) + } /// Prints local variables in a scope tree. fn write_scope_tree( @@ -695,89 +695,89 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { /////////////////////////////////////////////////////////////////////////// // Basic blocks and their parts (statements, terminators, ...) -/// Write out a human-readable textual representation for the given basic block. -fn write_basic_block<'tcx>( - tcx: TyCtxt<'tcx>, - block: BasicBlock, - body: &Body<'tcx>, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, - w: &mut dyn io::Write, - options: PrettyPrintMirOptions, -) -> 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}: {{")?; + /// Write out a human-readable textual representation for the given basic block. + fn write_basic_block<'tcx>( + tcx: TyCtxt<'tcx>, + block: BasicBlock, + body: &Body<'tcx>, + extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, + w: &mut dyn io::Write, + options: PrettyPrintMirOptions, + ) -> 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}: {{")?; + + // 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!( + w, + "{:A$} // {}{}", + indented_body, + if tcx.sess.verbose_internals() { + format!("{current_location:?}: ") + } else { + String::new() + }, + comment(tcx, statement.source_info), + A = ALIGN, + )?; + } else { + writeln!(w, "{indented_body}")?; + } - // 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!( + write_extra( + 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), + options, )?; - } else { - writeln!(w, "{indented_body}")?; - } - write_extra( - tcx, - w, - &|visitor| visitor.visit_statement(statement, current_location), - options, - )?; + extra_data(PassWhere::AfterLocation(current_location), w)?; - extra_data(PassWhere::AfterLocation(current_location), w)?; + current_location.statement_index += 1; + } - 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!( + w, + "{:A$} // {}{}", + indented_terminator, + if tcx.sess.verbose_internals() { + format!("{current_location:?}: ") + } else { + String::new() + }, + comment(tcx, data.terminator().source_info), + A = ALIGN, + )?; + } else { + writeln!(w, "{indented_terminator}")?; + } - // 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( + 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), + options, )?; - } else { - writeln!(w, "{indented_terminator}")?; } - write_extra( - tcx, - w, - &|visitor| visitor.visit_terminator(data.terminator(), current_location), - options, - )?; - } - - extra_data(PassWhere::AfterLocation(current_location), w)?; - extra_data(PassWhere::AfterTerminator(block), w)?; + extra_data(PassWhere::AfterLocation(current_location), w)?; + extra_data(PassWhere::AfterTerminator(block), w)?; - writeln!(w, "{INDENT}}}") -} + writeln!(w, "{INDENT}}}") + } impl Debug for Statement<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { From ac16ad5ec9270aceed32ff542933a712bf4cf5b6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 15 Aug 2025 14:38:17 +1000 Subject: [PATCH 07/14] Introduce `MirDumper` and `MirWriter`. MIR dumping is a mess. There are lot of functions an entry points, e.g. `dump_mir`, `dump_mir_with_options`, `dump_polonius_mir`, `dump_mir_to_writer`. Also, it's crucial that `create_dump_file` is never called without `dump_enabled` first being checked, but there is no mechanism for ensuring this and it's hard to tell if it is satisfied on all paths. (`dump_enabled` is checked twice on some paths, however!) This commit introduces `MirWriter`, which controls the MIR writing, and encapsulates the `extra_data` closure and `options`. Two existing functions are now methods of this type. It sets reasonable defaults, allowing the removal of many `|_, _| Ok(())` closures. The commit also introduces `MirDumper`, which is layered on top of `MirWriter`, and which manages the creation of the dump files, encapsulating pass names, disambiguators, etc. Four existing functions are now methods of this type. - `MirDumper::new` will only succeed if dumps are enabled, and will return `None` otherwise, which makes it impossible to dump when you shouldn't. - It also sets reasonable defaults for various things like disambiguators, which means you no longer need to specify them in many cases. When they do need to be specified, it's now done via setter methods. - It avoids some repetition. E.g. `dump_nll_mir` previously specifed the pass name `"nll"` four times and the disambiguator `&0` three times; now it specifies them just once, to put them in the `MirDumper`. - For Polonius, the `extra_data` closure can now be specified earlier, which avoids having to pass some arguments through some functions. --- compiler/rustc_borrowck/src/nll.rs | 35 +-- compiler/rustc_borrowck/src/polonius/dump.rs | 87 ++---- compiler/rustc_codegen_cranelift/src/base.rs | 5 +- compiler/rustc_middle/src/mir/mod.rs | 4 +- compiler/rustc_middle/src/mir/pretty.rs | 282 +++++++++--------- compiler/rustc_mir_build/src/builder/mod.rs | 12 +- .../src/framework/graphviz.rs | 13 +- compiler/rustc_mir_transform/src/coroutine.rs | 21 +- .../src/coroutine/by_move_body.rs | 7 +- .../rustc_mir_transform/src/coroutine/drop.rs | 12 +- compiler/rustc_mir_transform/src/dest_prop.rs | 20 +- .../src/lint_tail_expr_drop_order.rs | 9 +- .../rustc_mir_transform/src/pass_manager.rs | 24 +- compiler/rustc_mir_transform/src/shim.rs | 14 +- 14 files changed, 273 insertions(+), 272 deletions(-) diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 021dba9c9a922..eb0f56bb869a3 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -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)?; }; } diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 173fb5969769d..62f9ae173474d 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -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 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 = create_dump_file(tcx, "html", false, "polonius", &0, body)?; + let mut file = dumper.create_dump_file("html", body)?; emit_polonius_dump( - tcx, + &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>, out: &mut dyn io::Write, ) -> io::Result<()> { // Prepare the HTML dump file prologue. @@ -79,15 +95,7 @@ fn emit_polonius_dump<'tcx>( writeln!(out, "
")?; writeln!(out, "Raw MIR dump")?; writeln!(out, "
")?;
-    emit_html_mir(
-        tcx,
-        body,
-        regioncx,
-        borrow_set,
-        &localized_outlives_constraints,
-        closure_region_requirements,
-        out,
-    )?;
+    emit_html_mir(dumper, body, out)?;
     writeln!(out, "
")?; writeln!(out, "
")?; @@ -116,7 +124,7 @@ fn emit_polonius_dump<'tcx>( writeln!(out, "
")?; writeln!(out, "NLL regions")?; writeln!(out, "
")?;
-    emit_mermaid_nll_regions(tcx, regioncx, out)?;
+    emit_mermaid_nll_regions(dumper.tcx(), regioncx, out)?;
     writeln!(out, "
")?; writeln!(out, "
")?; @@ -124,7 +132,7 @@ fn emit_polonius_dump<'tcx>( writeln!(out, "
")?; writeln!(out, "NLL SCCs")?; writeln!(out, "
")?;
-    emit_mermaid_nll_sccs(tcx, regioncx, out)?;
+    emit_mermaid_nll_sccs(dumper.tcx(), regioncx, out)?;
     writeln!(out, "
")?; writeln!(out, "
")?; @@ -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>, 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. diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index aefb9c9a11564..3a28dd7e73cb3 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -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, &|_, _| 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() }); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index c55c7fc6002c7..9a271abc40253 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -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>; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 85ac793b016d1..37fb50fd55906 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -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,6 +58,79 @@ impl PrettyPrintMirOptions { } } +/// 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>, +} + +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 { + 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), + }) + } + + pub fn tcx(&self) -> TyCtxt<'tcx> { + self.writer.tcx + } + + #[must_use] + pub fn set_show_pass_num(mut self) -> Self { + self.show_pass_num = true; + self + } + + #[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: /// @@ -82,125 +155,49 @@ impl PrettyPrintMirOptions { /// 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>( - tcx: TyCtxt<'tcx>, - pass_num: bool, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, - extra_data: &dyn Fn(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>( - tcx: TyCtxt<'tcx>, - pass_num: bool, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, - options: PrettyPrintMirOptions, - ) { - if !dump_enabled(tcx, pass_name, body.source.def_id()) { - return; - } - + pub fn dump_mir(&self, body: &Body<'tcx>) { 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)?; + let mut file = self.create_dump_file("mir", body)?; + self.dump_mir_to_writer(body, &mut file)?; }; - if tcx.sess.opts.unstable_opts.dump_mir_graphviz { + if self.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)?; + let mut file = self.create_dump_file("dot", body)?; + write_mir_fn_graphviz(self.tcx(), body, false, &mut file)?; }; } } - 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>( - tcx: TyCtxt<'tcx>, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, - w: &mut dyn io::Write, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, - options: PrettyPrintMirOptions, - ) -> io::Result<()> { + // #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!(tcx.def_path_str(body.source.def_id())); + 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, " {disambiguator} {pass_name}")?; + writeln!(w, " {} {}", self.disambiguator, self.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, extra_data, w, options)?; - extra_data(PassWhere::AfterCFG, 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<'tcx>( - tcx: TyCtxt<'tcx>, - extension: &str, - pass_num: bool, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, - ) -> PathBuf { + 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:?}"), @@ -209,7 +206,7 @@ impl PrettyPrintMirOptions { let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number { String::new() - } else if pass_num { + } else if self.show_pass_num { let (dialect_index, phase_index) = body.phase.index(); format!(".{}-{}-{:03}", dialect_index, phase_index, body.pass_count) } else { @@ -222,8 +219,8 @@ impl PrettyPrintMirOptions { // 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. + // 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, @@ -275,6 +272,8 @@ impl PrettyPrintMirOptions { 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}", ); @@ -288,15 +287,12 @@ impl PrettyPrintMirOptions { /// 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>, + pub fn create_dump_file( + &self, extension: &str, - pass_num: bool, - pass_name: &str, - disambiguator: &dyn Display, body: &Body<'tcx>, ) -> io::Result> { - let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, body); + 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( @@ -309,6 +305,7 @@ impl PrettyPrintMirOptions { io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}")) }) } +} /////////////////////////////////////////////////////////////////////////// // Whole MIR bodies @@ -320,7 +317,7 @@ pub fn write_mir_pretty<'tcx>( single: Option, 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.")?; @@ -336,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, &|_, _| Ok(()), w, options)?; + writer.write_mir_fn(body, w)?; for body in tcx.promoted_mir(def_id) { writeln!(w)?; - write_mir_fn(tcx, body, &|_, _| Ok(()), w, options)?; + writer.write_mir_fn(body, w)?; } Ok(()) }; @@ -352,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), &|_, _| 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)?; @@ -361,18 +358,24 @@ pub fn write_mir_pretty<'tcx>( Ok(()) } +/// Does the writing of MIR to output, e.g. a file. +pub struct MirWriter<'de, 'tcx> { + tcx: TyCtxt<'tcx>, + extra_data: &'de dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, + options: PrettyPrintMirOptions, +} + +impl<'de, 'tcx> MirWriter<'de, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>) -> Self { + MirWriter { tcx, extra_data: &|_, _| Ok(()), options: PrettyPrintMirOptions::from_cli(tcx) } + } + /// Write out a human-readable textual representation for the given function. - pub fn write_mir_fn<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, - w: &mut dyn io::Write, - options: PrettyPrintMirOptions, - ) -> io::Result<()> { - write_mir_intro(tcx, body, w, options)?; + 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() { - extra_data(PassWhere::BeforeBlock(block), w)?; - write_basic_block(tcx, block, body, extra_data, w, options)?; + (self.extra_data)(PassWhere::BeforeBlock(block), w)?; + self.write_basic_block(block, body, w)?; if block.index() + 1 != body.basic_blocks.len() { writeln!(w)?; } @@ -380,10 +383,11 @@ pub fn write_mir_pretty<'tcx>( writeln!(w, "}}")?; - write_allocations(tcx, body, w)?; + write_allocations(self.tcx, body, w)?; Ok(()) } +} /// Prints local variables in a scope tree. fn write_scope_tree( @@ -695,14 +699,13 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { /////////////////////////////////////////////////////////////////////////// // Basic blocks and their parts (statements, terminators, ...) +impl<'de, 'tcx> MirWriter<'de, 'tcx> { /// Write out a human-readable textual representation for the given basic block. - fn write_basic_block<'tcx>( - tcx: TyCtxt<'tcx>, + fn write_basic_block( + &self, block: BasicBlock, body: &Body<'tcx>, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, w: &mut dyn io::Write, - options: PrettyPrintMirOptions, ) -> io::Result<()> { let data = &body[block]; @@ -713,19 +716,19 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { // 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)?; + (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?; let indented_body = format!("{INDENT}{INDENT}{statement:?};"); - if options.include_extra_comments { + if self.options.include_extra_comments { writeln!( w, "{:A$} // {}{}", indented_body, - if tcx.sess.verbose_internals() { + if self.tcx.sess.verbose_internals() { format!("{current_location:?}: ") } else { String::new() }, - comment(tcx, statement.source_info), + comment(self.tcx, statement.source_info), A = ALIGN, )?; } else { @@ -733,32 +736,32 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { } write_extra( - tcx, + self.tcx, w, &|visitor| visitor.visit_statement(statement, current_location), - options, + self.options, )?; - extra_data(PassWhere::AfterLocation(current_location), w)?; + (self.extra_data)(PassWhere::AfterLocation(current_location), w)?; current_location.statement_index += 1; } // Terminator at the bottom. - extra_data(PassWhere::BeforeLocation(current_location), w)?; + (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 options.include_extra_comments { + if self.options.include_extra_comments { writeln!( w, "{:A$} // {}{}", indented_terminator, - if tcx.sess.verbose_internals() { + if self.tcx.sess.verbose_internals() { format!("{current_location:?}: ") } else { String::new() }, - comment(tcx, data.terminator().source_info), + comment(self.tcx, data.terminator().source_info), A = ALIGN, )?; } else { @@ -766,18 +769,19 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { } write_extra( - tcx, + self.tcx, w, &|visitor| visitor.visit_terminator(data.terminator(), current_location), - options, + self.options, )?; } - extra_data(PassWhere::AfterLocation(current_location), w)?; - extra_data(PassWhere::AfterTerminator(block), w)?; + (self.extra_data)(PassWhere::AfterLocation(current_location), w)?; + (self.extra_data)(PassWhere::AfterTerminator(block), w)?; writeln!(w, "{INDENT}}}") } +} impl Debug for Statement<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 3d7b9feefabc1..f4959bbecc2ea 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -804,18 +804,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, - &|_, _| 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); } } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index f86a15a8f9215..a2a3aaa388318 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -11,8 +11,7 @@ use regex::Regex; use rustc_hir::def_id::DefId; 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 { diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 592192944d2b8..4603c695dedd5 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -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); diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 5ba6fea9fafb0..951ff69c19e3e 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -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(); diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index 6dffbc866278d..fd2d8b2b0563e 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -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 } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index bb68d1a065957..cf7425251e8e0 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -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::>() }; - 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) + } } diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs index 0d0a71bc6c7e9..794984d2f3edf 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -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()); diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 77231f730d82f..cd176c498779b 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -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; @@ -264,7 +264,6 @@ fn run_passes_inner<'tcx>( } let prof_arg = tcx.sess.prof.enabled().then(|| format!("{:?}", body.source.def_id())); - let pass_num = true; if !body.should_skip() { let validate = validate_each & tcx.sess.opts.unstable_opts.validate_mir; @@ -277,10 +276,16 @@ fn run_passes_inner<'tcx>( 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 { - mir::dump_mir(tcx, pass_num, pass_name, &"before", body, &|_, _| Ok(())); + if let Some(dumper) = dumper.as_ref() { + dumper.dump_mir(body); } if let Some(prof_arg) = &prof_arg { @@ -292,9 +297,10 @@ fn run_passes_inner<'tcx>( pass.run_pass(tcx, body); } - if dump_enabled { - mir::dump_mir(tcx, pass_num, pass_name, &"after", body, &|_, _| Ok(())); + if let Some(dumper) = dumper { + dumper.set_disambiguator(&"after").dump_mir(body); } + if validate { validate_body(tcx, body, format!("after pass {pass_name}")); } @@ -337,5 +343,7 @@ pub(super) fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when 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) + } } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index a78b32ea4c976..0049aff7f0a15 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -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 } From b951b5dca116803b89380aae55ce9053a4674f31 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Fri, 15 Aug 2025 16:56:11 +0000 Subject: [PATCH 08/14] stabilize strict provenance atomic ptr --- library/core/src/sync/atomic.rs | 21 +++++++-------------- library/coretests/tests/lib.rs | 1 - library/std/src/lib.rs | 1 - src/tools/miri/tests/pass/atomic.rs | 1 - tests/codegen-llvm/atomicptr.rs | 1 - 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 44a6895f90ac6..7bd68bcd0bc5d 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -2199,7 +2199,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let atom = AtomicPtr::::new(core::ptr::null_mut()); @@ -2209,7 +2208,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_ptr_add(&self, val: usize, order: Ordering) -> *mut T { self.fetch_byte_add(val.wrapping_mul(size_of::()), order) @@ -2240,7 +2239,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let array = [1i32, 2i32]; @@ -2254,7 +2252,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_ptr_sub(&self, val: usize, order: Ordering) -> *mut T { self.fetch_byte_sub(val.wrapping_mul(size_of::()), order) @@ -2279,7 +2277,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let atom = AtomicPtr::::new(core::ptr::null_mut()); @@ -2289,7 +2286,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_byte_add(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2315,7 +2312,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let mut arr = [0i64, 1]; @@ -2325,7 +2321,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_byte_sub(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2361,7 +2357,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let pointer = &mut 3i64 as *mut i64; @@ -2376,7 +2371,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_or(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2412,7 +2407,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let pointer = &mut 3i64 as *mut i64; @@ -2426,7 +2420,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_and(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2462,7 +2456,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let pointer = &mut 3i64 as *mut i64; @@ -2474,7 +2467,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_xor(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 0c4d49f3c994b..92774e1268108 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -95,7 +95,6 @@ #![feature(std_internals)] #![feature(step_trait)] #![feature(str_internals)] -#![feature(strict_provenance_atomic_ptr)] #![feature(strict_provenance_lints)] #![feature(test)] #![feature(trusted_len)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f111fcb4a4712..77c7878ce0896 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -370,7 +370,6 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] -#![feature(strict_provenance_atomic_ptr)] #![feature(sync_unsafe_cell)] #![feature(temporary_niche_types)] #![feature(ub_checks)] diff --git a/src/tools/miri/tests/pass/atomic.rs b/src/tools/miri/tests/pass/atomic.rs index 3de34e570c7ee..d8ac5114f27fa 100644 --- a/src/tools/miri/tests/pass/atomic.rs +++ b/src/tools/miri/tests/pass/atomic.rs @@ -2,7 +2,6 @@ //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-strict-provenance -#![feature(strict_provenance_atomic_ptr)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/tests/codegen-llvm/atomicptr.rs b/tests/codegen-llvm/atomicptr.rs index ce6c4aa0d2b8e..9d5e618fe76f2 100644 --- a/tests/codegen-llvm/atomicptr.rs +++ b/tests/codegen-llvm/atomicptr.rs @@ -6,7 +6,6 @@ //@ compile-flags: -Copt-level=3 -Cno-prepopulate-passes #![crate_type = "lib"] -#![feature(strict_provenance_atomic_ptr)] use std::ptr::without_provenance_mut; use std::sync::atomic::AtomicPtr; From e42c1b12961385d6a5e9e442344fcaef8678ab58 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:58:36 -0400 Subject: [PATCH 09/14] Stabilize `round_char_boundary` feature --- compiler/rustc_middle/src/lib.rs | 2 +- compiler/rustc_span/src/lib.rs | 2 +- library/alloctests/tests/lib.rs | 1 - library/core/src/str/mod.rs | 8 +++--- library/std/src/lib.rs | 1 - src/librustdoc/lib.rs | 2 +- src/tools/clippy/clippy_lints/src/lib.rs | 2 +- .../ui/char_indices_as_byte_indices.fixed | 1 - .../tests/ui/char_indices_as_byte_indices.rs | 1 - .../ui/char_indices_as_byte_indices.stderr | 28 +++++++++---------- 10 files changed, 22 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index e5cc23c213d22..5d0723a0090f7 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -29,6 +29,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::direct_use_of_rustc_type_ir)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(round_char_boundary))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(allocator_api)] @@ -51,7 +52,6 @@ #![feature(negative_impls)] #![feature(never_type)] #![feature(ptr_alignment_type)] -#![feature(round_char_boundary)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![feature(sized_hierarchy)] diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index d647ec28aae5c..ae6755f076424 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -17,6 +17,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(round_char_boundary))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -26,7 +27,6 @@ #![feature(map_try_insert)] #![feature(negative_impls)] #![feature(read_buf)] -#![feature(round_char_boundary)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index fcfc7f8dd296d..cb9479abee375 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -23,7 +23,6 @@ #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] -#![feature(round_char_boundary)] #![feature(slice_partition_dedup)] #![feature(string_from_utf8_lossy_owned)] #![feature(string_remove_matches)] diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 1b6e84175b934..89115b254b104 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -396,7 +396,6 @@ impl str { /// # Examples /// /// ``` - /// #![feature(round_char_boundary)] /// let s = "โค๏ธ๐Ÿงก๐Ÿ’›๐Ÿ’š๐Ÿ’™๐Ÿ’œ"; /// assert_eq!(s.len(), 26); /// assert!(!s.is_char_boundary(13)); @@ -405,7 +404,8 @@ impl str { /// assert_eq!(closest, 10); /// assert_eq!(&s[..closest], "โค๏ธ๐Ÿงก"); /// ``` - #[unstable(feature = "round_char_boundary", issue = "93743")] + #[stable(feature = "round_char_boundary", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "round_char_boundary", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn floor_char_boundary(&self, index: usize) -> usize { if index >= self.len() { @@ -439,7 +439,6 @@ impl str { /// # Examples /// /// ``` - /// #![feature(round_char_boundary)] /// let s = "โค๏ธ๐Ÿงก๐Ÿ’›๐Ÿ’š๐Ÿ’™๐Ÿ’œ"; /// assert_eq!(s.len(), 26); /// assert!(!s.is_char_boundary(13)); @@ -448,7 +447,8 @@ impl str { /// assert_eq!(closest, 14); /// assert_eq!(&s[..closest], "โค๏ธ๐Ÿงก๐Ÿ’›"); /// ``` - #[unstable(feature = "round_char_boundary", issue = "93743")] + #[stable(feature = "round_char_boundary", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "round_char_boundary", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn ceil_char_boundary(&self, index: usize) -> usize { if index >= self.len() { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ab417b6c72f9b..74ac1230ff5e4 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -263,7 +263,6 @@ all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) )] -#![cfg_attr(any(windows, target_os = "uefi"), feature(round_char_boundary))] #![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] // diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 28dbd8ba7d3ac..a59272d49c740 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -1,4 +1,5 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(round_char_boundary))] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/" @@ -12,7 +13,6 @@ #![feature(format_args_nl)] #![feature(if_let_guard)] #![feature(iter_intersperse)] -#![feature(round_char_boundary)] #![feature(rustc_private)] #![feature(test)] #![warn(rustc::internal)] diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 844bc1b0e3909..edc91718263de 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -7,7 +7,7 @@ #![feature(iter_intersperse)] #![feature(iter_partition_in_place)] #![feature(never_type)] -#![feature(round_char_boundary)] +#![cfg_attr(bootstrap, feature(round_char_boundary))] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] #![feature(unwrap_infallible)] diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed index 04c8f6782c51e..375a101c2e3ac 100644 --- a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed +++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed @@ -1,4 +1,3 @@ -#![feature(round_char_boundary)] #![warn(clippy::char_indices_as_byte_indices)] trait StrExt { diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs index 773a4fc65f12f..eebc39962a2db 100644 --- a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs +++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs @@ -1,4 +1,3 @@ -#![feature(round_char_boundary)] #![warn(clippy::char_indices_as_byte_indices)] trait StrExt { diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr index e2b4c1db78cf4..fae81fd772db8 100644 --- a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr +++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr @@ -1,12 +1,12 @@ error: indexing into a string with a character position where a byte index is expected - --> tests/ui/char_indices_as_byte_indices.rs:13:24 + --> tests/ui/char_indices_as_byte_indices.rs:12:24 | LL | let _ = prim[..idx]; | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/char_indices_as_byte_indices.rs:12:10 + --> tests/ui/char_indices_as_byte_indices.rs:11:10 | LL | for (idx, _) in prim.chars().enumerate() { | ^^^ ^^^^^^^^^^^ @@ -19,14 +19,14 @@ LL + for (idx, _) in prim.char_indices() { | error: passing a character position to a method that expects a byte index - --> tests/ui/char_indices_as_byte_indices.rs:15:23 + --> tests/ui/char_indices_as_byte_indices.rs:14:23 | LL | prim.split_at(idx); | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/char_indices_as_byte_indices.rs:12:10 + --> tests/ui/char_indices_as_byte_indices.rs:11:10 | LL | for (idx, _) in prim.chars().enumerate() { | ^^^ ^^^^^^^^^^^ @@ -37,14 +37,14 @@ LL + for (idx, _) in prim.char_indices() { | error: passing a character position to a method that expects a byte index - --> tests/ui/char_indices_as_byte_indices.rs:19:49 + --> tests/ui/char_indices_as_byte_indices.rs:18:49 | LL | let _ = prim[..prim.floor_char_boundary(idx)]; | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/char_indices_as_byte_indices.rs:12:10 + --> tests/ui/char_indices_as_byte_indices.rs:11:10 | LL | for (idx, _) in prim.chars().enumerate() { | ^^^ ^^^^^^^^^^^ @@ -55,14 +55,14 @@ LL + for (idx, _) in prim.char_indices() { | error: indexing into a string with a character position where a byte index is expected - --> tests/ui/char_indices_as_byte_indices.rs:29:24 + --> tests/ui/char_indices_as_byte_indices.rs:28:24 | LL | let _ = prim[..c.0]; | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/char_indices_as_byte_indices.rs:28:9 + --> tests/ui/char_indices_as_byte_indices.rs:27:9 | LL | for c in prim.chars().enumerate() { | ^ ^^^^^^^^^^^ @@ -73,14 +73,14 @@ LL + for c in prim.char_indices() { | error: passing a character position to a method that expects a byte index - --> tests/ui/char_indices_as_byte_indices.rs:31:23 + --> tests/ui/char_indices_as_byte_indices.rs:30:23 | LL | prim.split_at(c.0); | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/char_indices_as_byte_indices.rs:28:9 + --> tests/ui/char_indices_as_byte_indices.rs:27:9 | LL | for c in prim.chars().enumerate() { | ^ ^^^^^^^^^^^ @@ -91,14 +91,14 @@ LL + for c in prim.char_indices() { | error: indexing into a string with a character position where a byte index is expected - --> tests/ui/char_indices_as_byte_indices.rs:36:26 + --> tests/ui/char_indices_as_byte_indices.rs:35:26 | LL | let _ = string[..idx]; | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/char_indices_as_byte_indices.rs:35:10 + --> tests/ui/char_indices_as_byte_indices.rs:34:10 | LL | for (idx, _) in string.chars().enumerate() { | ^^^ ^^^^^^^^^^^ @@ -109,14 +109,14 @@ LL + for (idx, _) in string.char_indices() { | error: passing a character position to a method that expects a byte index - --> tests/ui/char_indices_as_byte_indices.rs:38:25 + --> tests/ui/char_indices_as_byte_indices.rs:37:25 | LL | string.split_at(idx); | ^^^ | = note: a character can take up more than one byte, so they are not interchangeable note: position comes from the enumerate iterator - --> tests/ui/char_indices_as_byte_indices.rs:35:10 + --> tests/ui/char_indices_as_byte_indices.rs:34:10 | LL | for (idx, _) in string.chars().enumerate() { | ^^^ ^^^^^^^^^^^ From 4edfeb2f60e4c9f871117965070946ff566810d0 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Thu, 28 Aug 2025 20:11:07 +0200 Subject: [PATCH 10/14] compiler: Include span of too huge enum with -Cdebuginfo=2 We have a ui test to ensure we emit an error if we encounter too big enums. Before this fix, compiling the test with `-Cdebuginfo=2` would not include the span of the instantiation site, because the error is then emitted from a different code path that does not include the span. Propagate the span to the error also in the debuginfo case, so the test passes regardless of debuginfo level. --- .../rustc_codegen_llvm/src/debuginfo/metadata.rs | 14 ++++++++++++-- .../src/debuginfo/metadata/enums/mod.rs | 5 +++-- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 6 ++++-- ...enum.stderr => huge-enum.full-debuginfo.stderr} | 2 +- tests/ui/limits/huge-enum.no-debuginfo.stderr | 8 ++++++++ tests/ui/limits/huge-enum.rs | 8 +++++--- 6 files changed, 33 insertions(+), 10 deletions(-) rename tests/ui/limits/{huge-enum.stderr => huge-enum.full-debuginfo.stderr} (85%) create mode 100644 tests/ui/limits/huge-enum.no-debuginfo.stderr diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 0e9dbfba658d2..caa3369f413a8 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -19,7 +19,9 @@ use rustc_middle::ty::{ self, AdtKind, CoroutineArgsExt, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility, }; use rustc_session::config::{self, DebugInfo, Lto}; -use rustc_span::{DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, Symbol, hygiene}; +use rustc_span::{ + DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, Span, Symbol, hygiene, +}; use rustc_symbol_mangling::typeid_for_trait_ref; use rustc_target::spec::DebuginfoKind; use smallvec::smallvec; @@ -423,6 +425,14 @@ fn build_slice_type_di_node<'ll, 'tcx>( /// This function will look up the debuginfo node in the TypeMap. If it can't find it, it /// will create the node by dispatching to the corresponding `build_*_di_node()` function. pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { + spanned_type_di_node(cx, t, DUMMY_SP) +} + +pub(crate) fn spanned_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + t: Ty<'tcx>, + span: Span, +) -> &'ll DIType { let unique_type_id = UniqueTypeId::for_ty(cx.tcx, t); if let Some(existing_di_node) = debug_context(cx).type_map.di_node_for_unique_id(unique_type_id) @@ -460,7 +470,7 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> ty::Adt(def, ..) => match def.adt_kind() { AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id), AdtKind::Union => build_union_type_di_node(cx, unique_type_id), - AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id), + AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span), }, ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), _ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index 7c701926d2c5e..caff358607974 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -10,7 +10,7 @@ use rustc_middle::bug; use rustc_middle::mir::CoroutineLayout; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty, VariantDef}; -use rustc_span::Symbol; +use rustc_span::{Span, Symbol}; use super::type_map::{DINodeCreationResult, UniqueTypeId}; use super::{SmallVec, size_and_align_of}; @@ -30,13 +30,14 @@ mod native; pub(super) fn build_enum_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, + span: Span, ) -> DINodeCreationResult<'ll> { let enum_type = unique_type_id.expect_ty(); let &ty::Adt(enum_adt_def, _) = enum_type.kind() else { bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type) }; - let enum_type_and_layout = cx.layout_of(enum_type); + let enum_type_and_layout = cx.spanned_layout_of(enum_type, span); if wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout) { return build_c_style_enum_di_node(cx, enum_adt_def, enum_type_and_layout); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 2c3a84499ac55..79334f7f9fe84 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -28,7 +28,9 @@ use rustc_target::spec::DebuginfoKind; use smallvec::SmallVec; use tracing::debug; -use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER, file_metadata, type_di_node}; +use self::metadata::{ + UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER, file_metadata, spanned_type_di_node, type_di_node, +}; use self::namespace::mangled_name_of_instance; use self::utils::{DIB, create_DIArray, is_node_local_to_unit}; use crate::builder::Builder; @@ -626,7 +628,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let loc = self.lookup_debug_loc(span.lo()); let file_metadata = file_metadata(self, &loc.file); - let type_metadata = type_di_node(self, variable_type); + let type_metadata = spanned_type_di_node(self, variable_type, span); let (argument_index, dwarf_tag) = match variable_kind { ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), diff --git a/tests/ui/limits/huge-enum.stderr b/tests/ui/limits/huge-enum.full-debuginfo.stderr similarity index 85% rename from tests/ui/limits/huge-enum.stderr rename to tests/ui/limits/huge-enum.full-debuginfo.stderr index 5b97a2104a3c6..4b179d59e5f0e 100644 --- a/tests/ui/limits/huge-enum.stderr +++ b/tests/ui/limits/huge-enum.full-debuginfo.stderr @@ -1,5 +1,5 @@ error: values of the type `Option` are too big for the target architecture - --> $DIR/huge-enum.rs:15:9 + --> $DIR/huge-enum.rs:17:9 | LL | let big: BIG = None; | ^^^ diff --git a/tests/ui/limits/huge-enum.no-debuginfo.stderr b/tests/ui/limits/huge-enum.no-debuginfo.stderr new file mode 100644 index 0000000000000..4b179d59e5f0e --- /dev/null +++ b/tests/ui/limits/huge-enum.no-debuginfo.stderr @@ -0,0 +1,8 @@ +error: values of the type `Option` are too big for the target architecture + --> $DIR/huge-enum.rs:17:9 + | +LL | let big: BIG = None; + | ^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/limits/huge-enum.rs b/tests/ui/limits/huge-enum.rs index 6d5b86411f3a7..9395bee976570 100644 --- a/tests/ui/limits/huge-enum.rs +++ b/tests/ui/limits/huge-enum.rs @@ -1,9 +1,11 @@ +// FIXME(#61117): Remove revisions once x86_64-gnu-debug CI job sets rust.debuginfo-level-tests=2 +// NOTE: The .stderr for both revisions shall be identical. +//@ revisions: no-debuginfo full-debuginfo //@ build-fail //@ normalize-stderr: "std::option::Option<\[u32; \d+\]>" -> "TYPE" //@ normalize-stderr: "\[u32; \d+\]" -> "TYPE" - -// FIXME(#61117): Respect debuginfo-level-tests, do not force debuginfo-level=0 -//@ compile-flags: -Cdebuginfo=0 +//@[no-debuginfo] compile-flags: -Cdebuginfo=0 +//@[full-debuginfo] compile-flags: -Cdebuginfo=2 #[cfg(target_pointer_width = "32")] type BIG = Option<[u32; (1<<29)-1]>; From 2d0668d37cd5d10504bdd62bcedef457b8fbad40 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Thu, 28 Aug 2025 23:03:25 +0000 Subject: [PATCH 11/14] Mark pipe2 supported in Android Android has supported pipe2 since 2010, long before the current min SDK. --- library/std/src/sys/pal/unix/pipe.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs index 6b0cd14da4f61..4798acf9dad6b 100644 --- a/library/std/src/sys/pal/unix/pipe.rs +++ b/library/std/src/sys/pal/unix/pipe.rs @@ -20,6 +20,7 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { // and musl 0.9.3, and some other targets also have it. cfg_select! { any( + target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", From e3f1e94be775db497c15e2119d09b62bb834c91f Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Fri, 29 Aug 2025 12:13:30 +0300 Subject: [PATCH 12/14] std: haiku: fix `B_FIND_PATH_IMAGE_PATH` --- library/std/src/sys/pal/unix/os.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 81275afa70777..d70f1aab77ea1 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -469,7 +469,7 @@ pub fn current_exe() -> io::Result { unsafe { let result = libc::find_path( crate::ptr::null_mut(), - libc::path_base_directory::B_FIND_PATH_IMAGE_PATH, + libc::B_FIND_PATH_IMAGE_PATH, crate::ptr::null_mut(), name.as_mut_ptr(), name.len(), From 85cefabfcd8caa1ab95926537252164a6d63351d Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 11 Aug 2025 12:06:38 +0200 Subject: [PATCH 13/14] std: use a TAIT to define `SplitPaths` on UNIX --- library/std/src/sys/pal/unix/os.rs | 36 ++++++------------------------ 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 81275afa70777..0b1abe88275dc 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -16,10 +16,7 @@ use crate::{fmt, io, iter, mem, ptr, slice, str}; const TMPBUF_SZ: usize = 128; -const PATH_SEPARATOR: u8 = cfg_select! { - target_os = "redox" => b';', - _ => b':', -}; +const PATH_SEPARATOR: u8 = if cfg!(target_os = "redox") { b';' } else { b':' }; unsafe extern "C" { #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] @@ -189,33 +186,14 @@ pub fn chdir(p: &path::Path) -> io::Result<()> { if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) } } -pub struct SplitPaths<'a> { - iter: iter::Map bool>, fn(&'a [u8]) -> PathBuf>, -} +pub type SplitPaths<'a> = impl Iterator; +#[define_opaque(SplitPaths)] pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { - fn bytes_to_path(b: &[u8]) -> PathBuf { - PathBuf::from(::from_bytes(b)) - } - fn is_separator(b: &u8) -> bool { - *b == PATH_SEPARATOR - } - let unparsed = unparsed.as_bytes(); - SplitPaths { - iter: unparsed - .split(is_separator as fn(&u8) -> bool) - .map(bytes_to_path as fn(&[u8]) -> PathBuf), - } -} - -impl<'a> Iterator for SplitPaths<'a> { - type Item = PathBuf; - fn next(&mut self) -> Option { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } + unparsed + .as_bytes() + .split(|&b| b == PATH_SEPARATOR) + .map(|part| PathBuf::from(OsStr::from_bytes(part))) } #[derive(Debug)] From 638a52c789e92009cf26ed73a4a5bc597814fe7b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 29 Aug 2025 16:26:23 +0200 Subject: [PATCH 14/14] Improve librustdoc error when a file creation/modification failed --- src/librustdoc/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index e2682045ab432..058d9cc7c90cb 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -760,7 +760,9 @@ fn run_renderer< tcx.dcx().struct_fatal(format!("couldn't generate documentation: {}", e.error)); let file = e.file.display().to_string(); if !file.is_empty() { - msg.note(format!("failed to create or modify \"{file}\"")); + msg.note(format!("failed to create or modify {e}")); + } else { + msg.note(format!("failed to create or modify file: {e}")); } msg.emit(); }