mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 23:34:48 +00:00
centralize does_not_live_long_enough
error reporting
This commit is contained in:
parent
ed636c5c5a
commit
e798cb0e52
@ -16,7 +16,7 @@ use rustc_data_structures::indexed_vec::Idx;
|
|||||||
|
|
||||||
use super::{MirBorrowckCtxt, Context};
|
use super::{MirBorrowckCtxt, Context};
|
||||||
use super::{InitializationRequiringAction, PrefixSet};
|
use super::{InitializationRequiringAction, PrefixSet};
|
||||||
use dataflow::{BorrowData, FlowAtLocation, MovingOutStatements};
|
use dataflow::{BorrowData, Borrows, FlowAtLocation, MovingOutStatements};
|
||||||
use dataflow::move_paths::MovePathIndex;
|
use dataflow::move_paths::MovePathIndex;
|
||||||
use util::borrowck_errors::{BorrowckErrors, Origin};
|
use util::borrowck_errors::{BorrowckErrors, Origin};
|
||||||
|
|
||||||
@ -319,18 +319,43 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
pub(super) fn report_borrowed_value_does_not_live_long_enough(
|
pub(super) fn report_borrowed_value_does_not_live_long_enough(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Context,
|
_: Context,
|
||||||
(place, span): (&Place<'tcx>, Span),
|
borrow: &BorrowData<'tcx>,
|
||||||
end_span: Option<Span>,
|
drop_span: Span,
|
||||||
|
borrows: &Borrows<'cx, 'gcx, 'tcx>
|
||||||
) {
|
) {
|
||||||
let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
|
let end_span = borrows.opt_region_end_span(&borrow.region);
|
||||||
|
let root_place = self.prefixes(&borrow.place, PrefixSet::All).last().unwrap();
|
||||||
|
|
||||||
|
match root_place {
|
||||||
|
&Place::Local(local) => {
|
||||||
|
if let Some(_) = self.storage_dead_or_drop_error_reported_l.replace(local) {
|
||||||
|
debug!("report_does_not_live_long_enough({:?}): <suppressed>",
|
||||||
|
(borrow, drop_span));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&Place::Static(ref statik) => {
|
||||||
|
if let Some(_) = self.storage_dead_or_drop_error_reported_s
|
||||||
|
.replace(statik.def_id)
|
||||||
|
{
|
||||||
|
debug!("report_does_not_live_long_enough({:?}): <suppressed>",
|
||||||
|
(borrow, drop_span));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
&Place::Projection(_) =>
|
||||||
|
unreachable!("root_place is an unreachable???")
|
||||||
|
};
|
||||||
|
|
||||||
let proper_span = match *root_place {
|
let proper_span = match *root_place {
|
||||||
Place::Local(local) => self.mir.local_decls[local].source_info.span,
|
Place::Local(local) => self.mir.local_decls[local].source_info.span,
|
||||||
_ => span,
|
_ => drop_span,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut err = self.tcx
|
let mut err = self.tcx
|
||||||
.path_does_not_live_long_enough(span, "borrowed value", Origin::Mir);
|
.path_does_not_live_long_enough(drop_span, "borrowed value", Origin::Mir);
|
||||||
err.span_label(proper_span, "temporary value created here");
|
err.span_label(proper_span, "temporary value created here");
|
||||||
err.span_label(span, "temporary value dropped here while still borrowed");
|
err.span_label(drop_span, "temporary value dropped here while still borrowed");
|
||||||
err.note("consider using a `let` binding to increase its lifetime");
|
err.note("consider using a `let` binding to increase its lifetime");
|
||||||
|
|
||||||
if let Some(end) = end_span {
|
if let Some(end) = end_span {
|
||||||
|
@ -233,7 +233,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
|||||||
hir::BodyOwnerKind::Static(_) => false,
|
hir::BodyOwnerKind::Static(_) => false,
|
||||||
hir::BodyOwnerKind::Fn => true,
|
hir::BodyOwnerKind::Fn => true,
|
||||||
},
|
},
|
||||||
storage_dead_or_drop_error_reported: FxHashSet(),
|
storage_dead_or_drop_error_reported_l: FxHashSet(),
|
||||||
|
storage_dead_or_drop_error_reported_s: FxHashSet(),
|
||||||
};
|
};
|
||||||
|
|
||||||
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
|
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
|
||||||
@ -258,7 +259,9 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
|
|||||||
/// This field keeps track of when storage dead or drop errors are reported
|
/// This field keeps track of when storage dead or drop errors are reported
|
||||||
/// in order to stop duplicate error reporting and identify the conditions required
|
/// in order to stop duplicate error reporting and identify the conditions required
|
||||||
/// for a "temporary value dropped here while still borrowed" error. See #45360.
|
/// for a "temporary value dropped here while still borrowed" error. See #45360.
|
||||||
storage_dead_or_drop_error_reported: FxHashSet<Local>,
|
storage_dead_or_drop_error_reported_l: FxHashSet<Local>,
|
||||||
|
/// Same as the above, but for statics (thread-locals)
|
||||||
|
storage_dead_or_drop_error_reported_s: FxHashSet<DefId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that:
|
// Check that:
|
||||||
@ -502,19 +505,9 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
|
|||||||
flow_state.borrows.with_elems_outgoing(|borrows| {
|
flow_state.borrows.with_elems_outgoing(|borrows| {
|
||||||
for i in borrows {
|
for i in borrows {
|
||||||
let borrow = &data[i];
|
let borrow = &data[i];
|
||||||
|
let context = ContextKind::StorageDead.new(loc);
|
||||||
|
|
||||||
if self.place_is_invalidated_at_exit(&borrow.place) {
|
self.check_for_invalidation_at_exit(context, borrow, span, flow_state);
|
||||||
debug!("borrow conflicts at exit {:?}", borrow);
|
|
||||||
// FIXME: should be talking about the region lifetime instead
|
|
||||||
// of just a span here.
|
|
||||||
let end_span = domain.opt_region_end_span(&borrow.region);
|
|
||||||
|
|
||||||
self.report_borrowed_value_does_not_live_long_enough(
|
|
||||||
ContextKind::StorageDead.new(loc),
|
|
||||||
(&borrow.place, end_span.unwrap_or(span)),
|
|
||||||
end_span,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -663,34 +656,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
) -> AccessErrorsReported {
|
) -> AccessErrorsReported {
|
||||||
let (sd, rw) = kind;
|
let (sd, rw) = kind;
|
||||||
|
|
||||||
let storage_dead_or_drop_local = match (place_span.0, rw) {
|
|
||||||
(&Place::Local(local), Write(WriteKind::StorageDeadOrDrop)) => Some(local),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check if error has already been reported to stop duplicate reporting.
|
|
||||||
if let Some(local) = storage_dead_or_drop_local {
|
|
||||||
if self.storage_dead_or_drop_error_reported.contains(&local) {
|
|
||||||
return AccessErrorsReported {
|
|
||||||
mutability_error: false,
|
|
||||||
conflict_error: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mutability_error =
|
let mutability_error =
|
||||||
self.check_access_permissions(place_span, rw, is_local_mutation_allowed);
|
self.check_access_permissions(place_span, rw, is_local_mutation_allowed);
|
||||||
let conflict_error =
|
let conflict_error =
|
||||||
self.check_access_for_conflict(context, place_span, sd, rw, flow_state);
|
self.check_access_for_conflict(context, place_span, sd, rw, flow_state);
|
||||||
|
|
||||||
// A conflict with a storagedead/drop is a "borrow does not live long enough"
|
|
||||||
// error. Avoid reporting such an error multiple times for the same local.
|
|
||||||
if conflict_error {
|
|
||||||
if let Some(local) = storage_dead_or_drop_local {
|
|
||||||
self.storage_dead_or_drop_error_reported.insert(local);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AccessErrorsReported { mutability_error, conflict_error }
|
AccessErrorsReported { mutability_error, conflict_error }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,16 +719,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
WriteKind::StorageDeadOrDrop => {
|
WriteKind::StorageDeadOrDrop => {
|
||||||
let end_span = flow_state
|
|
||||||
.borrows
|
|
||||||
.operator()
|
|
||||||
.opt_region_end_span(&borrow.region);
|
|
||||||
error_reported = true;
|
error_reported = true;
|
||||||
this.report_borrowed_value_does_not_live_long_enough(
|
this.report_borrowed_value_does_not_live_long_enough(
|
||||||
context,
|
context, borrow, place_span.1, flow_state.borrows.operator());
|
||||||
place_span,
|
|
||||||
end_span,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
WriteKind::Mutate => {
|
WriteKind::Mutate => {
|
||||||
error_reported = true;
|
error_reported = true;
|
||||||
@ -947,8 +910,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
|
|
||||||
/// Returns whether a borrow of this place is invalidated when the function
|
/// Returns whether a borrow of this place is invalidated when the function
|
||||||
/// exits
|
/// exits
|
||||||
fn place_is_invalidated_at_exit(&mut self, place: &Place<'tcx>) -> bool {
|
fn check_for_invalidation_at_exit(&mut self,
|
||||||
debug!("place_is_invalidated_at_exit({:?})", place);
|
context: Context,
|
||||||
|
borrow: &BorrowData<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
flow_state: &Flows<'cx, 'gcx, 'tcx>)
|
||||||
|
{
|
||||||
|
debug!("check_for_invalidation_at_exit({:?})", borrow);
|
||||||
|
let place = &borrow.place;
|
||||||
let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
|
let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
|
||||||
|
|
||||||
// FIXME(nll-rfc#40): do more precise destructor tracking here. For now
|
// FIXME(nll-rfc#40): do more precise destructor tracking here. For now
|
||||||
@ -956,7 +925,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
// we'll have a memory leak) and assume that all statics have a destructor.
|
// we'll have a memory leak) and assume that all statics have a destructor.
|
||||||
//
|
//
|
||||||
// FIXME: allow thread-locals to borrow other thread locals?
|
// FIXME: allow thread-locals to borrow other thread locals?
|
||||||
let (might_be_alive, will_be_dropped, local) = match root_place {
|
let (might_be_alive, will_be_dropped) = match root_place {
|
||||||
Place::Static(statik) => {
|
Place::Static(statik) => {
|
||||||
// Thread-locals might be dropped after the function exits, but
|
// Thread-locals might be dropped after the function exits, but
|
||||||
// "true" statics will never be.
|
// "true" statics will never be.
|
||||||
@ -965,12 +934,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
.iter()
|
.iter()
|
||||||
.any(|attr| attr.check_name("thread_local"));
|
.any(|attr| attr.check_name("thread_local"));
|
||||||
|
|
||||||
(true, is_thread_local, None)
|
(true, is_thread_local)
|
||||||
}
|
}
|
||||||
Place::Local(local) => {
|
Place::Local(_) => {
|
||||||
// Locals are always dropped at function exit, and if they
|
// Locals are always dropped at function exit, and if they
|
||||||
// have a destructor it would've been called already.
|
// have a destructor it would've been called already.
|
||||||
(false, self.locals_are_invalidated_at_exit, Some(*local))
|
(false, self.locals_are_invalidated_at_exit)
|
||||||
}
|
}
|
||||||
Place::Projection(..) => {
|
Place::Projection(..) => {
|
||||||
bug!("root of {:?} is a projection ({:?})?", place, root_place)
|
bug!("root of {:?} is a projection ({:?})?", place, root_place)
|
||||||
@ -982,30 +951,28 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
"place_is_invalidated_at_exit({:?}) - won't be dropped",
|
"place_is_invalidated_at_exit({:?}) - won't be dropped",
|
||||||
place
|
place
|
||||||
);
|
);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: replace this with a proper borrow_conflicts_with_place when
|
// FIXME: replace this with a proper borrow_conflicts_with_place when
|
||||||
// that is merged.
|
// that is merged.
|
||||||
let prefix_set = if might_be_alive {
|
let sd = if might_be_alive {
|
||||||
PrefixSet::Supporting
|
Deep
|
||||||
} else {
|
} else {
|
||||||
PrefixSet::Shallow
|
Shallow(None)
|
||||||
};
|
};
|
||||||
|
|
||||||
let result =
|
if self.places_conflict(place, root_place, sd) {
|
||||||
self.prefixes(place, prefix_set).any(|prefix| prefix == root_place);
|
debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
|
||||||
|
// FIXME: should be talking about the region lifetime instead
|
||||||
if result {
|
// of just a span here.
|
||||||
if let Some(local) = local {
|
self.report_borrowed_value_does_not_live_long_enough(
|
||||||
if let Some(_) = self.storage_dead_or_drop_error_reported.replace(local) {
|
context,
|
||||||
debug!("place_is_invalidated_at_exit({:?}) - suppressed", place);
|
borrow,
|
||||||
return false;
|
span.end_point(),
|
||||||
}
|
flow_state.borrows.operator()
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1358,7 +1325,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
// Mutably borrowed data is mutable, but only if we have a
|
// Mutably borrowed data is mutable, but only if we have a
|
||||||
// unique path to the `&mut`
|
// unique path to the `&mut`
|
||||||
hir::MutMutable => {
|
hir::MutMutable => {
|
||||||
let mode = match self.is_upvar_field_projection(&proj.base) {
|
let mode = match
|
||||||
|
self.is_upvar_field_projection(&proj.base)
|
||||||
|
{
|
||||||
Some(field) if {
|
Some(field) if {
|
||||||
self.mir.upvar_decls[field.index()].by_ref
|
self.mir.upvar_decls[field.index()].by_ref
|
||||||
} => is_local_mutation_allowed,
|
} => is_local_mutation_allowed,
|
||||||
@ -1485,10 +1454,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
Overlap::Disjoint
|
Overlap::Disjoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Place::Static(..), Place::Static(..)) => {
|
(Place::Static(static1), Place::Static(static2)) => {
|
||||||
// Borrows of statics do not have to be tracked here.
|
if static1.def_id != static2.def_id {
|
||||||
debug!("place_element_conflict: IGNORED-STATIC");
|
debug!("place_element_conflict: DISJOINT-STATIC");
|
||||||
Overlap::Disjoint
|
Overlap::Disjoint
|
||||||
|
} else if self.tcx.is_static_mut(static1.def_id) {
|
||||||
|
// We ignore mutable statics - they can only be unsafe code.
|
||||||
|
debug!("place_element_conflict: IGNORE-STATIC-MUT");
|
||||||
|
Overlap::Disjoint
|
||||||
|
} else {
|
||||||
|
debug!("place_element_conflict: DISJOINT-OR-EQ-STATIC");
|
||||||
|
Overlap::EqualOrDisjoint
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(Place::Local(_), Place::Static(_)) |
|
(Place::Local(_), Place::Static(_)) |
|
||||||
(Place::Static(_), Place::Local(_)) => {
|
(Place::Static(_), Place::Local(_)) => {
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
// revisions: ast mir
|
||||||
|
//[mir]compile-flags: -Z borrowck=mir
|
||||||
|
|
||||||
#![feature(thread_local)]
|
#![feature(thread_local)]
|
||||||
|
|
||||||
#[thread_local]
|
#[thread_local]
|
||||||
@ -15,11 +18,12 @@ static FOO: u8 = 3;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let a = &FOO;
|
let a = &FOO;
|
||||||
//~^ ERROR borrowed value does not live long enough
|
//[ast]~^ ERROR borrowed value does not live long enough
|
||||||
//~| does not live long enough
|
//[ast]~| does not live long enough
|
||||||
//~| NOTE borrowed value must be valid for the static lifetime
|
//[ast]~| NOTE borrowed value must be valid for the static lifetime
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
println!("{}", a);
|
println!("{}", a);
|
||||||
});
|
});
|
||||||
} //~ temporary value only lives until here
|
} //[ast]~ temporary value only lives until here
|
||||||
|
//[mir]~^ ERROR borrowed value does not live long enough
|
||||||
|
Loading…
Reference in New Issue
Block a user