Skip to content

Commit e798cb0

Browse files
committed
centralize does_not_live_long_enough error reporting
1 parent ed636c5 commit e798cb0

File tree

3 files changed

+89
-83
lines changed

3 files changed

+89
-83
lines changed

src/librustc_mir/borrow_check/error_reporting.rs

+32-7
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_data_structures::indexed_vec::Idx;
1616

1717
use super::{MirBorrowckCtxt, Context};
1818
use super::{InitializationRequiringAction, PrefixSet};
19-
use dataflow::{BorrowData, FlowAtLocation, MovingOutStatements};
19+
use dataflow::{BorrowData, Borrows, FlowAtLocation, MovingOutStatements};
2020
use dataflow::move_paths::MovePathIndex;
2121
use util::borrowck_errors::{BorrowckErrors, Origin};
2222

@@ -319,18 +319,43 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
319319
pub(super) fn report_borrowed_value_does_not_live_long_enough(
320320
&mut self,
321321
_: Context,
322-
(place, span): (&Place<'tcx>, Span),
323-
end_span: Option<Span>,
322+
borrow: &BorrowData<'tcx>,
323+
drop_span: Span,
324+
borrows: &Borrows<'cx, 'gcx, 'tcx>
324325
) {
325-
let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
326+
let end_span = borrows.opt_region_end_span(&borrow.region);
327+
let root_place = self.prefixes(&borrow.place, PrefixSet::All).last().unwrap();
328+
329+
match root_place {
330+
&Place::Local(local) => {
331+
if let Some(_) = self.storage_dead_or_drop_error_reported_l.replace(local) {
332+
debug!("report_does_not_live_long_enough({:?}): <suppressed>",
333+
(borrow, drop_span));
334+
return
335+
}
336+
}
337+
&Place::Static(ref statik) => {
338+
if let Some(_) = self.storage_dead_or_drop_error_reported_s
339+
.replace(statik.def_id)
340+
{
341+
debug!("report_does_not_live_long_enough({:?}): <suppressed>",
342+
(borrow, drop_span));
343+
return
344+
}
345+
},
346+
&Place::Projection(_) =>
347+
unreachable!("root_place is an unreachable???")
348+
};
349+
326350
let proper_span = match *root_place {
327351
Place::Local(local) => self.mir.local_decls[local].source_info.span,
328-
_ => span,
352+
_ => drop_span,
329353
};
354+
330355
let mut err = self.tcx
331-
.path_does_not_live_long_enough(span, "borrowed value", Origin::Mir);
356+
.path_does_not_live_long_enough(drop_span, "borrowed value", Origin::Mir);
332357
err.span_label(proper_span, "temporary value created here");
333-
err.span_label(span, "temporary value dropped here while still borrowed");
358+
err.span_label(drop_span, "temporary value dropped here while still borrowed");
334359
err.note("consider using a `let` binding to increase its lifetime");
335360

336361
if let Some(end) = end_span {

src/librustc_mir/borrow_check/mod.rs

+49-72
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
233233
hir::BodyOwnerKind::Static(_) => false,
234234
hir::BodyOwnerKind::Fn => true,
235235
},
236-
storage_dead_or_drop_error_reported: FxHashSet(),
236+
storage_dead_or_drop_error_reported_l: FxHashSet(),
237+
storage_dead_or_drop_error_reported_s: FxHashSet(),
237238
};
238239

239240
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
@@ -258,7 +259,9 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
258259
/// This field keeps track of when storage dead or drop errors are reported
259260
/// in order to stop duplicate error reporting and identify the conditions required
260261
/// for a "temporary value dropped here while still borrowed" error. See #45360.
261-
storage_dead_or_drop_error_reported: FxHashSet<Local>,
262+
storage_dead_or_drop_error_reported_l: FxHashSet<Local>,
263+
/// Same as the above, but for statics (thread-locals)
264+
storage_dead_or_drop_error_reported_s: FxHashSet<DefId>,
262265
}
263266

264267
// Check that:
@@ -502,19 +505,9 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
502505
flow_state.borrows.with_elems_outgoing(|borrows| {
503506
for i in borrows {
504507
let borrow = &data[i];
508+
let context = ContextKind::StorageDead.new(loc);
505509

506-
if self.place_is_invalidated_at_exit(&borrow.place) {
507-
debug!("borrow conflicts at exit {:?}", borrow);
508-
// FIXME: should be talking about the region lifetime instead
509-
// of just a span here.
510-
let end_span = domain.opt_region_end_span(&borrow.region);
511-
512-
self.report_borrowed_value_does_not_live_long_enough(
513-
ContextKind::StorageDead.new(loc),
514-
(&borrow.place, end_span.unwrap_or(span)),
515-
end_span,
516-
)
517-
}
510+
self.check_for_invalidation_at_exit(context, borrow, span, flow_state);
518511
}
519512
});
520513
}
@@ -663,34 +656,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
663656
) -> AccessErrorsReported {
664657
let (sd, rw) = kind;
665658

666-
let storage_dead_or_drop_local = match (place_span.0, rw) {
667-
(&Place::Local(local), Write(WriteKind::StorageDeadOrDrop)) => Some(local),
668-
_ => None,
669-
};
670-
671-
// Check if error has already been reported to stop duplicate reporting.
672-
if let Some(local) = storage_dead_or_drop_local {
673-
if self.storage_dead_or_drop_error_reported.contains(&local) {
674-
return AccessErrorsReported {
675-
mutability_error: false,
676-
conflict_error: true
677-
};
678-
}
679-
}
680-
681659
let mutability_error =
682660
self.check_access_permissions(place_span, rw, is_local_mutation_allowed);
683661
let conflict_error =
684662
self.check_access_for_conflict(context, place_span, sd, rw, flow_state);
685663

686-
// A conflict with a storagedead/drop is a "borrow does not live long enough"
687-
// error. Avoid reporting such an error multiple times for the same local.
688-
if conflict_error {
689-
if let Some(local) = storage_dead_or_drop_local {
690-
self.storage_dead_or_drop_error_reported.insert(local);
691-
}
692-
}
693-
694664
AccessErrorsReported { mutability_error, conflict_error }
695665
}
696666

@@ -749,16 +719,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
749719
)
750720
}
751721
WriteKind::StorageDeadOrDrop => {
752-
let end_span = flow_state
753-
.borrows
754-
.operator()
755-
.opt_region_end_span(&borrow.region);
756722
error_reported = true;
757723
this.report_borrowed_value_does_not_live_long_enough(
758-
context,
759-
place_span,
760-
end_span,
761-
)
724+
context, borrow, place_span.1, flow_state.borrows.operator());
762725
}
763726
WriteKind::Mutate => {
764727
error_reported = true;
@@ -947,16 +910,22 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
947910

948911
/// Returns whether a borrow of this place is invalidated when the function
949912
/// exits
950-
fn place_is_invalidated_at_exit(&mut self, place: &Place<'tcx>) -> bool {
951-
debug!("place_is_invalidated_at_exit({:?})", place);
913+
fn check_for_invalidation_at_exit(&mut self,
914+
context: Context,
915+
borrow: &BorrowData<'tcx>,
916+
span: Span,
917+
flow_state: &Flows<'cx, 'gcx, 'tcx>)
918+
{
919+
debug!("check_for_invalidation_at_exit({:?})", borrow);
920+
let place = &borrow.place;
952921
let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
953922

954923
// FIXME(nll-rfc#40): do more precise destructor tracking here. For now
955924
// we just know that all locals are dropped at function exit (otherwise
956925
// we'll have a memory leak) and assume that all statics have a destructor.
957926
//
958927
// FIXME: allow thread-locals to borrow other thread locals?
959-
let (might_be_alive, will_be_dropped, local) = match root_place {
928+
let (might_be_alive, will_be_dropped) = match root_place {
960929
Place::Static(statik) => {
961930
// Thread-locals might be dropped after the function exits, but
962931
// "true" statics will never be.
@@ -965,12 +934,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
965934
.iter()
966935
.any(|attr| attr.check_name("thread_local"));
967936

968-
(true, is_thread_local, None)
937+
(true, is_thread_local)
969938
}
970-
Place::Local(local) => {
939+
Place::Local(_) => {
971940
// Locals are always dropped at function exit, and if they
972941
// have a destructor it would've been called already.
973-
(false, self.locals_are_invalidated_at_exit, Some(*local))
942+
(false, self.locals_are_invalidated_at_exit)
974943
}
975944
Place::Projection(..) => {
976945
bug!("root of {:?} is a projection ({:?})?", place, root_place)
@@ -982,30 +951,28 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
982951
"place_is_invalidated_at_exit({:?}) - won't be dropped",
983952
place
984953
);
985-
return false;
954+
return;
986955
}
987956

988957
// FIXME: replace this with a proper borrow_conflicts_with_place when
989958
// that is merged.
990-
let prefix_set = if might_be_alive {
991-
PrefixSet::Supporting
959+
let sd = if might_be_alive {
960+
Deep
992961
} else {
993-
PrefixSet::Shallow
962+
Shallow(None)
994963
};
995964

996-
let result =
997-
self.prefixes(place, prefix_set).any(|prefix| prefix == root_place);
998-
999-
if result {
1000-
if let Some(local) = local {
1001-
if let Some(_) = self.storage_dead_or_drop_error_reported.replace(local) {
1002-
debug!("place_is_invalidated_at_exit({:?}) - suppressed", place);
1003-
return false;
1004-
}
1005-
}
965+
if self.places_conflict(place, root_place, sd) {
966+
debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
967+
// FIXME: should be talking about the region lifetime instead
968+
// of just a span here.
969+
self.report_borrowed_value_does_not_live_long_enough(
970+
context,
971+
borrow,
972+
span.end_point(),
973+
flow_state.borrows.operator()
974+
)
1006975
}
1007-
1008-
result
1009976
}
1010977
}
1011978

@@ -1358,7 +1325,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13581325
// Mutably borrowed data is mutable, but only if we have a
13591326
// unique path to the `&mut`
13601327
hir::MutMutable => {
1361-
let mode = match self.is_upvar_field_projection(&proj.base) {
1328+
let mode = match
1329+
self.is_upvar_field_projection(&proj.base)
1330+
{
13621331
Some(field) if {
13631332
self.mir.upvar_decls[field.index()].by_ref
13641333
} => is_local_mutation_allowed,
@@ -1485,10 +1454,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
14851454
Overlap::Disjoint
14861455
}
14871456
}
1488-
(Place::Static(..), Place::Static(..)) => {
1489-
// Borrows of statics do not have to be tracked here.
1490-
debug!("place_element_conflict: IGNORED-STATIC");
1491-
Overlap::Disjoint
1457+
(Place::Static(static1), Place::Static(static2)) => {
1458+
if static1.def_id != static2.def_id {
1459+
debug!("place_element_conflict: DISJOINT-STATIC");
1460+
Overlap::Disjoint
1461+
} else if self.tcx.is_static_mut(static1.def_id) {
1462+
// We ignore mutable statics - they can only be unsafe code.
1463+
debug!("place_element_conflict: IGNORE-STATIC-MUT");
1464+
Overlap::Disjoint
1465+
} else {
1466+
debug!("place_element_conflict: DISJOINT-OR-EQ-STATIC");
1467+
Overlap::EqualOrDisjoint
1468+
}
14921469
}
14931470
(Place::Local(_), Place::Static(_)) |
14941471
(Place::Static(_), Place::Local(_)) => {

src/test/compile-fail/issue-17954.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,22 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// revisions: ast mir
12+
//[mir]compile-flags: -Z borrowck=mir
13+
1114
#![feature(thread_local)]
1215

1316
#[thread_local]
1417
static FOO: u8 = 3;
1518

1619
fn main() {
1720
let a = &FOO;
18-
//~^ ERROR borrowed value does not live long enough
19-
//~| does not live long enough
20-
//~| NOTE borrowed value must be valid for the static lifetime
21+
//[ast]~^ ERROR borrowed value does not live long enough
22+
//[ast]~| does not live long enough
23+
//[ast]~| NOTE borrowed value must be valid for the static lifetime
2124

2225
std::thread::spawn(move || {
2326
println!("{}", a);
2427
});
25-
} //~ temporary value only lives until here
28+
} //[ast]~ temporary value only lives until here
29+
//[mir]~^ ERROR borrowed value does not live long enough

0 commit comments

Comments
 (0)