diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index b10df60e0f75b..1118aad5ef3d9 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -256,16 +256,21 @@ struct DropNodeKey { impl Scope { /// Whether there's anything to do for the cleanup path, that is, - /// when unwinding through this scope. This includes destructors, - /// but not StorageDead statements, which don't get emitted at all - /// for unwinding, for several reasons: - /// * clang doesn't emit llvm.lifetime.end for C++ unwinding - /// * LLVM's memory dependency analysis can't handle it atm - /// * polluting the cleanup MIR with StorageDead creates - /// landing pads even though there's no actual destructors - /// * 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. + /// when unwinding through this scope. This includes destructors + /// (Value and ForLint drops). StorageDead drops are not included + /// here because they don't require cleanup blocks. + /// + /// StorageDead statements are only needed for borrow-checking consistency: + /// they ensure the borrow-checker treats locals as dead at the same point on + /// all paths (normal and unwind). We only include StorageDead in cleanup blocks + /// when there are also Value or ForLint drops present, because: + /// - StorageDead is only relevant for borrow-checking when there are destructors + /// that might reference the dead variable + /// - We don't create cleanup blocks with only StorageDead (no destructors to run) + /// - StorageDead in cleanup blocks is removed by `CleanupPostBorrowck` after + /// borrow-checking, so it doesn't affect codegen + /// - StorageDead on normal paths may be used by codegen backends and by the + /// coroutine-to-state-machine transform, so it's preserved there fn needs_cleanup(&self) -> bool { self.drops.iter().any(|drop| match drop.kind { DropKind::Value | DropKind::ForLint => true, @@ -1124,6 +1129,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); let typing_env = self.typing_env(); let unwind_drops = &mut self.scopes.unwind_drops; + let has_storage_drops = self.scopes.scopes[1..] + .iter() + .any(|scope| scope.drops.iter().any(|d| d.kind == DropKind::Storage)); // the innermost scope contains only the destructors for the tail call arguments // we only want to drop these in case of a panic, so we skip it @@ -1133,7 +1141,11 @@ 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) { + // Skip Value drops for types that don't need drop, but process + // StorageDead and ForLint drops for all locals + if drop_data.kind == DropKind::Value + && !self.local_decls[local].ty.needs_drop(self.tcx, typing_env) + { continue; } @@ -1142,15 +1154,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // `unwind_to` should drop the value that we're about to // schedule. If dropping this value panics, then we continue // with the *next* value on the unwind path. - 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; + // Only adjust if the drop matches what unwind_to is pointing to + // (since we process drops in reverse order, unwind_to might not + // match the current drop if there are StorageDead or ForLint drops + // between Value drops). + if unwind_to != DropIdx::MAX + && unwind_drops.drop_nodes[unwind_to].data.local == drop_data.local + && unwind_drops.drop_nodes[unwind_to].data.kind == drop_data.kind + { + unwind_to = unwind_drops.drop_nodes[unwind_to].next; + } let mut unwind_entry_point = unwind_to; @@ -1177,6 +1190,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block = next; } DropKind::ForLint => { + // If this ForLint drop is in unwind_drops, we need to adjust + // unwind_to to match, just like in build_scope_drops. + // Only adjust if the drop matches what unwind_to is pointing to + // (since we process drops in reverse order, unwind_to might not + // match the current drop). + if has_storage_drops + && unwind_to != DropIdx::MAX + && unwind_drops.drop_nodes[unwind_to].data.local == drop_data.local + && unwind_drops.drop_nodes[unwind_to].data.kind == drop_data.kind + { + unwind_to = unwind_drops.drop_nodes[unwind_to].next; + } self.cfg.push( block, Statement::new( @@ -1189,6 +1214,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); } DropKind::Storage => { + // If this StorageDead drop is in unwind_drops, we need to adjust + // unwind_to to match, just like in build_scope_drops. + // Only adjust if the drop matches what unwind_to is pointing to + // (since we process drops in reverse order, unwind_to might not + // match the current drop). + if has_storage_drops + && unwind_to != DropIdx::MAX + && unwind_drops.drop_nodes[unwind_to].data.local == drop_data.local + && unwind_drops.drop_nodes[unwind_to].data.kind == drop_data.kind + { + unwind_to = unwind_drops.drop_nodes[unwind_to].next; + } // Only temps and vars need their storage dead. assert!(local.index() > self.arg_count); self.cfg.push( @@ -1224,6 +1261,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // diverge cleanup pads ready in case that drop panics. let needs_cleanup = self.scopes.scopes.last().is_some_and(|scope| scope.needs_cleanup()); let is_coroutine = self.coroutine.is_some(); + // `unwind_to` tracks the position in the unwind drop tree, not just whether cleanup + // is needed. It's used by `build_scope_drops` to correctly position drops in the + // unwind tree. We use `needs_cleanup` to determine if we need to initialize it. let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX }; let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes"); @@ -1240,7 +1280,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, unwind_to, dropline_to, - is_coroutine && needs_cleanup, + needs_cleanup, self.arg_count, |v: Local| Self::is_async_drop_impl(self.tcx, &self.local_decls, typing_env, v), ) @@ -1624,9 +1664,41 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } let is_coroutine = self.coroutine.is_some(); + // Check if there are Value or ForLint drops present. We only add StorageDead drops + // when there are Value/ForLint drops, since StorageDead is only relevant for + // borrow-checking when there are destructors that might reference the dead variable. + let has_value_or_forlint_drops = + self.scopes.scopes[uncached_scope..=target].iter().any(|scope| scope.needs_cleanup()); + for scope in &mut self.scopes.scopes[uncached_scope..=target] { for drop in &scope.drops { - if is_coroutine || drop.kind == DropKind::Value { + // We add drops to unwind_drops to ensure the borrow-checker treats locals as dead + // at the same point on all paths (normal and unwind). This is for static semantics + // (borrow-checking), not runtime drop order. + // + // For coroutines, we always add all drops (including StorageDead) because the + // coroutine-to-state-machine transform (which runs after CleanupPostBorrowck) needs + // StorageDead statements to determine storage liveness at suspension points. + // + // For other functions, we add: + // - Value drops (always, as they require cleanup - running destructors) + // - ForLint drops (for future-compatibility linting, though they don't run code) + // - StorageDead drops (when there are Value or ForLint drops present) + // + // Note: ForLint drops don't actually run code - they turn into + // `BackwardIncompatibleDropHint` statements for future-compatibility linting. + // They don't require cleanup, but we include them for more precise linting. + // + // These StorageDead statements in cleanup blocks are removed by the + // `CleanupPostBorrowck` MIR transform pass (for non-coroutines only), and empty + // cleanup blocks are removed by `RemoveNoopLandingPads`, so they don't affect codegen. + // This is codegen backend agnostic. They only affect static analysis (borrow-checking). + let should_add = is_coroutine + || drop.kind == DropKind::Value + || drop.kind == DropKind::ForLint + || (drop.kind == DropKind::Storage && has_value_or_forlint_drops); + + if should_add { cached_drop = self.scopes.unwind_drops.add_drop(*drop, cached_drop); } } @@ -1808,11 +1880,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// * `scope`, describes the drops that will occur on exiting the scope in regular execution /// * `block`, the block to branch to once drops are complete (assuming no unwind occurs) /// * `unwind_to`, describes the drops that would occur at this point in the code if a -/// panic occurred (a subset of the drops in `scope`, since we sometimes elide StorageDead and other -/// instructions on unwinding) +/// panic occurred (a subset of the drops in `scope`) /// * `dropline_to`, describes the drops that would occur at this point in the code if a /// coroutine drop occurred. -/// * `storage_dead_on_unwind`, if true, then we should emit `StorageDead` even when unwinding +/// * `needs_cleanup`, if true, indicates there's a cleanup block (i.e., Value or ForLint drops +/// that require cleanup). When true and there are StorageDead drops, we emit `StorageDead` on +/// the unwind path and adjust `unwind_to` accordingly. /// * `arg_count`, number of MIR local variables corresponding to fn arguments (used to assert that we don't drop those) fn build_scope_drops<'tcx, F>( cfg: &mut CFG<'tcx>, @@ -1822,7 +1895,7 @@ fn build_scope_drops<'tcx, F>( block: BasicBlock, unwind_to: DropIdx, dropline_to: Option, - storage_dead_on_unwind: bool, + needs_cleanup: bool, arg_count: usize, is_async_drop: F, ) -> BlockAnd<()> @@ -1831,6 +1904,12 @@ where { debug!("build_scope_drops({:?} -> {:?}), dropline_to={:?}", block, scope, dropline_to); + // Compute whether we should emit StorageDead on unwind paths. + // We only emit StorageDead on unwind paths when there's a cleanup block (needs_cleanup) + // and there are StorageDead drops in the scope. + let has_storage_drops = scope.drops.iter().any(|d| d.kind == DropKind::Storage); + let storage_dead_on_unwind = has_storage_drops && needs_cleanup; + // Build up the drops in evaluation order. The end result will // look like: // @@ -1845,10 +1924,14 @@ where // drops panic (panicking while unwinding will abort, so there's no need for // another set of arrows). // - // For coroutines, we unwind from a drop on a local to its StorageDead - // statement. For other functions we don't worry about StorageDead. The - // drops for the unwind path should have already been generated by - // `diverge_cleanup_gen`. + // We unwind from a drop on a local to its StorageDead statement for all + // functions (not just coroutines) to ensure the borrow-checker treats locals + // as dead at the same point on all paths. This is for static semantics + // (borrow-checking), not runtime drop order. The drops for the unwind path + // should have already been generated by `diverge_cleanup_gen`. + // + // Note: StorageDead statements in cleanup blocks are removed by the `CleanupPostBorrowck` MIR + // transform pass, so they don't affect codegen. // `unwind_to` indicates what needs to be dropped should unwinding occur. // This is a subset of what needs to be dropped when exiting the scope. @@ -1877,9 +1960,34 @@ where // // We adjust this BEFORE we create the drop (e.g., `drops[n]`) // because `drops[n]` should unwind to `drops[n-1]`. - 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; + // + // Since we process drops in reverse order and the unwind tree may contain + // StorageDead/ForLint drops interleaved with Value drops, `unwind_to` might + // not initially point to the current Value drop. We skip over non-matching + // drops in the unwind tree until we find the matching Value drop. + if unwind_to != DropIdx::MAX { + // Skip over any non-matching drops (e.g., StorageDead/ForLint) until we + // find the matching Value drop in the unwind tree. + while unwind_to != DropIdx::MAX + && (unwind_drops.drop_nodes[unwind_to].data.local != drop_data.local + || unwind_drops.drop_nodes[unwind_to].data.kind != drop_data.kind) + { + unwind_to = unwind_drops.drop_nodes[unwind_to].next; + } + // Now `unwind_to` should point to the matching Value drop, or be MAX if + // we've reached the end of the unwind chain. + if unwind_to != DropIdx::MAX { + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.local, drop_data.local, + "unwind_to should point to the current Value drop" + ); + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.kind, drop_data.kind, + "unwind_to should point to the current Value drop" + ); + 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); @@ -1895,7 +2003,11 @@ where continue; } - unwind_drops.add_entry_point(block, unwind_to); + // Only add an entry point if there are more drops in the unwind path. + // `DropIdx::MAX` indicates the end of the unwind drop chain. + if unwind_to != DropIdx::MAX { + unwind_drops.add_entry_point(block, unwind_to); + } if let Some(to) = dropline_to && is_async_drop(local) { @@ -1919,17 +2031,35 @@ where } DropKind::ForLint => { // As in the `DropKind::Storage` case below: - // normally lint-related drops are not emitted for unwind, - // so we can just leave `unwind_to` unmodified, but in some - // cases we emit things ALSO on the unwind path, so we need to adjust - // `unwind_to` in that case. - if storage_dead_on_unwind { - 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; + // we emit lint-related drops on the unwind path when `storage_dead_on_unwind` + // is true, so we need to adjust `unwind_to` in that case. + // + // Since we process drops in reverse order and the unwind tree may contain + // StorageDead/Value drops interleaved with ForLint drops, `unwind_to` might + // not initially point to the current ForLint drop. We skip over non-matching + // drops in the unwind tree until we find the matching ForLint drop. + if storage_dead_on_unwind && unwind_to != DropIdx::MAX { + // Skip over any non-matching drops (e.g., StorageDead/Value) until we + // find the matching ForLint drop in the unwind tree. + while unwind_to != DropIdx::MAX + && (unwind_drops.drop_nodes[unwind_to].data.local != drop_data.local + || unwind_drops.drop_nodes[unwind_to].data.kind != drop_data.kind) + { + unwind_to = unwind_drops.drop_nodes[unwind_to].next; + } + // Now `unwind_to` should point to the matching ForLint drop, or be MAX if + // we've reached the end of the unwind chain. + if unwind_to != DropIdx::MAX { + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.local, drop_data.local, + "unwind_to should point to the current ForLint drop" + ); + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.kind, drop_data.kind, + "unwind_to should point to the current ForLint drop" + ); + unwind_to = unwind_drops.drop_nodes[unwind_to].next; + } } // If the operand has been moved, and we are not on an unwind @@ -1952,17 +2082,28 @@ where ); } DropKind::Storage => { - // Ordinarily, storage-dead nodes are not emitted on unwind, so we don't - // need to adjust `unwind_to` on this path. However, in some specific cases - // we *do* emit storage-dead nodes on the unwind path, and in that case now that - // the storage-dead has completed, we need to adjust the `unwind_to` pointer - // so that any future drops we emit will not register storage-dead. - if storage_dead_on_unwind { - 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); + // We emit StorageDead statements on the unwind path to ensure the borrow-checker + // treats locals as dead at the same point on all paths. This is for static semantics + // (borrow-checking), not runtime drop order. StorageDead is only needed when there + // are Value or ForLint drops present, because: + // 1. StorageDead is only relevant for borrow-checking when there are destructors + // that might reference the dead variable + // 2. If there are no drops, we don't need a cleanup block (unwinding still occurs, + // but there are no destructors to run before popping the stack frame) + // + // These StorageDead statements are removed by the `CleanupPostBorrowck` MIR + // transform pass, so they don't affect codegen. + // + // When `storage_dead_on_unwind` is true, we need to adjust the `unwind_to` pointer + // now that the storage-dead has completed, so that any future drops we emit will + // not register storage-dead. + // Only adjust if the drop matches what unwind_to is pointing to (since we process + // drops in reverse order, unwind_to might not match the current drop). + if storage_dead_on_unwind + && unwind_to != DropIdx::MAX + && unwind_drops.drop_nodes[unwind_to].data.local == drop_data.local + && 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 { @@ -1997,30 +2138,52 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { // Link the exit drop tree to unwind drop tree. if drops.drop_nodes.iter().any(|drop_node| drop_node.data.kind == DropKind::Value) { let unwind_target = self.diverge_cleanup_target(else_scope, span); + + // Check if there are Value or ForLint drops present. We only add StorageDead drops + // when there are Value/ForLint drops, since StorageDead is only relevant for + // borrow-checking when there are destructors that might reference the dead variable. + let has_value_or_forlint_drops = drops.drop_nodes.iter().any(|drop_node| { + drop_node.data.kind == DropKind::Value || drop_node.data.kind == DropKind::ForLint + }); + let mut unwind_indices = IndexVec::from_elem_n(unwind_target, 1); for (drop_idx, drop_node) in drops.drop_nodes.iter_enumerated().skip(1) { - match drop_node.data.kind { - DropKind::Storage | DropKind::ForLint => { - if is_coroutine { + // For coroutines, we always add all drops (including StorageDead) because the + // coroutine-to-state-machine transform needs StorageDead statements. + // + // For other functions, we add: + // - Value drops (always, as they require cleanup - running destructors) + // - ForLint drops (for future-compatibility linting, though they don't run code) + // - StorageDead drops (when there are Value or ForLint drops present) + // + // These StorageDead statements in cleanup blocks are removed by the + // `CleanupPostBorrowck` MIR transform pass (for non-coroutines only), and empty + // cleanup blocks are removed by `RemoveNoopLandingPads`, so they don't affect codegen. + let should_add = is_coroutine + || drop_node.data.kind == DropKind::Value + || drop_node.data.kind == DropKind::ForLint + || (drop_node.data.kind == DropKind::Storage && has_value_or_forlint_drops); + + if should_add { + match drop_node.data.kind { + DropKind::Storage | DropKind::ForLint => { let unwind_drop = self .scopes .unwind_drops .add_drop(drop_node.data, unwind_indices[drop_node.next]); unwind_indices.push(unwind_drop); - } else { - unwind_indices.push(unwind_indices[drop_node.next]); } - } - DropKind::Value => { - let unwind_drop = self - .scopes - .unwind_drops - .add_drop(drop_node.data, unwind_indices[drop_node.next]); - self.scopes.unwind_drops.add_entry_point( - blocks[drop_idx].unwrap(), - unwind_indices[drop_node.next], - ); - unwind_indices.push(unwind_drop); + DropKind::Value => { + let unwind_drop = self + .scopes + .unwind_drops + .add_drop(drop_node.data, unwind_indices[drop_node.next]); + self.scopes.unwind_drops.add_entry_point( + blocks[drop_idx].unwrap(), + unwind_indices[drop_node.next], + ); + unwind_indices.push(unwind_drop); + } } } } diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index e6f56b509af63..6f15087cb8c32 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -6,6 +6,10 @@ //! - [`FakeRead`] //! - [`Assign`] statements with a [`Fake`] borrow //! - [`Coverage`] statements of kind [`BlockMarker`] or [`SpanMarker`] +//! - [`StorageDead`] statements in cleanup blocks (unwind paths) - these are only needed +//! for borrow-checking and are removed from cleanup blocks after borrowck completes. +//! StorageDead on normal paths may be needed by later passes (e.g., coroutine transforms) +//! and will be conditionally removed by RemoveStorageMarkers during optimization if enabled. //! //! [`AscribeUserType`]: rustc_middle::mir::StatementKind::AscribeUserType //! [`Assign`]: rustc_middle::mir::StatementKind::Assign @@ -15,6 +19,7 @@ //! [`Coverage`]: rustc_middle::mir::StatementKind::Coverage //! [`BlockMarker`]: rustc_middle::mir::coverage::CoverageKind::BlockMarker //! [`SpanMarker`]: rustc_middle::mir::coverage::CoverageKind::SpanMarker +//! [`StorageDead`]: rustc_middle::mir::StatementKind::StorageDead use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::*; @@ -28,6 +33,11 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck { // Manually invalidate CFG caches if we actually change a terminator's edges. let mut invalidate_cfg = false; for basic_block in body.basic_blocks.as_mut_preserves_cfg().iter_mut() { + // Only remove StorageDead from cleanup blocks (unwind paths). + // StorageDead on normal paths may be needed by later passes (e.g., coroutine + // transforms) and may be used by codegen backends. RemoveStorageMarkers will + // conditionally remove them during optimization if enabled (when mir_opt_level > 0 + // and lifetime markers are not being emitted). for statement in basic_block.statements.iter_mut() { match statement.kind { StatementKind::AscribeUserType(..) @@ -41,6 +51,11 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck { | StatementKind::BackwardIncompatibleDropHint { .. } => { statement.make_nop(true) } + StatementKind::StorageDead(..) + if basic_block.is_cleanup && body.coroutine.is_none() => + { + statement.make_nop(true) + } StatementKind::Assign(box ( _, Rvalue::Cast( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index 6c5d226cac1bf..2da7aa911e7f2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -426,11 +426,10 @@ pub fn search_dependencies( let import_maps: Vec<_> = krate.data(db).dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect(); - let mut op = fst::map::OpBuilder::new(); - match query.search_mode { SearchMode::Exact => { let automaton = fst::automaton::Str::new(&query.lowercased); + let mut op = fst::map::OpBuilder::new(); for map in &import_maps { op = op.add(map.fst.search(&automaton)); @@ -439,6 +438,7 @@ pub fn search_dependencies( } SearchMode::Fuzzy => { let automaton = fst::automaton::Subsequence::new(&query.lowercased); + let mut op = fst::map::OpBuilder::new(); for map in &import_maps { op = op.add(map.fst.search(&automaton)); @@ -447,6 +447,7 @@ pub fn search_dependencies( } SearchMode::Prefix => { let automaton = fst::automaton::Str::new(&query.lowercased).starts_with(); + let mut op = fst::map::OpBuilder::new(); for map in &import_maps { op = op.add(map.fst.search(&automaton)); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index 183f6b6495375..68a0789e47218 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -569,11 +569,10 @@ impl Query { cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow, ) -> Option { let _p = tracing::info_span!("symbol_index::Query::search").entered(); - - let mut op = fst::map::OpBuilder::new(); match self.mode { SearchMode::Exact => { let automaton = fst::automaton::Str::new(&self.lowercased); + let mut op = fst::map::OpBuilder::new(); for index in indices.iter() { op = op.add(index.map.search(&automaton)); @@ -582,6 +581,7 @@ impl Query { } SearchMode::Fuzzy => { let automaton = fst::automaton::Subsequence::new(&self.lowercased); + let mut op = fst::map::OpBuilder::new(); for index in indices.iter() { op = op.add(index.map.search(&automaton)); @@ -590,6 +590,7 @@ impl Query { } SearchMode::Prefix => { let automaton = fst::automaton::Str::new(&self.lowercased).starts_with(); + let mut op = fst::map::OpBuilder::new(); for index in indices.iter() { op = op.add(index.map.search(&automaton)); diff --git a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir index 1dc1d08136290..3d47e948a8da6 100644 --- a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir +++ b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir @@ -44,7 +44,6 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) } bb3 (cleanup): { - nop; nop; goto -> bb5; } diff --git a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir index aa7d75242b408..4943fb2ccb099 100644 --- a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir @@ -73,14 +73,19 @@ fn main() -> () { } bb6 (cleanup): { + StorageDead(_6); drop(_5) -> [return: bb7, unwind terminate(cleanup)]; } bb7 (cleanup): { + StorageDead(_5); drop(_4) -> [return: bb8, unwind terminate(cleanup)]; } bb8 (cleanup): { + StorageDead(_4); + StorageDead(_2); + StorageDead(_1); resume; } } diff --git a/tests/mir-opt/building/logical_or_in_conditional.test_complex.built.after.mir b/tests/mir-opt/building/logical_or_in_conditional.test_complex.built.after.mir index 327b3b618a03a..357f97cf4ab51 100644 --- a/tests/mir-opt/building/logical_or_in_conditional.test_complex.built.after.mir +++ b/tests/mir-opt/building/logical_or_in_conditional.test_complex.built.after.mir @@ -19,7 +19,7 @@ fn test_complex() -> () { bb0: { StorageLive(_1); StorageLive(_2); - _2 = E::f() -> [return: bb1, unwind: bb35]; + _2 = E::f() -> [return: bb1, unwind: bb38]; } bb1: { @@ -42,7 +42,7 @@ fn test_complex() -> () { bb5: { StorageLive(_4); - _4 = always_true() -> [return: bb6, unwind: bb35]; + _4 = always_true() -> [return: bb6, unwind: bb38]; } bb6: { @@ -64,7 +64,7 @@ fn test_complex() -> () { } bb9: { - drop(_7) -> [return: bb11, unwind: bb35]; + drop(_7) -> [return: bb11, unwind: bb37]; } bb10: { @@ -78,7 +78,7 @@ fn test_complex() -> () { } bb12: { - drop(_7) -> [return: bb13, unwind: bb35]; + drop(_7) -> [return: bb13, unwind: bb37]; } bb13: { @@ -98,7 +98,7 @@ fn test_complex() -> () { } bb15: { - drop(_10) -> [return: bb17, unwind: bb35]; + drop(_10) -> [return: bb17, unwind: bb36]; } bb16: { @@ -139,7 +139,7 @@ fn test_complex() -> () { StorageDead(_4); StorageDead(_1); StorageLive(_11); - _11 = always_true() -> [return: bb23, unwind: bb35]; + _11 = always_true() -> [return: bb23, unwind: bb38]; } bb23: { @@ -156,7 +156,7 @@ fn test_complex() -> () { bb26: { StorageLive(_12); - _12 = E::f() -> [return: bb27, unwind: bb35]; + _12 = E::f() -> [return: bb27, unwind: bb38]; } bb27: { @@ -199,6 +199,25 @@ fn test_complex() -> () { } bb35 (cleanup): { + StorageDead(_10); + StorageDead(_9); + StorageDead(_2); + goto -> bb38; + } + + bb36 (cleanup): { + StorageDead(_10); + StorageDead(_9); + goto -> bb38; + } + + bb37 (cleanup): { + StorageDead(_7); + StorageDead(_6); + goto -> bb38; + } + + bb38 (cleanup): { resume; } } diff --git a/tests/mir-opt/building/logical_or_in_conditional.test_or.built.after.mir b/tests/mir-opt/building/logical_or_in_conditional.test_or.built.after.mir index 2fc19e7e0fdcd..7aa8dd0a147b7 100644 --- a/tests/mir-opt/building/logical_or_in_conditional.test_or.built.after.mir +++ b/tests/mir-opt/building/logical_or_in_conditional.test_or.built.after.mir @@ -20,7 +20,7 @@ fn test_or() -> () { } bb1: { - drop(_3) -> [return: bb3, unwind: bb13]; + drop(_3) -> [return: bb3, unwind: bb14]; } bb2: { @@ -34,7 +34,7 @@ fn test_or() -> () { } bb4: { - drop(_3) -> [return: bb5, unwind: bb13]; + drop(_3) -> [return: bb5, unwind: bb14]; } bb5: { @@ -86,6 +86,19 @@ fn test_or() -> () { } bb13 (cleanup): { + StorageDead(_6); + StorageDead(_5); + goto -> bb15; + } + + bb14 (cleanup): { + StorageDead(_3); + StorageDead(_2); + goto -> bb15; + } + + bb15 (cleanup): { + StorageDead(_1); resume; } } diff --git a/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir b/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir index 8d16f074b1e34..d0c78d8ccdd4f 100644 --- a/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir +++ b/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir @@ -43,7 +43,7 @@ fn const_array_len(_1: [T; 5]) -> () { StorageLive(_6); StorageLive(_7); _7 = move _2; - _6 = opaque::(move _7) -> [return: bb3, unwind: bb17]; + _6 = opaque::(move _7) -> [return: bb3, unwind: bb20]; } bb3: { @@ -52,7 +52,7 @@ fn const_array_len(_1: [T; 5]) -> () { StorageLive(_8); StorageLive(_9); _9 = move _3; - _8 = opaque::(move _9) -> [return: bb4, unwind: bb16]; + _8 = opaque::(move _9) -> [return: bb4, unwind: bb18]; } bb4: { @@ -61,7 +61,7 @@ fn const_array_len(_1: [T; 5]) -> () { StorageLive(_10); StorageLive(_11); _11 = move _4; - _10 = opaque::<[T; 2]>(move _11) -> [return: bb5, unwind: bb15]; + _10 = opaque::<[T; 2]>(move _11) -> [return: bb5, unwind: bb16]; } bb5: { @@ -77,7 +77,7 @@ fn const_array_len(_1: [T; 5]) -> () { StorageDead(_13); StorageDead(_12); _0 = const (); - drop(_5) -> [return: bb8, unwind: bb19]; + drop(_5) -> [return: bb8, unwind: bb23]; } bb7: { @@ -87,17 +87,17 @@ fn const_array_len(_1: [T; 5]) -> () { bb8: { StorageDead(_5); - drop(_4) -> [return: bb9, unwind: bb20]; + drop(_4) -> [return: bb9, unwind: bb24]; } bb9: { StorageDead(_4); - drop(_3) -> [return: bb10, unwind: bb21]; + drop(_3) -> [return: bb10, unwind: bb25]; } bb10: { StorageDead(_3); - drop(_2) -> [return: bb11, unwind: bb22]; + drop(_2) -> [return: bb11, unwind: bb26]; } bb11: { @@ -106,7 +106,7 @@ fn const_array_len(_1: [T; 5]) -> () { } bb12: { - drop(_1) -> [return: bb13, unwind: bb23]; + drop(_1) -> [return: bb13, unwind: bb27]; } bb13: { @@ -114,42 +114,70 @@ fn const_array_len(_1: [T; 5]) -> () { } bb14 (cleanup): { - drop(_13) -> [return: bb18, unwind terminate(cleanup)]; + drop(_13) -> [return: bb15, unwind terminate(cleanup)]; } bb15 (cleanup): { - drop(_11) -> [return: bb18, unwind terminate(cleanup)]; + StorageDead(_13); + StorageDead(_12); + goto -> bb22; } bb16 (cleanup): { - drop(_9) -> [return: bb18, unwind terminate(cleanup)]; + drop(_11) -> [return: bb17, unwind terminate(cleanup)]; } bb17 (cleanup): { - drop(_7) -> [return: bb18, unwind terminate(cleanup)]; + StorageDead(_11); + StorageDead(_10); + goto -> bb22; } bb18 (cleanup): { - drop(_5) -> [return: bb19, unwind terminate(cleanup)]; + drop(_9) -> [return: bb19, unwind terminate(cleanup)]; } bb19 (cleanup): { - drop(_4) -> [return: bb20, unwind terminate(cleanup)]; + StorageDead(_9); + StorageDead(_8); + goto -> bb22; } bb20 (cleanup): { - drop(_3) -> [return: bb21, unwind terminate(cleanup)]; + drop(_7) -> [return: bb21, unwind terminate(cleanup)]; } bb21 (cleanup): { - drop(_2) -> [return: bb22, unwind terminate(cleanup)]; + StorageDead(_7); + StorageDead(_6); + goto -> bb22; } bb22 (cleanup): { - drop(_1) -> [return: bb23, unwind terminate(cleanup)]; + drop(_5) -> [return: bb23, unwind terminate(cleanup)]; } bb23 (cleanup): { + StorageDead(_5); + drop(_4) -> [return: bb24, unwind terminate(cleanup)]; + } + + bb24 (cleanup): { + StorageDead(_4); + drop(_3) -> [return: bb25, unwind terminate(cleanup)]; + } + + bb25 (cleanup): { + StorageDead(_3); + drop(_2) -> [return: bb26, unwind terminate(cleanup)]; + } + + bb26 (cleanup): { + StorageDead(_2); + drop(_1) -> [return: bb27, unwind terminate(cleanup)]; + } + + bb27 (cleanup): { resume; } } diff --git a/tests/mir-opt/building/storage_dead_unwind_path.rs b/tests/mir-opt/building/storage_dead_unwind_path.rs new file mode 100644 index 0000000000000..db73f374efbee --- /dev/null +++ b/tests/mir-opt/building/storage_dead_unwind_path.rs @@ -0,0 +1,29 @@ +//@ compile-flags: -Zmir-opt-level=0 +// skip-filecheck +// Test that StorageDead statements are emitted on unwind paths for borrow-checking. +// This ensures the borrow-checker treats locals as dead at the same point on all paths. + +// EMIT_MIR storage_dead_unwind_path.test.built.after.mir + +struct Droppable; + +impl Drop for Droppable { + fn drop(&mut self) {} +} + +fn test() { + // StorageDead drop (non-drop type) + let x = 42i32; + // Value drop (drop type) + let y = Droppable; + // Function call that might panic - if it does, we should see StorageDead(x) in cleanup + may_panic(); +} + +fn may_panic() { + // This function might panic, triggering unwind +} + +fn main() { + test(); +} diff --git a/tests/mir-opt/building/storage_dead_unwind_path.test.built.after.mir b/tests/mir-opt/building/storage_dead_unwind_path.test.built.after.mir new file mode 100644 index 0000000000000..82e0a5343cae2 --- /dev/null +++ b/tests/mir-opt/building/storage_dead_unwind_path.test.built.after.mir @@ -0,0 +1,48 @@ +// MIR for `test` after built + +fn test() -> () { + let mut _0: (); + let _1: i32; + let _3: (); + scope 1 { + debug x => _1; + let _2: Droppable; + scope 2 { + debug y => _2; + } + } + + bb0: { + StorageLive(_1); + _1 = const 42_i32; + FakeRead(ForLet(None), _1); + StorageLive(_2); + _2 = Droppable; + FakeRead(ForLet(None), _2); + StorageLive(_3); + _3 = may_panic() -> [return: bb1, unwind: bb3]; + } + + bb1: { + StorageDead(_3); + _0 = const (); + drop(_2) -> [return: bb2, unwind: bb4]; + } + + bb2: { + StorageDead(_2); + StorageDead(_1); + return; + } + + bb3 (cleanup): { + StorageDead(_3); + drop(_2) -> [return: bb4, unwind terminate(cleanup)]; + } + + bb4 (cleanup): { + StorageDead(_2); + StorageDead(_1); + resume; + } +} diff --git a/tests/mir-opt/building/uniform_array_move_out.move_out_by_subslice.built.after.mir b/tests/mir-opt/building/uniform_array_move_out.move_out_by_subslice.built.after.mir index 839bdeca86a88..86bffa91f0ad1 100644 --- a/tests/mir-opt/building/uniform_array_move_out.move_out_by_subslice.built.after.mir +++ b/tests/mir-opt/building/uniform_array_move_out.move_out_by_subslice.built.after.mir @@ -20,7 +20,7 @@ fn move_out_by_subslice() -> () { bb0: { StorageLive(_1); StorageLive(_2); - _3 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb1, unwind: bb13]; + _3 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb1, unwind: bb17]; } bb1: { @@ -28,13 +28,13 @@ fn move_out_by_subslice() -> () { _4 = ShallowInitBox(move _3, i32); (*_4) = const 1_i32; _2 = move _4; - drop(_4) -> [return: bb2, unwind: bb12]; + drop(_4) -> [return: bb2, unwind: bb14]; } bb2: { StorageDead(_4); StorageLive(_5); - _6 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb3, unwind: bb12]; + _6 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb3, unwind: bb15]; } bb3: { @@ -42,18 +42,18 @@ fn move_out_by_subslice() -> () { _7 = ShallowInitBox(move _6, i32); (*_7) = const 2_i32; _5 = move _7; - drop(_7) -> [return: bb4, unwind: bb11]; + drop(_7) -> [return: bb4, unwind: bb12]; } bb4: { StorageDead(_7); _1 = [move _2, move _5]; - drop(_5) -> [return: bb5, unwind: bb12]; + drop(_5) -> [return: bb5, unwind: bb13]; } bb5: { StorageDead(_5); - drop(_2) -> [return: bb6, unwind: bb13]; + drop(_2) -> [return: bb6, unwind: bb16]; } bb6: { @@ -73,7 +73,7 @@ fn move_out_by_subslice() -> () { bb8: { StorageDead(_8); - drop(_1) -> [return: bb9, unwind: bb13]; + drop(_1) -> [return: bb9, unwind: bb11]; } bb9: { @@ -82,18 +82,40 @@ fn move_out_by_subslice() -> () { } bb10 (cleanup): { - drop(_1) -> [return: bb13, unwind terminate(cleanup)]; + StorageDead(_8); + drop(_1) -> [return: bb11, unwind terminate(cleanup)]; } bb11 (cleanup): { - drop(_5) -> [return: bb12, unwind terminate(cleanup)]; + StorageDead(_1); + goto -> bb17; } bb12 (cleanup): { - drop(_2) -> [return: bb13, unwind terminate(cleanup)]; + StorageDead(_7); + drop(_5) -> [return: bb13, unwind terminate(cleanup)]; } bb13 (cleanup): { + StorageDead(_5); + goto -> bb15; + } + + bb14 (cleanup): { + StorageDead(_4); + goto -> bb15; + } + + bb15 (cleanup): { + drop(_2) -> [return: bb16, unwind terminate(cleanup)]; + } + + bb16 (cleanup): { + StorageDead(_2); + goto -> bb17; + } + + bb17 (cleanup): { resume; } } diff --git a/tests/mir-opt/building/uniform_array_move_out.move_out_from_end.built.after.mir b/tests/mir-opt/building/uniform_array_move_out.move_out_from_end.built.after.mir index 7fda69c7500a3..27cbe3a21af60 100644 --- a/tests/mir-opt/building/uniform_array_move_out.move_out_from_end.built.after.mir +++ b/tests/mir-opt/building/uniform_array_move_out.move_out_from_end.built.after.mir @@ -20,7 +20,7 @@ fn move_out_from_end() -> () { bb0: { StorageLive(_1); StorageLive(_2); - _3 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb1, unwind: bb13]; + _3 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb1, unwind: bb17]; } bb1: { @@ -28,13 +28,13 @@ fn move_out_from_end() -> () { _4 = ShallowInitBox(move _3, i32); (*_4) = const 1_i32; _2 = move _4; - drop(_4) -> [return: bb2, unwind: bb12]; + drop(_4) -> [return: bb2, unwind: bb14]; } bb2: { StorageDead(_4); StorageLive(_5); - _6 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb3, unwind: bb12]; + _6 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb3, unwind: bb15]; } bb3: { @@ -42,18 +42,18 @@ fn move_out_from_end() -> () { _7 = ShallowInitBox(move _6, i32); (*_7) = const 2_i32; _5 = move _7; - drop(_7) -> [return: bb4, unwind: bb11]; + drop(_7) -> [return: bb4, unwind: bb12]; } bb4: { StorageDead(_7); _1 = [move _2, move _5]; - drop(_5) -> [return: bb5, unwind: bb12]; + drop(_5) -> [return: bb5, unwind: bb13]; } bb5: { StorageDead(_5); - drop(_2) -> [return: bb6, unwind: bb13]; + drop(_2) -> [return: bb6, unwind: bb16]; } bb6: { @@ -73,7 +73,7 @@ fn move_out_from_end() -> () { bb8: { StorageDead(_8); - drop(_1) -> [return: bb9, unwind: bb13]; + drop(_1) -> [return: bb9, unwind: bb11]; } bb9: { @@ -82,18 +82,40 @@ fn move_out_from_end() -> () { } bb10 (cleanup): { - drop(_1) -> [return: bb13, unwind terminate(cleanup)]; + StorageDead(_8); + drop(_1) -> [return: bb11, unwind terminate(cleanup)]; } bb11 (cleanup): { - drop(_5) -> [return: bb12, unwind terminate(cleanup)]; + StorageDead(_1); + goto -> bb17; } bb12 (cleanup): { - drop(_2) -> [return: bb13, unwind terminate(cleanup)]; + StorageDead(_7); + drop(_5) -> [return: bb13, unwind terminate(cleanup)]; } bb13 (cleanup): { + StorageDead(_5); + goto -> bb15; + } + + bb14 (cleanup): { + StorageDead(_4); + goto -> bb15; + } + + bb15 (cleanup): { + drop(_2) -> [return: bb16, unwind terminate(cleanup)]; + } + + bb16 (cleanup): { + StorageDead(_2); + goto -> bb17; + } + + bb17 (cleanup): { resume; } } diff --git a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir b/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir index 69e7219af9ff8..e010b63ba315b 100644 --- a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir +++ b/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir @@ -37,7 +37,6 @@ fn main::{closure#0}(_1: *mut {coroutine@$DIR/coroutine_drop_cleanup.rs:12:5: 12 } bb5 (cleanup): { - nop; goto -> bb4; } diff --git a/tests/mir-opt/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir b/tests/mir-opt/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir index 14e1782b86016..4baba23278e00 100644 --- a/tests/mir-opt/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir +++ b/tests/mir-opt/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir @@ -36,7 +36,7 @@ yields () StorageLive(_7); StorageLive(_8); _8 = move _3; - _7 = take::(move _8) -> [return: bb2, unwind: bb10]; + _7 = take::(move _8) -> [return: bb2, unwind: bb9]; } bb2: { @@ -45,7 +45,7 @@ yields () StorageLive(_9); StorageLive(_10); _10 = move _4; - _9 = take::(move _10) -> [return: bb3, unwind: bb9]; + _9 = take::(move _10) -> [return: bb3, unwind: bb10]; } bb3: { @@ -58,7 +58,7 @@ yields () bb4: { StorageDead(_3); - drop(_1) -> [return: bb5, unwind: bb14]; + drop(_1) -> [return: bb5, unwind: bb12]; } bb5: { @@ -69,12 +69,12 @@ yields () StorageDead(_6); StorageDead(_5); StorageDead(_4); - drop(_3) -> [return: bb7, unwind: bb15]; + drop(_3) -> [return: bb7, unwind: bb13]; } bb7: { StorageDead(_3); - drop(_1) -> [return: bb8, unwind: bb14]; + drop(_1) -> [return: bb8, unwind: bb12]; } bb8: { @@ -82,9 +82,7 @@ yields () } bb9 (cleanup): { - StorageDead(_10); - StorageDead(_9); - goto -> bb12; + goto -> bb10; } bb10 (cleanup): { @@ -92,27 +90,14 @@ yields () } bb11 (cleanup): { - StorageDead(_8); - StorageDead(_7); - goto -> bb12; + drop(_1) -> [return: bb12, unwind terminate(cleanup)]; } bb12 (cleanup): { - StorageDead(_4); - goto -> bb13; - } - - bb13 (cleanup): { - StorageDead(_3); - drop(_1) -> [return: bb14, unwind terminate(cleanup)]; - } - - bb14 (cleanup): { resume; } - bb15 (cleanup): { - StorageDead(_3); - drop(_1) -> [return: bb14, unwind terminate(cleanup)]; + bb13 (cleanup): { + drop(_1) -> [return: bb12, unwind terminate(cleanup)]; } } diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 59417ce646513..9c506ccc45c88 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -263,13 +263,6 @@ + } + + bb11 (cleanup): { -+ StorageDead(_22); -+ StorageDead(_19); -+ StorageDead(_23); -+ StorageDead(_21); -+ StorageDead(_18); -+ StorageDead(_17); -+ StorageDead(_12); + drop((((*_32) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb12, unwind terminate(cleanup)]; + } + diff --git a/tests/mir-opt/issue_91633.bar.built.after.mir b/tests/mir-opt/issue_91633.bar.built.after.mir index 53829588a1b36..0cb9da79b5a0f 100644 --- a/tests/mir-opt/issue_91633.bar.built.after.mir +++ b/tests/mir-opt/issue_91633.bar.built.after.mir @@ -33,6 +33,8 @@ fn bar(_1: Box<[T]>) -> () { } bb4 (cleanup): { + StorageDead(_3); + StorageDead(_2); drop(_1) -> [return: bb5, unwind terminate(cleanup)]; } diff --git a/tests/mir-opt/issue_91633.foo.built.after.mir b/tests/mir-opt/issue_91633.foo.built.after.mir index 53f48350596ee..d003419f2c8ec 100644 --- a/tests/mir-opt/issue_91633.foo.built.after.mir +++ b/tests/mir-opt/issue_91633.foo.built.after.mir @@ -34,12 +34,12 @@ fn foo(_1: Box<[T]>) -> T { FakeRead(ForLet(None), _2); StorageDead(_4); _0 = move _2; - drop(_2) -> [return: bb3, unwind: bb5]; + drop(_2) -> [return: bb3, unwind: bb6]; } bb3: { StorageDead(_2); - drop(_1) -> [return: bb4, unwind: bb6]; + drop(_1) -> [return: bb4, unwind: bb7]; } bb4: { @@ -47,10 +47,17 @@ fn foo(_1: Box<[T]>) -> T { } bb5 (cleanup): { - drop(_1) -> [return: bb6, unwind terminate(cleanup)]; + StorageDead(_3); + StorageDead(_4); + goto -> bb6; } bb6 (cleanup): { + StorageDead(_2); + drop(_1) -> [return: bb7, unwind terminate(cleanup)]; + } + + bb7 (cleanup): { resume; } } 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 484bc7dad1289..f96045bb69448 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 @@ -110,7 +110,7 @@ - bb10: { + bb7: { _0 = const 1_i32; -- drop(_7) -> [return: bb19, unwind: bb25]; +- drop(_7) -> [return: bb19, unwind: bb26]; + drop(_7) -> [return: bb16, unwind: bb22]; } @@ -224,7 +224,7 @@ } - bb22: { -- drop(_2) -> [return: bb24, unwind: bb26]; +- drop(_2) -> [return: bb24, unwind: bb28]; + bb19: { + goto -> bb26; } @@ -233,7 +233,7 @@ + bb20: { StorageDead(_8); StorageDead(_6); -- drop(_2) -> [return: bb24, unwind: bb26]; +- drop(_2) -> [return: bb24, unwind: bb28]; + drop(_2) -> [return: bb21, unwind: bb23]; } @@ -243,20 +243,30 @@ } - bb25 (cleanup): { -- drop(_2) -> [return: bb26, unwind terminate(cleanup)]; +- StorageDead(_16); +- StorageDead(_15); + bb22 (cleanup): { -+ goto -> bb27; + goto -> bb27; } - bb26 (cleanup): { +- StorageDead(_7); +- StorageDead(_5); +- StorageDead(_8); +- StorageDead(_6); +- goto -> bb27; + bb23 (cleanup): { - resume; -+ } -+ ++ resume; + } + +- bb27 (cleanup): { +- drop(_2) -> [return: bb28, unwind terminate(cleanup)]; + bb24: { + goto -> bb21; -+ } -+ + } + +- bb28 (cleanup): { +- resume; + bb25 (cleanup): { + goto -> bb23; + } diff --git a/tests/mir-opt/storage_dead_removed_after_borrowck.rs b/tests/mir-opt/storage_dead_removed_after_borrowck.rs new file mode 100644 index 0000000000000..7dafe3f0f821c --- /dev/null +++ b/tests/mir-opt/storage_dead_removed_after_borrowck.rs @@ -0,0 +1,31 @@ +//@ compile-flags: -Zmir-opt-level=0 +//@ test-mir-pass: CleanupPostBorrowck +// skip-filecheck +// Test that StorageDead statements are removed after borrow-checking by CleanupPostBorrowck. +// StorageDead is only needed for borrow-checking and should be removed before optimization passes. + +// EMIT_MIR storage_dead_removed_after_borrowck.test.built.after.mir +// EMIT_MIR storage_dead_removed_after_borrowck.test.CleanupPostBorrowck.after.mir + +struct Droppable; + +impl Drop for Droppable { + fn drop(&mut self) {} +} + +fn test() { + // StorageDead drop (non-drop type) + let x = 42i32; + // Value drop (drop type) + let y = Droppable; + // Function call that might panic + may_panic(); +} + +fn may_panic() { + // This function might panic, triggering unwind +} + +fn main() { + test(); +} diff --git a/tests/mir-opt/storage_dead_removed_after_borrowck.test.CleanupPostBorrowck.after.mir b/tests/mir-opt/storage_dead_removed_after_borrowck.test.CleanupPostBorrowck.after.mir new file mode 100644 index 0000000000000..c41654f8dfe3f --- /dev/null +++ b/tests/mir-opt/storage_dead_removed_after_borrowck.test.CleanupPostBorrowck.after.mir @@ -0,0 +1,48 @@ +// MIR for `test` after CleanupPostBorrowck + +fn test() -> () { + let mut _0: (); + let _1: i32; + let _3: (); + scope 1 { + debug x => _1; + let _2: Droppable; + scope 2 { + debug y => _2; + } + } + + bb0: { + StorageLive(_1); + _1 = const 42_i32; + nop; + StorageLive(_2); + _2 = Droppable; + nop; + StorageLive(_3); + _3 = may_panic() -> [return: bb1, unwind: bb3]; + } + + bb1: { + StorageDead(_3); + _0 = const (); + drop(_2) -> [return: bb2, unwind: bb4]; + } + + bb2: { + StorageDead(_2); + StorageDead(_1); + return; + } + + bb3 (cleanup): { + nop; + drop(_2) -> [return: bb4, unwind terminate(cleanup)]; + } + + bb4 (cleanup): { + nop; + nop; + resume; + } +} diff --git a/tests/mir-opt/storage_dead_removed_after_borrowck.test.built.after.mir b/tests/mir-opt/storage_dead_removed_after_borrowck.test.built.after.mir new file mode 100644 index 0000000000000..82e0a5343cae2 --- /dev/null +++ b/tests/mir-opt/storage_dead_removed_after_borrowck.test.built.after.mir @@ -0,0 +1,48 @@ +// MIR for `test` after built + +fn test() -> () { + let mut _0: (); + let _1: i32; + let _3: (); + scope 1 { + debug x => _1; + let _2: Droppable; + scope 2 { + debug y => _2; + } + } + + bb0: { + StorageLive(_1); + _1 = const 42_i32; + FakeRead(ForLet(None), _1); + StorageLive(_2); + _2 = Droppable; + FakeRead(ForLet(None), _2); + StorageLive(_3); + _3 = may_panic() -> [return: bb1, unwind: bb3]; + } + + bb1: { + StorageDead(_3); + _0 = const (); + drop(_2) -> [return: bb2, unwind: bb4]; + } + + bb2: { + StorageDead(_2); + StorageDead(_1); + return; + } + + bb3 (cleanup): { + StorageDead(_3); + drop(_2) -> [return: bb4, unwind terminate(cleanup)]; + } + + bb4 (cleanup): { + StorageDead(_2); + StorageDead(_1); + resume; + } +} diff --git a/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff index f13ee78aa368c..58d8a87986d6b 100644 --- a/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff +++ b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff @@ -66,6 +66,7 @@ bb6: { + _8 = const false; StorageDead(_4); + StorageDead(_3); - drop(_2) -> [return: bb7, unwind continue]; + drop(_2) -> [return: bb7, unwind: bb12]; } diff --git a/tests/mir-opt/tail_call_drops.f.built.after.panic-unwind.mir b/tests/mir-opt/tail_call_drops.f.built.after.panic-unwind.mir index e017424a4ccdd..8dcd349408117 100644 --- a/tests/mir-opt/tail_call_drops.f.built.after.panic-unwind.mir +++ b/tests/mir-opt/tail_call_drops.f.built.after.panic-unwind.mir @@ -24,7 +24,7 @@ fn f() -> () { bb0: { StorageLive(_2); - _2 = String::new() -> [return: bb1, unwind: bb17]; + _2 = String::new() -> [return: bb1, unwind: bb18]; } bb1: { @@ -63,6 +63,7 @@ fn f() -> () { bb6: { StorageDead(_4); + StorageDead(_3); drop(_2) -> [return: bb7, unwind: bb17]; } @@ -100,18 +101,28 @@ fn f() -> () { } bb14 (cleanup): { + StorageDead(_7); + StorageDead(_6); drop(_5) -> [return: bb15, unwind terminate(cleanup)]; } bb15 (cleanup): { + StorageDead(_5); drop(_4) -> [return: bb16, unwind terminate(cleanup)]; } bb16 (cleanup): { + StorageDead(_4); + StorageDead(_3); drop(_2) -> [return: bb17, unwind terminate(cleanup)]; } bb17 (cleanup): { + StorageDead(_2); + goto -> bb18; + } + + bb18 (cleanup): { resume; } } diff --git a/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-unwind.diff b/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-unwind.diff index 4fba0032729e6..98c5556e0bce1 100644 --- a/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-unwind.diff +++ b/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-unwind.diff @@ -80,6 +80,7 @@ bb8: { + _12 = const false; StorageDead(_6); + StorageDead(_5); drop(_4) -> [return: bb9, unwind: bb16]; } diff --git a/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-unwind.mir b/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-unwind.mir index 9ec358ec18930..a1863a4e926d0 100644 --- a/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-unwind.mir +++ b/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-unwind.mir @@ -28,7 +28,7 @@ fn f_with_arg(_1: String, _2: String) -> () { bb0: { StorageLive(_4); - _4 = String::new() -> [return: bb1, unwind: bb34]; + _4 = String::new() -> [return: bb1, unwind: bb36]; } bb1: { @@ -37,13 +37,13 @@ fn f_with_arg(_1: String, _2: String) -> () { _5 = const 12_i32; FakeRead(ForLet(None), _5); StorageLive(_6); - _6 = String::new() -> [return: bb2, unwind: bb33]; + _6 = String::new() -> [return: bb2, unwind: bb35]; } bb2: { FakeRead(ForLet(None), _6); StorageLive(_7); - _7 = String::new() -> [return: bb3, unwind: bb32]; + _7 = String::new() -> [return: bb3, unwind: bb34]; } bb3: { @@ -51,14 +51,14 @@ fn f_with_arg(_1: String, _2: String) -> () { StorageLive(_8); StorageLive(_9); _9 = move _6; - _8 = std::mem::drop::(move _9) -> [return: bb4, unwind: bb30]; + _8 = std::mem::drop::(move _9) -> [return: bb4, unwind: bb31]; } bb4: { StorageDead(_9); StorageDead(_8); StorageLive(_10); - _10 = String::new() -> [return: bb5, unwind: bb31]; + _10 = String::new() -> [return: bb5, unwind: bb33]; } bb5: { @@ -77,6 +77,7 @@ fn f_with_arg(_1: String, _2: String) -> () { bb8: { StorageDead(_6); + StorageDead(_5); drop(_4) -> [return: bb9, unwind: bb23]; } @@ -96,18 +97,18 @@ fn f_with_arg(_1: String, _2: String) -> () { bb12: { StorageDead(_11); StorageDead(_10); - drop(_7) -> [return: bb13, unwind: bb32]; + drop(_7) -> [return: bb13, unwind: bb34]; } bb13: { StorageDead(_7); - drop(_6) -> [return: bb14, unwind: bb33]; + drop(_6) -> [return: bb14, unwind: bb35]; } bb14: { StorageDead(_6); StorageDead(_5); - drop(_4) -> [return: bb15, unwind: bb34]; + drop(_4) -> [return: bb15, unwind: bb36]; } bb15: { @@ -116,11 +117,11 @@ fn f_with_arg(_1: String, _2: String) -> () { } bb16: { - drop(_2) -> [return: bb17, unwind: bb35]; + drop(_2) -> [return: bb17, unwind: bb37]; } bb17: { - drop(_1) -> [return: bb18, unwind: bb36]; + drop(_1) -> [return: bb18, unwind: bb38]; } bb18: { @@ -132,7 +133,7 @@ fn f_with_arg(_1: String, _2: String) -> () { } bb20 (cleanup): { - drop(_11) -> [return: bb36, unwind terminate(cleanup)]; + drop(_11) -> [return: bb38, unwind terminate(cleanup)]; } bb21 (cleanup): { @@ -140,7 +141,7 @@ fn f_with_arg(_1: String, _2: String) -> () { } bb22 (cleanup): { - drop(_11) -> [return: bb35, unwind terminate(cleanup)]; + drop(_11) -> [return: bb37, unwind terminate(cleanup)]; } bb23 (cleanup): { @@ -148,7 +149,7 @@ fn f_with_arg(_1: String, _2: String) -> () { } bb24 (cleanup): { - drop(_11) -> [return: bb34, unwind terminate(cleanup)]; + drop(_11) -> [return: bb36, unwind terminate(cleanup)]; } bb25 (cleanup): { @@ -156,7 +157,7 @@ fn f_with_arg(_1: String, _2: String) -> () { } bb26 (cleanup): { - drop(_11) -> [return: bb33, unwind terminate(cleanup)]; + drop(_11) -> [return: bb35, unwind terminate(cleanup)]; } bb27 (cleanup): { @@ -164,38 +165,54 @@ fn f_with_arg(_1: String, _2: String) -> () { } bb28 (cleanup): { - drop(_11) -> [return: bb32, unwind terminate(cleanup)]; + drop(_11) -> [return: bb34, unwind terminate(cleanup)]; } bb29 (cleanup): { - drop(_10) -> [return: bb31, unwind terminate(cleanup)]; + StorageDead(_11); + drop(_10) -> [return: bb30, unwind terminate(cleanup)]; } bb30 (cleanup): { - drop(_9) -> [return: bb31, unwind terminate(cleanup)]; + StorageDead(_10); + goto -> bb33; } bb31 (cleanup): { - drop(_7) -> [return: bb32, unwind terminate(cleanup)]; + drop(_9) -> [return: bb32, unwind terminate(cleanup)]; } bb32 (cleanup): { - drop(_6) -> [return: bb33, unwind terminate(cleanup)]; + StorageDead(_9); + StorageDead(_8); + goto -> bb33; } bb33 (cleanup): { - drop(_4) -> [return: bb34, unwind terminate(cleanup)]; + drop(_7) -> [return: bb34, unwind terminate(cleanup)]; } bb34 (cleanup): { - drop(_2) -> [return: bb35, unwind terminate(cleanup)]; + StorageDead(_7); + drop(_6) -> [return: bb35, unwind terminate(cleanup)]; } bb35 (cleanup): { - drop(_1) -> [return: bb36, unwind terminate(cleanup)]; + StorageDead(_6); + StorageDead(_5); + drop(_4) -> [return: bb36, unwind terminate(cleanup)]; } bb36 (cleanup): { + StorageDead(_4); + drop(_2) -> [return: bb37, unwind terminate(cleanup)]; + } + + bb37 (cleanup): { + drop(_1) -> [return: bb38, unwind terminate(cleanup)]; + } + + bb38 (cleanup): { resume; } } diff --git a/tests/ui/borrowck/storage-dead-unwind-path.rs b/tests/ui/borrowck/storage-dead-unwind-path.rs new file mode 100644 index 0000000000000..d91e68c361e01 --- /dev/null +++ b/tests/ui/borrowck/storage-dead-unwind-path.rs @@ -0,0 +1,27 @@ +//@ check-pass +// Test that StorageDead on unwind paths ensures consistent borrow-checking. +// This test verifies that the borrow-checker correctly treats variables as dead +// at the same point on all paths (normal and unwind), making the borrow-checker +// stricter and more consistent. + +struct Droppable; + +impl Drop for Droppable { + fn drop(&mut self) {} +} + +fn test_storage_dead_consistency() { + // StorageDead drop (non-drop type) + let x = 42i32; + // Value drop (drop type) - if this panics, x should be considered dead + let y = Droppable; + // After y is dropped, x should be considered dead for borrow-checking purposes + // even on the unwind path due to StorageDead being emitted on unwind paths + drop(y); + // x is still in scope here, but StorageDead ensures consistent treatment + let _val = x; +} + +fn main() { + test_storage_dead_consistency(); +} diff --git a/tests/ui/dropck/dropck_trait_cycle_checked.rs b/tests/ui/dropck/dropck_trait_cycle_checked.rs index ffe43480b12cb..89ce1e75058e7 100644 --- a/tests/ui/dropck/dropck_trait_cycle_checked.rs +++ b/tests/ui/dropck/dropck_trait_cycle_checked.rs @@ -110,10 +110,10 @@ fn f() { let (o1, o2, o3): (Box, Box, Box) = (O::new(), O::new(), O::new()); o1.set0(&o2); //~ ERROR `o2` does not live long enough o1.set1(&o3); //~ ERROR `o3` does not live long enough - o2.set0(&o2); //~ ERROR `o2` does not live long enough - o2.set1(&o3); //~ ERROR `o3` does not live long enough + o2.set0(&o2); + o2.set1(&o3); o3.set0(&o1); //~ ERROR `o1` does not live long enough - o3.set1(&o2); //~ ERROR `o2` does not live long enough + o3.set1(&o2); } fn main() { diff --git a/tests/ui/dropck/dropck_trait_cycle_checked.stderr b/tests/ui/dropck/dropck_trait_cycle_checked.stderr index f32736f1a674c..702dd952c5921 100644 --- a/tests/ui/dropck/dropck_trait_cycle_checked.stderr +++ b/tests/ui/dropck/dropck_trait_cycle_checked.stderr @@ -25,34 +25,6 @@ LL | } | = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` -error[E0597]: `o2` does not live long enough - --> $DIR/dropck_trait_cycle_checked.rs:113:13 - | -LL | let (o1, o2, o3): (Box, Box, Box) = (O::new(), O::new(), O::new()); - | -- binding `o2` declared here -------- coercion requires that `o2` is borrowed for `'static` -... -LL | o2.set0(&o2); - | ^^^ borrowed value does not live long enough -... -LL | } - | - `o2` dropped here while still borrowed - | - = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` - -error[E0597]: `o3` does not live long enough - --> $DIR/dropck_trait_cycle_checked.rs:114:13 - | -LL | let (o1, o2, o3): (Box, Box, Box) = (O::new(), O::new(), O::new()); - | -- binding `o3` declared here -------- coercion requires that `o3` is borrowed for `'static` -... -LL | o2.set1(&o3); - | ^^^ borrowed value does not live long enough -... -LL | } - | - `o3` dropped here while still borrowed - | - = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` - error[E0597]: `o1` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:115:13 | @@ -67,19 +39,6 @@ LL | } | = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` -error[E0597]: `o2` does not live long enough - --> $DIR/dropck_trait_cycle_checked.rs:116:13 - | -LL | let (o1, o2, o3): (Box, Box, Box) = (O::new(), O::new(), O::new()); - | -- binding `o2` declared here -------- coercion requires that `o2` is borrowed for `'static` -... -LL | o3.set1(&o2); - | ^^^ borrowed value does not live long enough -LL | } - | - `o2` dropped here while still borrowed - | - = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` - -error: aborting due to 6 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr b/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr index ece581dc62638..75fb13c378c24 100644 --- a/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr +++ b/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr @@ -4,9 +4,10 @@ error[E0597]: `local` does not live long enough LL | let local = Type; | ----- binding `local` declared here LL | become takes_borrow(&local); - | ^^^^^^- `local` dropped here while still borrowed - | | - | borrowed value does not live long enough + | ^^^^^^ borrowed value does not live long enough +LL | +LL | } + | - `local` dropped here while still borrowed error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-147875.rs b/tests/ui/issues/issue-147875.rs new file mode 100644 index 0000000000000..e3b77e6096f13 --- /dev/null +++ b/tests/ui/issues/issue-147875.rs @@ -0,0 +1,24 @@ +//@ check-pass +// Test for issue #147875: StorageDead on unwind paths ensures consistent borrow-checking. +// This test verifies that the borrow-checker correctly treats variables as dead at the +// same point on all paths (normal and unwind), making the borrow-checker stricter and +// more consistent. + +struct Droppable; + +impl Drop for Droppable { + fn drop(&mut self) {} +} + +fn test_storage_dead_on_unwind() { + // StorageDead drop (non-drop type) + let x = 42i32; + // Value drop (drop type) - if this panics, x should be considered dead + let y = Droppable; + // After y is dropped, x should be considered dead for borrow-checking purposes + // even on the unwind path +} + +fn main() { + test_storage_dead_on_unwind(); +}