about summary refs log tree commit diff
path: root/compiler/rustc_middle/src/mir/pretty.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_middle/src/mir/pretty.rs')
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs282
1 files changed, 143 insertions, 139 deletions
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 14f504bb2a9..a7d99f513a1 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<Self> {
+        let dump_enabled = if let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir {
+            // see notes on #41697 below
+            let node_path =
+                ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
+            filters.split('|').any(|or_filter| {
+                or_filter.split('&').all(|and_filter| {
+                    let and_filter_trimmed = and_filter.trim();
+                    and_filter_trimmed == "all"
+                        || pass_name.contains(and_filter_trimmed)
+                        || node_path.contains(and_filter_trimmed)
+                })
+            })
+        } else {
+            false
+        };
+
+        dump_enabled.then_some(MirDumper {
+            show_pass_num: false,
+            pass_name,
+            disambiguator: &0,
+            writer: MirWriter::new(tcx),
+        })
+    }
+
+    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<io::BufWriter<fs::File>> {
-        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<DefId>,
     w: &mut dyn io::Write,
 ) -> io::Result<()> {
-    let options = PrettyPrintMirOptions::from_cli(tcx);
+    let writer = MirWriter::new(tcx);
 
     writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
     writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
@@ -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<DefId>) -> Vec<DefId> {
 ///////////////////////////////////////////////////////////////////////////
 // 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<DefId>) -> Vec<DefId> {
         // 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<DefId>) -> Vec<DefId> {
             }
 
             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<DefId>) -> Vec<DefId> {
             }
 
             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 {