Skip to content

Commit

Permalink
Auto merge of rust-lang#134523 - dingxiangfei2009:issue-130836-attemp…
Browse files Browse the repository at this point in the history
…t-2, r=<try>

Run borrowck tests on BIDs and emit tail-expr-drop-order lints for violations

Fix rust-lang#132861

r? `@nikomatsakis`
cc `@compiler-errors`

This patch enlarges the scope where the `tail-expr-drop-order` lint applies, so that all locals involved in tail expressions are inspected. This is necessary to run borrow-checking to capture the cases where it used to compile under Edition 2021 but is not going to pass borrow-checking from Edition 2024 onwards.

The way it works is to inspect each BID against the set of borrows that are still live. If the local involved in BID has a borrow index which happens to be live as well at the location of this BID statement, in the future this will be a borrow-checking violation. The lint will fire in this case.
  • Loading branch information
bors committed Jan 5, 2025
2 parents dcfa38f + 4749325 commit 214d5c9
Show file tree
Hide file tree
Showing 12 changed files with 252 additions and 37 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_borrowck/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
88 changes: 73 additions & 15 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#![warn(unreachable_pub)]
// tidy-alphabetical-end

use std::borrow::Cow;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::ops::Deref;
Expand All @@ -23,6 +24,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};
Expand All @@ -42,7 +44,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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -1007,6 +1011,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
}
}

fn borrows_in_scope<'s>(
&self,
location: Location,
state: &'s BorrowckDomain,
) -> Cow<'s, BitSet<BorrowIndex>> {
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,
Expand All @@ -1018,18 +1040,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,
Expand Down Expand Up @@ -1149,6 +1160,53 @@ 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, place_span): (Place<'tcx>, Span),
state: &BorrowckDomain,
) {
let tcx = self.infcx.tcx;
// If this type does not need `Drop`, then treat it like a `StorageDead`.
// This is needed because we track the borrows of refs to thread locals,
// and we'll ICE because we don't track borrows behind shared references.
let sd = if place.ty(self.body, tcx).ty.needs_drop(tcx, self.body.typing_env(tcx)) {
AccessDepth::Drop
} else {
AccessDepth::Shallow(None)
};

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),
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,
session_diagnostics::TailExprDropOrder { borrowed },
);
// We may stop at the first case
Control::Break
},
);
}

fn mutate_place(
&mut self,
location: Location,
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_borrowck/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
6 changes: 3 additions & 3 deletions compiler/rustc_mir_build/src/builder/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,14 +351,19 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
{
return;
}

// FIXME(typing_env): This should be able to reveal the opaques local to the
// body using the typeck results.
let typing_env = ty::TypingEnv::non_body_analysis(tcx, def_id);

// ## About BIDs in blocks ##
// Track the set of blocks that contain a backwards-incompatible drop (BID)
// and, for each block, the vector of locations.
//
// We group them per-block because they tend to scheduled in the same drop ladder block.
let mut bid_per_block = IndexMap::default();
let mut bid_places = UnordSet::new();
let typing_env = ty::TypingEnv::post_analysis(tcx, def_id);

let mut ty_dropped_components = UnordMap::default();
for (block, data) in body.basic_blocks.iter_enumerated() {
for (statement_index, stmt) in data.statements.iter().enumerate() {
Expand Down
37 changes: 37 additions & 0 deletions tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 40 additions & 0 deletions tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr
Original file line number Diff line number Diff line change
@@ -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

1 change: 0 additions & 1 deletion tests/ui/drop/lint-tail-expr-drop-order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ impl Drop for LoudDropper {
//~| NOTE: `#1` invokes this custom destructor
//~| NOTE: `x` invokes this custom destructor
//~| NOTE: `#1` invokes this custom destructor
//~| NOTE: `future` invokes this custom destructor
//~| NOTE: `_x` invokes this custom destructor
//~| NOTE: `#1` invokes this custom destructor
fn drop(&mut self) {
Expand Down
27 changes: 10 additions & 17 deletions tests/ui/drop/lint-tail-expr-drop-order.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: relative drop order changing in Rust 2024
--> $DIR/lint-tail-expr-drop-order.rs:41:15
--> $DIR/lint-tail-expr-drop-order.rs:40:15
|
LL | let x = LoudDropper;
| -
Expand Down Expand Up @@ -40,7 +40,7 @@ LL | #![deny(tail_expr_drop_order)]
| ^^^^^^^^^^^^^^^^^^^^

error: relative drop order changing in Rust 2024
--> $DIR/lint-tail-expr-drop-order.rs:66:19
--> $DIR/lint-tail-expr-drop-order.rs:65:19
|
LL | let x = LoudDropper;
| -
Expand Down Expand Up @@ -76,7 +76,7 @@ LL | | }
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages

error: relative drop order changing in Rust 2024
--> $DIR/lint-tail-expr-drop-order.rs:93:7
--> $DIR/lint-tail-expr-drop-order.rs:92:7
|
LL | let x = LoudDropper;
| -
Expand Down Expand Up @@ -112,7 +112,7 @@ LL | | }
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages

error: relative drop order changing in Rust 2024
--> $DIR/lint-tail-expr-drop-order.rs:146:5
--> $DIR/lint-tail-expr-drop-order.rs:145:5
|
LL | let future = f();
| ------
Expand All @@ -136,19 +136,12 @@ note: `#1` invokes this custom destructor
|
LL | / impl Drop for LoudDropper {
... |
LL | | }
| |_^
note: `future` invokes this custom destructor
--> $DIR/lint-tail-expr-drop-order.rs:10:1
|
LL | / impl Drop for LoudDropper {
... |
LL | | }
| |_^
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages

error: relative drop order changing in Rust 2024
--> $DIR/lint-tail-expr-drop-order.rs:163:14
--> $DIR/lint-tail-expr-drop-order.rs:162:14
|
LL | let x = T::default();
| -
Expand All @@ -170,7 +163,7 @@ LL | }
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages

error: relative drop order changing in Rust 2024
--> $DIR/lint-tail-expr-drop-order.rs:177:5
--> $DIR/lint-tail-expr-drop-order.rs:176:5
|
LL | let x: Result<LoudDropper, ()> = Ok(LoudDropper);
| -
Expand Down Expand Up @@ -206,7 +199,7 @@ LL | | }
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages

error: relative drop order changing in Rust 2024
--> $DIR/lint-tail-expr-drop-order.rs:221:5
--> $DIR/lint-tail-expr-drop-order.rs:220:5
|
LL | let x = LoudDropper2;
| -
Expand All @@ -226,7 +219,7 @@ LL | }
= 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: `#1` invokes this custom destructor
--> $DIR/lint-tail-expr-drop-order.rs:194:5
--> $DIR/lint-tail-expr-drop-order.rs:193:5
|
LL | / impl Drop for LoudDropper3 {
LL | |
Expand All @@ -236,7 +229,7 @@ LL | | }
LL | | }
| |_____^
note: `x` invokes this custom destructor
--> $DIR/lint-tail-expr-drop-order.rs:206:5
--> $DIR/lint-tail-expr-drop-order.rs:205:5
|
LL | / impl Drop for LoudDropper2 {
LL | |
Expand All @@ -248,7 +241,7 @@ LL | | }
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages

error: relative drop order changing in Rust 2024
--> $DIR/lint-tail-expr-drop-order.rs:234:13
--> $DIR/lint-tail-expr-drop-order.rs:233:13
|
LL | LoudDropper.get()
| ^^^^^^^^^^^
Expand Down
Loading

0 comments on commit 214d5c9

Please sign in to comment.