From 3ee62a906e8a025834d11c46b8c2f133d3a12448 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 30 Mar 2025 18:52:25 +0000 Subject: [PATCH 1/9] Do not optimize out SwitchInt before borrowck, or if Zmir-preserve-ub --- compiler/rustc_interface/src/tests.rs | 2 +- .../src/early_otherwise_branch.rs | 2 +- compiler/rustc_mir_transform/src/inline.rs | 4 +- .../rustc_mir_transform/src/match_branches.rs | 2 +- .../src/remove_place_mention.rs | 2 +- .../src/remove_unneeded_drops.rs | 2 +- compiler/rustc_mir_transform/src/simplify.rs | 35 +++++++++---- compiler/rustc_session/src/options.rs | 6 +-- src/tools/miri/src/lib.rs | 2 +- .../tests/fail/read_from_trivial_switch.rs | 14 ++++++ .../fail/read_from_trivial_switch.stderr | 15 ++++++ ....match_tuple.SimplifyCfg-initial.after.mir | 34 +++++++------ .../dead-store-elimination/place_mention.rs | 2 +- ...le_switchint.SimplifyCfg-initial.after.mir | 38 ++++++++------ ...ivial_switch.main.SimplifyCfg-initial.diff | 49 +++++++++++++++++++ tests/mir-opt/read_from_trivial_switch.rs | 15 ++++++ tests/ui/pattern/uninit-trivial.rs | 8 +++ tests/ui/pattern/uninit-trivial.stderr | 16 ++++++ 18 files changed, 197 insertions(+), 51 deletions(-) create mode 100644 src/tools/miri/tests/fail/read_from_trivial_switch.rs create mode 100644 src/tools/miri/tests/fail/read_from_trivial_switch.stderr create mode 100644 tests/mir-opt/read_from_trivial_switch.main.SimplifyCfg-initial.diff create mode 100644 tests/mir-opt/read_from_trivial_switch.rs create mode 100644 tests/ui/pattern/uninit-trivial.rs create mode 100644 tests/ui/pattern/uninit-trivial.stderr diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index a8e556632572e..af30a2d8aa887 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -817,8 +817,8 @@ fn test_unstable_options_tracking_hash() { tracked!(min_function_alignment, Some(Align::EIGHT)); tracked!(mir_emit_retag, true); tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]); - tracked!(mir_keep_place_mention, true); tracked!(mir_opt_level, Some(4)); + tracked!(mir_preserve_ub, true); tracked!(move_size_limit, Some(4096)); tracked!(mutable_noalias, false); tracked!(next_solver, NextSolverConfig { coherence: true, globally: true }); diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 57f7893be1b8c..ac97b08fbed1e 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -224,7 +224,7 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { // Since this optimization adds new basic blocks and invalidates others, // clean up the cfg to make it nicer for other passes if should_cleanup { - simplify_cfg(body); + simplify_cfg(tcx, body); } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 0ab24e48d443c..babee81a2ad06 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -63,7 +63,7 @@ impl<'tcx> crate::MirPass<'tcx> for Inline { let _guard = span.enter(); if inline::>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); - simplify_cfg(body); + simplify_cfg(tcx, body); deref_finder(tcx, body); } } @@ -99,7 +99,7 @@ impl<'tcx> crate::MirPass<'tcx> for ForceInline { let _guard = span.enter(); if inline::>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); - simplify_cfg(body); + simplify_cfg(tcx, body); deref_finder(tcx, body); } } diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 0d9d0368d3729..d43a35124c2aa 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -45,7 +45,7 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { } if should_cleanup { - simplify_cfg(body); + simplify_cfg(tcx, body); } } diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs index 15fe77d53195a..cb598ceb4dfea 100644 --- a/compiler/rustc_mir_transform/src/remove_place_mention.rs +++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs @@ -8,7 +8,7 @@ pub(super) struct RemovePlaceMention; impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - !sess.opts.unstable_opts.mir_keep_place_mention + !sess.opts.unstable_opts.mir_preserve_ub } fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index 8a8cdafc69070..43f80508e4a87 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -35,7 +35,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops { // if we applied optimizations, we potentially have some cfg to cleanup to // make it easier for further passes if should_simplify { - simplify_cfg(body); + simplify_cfg(tcx, body); } } diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 84905f4a400f3..6c8fa1fe9b3f1 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -26,6 +26,13 @@ //! Here the block (`{ return; }`) has the return type `char`, rather than `()`, but the MIR we //! naively generate still contains the `_a = ()` write in the unreachable block "after" the //! return. +//! +//! **WARNING**: This is one of the few optimizations that runs on built and analysis MIR, and +//! so its effects may affect the type-checking, borrow-checking, and other analysis of MIR. +//! We must be extremely careful to only apply optimizations that preserve UB and all +//! non-determinism, since changes here can affect which programs compile in an insta-stable way. +//! The normal logic that a program with UB can be changed to do anything does not apply to +//! pre-"runtime" MIR! use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -66,8 +73,8 @@ impl SimplifyCfg { } } -pub(super) fn simplify_cfg(body: &mut Body<'_>) { - CfgSimplifier::new(body).simplify(); +pub(super) fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + CfgSimplifier::new(tcx, body).simplify(); remove_dead_blocks(body); // FIXME: Should probably be moved into some kind of pass manager @@ -79,9 +86,9 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg { self.name() } - fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source); - simplify_cfg(body); + simplify_cfg(tcx, body); } fn is_required(&self) -> bool { @@ -90,12 +97,13 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg { } struct CfgSimplifier<'a, 'tcx> { + preserve_switch_reads: bool, basic_blocks: &'a mut IndexSlice>, pred_count: IndexVec, } impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { - fn new(body: &'a mut Body<'tcx>) -> Self { + fn new(tcx: TyCtxt<'tcx>, body: &'a mut Body<'tcx>) -> Self { let mut pred_count = IndexVec::from_elem(0u32, &body.basic_blocks); // we can't use mir.predecessors() here because that counts @@ -110,9 +118,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { } } + // Preserve `SwitchInt` reads on built and analysis MIR, or if `-Zmir-preserve-ub`. + let preserve_switch_reads = matches!(body.phase, MirPhase::Built | MirPhase::Analysis(_)) + || tcx.sess.opts.unstable_opts.mir_preserve_ub; let basic_blocks = body.basic_blocks_mut(); - CfgSimplifier { basic_blocks, pred_count } + CfgSimplifier { preserve_switch_reads, basic_blocks, pred_count } } fn simplify(mut self) { @@ -253,9 +264,15 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // turn a branch with all successors identical to a goto fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool { - match terminator.kind { - TerminatorKind::SwitchInt { .. } => {} - _ => return false, + // Removing a `SwitchInt` terminator may remove reads that result in UB, + // so we must not apply this optimization before borrowck or when + // `-Zmir-preserve-ub` is set. + if self.preserve_switch_reads { + return false; + } + + let TerminatorKind::SwitchInt { .. } = terminator.kind else { + return false; }; let first_succ = { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c70f1500d3930..ea959d4ced1e9 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2319,12 +2319,12 @@ options! { mir_include_spans: MirIncludeSpans = (MirIncludeSpans::default(), parse_mir_include_spans, [UNTRACKED], "include extra comments in mir pretty printing, like line numbers and statement indices, \ details about types, etc. (boolean for all passes, 'nll' to enable in NLL MIR only, default: 'nll')"), - mir_keep_place_mention: bool = (false, parse_bool, [TRACKED], - "keep place mention MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ - (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")] mir_opt_level: Option = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), + mir_preserve_ub: bool = (false, parse_bool, [TRACKED], + "keep place mention statements and reads in trivial SwitchInt terminators, which are interpreted \ + e.g., by miri; implies -Zmir-opt-level=0 (default: no)"), mir_strip_debuginfo: MirStripDebugInfo = (MirStripDebugInfo::None, parse_mir_strip_debuginfo, [TRACKED], "Whether to remove some of the MIR debug info from methods. Default: None"), move_size_limit: Option = (None, parse_opt_number, [TRACKED], diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 03f76cfa6524c..6f227e04d00fa 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -169,7 +169,7 @@ pub const MIRI_DEFAULT_ARGS: &[&str] = &[ "-Zalways-encode-mir", "-Zextra-const-ub-checks", "-Zmir-emit-retag", - "-Zmir-keep-place-mention", + "-Zmir-preserve-ub", "-Zmir-opt-level=0", "-Zmir-enable-passes=-CheckAlignment,-CheckNull", // Deduplicating diagnostics means we miss events when tracking what happens during an diff --git a/src/tools/miri/tests/fail/read_from_trivial_switch.rs b/src/tools/miri/tests/fail/read_from_trivial_switch.rs new file mode 100644 index 0000000000000..d34b1cd582009 --- /dev/null +++ b/src/tools/miri/tests/fail/read_from_trivial_switch.rs @@ -0,0 +1,14 @@ +// Ensure that we don't optimize out `SwitchInt` reads even if that terminator +// branches to the same basic block on every target, since the operand may have +// side-effects that affect analysis of the MIR. +// +// See . + +use std::mem::MaybeUninit; + +fn main() { + let uninit: MaybeUninit = MaybeUninit::uninit(); + let bad_ref: &i32 = unsafe { uninit.assume_init_ref() }; + let &(0 | _) = bad_ref; + //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory +} diff --git a/src/tools/miri/tests/fail/read_from_trivial_switch.stderr b/src/tools/miri/tests/fail/read_from_trivial_switch.stderr new file mode 100644 index 0000000000000..6b3d4539b9681 --- /dev/null +++ b/src/tools/miri/tests/fail/read_from_trivial_switch.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory + --> tests/fail/read_from_trivial_switch.rs:LL:CC + | +LL | let &(0 | _) = bad_ref; + | ^^^^^^^^ using uninitialized data, but this operation requires initialized memory + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/read_from_trivial_switch.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir index d52241b459ebd..2a965fe67b9e7 100644 --- a/tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir @@ -24,43 +24,47 @@ fn match_tuple(_1: (u32, bool, Option, u32)) -> u32 { bb1: { _0 = const 0_u32; - goto -> bb10; + goto -> bb11; } bb2: { - _2 = discriminant((_1.2: std::option::Option)); - switchInt(move _2) -> [0: bb4, 1: bb3, otherwise: bb1]; + switchInt(copy (_1.1: bool)) -> [0: bb3, otherwise: bb3]; } bb3: { - switchInt(copy (((_1.2: std::option::Option) as Some).0: i32)) -> [1: bb4, 8: bb4, otherwise: bb1]; + _2 = discriminant((_1.2: std::option::Option)); + switchInt(move _2) -> [0: bb5, 1: bb4, otherwise: bb1]; } bb4: { - _5 = Le(const 6_u32, copy (_1.3: u32)); - switchInt(move _5) -> [0: bb5, otherwise: bb7]; + switchInt(copy (((_1.2: std::option::Option) as Some).0: i32)) -> [1: bb5, 8: bb5, otherwise: bb1]; } bb5: { - _3 = Le(const 13_u32, copy (_1.3: u32)); - switchInt(move _3) -> [0: bb1, otherwise: bb6]; + _5 = Le(const 6_u32, copy (_1.3: u32)); + switchInt(move _5) -> [0: bb6, otherwise: bb8]; } bb6: { - _4 = Le(copy (_1.3: u32), const 16_u32); - switchInt(move _4) -> [0: bb1, otherwise: bb8]; + _3 = Le(const 13_u32, copy (_1.3: u32)); + switchInt(move _3) -> [0: bb1, otherwise: bb7]; } bb7: { - _6 = Le(copy (_1.3: u32), const 9_u32); - switchInt(move _6) -> [0: bb5, otherwise: bb8]; + _4 = Le(copy (_1.3: u32), const 16_u32); + switchInt(move _4) -> [0: bb1, otherwise: bb9]; } bb8: { - falseEdge -> [real: bb9, imaginary: bb1]; + _6 = Le(copy (_1.3: u32), const 9_u32); + switchInt(move _6) -> [0: bb6, otherwise: bb9]; } bb9: { + falseEdge -> [real: bb10, imaginary: bb1]; + } + + bb10: { StorageLive(_7); _7 = copy (_1.0: u32); StorageLive(_8); @@ -74,10 +78,10 @@ fn match_tuple(_1: (u32, bool, Option, u32)) -> u32 { StorageDead(_9); StorageDead(_8); StorageDead(_7); - goto -> bb10; + goto -> bb11; } - bb10: { + bb11: { return; } } diff --git a/tests/mir-opt/dead-store-elimination/place_mention.rs b/tests/mir-opt/dead-store-elimination/place_mention.rs index 5e4a286a20874..1848a02829754 100644 --- a/tests/mir-opt/dead-store-elimination/place_mention.rs +++ b/tests/mir-opt/dead-store-elimination/place_mention.rs @@ -2,7 +2,7 @@ // and don't remove it as a dead store. // //@ test-mir-pass: DeadStoreElimination-initial -//@ compile-flags: -Zmir-keep-place-mention +//@ compile-flags: -Zmir-preserve-ub // EMIT_MIR place_mention.main.DeadStoreElimination-initial.diff fn main() { diff --git a/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir index 889ff6f9f5e2b..be0931eaa61d7 100644 --- a/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir @@ -14,7 +14,7 @@ fn single_switchint() -> () { } bb1: { - switchInt(copy (_2.0: i32)) -> [3: bb8, 4: bb8, otherwise: bb7]; + switchInt(copy (_2.0: i32)) -> [3: bb9, 4: bb9, otherwise: bb8]; } bb2: { @@ -22,7 +22,7 @@ fn single_switchint() -> () { } bb3: { - falseEdge -> [real: bb12, imaginary: bb4]; + falseEdge -> [real: bb14, imaginary: bb4]; } bb4: { @@ -30,43 +30,51 @@ fn single_switchint() -> () { } bb5: { - falseEdge -> [real: bb11, imaginary: bb6]; + falseEdge -> [real: bb13, imaginary: bb6]; } bb6: { - falseEdge -> [real: bb10, imaginary: bb1]; + switchInt(copy (_2.1: bool)) -> [0: bb7, otherwise: bb7]; } bb7: { - _1 = const 5_i32; - goto -> bb13; + falseEdge -> [real: bb12, imaginary: bb1]; } bb8: { - falseEdge -> [real: bb9, imaginary: bb7]; + _1 = const 5_i32; + goto -> bb15; } bb9: { - _1 = const 4_i32; - goto -> bb13; + switchInt(copy (_2.1: bool)) -> [0: bb10, otherwise: bb10]; } bb10: { - _1 = const 3_i32; - goto -> bb13; + falseEdge -> [real: bb11, imaginary: bb8]; } bb11: { - _1 = const 2_i32; - goto -> bb13; + _1 = const 4_i32; + goto -> bb15; } bb12: { - _1 = const 1_i32; - goto -> bb13; + _1 = const 3_i32; + goto -> bb15; } bb13: { + _1 = const 2_i32; + goto -> bb15; + } + + bb14: { + _1 = const 1_i32; + goto -> bb15; + } + + bb15: { StorageDead(_2); StorageDead(_1); _0 = const (); diff --git a/tests/mir-opt/read_from_trivial_switch.main.SimplifyCfg-initial.diff b/tests/mir-opt/read_from_trivial_switch.main.SimplifyCfg-initial.diff new file mode 100644 index 0000000000000..87758408a1c37 --- /dev/null +++ b/tests/mir-opt/read_from_trivial_switch.main.SimplifyCfg-initial.diff @@ -0,0 +1,49 @@ +- // MIR for `main` before SimplifyCfg-initial ++ // MIR for `main` after SimplifyCfg-initial + + fn main() -> () { + let mut _0: (); + let _1: &i32; + let _2: i32; + scope 1 { + debug ref_ => _1; + scope 2 { + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + _2 = const 1_i32; + _1 = &_2; + FakeRead(ForLet(None), _1); + PlaceMention(_1); +- switchInt(copy (*_1)) -> [0: bb2, otherwise: bb1]; ++ switchInt(copy (*_1)) -> [0: bb1, otherwise: bb1]; + } + + bb1: { +- goto -> bb5; +- } +- +- bb2: { +- goto -> bb5; +- } +- +- bb3: { +- goto -> bb1; +- } +- +- bb4: { +- FakeRead(ForMatchedPlace(None), _1); +- unreachable; +- } +- +- bb5: { + _0 = const (); + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/read_from_trivial_switch.rs b/tests/mir-opt/read_from_trivial_switch.rs new file mode 100644 index 0000000000000..1c64c1d45e831 --- /dev/null +++ b/tests/mir-opt/read_from_trivial_switch.rs @@ -0,0 +1,15 @@ +// Ensure that we don't optimize out `SwitchInt` reads even if that terminator +// branches to the same basic block on every target, since the operand may have +// side-effects that affect analysis of the MIR. +// +// See . + +//@ test-mir-pass: SimplifyCfg-initial +//@ compile-flags: -Zmir-preserve-ub + +// EMIT_MIR read_from_trivial_switch.main.SimplifyCfg-initial.diff +fn main() { + let ref_ = &1i32; + // CHECK: switchInt + let &(0 | _) = ref_; +} diff --git a/tests/ui/pattern/uninit-trivial.rs b/tests/ui/pattern/uninit-trivial.rs new file mode 100644 index 0000000000000..6ea6796c1c1f0 --- /dev/null +++ b/tests/ui/pattern/uninit-trivial.rs @@ -0,0 +1,8 @@ +// Regression test for the semantic changes in +// . + +fn main() { + let x; + let (0 | _) = x; + //~^ ERROR used binding `x` isn't initialized +} diff --git a/tests/ui/pattern/uninit-trivial.stderr b/tests/ui/pattern/uninit-trivial.stderr new file mode 100644 index 0000000000000..2ff8557c94543 --- /dev/null +++ b/tests/ui/pattern/uninit-trivial.stderr @@ -0,0 +1,16 @@ +error[E0381]: used binding `x` isn't initialized + --> $DIR/uninit-trivial.rs:6:10 + | +LL | let x; + | - binding declared here but left uninitialized +LL | let (0 | _) = x; + | ^^^^^ `x` used here but it isn't initialized + | +help: consider assigning a value + | +LL | let x = 42; + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0381`. From d1e82ba37c3dd1998808665971bbee4e1ae028b8 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Mon, 14 Apr 2025 21:43:53 -0700 Subject: [PATCH 2/9] Setup editor file associations for non-rs extensions .gitattributes lists *.fixed, *.pp, and *.mir as file extensions which should be treated as Rust source code. Do the same for VS Code and Zed. This only does syntax highlighting, which is appropriate, as MIR isn't really Rust code. At the same time, consistently order `rust-analyzer.linkedProjects` between editors. For some reason, Eglot didn't include library/Cargo.toml. --- src/bootstrap/src/core/build_steps/setup.rs | 3 +++ src/etc/rust_analyzer_eglot.el | 7 ++++--- src/etc/rust_analyzer_settings.json | 11 ++++++++--- src/etc/rust_analyzer_zed.json | 13 ++++++++----- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 80d92135dd378..83083e12ef1fc 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -584,6 +584,7 @@ Select which editor you would like to set up [default: None]: "; "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0", "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45", "b5dd299b93dca3ceeb9b335f929293cb3d4bf4977866fbe7ceeac2a8a9f99088", + "631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9", ], EditorKind::Helix => &[ "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233", @@ -602,10 +603,12 @@ Select which editor you would like to set up [default: None]: "; "4eecb58a2168b252077369da446c30ed0e658301efe69691979d1ef0443928f4", "c394386e6133bbf29ffd32c8af0bb3d4aac354cba9ee051f29612aa9350f8f8d", "e53e9129ca5ee5dcbd6ec8b68c2d87376474eb154992deba3c6d9ab1703e0717", + "f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893", ], EditorKind::Zed => &[ "bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c", "a5380cf5dd9328731aecc5dfb240d16dac46ed272126b9728006151ef42f5909", + "2e96bf0d443852b12f016c8fc9840ab3d0a2b4fe0b0fb3a157e8d74d5e7e0e26", ], } } diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el index 6b40371d9af11..90bd38aa89477 100644 --- a/src/etc/rust_analyzer_eglot.el +++ b/src/etc/rust_analyzer_eglot.el @@ -8,10 +8,11 @@ "check" "--json-output"]) :linkedProjects ["Cargo.toml" - "src/bootstrap/Cargo.toml" - "src/tools/rust-analyzer/Cargo.toml" "compiler/rustc_codegen_cranelift/Cargo.toml" - "compiler/rustc_codegen_gcc/Cargo.toml"] + "compiler/rustc_codegen_gcc/Cargo.toml" + "library/Cargo.toml" + "src/bootstrap/Cargo.toml" + "src/tools/rust-analyzer/Cargo.toml"] :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt" "--edition=2021"]) :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv" diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json index da7d326a512cc..5ce886a9b6594 100644 --- a/src/etc/rust_analyzer_settings.json +++ b/src/etc/rust_analyzer_settings.json @@ -9,11 +9,11 @@ ], "rust-analyzer.linkedProjects": [ "Cargo.toml", + "compiler/rustc_codegen_cranelift/Cargo.toml", + "compiler/rustc_codegen_gcc/Cargo.toml", "library/Cargo.toml", "src/bootstrap/Cargo.toml", - "src/tools/rust-analyzer/Cargo.toml", - "compiler/rustc_codegen_cranelift/Cargo.toml", - "compiler/rustc_codegen_gcc/Cargo.toml" + "src/tools/rust-analyzer/Cargo.toml" ], "rust-analyzer.rustfmt.overrideCommand": [ "${workspaceFolder}/build/host/rustfmt/bin/rustfmt", @@ -36,5 +36,10 @@ }, "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "nightly" + }, + "files.associations": { + "*.fixed": "rust", + "*.pp": "rust", + "*.mir": "rust" } } diff --git a/src/etc/rust_analyzer_zed.json b/src/etc/rust_analyzer_zed.json index abc6ddbc213c4..3461ff887d9b4 100644 --- a/src/etc/rust_analyzer_zed.json +++ b/src/etc/rust_analyzer_zed.json @@ -21,15 +21,15 @@ }, "linkedProjects": [ "Cargo.toml", + "compiler/rustc_codegen_cranelift/Cargo.toml", + "compiler/rustc_codegen_gcc/Cargo.toml", "library/Cargo.toml", "src/bootstrap/Cargo.toml", - "src/tools/rust-analyzer/Cargo.toml", - "compiler/rustc_codegen_cranelift/Cargo.toml", - "compiler/rustc_codegen_gcc/Cargo.toml" + "src/tools/rust-analyzer/Cargo.toml" ], "procMacro": { - "enable": true, - "server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv" + "enable": true, + "server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv" }, "rustc": { "source": "./Cargo.toml" @@ -47,5 +47,8 @@ } } } + }, + "file_types": { + "Rust": ["fixed", "pp", "mir"] } } From 136171c8d8c81ff8de4446fb4bb339a1bd475a49 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Fri, 18 Apr 2025 00:58:04 -0400 Subject: [PATCH 3/9] skip llvm-config in autodiff check builds, when its unavailable --- src/bootstrap/src/core/build_steps/compile.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index dab58fccf5e68..106a04de5d9a7 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1194,8 +1194,7 @@ pub fn rustc_cargo( let enzyme_dir = builder.build.out.join(arch).join("enzyme").join("lib"); cargo.rustflag("-L").rustflag(enzyme_dir.to_str().expect("Invalid path")); - if !builder.config.dry_run() { - let llvm_config = builder.llvm_config(builder.config.build).unwrap(); + if let Some(llvm_config) = builder.llvm_config(builder.config.build) { let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config); cargo.rustflag("-l").rustflag(&format!("Enzyme-{llvm_version_major}")); } From 8f6ef1f1f6a4fcec0325e4ff6fa50b8f42f853aa Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 18 Apr 2025 13:35:49 +0200 Subject: [PATCH 4/9] Improve `clean_maybe_renamed_item` function code a bit --- src/librustdoc/clean/mod.rs | 47 ++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index fe9dc9a9e2145..a693d9f8564e9 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2797,10 +2797,35 @@ fn clean_maybe_renamed_item<'tcx>( ) -> Vec { use hir::ItemKind; - let def_id = item.owner_id.to_def_id(); - let mut name = if renamed.is_some() { renamed } else { cx.tcx.hir_opt_name(item.hir_id()) }; + fn get_name( + cx: &DocContext<'_>, + item: &hir::Item<'_>, + renamed: Option, + ) -> Option { + renamed.or_else(|| cx.tcx.hir_opt_name(item.hir_id())) + } + let def_id = item.owner_id.to_def_id(); cx.with_param_env(def_id, |cx| { + // These kinds of item either don't need a `name` or accept a `None` one so we handle them + // before. + match item.kind { + ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx), + ItemKind::Use(path, kind) => { + return clean_use_statement( + item, + get_name(cx, item, renamed), + path, + kind, + cx, + &mut FxHashSet::default(), + ); + } + _ => {} + } + + let mut name = get_name(cx, item, renamed).unwrap(); + let kind = match item.kind { ItemKind::Static(_, ty, mutability, body_id) => StaticItem(Static { type_: Box::new(clean_ty(ty, cx)), @@ -2839,7 +2864,7 @@ fn clean_maybe_renamed_item<'tcx>( item_type: Some(type_), })), item.owner_id.def_id.to_def_id(), - name.unwrap(), + name, import_id, renamed, )); @@ -2862,17 +2887,14 @@ fn clean_maybe_renamed_item<'tcx>( generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), - ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx), ItemKind::Macro(_, macro_def, MacroKind::Bang) => MacroItem(Macro { - source: display_macro_source(cx, name.unwrap(), macro_def), + source: display_macro_source(cx, name, macro_def), macro_rules: macro_def.macro_rules, }), - ItemKind::Macro(_, _, macro_kind) => { - clean_proc_macro(item, name.as_mut().unwrap(), macro_kind, cx) - } + ItemKind::Macro(_, _, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx), // proc macros can have a name set by attributes ItemKind::Fn { ref sig, generics, body: body_id, .. } => { - clean_fn_or_proc_macro(item, sig, generics, body_id, name.as_mut().unwrap(), cx) + clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) } ItemKind::Trait(_, _, _, generics, bounds, item_ids) => { let items = item_ids @@ -2888,10 +2910,7 @@ fn clean_maybe_renamed_item<'tcx>( })) } ItemKind::ExternCrate(orig_name, _) => { - return clean_extern_crate(item, name.unwrap(), orig_name, cx); - } - ItemKind::Use(path, kind) => { - return clean_use_statement(item, name, path, kind, cx, &mut FxHashSet::default()); + return clean_extern_crate(item, name, orig_name, cx); } _ => span_bug!(item.span, "not yet converted"), }; @@ -2900,7 +2919,7 @@ fn clean_maybe_renamed_item<'tcx>( cx, kind, item.owner_id.def_id.to_def_id(), - name.unwrap(), + name, import_id, renamed, )] From bd5c43835ab77b30086b818e17c3a566849c2af3 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 18 Apr 2025 23:34:37 +0000 Subject: [PATCH 5/9] Remove early exits from JumpThreading. --- .../rustc_mir_transform/src/jump_threading.rs | 135 +++++++++--------- 1 file changed, 64 insertions(+), 71 deletions(-) diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 8b4b214a3d450..9732225e48ddd 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -90,11 +90,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { }; for bb in body.basic_blocks.indices() { - let old_len = finder.opportunities.len(); - // If we have any const-eval errors discard any opportunities found - if finder.start_from_switch(bb).is_none() { - finder.opportunities.truncate(old_len); - } + finder.start_from_switch(bb); } let opportunities = finder.opportunities; @@ -201,28 +197,26 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { /// Recursion entry point to find threading opportunities. #[instrument(level = "trace", skip(self))] - fn start_from_switch(&mut self, bb: BasicBlock) -> Option<()> { + fn start_from_switch(&mut self, bb: BasicBlock) { let bbdata = &self.body[bb]; if bbdata.is_cleanup || self.loop_headers.contains(bb) { - return Some(()); + return; } - let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return Some(()) }; - let Some(discr) = discr.place() else { return Some(()) }; + let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return }; + let Some(discr) = discr.place() else { return }; debug!(?discr, ?bb); let discr_ty = discr.ty(self.body, self.tcx).ty; - let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { - return Some(()); - }; + let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return }; - let Some(discr) = self.map.find(discr.as_ref()) else { return Some(()) }; + let Some(discr) = self.map.find(discr.as_ref()) else { return }; debug!(?discr); let cost = CostChecker::new(self.tcx, self.typing_env, None, self.body); let mut state = State::new_reachable(); let conds = if let Some((value, then, else_)) = targets.as_static_if() { - let value = ScalarInt::try_from_uint(value, discr_layout.size)?; + let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return }; self.arena.alloc_from_iter([ Condition { value, polarity: Polarity::Eq, target: then }, Condition { value, polarity: Polarity::Ne, target: else_ }, @@ -248,10 +242,10 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { mut state: State>, mut cost: CostChecker<'_, 'tcx>, depth: usize, - ) -> Option<()> { + ) { // Do not thread through loop headers. if self.loop_headers.contains(bb) { - return Some(()); + return; } debug!(cost = ?cost.cost()); @@ -259,16 +253,16 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { self.body.basic_blocks[bb].statements.iter().enumerate().rev() { if self.is_empty(&state) { - return Some(()); + return; } cost.visit_statement(stmt, Location { block: bb, statement_index }); if cost.cost() > MAX_COST { - return Some(()); + return; } // Attempt to turn the `current_condition` on `lhs` into a condition on another place. - self.process_statement(bb, stmt, &mut state)?; + self.process_statement(bb, stmt, &mut state); // When a statement mutates a place, assignments to that place that happen // above the mutation cannot fulfill a condition. @@ -280,7 +274,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } if self.is_empty(&state) || depth >= MAX_BACKTRACK { - return Some(()); + return; } let last_non_rec = self.opportunities.len(); @@ -293,9 +287,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { match term.kind { TerminatorKind::SwitchInt { ref discr, ref targets } => { self.process_switch_int(discr, targets, bb, &mut state); - self.find_opportunity(pred, state, cost, depth + 1)?; + self.find_opportunity(pred, state, cost, depth + 1); } - _ => self.recurse_through_terminator(pred, || state, &cost, depth)?, + _ => self.recurse_through_terminator(pred, || state, &cost, depth), } } else if let &[ref predecessors @ .., last_pred] = &predecessors[..] { for &pred in predecessors { @@ -320,13 +314,12 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { let first = &mut new_tos[0]; *first = ThreadingOpportunity { chain: vec![bb], target: first.target }; self.opportunities.truncate(last_non_rec + 1); - return Some(()); + return; } for op in self.opportunities[last_non_rec..].iter_mut() { op.chain.push(bb); } - Some(()) } /// Extract the mutated place from a statement. @@ -440,23 +433,23 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { lhs: PlaceIndex, rhs: &Operand<'tcx>, state: &mut State>, - ) -> Option<()> { + ) { match rhs { // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. Operand::Constant(constant) => { - let constant = self - .ecx - .eval_mir_constant(&constant.const_, constant.span, None) - .discard_err()?; + let Some(constant) = + self.ecx.eval_mir_constant(&constant.const_, constant.span, None).discard_err() + else { + return; + }; self.process_constant(bb, lhs, constant, state); } // Transfer the conditions on the copied rhs. Operand::Move(rhs) | Operand::Copy(rhs) => { - let Some(rhs) = self.map.find(rhs.as_ref()) else { return Some(()) }; + let Some(rhs) = self.map.find(rhs.as_ref()) else { return }; state.insert_place_idx(rhs, lhs, &self.map); } } - Some(()) } #[instrument(level = "trace", skip(self))] @@ -466,18 +459,14 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { lhs_place: &Place<'tcx>, rhs: &Rvalue<'tcx>, state: &mut State>, - ) -> Option<()> { - let Some(lhs) = self.map.find(lhs_place.as_ref()) else { - return Some(()); - }; + ) { + let Some(lhs) = self.map.find(lhs_place.as_ref()) else { return }; match rhs { - Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state)?, + Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state), // Transfer the conditions on the copy rhs. - Rvalue::CopyForDeref(rhs) => { - self.process_operand(bb, lhs, &Operand::Copy(*rhs), state)? - } + Rvalue::CopyForDeref(rhs) => self.process_operand(bb, lhs, &Operand::Copy(*rhs), state), Rvalue::Discriminant(rhs) => { - let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return Some(()) }; + let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return }; state.insert_place_idx(rhs, lhs, &self.map); } // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. @@ -485,7 +474,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { let agg_ty = lhs_place.ty(self.body, self.tcx).ty; let lhs = match kind { // Do not support unions. - AggregateKind::Adt(.., Some(_)) => return Some(()), + AggregateKind::Adt(.., Some(_)) => return, AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => { if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant) && let Some(discr_value) = self @@ -498,23 +487,23 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { if let Some(idx) = self.map.apply(lhs, TrackElem::Variant(*variant_index)) { idx } else { - return Some(()); + return; } } _ => lhs, }; for (field_index, operand) in operands.iter_enumerated() { if let Some(field) = self.map.apply(lhs, TrackElem::Field(field_index)) { - self.process_operand(bb, field, operand, state)?; + self.process_operand(bb, field, operand, state); } } } // Transfer the conditions on the copy rhs, after inverting the value of the condition. Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => { let layout = self.ecx.layout_of(place.ty(self.body, self.tcx).ty).unwrap(); - let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return Some(()) }; - let Some(place) = self.map.find(place.as_ref()) else { return Some(()) }; - let conds = conditions.map(self.arena, |mut cond| { + let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return }; + let Some(place) = self.map.find(place.as_ref()) else { return }; + let Some(conds) = conditions.map(self.arena, |mut cond| { cond.value = self .ecx .unary_op(UnOp::Not, &ImmTy::from_scalar_int(cond.value, layout)) @@ -522,7 +511,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { .to_scalar_int() .discard_err()?; Some(cond) - })?; + }) else { + return; + }; state.insert_value_idx(place, conds, &self.map); } // We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`. @@ -532,34 +523,38 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { box (Operand::Move(place) | Operand::Copy(place), Operand::Constant(value)) | box (Operand::Constant(value), Operand::Move(place) | Operand::Copy(place)), ) => { - let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return Some(()) }; - let Some(place) = self.map.find(place.as_ref()) else { return Some(()) }; + let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return }; + let Some(place) = self.map.find(place.as_ref()) else { return }; let equals = match op { BinOp::Eq => ScalarInt::TRUE, BinOp::Ne => ScalarInt::FALSE, - _ => return Some(()), + _ => return, }; if value.const_.ty().is_floating_point() { // Floating point equality does not follow bit-patterns. // -0.0 and NaN both have special rules for equality, // and therefore we cannot use integer comparisons for them. // Avoid handling them, though this could be extended in the future. - return Some(()); + return; } - let value = value.const_.try_eval_scalar_int(self.tcx, self.typing_env)?; - let conds = conditions.map(self.arena, |c| { + let Some(value) = value.const_.try_eval_scalar_int(self.tcx, self.typing_env) + else { + return; + }; + let Some(conds) = conditions.map(self.arena, |c| { Some(Condition { value, polarity: if c.matches(equals) { Polarity::Eq } else { Polarity::Ne }, ..c }) - })?; + }) else { + return; + }; state.insert_value_idx(place, conds, &self.map); } _ => {} } - Some(()) } #[instrument(level = "trace", skip(self))] @@ -568,7 +563,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { bb: BasicBlock, stmt: &Statement<'tcx>, state: &mut State>, - ) -> Option<()> { + ) { let register_opportunity = |c: Condition| { debug!(?bb, ?c.target, "register"); self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target }) @@ -581,32 +576,30 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { // If we expect `discriminant(place) ?= A`, // we have an opportunity if `variant_index ?= A`. StatementKind::SetDiscriminant { box place, variant_index } => { - let Some(discr_target) = self.map.find_discr(place.as_ref()) else { - return Some(()); - }; + let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return }; let enum_ty = place.ty(self.body, self.tcx).ty; // `SetDiscriminant` guarantees that the discriminant is now `variant_index`. // Even if the discriminant write does nothing due to niches, it is UB to set the // discriminant when the data does not encode the desired discriminant. - let discr = - self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err()?; - self.process_immediate(bb, discr_target, discr, state); + let Some(discr) = + self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err() + else { + return; + }; + self.process_immediate(bb, discr_target, discr, state) } // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`. StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume( Operand::Copy(place) | Operand::Move(place), )) => { - let Some(conditions) = state.try_get(place.as_ref(), &self.map) else { - return Some(()); - }; - conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity); + let Some(conditions) = state.try_get(place.as_ref(), &self.map) else { return }; + conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity) } StatementKind::Assign(box (lhs_place, rhs)) => { - self.process_assign(bb, lhs_place, rhs, state)?; + self.process_assign(bb, lhs_place, rhs, state) } _ => {} } - Some(()) } #[instrument(level = "trace", skip(self, state, cost))] @@ -617,7 +610,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { state: impl FnOnce() -> State>, cost: &CostChecker<'_, 'tcx>, depth: usize, - ) -> Option<()> { + ) { let term = self.body.basic_blocks[bb].terminator(); let place_to_flood = match term.kind { // We come from a target, so those are not possible. @@ -632,9 +625,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Yield { .. } => bug!("{term:?} invalid"), // Cannot reason about inline asm. - TerminatorKind::InlineAsm { .. } => return Some(()), + TerminatorKind::InlineAsm { .. } => return, // `SwitchInt` is handled specially. - TerminatorKind::SwitchInt { .. } => return Some(()), + TerminatorKind::SwitchInt { .. } => return, // We can recurse, no thing particular to do. TerminatorKind::Goto { .. } => None, // Flood the overwritten place, and progress through. From 1f491dccba0d593ba6ba87ae830a59e571b9309f Mon Sep 17 00:00:00 2001 From: Jonathan Gruner Date: Tue, 8 Apr 2025 17:17:21 +0200 Subject: [PATCH 6/9] add next_index to Enumerate --- library/core/src/iter/adapters/enumerate.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/core/src/iter/adapters/enumerate.rs b/library/core/src/iter/adapters/enumerate.rs index bd093e279c38e..1f2cf3244b357 100644 --- a/library/core/src/iter/adapters/enumerate.rs +++ b/library/core/src/iter/adapters/enumerate.rs @@ -23,6 +23,18 @@ impl Enumerate { pub(in crate::iter) fn new(iter: I) -> Enumerate { Enumerate { iter, count: 0 } } + + /// Retrieve the current position of the iterator. + /// + /// If the iterator has not advanced, the position returned will be 0. + /// + /// The position may also exceed the bounds of the iterator to allow for calculating + /// the displacement of the iterator from following calls to [`Iterator::next`]. + #[inline] + #[unstable(feature = "next_index", issue = "130711")] + pub fn next_index(&self) -> usize { + self.count + } } #[stable(feature = "rust1", since = "1.0.0")] From 5a71fabe297ab172eb873a03ab5e057ca55f78db Mon Sep 17 00:00:00 2001 From: Jonathan Gruner Date: Sat, 19 Apr 2025 12:06:30 +0200 Subject: [PATCH 7/9] added doctest for Enumerate::next_index --- library/core/src/iter/adapters/enumerate.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/library/core/src/iter/adapters/enumerate.rs b/library/core/src/iter/adapters/enumerate.rs index 1f2cf3244b357..f7b9f0b7a5e9d 100644 --- a/library/core/src/iter/adapters/enumerate.rs +++ b/library/core/src/iter/adapters/enumerate.rs @@ -30,6 +30,27 @@ impl Enumerate { /// /// The position may also exceed the bounds of the iterator to allow for calculating /// the displacement of the iterator from following calls to [`Iterator::next`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(next_index)] + /// + /// let arr = ['a', 'b']; + /// + /// let mut iter = arr.iter().enumerate(); + /// + /// assert_eq!(iter.next_index(), 0); + /// assert_eq!(iter.next(), Some((0, &'a'))); + /// + /// assert_eq!(iter.next_index(), 1); + /// assert_eq!(iter.next_index(), 1); + /// assert_eq!(iter.next(), Some((1, &'b'))); + /// + /// assert_eq!(iter.next_index(), 2); + /// assert_eq!(iter.next(), None); + /// assert_eq!(iter.next_index(), 2); + /// ``` #[inline] #[unstable(feature = "next_index", issue = "130711")] pub fn next_index(&self) -> usize { From f121c7c3605d195a05a9802735994e3576be9d62 Mon Sep 17 00:00:00 2001 From: Jonathan Gruner Date: Sat, 19 Apr 2025 12:26:57 +0200 Subject: [PATCH 8/9] added test for Enumerate::next_index on empty iterator --- library/coretests/tests/iter/adapters/enumerate.rs | 10 ++++++++++ library/coretests/tests/lib.rs | 1 + 2 files changed, 11 insertions(+) diff --git a/library/coretests/tests/iter/adapters/enumerate.rs b/library/coretests/tests/iter/adapters/enumerate.rs index b57d51c077e9b..2294f856b58d6 100644 --- a/library/coretests/tests/iter/adapters/enumerate.rs +++ b/library/coretests/tests/iter/adapters/enumerate.rs @@ -120,3 +120,13 @@ fn test_double_ended_enumerate() { assert_eq!(it.next_back(), Some((2, 3))); assert_eq!(it.next(), None); } + +#[test] +fn test_empty_iterator_enumerate_next_index() { + let mut it = empty::().enumerate(); + assert_eq!(it.next_index(), 0); + assert_eq!(it.next_index(), 0); + assert_eq!(it.next(), None); + assert_eq!(it.next_index(), 0); + assert_eq!(it.next_index(), 0); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 1c43bfe0ed4a9..ac11157593832 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -63,6 +63,7 @@ #![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(never_type)] +#![feature(next_index)] #![feature(numfmt)] #![feature(pattern)] #![feature(pointer_is_aligned_to)] From 1d87d14cdbbcff685569509ab2b9b06cb605c759 Mon Sep 17 00:00:00 2001 From: apiraino Date: Sat, 19 Apr 2025 14:42:02 +0200 Subject: [PATCH 9/9] Add option for stable backport poll skip-checks: true --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index 226f024c156b7..0f17d022fbb84 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -659,6 +659,7 @@ message_on_add = [ """\ /poll Approve stable backport of #{number}? approve +approve (but does not justify new dot release on its own) decline don't know """,