diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index ee4b2f95cb151..c1d5af59b1b5b 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -213,6 +213,9 @@ borrowck_suggest_create_fresh_reborrow = borrowck_suggest_iterate_over_slice = consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop +borrowck_tail_expr_drop_order = a temporary value will be dropped here before the execution exits the block in Edition 2024, which will raise borrow checking error + .label = consider using a `let` binding to create a longer lived value; or replacing the `{"{"} .. {"}"}` block with curly brackets `( .. )`; or folding the rest of the expression into the surrounding `unsafe {"{"} .. {"}"}` + borrowck_ty_no_impl_copy = {$is_partial_move -> [true] partial move diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 19b5c8689c840..4dac4be9bc8a9 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -15,6 +15,7 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end +use std::borrow::Cow; use std::cell::RefCell; use std::collections::BTreeMap; use std::marker::PhantomData; @@ -24,8 +25,8 @@ use rustc_abi::FieldIdx; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::dominators::Dominators; use rustc_errors::Diag; -use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; +use rustc_hir::{self as hir, CRATE_HIR_ID}; use rustc_index::bit_set::{BitSet, MixedBitSet}; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::{ @@ -44,7 +45,7 @@ use rustc_mir_dataflow::move_paths::{ InitIndex, InitLocation, LookupResult, MoveData, MoveOutIndex, MovePathIndex, }; use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results}; -use rustc_session::lint::builtin::UNUSED_MUT; +use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; use rustc_span::{Span, Symbol}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -622,9 +623,11 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< | StatementKind::Coverage(..) // These do not actually affect borrowck | StatementKind::ConstEvalCounter - // This do not affect borrowck - | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::StorageLive(..) => {} + // This does not affect borrowck + StatementKind::BackwardIncompatibleDropHint { place, reason: BackwardIncompatibleDropReason::Edition2024 } => { + self.check_backward_incompatible_drop(location, (**place, span), state); + } StatementKind::StorageDead(local) => { self.access_place( location, @@ -994,6 +997,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } } + fn borrows_in_scope<'s>( + &self, + location: Location, + state: &'s BorrowckDomain, + ) -> Cow<'s, BitSet> { + if let Some(polonius) = &self.polonius_output { + // Use polonius output if it has been enabled. + let location = self.location_table.start_index(location); + let mut polonius_output = BitSet::new_empty(self.borrow_set.len()); + for &idx in polonius.errors_at(location) { + polonius_output.insert(idx); + } + Cow::Owned(polonius_output) + } else { + Cow::Borrowed(&state.borrows) + } + } + #[instrument(level = "debug", skip(self, state))] fn check_access_for_conflict( &mut self, @@ -1005,18 +1026,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ) -> bool { let mut error_reported = false; - // Use polonius output if it has been enabled. - let mut polonius_output; - let borrows_in_scope = if let Some(polonius) = &self.polonius_output { - let location = self.location_table.start_index(location); - polonius_output = BitSet::new_empty(self.borrow_set.len()); - for &idx in polonius.errors_at(location) { - polonius_output.insert(idx); - } - &polonius_output - } else { - &state.borrows - }; + let borrows_in_scope = self.borrows_in_scope(location, state); each_borrow_involving_path( self, @@ -1136,6 +1146,45 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { error_reported } + /// Through #123739, backward incompatible drops (BIDs) are introduced. + /// We would like to emit lints whether borrow checking fails at these future drop locations. + #[instrument(level = "debug", skip(self, state))] + fn check_backward_incompatible_drop( + &mut self, + location: Location, + place_span: (Place<'tcx>, Span), + state: &BorrowckDomain, + ) { + let sd = AccessDepth::Drop; + + let borrows_in_scope = self.borrows_in_scope(location, state); + + // This is a very simplified version of `Self::check_access_for_conflict`. + // We are here checking on BIDs and specifically still-live borrows of data involving the BIDs. + each_borrow_involving_path( + self, + self.infcx.tcx, + self.body, + (sd, place_span.0), + self.borrow_set, + |borrow_index| borrows_in_scope.contains(borrow_index), + |this, _borrow_index, borrow| { + if matches!(borrow.kind, BorrowKind::Fake(_)) { + return Control::Continue; + } + let borrowed = this.retrieve_borrow_spans(borrow).var_or_use_path_span(); + this.infcx.tcx.emit_node_span_lint( + TAIL_EXPR_DROP_ORDER, + CRATE_HIR_ID, + place_span.1, + session_diagnostics::TailExprDropOrder { borrowed }, + ); + // We may stop at the first case + Control::Break + }, + ); + } + fn mutate_place( &mut self, location: Location, diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 627444a4ce5b8..4be5d0dbf4284 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -480,3 +480,10 @@ pub(crate) struct SimdIntrinsicArgConst { pub arg: usize, pub intrinsic: String, } + +#[derive(LintDiagnostic)] +#[diag(borrowck_tail_expr_drop_order)] +pub(crate) struct TailExprDropOrder { + #[label] + pub borrowed: Span, +} diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index fd9f9da6e770c..ab7600d87243e 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -1131,15 +1131,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Schedule emission of a backwards incompatible drop lint hint. /// Applicable only to temporary values for now. + #[instrument(level = "debug", skip(self))] pub(crate) fn schedule_backwards_incompatible_drop( &mut self, span: Span, region_scope: region::Scope, local: Local, ) { - if !self.local_decls[local].ty.has_significant_drop(self.tcx, self.typing_env()) { - return; - } + // Note that we are *not* gating BIDs here on whether they have significant destructor. + // We need to know all of them so that we can capture potential borrow-checking errors. for scope in self.scopes.scopes.iter_mut().rev() { // Since we are inserting linting MIR statement, we have to invalidate the caches scope.invalidate_cache(); diff --git a/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs new file mode 100644 index 0000000000000..1bd5655d7fe1d --- /dev/null +++ b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs @@ -0,0 +1,37 @@ +// Edition 2024 lint for change in drop order at tail expression +// This lint is to capture potential borrow-checking errors +// due to implementation of RFC 3606 +//@ edition: 2021 + +#![deny(tail_expr_drop_order)] //~ NOTE: the lint level is defined here + +fn should_lint_with_potential_borrowck_err() { + let _ = { String::new().as_str() }.len(); + //~^ ERROR: a temporary value will be dropped here + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: consider using a `let` binding + //~| NOTE: for more information, see +} + +fn should_lint_with_unsafe_block() { + fn f(_: usize) {} + f(unsafe { String::new().as_str() }.len()); + //~^ ERROR: a temporary value will be dropped here + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: consider using a `let` binding + //~| NOTE: for more information, see +} + +#[rustfmt::skip] +fn should_lint_with_big_block() { + fn f(_: T) {} + f({ + &mut || 0 + //~^ ERROR: a temporary value will be dropped here + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: consider using a `let` binding + //~| NOTE: for more information, see + }) +} + +fn main() {} diff --git a/tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr new file mode 100644 index 0000000000000..98ef0547c9075 --- /dev/null +++ b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr @@ -0,0 +1,40 @@ +error: a temporary value will be dropped here before the execution exits the block in Edition 2024, which will raise borrow checking error + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:9:36 + | +LL | let _ = { String::new().as_str() }.len(); + | ------------- ^ + | | + | consider using a `let` binding to create a longer lived value; or replacing the `{ .. }` block with curly brackets `( .. )`; or folding the rest of the expression into the surrounding `unsafe { .. }` + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: the lint level is defined here + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:6:9 + | +LL | #![deny(tail_expr_drop_order)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: a temporary value will be dropped here before the execution exits the block in Edition 2024, which will raise borrow checking error + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:18:37 + | +LL | f(unsafe { String::new().as_str() }.len()); + | ------------- ^ + | | + | consider using a `let` binding to create a longer lived value; or replacing the `{ .. }` block with curly brackets `( .. )`; or folding the rest of the expression into the surrounding `unsafe { .. }` + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see + +error: a temporary value will be dropped here before the execution exits the block in Edition 2024, which will raise borrow checking error + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:29:17 + | +LL | &mut || 0 + | --------^ + | | + | consider using a `let` binding to create a longer lived value; or replacing the `{ .. }` block with curly brackets `( .. )`; or folding the rest of the expression into the surrounding `unsafe { .. }` + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see + +error: aborting due to 3 previous errors +