summaryrefslogtreecommitdiffstats
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.rs265
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()
}