diff options
Diffstat (limited to 'compiler/rustc_borrowck/src')
| -rw-r--r-- | compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_borrowck/src/diagnostics/mod.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_borrowck/src/diagnostics/move_errors.rs | 156 | ||||
| -rw-r--r-- | compiler/rustc_borrowck/src/lib.rs | 233 | ||||
| -rw-r--r-- | compiler/rustc_borrowck/src/type_check/liveness/trace.rs | 2 |
5 files changed, 280 insertions, 119 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index ecf7930ff6b..b5ad02dc688 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2633,9 +2633,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /* Check if the mpi is initialized as an argument */ let mut is_argument = false; for arg in self.body.args_iter() { - let path = self.move_data.rev_lookup.find_local(arg); - if mpis.contains(&path) { - is_argument = true; + if let Some(path) = self.move_data.rev_lookup.find_local(arg) { + if mpis.contains(&path) { + is_argument = true; + } } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 6a911b23462..4b95b4783eb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -46,6 +46,7 @@ mod mutability_errors; mod region_errors; pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo}; +pub(crate) use move_errors::{IllegalMoveOriginKind, MoveError}; pub(crate) use mutability_errors::AccessKind; pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder; pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index e05c04e1188..695ac6980cd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -1,9 +1,7 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_middle::mir::*; -use rustc_middle::ty; -use rustc_mir_dataflow::move_paths::{ - IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex, -}; +use rustc_middle::ty::{self, Ty}; +use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; use rustc_span::{BytePos, Span}; use crate::diagnostics::CapturedMessageOpt; @@ -11,6 +9,42 @@ use crate::diagnostics::{DescribePlaceOpt, UseSpans}; use crate::prefixes::PrefixSet; use crate::MirBorrowckCtxt; +#[derive(Debug)] +pub enum IllegalMoveOriginKind<'tcx> { + /// Illegal move due to attempt to move from behind a reference. + BorrowedContent { + /// The place the reference refers to: if erroneous code was trying to + /// move from `(*x).f` this will be `*x`. + target_place: Place<'tcx>, + }, + + /// Illegal move due to attempt to move from field of an ADT that + /// implements `Drop`. Rust maintains invariant that all `Drop` + /// ADT's remain fully-initialized so that user-defined destructor + /// can safely read from all of the ADT's fields. + InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> }, + + /// Illegal move due to attempt to move out of a slice or array. + InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool }, +} + +#[derive(Debug)] +pub(crate) struct MoveError<'tcx> { + place: Place<'tcx>, + location: Location, + kind: IllegalMoveOriginKind<'tcx>, +} + +impl<'tcx> MoveError<'tcx> { + pub(crate) fn new( + place: Place<'tcx>, + location: Location, + kind: IllegalMoveOriginKind<'tcx>, + ) -> Self { + MoveError { place, location, kind } + } +} + // Often when desugaring a pattern match we may have many individual moves in // MIR that are all part of one operation from the user's point-of-view. For // example: @@ -53,20 +87,18 @@ enum GroupedMoveError<'tcx> { } impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { - pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) { - let grouped_errors = self.group_move_errors(move_errors); + pub(crate) fn report_move_errors(&mut self) { + let grouped_errors = self.group_move_errors(); for error in grouped_errors { self.report(error); } } - fn group_move_errors( - &self, - errors: Vec<(Place<'tcx>, MoveError<'tcx>)>, - ) -> Vec<GroupedMoveError<'tcx>> { + fn group_move_errors(&mut self) -> Vec<GroupedMoveError<'tcx>> { let mut grouped_errors = Vec::new(); - for (original_path, error) in errors { - self.append_to_grouped_errors(&mut grouped_errors, original_path, error); + let errors = std::mem::take(&mut self.move_errors); + for error in errors { + self.append_to_grouped_errors(&mut grouped_errors, error); } grouped_errors } @@ -74,66 +106,58 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn append_to_grouped_errors( &self, grouped_errors: &mut Vec<GroupedMoveError<'tcx>>, - original_path: Place<'tcx>, error: MoveError<'tcx>, ) { - match error { - MoveError::UnionMove { .. } => { - unimplemented!("don't know how to report union move errors yet.") - } - MoveError::IllegalMove { cannot_move_out_of: IllegalMoveOrigin { location, kind } } => { - // Note: that the only time we assign a place isn't a temporary - // to a user variable is when initializing it. - // If that ever stops being the case, then the ever initialized - // flow could be used. - if let Some(StatementKind::Assign(box ( - place, - Rvalue::Use(Operand::Move(move_from)), - ))) = self.body.basic_blocks[location.block] - .statements - .get(location.statement_index) - .map(|stmt| &stmt.kind) + let MoveError { place: original_path, location, kind } = error; + + // Note: that the only time we assign a place isn't a temporary + // to a user variable is when initializing it. + // If that ever stops being the case, then the ever initialized + // flow could be used. + if let Some(StatementKind::Assign(box (place, Rvalue::Use(Operand::Move(move_from))))) = + self.body.basic_blocks[location.block] + .statements + .get(location.statement_index) + .map(|stmt| &stmt.kind) + { + if let Some(local) = place.as_local() { + let local_decl = &self.body.local_decls[local]; + // opt_match_place is the + // match_span is the span of the expression being matched on + // match *x.y { ... } match_place is Some(*x.y) + // ^^^^ match_span is the span of *x.y + // + // opt_match_place is None for let [mut] x = ... statements, + // whether or not the right-hand side is a place expression + if let LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((opt_match_place, match_span)), + binding_mode: _, + opt_ty_info: _, + pat_span: _, + })) = *local_decl.local_info() { - if let Some(local) = place.as_local() { - let local_decl = &self.body.local_decls[local]; - // opt_match_place is the - // match_span is the span of the expression being matched on - // match *x.y { ... } match_place is Some(*x.y) - // ^^^^ match_span is the span of *x.y - // - // opt_match_place is None for let [mut] x = ... statements, - // whether or not the right-hand side is a place expression - if let LocalInfo::User(BindingForm::Var(VarBindingForm { - opt_match_place: Some((opt_match_place, match_span)), - binding_mode: _, - opt_ty_info: _, - pat_span: _, - })) = *local_decl.local_info() - { - let stmt_source_info = self.body.source_info(location); - self.append_binding_error( - grouped_errors, - kind, - original_path, - *move_from, - local, - opt_match_place, - match_span, - stmt_source_info.span, - ); - return; - } - } + let stmt_source_info = self.body.source_info(location); + self.append_binding_error( + grouped_errors, + kind, + original_path, + *move_from, + local, + opt_match_place, + match_span, + stmt_source_info.span, + ); + return; } - - let move_spans = self.move_spans(original_path.as_ref(), location); - grouped_errors.push(GroupedMoveError::OtherIllegalMove { - use_spans: move_spans, - original_path, - kind, - }); } } + + let move_spans = self.move_spans(original_path.as_ref(), location); + grouped_errors.push(GroupedMoveError::OtherIllegalMove { + use_spans: move_spans, + original_path, + kind, + }); } fn append_binding_error( diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index ee34be847cc..1a74582389d 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -31,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}; @@ -55,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}; @@ -224,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 }; @@ -313,36 +304,49 @@ fn do_mir_borrowck<'tcx>( 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_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, - 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 { @@ -366,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, }; @@ -378,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), @@ -387,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 @@ -595,6 +600,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> { polonius_output: Option<Rc<PoloniusOutput>>, errors: error::BorrowckErrors<'tcx>, + move_errors: Vec<MoveError<'tcx>>, } // Check that: @@ -725,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); @@ -1409,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 { @@ -1469,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, @@ -1590,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, @@ -2074,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() } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 80e6c6c1dfb..cfefe0a3e65 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -317,7 +317,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { fn compute_drop_live_points_for(&mut self, local: Local) { debug!("compute_drop_live_points_for(local={:?})", local); - let mpi = self.cx.move_data.rev_lookup.find_local(local); + let Some(mpi) = self.cx.move_data.rev_lookup.find_local(local) else { return }; debug!("compute_drop_live_points_for: mpi = {:?}", mpi); // Find the drops where `local` is initialized. |
