diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 3fdb7d7f27d7e..1d91fa365d547 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -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, + 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( diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 06884875598df..769e1097bff05 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -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, ... }` - // * `struct D { field: Box<(D, D)>, ... }`: - // *accepted*, though impossible to construct - // - // Here is *NOT* an example: - // * `struct Z { field: Option>, ... }`: - // 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 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>>`, 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( diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs index 755148b699253..f4052f948013a 100644 --- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs @@ -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; } } diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs index cd760746c125a..3d387963da9da 100644 --- a/src/librustc_mir/borrow_check/nll/invalidation.rs +++ b/src/librustc_mir/borrow_check/nll/invalidation.rs @@ -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, 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, - 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; } diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 7cab0acb4326a..b9f22c7402a07 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -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, ); diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs index 70b4e0ea2f03c..67787d23db692 100644 --- a/src/librustc_mir/borrow_check/path_utils.rs +++ b/src/librustc_mir/borrow_check/path_utils.rs @@ -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, diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs index 3f055283e0c3f..e371b34c4800c 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -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 { .. }, _, _) diff --git a/src/test/ui/dropck/dropck-union.nll.stderr b/src/test/ui/dropck/dropck-union.nll.stderr index 35d7ffc7879ed..ffb322b85dc34 100644 --- a/src/test/ui/dropck/dropck-union.nll.stderr +++ b/src/test/ui/dropck/dropck-union.nll.stderr @@ -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 diff --git a/src/test/ui/generator/dropck.nll.stderr b/src/test/ui/generator/dropck.nll.stderr index b49bf81715079..ef7e64ffd97ae 100644 --- a/src/test/ui/generator/dropck.nll.stderr +++ b/src/test/ui/generator/dropck.nll.stderr @@ -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 diff --git a/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.migrate.stderr b/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.migrate.stderr index 70d819f0f4678..5c753817e358d 100644 --- a/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.migrate.stderr +++ b/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.migrate.stderr @@ -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 diff --git a/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.nll.stderr b/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.nll.stderr index 72ec5affb182b..79a7c0631f480 100644 --- a/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.nll.stderr +++ b/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.nll.stderr @@ -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 diff --git a/src/test/ui/nll/enum-drop-access.rs b/src/test/ui/nll/enum-drop-access.rs new file mode 100644 index 0000000000000..dc436d20fd857 --- /dev/null +++ b/src/test/ui/nll/enum-drop-access.rs @@ -0,0 +1,51 @@ +#![feature(nll)] + +enum DropOption { + Some(T), + None, +} + +impl Drop for DropOption { + 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>) -> 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() {} diff --git a/src/test/ui/nll/enum-drop-access.stderr b/src/test/ui/nll/enum-drop-access.stderr new file mode 100644 index 0000000000000..57daf26596dfb --- /dev/null +++ b/src/test/ui/nll/enum-drop-access.stderr @@ -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>) -> 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`. diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.nll.stderr b/src/test/ui/span/borrowck-ref-into-rvalue.nll.stderr index c94558f12bbde..c565842c2c002 100644 --- a/src/test/ui/span/borrowck-ref-into-rvalue.nll.stderr +++ b/src/test/ui/span/borrowck-ref-into-rvalue.nll.stderr @@ -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`. diff --git a/src/test/ui/span/dropck_arr_cycle_checked.nll.stderr b/src/test/ui/span/dropck_arr_cycle_checked.nll.stderr index 76a25fa661ece..74db695e7e560 100644 --- a/src/test/ui/span/dropck_arr_cycle_checked.nll.stderr +++ b/src/test/ui/span/dropck_arr_cycle_checked.nll.stderr @@ -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 diff --git a/src/test/ui/span/dropck_direct_cycle_with_drop.nll.stderr b/src/test/ui/span/dropck_direct_cycle_with_drop.nll.stderr index 2884b1818baa9..baf3cef2ae83c 100644 --- a/src/test/ui/span/dropck_direct_cycle_with_drop.nll.stderr +++ b/src/test/ui/span/dropck_direct_cycle_with_drop.nll.stderr @@ -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 diff --git a/src/test/ui/span/dropck_vec_cycle_checked.nll.stderr b/src/test/ui/span/dropck_vec_cycle_checked.nll.stderr index e6f43e0a71b56..f7ff4e5169fd4 100644 --- a/src/test/ui/span/dropck_vec_cycle_checked.nll.stderr +++ b/src/test/ui/span/dropck_vec_cycle_checked.nll.stderr @@ -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 diff --git a/src/test/ui/span/issue-29106.nll.stderr b/src/test/ui/span/issue-29106.nll.stderr index 2cf408d097b83..a1451866e677c 100644 --- a/src/test/ui/span/issue-29106.nll.stderr +++ b/src/test/ui/span/issue-29106.nll.stderr @@ -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 diff --git a/src/test/ui/span/issue28498-reject-ex1.nll.stderr b/src/test/ui/span/issue28498-reject-ex1.nll.stderr index 1f72b78ebc76d..0f6f6e381d801 100644 --- a/src/test/ui/span/issue28498-reject-ex1.nll.stderr +++ b/src/test/ui/span/issue28498-reject-ex1.nll.stderr @@ -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 diff --git a/src/test/ui/span/vec-must-not-hide-type-from-dropck.nll.stderr b/src/test/ui/span/vec-must-not-hide-type-from-dropck.nll.stderr index ee51304800d32..292c007f51225 100644 --- a/src/test/ui/span/vec-must-not-hide-type-from-dropck.nll.stderr +++ b/src/test/ui/span/vec-must-not-hide-type-from-dropck.nll.stderr @@ -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 diff --git a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.nll.stderr b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.nll.stderr index 8cda1e60ba988..afd90237d16ae 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.nll.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.nll.stderr @@ -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