From 50b922df7831227a32ed7d77ae15e3c1c8c3b636 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 2 Sep 2026 21:53:08 -0700 Subject: [PATCH 1/5] tests --- ...fake-borrow-in-divergent-indexing-chain.rs | 56 ++++++++++++++ .../match/fake-borrow-in-divergent-guard.rs | 74 +++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.rs create mode 100644 tests/ui/match/fake-borrow-in-divergent-guard.rs diff --git a/tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.rs b/tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.rs new file mode 100644 index 0000000000000..5b1d3b4037514 --- /dev/null +++ b/tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.rs @@ -0,0 +1,56 @@ +//! Regression test for : we need to keep fake +//! borrows on indexed-into slice pointers alive for bounds-checks even if the end of the indexing +//! chain is unreachable. This prevents bounds-checks from performing out-of-bounds accesses. +//@ check-pass +// TODO: this should be check-fail + +#![feature(explicit_tail_calls)] + +fn main() { + let mut x: &[&[&[u32]]] = &[&[&[0]]]; + let y: &[&[&[u32]]] = &[]; + x[0][{ x = y; 0 }][{ return; 0 }]; + // TODO: ERROR: cannot assign `x` in indexing expression +} + +// In the following tests, no bounds-checks are reachable after mutating `x`. We keep the fake +// borrow on it alive for consistency, though it isn't necessary for soundness. + +fn always_return_after_mutation() { + let mut x: &[&[&[u32]]] = &[&[&[0]]]; + let y: &[&[&[u32]]] = &[]; + x[0][{ x = y; return; 0 }]; + // TODO: ERROR: cannot assign `x` in indexing expression +} + +fn always_panic_after_mutation() { + let mut x: &[&[&[u32]]] = &[&[&[0]]]; + let y: &[&[&[u32]]] = &[]; + x[0][{ x = y; panic!(); 0 }]; + // TODO: ERROR: cannot assign `x` in indexing expression +} + +fn always_break_after_mutation() { + let mut x: &[&[&[u32]]] = &[&[&[0]]]; + let y: &[&[&[u32]]] = &[]; + 'b: { + x[0][{ x = y; break 'b; 0 }]; + // TODO: ERROR: cannot assign `x` in indexing expression + } +} + +fn always_continue_after_mutation() { + let mut x: &[&[&[u32]]] = &[&[&[0]]]; + let y: &[&[&[u32]]] = &[]; + loop { + x[0][{ x = y; continue; 0 }]; + // TODO: ERROR: cannot assign `x` in indexing expression + } +} + +fn always_become_after_mutation() { + let mut x: &[&[&[u32]]] = &[&[&[0]]]; + let y: &[&[&[u32]]] = &[]; + x[0][{ x = y; become always_become_after_mutation(); 0 }]; + // TODO: ERROR: cannot assign `x` in indexing expression +} diff --git a/tests/ui/match/fake-borrow-in-divergent-guard.rs b/tests/ui/match/fake-borrow-in-divergent-guard.rs new file mode 100644 index 0000000000000..34f59f50526c2 --- /dev/null +++ b/tests/ui/match/fake-borrow-in-divergent-guard.rs @@ -0,0 +1,74 @@ +//! Regression test for : the fake borrow on `x` +//! below was ignored previously because the fake read keeping it live was unreachable. +//@ check-pass +// TODO: this should be check-fail + +#![feature(explicit_tail_calls)] + +// In this first test, it's possible for the guard to fail. We need the fake borrow for soundness. + +fn main() { + let mut x: Option> = Some(Box::new(7)); + match x { + Some(_) if { x = None; false } && return => {} + // TODO: ERROR: cannot assign `x` in match guard + Some(b) => println!("{b}"), + None => println!("none"), + } +} + +// In the following tests, the guard can't fail, but we keep the fake borrow alive for consistency. + +fn always_return_after_mutation() { + let mut x: Option> = Some(Box::new(7)); + match x { + Some(_) if { x = None; return } => {} + // TODO: ERROR: cannot assign `x` in match guard + Some(b) => println!("{b}"), + None => println!("none"), + } +} + +fn always_panic_after_mutation() { + let mut x: Option> = Some(Box::new(7)); + match x { + Some(_) if { x = None; panic!() } => {} + // TODO: ERROR: cannot assign `x` in match guard + Some(b) => println!("{b}"), + None => println!("none"), + } +} + +fn always_break_after_mutation() { + let mut x: Option> = Some(Box::new(7)); + 'b: { + match x { + Some(_) if { x = None; break 'b } => {} + // TODO: ERROR: cannot assign `x` in match guard + Some(b) => println!("{b}"), + None => println!("none"), + } + } +} + +fn always_continue_after_mutation() { + let mut x: Option> = Some(Box::new(7)); + loop { + match x { + Some(_) if { x = None; continue } => {} + // TODO: ERROR: cannot assign `x` in match guard + Some(ref b) => println!("{b}"), + None => println!("none"), + } + } +} + +fn always_become_after_mutation() { + let mut x: Option> = Some(Box::new(7)); + match x { + Some(_) if { x = None; become always_become_after_mutation() } => {} + // TODO: ERROR: cannot assign `x` in match guard + Some(b) => println!("{b}"), + None => println!("none"), + } +} From 56ddbccb4f2b3cf8ac6ca9632224080008a99105 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 2 Sep 2026 22:23:53 -0700 Subject: [PATCH 2/5] factor out logic for statements in drop trees --- compiler/rustc_mir_build/src/builder/scope.rs | 64 +++++++++---------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index c1e028357359f..52db7b9b27440 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -431,49 +431,43 @@ impl DropTree { cfg.terminate(block, drop_node.data.source_info, terminator); } DropKind::ForLint => { - let stmt = Statement::new( - drop_node.data.source_info, - StatementKind::BackwardIncompatibleDropHint { - place: Box::new(drop_node.data.local.into()), - reason: BackwardIncompatibleDropReason::Edition2024, - }, - ); - cfg.push(block, stmt); - let target = blocks[drop_node.next].unwrap(); - if target != block { - // Diagnostics don't use this `Span` but debuginfo - // might. Since we don't want breakpoints to be placed - // here, especially when this is on an unwind path, we - // use `DUMMY_SP`. - let source_info = - SourceInfo { span: DUMMY_SP, ..drop_node.data.source_info }; - let terminator = TerminatorKind::Goto { target }; - cfg.terminate(block, source_info, terminator); - } + let kind = StatementKind::BackwardIncompatibleDropHint { + place: Box::new(drop_node.data.local.into()), + reason: BackwardIncompatibleDropReason::Edition2024, + }; + self.link_statement(cfg, blocks, block, drop_node, kind); } // Root nodes don't correspond to a drop. DropKind::Storage if drop_idx == ROOT_NODE => {} DropKind::Storage => { - let stmt = Statement::new( - drop_node.data.source_info, - StatementKind::StorageDead(drop_node.data.local), - ); - cfg.push(block, stmt); - let target = blocks[drop_node.next].unwrap(); - if target != block { - // Diagnostics don't use this `Span` but debuginfo - // might. Since we don't want breakpoints to be placed - // here, especially when this is on an unwind path, we - // use `DUMMY_SP`. - let source_info = - SourceInfo { span: DUMMY_SP, ..drop_node.data.source_info }; - let terminator = TerminatorKind::Goto { target }; - cfg.terminate(block, source_info, terminator); - } + let kind = StatementKind::StorageDead(drop_node.data.local); + self.link_statement(cfg, blocks, block, drop_node, kind); } } } } + + /// For drops that lower to a statement, adds a goto if the next drop is in a different block. + fn link_statement<'tcx>( + &self, + cfg: &mut CFG<'tcx>, + blocks: &IndexSlice>, + block: BasicBlock, + drop_node: &DropNode, + kind: StatementKind<'tcx>, + ) { + cfg.push(block, Statement::new(drop_node.data.source_info, kind)); + let target = blocks[drop_node.next].unwrap(); + if target != block { + // Diagnostics don't use this `Span` but debuginfo + // might. Since we don't want breakpoints to be placed + // here, especially when this is on an unwind path, we + // use `DUMMY_SP`. + let source_info = SourceInfo { span: DUMMY_SP, ..drop_node.data.source_info }; + let terminator = TerminatorKind::Goto { target }; + cfg.terminate(block, source_info, terminator); + } + } } impl<'tcx> Scopes<'tcx> { From 21d1dcec8cef2bb11a4bfdf4b6d427b677febe73 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 2 Sep 2026 23:39:16 -0700 Subject: [PATCH 3/5] add drop kind for fake reads --- compiler/rustc_middle/src/mir/syntax.rs | 2 +- compiler/rustc_mir_build/src/builder/scope.rs | 73 +++++++++++++++++-- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 4c4a16953d5ed..9f083eb3c0d1f 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -503,7 +503,7 @@ impl WithRetag { } /// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists. -#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, StableHash, PartialEq)] +#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, StableHash, PartialEq, Eq, Hash)] pub enum FakeReadCause { /// A fake read injected into a match guard to ensure that the discriminants /// that are being matched on aren't modified while the match guard is being diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 52db7b9b27440..7d3cd5e9e945f 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -166,6 +166,12 @@ enum DropKind { Value, Storage, ForLint, + /// We fake-read fake borrow temporaries when leaving their scopes, as if dropping them, to keep + /// fake borrows alive through their entire scopes (#161578, #161852). + #[expect(unused)] + FakeRead { + cause: FakeReadCause, + }, } #[derive(Debug)] @@ -265,9 +271,12 @@ impl Scope { /// * freeing up stack space has no effect during unwinding /// Note that for coroutines we do emit StorageDeads, for the /// use of optimizations in the MIR coroutine transform. + /// + /// We add fake reads of fake borrow temporaries on cleanup paths to keep fake borrows alive on + /// paths that unconditionally panic. These are removed after borrowck. fn needs_cleanup(&self) -> bool { self.drops.iter().any(|drop| match drop.kind { - DropKind::Value | DropKind::ForLint => true, + DropKind::Value | DropKind::ForLint | DropKind::FakeRead { .. } => true, DropKind::Storage => false, }) } @@ -437,6 +446,11 @@ impl DropTree { }; self.link_statement(cfg, blocks, block, drop_node, kind); } + DropKind::FakeRead { cause } => { + let kind = + StatementKind::FakeRead(Box::new((cause, drop_node.data.local.into()))); + self.link_statement(cfg, blocks, block, drop_node, kind); + } // Root nodes don't correspond to a drop. DropKind::Storage if drop_idx == ROOT_NODE => {} DropKind::Storage => { @@ -1122,7 +1136,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = drop_data.source_info; let local = drop_data.local; - if !self.local_decls[local].ty.needs_drop(self.tcx, typing_env) { + if !self.local_decls[local].ty.needs_drop(self.tcx, typing_env) + && !matches!(drop_data.kind, DropKind::FakeRead { .. }) + { continue; } @@ -1176,6 +1192,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ), ); } + DropKind::FakeRead { cause } => { + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.local, + drop_data.local + ); + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.kind, + drop_data.kind + ); + unwind_to = unwind_drops.drop_nodes[unwind_to].next; + self.cfg.push_fake_read(block, source_info, cause, local.into()); + } DropKind::Storage => { // Only temps and vars need their storage dead. assert!(local.index() > self.arg_count); @@ -1439,7 +1467,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // the unwind or coroutine drop paths. This means that for // non-coroutines we don't need to invalidate caches for `DropKind::Storage`. let invalidate_caches = match drop_kind { - DropKind::Value | DropKind::ForLint => true, + DropKind::Value | DropKind::ForLint | DropKind::FakeRead { .. } => true, DropKind::Storage => self.coroutine.is_some(), }; for scope in self.scopes.scopes.iter_mut().rev() { @@ -1512,6 +1540,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.schedule_drop(span, region_scope, local, DropKind::ForLint); } + /// Schedule a fake read. Used to keep fake borrows alive until their scopes end. + #[expect(unused)] + pub(crate) fn schedule_drop_fake_read( + &mut self, + span: Span, + region_scope: region::Scope, + local: Local, + cause: FakeReadCause, + ) { + self.schedule_drop(span, region_scope, local, DropKind::FakeRead { cause }); + } + /// Indicates that the "local operand" stored in `local` is /// *moved* at some point during execution (see `local_scope` for /// more information about what a "local operand" is -- in short, @@ -1604,7 +1644,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let is_coroutine = self.coroutine.is_some(); for scope in &mut self.scopes.scopes[uncached_scope..=target] { for drop in &scope.drops { - if is_coroutine || drop.kind == DropKind::Value { + if is_coroutine || matches!(drop.kind, DropKind::Value | DropKind::FakeRead { .. }) + { cached_drop = self.scopes.unwind_drops.add_drop(*drop, cached_drop); } } @@ -1950,6 +1991,21 @@ where assert!(local.index() > arg_count); cfg.push(block, Statement::new(source_info, StatementKind::StorageDead(local))); } + DropKind::FakeRead { cause } => { + // We always emit fake reads on unwind to keep fake borrows alive on paths that + // unconditionally panic. + debug_assert_eq!(unwind_drops.drop_nodes[unwind_to].data.local, drop_data.local); + debug_assert_eq!(unwind_drops.drop_nodes[unwind_to].data.kind, drop_data.kind); + unwind_to = unwind_drops.drop_nodes[unwind_to].next; + + if let Some(idx) = dropline_to { + debug_assert_eq!(coroutine_drops.drop_nodes[idx].data.local, drop_data.local); + debug_assert_eq!(coroutine_drops.drop_nodes[idx].data.kind, drop_data.kind); + dropline_to = Some(coroutine_drops.drop_nodes[idx].next); + } + + cfg.push_fake_read(block, source_info, cause, local.into()); + } } } block.unit() @@ -1987,6 +2043,13 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { unwind_indices.push(unwind_indices[drop_node.next]); } } + DropKind::FakeRead { .. } => { + let unwind_drop = self + .scopes + .unwind_drops + .add_drop(drop_node.data, unwind_indices[drop_node.next]); + unwind_indices.push(unwind_drop); + } DropKind::Value => { let unwind_drop = self .scopes @@ -2015,7 +2078,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { .coroutine_drops .add_drop(drop_data.data, dropline_indices[drop_data.next]); match drop_data.data.kind { - DropKind::Storage | DropKind::ForLint => {} + DropKind::Storage | DropKind::ForLint | DropKind::FakeRead { .. } => {} DropKind::Value => { if self.is_async_drop(drop_data.data.local) { self.scopes.coroutine_drops.add_entry_point( From 08c3d89a27c414e1090d9a8c7539a91064005a2e Mon Sep 17 00:00:00 2001 From: dianne Date: Thu, 3 Sep 2026 05:48:05 -0700 Subject: [PATCH 4/5] schedule fake reads for matches' fake borrows --- compiler/rustc_middle/src/middle/region.rs | 5 ++ .../src/builder/matches/mod.rs | 50 ++++++++++-------- compiler/rustc_mir_build/src/builder/scope.rs | 2 - ...se_edges.full_tested_match.built.after.mir | 2 + ...e_edges.full_tested_match2.built.after.mir | 2 + .../match_false_edges.main.built.after.mir | 3 ++ ....constant_eq.SimplifyCfg-initial.after.mir | 7 ++- ...joint_ranges.SimplifyCfg-initial.after.mir | 1 + ...oo-{closure#0}-{closure#0}.built.after.mir | 4 +- ...-{closure#0}-{synthetic#0}.built.after.mir | 4 +- ...fg-initial.after-ElaborateDrops.after.diff | 10 +++- ...fg-initial.after-ElaborateDrops.after.diff | 10 +++- ...guard.CleanupPostBorrowck.panic-abort.diff | 14 +++-- ...uard.CleanupPostBorrowck.panic-unwind.diff | 14 +++-- .../match/fake-borrow-in-divergent-guard.rs | 14 +++-- .../fake-borrow-in-divergent-guard.stderr | 51 +++++++++++++++++++ 16 files changed, 148 insertions(+), 45 deletions(-) create mode 100644 tests/ui/match/fake-borrow-in-divergent-guard.stderr diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 0ab469174d5f7..c47faa1fd8b97 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -97,6 +97,7 @@ impl fmt::Debug for Scope { ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.local_id), ScopeData::IfThenRescope => write!(fmt, "IfThen[edition2024]({:?})", self.local_id), ScopeData::MatchGuard => write!(fmt, "MatchGuard({:?})", self.local_id), + ScopeData::MatchFakeBorrows => write!(fmt, "MatchFakeBorrows({:?})", self.local_id), ScopeData::Remainder(fsi) => write!( fmt, "Remainder {{ block: {:?}, first_statement_index: {}}}", @@ -137,6 +138,9 @@ pub enum ScopeData { /// whose lifetimes do not cross beyond this scope. MatchGuard, + /// Dummy scope used for matches' fake borrow temporaries. + MatchFakeBorrows, + /// Scope following a `let id = expr;` binding in a block. Remainder(FirstStatementIndex), } @@ -323,6 +327,7 @@ impl ScopeTree { | ScopeData::CallSite | ScopeData::Arguments | ScopeData::IfThen + | ScopeData::MatchFakeBorrows | ScopeData::Remainder(_) => { // If we haven't already passed through a backwards-incompatible node, // then check if we are passing through one now and record it if so. diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index a520acda5e6c8..67589ec13d79c 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -14,7 +14,7 @@ use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node}; -use rustc_middle::middle::region::{self, TempLifetime}; +use rustc_middle::middle::region::{self, ScopeData, TempLifetime}; use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind}; @@ -2432,21 +2432,35 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow); } - let mut guard_span = rustc_span::DUMMY_SP; + let guard_span = self.thir[guard].span; + let source_info = self.source_info(guard_span); - let (guard_true_block, guard_false_block) = - self.in_if_then_scope(match_scope, guard_span, |this| { - guard_span = this.thir[guard].span; - this.lower_if_condition( - block, - guard, - LowerIfCondArgs { - temp_scope_override: None, // Use `this.local_scope()`. - variable_source_info: this.source_info(arm.span), - // For guards, `let` bindings are declared separately. - declare_let_bindings: DeclareLetBindings::No, - }, - ) + let fake_borrow_scope = region::Scope { + data: ScopeData::MatchFakeBorrows, + local_id: self.thir[guard].temp_scope_id, + }; + let BlockAnd(guard_true_block, guard_false_block) = + self.in_scope((fake_borrow_scope, source_info), LintLevel::Inherited, |this| { + // Schedule fake reads on guard exit to keep fake borrows alive on every path. + let cause = FakeReadCause::ForMatchGuard; + for &(_, temp, _) in fake_borrows { + this.schedule_drop_fake_read(guard_span, fake_borrow_scope, temp, cause); + } + + let (guard_true_block, guard_false_block) = + this.in_if_then_scope(match_scope, guard_span, |this| { + this.lower_if_condition( + block, + guard, + LowerIfCondArgs { + temp_scope_override: None, // Use `this.local_scope()`. + variable_source_info: this.source_info(arm.span), + // For guards, `let` bindings are declared separately. + declare_let_bindings: DeclareLetBindings::No, + }, + ) + }); + guard_true_block.and(guard_false_block) }); // If this isn't the final sub-branch being lowered, we need to unschedule drops of @@ -2456,16 +2470,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.clear_match_arm_and_guard_scopes(arm.scope); } - let source_info = self.source_info(guard_span); let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span)); let guard_frame = self.guard_context.pop().unwrap(); debug!("Exiting guard building context with locals: {:?}", guard_frame); - for &(_, temp, _) in fake_borrows { - let cause = FakeReadCause::ForMatchGuard; - self.cfg.push_fake_read(guard_true_block, guard_end, cause, Place::from(temp)); - } - self.cfg.goto(guard_false_block, source_info, sub_branch.otherwise_block); // We want to ensure that the matched candidates are bound diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 7d3cd5e9e945f..daaa083d1c60a 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -168,7 +168,6 @@ enum DropKind { ForLint, /// We fake-read fake borrow temporaries when leaving their scopes, as if dropping them, to keep /// fake borrows alive through their entire scopes (#161578, #161852). - #[expect(unused)] FakeRead { cause: FakeReadCause, }, @@ -1541,7 +1540,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Schedule a fake read. Used to keep fake borrows alive until their scopes end. - #[expect(unused)] pub(crate) fn schedule_drop_fake_read( &mut self, span: Span, diff --git a/tests/mir-opt/building/match/match_false_edges.full_tested_match.built.after.mir b/tests/mir-opt/building/match/match_false_edges.full_tested_match.built.after.mir index 4b0cdcfbb8662..57f6116e3bff6 100644 --- a/tests/mir-opt/building/match/match_false_edges.full_tested_match.built.after.mir +++ b/tests/mir-opt/building/match/match_false_edges.full_tested_match.built.after.mir @@ -105,6 +105,7 @@ fn full_tested_match() -> () { bb13: { StorageDead(_7); + FakeRead(ForMatchGuard, _3); StorageDead(_6); goto -> bb9; } @@ -123,6 +124,7 @@ fn full_tested_match() -> () { } bb16 (cleanup): { + FakeRead(ForMatchGuard, _3); resume; } } diff --git a/tests/mir-opt/building/match/match_false_edges.full_tested_match2.built.after.mir b/tests/mir-opt/building/match/match_false_edges.full_tested_match2.built.after.mir index 63ec71fdf5133..e6fd49448801d 100644 --- a/tests/mir-opt/building/match/match_false_edges.full_tested_match2.built.after.mir +++ b/tests/mir-opt/building/match/match_false_edges.full_tested_match2.built.after.mir @@ -105,6 +105,7 @@ fn full_tested_match2() -> () { bb13: { StorageDead(_7); + FakeRead(ForMatchGuard, _3); StorageDead(_6); goto -> bb9; } @@ -123,6 +124,7 @@ fn full_tested_match2() -> () { } bb16 (cleanup): { + FakeRead(ForMatchGuard, _3); resume; } } diff --git a/tests/mir-opt/building/match/match_false_edges.main.built.after.mir b/tests/mir-opt/building/match/match_false_edges.main.built.after.mir index 3b10adb499cd5..bded027a968ef 100644 --- a/tests/mir-opt/building/match/match_false_edges.main.built.after.mir +++ b/tests/mir-opt/building/match/match_false_edges.main.built.after.mir @@ -136,6 +136,7 @@ fn main() -> () { bb17: { StorageDead(_8); + FakeRead(ForMatchGuard, _3); StorageDead(_7); goto -> bb13; } @@ -164,6 +165,7 @@ fn main() -> () { bb21: { StorageDead(_13); StorageDead(_12); + FakeRead(ForMatchGuard, _3); StorageDead(_11); goto -> bb10; } @@ -182,6 +184,7 @@ fn main() -> () { } bb24 (cleanup): { + FakeRead(ForMatchGuard, _3); resume; } } diff --git a/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir index 4d13d087586e7..8a58d7dbe2cb3 100644 --- a/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir @@ -102,15 +102,18 @@ fn constant_eq(_1: &str, _2: bool) -> u32 { bb16: { StorageDead(_13); - FakeRead(ForMatchGuard, _6); - FakeRead(ForMatchGuard, _7); FakeRead(ForMatchGuard, _8); + FakeRead(ForMatchGuard, _7); + FakeRead(ForMatchGuard, _6); _0 = const 1_u32; goto -> bb18; } bb17: { StorageDead(_13); + FakeRead(ForMatchGuard, _8); + FakeRead(ForMatchGuard, _7); + FakeRead(ForMatchGuard, _6); falseEdge -> [real: bb3, imaginary: bb5]; } diff --git a/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir index d625168081435..b2bab9be79af8 100644 --- a/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir @@ -79,6 +79,7 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 { bb13: { StorageDead(_8); + FakeRead(ForMatchGuard, _3); falseEdge -> [real: bb1, imaginary: bb3]; } diff --git a/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir b/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir index 4c9ca11f82834..8cfd17ca8a9e4 100644 --- a/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir +++ b/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir @@ -47,8 +47,8 @@ yields () bb7: { StorageDead(_6); - FakeRead(ForMatchGuard, _3); FakeRead(ForMatchGuard, _4); + FakeRead(ForMatchGuard, _3); _0 = const (); goto -> bb10; } @@ -59,6 +59,8 @@ yields () bb9: { StorageDead(_6); + FakeRead(ForMatchGuard, _4); + FakeRead(ForMatchGuard, _3); goto -> bb6; } diff --git a/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir b/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir index e80fdea7051dc..8691045c72f32 100644 --- a/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir +++ b/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir @@ -35,14 +35,16 @@ yields () bb4: { StorageDead(_6); - FakeRead(ForMatchGuard, _3); FakeRead(ForMatchGuard, _4); + FakeRead(ForMatchGuard, _3); _0 = const (); goto -> bb6; } bb5: { StorageDead(_6); + FakeRead(ForMatchGuard, _4); + FakeRead(ForMatchGuard, _3); falseEdge -> [real: bb1, imaginary: bb1]; } diff --git a/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff b/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff index 3b5b917af5c75..8f2ab9c49b4f0 100644 --- a/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff +++ b/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff @@ -134,8 +134,8 @@ + bb10: { StorageDead(_10); StorageDead(_9); -- FakeRead(ForMatchGuard, _3); - FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); - FakeRead(ForGuardBinding, _6); - FakeRead(ForGuardBinding, _8); StorageLive(_5); @@ -150,6 +150,8 @@ + bb11: { StorageDead(_10); StorageDead(_9); +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); StorageDead(_8); StorageDead(_6); - falseEdge -> [real: bb3, imaginary: bb3]; @@ -176,8 +178,8 @@ + bb14: { StorageDead(_13); StorageDead(_12); -- FakeRead(ForMatchGuard, _3); - FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); - FakeRead(ForGuardBinding, _6); - FakeRead(ForGuardBinding, _8); StorageLive(_5); @@ -192,6 +194,8 @@ + bb15: { StorageDead(_13); StorageDead(_12); +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); StorageDead(_8); StorageDead(_6); - falseEdge -> [real: bb1, imaginary: bb1]; @@ -230,6 +234,8 @@ } - bb23: { +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); + bb20: { StorageDead(_8); StorageDead(_6); diff --git a/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff b/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff index 3b5b917af5c75..8f2ab9c49b4f0 100644 --- a/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff +++ b/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff @@ -134,8 +134,8 @@ + bb10: { StorageDead(_10); StorageDead(_9); -- FakeRead(ForMatchGuard, _3); - FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); - FakeRead(ForGuardBinding, _6); - FakeRead(ForGuardBinding, _8); StorageLive(_5); @@ -150,6 +150,8 @@ + bb11: { StorageDead(_10); StorageDead(_9); +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); StorageDead(_8); StorageDead(_6); - falseEdge -> [real: bb3, imaginary: bb3]; @@ -176,8 +178,8 @@ + bb14: { StorageDead(_13); StorageDead(_12); -- FakeRead(ForMatchGuard, _3); - FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); - FakeRead(ForGuardBinding, _6); - FakeRead(ForGuardBinding, _8); StorageLive(_5); @@ -192,6 +194,8 @@ + bb15: { StorageDead(_13); StorageDead(_12); +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); StorageDead(_8); StorageDead(_6); - falseEdge -> [real: bb1, imaginary: bb1]; @@ -230,6 +234,8 @@ } - bb23: { +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); + bb20: { StorageDead(_8); StorageDead(_6); diff --git a/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-abort.diff b/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-abort.diff index 8c6c5e0d99349..3cee3566f628d 100644 --- a/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-abort.diff +++ b/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-abort.diff @@ -48,10 +48,10 @@ bb5: { StorageDead(_8); -- FakeRead(ForMatchGuard, _3); -- FakeRead(ForMatchGuard, _4); -- FakeRead(ForMatchGuard, _5); - FakeRead(ForMatchGuard, _6); +- FakeRead(ForMatchGuard, _5); +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); + nop; + nop; + nop; @@ -62,7 +62,15 @@ bb6: { StorageDead(_8); +- FakeRead(ForMatchGuard, _6); +- FakeRead(ForMatchGuard, _5); +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); - falseEdge -> [real: bb1, imaginary: bb1]; ++ nop; ++ nop; ++ nop; ++ nop; + goto -> bb1; } diff --git a/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-unwind.diff b/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-unwind.diff index 8c6c5e0d99349..3cee3566f628d 100644 --- a/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-unwind.diff +++ b/tests/mir-opt/remove_fake_borrows.match_guard.CleanupPostBorrowck.panic-unwind.diff @@ -48,10 +48,10 @@ bb5: { StorageDead(_8); -- FakeRead(ForMatchGuard, _3); -- FakeRead(ForMatchGuard, _4); -- FakeRead(ForMatchGuard, _5); - FakeRead(ForMatchGuard, _6); +- FakeRead(ForMatchGuard, _5); +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); + nop; + nop; + nop; @@ -62,7 +62,15 @@ bb6: { StorageDead(_8); +- FakeRead(ForMatchGuard, _6); +- FakeRead(ForMatchGuard, _5); +- FakeRead(ForMatchGuard, _4); +- FakeRead(ForMatchGuard, _3); - falseEdge -> [real: bb1, imaginary: bb1]; ++ nop; ++ nop; ++ nop; ++ nop; + goto -> bb1; } diff --git a/tests/ui/match/fake-borrow-in-divergent-guard.rs b/tests/ui/match/fake-borrow-in-divergent-guard.rs index 34f59f50526c2..b3ba1637b9214 100644 --- a/tests/ui/match/fake-borrow-in-divergent-guard.rs +++ b/tests/ui/match/fake-borrow-in-divergent-guard.rs @@ -1,7 +1,5 @@ //! Regression test for : the fake borrow on `x` //! below was ignored previously because the fake read keeping it live was unreachable. -//@ check-pass -// TODO: this should be check-fail #![feature(explicit_tail_calls)] @@ -11,7 +9,7 @@ fn main() { let mut x: Option> = Some(Box::new(7)); match x { Some(_) if { x = None; false } && return => {} - // TODO: ERROR: cannot assign `x` in match guard + //~^ ERROR: cannot assign `x` in match guard Some(b) => println!("{b}"), None => println!("none"), } @@ -23,7 +21,7 @@ fn always_return_after_mutation() { let mut x: Option> = Some(Box::new(7)); match x { Some(_) if { x = None; return } => {} - // TODO: ERROR: cannot assign `x` in match guard + //~^ ERROR: cannot assign `x` in match guard Some(b) => println!("{b}"), None => println!("none"), } @@ -33,7 +31,7 @@ fn always_panic_after_mutation() { let mut x: Option> = Some(Box::new(7)); match x { Some(_) if { x = None; panic!() } => {} - // TODO: ERROR: cannot assign `x` in match guard + //~^ ERROR: cannot assign `x` in match guard Some(b) => println!("{b}"), None => println!("none"), } @@ -44,7 +42,7 @@ fn always_break_after_mutation() { 'b: { match x { Some(_) if { x = None; break 'b } => {} - // TODO: ERROR: cannot assign `x` in match guard + //~^ ERROR: cannot assign `x` in match guard Some(b) => println!("{b}"), None => println!("none"), } @@ -56,7 +54,7 @@ fn always_continue_after_mutation() { loop { match x { Some(_) if { x = None; continue } => {} - // TODO: ERROR: cannot assign `x` in match guard + //~^ ERROR: cannot assign `x` in match guard Some(ref b) => println!("{b}"), None => println!("none"), } @@ -67,7 +65,7 @@ fn always_become_after_mutation() { let mut x: Option> = Some(Box::new(7)); match x { Some(_) if { x = None; become always_become_after_mutation() } => {} - // TODO: ERROR: cannot assign `x` in match guard + //~^ ERROR: cannot assign `x` in match guard Some(b) => println!("{b}"), None => println!("none"), } diff --git a/tests/ui/match/fake-borrow-in-divergent-guard.stderr b/tests/ui/match/fake-borrow-in-divergent-guard.stderr new file mode 100644 index 0000000000000..66980b3c66b85 --- /dev/null +++ b/tests/ui/match/fake-borrow-in-divergent-guard.stderr @@ -0,0 +1,51 @@ +error[E0510]: cannot assign `x` in match guard + --> $DIR/fake-borrow-in-divergent-guard.rs:11:22 + | +LL | match x { + | - value is immutable in match guard +LL | Some(_) if { x = None; false } && return => {} + | ^ cannot assign + +error[E0510]: cannot assign `x` in match guard + --> $DIR/fake-borrow-in-divergent-guard.rs:23:22 + | +LL | match x { + | - value is immutable in match guard +LL | Some(_) if { x = None; return } => {} + | ^ cannot assign + +error[E0510]: cannot assign `x` in match guard + --> $DIR/fake-borrow-in-divergent-guard.rs:33:22 + | +LL | match x { + | - value is immutable in match guard +LL | Some(_) if { x = None; panic!() } => {} + | ^ cannot assign + +error[E0510]: cannot assign `x` in match guard + --> $DIR/fake-borrow-in-divergent-guard.rs:44:26 + | +LL | match x { + | - value is immutable in match guard +LL | Some(_) if { x = None; break 'b } => {} + | ^ cannot assign + +error[E0510]: cannot assign `x` in match guard + --> $DIR/fake-borrow-in-divergent-guard.rs:56:26 + | +LL | match x { + | - value is immutable in match guard +LL | Some(_) if { x = None; continue } => {} + | ^ cannot assign + +error[E0510]: cannot assign `x` in match guard + --> $DIR/fake-borrow-in-divergent-guard.rs:67:22 + | +LL | match x { + | - value is immutable in match guard +LL | Some(_) if { x = None; become always_become_after_mutation() } => {} + | ^ cannot assign + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0510`. From 472c1451030a5350cc4319dead0c3d03a2df76ea Mon Sep 17 00:00:00 2001 From: dianne Date: Thu, 3 Sep 2026 06:08:10 -0700 Subject: [PATCH 5/5] schedule fake reads for indexings' fake borrows --- .../src/builder/expr/as_place.rs | 55 ++++++++----------- ...fake-borrow-in-divergent-indexing-chain.rs | 14 ++--- ...-borrow-in-divergent-indexing-chain.stderr | 51 +++++++++++++++++ 3 files changed, 80 insertions(+), 40 deletions(-) create mode 100644 tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.stderr diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 839387c65be70..6737116a31a01 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -5,6 +5,7 @@ use std::{assert_matches, iter}; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_hir::def_id::LocalDefId; use rustc_middle::hir::place::{Projection as HirProjection, ProjectionKind as HirProjectionKind}; +use rustc_middle::middle::region; use rustc_middle::mir::AssertKind::BoundsCheck; use rustc_middle::mir::*; use rustc_middle::thir::*; @@ -419,7 +420,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mut block: BasicBlock, expr_id: ExprId, mutability: Mutability, - fake_borrow_temps: Option<&mut Vec>, + fake_borrow_scope: Option, ) -> BlockAnd> { let expr = &self.thir[expr_id]; debug!("expr_as_place(block={:?}, expr={:?}, mutability={:?})", block, expr, mutability); @@ -431,13 +432,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Scope { region_scope, hir_id, value } => { this.in_scope((region_scope, source_info), LintLevel::Explicit(hir_id), |this| { this.push_coverage_point_for_expr(block, source_info, hir_id); - this.expr_as_place(block, value, mutability, fake_borrow_temps) + this.expr_as_place(block, value, mutability, fake_borrow_scope) }) } ExprKind::Field { lhs, variant_index, name } => { let lhs_expr = &this.thir[lhs]; let mut place_builder = - unpack!(block = this.expr_as_place(block, lhs, mutability, fake_borrow_temps,)); + unpack!(block = this.expr_as_place(block, lhs, mutability, fake_borrow_scope,)); if let ty::Adt(adt_def, _) = lhs_expr.ty.kind() { if adt_def.is_enum() { place_builder = place_builder.downcast(*adt_def, variant_index); @@ -447,7 +448,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ExprKind::Deref { arg } => { let place_builder = - unpack!(block = this.expr_as_place(block, arg, mutability, fake_borrow_temps,)); + unpack!(block = this.expr_as_place(block, arg, mutability, fake_borrow_scope,)); block.and(place_builder.deref()) } ExprKind::Index { lhs, index } => this.lower_index_expression( @@ -455,7 +456,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { lhs, index, mutability, - fake_borrow_temps, + fake_borrow_scope, expr_span, source_info, ), @@ -476,7 +477,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::PlaceTypeAscription { source, ref user_ty, user_ty_span } => { let place_builder = unpack!( - block = this.expr_as_place(block, source, mutability, fake_borrow_temps,) + block = this.expr_as_place(block, source, mutability, fake_borrow_scope,) ); if let Some(user_ty) = user_ty { let ty_source_info = this.source_info(user_ty_span); @@ -535,7 +536,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::PlaceUnwrapUnsafeBinder { source } => { let place_builder = unpack!( - block = this.expr_as_place(block, source, mutability, fake_borrow_temps,) + block = this.expr_as_place(block, source, mutability, fake_borrow_scope,) ); block.and(place_builder.project(PlaceElem::UnwrapUnsafeBinder(expr.ty))) } @@ -625,16 +626,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { base: ExprId, index: ExprId, mutability: Mutability, - fake_borrow_temps: Option<&mut Vec>, + fake_borrow_scope: Option, expr_span: Span, source_info: SourceInfo, ) -> BlockAnd> { - let base_fake_borrow_temps = &mut Vec::new(); - let is_outermost_index = fake_borrow_temps.is_none(); - let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps); + let is_outermost_index = fake_borrow_scope.is_none(); + let fake_borrow_scope = fake_borrow_scope.unwrap_or_else(|| self.local_scope()); let base_place = - unpack!(block = self.expr_as_place(block, base, mutability, Some(fake_borrow_temps),)); + unpack!(block = self.expr_as_place(block, base, mutability, Some(fake_borrow_scope),)); // Making this a *fresh* temporary means we do not have to worry about // the index changing later: Nothing will ever change this temporary. @@ -647,13 +647,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block = self.bounds_check(block, &base_place, idx, expr_span, source_info); - if is_outermost_index { - self.read_fake_borrows(block, fake_borrow_temps, source_info) - } else { + if !is_outermost_index { self.add_fake_borrows_of_base( base_place.to_place(self), block, - fake_borrow_temps, + fake_borrow_scope, expr_span, source_info, ); @@ -760,7 +758,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, base_place: Place<'tcx>, block: BasicBlock, - fake_borrow_temps: &mut Vec, + fake_borrow_scope: region::Scope, expr_span: Span, source_info: SourceInfo, ) { @@ -791,7 +789,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Place { local: base_place.local, projection }, ), ); - fake_borrow_temps.push(fake_borrow_temp); + // Schedule a fake read to keep the fake borrow alive until exiting + // `fake_borrow_scope`. + self.schedule_drop_fake_read( + expr_span, + fake_borrow_scope, + fake_borrow_temp, + FakeReadCause::ForIndex, + ); } ProjectionElem::Index(_) => { let index_ty = base_place.ty(&self.local_decls, tcx); @@ -814,20 +819,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } } - - fn read_fake_borrows( - &mut self, - bb: BasicBlock, - fake_borrow_temps: &mut Vec, - source_info: SourceInfo, - ) { - // All indexes have been evaluated now, read all of the - // fake borrows so that they are live across those index - // expressions. - for temp in fake_borrow_temps { - self.cfg.push_fake_read(bb, source_info, FakeReadCause::ForIndex, Place::from(*temp)); - } - } } /// Precise capture is enabled if user is using Rust Edition 2021 or higher. diff --git a/tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.rs b/tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.rs index 5b1d3b4037514..b2882c2df1404 100644 --- a/tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.rs +++ b/tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.rs @@ -1,8 +1,6 @@ //! Regression test for : we need to keep fake //! borrows on indexed-into slice pointers alive for bounds-checks even if the end of the indexing //! chain is unreachable. This prevents bounds-checks from performing out-of-bounds accesses. -//@ check-pass -// TODO: this should be check-fail #![feature(explicit_tail_calls)] @@ -10,7 +8,7 @@ fn main() { let mut x: &[&[&[u32]]] = &[&[&[0]]]; let y: &[&[&[u32]]] = &[]; x[0][{ x = y; 0 }][{ return; 0 }]; - // TODO: ERROR: cannot assign `x` in indexing expression + //~^ ERROR: cannot assign `x` in indexing expression } // In the following tests, no bounds-checks are reachable after mutating `x`. We keep the fake @@ -20,14 +18,14 @@ fn always_return_after_mutation() { let mut x: &[&[&[u32]]] = &[&[&[0]]]; let y: &[&[&[u32]]] = &[]; x[0][{ x = y; return; 0 }]; - // TODO: ERROR: cannot assign `x` in indexing expression + //~^ ERROR: cannot assign `x` in indexing expression } fn always_panic_after_mutation() { let mut x: &[&[&[u32]]] = &[&[&[0]]]; let y: &[&[&[u32]]] = &[]; x[0][{ x = y; panic!(); 0 }]; - // TODO: ERROR: cannot assign `x` in indexing expression + //~^ ERROR: cannot assign `x` in indexing expression } fn always_break_after_mutation() { @@ -35,7 +33,7 @@ fn always_break_after_mutation() { let y: &[&[&[u32]]] = &[]; 'b: { x[0][{ x = y; break 'b; 0 }]; - // TODO: ERROR: cannot assign `x` in indexing expression + //~^ ERROR: cannot assign `x` in indexing expression } } @@ -44,7 +42,7 @@ fn always_continue_after_mutation() { let y: &[&[&[u32]]] = &[]; loop { x[0][{ x = y; continue; 0 }]; - // TODO: ERROR: cannot assign `x` in indexing expression + //~^ ERROR: cannot assign `x` in indexing expression } } @@ -52,5 +50,5 @@ fn always_become_after_mutation() { let mut x: &[&[&[u32]]] = &[&[&[0]]]; let y: &[&[&[u32]]] = &[]; x[0][{ x = y; become always_become_after_mutation(); 0 }]; - // TODO: ERROR: cannot assign `x` in indexing expression + //~^ ERROR: cannot assign `x` in indexing expression } diff --git a/tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.stderr b/tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.stderr new file mode 100644 index 0000000000000..73da77cf54219 --- /dev/null +++ b/tests/ui/indexing/fake-borrow-in-divergent-indexing-chain.stderr @@ -0,0 +1,51 @@ +error[E0510]: cannot assign `x` in indexing expression + --> $DIR/fake-borrow-in-divergent-indexing-chain.rs:10:12 + | +LL | x[0][{ x = y; 0 }][{ return; 0 }]; + | ---- ^^^^^ cannot assign + | | + | value is immutable in indexing expression + +error[E0510]: cannot assign `x` in indexing expression + --> $DIR/fake-borrow-in-divergent-indexing-chain.rs:20:12 + | +LL | x[0][{ x = y; return; 0 }]; + | ---- ^^^^^ cannot assign + | | + | value is immutable in indexing expression + +error[E0510]: cannot assign `x` in indexing expression + --> $DIR/fake-borrow-in-divergent-indexing-chain.rs:27:12 + | +LL | x[0][{ x = y; panic!(); 0 }]; + | ---- ^^^^^ cannot assign + | | + | value is immutable in indexing expression + +error[E0510]: cannot assign `x` in indexing expression + --> $DIR/fake-borrow-in-divergent-indexing-chain.rs:35:16 + | +LL | x[0][{ x = y; break 'b; 0 }]; + | ---- ^^^^^ cannot assign + | | + | value is immutable in indexing expression + +error[E0510]: cannot assign `x` in indexing expression + --> $DIR/fake-borrow-in-divergent-indexing-chain.rs:44:16 + | +LL | x[0][{ x = y; continue; 0 }]; + | ---- ^^^^^ cannot assign + | | + | value is immutable in indexing expression + +error[E0510]: cannot assign `x` in indexing expression + --> $DIR/fake-borrow-in-divergent-indexing-chain.rs:52:12 + | +LL | x[0][{ x = y; become always_become_after_mutation(); 0 }]; + | ---- ^^^^^ cannot assign + | | + | value is immutable in indexing expression + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0510`.