Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit beada15

Browse files
committedDec 19, 2024
run borrowck tests on BIDs and emit tail-expr-drop-order lints for
potential violations
1 parent 3bf62cc commit beada15

File tree

6 files changed

+155
-18
lines changed

6 files changed

+155
-18
lines changed
 

‎compiler/rustc_borrowck/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ borrowck_suggest_create_fresh_reborrow =
213213
borrowck_suggest_iterate_over_slice =
214214
consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop
215215
216+
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
217+
.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 {"{"} .. {"}"}`
218+
216219
borrowck_ty_no_impl_copy =
217220
{$is_partial_move ->
218221
[true] partial move

‎compiler/rustc_borrowck/src/lib.rs

+65-15
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#![warn(unreachable_pub)]
1616
// tidy-alphabetical-end
1717

18+
use std::borrow::Cow;
1819
use std::cell::RefCell;
1920
use std::collections::BTreeMap;
2021
use std::marker::PhantomData;
@@ -24,8 +25,8 @@ use rustc_abi::FieldIdx;
2425
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
2526
use rustc_data_structures::graph::dominators::Dominators;
2627
use rustc_errors::Diag;
27-
use rustc_hir as hir;
2828
use rustc_hir::def_id::LocalDefId;
29+
use rustc_hir::{self as hir, CRATE_HIR_ID};
2930
use rustc_index::bit_set::{BitSet, MixedBitSet};
3031
use rustc_index::{IndexSlice, IndexVec};
3132
use rustc_infer::infer::{
@@ -44,7 +45,7 @@ use rustc_mir_dataflow::move_paths::{
4445
InitIndex, InitLocation, LookupResult, MoveData, MoveOutIndex, MovePathIndex,
4546
};
4647
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
47-
use rustc_session::lint::builtin::UNUSED_MUT;
48+
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
4849
use rustc_span::{Span, Symbol};
4950
use smallvec::SmallVec;
5051
use tracing::{debug, instrument};
@@ -622,9 +623,11 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
622623
| StatementKind::Coverage(..)
623624
// These do not actually affect borrowck
624625
| StatementKind::ConstEvalCounter
625-
// This do not affect borrowck
626-
| StatementKind::BackwardIncompatibleDropHint { .. }
627626
| StatementKind::StorageLive(..) => {}
627+
// This do not affect borrowck
628+
StatementKind::BackwardIncompatibleDropHint { place, reason: BackwardIncompatibleDropReason::Edition2024 } => {
629+
self.check_backward_incompatible_drop(location, (**place, span), state);
630+
}
628631
StatementKind::StorageDead(local) => {
629632
self.access_place(
630633
location,
@@ -994,6 +997,23 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
994997
}
995998
}
996999

1000+
fn maybe_polonius_borrows_in_scope<'s>(
1001+
&self,
1002+
location: Location,
1003+
state: &'s BorrowckDomain,
1004+
) -> Cow<'s, BitSet<BorrowIndex>> {
1005+
if let Some(polonius) = &self.polonius_output {
1006+
let location = self.location_table.start_index(location);
1007+
let mut polonius_output = BitSet::new_empty(self.borrow_set.len());
1008+
for &idx in polonius.errors_at(location) {
1009+
polonius_output.insert(idx);
1010+
}
1011+
Cow::Owned(polonius_output)
1012+
} else {
1013+
Cow::Borrowed(&state.borrows)
1014+
}
1015+
}
1016+
9971017
#[instrument(level = "debug", skip(self, state))]
9981018
fn check_access_for_conflict(
9991019
&mut self,
@@ -1006,17 +1026,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
10061026
let mut error_reported = false;
10071027

10081028
// Use polonius output if it has been enabled.
1009-
let mut polonius_output;
1010-
let borrows_in_scope = if let Some(polonius) = &self.polonius_output {
1011-
let location = self.location_table.start_index(location);
1012-
polonius_output = BitSet::new_empty(self.borrow_set.len());
1013-
for &idx in polonius.errors_at(location) {
1014-
polonius_output.insert(idx);
1015-
}
1016-
&polonius_output
1017-
} else {
1018-
&state.borrows
1019-
};
1029+
let borrows_in_scope = self.maybe_polonius_borrows_in_scope(location, state);
10201030

10211031
each_borrow_involving_path(
10221032
self,
@@ -1136,6 +1146,46 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
11361146
error_reported
11371147
}
11381148

1149+
/// Through #123739, backward incompatible drops (BIDs) are introduced.
1150+
/// We would like to emit lints whether borrow checking fails at these future drop locations.
1151+
#[instrument(level = "debug", skip(self, state))]
1152+
fn check_backward_incompatible_drop(
1153+
&mut self,
1154+
location: Location,
1155+
place_span: (Place<'tcx>, Span),
1156+
state: &BorrowckDomain,
1157+
) {
1158+
let sd = AccessDepth::Drop;
1159+
1160+
// Use polonius output if it has been enabled.
1161+
let borrows_in_scope = self.maybe_polonius_borrows_in_scope(location, state);
1162+
1163+
// This is a very simplified version of `Self::check_access_for_conflict`.
1164+
// We are here checking on BIDs and specifically still-live borrows of data involving the BIDs.
1165+
each_borrow_involving_path(
1166+
self,
1167+
self.infcx.tcx,
1168+
self.body,
1169+
(sd, place_span.0),
1170+
self.borrow_set,
1171+
|borrow_index| borrows_in_scope.contains(borrow_index),
1172+
|this, _borrow_index, borrow| {
1173+
if matches!(borrow.kind, BorrowKind::Fake(_)) {
1174+
return Control::Continue;
1175+
}
1176+
let borrowed = this.retrieve_borrow_spans(borrow).var_or_use_path_span();
1177+
this.infcx.tcx.emit_node_span_lint(
1178+
TAIL_EXPR_DROP_ORDER,
1179+
CRATE_HIR_ID,
1180+
place_span.1,
1181+
session_diagnostics::TailExprDropOrder { borrowed },
1182+
);
1183+
// We may stop at the first case
1184+
Control::Break
1185+
},
1186+
);
1187+
}
1188+
11391189
fn mutate_place(
11401190
&mut self,
11411191
location: Location,

‎compiler/rustc_borrowck/src/session_diagnostics.rs

+7
Original file line numberDiff line numberDiff line change
@@ -480,3 +480,10 @@ pub(crate) struct SimdIntrinsicArgConst {
480480
pub arg: usize,
481481
pub intrinsic: String,
482482
}
483+
484+
#[derive(LintDiagnostic)]
485+
#[diag(borrowck_tail_expr_drop_order)]
486+
pub(crate) struct TailExprDropOrder {
487+
#[label]
488+
pub borrowed: Span,
489+
}

‎compiler/rustc_mir_build/src/builder/scope.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1113,15 +1113,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
11131113

11141114
/// Schedule emission of a backwards incompatible drop lint hint.
11151115
/// Applicable only to temporary values for now.
1116+
#[instrument(level = "debug", skip(self))]
11161117
pub(crate) fn schedule_backwards_incompatible_drop(
11171118
&mut self,
11181119
span: Span,
11191120
region_scope: region::Scope,
11201121
local: Local,
11211122
) {
1122-
if !self.local_decls[local].ty.has_significant_drop(self.tcx, self.typing_env()) {
1123-
return;
1124-
}
1123+
// Note that we are *not* gating BIDs here on whether they have significant destructor.
1124+
// We need to know all of them so that we can capture potential borrow-checking errors.
11251125
for scope in self.scopes.scopes.iter_mut().rev() {
11261126
// Since we are inserting linting MIR statement, we have to invalidate the caches
11271127
scope.invalidate_cache();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Edition 2024 lint for change in drop order at tail expression
2+
// This lint is to capture potential borrow-checking errors
3+
// due to implementation of RFC 3606 <https://github.com/rust-lang/rfcs/pull/3606>
4+
//@ edition: 2021
5+
6+
#![deny(tail_expr_drop_order)] //~ NOTE: the lint level is defined here
7+
8+
fn should_lint_with_potential_borrowck_err() {
9+
let _ = { String::new().as_str() }.len();
10+
//~^ ERROR: a temporary value will be dropped here
11+
//~| WARN: this changes meaning in Rust 2024
12+
//~| NOTE: consider using a `let` binding
13+
//~| NOTE: for more information, see
14+
}
15+
16+
fn should_lint_with_unsafe_block() {
17+
fn f(_: usize) {}
18+
f(unsafe { String::new().as_str() }.len());
19+
//~^ ERROR: a temporary value will be dropped here
20+
//~| WARN: this changes meaning in Rust 2024
21+
//~| NOTE: consider using a `let` binding
22+
//~| NOTE: for more information, see
23+
}
24+
25+
#[rustfmt::skip]
26+
fn should_lint_with_big_block() {
27+
fn f<T>(_: T) {}
28+
f({
29+
&mut || 0
30+
//~^ ERROR: a temporary value will be dropped here
31+
//~| WARN: this changes meaning in Rust 2024
32+
//~| NOTE: consider using a `let` binding
33+
//~| NOTE: for more information, see
34+
})
35+
}
36+
37+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error: a temporary value will be dropped here before the execution exits the block in Edition 2024, which will raise borrow checking error
2+
--> $DIR/lint-tail-expr-drop-order-borrowck.rs:9:36
3+
|
4+
LL | let _ = { String::new().as_str() }.len();
5+
| ------------- ^
6+
| |
7+
| 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 { .. }`
8+
|
9+
= warning: this changes meaning in Rust 2024
10+
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
11+
note: the lint level is defined here
12+
--> $DIR/lint-tail-expr-drop-order-borrowck.rs:6:9
13+
|
14+
LL | #![deny(tail_expr_drop_order)]
15+
| ^^^^^^^^^^^^^^^^^^^^
16+
17+
error: a temporary value will be dropped here before the execution exits the block in Edition 2024, which will raise borrow checking error
18+
--> $DIR/lint-tail-expr-drop-order-borrowck.rs:18:37
19+
|
20+
LL | f(unsafe { String::new().as_str() }.len());
21+
| ------------- ^
22+
| |
23+
| 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 { .. }`
24+
|
25+
= warning: this changes meaning in Rust 2024
26+
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
27+
28+
error: a temporary value will be dropped here before the execution exits the block in Edition 2024, which will raise borrow checking error
29+
--> $DIR/lint-tail-expr-drop-order-borrowck.rs:29:17
30+
|
31+
LL | &mut || 0
32+
| --------^
33+
| |
34+
| 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 { .. }`
35+
|
36+
= warning: this changes meaning in Rust 2024
37+
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
38+
39+
error: aborting due to 3 previous errors
40+

0 commit comments

Comments
 (0)
Failed to load comments.