Skip to content

Commit dec0a98

Browse files
committed
Auto merge of #58739 - matthewjasper:more-restrictive-tpb, r=pnkfelix
More restrictive 2 phase borrows - take 2 Signal lint diagnostic `mutable_borrow_reservation_conflict` when borrow-check finds a 2-phase borrow's reservation overlapping with a shared borrow. (pnkfelix updated description) cc #56254 , #59159 blocks PR #59114 r? @pnkfelix cc @RalfJung @nikomatsakis
2 parents 0913800 + cc5088d commit dec0a98

22 files changed

+455
-207
lines changed

src/librustc/lint/builtin.rs

+7
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,12 @@ declare_lint! {
392392
"nested occurrence of `impl Trait` type"
393393
}
394394

395+
declare_lint! {
396+
pub MUTABLE_BORROW_RESERVATION_CONFLICT,
397+
Warn,
398+
"reservation of a two-phased borrow conflicts with other shared borrows"
399+
}
400+
395401
declare_lint_pass! {
396402
/// Does nothing as a lint pass, but registers some `Lint`s
397403
/// that are used by other parts of the compiler.
@@ -457,6 +463,7 @@ declare_lint_pass! {
457463
AMBIGUOUS_ASSOCIATED_ITEMS,
458464
NESTED_IMPL_TRAIT,
459465
DUPLICATE_MATCHER_BINDING_NAME,
466+
MUTABLE_BORROW_RESERVATION_CONFLICT,
460467
]
461468
}
462469

src/librustc/lint/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -712,10 +712,14 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
712712
"this was previously accepted by the compiler but is being phased out; \
713713
it will become a hard error";
714714

715-
let explanation = if lint_id == LintId::of(crate::lint::builtin::UNSTABLE_NAME_COLLISIONS) {
715+
let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) {
716716
"once this method is added to the standard library, \
717717
the ambiguity may cause an error or change in behavior!"
718718
.to_owned()
719+
} else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) {
720+
"this borrowing pattern was not meant to be accepted, \
721+
and may become a hard error in the future"
722+
.to_owned()
719723
} else if let Some(edition) = future_incompatible.edition {
720724
format!("{} in the {} edition!", STANDARD_MESSAGE, edition)
721725
} else {

src/librustc_codegen_llvm/abi.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
266266
OperandValue::Ref(next(), Some(next()), self.layout.align.abi).store(bx, dst);
267267
}
268268
PassMode::Direct(_) | PassMode::Indirect(_, None) | PassMode::Cast(_) => {
269-
self.store(bx, next(), dst);
269+
let next_arg = next();
270+
self.store(bx, next_arg, dst);
270271
}
271272
}
272273
}

src/librustc_lint/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
438438
reference: "issue #59014 <https://github.com/rust-lang/rust/issues/59014>",
439439
edition: None,
440440
},
441+
FutureIncompatibleInfo {
442+
id: LintId::of(MUTABLE_BORROW_RESERVATION_CONFLICT),
443+
reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>",
444+
edition: None,
445+
}
441446
]);
442447

443448
// Register renamed and removed lints.

src/librustc_mir/borrow_check/borrow_set.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ crate enum TwoPhaseActivation {
5252
ActivatedAt(Location),
5353
}
5454

55-
#[derive(Debug)]
55+
#[derive(Debug, Clone)]
5656
crate struct BorrowData<'tcx> {
5757
/// Location where the borrow reservation starts.
5858
/// In many cases, this will be equal to the activation location but not always.

src/librustc_mir/borrow_check/error_reporting.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
318318
context: Context,
319319
(place, _span): (&Place<'tcx>, Span),
320320
borrow: &BorrowData<'tcx>,
321-
) {
321+
) -> DiagnosticBuilder<'cx> {
322322
let tcx = self.infcx.tcx;
323323

324324
let borrow_spans = self.retrieve_borrow_spans(borrow);
@@ -347,7 +347,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
347347

348348
self.explain_why_borrow_contains_point(context, borrow, None)
349349
.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None);
350-
err.buffer(&mut self.errors_buffer);
350+
err
351351
}
352352

353353
pub(super) fn report_conflicting_borrow(
@@ -356,7 +356,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
356356
(place, span): (&Place<'tcx>, Span),
357357
gen_borrow_kind: BorrowKind,
358358
issued_borrow: &BorrowData<'tcx>,
359-
) {
359+
) -> DiagnosticBuilder<'cx> {
360360
let issued_spans = self.retrieve_borrow_spans(issued_borrow);
361361
let issued_span = issued_spans.args_or_use();
362362

@@ -460,9 +460,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
460460
"borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe()
461461
),
462462
);
463-
err.buffer(&mut self.errors_buffer);
464463

465-
return;
464+
return err;
466465
}
467466

468467
(BorrowKind::Unique, _, _, _, _, _) => {
@@ -563,7 +562,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
563562
None,
564563
);
565564

566-
err.buffer(&mut self.errors_buffer);
565+
err
567566
}
568567

569568
/// Returns the description of the root place for a conflicting borrow and the full

src/librustc_mir/borrow_check/mod.rs

+95-24
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc::hir::Node;
66
use rustc::hir::def_id::DefId;
77
use rustc::infer::InferCtxt;
88
use rustc::lint::builtin::UNUSED_MUT;
9+
use rustc::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT};
910
use rustc::middle::borrowck::SignalledError;
1011
use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
1112
use rustc::mir::{
@@ -18,14 +19,15 @@ use rustc::ty::{self, TyCtxt};
1819

1920
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, Level};
2021
use rustc_data_structures::bit_set::BitSet;
21-
use rustc_data_structures::fx::FxHashSet;
22+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2223
use rustc_data_structures::graph::dominators::Dominators;
2324
use smallvec::SmallVec;
2425

25-
use std::rc::Rc;
2626
use std::collections::BTreeMap;
27+
use std::mem;
28+
use std::rc::Rc;
2729

28-
use syntax_pos::Span;
30+
use syntax_pos::{Span, DUMMY_SP};
2931

3032
use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex};
3133
use crate::dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveError};
@@ -238,6 +240,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
238240
locals_are_invalidated_at_exit,
239241
access_place_error_reported: Default::default(),
240242
reservation_error_reported: Default::default(),
243+
reservation_warnings: Default::default(),
241244
move_error_reported: BTreeMap::new(),
242245
uninitialized_error_reported: Default::default(),
243246
errors_buffer,
@@ -260,6 +263,29 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
260263
}
261264
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
262265

266+
// Convert any reservation warnings into lints.
267+
let reservation_warnings = mem::replace(&mut mbcx.reservation_warnings, Default::default());
268+
for (_, (place, span, context, bk, borrow)) in reservation_warnings {
269+
let mut initial_diag = mbcx.report_conflicting_borrow(context, (&place, span), bk, &borrow);
270+
271+
let lint_root = if let ClearCrossCrate::Set(ref vsi) = mbcx.mir.source_scope_local_data {
272+
let scope = mbcx.mir.source_info(context.loc).scope;
273+
vsi[scope].lint_root
274+
} else {
275+
id
276+
};
277+
278+
// Span and message don't matter; we overwrite them below anyway
279+
let mut diag = mbcx.infcx.tcx.struct_span_lint_hir(
280+
MUTABLE_BORROW_RESERVATION_CONFLICT, lint_root, DUMMY_SP, "");
281+
282+
diag.message = initial_diag.styled_message().clone();
283+
diag.span = initial_diag.span.clone();
284+
285+
initial_diag.cancel();
286+
diag.buffer(&mut mbcx.errors_buffer);
287+
}
288+
263289
// For each non-user used mutable variable, check if it's been assigned from
264290
// a user-declared local. If so, then put that local into the used_mut set.
265291
// Note that this set is expected to be small - only upvars from closures
@@ -341,18 +367,9 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
341367
// if AST-borrowck signalled no errors, then
342368
// downgrade all the buffered MIR-borrowck errors
343369
// to warnings.
344-
for err in &mut mbcx.errors_buffer {
345-
if err.is_error() {
346-
err.level = Level::Warning;
347-
err.warn(
348-
"this error has been downgraded to a warning for backwards \
349-
compatibility with previous releases",
350-
);
351-
err.warn(
352-
"this represents potential undefined behavior in your code and \
353-
this warning will become a hard error in the future",
354-
);
355-
}
370+
371+
for err in mbcx.errors_buffer.iter_mut() {
372+
downgrade_if_error(err);
356373
}
357374
}
358375
SignalledError::SawSomeError => {
@@ -378,6 +395,20 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
378395
result
379396
}
380397

398+
fn downgrade_if_error(diag: &mut Diagnostic) {
399+
if diag.is_error() {
400+
diag.level = Level::Warning;
401+
diag.warn(
402+
"this error has been downgraded to a warning for backwards \
403+
compatibility with previous releases",
404+
);
405+
diag.warn(
406+
"this represents potential undefined behavior in your code and \
407+
this warning will become a hard error in the future",
408+
);
409+
}
410+
}
411+
381412
pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
382413
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
383414
mir: &'cx Mir<'tcx>,
@@ -410,6 +441,13 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
410441
// but it is currently inconvenient to track down the `BorrowIndex`
411442
// at the time we detect and report a reservation error.
412443
reservation_error_reported: FxHashSet<Place<'tcx>>,
444+
/// Migration warnings to be reported for #56254. We delay reporting these
445+
/// so that we can suppress the warning if there's a corresponding error
446+
/// for the activation of the borrow.
447+
reservation_warnings: FxHashMap<
448+
BorrowIndex,
449+
(Place<'tcx>, Span, Context, BorrowKind, BorrowData<'tcx>)
450+
>,
413451
/// This field keeps track of move errors that are to be reported for given move indicies.
414452
///
415453
/// There are situations where many errors can be reported for a single move out (see #53807)
@@ -921,11 +959,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
921959
let conflict_error =
922960
self.check_access_for_conflict(context, place_span, sd, rw, flow_state);
923961

962+
if let (Activation(_, borrow_idx), true) = (kind.1, conflict_error) {
963+
// Suppress this warning when there's an error being emited for the
964+
// same borrow: fixing the error is likely to fix the warning.
965+
self.reservation_warnings.remove(&borrow_idx);
966+
}
967+
924968
if conflict_error || mutability_error {
925969
debug!(
926970
"access_place: logging error place_span=`{:?}` kind=`{:?}`",
927971
place_span, kind
928972
);
973+
929974
self.access_place_error_reported
930975
.insert((place_span.0.clone(), place_span.1));
931976
}
@@ -976,8 +1021,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
9761021
Control::Continue
9771022
}
9781023

979-
(Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared)
980-
| (Read(_), BorrowKind::Shallow) | (Reservation(..), BorrowKind::Shallow)
1024+
(Read(_), BorrowKind::Shared)
1025+
| (Read(_), BorrowKind::Shallow)
9811026
| (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Unique)
9821027
| (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Mut { .. }) => {
9831028
Control::Continue
@@ -991,28 +1036,53 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
9911036
(Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut { .. }) => {
9921037
// Reading from mere reservations of mutable-borrows is OK.
9931038
if !is_active(&this.dominators, borrow, context.loc) {
994-
assert!(allow_two_phase_borrow(&this.infcx.tcx, borrow.kind));
1039+
assert!(allow_two_phase_borrow(&tcx, borrow.kind));
9951040
return Control::Continue;
9961041
}
9971042

9981043
error_reported = true;
9991044
match kind {
10001045
ReadKind::Copy => {
10011046
this.report_use_while_mutably_borrowed(context, place_span, borrow)
1047+
.buffer(&mut this.errors_buffer);
10021048
}
10031049
ReadKind::Borrow(bk) => {
1004-
this.report_conflicting_borrow(context, place_span, bk, &borrow)
1050+
this.report_conflicting_borrow(context, place_span, bk, borrow)
1051+
.buffer(&mut this.errors_buffer);
10051052
}
10061053
}
10071054
Control::Break
10081055
}
10091056

1010-
(Reservation(kind), BorrowKind::Unique)
1011-
| (Reservation(kind), BorrowKind::Mut { .. })
1057+
(Reservation(WriteKind::MutableBorrow(bk)), BorrowKind::Shallow)
1058+
| (Reservation(WriteKind::MutableBorrow(bk)), BorrowKind::Shared) if {
1059+
tcx.migrate_borrowck()
1060+
} => {
1061+
let bi = this.borrow_set.location_map[&context.loc];
1062+
debug!(
1063+
"recording invalid reservation of place: {:?} with \
1064+
borrow index {:?} as warning",
1065+
place_span.0,
1066+
bi,
1067+
);
1068+
// rust-lang/rust#56254 - This was previously permitted on
1069+
// the 2018 edition so we emit it as a warning. We buffer
1070+
// these sepately so that we only emit a warning if borrow
1071+
// checking was otherwise successful.
1072+
this.reservation_warnings.insert(
1073+
bi,
1074+
(place_span.0.clone(), place_span.1, context, bk, borrow.clone()),
1075+
);
1076+
1077+
// Don't suppress actual errors.
1078+
Control::Continue
1079+
}
1080+
1081+
(Reservation(kind), _)
10121082
| (Activation(kind, _), _)
10131083
| (Write(kind), _) => {
10141084
match rw {
1015-
Reservation(_) => {
1085+
Reservation(..) => {
10161086
debug!(
10171087
"recording invalid reservation of \
10181088
place: {:?}",
@@ -1033,7 +1103,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10331103
error_reported = true;
10341104
match kind {
10351105
WriteKind::MutableBorrow(bk) => {
1036-
this.report_conflicting_borrow(context, place_span, bk, &borrow)
1106+
this.report_conflicting_borrow(context, place_span, bk, borrow)
1107+
.buffer(&mut this.errors_buffer);
10371108
}
10381109
WriteKind::StorageDeadOrDrop => {
10391110
this.report_borrowed_value_does_not_live_long_enough(
@@ -1046,7 +1117,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10461117
this.report_illegal_mutation_of_borrowed(context, place_span, borrow)
10471118
}
10481119
WriteKind::Move => {
1049-
this.report_move_out_while_borrowed(context, place_span, &borrow)
1120+
this.report_move_out_while_borrowed(context, place_span, borrow)
10501121
}
10511122
}
10521123
Control::Break

src/librustc_mir/borrow_check/nll/invalidation.rs

+12-13
Original file line numberDiff line numberDiff line change
@@ -428,11 +428,11 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cx, 'tcx, 'gcx> {
428428
// have already taken the reservation
429429
}
430430

431-
(Read(_), BorrowKind::Shallow) | (Reservation(..), BorrowKind::Shallow)
432-
| (Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared)
431+
(Read(_), BorrowKind::Shallow)
432+
| (Read(_), BorrowKind::Shared)
433433
| (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Unique)
434434
| (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Mut { .. }) => {
435-
// Reads/reservations don't invalidate shared or shallow borrows
435+
// Reads don't invalidate shared or shallow borrows
436436
}
437437

438438
(Read(_), BorrowKind::Unique) | (Read(_), BorrowKind::Mut { .. }) => {
@@ -448,16 +448,15 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cx, 'tcx, 'gcx> {
448448
this.generate_invalidates(borrow_index, context.loc);
449449
}
450450

451-
(Reservation(_), BorrowKind::Unique)
452-
| (Reservation(_), BorrowKind::Mut { .. })
453-
| (Activation(_, _), _)
454-
| (Write(_), _) => {
455-
// unique or mutable borrows are invalidated by writes.
456-
// Reservations count as writes since we need to check
457-
// that activating the borrow will be OK
458-
// FIXME(bob_twinkles) is this actually the right thing to do?
459-
this.generate_invalidates(borrow_index, context.loc);
460-
}
451+
(Reservation(_), _)
452+
| (Activation(_, _), _)
453+
| (Write(_), _) => {
454+
// unique or mutable borrows are invalidated by writes.
455+
// Reservations count as writes since we need to check
456+
// that activating the borrow will be OK
457+
// FIXME(bob_twinkles) is this actually the right thing to do?
458+
this.generate_invalidates(borrow_index, context.loc);
459+
}
461460
}
462461
Control::Continue
463462
},

0 commit comments

Comments
 (0)