Skip to content

Commit f8e2beb

Browse files
committed
Treat two-phase borrow reservations as mutable accesses
1 parent 7eda723 commit f8e2beb

11 files changed

+328
-75
lines changed

src/librustc_mir/borrow_check/borrow_set.rs

Lines changed: 1 addition & 1 deletion
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

Lines changed: 5 additions & 6 deletions
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

Lines changed: 78 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ use rustc::ty::{self, TyCtxt};
1818

1919
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, Level};
2020
use rustc_data_structures::bit_set::BitSet;
21-
use rustc_data_structures::fx::FxHashSet;
21+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2222
use rustc_data_structures::graph::dominators::Dominators;
2323
use smallvec::SmallVec;
2424

25-
use std::rc::Rc;
2625
use std::collections::BTreeMap;
26+
use std::mem;
27+
use std::rc::Rc;
2728

2829
use syntax_pos::Span;
2930

@@ -238,6 +239,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
238239
locals_are_invalidated_at_exit,
239240
access_place_error_reported: Default::default(),
240241
reservation_error_reported: Default::default(),
242+
reservation_warnings: Default::default(),
241243
move_error_reported: BTreeMap::new(),
242244
uninitialized_error_reported: Default::default(),
243245
errors_buffer,
@@ -260,6 +262,14 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
260262
}
261263
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
262264

265+
// Buffer any reservation warnings.
266+
let reservation_warnings = mem::replace(&mut mbcx.reservation_warnings, Default::default());
267+
for (_, (place, span, context, bk, borrow)) in reservation_warnings {
268+
let mut diag = mbcx.report_conflicting_borrow(context, (&place, span), bk, &borrow);
269+
downgrade_if_error(&mut diag);
270+
diag.buffer(&mut mbcx.errors_buffer);
271+
}
272+
263273
// For each non-user used mutable variable, check if it's been assigned from
264274
// a user-declared local. If so, then put that local into the used_mut set.
265275
// Note that this set is expected to be small - only upvars from closures
@@ -341,18 +351,9 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
341351
// if AST-borrowck signalled no errors, then
342352
// downgrade all the buffered MIR-borrowck errors
343353
// 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-
}
354+
355+
for err in mbcx.errors_buffer.iter_mut() {
356+
downgrade_if_error(err);
356357
}
357358
}
358359
SignalledError::SawSomeError => {
@@ -378,6 +379,20 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
378379
result
379380
}
380381

382+
fn downgrade_if_error(diag: &mut Diagnostic) {
383+
if diag.is_error() {
384+
diag.level = Level::Warning;
385+
diag.warn(
386+
"this error has been downgraded to a warning for backwards \
387+
compatibility with previous releases",
388+
);
389+
diag.warn(
390+
"this represents potential undefined behavior in your code and \
391+
this warning will become a hard error in the future",
392+
);
393+
}
394+
}
395+
381396
pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
382397
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
383398
mir: &'cx Mir<'tcx>,
@@ -410,6 +425,13 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
410425
// but it is currently inconvenient to track down the `BorrowIndex`
411426
// at the time we detect and report a reservation error.
412427
reservation_error_reported: FxHashSet<Place<'tcx>>,
428+
/// Migration warnings to be reported for #56254. We delay reporting these
429+
/// so that we can suppress the warning if there's a corresponding error
430+
/// for the activation of the borrow.
431+
reservation_warnings: FxHashMap<
432+
BorrowIndex,
433+
(Place<'tcx>, Span, Context, BorrowKind, BorrowData<'tcx>)
434+
>,
413435
/// This field keeps track of move errors that are to be reported for given move indicies.
414436
///
415437
/// There are situations where many errors can be reported for a single move out (see #53807)
@@ -921,11 +943,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
921943
let conflict_error =
922944
self.check_access_for_conflict(context, place_span, sd, rw, flow_state);
923945

946+
if let (Activation(_, borrow_idx), true) = (kind.1, conflict_error) {
947+
// Suppress this warning when there's an error being emited for the
948+
// same borrow: fixing the error is likely to fix the warning.
949+
self.reservation_warnings.remove(&borrow_idx);
950+
}
951+
924952
if conflict_error || mutability_error {
925953
debug!(
926954
"access_place: logging error place_span=`{:?}` kind=`{:?}`",
927955
place_span, kind
928956
);
957+
929958
self.access_place_error_reported
930959
.insert((place_span.0.clone(), place_span.1));
931960
}
@@ -976,8 +1005,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
9761005
Control::Continue
9771006
}
9781007

979-
(Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared)
980-
| (Read(_), BorrowKind::Shallow) | (Reservation(..), BorrowKind::Shallow)
1008+
(Read(_), BorrowKind::Shared)
1009+
| (Read(_), BorrowKind::Shallow)
9811010
| (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Unique)
9821011
| (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Mut { .. }) => {
9831012
Control::Continue
@@ -991,28 +1020,53 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
9911020
(Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut { .. }) => {
9921021
// Reading from mere reservations of mutable-borrows is OK.
9931022
if !is_active(&this.dominators, borrow, context.loc) {
994-
assert!(allow_two_phase_borrow(&this.infcx.tcx, borrow.kind));
1023+
assert!(allow_two_phase_borrow(&tcx, borrow.kind));
9951024
return Control::Continue;
9961025
}
9971026

9981027
error_reported = true;
9991028
match kind {
10001029
ReadKind::Copy => {
10011030
this.report_use_while_mutably_borrowed(context, place_span, borrow)
1031+
.buffer(&mut this.errors_buffer);
10021032
}
10031033
ReadKind::Borrow(bk) => {
1004-
this.report_conflicting_borrow(context, place_span, bk, &borrow)
1034+
this.report_conflicting_borrow(context, place_span, bk, borrow)
1035+
.buffer(&mut this.errors_buffer);
10051036
}
10061037
}
10071038
Control::Break
10081039
}
10091040

1010-
(Reservation(kind), BorrowKind::Unique)
1011-
| (Reservation(kind), BorrowKind::Mut { .. })
1041+
(Reservation(WriteKind::MutableBorrow(bk)), BorrowKind::Shallow)
1042+
| (Reservation(WriteKind::MutableBorrow(bk)), BorrowKind::Shared) if {
1043+
tcx.migrate_borrowck()
1044+
} => {
1045+
let bi = this.borrow_set.location_map[&context.loc];
1046+
debug!(
1047+
"recording invalid reservation of place: {:?} with \
1048+
borrow index {:?} as warning",
1049+
place_span.0,
1050+
bi,
1051+
);
1052+
// rust-lang/rust#56254 - This was previously permitted on
1053+
// the 2018 edition so we emit it as a warning. We buffer
1054+
// these sepately so that we only emit a warning if borrow
1055+
// checking was otherwise successful.
1056+
this.reservation_warnings.insert(
1057+
bi,
1058+
(place_span.0.clone(), place_span.1, context, bk, borrow.clone()),
1059+
);
1060+
1061+
// Don't suppress actual errors.
1062+
Control::Continue
1063+
}
1064+
1065+
(Reservation(kind), _)
10121066
| (Activation(kind, _), _)
10131067
| (Write(kind), _) => {
10141068
match rw {
1015-
Reservation(_) => {
1069+
Reservation(..) => {
10161070
debug!(
10171071
"recording invalid reservation of \
10181072
place: {:?}",
@@ -1033,7 +1087,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10331087
error_reported = true;
10341088
match kind {
10351089
WriteKind::MutableBorrow(bk) => {
1036-
this.report_conflicting_borrow(context, place_span, bk, &borrow)
1090+
this.report_conflicting_borrow(context, place_span, bk, borrow)
1091+
.buffer(&mut this.errors_buffer);
10371092
}
10381093
WriteKind::StorageDeadOrDrop => {
10391094
this.report_borrowed_value_does_not_live_long_enough(
@@ -1046,7 +1101,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10461101
this.report_illegal_mutation_of_borrowed(context, place_span, borrow)
10471102
}
10481103
WriteKind::Move => {
1049-
this.report_move_out_while_borrowed(context, place_span, &borrow)
1104+
this.report_move_out_while_borrowed(context, place_span, borrow)
10501105
}
10511106
}
10521107
Control::Break

src/librustc_mir/borrow_check/nll/invalidation.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -428,8 +428,8 @@ 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 { .. }) => {
435435
// Reads/reservations don't invalidate shared or shallow borrows
@@ -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
},
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
2+
--> $DIR/two-phase-reservation-sharing-interference-2.rs:19:5
3+
|
4+
LL | let shared = &v;
5+
| - immutable borrow occurs here
6+
LL |
7+
LL | v.extend(shared);
8+
| ^ mutable borrow occurs here
9+
...
10+
LL | }
11+
| - immutable borrow ends here
12+
13+
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
14+
--> $DIR/two-phase-reservation-sharing-interference-2.rs:30:15
15+
|
16+
LL | v.extend(&v);
17+
| - ^- mutable borrow ends here
18+
| | |
19+
| | immutable borrow occurs here
20+
| mutable borrow occurs here
21+
22+
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
23+
--> $DIR/two-phase-reservation-sharing-interference-2.rs:42:5
24+
|
25+
LL | let shared = &v;
26+
| - immutable borrow occurs here
27+
LL |
28+
LL | v.push(shared.len());
29+
| ^ mutable borrow occurs here
30+
...
31+
LL | }
32+
| - immutable borrow ends here
33+
34+
error: aborting due to 3 previous errors
35+
36+
For more information about this error, try `rustc --explain E0502`.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
2+
--> $DIR/two-phase-reservation-sharing-interference-2.rs:19:5
3+
|
4+
LL | let shared = &v;
5+
| -- immutable borrow occurs here
6+
LL |
7+
LL | v.extend(shared);
8+
| ^^------^^^^^^^^
9+
| | |
10+
| | immutable borrow later used by call
11+
| mutable borrow occurs here
12+
13+
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
14+
--> $DIR/two-phase-reservation-sharing-interference-2.rs:30:5
15+
|
16+
LL | v.extend(&v);
17+
| ^^------^--^
18+
| | | |
19+
| | | immutable borrow occurs here
20+
| | immutable borrow later used by call
21+
| mutable borrow occurs here
22+
23+
warning[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
24+
--> $DIR/two-phase-reservation-sharing-interference-2.rs:42:5
25+
|
26+
LL | let shared = &v;
27+
| -- immutable borrow occurs here
28+
LL |
29+
LL | v.push(shared.len());
30+
| ^ ------ immutable borrow later used here
31+
| |
32+
| mutable borrow occurs here
33+
|
34+
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
35+
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
36+
37+
error: aborting due to 2 previous errors
38+
39+
For more information about this error, try `rustc --explain E0502`.

0 commit comments

Comments
 (0)