Auto merge of #54509 - matthewjasper:better-drop-access, r=pnkfelix
[NLL] Rework checking for borrows conflicting with drops Previously, we would split the drop access into multiple checks for each field of a struct/tuple/closure and through `Box` dereferences. This changes this to check if the borrow is accessed by the drop in `places_conflict`. We also now handle enums containing `Drop` types. Closes #53569 r? @nikomatsakis cc @pnkfelix
This commit is contained in:
commit
a072d1bca6
@ -8,14 +8,14 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use borrow_check::{WriteKind, StorageDeadOrDrop};
|
||||
use borrow_check::WriteKind;
|
||||
use borrow_check::prefixes::IsPrefixOf;
|
||||
use borrow_check::nll::explain_borrow::BorrowExplanation;
|
||||
use rustc::middle::region::ScopeTree;
|
||||
use rustc::mir::{
|
||||
self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, FakeReadCause, Field, Local,
|
||||
LocalDecl, LocalKind, Location, Operand, Place, ProjectionElem, Rvalue, Statement,
|
||||
StatementKind, TerminatorKind, VarBindingForm,
|
||||
LocalDecl, LocalKind, Location, Operand, Place, PlaceProjection, ProjectionElem, Rvalue,
|
||||
Statement, StatementKind, TerminatorKind, VarBindingForm,
|
||||
};
|
||||
use rustc::hir;
|
||||
use rustc::hir::def_id::DefId;
|
||||
@ -452,13 +452,21 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
self.access_place_error_reported
|
||||
.insert((root_place.clone(), borrow_span));
|
||||
|
||||
if let Some(WriteKind::StorageDeadOrDrop(StorageDeadOrDrop::Destructor)) = kind {
|
||||
if let StorageDeadOrDrop::Destructor(dropped_ty)
|
||||
= self.classify_drop_access_kind(&borrow.borrowed_place)
|
||||
{
|
||||
// If a borrow of path `B` conflicts with drop of `D` (and
|
||||
// we're not in the uninteresting case where `B` is a
|
||||
// prefix of `D`), then report this as a more interesting
|
||||
// destructor conflict.
|
||||
if !borrow.borrowed_place.is_prefix_of(place_span.0) {
|
||||
self.report_borrow_conflicts_with_destructor(context, borrow, place_span, kind);
|
||||
self.report_borrow_conflicts_with_destructor(
|
||||
context,
|
||||
borrow,
|
||||
place_span,
|
||||
kind,
|
||||
dropped_ty,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -566,6 +574,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
borrow: &BorrowData<'tcx>,
|
||||
(place, drop_span): (&Place<'tcx>, Span),
|
||||
kind: Option<WriteKind>,
|
||||
dropped_ty: ty::Ty<'tcx>,
|
||||
) {
|
||||
debug!(
|
||||
"report_borrow_conflicts_with_destructor(\
|
||||
@ -579,28 +588,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
|
||||
let mut err = self.infcx.tcx.cannot_borrow_across_destructor(borrow_span, Origin::Mir);
|
||||
|
||||
let (what_was_dropped, dropped_ty) = {
|
||||
let desc = match self.describe_place(place) {
|
||||
Some(name) => format!("`{}`", name.as_str()),
|
||||
None => format!("temporary value"),
|
||||
};
|
||||
let ty = place.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
|
||||
(desc, ty)
|
||||
let what_was_dropped = match self.describe_place(place) {
|
||||
Some(name) => format!("`{}`", name.as_str()),
|
||||
None => format!("temporary value"),
|
||||
};
|
||||
|
||||
let label = match dropped_ty.sty {
|
||||
ty::Adt(adt, _) if adt.has_dtor(self.infcx.tcx) && !adt.is_box() => {
|
||||
match self.describe_place(&borrow.borrowed_place) {
|
||||
Some(borrowed) =>
|
||||
format!("here, drop of {D} needs exclusive access to `{B}`, \
|
||||
because the type `{T}` implements the `Drop` trait",
|
||||
D=what_was_dropped, T=dropped_ty, B=borrowed),
|
||||
None =>
|
||||
format!("here is drop of {D}; whose type `{T}` implements the `Drop` trait",
|
||||
D=what_was_dropped, T=dropped_ty),
|
||||
}
|
||||
}
|
||||
_ => format!("drop of {D} occurs here", D=what_was_dropped),
|
||||
let label = match self.describe_place(&borrow.borrowed_place) {
|
||||
Some(borrowed) =>
|
||||
format!("here, drop of {D} needs exclusive access to `{B}`, \
|
||||
because the type `{T}` implements the `Drop` trait",
|
||||
D=what_was_dropped, T=dropped_ty, B=borrowed),
|
||||
None =>
|
||||
format!("here is drop of {D}; whose type `{T}` implements the `Drop` trait",
|
||||
D=what_was_dropped, T=dropped_ty),
|
||||
};
|
||||
err.span_label(drop_span, label);
|
||||
|
||||
@ -880,6 +880,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
|
||||
pub(super) struct IncludingDowncast(bool);
|
||||
|
||||
/// Which case a StorageDeadOrDrop is for.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum StorageDeadOrDrop<'tcx> {
|
||||
LocalStorageDead,
|
||||
BoxedStorageDead,
|
||||
Destructor(ty::Ty<'tcx>),
|
||||
}
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
// End-user visible description of `place` if one can be found. If the
|
||||
// place is a temporary for instance, None will be returned.
|
||||
@ -1167,6 +1175,56 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn classify_drop_access_kind(&self, place: &Place<'tcx>) -> StorageDeadOrDrop<'tcx> {
|
||||
let tcx = self.infcx.tcx;
|
||||
match place {
|
||||
Place::Local(_)
|
||||
| Place::Static(_)
|
||||
| Place::Promoted(_) => StorageDeadOrDrop::LocalStorageDead,
|
||||
Place::Projection(box PlaceProjection { base, elem }) => {
|
||||
let base_access = self.classify_drop_access_kind(base);
|
||||
match elem {
|
||||
ProjectionElem::Deref => {
|
||||
match base_access {
|
||||
StorageDeadOrDrop::LocalStorageDead
|
||||
| StorageDeadOrDrop::BoxedStorageDead => {
|
||||
assert!(base.ty(self.mir, tcx).to_ty(tcx).is_box(),
|
||||
"Drop of value behind a reference or raw pointer");
|
||||
StorageDeadOrDrop::BoxedStorageDead
|
||||
}
|
||||
StorageDeadOrDrop::Destructor(_) => {
|
||||
base_access
|
||||
}
|
||||
}
|
||||
}
|
||||
ProjectionElem::Field(..)
|
||||
| ProjectionElem::Downcast(..) => {
|
||||
let base_ty = base.ty(self.mir, tcx).to_ty(tcx);
|
||||
match base_ty.sty {
|
||||
ty::Adt(def, _) if def.has_dtor(tcx) => {
|
||||
// Report the outermost adt with a destructor
|
||||
match base_access {
|
||||
StorageDeadOrDrop::Destructor(_) => {
|
||||
base_access
|
||||
}
|
||||
StorageDeadOrDrop::LocalStorageDead
|
||||
| StorageDeadOrDrop::BoxedStorageDead => {
|
||||
StorageDeadOrDrop::Destructor(base_ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => base_access,
|
||||
}
|
||||
}
|
||||
|
||||
ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::Index(_) => base_access,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Annotate argument and return type of function and closure with (synthesized) lifetime for
|
||||
/// borrow of local value that does not live long enough.
|
||||
fn annotate_argument_and_return_for_borrow(
|
||||
|
@ -23,13 +23,12 @@ use rustc::mir::{ClearCrossCrate, Local, Location, Mir, Mutability, Operand, Pla
|
||||
use rustc::mir::{Field, Projection, ProjectionElem, Rvalue, Statement, StatementKind};
|
||||
use rustc::mir::{Terminator, TerminatorKind};
|
||||
use rustc::ty::query::Providers;
|
||||
use rustc::ty::{self, ParamEnv, TyCtxt, Ty};
|
||||
use rustc::ty::{self, TyCtxt};
|
||||
|
||||
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, Level};
|
||||
use rustc_data_structures::bit_set::BitSet;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use std::rc::Rc;
|
||||
@ -251,7 +250,6 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
||||
mir,
|
||||
mir_def_id: def_id,
|
||||
move_data: &mdpe.move_data,
|
||||
param_env: param_env,
|
||||
location_table,
|
||||
movable_generator,
|
||||
locals_are_invalidated_at_exit,
|
||||
@ -393,7 +391,6 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
|
||||
/// when MIR borrowck begins.
|
||||
location_table: &'cx LocationTable,
|
||||
|
||||
param_env: ParamEnv<'gcx>,
|
||||
movable_generator: bool,
|
||||
/// This keeps track of whether local variables are free-ed when the function
|
||||
/// exits even without a `StorageDead`, which appears to be the case for
|
||||
@ -574,8 +571,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
|
||||
self.access_place(
|
||||
ContextKind::StorageDead.new(location),
|
||||
(&Place::Local(local), span),
|
||||
(Shallow(None), Write(WriteKind::StorageDeadOrDrop(
|
||||
StorageDeadOrDrop::LocalStorageDead))),
|
||||
(Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
flow_state,
|
||||
);
|
||||
@ -630,8 +626,13 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
|
||||
loc: {:?} term: {:?} drop_place: {:?} drop_place_ty: {:?} span: {:?}",
|
||||
loc, term, drop_place, drop_place_ty, span);
|
||||
|
||||
self.visit_terminator_drop(
|
||||
loc, term, flow_state, drop_place, drop_place_ty, span, SeenTy(None));
|
||||
self.access_place(
|
||||
ContextKind::Drop.new(loc),
|
||||
(drop_place, span),
|
||||
(AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
flow_state,
|
||||
);
|
||||
}
|
||||
TerminatorKind::DropAndReplace {
|
||||
location: ref drop_place,
|
||||
@ -748,7 +749,7 @@ enum MutateMode {
|
||||
}
|
||||
|
||||
use self::ReadOrWrite::{Activation, Read, Reservation, Write};
|
||||
use self::ShallowOrDeep::{Deep, Shallow};
|
||||
use self::AccessDepth::{Deep, Shallow};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum ArtificialField {
|
||||
@ -757,7 +758,7 @@ enum ArtificialField {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum ShallowOrDeep {
|
||||
enum AccessDepth {
|
||||
/// From the RFC: "A *shallow* access means that the immediate
|
||||
/// fields reached at P are accessed, but references or pointers
|
||||
/// found within are not dereferenced. Right now, the only access
|
||||
@ -769,6 +770,10 @@ enum ShallowOrDeep {
|
||||
/// through the given place may be invalidated or accesses by
|
||||
/// this action."
|
||||
Deep,
|
||||
|
||||
/// Access is Deep only when there is a Drop implementation that
|
||||
/// can reach the data behind the reference.
|
||||
Drop,
|
||||
}
|
||||
|
||||
/// Kind of access to a value: read or write
|
||||
@ -803,21 +808,12 @@ enum ReadKind {
|
||||
/// (For informational purposes only)
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum WriteKind {
|
||||
StorageDeadOrDrop(StorageDeadOrDrop),
|
||||
StorageDeadOrDrop,
|
||||
MutableBorrow(BorrowKind),
|
||||
Mutate,
|
||||
Move,
|
||||
}
|
||||
|
||||
/// Specify whether which case a StorageDeadOrDrop is in.
|
||||
/// (For informational purposes only)
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum StorageDeadOrDrop {
|
||||
LocalStorageDead,
|
||||
BoxedStorageDead,
|
||||
Destructor,
|
||||
}
|
||||
|
||||
/// When checking permissions for a place access, this flag is used to indicate that an immutable
|
||||
/// local place can be mutated.
|
||||
///
|
||||
@ -868,231 +864,7 @@ impl InitializationRequiringAction {
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple linked-list threaded up the stack of recursive calls in `visit_terminator_drop`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct SeenTy<'a, 'gcx: 'a>(Option<(Ty<'gcx>, &'a SeenTy<'a, 'gcx>)>);
|
||||
|
||||
impl<'a, 'gcx> SeenTy<'a, 'gcx> {
|
||||
/// Return a new list with `ty` prepended to the front of `self`.
|
||||
fn cons(&'a self, ty: Ty<'gcx>) -> Self {
|
||||
SeenTy(Some((ty, self)))
|
||||
}
|
||||
|
||||
/// True if and only if `ty` occurs on the linked list `self`.
|
||||
fn have_seen(self, ty: Ty) -> bool {
|
||||
let mut this = self.0;
|
||||
loop {
|
||||
match this {
|
||||
None => return false,
|
||||
Some((seen_ty, recur)) => {
|
||||
if seen_ty == ty {
|
||||
return true;
|
||||
} else {
|
||||
this = recur.0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
/// Invokes `access_place` as appropriate for dropping the value
|
||||
/// at `drop_place`. Note that the *actual* `Drop` in the MIR is
|
||||
/// always for a variable (e.g., `Drop(x)`) -- but we recursively
|
||||
/// break this variable down into subpaths (e.g., `Drop(x.foo)`)
|
||||
/// to indicate more precisely which fields might actually be
|
||||
/// accessed by a destructor.
|
||||
fn visit_terminator_drop(
|
||||
&mut self,
|
||||
loc: Location,
|
||||
term: &Terminator<'tcx>,
|
||||
flow_state: &Flows<'cx, 'gcx, 'tcx>,
|
||||
drop_place: &Place<'tcx>,
|
||||
erased_drop_place_ty: ty::Ty<'gcx>,
|
||||
span: Span,
|
||||
prev_seen: SeenTy<'_, 'gcx>,
|
||||
) {
|
||||
if prev_seen.have_seen(erased_drop_place_ty) {
|
||||
// if we have directly seen the input ty `T`, then we must
|
||||
// have had some *direct* ownership loop between `T` and
|
||||
// some directly-owned (as in, actually traversed by
|
||||
// recursive calls below) part that is also of type `T`.
|
||||
//
|
||||
// Note: in *all* such cases, the data in question cannot
|
||||
// be constructed (nor destructed) in finite time/space.
|
||||
//
|
||||
// Proper examples, some of which are statically rejected:
|
||||
//
|
||||
// * `struct A { field: A, ... }`:
|
||||
// statically rejected as infinite size
|
||||
//
|
||||
// * `type B = (B, ...);`:
|
||||
// statically rejected as cyclic
|
||||
//
|
||||
// * `struct C { field: Box<C>, ... }`
|
||||
// * `struct D { field: Box<(D, D)>, ... }`:
|
||||
// *accepted*, though impossible to construct
|
||||
//
|
||||
// Here is *NOT* an example:
|
||||
// * `struct Z { field: Option<Box<Z>>, ... }`:
|
||||
// Here, the type is both representable in finite space (due to the boxed indirection)
|
||||
// and constructable in finite time (since the recursion can bottom out with `None`).
|
||||
// This is an obvious instance of something the compiler must accept.
|
||||
//
|
||||
// Since some of the above impossible cases like `C` and
|
||||
// `D` are accepted by the compiler, we must take care not
|
||||
// to infinite-loop while processing them. But since such
|
||||
// cases cannot actually arise, it is sound for us to just
|
||||
// skip them during drop. If the developer uses unsafe
|
||||
// code to construct them, they should not be surprised by
|
||||
// weird drop behavior in their resulting code.
|
||||
debug!("visit_terminator_drop previously seen \
|
||||
erased_drop_place_ty: {:?} on prev_seen: {:?}; returning early.",
|
||||
erased_drop_place_ty, prev_seen);
|
||||
return;
|
||||
}
|
||||
|
||||
let gcx = self.infcx.tcx.global_tcx();
|
||||
let drop_field = |mir: &mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
|
||||
(index, field): (usize, ty::Ty<'gcx>)| {
|
||||
let field_ty = gcx.normalize_erasing_regions(mir.param_env, field);
|
||||
let place = drop_place.clone().field(Field::new(index), field_ty);
|
||||
|
||||
debug!("visit_terminator_drop drop_field place: {:?} field_ty: {:?}", place, field_ty);
|
||||
let seen = prev_seen.cons(erased_drop_place_ty);
|
||||
mir.visit_terminator_drop(loc, term, flow_state, &place, field_ty, span, seen);
|
||||
};
|
||||
|
||||
match erased_drop_place_ty.sty {
|
||||
// When a struct is being dropped, we need to check
|
||||
// whether it has a destructor, if it does, then we can
|
||||
// call it, if it does not then we need to check the
|
||||
// individual fields instead. This way if `foo` has a
|
||||
// destructor but `bar` does not, we will only check for
|
||||
// borrows of `x.foo` and not `x.bar`. See #47703.
|
||||
ty::Adt(def, substs) if def.is_struct() && !def.has_dtor(self.infcx.tcx) => {
|
||||
def.all_fields()
|
||||
.map(|field| field.ty(gcx, substs))
|
||||
.enumerate()
|
||||
.for_each(|field| drop_field(self, field));
|
||||
}
|
||||
// Same as above, but for tuples.
|
||||
ty::Tuple(tys) => {
|
||||
tys.iter()
|
||||
.cloned()
|
||||
.enumerate()
|
||||
.for_each(|field| drop_field(self, field));
|
||||
}
|
||||
// Closures also have disjoint fields, but they are only
|
||||
// directly accessed in the body of the closure.
|
||||
ty::Closure(def, substs)
|
||||
if *drop_place == Place::Local(Local::new(1))
|
||||
&& !self.mir.upvar_decls.is_empty() =>
|
||||
{
|
||||
substs
|
||||
.upvar_tys(def, self.infcx.tcx)
|
||||
.enumerate()
|
||||
.for_each(|field| drop_field(self, field));
|
||||
}
|
||||
// Generators also have disjoint fields, but they are only
|
||||
// directly accessed in the body of the generator.
|
||||
ty::Generator(def, substs, _)
|
||||
if *drop_place == Place::Local(Local::new(1))
|
||||
&& !self.mir.upvar_decls.is_empty() =>
|
||||
{
|
||||
substs
|
||||
.upvar_tys(def, self.infcx.tcx)
|
||||
.enumerate()
|
||||
.for_each(|field| drop_field(self, field));
|
||||
}
|
||||
|
||||
// #45696: special-case Box<T> by treating its dtor as
|
||||
// only deep *across owned content*. Namely, we know
|
||||
// dropping a box does not touch data behind any
|
||||
// references it holds; if we were to instead fall into
|
||||
// the base case below, we would have a Deep Write due to
|
||||
// the box being `needs_drop`, and that Deep Write would
|
||||
// touch `&mut` data in the box.
|
||||
ty::Adt(def, _) if def.is_box() => {
|
||||
// When/if we add a `&own T` type, this action would
|
||||
// be like running the destructor of the `&own T`.
|
||||
// (And the owner of backing storage referenced by the
|
||||
// `&own T` would be responsible for deallocating that
|
||||
// backing storage.)
|
||||
|
||||
// we model dropping any content owned by the box by
|
||||
// recurring on box contents. This catches cases like
|
||||
// `Box<Box<ScribbleWhenDropped<&mut T>>>`, while
|
||||
// still restricting Write to *owned* content.
|
||||
let ty = erased_drop_place_ty.boxed_ty();
|
||||
let deref_place = drop_place.clone().deref();
|
||||
debug!("visit_terminator_drop drop-box-content deref_place: {:?} ty: {:?}",
|
||||
deref_place, ty);
|
||||
let seen = prev_seen.cons(erased_drop_place_ty);
|
||||
self.visit_terminator_drop(
|
||||
loc, term, flow_state, &deref_place, ty, span, seen);
|
||||
}
|
||||
|
||||
_ => {
|
||||
// We have now refined the type of the value being
|
||||
// dropped (potentially) to just the type of a
|
||||
// subfield; so check whether that field's type still
|
||||
// "needs drop".
|
||||
if erased_drop_place_ty.needs_drop(gcx, self.param_env) {
|
||||
// If so, we assume that the destructor may access
|
||||
// any data it likes (i.e., a Deep Write).
|
||||
self.access_place(
|
||||
ContextKind::Drop.new(loc),
|
||||
(drop_place, span),
|
||||
(Deep, Write(WriteKind::StorageDeadOrDrop(
|
||||
StorageDeadOrDrop::Destructor))),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
flow_state,
|
||||
);
|
||||
} else {
|
||||
// If there is no destructor, we still include a
|
||||
// *shallow* write. This essentially ensures that
|
||||
// borrows of the memory directly at `drop_place`
|
||||
// cannot continue to be borrowed across the drop.
|
||||
//
|
||||
// If we were to use a Deep Write here, then any
|
||||
// `&mut T` that is reachable from `drop_place`
|
||||
// would get invalidated; fixing that is the
|
||||
// essence of resolving issue #45696.
|
||||
//
|
||||
// * Note: In the compiler today, doing a Deep
|
||||
// Write here would not actually break
|
||||
// anything beyond #45696; for example it does not
|
||||
// break this example:
|
||||
//
|
||||
// ```rust
|
||||
// fn reborrow(x: &mut i32) -> &mut i32 { &mut *x }
|
||||
// ```
|
||||
//
|
||||
// Why? Because we do not schedule/emit
|
||||
// `Drop(x)` in the MIR unless `x` needs drop in
|
||||
// the first place.
|
||||
self.access_place(
|
||||
ContextKind::Drop.new(loc),
|
||||
(drop_place, span),
|
||||
(Shallow(None), Write(WriteKind::StorageDeadOrDrop(
|
||||
// rust-lang/rust#52059: distinguish
|
||||
// between invaliding the backing storage
|
||||
// vs running a destructor.
|
||||
//
|
||||
// See also: rust-lang/rust#52782,
|
||||
// specifically #discussion_r206658948
|
||||
StorageDeadOrDrop::BoxedStorageDead))),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
flow_state,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks an access to the given place to see if it is allowed. Examines the set of borrows
|
||||
/// that are in scope, as well as which paths have been initialized, to ensure that (a) the
|
||||
/// place is initialized and (b) it is not borrowed in some way that would prevent this
|
||||
@ -1103,7 +875,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
&mut self,
|
||||
context: Context,
|
||||
place_span: (&Place<'tcx>, Span),
|
||||
kind: (ShallowOrDeep, ReadOrWrite),
|
||||
kind: (AccessDepth, ReadOrWrite),
|
||||
is_local_mutation_allowed: LocalMutationIsAllowed,
|
||||
flow_state: &Flows<'cx, 'gcx, 'tcx>,
|
||||
) {
|
||||
@ -1159,7 +931,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
&mut self,
|
||||
context: Context,
|
||||
place_span: (&Place<'tcx>, Span),
|
||||
sd: ShallowOrDeep,
|
||||
sd: AccessDepth,
|
||||
rw: ReadOrWrite,
|
||||
flow_state: &Flows<'cx, 'gcx, 'tcx>,
|
||||
) -> bool {
|
||||
@ -1252,7 +1024,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
error_reported = true;
|
||||
this.report_conflicting_borrow(context, place_span, bk, &borrow)
|
||||
}
|
||||
WriteKind::StorageDeadOrDrop(_) => {
|
||||
WriteKind::StorageDeadOrDrop => {
|
||||
error_reported = true;
|
||||
this.report_borrowed_value_does_not_live_long_enough(
|
||||
context,
|
||||
@ -1281,7 +1053,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
&mut self,
|
||||
context: Context,
|
||||
place_span: (&Place<'tcx>, Span),
|
||||
kind: ShallowOrDeep,
|
||||
kind: AccessDepth,
|
||||
mode: MutateMode,
|
||||
flow_state: &Flows<'cx, 'gcx, 'tcx>,
|
||||
) {
|
||||
@ -1925,9 +1697,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
|
||||
Reservation(wk @ WriteKind::Move)
|
||||
| Write(wk @ WriteKind::Move)
|
||||
| Reservation(wk @ WriteKind::StorageDeadOrDrop(_))
|
||||
| Reservation(wk @ WriteKind::StorageDeadOrDrop)
|
||||
| Reservation(wk @ WriteKind::MutableBorrow(BorrowKind::Shared))
|
||||
| Write(wk @ WriteKind::StorageDeadOrDrop(_))
|
||||
| Write(wk @ WriteKind::StorageDeadOrDrop)
|
||||
| Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shared)) => {
|
||||
if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) {
|
||||
if self.infcx.tcx.migrate_borrowck() {
|
||||
@ -1942,7 +1714,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
error_access = match wk {
|
||||
WriteKind::MutableBorrow(_) => AccessKind::MutableBorrow,
|
||||
WriteKind::Move => AccessKind::Move,
|
||||
WriteKind::StorageDeadOrDrop(_) |
|
||||
WriteKind::StorageDeadOrDrop |
|
||||
WriteKind::Mutate => AccessKind::Mutate,
|
||||
};
|
||||
self.report_mutability_error(
|
||||
|
@ -143,13 +143,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
Some(Cause::DropVar(local, location)) => match &mir.local_decls[local].name {
|
||||
Some(local_name) => {
|
||||
let mut should_note_order = false;
|
||||
if let Some((WriteKind::StorageDeadOrDrop(_), place)) = kind_place {
|
||||
if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place {
|
||||
if let Place::Local(borrowed_local) = place {
|
||||
let dropped_local_scope = mir.local_decls[local].visibility_scope;
|
||||
let borrowed_local_scope =
|
||||
mir.local_decls[*borrowed_local].visibility_scope;
|
||||
|
||||
if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope) {
|
||||
if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope)
|
||||
&& local != *borrowed_local
|
||||
{
|
||||
should_note_order = true;
|
||||
}
|
||||
}
|
||||
|
@ -11,32 +11,28 @@
|
||||
use borrow_check::borrow_set::BorrowSet;
|
||||
use borrow_check::location::LocationTable;
|
||||
use borrow_check::{JustWrite, WriteAndRead};
|
||||
use borrow_check::{ShallowOrDeep, Deep, Shallow};
|
||||
use borrow_check::{AccessDepth, Deep, Shallow};
|
||||
use borrow_check::{ReadOrWrite, Activation, Read, Reservation, Write};
|
||||
use borrow_check::{Context, ContextKind};
|
||||
use borrow_check::{LocalMutationIsAllowed, MutateMode};
|
||||
use borrow_check::ArtificialField;
|
||||
use borrow_check::{ReadKind, WriteKind, StorageDeadOrDrop};
|
||||
use borrow_check::{ReadKind, WriteKind};
|
||||
use borrow_check::nll::facts::AllFacts;
|
||||
use borrow_check::path_utils::*;
|
||||
use dataflow::move_paths::indexes::BorrowIndex;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc::mir::visit::Visitor;
|
||||
use rustc::mir::{BasicBlock, Location, Mir, Place, Rvalue, Local};
|
||||
use rustc::mir::{BasicBlock, Location, Mir, Place, Rvalue};
|
||||
use rustc::mir::{Statement, StatementKind};
|
||||
use rustc::mir::{Terminator, TerminatorKind};
|
||||
use rustc::mir::{Field, Operand, BorrowKind};
|
||||
use rustc::ty::{self, ParamEnv};
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use rustc::mir::{Operand, BorrowKind};
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
|
||||
pub(super) fn generate_invalidates<'cx, 'gcx, 'tcx>(
|
||||
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
|
||||
all_facts: &mut Option<AllFacts>,
|
||||
location_table: &LocationTable,
|
||||
mir: &Mir<'tcx>,
|
||||
mir_def_id: DefId,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
if !all_facts.is_some() {
|
||||
@ -44,37 +40,32 @@ pub(super) fn generate_invalidates<'cx, 'gcx, 'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
let param_env = infcx.tcx.param_env(mir_def_id);
|
||||
|
||||
if let Some(all_facts) = all_facts {
|
||||
let dominators = mir.dominators();
|
||||
let mut ig = InvalidationGenerator {
|
||||
all_facts,
|
||||
borrow_set,
|
||||
infcx,
|
||||
tcx,
|
||||
location_table,
|
||||
mir,
|
||||
dominators,
|
||||
param_env,
|
||||
};
|
||||
ig.visit_mir(mir);
|
||||
}
|
||||
}
|
||||
|
||||
/// 'cg = the duration of the constraint generation process itself.
|
||||
struct InvalidationGenerator<'cg, 'cx: 'cg, 'tcx: 'cx, 'gcx: 'tcx> {
|
||||
infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
all_facts: &'cg mut AllFacts,
|
||||
location_table: &'cg LocationTable,
|
||||
mir: &'cg Mir<'tcx>,
|
||||
struct InvalidationGenerator<'cx, 'tcx: 'cx, 'gcx: 'tcx> {
|
||||
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
|
||||
all_facts: &'cx mut AllFacts,
|
||||
location_table: &'cx LocationTable,
|
||||
mir: &'cx Mir<'tcx>,
|
||||
dominators: Dominators<BasicBlock>,
|
||||
borrow_set: &'cg BorrowSet<'tcx>,
|
||||
param_env: ParamEnv<'gcx>,
|
||||
borrow_set: &'cx BorrowSet<'tcx>,
|
||||
}
|
||||
|
||||
/// Visits the whole MIR and generates invalidates() facts
|
||||
/// Most of the code implementing this was stolen from borrow_check/mod.rs
|
||||
impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
|
||||
impl<'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx, 'gcx> {
|
||||
fn visit_statement(&mut self,
|
||||
block: BasicBlock,
|
||||
statement: &Statement<'tcx>,
|
||||
@ -154,8 +145,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc
|
||||
self.access_place(
|
||||
ContextKind::StorageDead.new(location),
|
||||
&Place::Local(local),
|
||||
(Shallow(None), Write(WriteKind::StorageDeadOrDrop(
|
||||
StorageDeadOrDrop::LocalStorageDead))),
|
||||
(Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
);
|
||||
}
|
||||
@ -184,12 +174,12 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc
|
||||
target: _,
|
||||
unwind: _,
|
||||
} => {
|
||||
let tcx = self.infcx.tcx;
|
||||
let gcx = tcx.global_tcx();
|
||||
let drop_place_ty = drop_place.ty(self.mir, tcx);
|
||||
let drop_place_ty = tcx.erase_regions(&drop_place_ty).to_ty(tcx);
|
||||
let drop_place_ty = gcx.lift(&drop_place_ty).unwrap();
|
||||
self.visit_terminator_drop(location, terminator, drop_place, drop_place_ty);
|
||||
self.access_place(
|
||||
ContextKind::Drop.new(location),
|
||||
drop_place,
|
||||
(AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
);
|
||||
}
|
||||
TerminatorKind::DropAndReplace {
|
||||
location: ref drop_place,
|
||||
@ -286,83 +276,13 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
|
||||
/// Simulates dropping of a variable
|
||||
fn visit_terminator_drop(
|
||||
&mut self,
|
||||
loc: Location,
|
||||
term: &Terminator<'tcx>,
|
||||
drop_place: &Place<'tcx>,
|
||||
erased_drop_place_ty: ty::Ty<'gcx>,
|
||||
) {
|
||||
let gcx = self.infcx.tcx.global_tcx();
|
||||
let drop_field = |
|
||||
ig: &mut InvalidationGenerator<'cg, 'cx, 'gcx, 'tcx>,
|
||||
(index, field): (usize, ty::Ty<'gcx>),
|
||||
| {
|
||||
let field_ty = gcx.normalize_erasing_regions(ig.param_env, field);
|
||||
let place = drop_place.clone().field(Field::new(index), field_ty);
|
||||
|
||||
ig.visit_terminator_drop(loc, term, &place, field_ty);
|
||||
};
|
||||
|
||||
match erased_drop_place_ty.sty {
|
||||
// When a struct is being dropped, we need to check
|
||||
// whether it has a destructor, if it does, then we can
|
||||
// call it, if it does not then we need to check the
|
||||
// individual fields instead. This way if `foo` has a
|
||||
// destructor but `bar` does not, we will only check for
|
||||
// borrows of `x.foo` and not `x.bar`. See #47703.
|
||||
ty::Adt(def, substs) if def.is_struct() && !def.has_dtor(self.infcx.tcx) => {
|
||||
def.all_fields()
|
||||
.map(|field| field.ty(gcx, substs))
|
||||
.enumerate()
|
||||
.for_each(|field| drop_field(self, field));
|
||||
}
|
||||
// Same as above, but for tuples.
|
||||
ty::Tuple(tys) => {
|
||||
tys.iter().cloned().enumerate()
|
||||
.for_each(|field| drop_field(self, field));
|
||||
}
|
||||
// Closures and generators also have disjoint fields, but they are only
|
||||
// directly accessed in the body of the closure/generator.
|
||||
ty::Generator(def, substs, ..)
|
||||
if *drop_place == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty()
|
||||
=> {
|
||||
substs.upvar_tys(def, self.infcx.tcx).enumerate()
|
||||
.for_each(|field| drop_field(self, field));
|
||||
}
|
||||
ty::Closure(def, substs)
|
||||
if *drop_place == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty()
|
||||
=> {
|
||||
substs.upvar_tys(def, self.infcx.tcx).enumerate()
|
||||
.for_each(|field| drop_field(self, field));
|
||||
}
|
||||
_ => {
|
||||
// We have now refined the type of the value being
|
||||
// dropped (potentially) to just the type of a
|
||||
// subfield; so check whether that field's type still
|
||||
// "needs drop". If so, we assume that the destructor
|
||||
// may access any data it likes (i.e., a Deep Write).
|
||||
if erased_drop_place_ty.needs_drop(gcx, self.param_env) {
|
||||
self.access_place(
|
||||
ContextKind::Drop.new(loc),
|
||||
drop_place,
|
||||
(Deep, Write(WriteKind::StorageDeadOrDrop(
|
||||
StorageDeadOrDrop::Destructor))),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cx, 'tcx, 'gcx> {
|
||||
/// Simulates mutation of a place
|
||||
fn mutate_place(
|
||||
&mut self,
|
||||
context: Context,
|
||||
place: &Place<'tcx>,
|
||||
kind: ShallowOrDeep,
|
||||
kind: AccessDepth,
|
||||
_mode: MutateMode,
|
||||
) {
|
||||
self.access_place(
|
||||
@ -412,7 +332,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
|
||||
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
|
||||
BorrowKind::Unique | BorrowKind::Mut { .. } => {
|
||||
let wk = WriteKind::MutableBorrow(bk);
|
||||
if allow_two_phase_borrow(&self.infcx.tcx, bk) {
|
||||
if allow_two_phase_borrow(&self.tcx, bk) {
|
||||
(Deep, Reservation(wk))
|
||||
} else {
|
||||
(Deep, Write(wk))
|
||||
@ -471,7 +391,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
|
||||
&mut self,
|
||||
context: Context,
|
||||
place: &Place<'tcx>,
|
||||
kind: (ShallowOrDeep, ReadOrWrite),
|
||||
kind: (AccessDepth, ReadOrWrite),
|
||||
_is_local_mutation_allowed: LocalMutationIsAllowed,
|
||||
) {
|
||||
let (sd, rw) = kind;
|
||||
@ -483,7 +403,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
|
||||
&mut self,
|
||||
context: Context,
|
||||
place: &Place<'tcx>,
|
||||
sd: ShallowOrDeep,
|
||||
sd: AccessDepth,
|
||||
rw: ReadOrWrite,
|
||||
) {
|
||||
debug!(
|
||||
@ -494,7 +414,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
|
||||
sd,
|
||||
rw,
|
||||
);
|
||||
let tcx = self.infcx.tcx;
|
||||
let tcx = self.tcx;
|
||||
let mir = self.mir;
|
||||
let borrow_set = self.borrow_set.clone();
|
||||
let indices = self.borrow_set.borrows.indices();
|
||||
@ -527,7 +447,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
|
||||
// Reading from mere reservations of mutable-borrows is OK.
|
||||
if !is_active(&this.dominators, borrow, context.loc) {
|
||||
// If the borrow isn't active yet, reads don't invalidate it
|
||||
assert!(allow_two_phase_borrow(&this.infcx.tcx, borrow.kind));
|
||||
assert!(allow_two_phase_borrow(&this.tcx, borrow.kind));
|
||||
return Control::Continue;
|
||||
}
|
||||
|
||||
|
@ -160,11 +160,10 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
|
||||
|
||||
// Generate various additional constraints.
|
||||
invalidation::generate_invalidates(
|
||||
infcx,
|
||||
infcx.tcx,
|
||||
&mut all_facts,
|
||||
location_table,
|
||||
&mir,
|
||||
def_id,
|
||||
borrow_set,
|
||||
);
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
use borrow_check::borrow_set::{BorrowSet, BorrowData, TwoPhaseActivation};
|
||||
use borrow_check::places_conflict;
|
||||
use borrow_check::Context;
|
||||
use borrow_check::ShallowOrDeep;
|
||||
use borrow_check::AccessDepth;
|
||||
use dataflow::indexes::BorrowIndex;
|
||||
use rustc::mir::{BasicBlock, Location, Mir, Place};
|
||||
use rustc::mir::{ProjectionElem, BorrowKind};
|
||||
@ -43,7 +43,7 @@ pub(super) fn each_borrow_involving_path<'a, 'tcx, 'gcx: 'tcx, F, I, S> (
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
_context: Context,
|
||||
access_place: (ShallowOrDeep, &Place<'tcx>),
|
||||
access_place: (AccessDepth, &Place<'tcx>),
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
candidates: I,
|
||||
mut op: F,
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
use borrow_check::ArtificialField;
|
||||
use borrow_check::Overlap;
|
||||
use borrow_check::{Deep, Shallow, ShallowOrDeep};
|
||||
use borrow_check::{Deep, Shallow, AccessDepth};
|
||||
use rustc::hir;
|
||||
use rustc::mir::{Mir, Place};
|
||||
use rustc::mir::{Projection, ProjectionElem};
|
||||
@ -22,7 +22,7 @@ pub(super) fn places_conflict<'gcx, 'tcx>(
|
||||
mir: &Mir<'tcx>,
|
||||
borrow_place: &Place<'tcx>,
|
||||
access_place: &Place<'tcx>,
|
||||
access: ShallowOrDeep,
|
||||
access: AccessDepth,
|
||||
) -> bool {
|
||||
debug!(
|
||||
"places_conflict({:?},{:?},{:?})",
|
||||
@ -49,7 +49,7 @@ fn place_components_conflict<'gcx, 'tcx>(
|
||||
mir: &Mir<'tcx>,
|
||||
mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
|
||||
mut access_components: PlaceComponentsIter<'_, 'tcx>,
|
||||
access: ShallowOrDeep,
|
||||
access: AccessDepth,
|
||||
) -> bool {
|
||||
// The borrowck rules for proving disjointness are applied from the "root" of the
|
||||
// borrow forwards, iterating over "similar" projections in lockstep until
|
||||
@ -179,16 +179,26 @@ fn place_components_conflict<'gcx, 'tcx>(
|
||||
return false;
|
||||
}
|
||||
(ProjectionElem::Deref, ty::Ref(_, _, hir::MutImmutable), _) => {
|
||||
// the borrow goes through a dereference of a shared reference.
|
||||
//
|
||||
// I'm not sure why we are tracking these borrows - shared
|
||||
// references can *always* be aliased, which means the
|
||||
// permission check already account for this borrow.
|
||||
debug!("places_conflict: behind a shared ref");
|
||||
// Shouldn't be tracked
|
||||
bug!("Tracking borrow behind shared reference.");
|
||||
}
|
||||
(ProjectionElem::Deref, ty::Ref(_, _, hir::MutMutable), AccessDepth::Drop) => {
|
||||
// Values behind a mutatble reference are not access either by Dropping a
|
||||
// value, or by StorageDead
|
||||
debug!("places_conflict: drop access behind ptr");
|
||||
return false;
|
||||
}
|
||||
|
||||
(ProjectionElem::Field { .. }, ty::Adt(def, _), AccessDepth::Drop) => {
|
||||
// Drop can read/write arbitrary projections, so places
|
||||
// conflict regardless of further projections.
|
||||
if def.has_dtor(tcx) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
(ProjectionElem::Deref, _, Deep)
|
||||
| (ProjectionElem::Deref, _, AccessDepth::Drop)
|
||||
| (ProjectionElem::Field { .. }, _, _)
|
||||
| (ProjectionElem::Index { .. }, _, _)
|
||||
| (ProjectionElem::ConstantIndex { .. }, _, _)
|
||||
|
@ -8,8 +8,6 @@ LL | }
|
||||
| |
|
||||
| `v` dropped here while still borrowed
|
||||
| borrow later used here, when `v` is dropped
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -9,6 +9,8 @@ LL | }
|
||||
| |
|
||||
| `*cell` dropped here while still borrowed
|
||||
| borrow later used here, when `gen` is dropped
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error[E0597]: `ref_` does not live long enough
|
||||
--> $DIR/dropck.rs:22:11
|
||||
|
@ -23,7 +23,7 @@ LL | &mut *(*s).0 //[nll]~ ERROR borrow may still be in use when destructor
|
||||
| ^^^^^^^^^^^^
|
||||
...
|
||||
LL | }
|
||||
| - here, drop of `*s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
|
||||
| - here, drop of `s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
|
||||
|
|
||||
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 72:20...
|
||||
--> $DIR/issue-45696-scribble-on-boxed-borrow.rs:72:20
|
||||
@ -41,7 +41,7 @@ LL | &mut *(**s).0 //[nll]~ ERROR borrow may still be in use when destructor
|
||||
| ^^^^^^^^^^^^^
|
||||
...
|
||||
LL | }
|
||||
| - here, drop of `**s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
|
||||
| - here, drop of `s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
|
||||
|
|
||||
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 82:26...
|
||||
--> $DIR/issue-45696-scribble-on-boxed-borrow.rs:82:26
|
||||
|
@ -20,7 +20,7 @@ LL | &mut *(*s).0 //[nll]~ ERROR borrow may still be in use when destructor
|
||||
| ^^^^^^^^^^^^
|
||||
...
|
||||
LL | }
|
||||
| - here, drop of `*s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
|
||||
| - here, drop of `s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
|
||||
|
|
||||
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 72:20...
|
||||
--> $DIR/issue-45696-scribble-on-boxed-borrow.rs:72:20
|
||||
@ -35,7 +35,7 @@ LL | &mut *(**s).0 //[nll]~ ERROR borrow may still be in use when destructor
|
||||
| ^^^^^^^^^^^^^
|
||||
...
|
||||
LL | }
|
||||
| - here, drop of `**s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
|
||||
| - here, drop of `s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
|
||||
|
|
||||
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 82:26...
|
||||
--> $DIR/issue-45696-scribble-on-boxed-borrow.rs:82:26
|
||||
|
51
src/test/ui/nll/enum-drop-access.rs
Normal file
51
src/test/ui/nll/enum-drop-access.rs
Normal file
@ -0,0 +1,51 @@
|
||||
#![feature(nll)]
|
||||
|
||||
enum DropOption<T> {
|
||||
Some(T),
|
||||
None,
|
||||
}
|
||||
|
||||
impl<T> Drop for DropOption<T> {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
// Dropping opt could access the value behind the reference,
|
||||
fn drop_enum(opt: DropOption<&mut i32>) -> Option<&mut i32> {
|
||||
match opt {
|
||||
DropOption::Some(&mut ref mut r) => { //~ ERROR
|
||||
Some(r)
|
||||
},
|
||||
DropOption::None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn optional_drop_enum(opt: Option<DropOption<&mut i32>>) -> Option<&mut i32> {
|
||||
match opt {
|
||||
Some(DropOption::Some(&mut ref mut r)) => { //~ ERROR
|
||||
Some(r)
|
||||
},
|
||||
Some(DropOption::None) | None => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, dropping opt doesn't access the reference
|
||||
fn optional_tuple(opt: Option<(&mut i32, String)>) -> Option<&mut i32> {
|
||||
match opt {
|
||||
Some((&mut ref mut r, _)) => {
|
||||
Some(r)
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, dropping res doesn't access the Ok case.
|
||||
fn different_variants(res: Result<&mut i32, String>) -> Option<&mut i32> {
|
||||
match res {
|
||||
Ok(&mut ref mut r) => {
|
||||
Some(r)
|
||||
},
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
45
src/test/ui/nll/enum-drop-access.stderr
Normal file
45
src/test/ui/nll/enum-drop-access.stderr
Normal file
@ -0,0 +1,45 @@
|
||||
error[E0713]: borrow may still be in use when destructor runs
|
||||
--> $DIR/enum-drop-access.rs:15:31
|
||||
|
|
||||
LL | DropOption::Some(&mut ref mut r) => { //~ ERROR
|
||||
| ^^^^^^^^^
|
||||
...
|
||||
LL | }
|
||||
| - here, drop of `opt` needs exclusive access to `*opt.0`, because the type `DropOption<&mut i32>` implements the `Drop` trait
|
||||
|
|
||||
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 13:1...
|
||||
--> $DIR/enum-drop-access.rs:13:1
|
||||
|
|
||||
LL | / fn drop_enum(opt: DropOption<&mut i32>) -> Option<&mut i32> {
|
||||
LL | | match opt {
|
||||
LL | | DropOption::Some(&mut ref mut r) => { //~ ERROR
|
||||
LL | | Some(r)
|
||||
... |
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error[E0713]: borrow may still be in use when destructor runs
|
||||
--> $DIR/enum-drop-access.rs:24:36
|
||||
|
|
||||
LL | Some(DropOption::Some(&mut ref mut r)) => { //~ ERROR
|
||||
| ^^^^^^^^^
|
||||
...
|
||||
LL | }
|
||||
| - here, drop of `opt` needs exclusive access to `*opt.0.0`, because the type `DropOption<&mut i32>` implements the `Drop` trait
|
||||
|
|
||||
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 22:1...
|
||||
--> $DIR/enum-drop-access.rs:22:1
|
||||
|
|
||||
LL | / fn optional_drop_enum(opt: Option<DropOption<&mut i32>>) -> Option<&mut i32> {
|
||||
LL | | match opt {
|
||||
LL | | Some(DropOption::Some(&mut ref mut r)) => { //~ ERROR
|
||||
LL | | Some(r)
|
||||
... |
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0713`.
|
@ -1,11 +1,11 @@
|
||||
error[E0713]: borrow may still be in use when destructor runs
|
||||
--> $DIR/borrowck-ref-into-rvalue.rs:14:14
|
||||
error[E0597]: borrowed value does not live long enough
|
||||
--> $DIR/borrowck-ref-into-rvalue.rs:13:11
|
||||
|
|
||||
LL | Some(ref m) => {
|
||||
| ^^^^^
|
||||
LL | match Some("Hello".to_string()) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
|
||||
...
|
||||
LL | }
|
||||
| - drop of temporary value occurs here
|
||||
| - temporary value only lives until here
|
||||
LL | println!("{}", *msg);
|
||||
| ---- borrow later used here
|
||||
|
|
||||
@ -13,4 +13,4 @@ LL | println!("{}", *msg);
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0713`.
|
||||
For more information about this error, try `rustc --explain E0597`.
|
||||
|
@ -9,6 +9,8 @@ LL | }
|
||||
| |
|
||||
| `b2` dropped here while still borrowed
|
||||
| borrow later used here, when `b1` is dropped
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error[E0597]: `b3` does not live long enough
|
||||
--> $DIR/dropck_arr_cycle_checked.rs:105:24
|
||||
@ -21,6 +23,8 @@ LL | }
|
||||
| |
|
||||
| `b3` dropped here while still borrowed
|
||||
| borrow later used here, when `b1` is dropped
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error[E0597]: `b1` does not live long enough
|
||||
--> $DIR/dropck_arr_cycle_checked.rs:111:24
|
||||
|
@ -23,8 +23,6 @@ LL | }
|
||||
| |
|
||||
| `d1` dropped here while still borrowed
|
||||
| borrow later used here, when `d1` is dropped
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -9,6 +9,8 @@ LL | }
|
||||
| |
|
||||
| `c2` dropped here while still borrowed
|
||||
| borrow later used here, when `c1` is dropped
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error[E0597]: `c3` does not live long enough
|
||||
--> $DIR/dropck_vec_cycle_checked.rs:115:24
|
||||
@ -21,6 +23,8 @@ LL | }
|
||||
| |
|
||||
| `c3` dropped here while still borrowed
|
||||
| borrow later used here, when `c1` is dropped
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error[E0597]: `c1` does not live long enough
|
||||
--> $DIR/dropck_vec_cycle_checked.rs:121:24
|
||||
|
@ -8,6 +8,8 @@ LL | }
|
||||
| |
|
||||
| `x` dropped here while still borrowed
|
||||
| borrow later used here, when `y` is dropped
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error[E0597]: `x` does not live long enough
|
||||
--> $DIR/issue-29106.rs:33:25
|
||||
@ -19,6 +21,8 @@ LL | }
|
||||
| |
|
||||
| `x` dropped here while still borrowed
|
||||
| borrow later used here, when `y` is dropped
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -11,7 +11,6 @@ LL | }
|
||||
| borrow later used here, when `foo` is dropped
|
||||
|
|
||||
= note: consider using a `let` binding to create a longer lived value
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -9,6 +9,8 @@ LL | }
|
||||
| |
|
||||
| `c2` dropped here while still borrowed
|
||||
| borrow later used here, when `c1` is dropped
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error[E0597]: `c1` does not live long enough
|
||||
--> $DIR/vec-must-not-hide-type-from-dropck.rs:129:24
|
||||
|
@ -11,8 +11,6 @@ LL | }
|
||||
| |
|
||||
| `factorial` dropped here while still borrowed
|
||||
| borrow later used here, when `factorial` is dropped
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are defined
|
||||
|
||||
error[E0506]: cannot assign to `factorial` because it is borrowed
|
||||
--> $DIR/unboxed-closures-failed-recursive-fn-1.rs:30:5
|
||||
|
Loading…
x
Reference in New Issue
Block a user