mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
run borrowck tests on BIDs and emit tail-expr-drop-order lints for
potential violations
This commit is contained in:
parent
6afee111c2
commit
045271cccc
@ -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
|
||||
|
@ -16,6 +16,7 @@
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{ControlFlow, Deref};
|
||||
@ -24,6 +25,7 @@ use rustc_abi::FieldIdx;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::CRATE_HIR_ID;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_index::bit_set::{BitSet, MixedBitSet};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
@ -43,7 +45,7 @@ use rustc_mir_dataflow::move_paths::{
|
||||
InitIndex, InitLocation, LookupResult, MoveData, 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};
|
||||
@ -636,9 +638,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,
|
||||
@ -1007,6 +1011,23 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_polonius_borrows_in_scope<'s>(
|
||||
&self,
|
||||
location: Location,
|
||||
state: &'s BorrowckDomain,
|
||||
) -> Cow<'s, BitSet<BorrowIndex>> {
|
||||
if let Some(polonius) = &self.polonius_output {
|
||||
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,
|
||||
@ -1019,17 +1040,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
||||
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.maybe_polonius_borrows_in_scope(location, state);
|
||||
|
||||
each_borrow_involving_path(
|
||||
self,
|
||||
@ -1149,6 +1160,46 @@ 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;
|
||||
|
||||
// Use polonius output if it has been enabled.
|
||||
let borrows_in_scope = self.maybe_polonius_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,
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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();
|
||||
|
37
tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs
Normal file
37
tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs
Normal file
@ -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 <https://github.com/rust-lang/rfcs/pull/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>(_: 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() {}
|
40
tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr
Normal file
40
tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr
Normal file
@ -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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
|
||||
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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
|
||||
|
||||
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 <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user