diff options
Diffstat (limited to 'compiler/rustc_borrowck/src/lib.rs')
-rw-r--r-- | compiler/rustc_borrowck/src/lib.rs | 265 |
1 files changed, 202 insertions, 63 deletions
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 1d17df8b7..5787ea13e 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1,5 +1,8 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. +#![allow(internal_features)] +#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![cfg_attr(not(bootstrap), doc(rust_logo))] #![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(let_chains)] @@ -11,7 +14,6 @@ #![feature(trusted_step)] #![feature(try_blocks)] #![recursion_limit = "256"] -#![allow(internal_features)] #[macro_use] extern crate rustc_middle; @@ -29,13 +31,8 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::{ InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt, }; -use rustc_middle::mir::{ - traversal, Body, ClearCrossCrate, Local, Location, MutBorrowKind, Mutability, - NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef, VarDebugInfoContents, -}; -use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; -use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; -use rustc_middle::mir::{ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; +use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::mir::*; use rustc_middle::query::Providers; use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt}; @@ -53,13 +50,13 @@ use rustc_mir_dataflow::impls::{ EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces, }; use rustc_mir_dataflow::move_paths::{InitIndex, MoveOutIndex, MovePathIndex}; -use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError}; +use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveData}; use rustc_mir_dataflow::Analysis; use rustc_mir_dataflow::MoveDataParamEnv; use crate::session_diagnostics::VarNeedNotMut; -use self::diagnostics::{AccessKind, RegionName}; +use self::diagnostics::{AccessKind, IllegalMoveOriginKind, MoveError, RegionName}; use self::location::LocationTable; use self::prefixes::PrefixSet; use consumers::{BodyWithBorrowckFacts, ConsumerOptions}; @@ -173,7 +170,9 @@ fn do_mir_borrowck<'tcx>( for var_debug_info in &input_body.var_debug_info { if let VarDebugInfoContents::Place(place) = var_debug_info.value { if let Some(local) = place.as_local() { - if let Some(prev_name) = local_names[local] && var_debug_info.name != prev_name { + if let Some(prev_name) = local_names[local] + && var_debug_info.name != prev_name + { span_bug!( var_debug_info.source_info.span, "local {:?} has many names (`{}` vs `{}`)", @@ -220,14 +219,10 @@ fn do_mir_borrowck<'tcx>( let location_table_owned = LocationTable::new(body); let location_table = &location_table_owned; - let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) = - match MoveData::gather_moves(&body, tcx, param_env) { - Ok(move_data) => (move_data, Vec::new()), - Err((move_data, move_errors)) => (move_data, move_errors), - }; - let promoted_errors = promoted + let move_data = MoveData::gather_moves(&body, tcx, param_env, |_| true); + let promoted_move_data = promoted .iter_enumerated() - .map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env))); + .map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env, |_| true))); let mdpe = MoveDataParamEnv { move_data, param_env }; @@ -298,47 +293,60 @@ fn do_mir_borrowck<'tcx>( .pass_name("borrowck") .iterate_to_fixpoint(); - let movable_generator = - // The first argument is the generator type passed by value + let movable_coroutine = + // The first argument is the coroutine type passed by value if let Some(local) = body.local_decls.raw.get(1) // Get the interior types and args which typeck computed - && let ty::Generator(_, _, hir::Movability::Static) = local.ty.kind() + && let ty::Coroutine(_, _, hir::Movability::Static) = local.ty.kind() { false } else { true }; - for (idx, move_data_results) in promoted_errors { - let promoted_body = &promoted[idx]; + for (idx, move_data) in promoted_move_data { + use rustc_middle::mir::visit::Visitor; - if let Err((move_data, move_errors)) = move_data_results { - let mut promoted_mbcx = MirBorrowckCtxt { - infcx: &infcx, - param_env, - body: promoted_body, - move_data: &move_data, - location_table, // no need to create a real one for the promoted, it is not used - movable_generator, - fn_self_span_reported: Default::default(), - locals_are_invalidated_at_exit, - access_place_error_reported: Default::default(), - reservation_error_reported: Default::default(), - uninitialized_error_reported: Default::default(), - regioncx: regioncx.clone(), - used_mut: Default::default(), - used_mut_upvars: SmallVec::new(), - borrow_set: Rc::clone(&borrow_set), - upvars: Vec::new(), - local_names: IndexVec::from_elem(None, &promoted_body.local_decls), - region_names: RefCell::default(), - next_region_name: RefCell::new(1), - polonius_output: None, - errors, - }; - promoted_mbcx.report_move_errors(move_errors); - errors = promoted_mbcx.errors; + let promoted_body = &promoted[idx]; + let mut promoted_mbcx = MirBorrowckCtxt { + infcx: &infcx, + param_env, + body: promoted_body, + move_data: &move_data, + location_table, // no need to create a real one for the promoted, it is not used + movable_coroutine, + fn_self_span_reported: Default::default(), + locals_are_invalidated_at_exit, + access_place_error_reported: Default::default(), + reservation_error_reported: Default::default(), + uninitialized_error_reported: Default::default(), + regioncx: regioncx.clone(), + used_mut: Default::default(), + used_mut_upvars: SmallVec::new(), + borrow_set: Rc::clone(&borrow_set), + upvars: Vec::new(), + local_names: IndexVec::from_elem(None, &promoted_body.local_decls), + region_names: RefCell::default(), + next_region_name: RefCell::new(1), + polonius_output: None, + move_errors: Vec::new(), + errors, }; + MoveVisitor { ctxt: &mut promoted_mbcx }.visit_body(promoted_body); + promoted_mbcx.report_move_errors(); + errors = promoted_mbcx.errors; + + struct MoveVisitor<'a, 'cx, 'tcx> { + ctxt: &'a mut MirBorrowckCtxt<'cx, 'tcx>, + } + + impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> { + fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { + if let Operand::Move(place) = operand { + self.ctxt.check_movable_place(location, *place); + } + } + } } let mut mbcx = MirBorrowckCtxt { @@ -347,7 +355,7 @@ fn do_mir_borrowck<'tcx>( body, move_data: &mdpe.move_data, location_table, - movable_generator, + movable_coroutine, locals_are_invalidated_at_exit, fn_self_span_reported: Default::default(), access_place_error_reported: Default::default(), @@ -362,6 +370,7 @@ fn do_mir_borrowck<'tcx>( region_names: RefCell::default(), next_region_name: RefCell::new(1), polonius_output, + move_errors: Vec::new(), errors, }; @@ -374,8 +383,6 @@ fn do_mir_borrowck<'tcx>( borrows: flow_borrows, }; - mbcx.report_move_errors(move_errors); - rustc_mir_dataflow::visit_results( body, traversal::reverse_postorder(body).map(|(bb, _)| bb), @@ -383,6 +390,8 @@ fn do_mir_borrowck<'tcx>( &mut mbcx, ); + mbcx.report_move_errors(); + // For each non-user used mutable variable, check if it's been assigned from // a user-declared local. If so, then put that local into the used_mut set. // Note that this set is expected to be small - only upvars from closures @@ -532,7 +541,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> { /// when MIR borrowck begins. location_table: &'cx LocationTable, - movable_generator: bool, + movable_coroutine: 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 /// constants. @@ -591,6 +600,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> { polonius_output: Option<Rc<PoloniusOutput>>, errors: error::BorrowckErrors<'tcx>, + move_errors: Vec<MoveError<'tcx>>, } // Check that: @@ -721,7 +731,6 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro } TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { self.consume_operand(loc, (cond, span), flow_state); - use rustc_middle::mir::AssertKind; if let AssertKind::BoundsCheck { len, index } = &**msg { self.consume_operand(loc, (len, span), flow_state); self.consume_operand(loc, (index, span), flow_state); @@ -774,7 +783,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro | TerminatorKind::Unreachable | TerminatorKind::UnwindResume | TerminatorKind::Return - | TerminatorKind::GeneratorDrop + | TerminatorKind::CoroutineDrop | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => { // no data used, thus irrelevant to borrowck @@ -793,7 +802,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro match term.kind { TerminatorKind::Yield { value: _, resume: _, resume_arg: _, drop: _ } => { - if self.movable_generator { + if self.movable_coroutine { // Look for any active borrows to locals let borrow_set = self.borrow_set.clone(); for i in flow_state.borrows.iter() { @@ -805,7 +814,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro TerminatorKind::UnwindResume | TerminatorKind::Return - | TerminatorKind::GeneratorDrop => { + | TerminatorKind::CoroutineDrop => { // Returning from the function implicitly kills storage for all locals and statics. // Often, the storage will already have been killed by an explicit // StorageDead, but we don't always emit those (notably on unwind paths), @@ -1322,7 +1331,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // moved into the closure and subsequently used by the closure, // in order to populate our used_mut set. match **aggregate_kind { - AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) => { + AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _, _) => { let def_id = def_id.expect_local(); let BorrowCheckResult { used_mut_upvars, .. } = self.infcx.tcx.mir_borrowck(def_id); @@ -1405,7 +1414,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // As such we have to search for the local that this // capture comes from and mark it as being used as mut. - let temp_mpi = self.move_data.rev_lookup.find_local(local); + let Some(temp_mpi) = self.move_data.rev_lookup.find_local(local) else { + bug!("temporary should be tracked"); + }; let init = if let [init_index] = *self.move_data.init_path_map[temp_mpi] { &self.move_data.inits[init_index] } else { @@ -1465,6 +1476,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } Operand::Move(place) => { + // Check if moving from this place makes sense. + self.check_movable_place(location, place); + // move of place: check if this is move of already borrowed path self.access_place( location, @@ -1545,12 +1559,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } /// Reports an error if this is a borrow of local data. - /// This is called for all Yield expressions on movable generators + /// This is called for all Yield expressions on movable coroutines fn check_for_local_borrow(&mut self, borrow: &BorrowData<'tcx>, yield_span: Span) { debug!("check_for_local_borrow({:?})", borrow); if borrow_of_local_data(borrow.borrowed_place) { - let err = self.cannot_borrow_across_generator_yield( + let err = self.cannot_borrow_across_coroutine_yield( self.retrieve_borrow_spans(borrow).var_or_use(), yield_span, ); @@ -1586,6 +1600,131 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + fn check_movable_place(&mut self, location: Location, place: Place<'tcx>) { + use IllegalMoveOriginKind::*; + + let body = self.body; + let tcx = self.infcx.tcx; + let mut place_ty = PlaceTy::from_ty(body.local_decls[place.local].ty); + for (place_ref, elem) in place.iter_projections() { + match elem { + ProjectionElem::Deref => match place_ty.ty.kind() { + ty::Ref(..) | ty::RawPtr(..) => { + self.move_errors.push(MoveError::new( + place, + location, + BorrowedContent { + target_place: place_ref.project_deeper(&[elem], tcx), + }, + )); + return; + } + ty::Adt(adt, _) => { + if !adt.is_box() { + bug!("Adt should be a box type when Place is deref"); + } + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::Coroutine(_, _, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Infer(_) + | ty::Error(_) + | ty::Placeholder(_) => { + bug!("When Place is Deref it's type shouldn't be {place_ty:#?}") + } + }, + ProjectionElem::Field(_, _) => match place_ty.ty.kind() { + ty::Adt(adt, _) => { + if adt.has_dtor(tcx) { + self.move_errors.push(MoveError::new( + place, + location, + InteriorOfTypeWithDestructor { container_ty: place_ty.ty }, + )); + return; + } + } + ty::Closure(_, _) | ty::Coroutine(_, _, _) | ty::Tuple(_) => (), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Alias(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Infer(_) + | ty::Error(_) + | ty::Placeholder(_) => bug!( + "When Place contains ProjectionElem::Field it's type shouldn't be {place_ty:#?}" + ), + }, + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { + match place_ty.ty.kind() { + ty::Slice(_) => { + self.move_errors.push(MoveError::new( + place, + location, + InteriorOfSliceOrArray { ty: place_ty.ty, is_index: false }, + )); + return; + } + ty::Array(_, _) => (), + _ => bug!("Unexpected type {:#?}", place_ty.ty), + } + } + ProjectionElem::Index(_) => match place_ty.ty.kind() { + ty::Array(..) | ty::Slice(..) => { + self.move_errors.push(MoveError::new( + place, + location, + InteriorOfSliceOrArray { ty: place_ty.ty, is_index: true }, + )); + return; + } + _ => bug!("Unexpected type {place_ty:#?}"), + }, + // `OpaqueCast`: only transmutes the type, so no moves there. + // `Downcast` : only changes information about a `Place` without moving. + // `Subtype` : only transmutes the type, so no moves. + // So it's safe to skip these. + ProjectionElem::OpaqueCast(_) + | ProjectionElem::Subtype(_) + | ProjectionElem::Downcast(_, _) => (), + } + + place_ty = place_ty.projection_ty(tcx, elem); + } + } + fn check_if_full_path_is_moved( &mut self, location: Location, @@ -1967,7 +2106,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { kind: mut_borrow_kind })) | Write(WriteKind::MutableBorrow(BorrowKind::Mut { kind: mut_borrow_kind })) => { let is_local_mutation_allowed = match mut_borrow_kind { - // `ClosureCapture` is used for mutable variable with a immutable binding. + // `ClosureCapture` is used for mutable variable with an immutable binding. // This is only behaviour difference between `ClosureCapture` and mutable borrows. MutBorrowKind::ClosureCapture => LocalMutationIsAllowed::Yes, MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow => { @@ -2070,7 +2209,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { local: Local, flow_state: &Flows<'cx, 'tcx>, ) -> Option<InitIndex> { - let mpi = self.move_data.rev_lookup.find_local(local); + let mpi = self.move_data.rev_lookup.find_local(local)?; let ii = &self.move_data.init_path_map[mpi]; ii.into_iter().find(|&&index| flow_state.ever_inits.contains(index)).copied() } |