about summary refs log tree commit diff
path: root/compiler/rustc_borrowck/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_borrowck/src/lib.rs')
-rw-r--r--compiler/rustc_borrowck/src/lib.rs263
1 files changed, 201 insertions, 62 deletions
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 9c77767e7a7..1a74582389d 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,
@@ -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()
     }