From 7aec84d485ec1159c3ead3800ba601fd6d1d7869 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 29 Aug 2025 17:03:38 +0300 Subject: [PATCH 01/15] resolve: Merge `ExternPreludeEntry::introduced_by_item` into `item_binding` --- .../rustc_resolve/src/build_reduced_graph.rs | 6 ++--- compiler/rustc_resolve/src/check_unused.rs | 2 +- compiler/rustc_resolve/src/diagnostics.rs | 5 ++-- compiler/rustc_resolve/src/lib.rs | 26 +++++++++++-------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index f75a625a279bb..feaaaa6f539df 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1008,16 +1008,14 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let msg = format!("extern crate `{ident}` already in extern prelude"); self.r.tcx.dcx().span_delayed_bug(item.span, msg); } else { - entry.item_binding = Some(imported_binding); - entry.introduced_by_item = orig_name.is_some(); + entry.item_binding = Some((imported_binding, orig_name.is_some())); } entry } Entry::Vacant(vacant) => vacant.insert(ExternPreludeEntry { - item_binding: Some(imported_binding), + item_binding: Some((imported_binding, true)), flag_binding: Cell::new(None), only_item: true, - introduced_by_item: true, }), }; } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 11d93a58ae296..50a1ad23a5462 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -204,7 +204,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { .r .extern_prelude .get(&Macros20NormalizedIdent::new(extern_crate.ident)) - .is_none_or(|entry| entry.introduced_by_item) + .is_none_or(|entry| entry.introduced_by_item()) { continue; } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 337e7d2dd8639..1ec527e3b81c8 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -322,7 +322,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let from_item = self .extern_prelude .get(&Macros20NormalizedIdent::new(ident)) - .is_none_or(|entry| entry.introduced_by_item); + .is_none_or(|entry| entry.introduced_by_item()); // Only suggest removing an import if both bindings are to the same def, if both spans // aren't dummy spans. Further, if both bindings are imports, then the ident must have // been introduced by an item. @@ -1845,7 +1845,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let AmbiguityError { kind, ident, b1, b2, misc1, misc2, .. } = *ambiguity_error; let extern_prelude_ambiguity = || { self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)).is_some_and(|entry| { - entry.item_binding == Some(b1) && entry.flag_binding.get() == Some(b2) + entry.item_binding.map(|(b, _)| b) == Some(b1) + && entry.flag_binding.get() == Some(b2) }) }; let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 2afb52ef4d4be..e3d896be0d20e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1028,15 +1028,20 @@ impl<'ra> NameBindingData<'ra> { #[derive(Default, Clone)] struct ExternPreludeEntry<'ra> { /// Binding from an `extern crate` item. - item_binding: Option>, + /// The boolean flag is true is `item_binding` is non-redundant, happens either when + /// `only_item` is true, or when `extern crate` introducing `item_binding` used renaming. + item_binding: Option<(NameBinding<'ra>, /* introduced by item */ bool)>, /// Binding from an `--extern` flag, lazily populated on first use. flag_binding: Cell>>, /// There was no `--extern` flag introducing this name, /// `flag_binding` doesn't need to be populated. only_item: bool, - /// `item_binding` is non-redundant, happens either when `only_item` is true, - /// or when `extern crate` introducing `item_binding` used renaming. - introduced_by_item: bool, +} + +impl ExternPreludeEntry<'_> { + fn introduced_by_item(&self) -> bool { + matches!(self.item_binding, Some((_, true))) + } } struct DeriveData { @@ -2062,12 +2067,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Avoid marking `extern crate` items that refer to a name from extern prelude, // but not introduce it, as used if they are accessed from lexical scope. - if used == Used::Scope { - if let Some(entry) = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)) { - if !entry.introduced_by_item && entry.item_binding == Some(used_binding) { - return; - } - } + if used == Used::Scope + && let Some(entry) = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)) + && entry.item_binding == Some((used_binding, false)) + { + return; } let old_used = self.import_use_map.entry(import).or_insert(used); if *old_used < used { @@ -2226,7 +2230,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { finalize: bool, ) -> Option> { let entry = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)); - entry.and_then(|entry| entry.item_binding).map(|binding| { + entry.and_then(|entry| entry.item_binding).map(|(binding, _)| { if finalize { self.get_mut().record_use(ident, binding, Used::Scope); } From f0dbfadaa2a5e6f6e839d49ed12951f0535ee03c Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 29 Aug 2025 17:36:10 +0300 Subject: [PATCH 02/15] resolve: Merge `ExternPreludeEntry::only_item` into `flag_binding` --- .../rustc_resolve/src/build_reduced_graph.rs | 3 +- compiler/rustc_resolve/src/diagnostics.rs | 2 +- compiler/rustc_resolve/src/lib.rs | 69 ++++++++++--------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index feaaaa6f539df..fa3c06059b3da 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1014,8 +1014,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } Entry::Vacant(vacant) => vacant.insert(ExternPreludeEntry { item_binding: Some((imported_binding, true)), - flag_binding: Cell::new(None), - only_item: true, + flag_binding: None, }), }; } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 1ec527e3b81c8..5dfc4292a3803 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1846,7 +1846,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let extern_prelude_ambiguity = || { self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)).is_some_and(|entry| { entry.item_binding.map(|(b, _)| b) == Some(b1) - && entry.flag_binding.get() == Some(b2) + && entry.flag_binding.as_ref().and_then(|pb| pb.get().binding()) == Some(b2) }) }; let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index e3d896be0d20e..9674c0356c21f 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -32,7 +32,7 @@ use std::sync::Arc; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use effective_visibilities::EffectiveVisibilitiesVisitor; use errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; -use imports::{Import, ImportData, ImportKind, NameResolution}; +use imports::{Import, ImportData, ImportKind, NameResolution, PendingBinding}; use late::{ ForwardGenericParamBanReason, HasGenericParams, PathSource, PatternSource, UnnecessaryQualification, @@ -1025,23 +1025,26 @@ impl<'ra> NameBindingData<'ra> { } } -#[derive(Default, Clone)] struct ExternPreludeEntry<'ra> { /// Binding from an `extern crate` item. /// The boolean flag is true is `item_binding` is non-redundant, happens either when - /// `only_item` is true, or when `extern crate` introducing `item_binding` used renaming. + /// `flag_binding` is `None`, or when `extern crate` introducing `item_binding` used renaming. item_binding: Option<(NameBinding<'ra>, /* introduced by item */ bool)>, /// Binding from an `--extern` flag, lazily populated on first use. - flag_binding: Cell>>, - /// There was no `--extern` flag introducing this name, - /// `flag_binding` doesn't need to be populated. - only_item: bool, + flag_binding: Option>>, } impl ExternPreludeEntry<'_> { fn introduced_by_item(&self) -> bool { matches!(self.item_binding, Some((_, true))) } + + fn flag() -> Self { + ExternPreludeEntry { + item_binding: None, + flag_binding: Some(Cell::new(PendingBinding::Pending)), + } + } } struct DeriveData { @@ -1533,7 +1536,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && let name = Symbol::intern(name) && name.can_be_raw() { - Some((Macros20NormalizedIdent::with_dummy_span(name), Default::default())) + let ident = Macros20NormalizedIdent::with_dummy_span(name); + Some((ident, ExternPreludeEntry::flag())) } else { None } @@ -1541,11 +1545,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .collect(); if !attr::contains_name(attrs, sym::no_core) { - extern_prelude - .insert(Macros20NormalizedIdent::with_dummy_span(sym::core), Default::default()); + let ident = Macros20NormalizedIdent::with_dummy_span(sym::core); + extern_prelude.insert(ident, ExternPreludeEntry::flag()); if !attr::contains_name(attrs, sym::no_std) { - extern_prelude - .insert(Macros20NormalizedIdent::with_dummy_span(sym::std), Default::default()); + let ident = Macros20NormalizedIdent::with_dummy_span(sym::std); + extern_prelude.insert(ident, ExternPreludeEntry::flag()); } } @@ -2240,31 +2244,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn extern_prelude_get_flag(&self, ident: Ident, finalize: bool) -> Option> { let entry = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)); - entry.and_then(|entry| match entry.flag_binding.get() { - Some(binding) => { - if finalize { - self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span); + entry.and_then(|entry| entry.flag_binding.as_ref()).and_then(|flag_binding| { + let binding = match flag_binding.get() { + PendingBinding::Ready(binding) => { + if finalize { + self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span); + } + binding } - Some(binding) - } - None if entry.only_item => None, - None => { - let crate_id = if finalize { - self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span) - } else { - self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name) - }; - match crate_id { - Some(crate_id) => { + PendingBinding::Pending => { + let crate_id = if finalize { + self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span) + } else { + self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name) + }; + crate_id.map(|crate_id| { let res = Res::Def(DefKind::Mod, crate_id.as_def_id()); - let binding = - self.arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT); - entry.flag_binding.set(Some(binding)); - Some(binding) - } - None => finalize.then_some(self.dummy_binding), + self.arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT) + }) } - } + }; + flag_binding.set(PendingBinding::Ready(binding)); + binding.or_else(|| finalize.then_some(self.dummy_binding)) }) } From 1112274275be9216b42ef2f27b1e85c5e88d67c5 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Thu, 28 Aug 2025 19:53:12 +0100 Subject: [PATCH 03/15] add `Bound::copied` Signed-off-by: Connor Tsui --- library/core/src/ops/range.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 95d1e2069ac94..c5a7744d73b39 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -736,6 +736,28 @@ impl Bound { } } +impl Bound<&T> { + /// Map a `Bound<&T>` to a `Bound` by copying the contents of the bound. + /// + /// # Examples + /// + /// ``` + /// use std::ops::Bound::*; + /// use std::ops::RangeBounds; + /// + /// assert_eq!((1..12).start_bound(), Included(&1)); + /// assert_eq!((1..12).start_bound().copied(), Included(1)); + /// ``` + #[unstable(feature = "bound_copied", issue = "145966")] + pub fn copied(self) -> Bound { + match self { + Bound::Unbounded => Bound::Unbounded, + Bound::Included(x) => Bound::Included(*x), + Bound::Excluded(x) => Bound::Excluded(*x), + } + } +} + impl Bound<&T> { /// Map a `Bound<&T>` to a `Bound` by cloning the contents of the bound. /// From 9c1255f0a46e7a8d22f17ad5705f0c8e2018bfe6 Mon Sep 17 00:00:00 2001 From: Connor Tsui <87130162+connortsui20@users.noreply.github.com> Date: Thu, 28 Aug 2025 20:38:07 +0100 Subject: [PATCH 04/15] add feature gate in doc test --- library/core/src/ops/range.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index c5a7744d73b39..b1df5afa20224 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -742,6 +742,8 @@ impl Bound<&T> { /// # Examples /// /// ``` + /// #![feature(bound_copied)] + /// /// use std::ops::Bound::*; /// use std::ops::RangeBounds; /// From 114c0c2fefbc90d1cb71f6a0c4d150174e73ca75 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Sat, 30 Aug 2025 11:15:41 +0100 Subject: [PATCH 05/15] Add `#[must_use] and update `cloned` documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Connor Tsui Co-authored-by: Jonas Böttiger --- library/core/src/ops/range.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index b1df5afa20224..c0a27775694c3 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -751,6 +751,7 @@ impl Bound<&T> { /// assert_eq!((1..12).start_bound().copied(), Included(1)); /// ``` #[unstable(feature = "bound_copied", issue = "145966")] + #[must_use] pub fn copied(self) -> Bound { match self { Bound::Unbounded => Bound::Unbounded, @@ -769,8 +770,11 @@ impl Bound<&T> { /// use std::ops::Bound::*; /// use std::ops::RangeBounds; /// - /// assert_eq!((1..12).start_bound(), Included(&1)); - /// assert_eq!((1..12).start_bound().cloned(), Included(1)); + /// let a1 = String::from("a"); + /// let (a2, a3, a4) = (a1.clone(), a1.clone(), a1.clone()); + /// + /// assert_eq!(Included(&a1), (a2..).start_bound()); + /// assert_eq!(Included(a3), (a4..).start_bound().cloned()); /// ``` #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "bound_cloned", since = "1.55.0")] From 18a36bccf5198dcc507d42e0daadcf2de4481741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 30 Aug 2025 23:28:46 +0000 Subject: [PATCH 06/15] Detect negative literal inferred to unsigned integer ``` error[E0277]: the trait bound `usize: Neg` is not satisfied --> $DIR/negative-literal-infered-to-unsigned.rs:2:14 | LL | for x in -5..5 { | ^^ the trait `Neg` is not implemented for `usize` | help: consider specifying an integer type that can be negative | LL | for x in -5isize..5 { | +++++ ``` --- .../traits/fulfillment_errors.rs | 41 ++++++++++++++++++- .../negative-literal-infered-to-unsigned.rs | 13 ++++++ ...egative-literal-infered-to-unsigned.stderr | 25 +++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 tests/ui/suggestions/negative-literal-infered-to-unsigned.rs create mode 100644 tests/ui/suggestions/negative-literal-infered-to-unsigned.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d768e0bf63fba..f1fd7ebfeba2f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -3,7 +3,8 @@ use std::borrow::Cow; use std::path::PathBuf; use rustc_abi::ExternAbi; -use rustc_ast::TraitObjectSyntax; +use rustc_ast::ast::LitKind; +use rustc_ast::{LitIntType, TraitObjectSyntax}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; @@ -280,6 +281,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (suggested, noted_missing_impl) = self.try_conversion_context(&obligation, main_trait_predicate, &mut err); } + suggested |= self.detect_negative_literal( + &obligation, + main_trait_predicate, + &mut err, + ); + if let Some(ret_span) = self.return_type_span(&obligation) { if is_try_conversion { let ty = self.tcx.short_string( @@ -950,6 +957,38 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Ok(()) } + fn detect_negative_literal( + &self, + obligation: &PredicateObligation<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + err: &mut Diag<'_>, + ) -> bool { + if let ObligationCauseCode::BinOp { lhs_hir_id, .. } = obligation.cause.code() + && let hir::Node::Expr(expr) = self.tcx.hir_node(*lhs_hir_id) + && let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = expr.kind + && let hir::ExprKind::Lit(lit) = inner.kind + && let LitKind::Int(_, LitIntType::Unsuffixed) = lit.node + { + err.span_suggestion_verbose( + lit.span.shrink_to_hi(), + "consider specifying an integer type that can be negative", + match trait_pred.skip_binder().self_ty().kind() { + ty::Uint(ty::UintTy::Usize) => "isize", + ty::Uint(ty::UintTy::U8) => "i8", + ty::Uint(ty::UintTy::U16) => "i16", + ty::Uint(ty::UintTy::U32) => "i32", + ty::Uint(ty::UintTy::U64) => "i64", + ty::Uint(ty::UintTy::U128) => "i128", + _ => "i64", + } + .to_string(), + Applicability::MaybeIncorrect, + ); + return true; + } + false + } + /// When the `E` of the resulting `Result` in an expression `foo().bar().baz()?`, /// identify those method chain sub-expressions that could or could not have been annotated /// with `?`. diff --git a/tests/ui/suggestions/negative-literal-infered-to-unsigned.rs b/tests/ui/suggestions/negative-literal-infered-to-unsigned.rs new file mode 100644 index 0000000000000..39797574b9777 --- /dev/null +++ b/tests/ui/suggestions/negative-literal-infered-to-unsigned.rs @@ -0,0 +1,13 @@ +fn main() { + for x in -5..5 { + //~^ ERROR: the trait bound `usize: Neg` is not satisfied + //~| HELP: consider specifying an integer type that can be negative + do_something(x); + } + let x = -5; + //~^ ERROR: the trait bound `usize: Neg` is not satisfied + //~| HELP: consider specifying an integer type that can be negative + do_something(x); +} + +fn do_something(_val: usize) {} diff --git a/tests/ui/suggestions/negative-literal-infered-to-unsigned.stderr b/tests/ui/suggestions/negative-literal-infered-to-unsigned.stderr new file mode 100644 index 0000000000000..b49ea224d2b17 --- /dev/null +++ b/tests/ui/suggestions/negative-literal-infered-to-unsigned.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `usize: Neg` is not satisfied + --> $DIR/negative-literal-infered-to-unsigned.rs:2:14 + | +LL | for x in -5..5 { + | ^^ the trait `Neg` is not implemented for `usize` + | +help: consider specifying an integer type that can be negative + | +LL | for x in -5isize..5 { + | +++++ + +error[E0277]: the trait bound `usize: Neg` is not satisfied + --> $DIR/negative-literal-infered-to-unsigned.rs:7:13 + | +LL | let x = -5; + | ^^ the trait `Neg` is not implemented for `usize` + | +help: consider specifying an integer type that can be negative + | +LL | let x = -5isize; + | +++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 263766bcc28a9441d0acfb1a335308e46cfd1917 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 31 Aug 2025 10:53:33 +0800 Subject: [PATCH 07/15] suggest method name with maybe ty mismatch --- compiler/rustc_hir_typeck/src/method/mod.rs | 2 +- ...-suggest-await-on-method-return-mismatch.stderr | 5 +++++ tests/ui/privacy/private-field-ty-err.stderr | 5 +++++ ...st-method-name-with-maybe-ty-mismatch-146008.rs | 14 ++++++++++++++ ...ethod-name-with-maybe-ty-mismatch-146008.stderr | 14 ++++++++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.rs create mode 100644 tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.stderr diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index e37ea03167262..652644ad78cce 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -117,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(Ambiguity(..)) => true, Err(PrivateMatch(..)) => false, Err(IllegalSizedBound { .. }) => true, - Err(BadReturnType) => false, + Err(BadReturnType) => true, Err(ErrorReported(_)) => false, } } diff --git a/tests/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr b/tests/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr index 1faaf4ddce2f6..aa2343e48a6e5 100644 --- a/tests/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr +++ b/tests/ui/async-await/dont-suggest-await-on-method-return-mismatch.stderr @@ -3,6 +3,11 @@ error[E0599]: no method named `test` found for opaque type `impl Future` + | +help: consider `await`ing on the `Future` and calling the method on its `Output` + | +LL | let x: u32 = foo().await.test(); + | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/privacy/private-field-ty-err.stderr b/tests/ui/privacy/private-field-ty-err.stderr index 17d50f24a945f..0eecad14cfc33 100644 --- a/tests/ui/privacy/private-field-ty-err.stderr +++ b/tests/ui/privacy/private-field-ty-err.stderr @@ -3,6 +3,11 @@ error[E0616]: field `len` of struct `Foo` is private | LL | if x.len { | ^^^ private field + | +help: a method `len` also exists, call it with parentheses + | +LL | if x.len() { + | ++ error: aborting due to 1 previous error diff --git a/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.rs b/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.rs new file mode 100644 index 0000000000000..671f280e81430 --- /dev/null +++ b/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.rs @@ -0,0 +1,14 @@ +struct LlamaModel; + +impl LlamaModel { + fn chat_template(&self) -> Result<&str, ()> { + todo!() + } +} + +fn template_from_str(_x: &str) {} + +fn main() { + let model = LlamaModel; + template_from_str(&model.chat_template); //~ ERROR attempted to take value of method `chat_template` on type `LlamaModel` +} diff --git a/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.stderr b/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.stderr new file mode 100644 index 0000000000000..7f5dd0617b1b6 --- /dev/null +++ b/tests/ui/typeck/suggest-method-name-with-maybe-ty-mismatch-146008.stderr @@ -0,0 +1,14 @@ +error[E0615]: attempted to take value of method `chat_template` on type `LlamaModel` + --> $DIR/suggest-method-name-with-maybe-ty-mismatch-146008.rs:13:30 + | +LL | template_from_str(&model.chat_template); + | ^^^^^^^^^^^^^ method, not a field + | +help: use parentheses to call the method + | +LL | template_from_str(&model.chat_template()); + | ++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0615`. From cfba491e674dcff8cc5edbf9ae3e3880518804fd Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Sun, 31 Aug 2025 10:03:49 +0200 Subject: [PATCH 08/15] fix --- library/std/tests/floats/f32.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs index 38c906c1d8771..c61a8ec4d2096 100644 --- a/library/std/tests/floats/f32.rs +++ b/library/std/tests/floats/f32.rs @@ -79,8 +79,8 @@ fn test_log() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(10.0f32.log(10.0), 1.0); - assert_approx_eq!(2.3f32.log(3.5), 0.664858); + assert_approx_eq!(10.0f32.log(10.0), 1.0, APPROX_DELTA); + assert_approx_eq!(2.3f32.log(3.5), 0.664858, APPROX_DELTA); assert_approx_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0, APPROX_DELTA); assert!(1.0f32.log(1.0).is_nan()); assert!(1.0f32.log(-13.9).is_nan()); From ea2daa33c8a61af4518d86d78c742ec8e922c93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 31 Aug 2025 21:13:12 +0000 Subject: [PATCH 09/15] Split `ObligationCauseCode::BinOp` for unops to `UnOp` --- compiler/rustc_hir_typeck/src/op.rs | 16 +++++++------ compiler/rustc_middle/src/traits/mod.rs | 8 +++++-- .../traits/fulfillment_errors.rs | 8 +++---- .../src/error_reporting/traits/suggestions.rs | 23 ++++++++----------- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 87a7cd62ae5eb..11defc3aa0339 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -962,13 +962,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip(); let cause = self.cause( span, - ObligationCauseCode::BinOp { - lhs_hir_id: lhs_expr.hir_id, - rhs_hir_id: opt_rhs_expr.map(|expr| expr.hir_id), - rhs_span: opt_rhs_expr.map(|expr| expr.span), - rhs_is_lit: opt_rhs_expr - .is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))), - output_ty: expected.only_has_type(self), + match opt_rhs_expr { + Some(rhs) => ObligationCauseCode::BinOp { + lhs_hir_id: lhs_expr.hir_id, + rhs_hir_id: rhs.hir_id, + rhs_span: rhs.span, + rhs_is_lit: matches!(rhs.kind, hir::ExprKind::Lit(_)), + output_ty: expected.only_has_type(self), + }, + None => ObligationCauseCode::UnOp { hir_id: lhs_expr.hir_id }, }, ); diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 32f91bfba6bee..ab8a314295392 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -389,10 +389,14 @@ pub enum ObligationCauseCode<'tcx> { /// against. MatchImpl(ObligationCause<'tcx>, DefId), + UnOp { + hir_id: HirId, + }, + BinOp { lhs_hir_id: HirId, - rhs_hir_id: Option, - rhs_span: Option, + rhs_hir_id: HirId, + rhs_span: Span, rhs_is_lit: bool, output_ty: Option>, }, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index f1fd7ebfeba2f..bc984f30472d9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -963,8 +963,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, err: &mut Diag<'_>, ) -> bool { - if let ObligationCauseCode::BinOp { lhs_hir_id, .. } = obligation.cause.code() - && let hir::Node::Expr(expr) = self.tcx.hir_node(*lhs_hir_id) + if let ObligationCauseCode::UnOp { hir_id, .. } = obligation.cause.code() + && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) && let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = expr.kind && let hir::ExprKind::Lit(lit) = inner.kind && let LitKind::Int(_, LitIntType::Unsuffixed) = lit.node @@ -2769,9 +2769,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { suggested: bool, ) { let body_def_id = obligation.cause.body_id; - let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } = - obligation.cause.code() - { + let span = if let ObligationCauseCode::BinOp { rhs_span, .. } = obligation.cause.code() { *rhs_span } else { span diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index aa153d3607bf5..40a27c9aebe2d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -554,7 +554,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return true; } } else if let ( - ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id: Some(rhs_hir_id), .. }, + ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. }, predicate, ) = code.peel_derives_with_predicate() && let Some(typeck_results) = &self.typeck_results @@ -2801,6 +2801,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | ObligationCauseCode::QuestionMark | ObligationCauseCode::CheckAssociatedTypeBounds { .. } | ObligationCauseCode::LetElse + | ObligationCauseCode::UnOp { .. } | ObligationCauseCode::BinOp { .. } | ObligationCauseCode::AscribeUserTypeProvePredicate(..) | ObligationCauseCode::AlwaysApplicableImpl @@ -3839,9 +3840,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) { let rhs_span = match obligation.cause.code() { - ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => { - span - } + ObligationCauseCode::BinOp { rhs_span, rhs_is_lit, .. } if *rhs_is_lit => rhs_span, _ => return, }; if let ty::Float(_) = trait_pred.skip_binder().self_ty().kind() @@ -5108,16 +5107,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let tcx = self.tcx; let predicate = predicate.upcast(tcx); match *cause_code { - ObligationCauseCode::BinOp { - lhs_hir_id, - rhs_hir_id: Some(rhs_hir_id), - rhs_span: Some(rhs_span), - .. - } if let Some(typeck_results) = &self.typeck_results - && let hir::Node::Expr(lhs) = tcx.hir_node(lhs_hir_id) - && let hir::Node::Expr(rhs) = tcx.hir_node(rhs_hir_id) - && let Some(lhs_ty) = typeck_results.expr_ty_opt(lhs) - && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) => + ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, rhs_span, .. } + if let Some(typeck_results) = &self.typeck_results + && let hir::Node::Expr(lhs) = tcx.hir_node(lhs_hir_id) + && let hir::Node::Expr(rhs) = tcx.hir_node(rhs_hir_id) + && let Some(lhs_ty) = typeck_results.expr_ty_opt(lhs) + && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) => { if let Some(pred) = predicate.as_trait_clause() && tcx.is_lang_item(pred.def_id(), LangItem::PartialEq) From 7ea882d4c475855fe3e453f709429cf2c8be295f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 Aug 2025 10:56:18 +1000 Subject: [PATCH 10/15] 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 128ae8549f7cb..94f9c58b5cf47 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 3a0d0be586c0135bff8e8e2a280ee647337a07e7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 Aug 2025 11:13:24 +1000 Subject: [PATCH 11/15] 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 5b3ddcc777be5..374421507230f 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -275,13 +275,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; @@ -290,7 +291,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 { @@ -303,13 +304,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; @@ -345,17 +346,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 d7faa5630daf13c3d298e81ac329bf048a0d5a01 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 15 Aug 2025 09:13:23 +1000 Subject: [PATCH 12/15] 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 94f9c58b5cf47..c2cc204258166 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]; @@ -1363,11 +1363,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 6a8f0b21ee0e0..4818f9bb0abff 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -834,7 +834,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pretty::write_mir_fn( self.tcx, &body, - &mut |_, _| Ok(()), + &|_, _| Ok(()), &mut std::io::stdout(), options, ) From d3e2c93498d2ed4b924ac7b1d42bd824d58ffe0e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 15 Aug 2025 13:13:14 +1000 Subject: [PATCH 13/15] 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 c2cc204258166..13b619a5a8bf1 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, )?; } @@ -1360,15 +1343,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 374421507230f..374623d4ea54d 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -291,7 +291,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 { @@ -304,7 +304,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}")); @@ -348,5 +348,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 c6760b3583f20..39d4aaa985792 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 2d21c134054b6d337d4a1b81550a1ad10509b913 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 15 Aug 2025 14:38:03 +1000 Subject: [PATCH 14/15] 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 13b619a5a8bf1..14f504bb2a957 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 5ce3797073ee5e6cc487b80effbc682533d9425c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 15 Aug 2025 14:38:17 +1000 Subject: [PATCH 15/15] Introduce `MirDumper` and `MirWriter`. MIR dumping is a mess. There are lots of functions and 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 | 18 +- .../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, 275 insertions(+), 276 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 c977e5329c292..da2245b12d2c2 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 14f504bb2a957..a7d99f513a12a 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 4818f9bb0abff..cdb2c5561ce6a 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -806,10 +806,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); body.coverage_info_hi = self.coverage_info.as_ref().map(|b| b.as_done()); - use rustc_middle::mir::pretty; - let options = pretty::PrettyPrintMirOptions::from_cli(self.tcx); - pretty::write_mir_fn(self.tcx, &body, &mut |_, _| Ok(()), &mut std::io::stdout(), options) - .unwrap(); + let writer = pretty::MirWriter::new(self.tcx); + writer.write_mir_fn(&body, &mut std::io::stdout()).unwrap(); } fn finish(self) -> Body<'tcx> { @@ -827,18 +825,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); body.coverage_info_hi = self.coverage_info.map(|b| b.into_done()); + let writer = pretty::MirWriter::new(self.tcx); for (index, block) in body.basic_blocks.iter().enumerate() { if block.terminator.is_none() { - use rustc_middle::mir::pretty; - let options = pretty::PrettyPrintMirOptions::from_cli(self.tcx); - pretty::write_mir_fn( - self.tcx, - &body, - &|_, _| 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 372a3f3a8b811..b85b82b8f6d92 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -10,8 +10,7 @@ use std::{io, ops, str}; use regex::Regex; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ - self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name, - traversal, + self, BasicBlock, Body, Location, MirDumper, graphviz_safe_def_name, traversal, }; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -61,11 +60,13 @@ where fs::File::create_buffered(&path)? } - None if dump_enabled(tcx, A::NAME, def_id) => { - create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? + None => { + let Some(dumper) = MirDumper::new(tcx, A::NAME, body) else { + return Ok(()); + }; + let disambiguator = &pass_name.unwrap_or("-----"); + dumper.set_disambiguator(disambiguator).create_dump_file("dot", body)? } - - _ => return Ok(()), } }; let mut file = match file { 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 374623d4ea54d..ab09cdf787ee7 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; @@ -275,7 +275,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; @@ -288,10 +287,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 { @@ -303,9 +308,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}")); } @@ -348,5 +354,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 39d4aaa985792..bca8ffb693b90 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 }