use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::graph::dominators::Dominators;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::{HirId, Node};
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_middle::mir::{
traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem,
PlaceRef,
};
use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt};
use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT};
use rustc_span::{Span, Symbol, DUMMY_SP};
use either::Either;
use smallvec::SmallVec;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::mem;
use std::rc::Rc;
use crate::dataflow;
use crate::dataflow::impls::{
Borrows, EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
};
use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex};
use crate::dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError};
use crate::dataflow::MoveDataParamEnv;
use crate::dataflow::{Analysis, BorrowckFlowState as Flows, BorrowckResults};
use self::diagnostics::{AccessKind, RegionName};
use self::location::LocationTable;
use self::prefixes::PrefixSet;
use self::MutateMode::{JustWrite, WriteAndRead};
use self::path_utils::*;
mod borrow_set;
mod constraint_generation;
mod constraints;
mod def_use;
mod diagnostics;
mod facts;
mod invalidation;
mod location;
mod member_constraints;
mod nll;
mod path_utils;
mod place_ext;
mod places_conflict;
mod prefixes;
mod region_infer;
mod renumber;
mod type_check;
mod universal_regions;
mod used_muts;
crate use borrow_set::{BorrowData, BorrowSet};
crate use nll::{PoloniusOutput, ToRegionVid};
crate use place_ext::PlaceExt;
crate use places_conflict::{places_conflict, PlaceConflictBias};
crate use region_infer::RegionInferenceContext;
#[derive(Debug)]
crate struct Upvar {
name: Symbol,
var_hir_id: HirId,
by_ref: bool,
mutability: Mutability,
}
const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref];
pub fn provide(providers: &mut Providers) {
*providers = Providers {
mir_borrowck: |tcx, did| {
if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) {
tcx.mir_borrowck_const_arg(def)
} else {
mir_borrowck(tcx, ty::WithOptConstParam::unknown(did))
}
},
mir_borrowck_const_arg: |tcx, (did, param_did)| {
mir_borrowck(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
},
..*providers
};
}
fn mir_borrowck<'tcx>(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
) -> &'tcx BorrowCheckResult<'tcx> {
let (input_body, promoted) = tcx.mir_promoted(def);
debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id()));
let opt_closure_req = tcx.infer_ctxt().enter(|infcx| {
let input_body: &Body<'_> = &input_body.borrow();
let promoted: &IndexVec<_, _> = &promoted.borrow();
do_mir_borrowck(&infcx, input_body, promoted)
});
debug!("mir_borrowck done");
tcx.arena.alloc(opt_closure_req)
}
fn do_mir_borrowck<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
input_body: &Body<'tcx>,
input_promoted: &IndexVec<Promoted, Body<'tcx>>,
) -> BorrowCheckResult<'tcx> {
let def = input_body.source.with_opt_param().as_local().unwrap();
debug!("do_mir_borrowck(def = {:?})", def);
let tcx = infcx.tcx;
let param_env = tcx.param_env(def.did);
let id = tcx.hir().local_def_id_to_hir_id(def.did);
let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
for var_debug_info in &input_body.var_debug_info {
if let Some(local) = var_debug_info.place.as_local() {
if let Some(prev_name) = local_names[local] {
if var_debug_info.name != prev_name {
span_bug!(
var_debug_info.source_info.span,
"local {:?} has many names (`{}` vs `{}`)",
local,
prev_name,
var_debug_info.name
);
}
}
local_names[local] = Some(var_debug_info.name);
}
}
let tables = tcx.typeck_opt_const_arg(def);
if let Some(ErrorReported) = tables.tainted_by_errors {
infcx.set_tainted_by_errors();
}
let upvars: Vec<_> = tables
.closure_captures
.get(&def.did.to_def_id())
.into_iter()
.flat_map(|v| v.values())
.map(|upvar_id| {
let var_hir_id = upvar_id.var_path.hir_id;
let capture = tables.upvar_capture(*upvar_id);
let by_ref = match capture {
ty::UpvarCapture::ByValue(_) => false,
ty::UpvarCapture::ByRef(..) => true,
};
let mut upvar = Upvar {
name: tcx.hir().name(var_hir_id),
var_hir_id,
by_ref,
mutability: Mutability::Not,
};
let bm = *tables.pat_binding_modes().get(var_hir_id).expect("missing binding mode");
if bm == ty::BindByValue(hir::Mutability::Mut) {
upvar.mutability = Mutability::Mut;
}
upvar
})
.collect();
let mut body = input_body.clone();
let mut promoted = input_promoted.clone();
let free_regions = nll::replace_regions_in_mir(infcx, param_env, &mut body, &mut promoted);
let body = &body;
let location_table = &LocationTable::new(&body);
let mut errors_buffer = Vec::new();
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
.iter_enumerated()
.map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env)));
let mdpe = MoveDataParamEnv { move_data, param_env };
let mut flow_inits = MaybeInitializedPlaces::new(tcx, &body, &mdpe)
.into_engine(tcx, &body)
.pass_name("borrowck")
.iterate_to_fixpoint()
.into_results_cursor(&body);
let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(id).is_fn_or_closure();
let borrow_set =
Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data));
let nll::NllOutput {
regioncx,
opaque_type_values,
polonius_output,
opt_closure_req,
nll_errors,
} = nll::compute_regions(
infcx,
free_regions,
body,
&promoted,
location_table,
param_env,
&mut flow_inits,
&mdpe.move_data,
&borrow_set,
&upvars,
);
nll::dump_mir_results(infcx, &body, ®ioncx, &opt_closure_req);
nll::dump_annotation(
infcx,
&body,
®ioncx,
&opt_closure_req,
&opaque_type_values,
&mut errors_buffer,
);
drop(flow_inits);
let regioncx = Rc::new(regioncx);
let flow_borrows = Borrows::new(tcx, &body, regioncx.clone(), &borrow_set)
.into_engine(tcx, &body)
.pass_name("borrowck")
.iterate_to_fixpoint();
let flow_uninits = MaybeUninitializedPlaces::new(tcx, &body, &mdpe)
.into_engine(tcx, &body)
.pass_name("borrowck")
.iterate_to_fixpoint();
let flow_ever_inits = EverInitializedPlaces::new(tcx, &body, &mdpe)
.into_engine(tcx, &body)
.pass_name("borrowck")
.iterate_to_fixpoint();
let movable_generator = match tcx.hir().get(id) {
Node::Expr(&hir::Expr {
kind: hir::ExprKind::Closure(.., Some(hir::Movability::Static)),
..
}) => false,
_ => true,
};
for (idx, move_data_results) in promoted_errors {
let promoted_body = &promoted[idx];
let dominators = promoted_body.dominators();
if let Err((move_data, move_errors)) = move_data_results {
let mut promoted_mbcx = MirBorrowckCtxt {
infcx,
param_env,
body: promoted_body,
move_data: &move_data,
location_table: &LocationTable::new(promoted_body),
movable_generator,
fn_self_span_reported: Default::default(),
locals_are_invalidated_at_exit,
access_place_error_reported: Default::default(),
reservation_error_reported: Default::default(),
reservation_warnings: Default::default(),
move_error_reported: BTreeMap::new(),
uninitialized_error_reported: Default::default(),
errors_buffer,
regioncx: regioncx.clone(),
used_mut: Default::default(),
used_mut_upvars: SmallVec::new(),
borrow_set: borrow_set.clone(),
dominators,
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,
};
promoted_mbcx.report_move_errors(move_errors);
errors_buffer = promoted_mbcx.errors_buffer;
};
}
let dominators = body.dominators();
let mut mbcx = MirBorrowckCtxt {
infcx,
param_env,
body,
move_data: &mdpe.move_data,
location_table,
movable_generator,
locals_are_invalidated_at_exit,
fn_self_span_reported: Default::default(),
access_place_error_reported: Default::default(),
reservation_error_reported: Default::default(),
reservation_warnings: Default::default(),
move_error_reported: BTreeMap::new(),
uninitialized_error_reported: Default::default(),
errors_buffer,
regioncx,
used_mut: Default::default(),
used_mut_upvars: SmallVec::new(),
borrow_set,
dominators,
upvars,
local_names,
region_names: RefCell::default(),
next_region_name: RefCell::new(1),
polonius_output,
};
mbcx.report_region_errors(nll_errors);
let results = BorrowckResults {
ever_inits: flow_ever_inits,
uninits: flow_uninits,
borrows: flow_borrows,
};
mbcx.report_move_errors(move_errors);
dataflow::visit_results(
&body,
traversal::reverse_postorder(&body).map(|(bb, _)| bb),
&results,
&mut mbcx,
);
let reservation_warnings = mem::take(&mut mbcx.reservation_warnings);
for (_, (place, span, location, bk, borrow)) in reservation_warnings {
let mut initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow);
let scope = mbcx.body.source_info(location).scope;
let lint_root = match &mbcx.body.source_scopes[scope].local_data {
ClearCrossCrate::Set(data) => data.lint_root,
_ => id,
};
mbcx.infcx.tcx.struct_span_lint_hir(
MUTABLE_BORROW_RESERVATION_CONFLICT,
lint_root,
DUMMY_SP,
|lint| {
let mut diag = lint.build("");
diag.message = initial_diag.styled_message().clone();
diag.span = initial_diag.span.clone();
diag.buffer(&mut mbcx.errors_buffer);
},
);
initial_diag.cancel();
}
let temporary_used_locals: FxHashSet<Local> = mbcx
.used_mut
.iter()
.filter(|&local| !mbcx.body.local_decls[*local].is_user_variable())
.cloned()
.collect();
let unused_mut_locals =
mbcx.body.mut_vars_iter().filter(|local| !mbcx.used_mut.contains(local)).collect();
mbcx.gather_used_muts(temporary_used_locals, unused_mut_locals);
debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
let used_mut = mbcx.used_mut;
for local in mbcx.body.mut_vars_and_args_iter().filter(|local| !used_mut.contains(local)) {
let local_decl = &mbcx.body.local_decls[local];
let lint_root = match &mbcx.body.source_scopes[local_decl.source_info.scope].local_data {
ClearCrossCrate::Set(data) => data.lint_root,
_ => continue,
};
match mbcx.local_names[local] {
Some(name) => {
if name.as_str().starts_with('_') {
continue;
}
}
None => continue,
}
let span = local_decl.source_info.span;
if span.desugaring_kind().is_some() {
continue;
}
tcx.struct_span_lint_hir(UNUSED_MUT, lint_root, span, |lint| {
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
lint.build("variable does not need to be mutable")
.span_suggestion_short(
mut_span,
"remove this `mut`",
String::new(),
Applicability::MachineApplicable,
)
.emit();
})
}
for (_, (_, diag)) in mbcx.move_error_reported {
diag.buffer(&mut mbcx.errors_buffer);
}
if !mbcx.errors_buffer.is_empty() {
mbcx.errors_buffer.sort_by_key(|diag| diag.sort_span);
for diag in mbcx.errors_buffer.drain(..) {
mbcx.infcx.tcx.sess.diagnostic().emit_diagnostic(&diag);
}
}
let result = BorrowCheckResult {
concrete_opaque_types: opaque_type_values,
closure_requirements: opt_closure_req,
used_mut_upvars: mbcx.used_mut_upvars,
};
debug!("do_mir_borrowck: result = {:#?}", result);
result
}
crate struct MirBorrowckCtxt<'cx, 'tcx> {
crate infcx: &'cx InferCtxt<'cx, 'tcx>,
param_env: ParamEnv<'tcx>,
body: &'cx Body<'tcx>,
move_data: &'cx MoveData<'tcx>,
location_table: &'cx LocationTable,
movable_generator: bool,
locals_are_invalidated_at_exit: bool,
access_place_error_reported: FxHashSet<(Place<'tcx>, Span)>,
reservation_error_reported: FxHashSet<Place<'tcx>>,
fn_self_span_reported: FxHashSet<Span>,
reservation_warnings:
FxHashMap<BorrowIndex, (Place<'tcx>, Span, Location, BorrowKind, BorrowData<'tcx>)>,
move_error_reported: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, DiagnosticBuilder<'cx>)>,
uninitialized_error_reported: FxHashSet<PlaceRef<'tcx>>,
errors_buffer: Vec<Diagnostic>,
used_mut: FxHashSet<Local>,
used_mut_upvars: SmallVec<[Field; 8]>,
regioncx: Rc<RegionInferenceContext<'tcx>>,
borrow_set: Rc<BorrowSet<'tcx>>,
dominators: Dominators<BasicBlock>,
upvars: Vec<Upvar>,
local_names: IndexVec<Local, Option<Symbol>>,
region_names: RefCell<FxHashMap<RegionVid, RegionName>>,
next_region_name: RefCell<usize>,
polonius_output: Option<Rc<PoloniusOutput>>,
}
impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> {
type FlowState = Flows<'cx, 'tcx>;
fn visit_statement_before_primary_effect(
&mut self,
flow_state: &Flows<'cx, 'tcx>,
stmt: &'cx Statement<'tcx>,
location: Location,
) {
debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {:?}", location, stmt, flow_state);
let span = stmt.source_info.span;
self.check_activations(location, span, flow_state);
match &stmt.kind {
StatementKind::Assign(box (lhs, ref rhs)) => {
self.consume_rvalue(location, (rhs, span), flow_state);
self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state);
}
StatementKind::FakeRead(_, box ref place) => {
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Use,
(place.as_ref(), span),
flow_state,
);
}
StatementKind::SetDiscriminant { place, variant_index: _ } => {
self.mutate_place(location, (**place, span), Shallow(None), JustWrite, flow_state);
}
StatementKind::LlvmInlineAsm(ref asm) => {
for (o, output) in asm.asm.outputs.iter().zip(asm.outputs.iter()) {
if o.is_indirect {
self.access_place(
location,
(*output, o.span),
(Deep, Read(ReadKind::Copy)),
LocalMutationIsAllowed::No,
flow_state,
);
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Use,
(output.as_ref(), o.span),
flow_state,
);
} else {
self.mutate_place(
location,
(*output, o.span),
if o.is_rw { Deep } else { Shallow(None) },
if o.is_rw { WriteAndRead } else { JustWrite },
flow_state,
);
}
}
for (_, input) in asm.inputs.iter() {
self.consume_operand(location, (input, span), flow_state);
}
}
StatementKind::Nop
| StatementKind::Coverage(..)
| StatementKind::AscribeUserType(..)
| StatementKind::Retag { .. }
| StatementKind::StorageLive(..) => {
}
StatementKind::StorageDead(local) => {
self.access_place(
location,
(Place::from(*local), span),
(Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
LocalMutationIsAllowed::Yes,
flow_state,
);
}
}
}
fn visit_terminator_before_primary_effect(
&mut self,
flow_state: &Flows<'cx, 'tcx>,
term: &'cx Terminator<'tcx>,
loc: Location,
) {
debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {:?}", loc, term, flow_state);
let span = term.source_info.span;
self.check_activations(loc, span, flow_state);
match term.kind {
TerminatorKind::SwitchInt { ref discr, switch_ty: _, targets: _ } => {
self.consume_operand(loc, (discr, span), flow_state);
}
TerminatorKind::Drop { place, target: _, unwind: _ } => {
debug!(
"visit_terminator_drop \
loc: {:?} term: {:?} place: {:?} span: {:?}",
loc, term, place, span
);
self.access_place(
loc,
(place, span),
(AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)),
LocalMutationIsAllowed::Yes,
flow_state,
);
}
TerminatorKind::DropAndReplace {
place: drop_place,
value: ref new_value,
target: _,
unwind: _,
} => {
self.mutate_place(loc, (drop_place, span), Deep, JustWrite, flow_state);
self.consume_operand(loc, (new_value, span), flow_state);
}
TerminatorKind::Call {
ref func,
ref args,
ref destination,
cleanup: _,
from_hir_call: _,
fn_span: _,
} => {
self.consume_operand(loc, (func, span), flow_state);
for arg in args {
self.consume_operand(loc, (arg, span), flow_state);
}
if let Some((dest, _ )) = *destination {
self.mutate_place(loc, (dest, span), Deep, JustWrite, flow_state);
}
}
TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => {
self.consume_operand(loc, (cond, span), flow_state);
use rustc_middle::mir::AssertKind;
if let AssertKind::BoundsCheck { ref len, ref index } = *msg {
self.consume_operand(loc, (len, span), flow_state);
self.consume_operand(loc, (index, span), flow_state);
}
}
TerminatorKind::Yield { ref value, resume: _, resume_arg, drop: _ } => {
self.consume_operand(loc, (value, span), flow_state);
self.mutate_place(loc, (resume_arg, span), Deep, JustWrite, flow_state);
}
TerminatorKind::InlineAsm {
template: _,
ref operands,
options: _,
line_spans: _,
destination: _,
} => {
for op in operands {
match *op {
InlineAsmOperand::In { reg: _, ref value }
| InlineAsmOperand::Const { ref value } => {
self.consume_operand(loc, (value, span), flow_state);
}
InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
if let Some(place) = place {
self.mutate_place(
loc,
(place, span),
Shallow(None),
JustWrite,
flow_state,
);
}
}
InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => {
self.consume_operand(loc, (in_value, span), flow_state);
if let Some(out_place) = out_place {
self.mutate_place(
loc,
(out_place, span),
Shallow(None),
JustWrite,
flow_state,
);
}
}
InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { def_id: _ } => {}
}
}
}
TerminatorKind::Goto { target: _ }
| TerminatorKind::Abort
| TerminatorKind::Unreachable
| TerminatorKind::Resume
| TerminatorKind::Return
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
| TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {
}
}
}
fn visit_terminator_after_primary_effect(
&mut self,
flow_state: &Flows<'cx, 'tcx>,
term: &'cx Terminator<'tcx>,
loc: Location,
) {
let span = term.source_info.span;
match term.kind {
TerminatorKind::Yield { value: _, resume: _, resume_arg: _, drop: _ } => {
if self.movable_generator {
let borrow_set = self.borrow_set.clone();
for i in flow_state.borrows.iter() {
let borrow = &borrow_set[i];
self.check_for_local_borrow(borrow, span);
}
}
}
TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {
let borrow_set = self.borrow_set.clone();
for i in flow_state.borrows.iter() {
let borrow = &borrow_set[i];
self.check_for_invalidation_at_exit(loc, borrow, span);
}
}
TerminatorKind::Abort
| TerminatorKind::Assert { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::Drop { .. }
| TerminatorKind::DropAndReplace { .. }
| TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
| TerminatorKind::FalseUnwind { real_target: _, unwind: _ }
| TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Unreachable
| TerminatorKind::InlineAsm { .. } => {}
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum MutateMode {
JustWrite,
WriteAndRead,
}
use self::AccessDepth::{Deep, Shallow};
use self::ReadOrWrite::{Activation, Read, Reservation, Write};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ArtificialField {
ArrayLength,
ShallowBorrow,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum AccessDepth {
Shallow(Option<ArtificialField>),
Deep,
Drop,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ReadOrWrite {
Read(ReadKind),
Write(WriteKind),
Reservation(WriteKind),
Activation(WriteKind, BorrowIndex),
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ReadKind {
Borrow(BorrowKind),
Copy,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum WriteKind {
StorageDeadOrDrop,
MutableBorrow(BorrowKind),
Mutate,
Move,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum LocalMutationIsAllowed {
Yes,
ExceptUpvars,
No,
}
#[derive(Copy, Clone, Debug)]
enum InitializationRequiringAction {
Update,
Borrow,
MatchOn,
Use,
Assignment,
PartialAssignment,
}
struct RootPlace<'tcx> {
place_local: Local,
place_projection: &'tcx [PlaceElem<'tcx>],
is_local_mutation_allowed: LocalMutationIsAllowed,
}
impl InitializationRequiringAction {
fn as_noun(self) -> &'static str {
match self {
InitializationRequiringAction::Update => "update",
InitializationRequiringAction::Borrow => "borrow",
InitializationRequiringAction::MatchOn => "use",
InitializationRequiringAction::Use => "use",
InitializationRequiringAction::Assignment => "assign",
InitializationRequiringAction::PartialAssignment => "assign to part",
}
}
fn as_verb_in_past_tense(self) -> &'static str {
match self {
InitializationRequiringAction::Update => "updated",
InitializationRequiringAction::Borrow => "borrowed",
InitializationRequiringAction::MatchOn => "matched on",
InitializationRequiringAction::Use => "used",
InitializationRequiringAction::Assignment => "assigned",
InitializationRequiringAction::PartialAssignment => "partially assigned",
}
}
}
impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
fn body(&self) -> &'cx Body<'tcx> {
self.body
}
fn access_place(
&mut self,
location: Location,
place_span: (Place<'tcx>, Span),
kind: (AccessDepth, ReadOrWrite),
is_local_mutation_allowed: LocalMutationIsAllowed,
flow_state: &Flows<'cx, 'tcx>,
) {
let (sd, rw) = kind;
if let Activation(_, borrow_index) = rw {
if self.reservation_error_reported.contains(&place_span.0) {
debug!(
"skipping access_place for activation of invalid reservation \
place: {:?} borrow_index: {:?}",
place_span.0, borrow_index
);
return;
}
}
if !self.access_place_error_reported.is_empty()
&& self.access_place_error_reported.contains(&(place_span.0, place_span.1))
{
debug!(
"access_place: suppressing error place_span=`{:?}` kind=`{:?}`",
place_span, kind
);
return;
}
let mutability_error = self.check_access_permissions(
place_span,
rw,
is_local_mutation_allowed,
flow_state,
location,
);
let conflict_error =
self.check_access_for_conflict(location, place_span, sd, rw, flow_state);
if let (Activation(_, borrow_idx), true) = (kind.1, conflict_error) {
self.reservation_warnings.remove(&borrow_idx);
}
if conflict_error || mutability_error {
debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind);
self.access_place_error_reported.insert((place_span.0, place_span.1));
}
}
fn check_access_for_conflict(
&mut self,
location: Location,
place_span: (Place<'tcx>, Span),
sd: AccessDepth,
rw: ReadOrWrite,
flow_state: &Flows<'cx, 'tcx>,
) -> bool {
debug!(
"check_access_for_conflict(location={:?}, place_span={:?}, sd={:?}, rw={:?})",
location, place_span, sd, rw,
);
let mut error_reported = false;
let tcx = self.infcx.tcx;
let body = self.body;
let borrow_set = self.borrow_set.clone();
let polonius_output = self.polonius_output.clone();
let borrows_in_scope = if let Some(polonius) = &polonius_output {
let location = self.location_table.start_index(location);
Either::Left(polonius.errors_at(location).iter().copied())
} else {
Either::Right(flow_state.borrows.iter())
};
each_borrow_involving_path(
self,
tcx,
body,
location,
(sd, place_span.0),
&borrow_set,
borrows_in_scope,
|this, borrow_index, borrow| match (rw, borrow.kind) {
(Activation(_, activating), _) if activating == borrow_index => {
debug!(
"check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \
skipping {:?} b/c activation of same borrow_index",
place_span,
sd,
rw,
(borrow_index, borrow),
);
Control::Continue
}
(Read(_), BorrowKind::Shared | BorrowKind::Shallow)
| (
Read(ReadKind::Borrow(BorrowKind::Shallow)),
BorrowKind::Unique | BorrowKind::Mut { .. },
) => Control::Continue,
(Write(WriteKind::Move), BorrowKind::Shallow) => {
Control::Continue
}
(Read(kind), BorrowKind::Unique | BorrowKind::Mut { .. }) => {
if !is_active(&this.dominators, borrow, location) {
assert!(allow_two_phase_borrow(borrow.kind));
return Control::Continue;
}
error_reported = true;
match kind {
ReadKind::Copy => {
this.report_use_while_mutably_borrowed(location, place_span, borrow)
.buffer(&mut this.errors_buffer);
}
ReadKind::Borrow(bk) => {
this.report_conflicting_borrow(location, place_span, bk, borrow)
.buffer(&mut this.errors_buffer);
}
}
Control::Break
}
(
Reservation(WriteKind::MutableBorrow(bk)),
BorrowKind::Shallow | BorrowKind::Shared,
) if { tcx.migrate_borrowck() && this.borrow_set.contains(&location) } => {
let bi = this.borrow_set.get_index_of(&location).unwrap();
debug!(
"recording invalid reservation of place: {:?} with \
borrow index {:?} as warning",
place_span.0, bi,
);
this.reservation_warnings
.insert(bi, (place_span.0, place_span.1, location, bk, borrow.clone()));
Control::Continue
}
(Reservation(kind) | Activation(kind, _) | Write(kind), _) => {
match rw {
Reservation(..) => {
debug!(
"recording invalid reservation of \
place: {:?}",
place_span.0
);
this.reservation_error_reported.insert(place_span.0);
}
Activation(_, activating) => {
debug!(
"observing check_place for activation of \
borrow_index: {:?}",
activating
);
}
Read(..) | Write(..) => {}
}
error_reported = true;
match kind {
WriteKind::MutableBorrow(bk) => {
this.report_conflicting_borrow(location, place_span, bk, borrow)
.buffer(&mut this.errors_buffer);
}
WriteKind::StorageDeadOrDrop => this
.report_borrowed_value_does_not_live_long_enough(
location,
borrow,
place_span,
Some(kind),
),
WriteKind::Mutate => {
this.report_illegal_mutation_of_borrowed(location, place_span, borrow)
}
WriteKind::Move => {
this.report_move_out_while_borrowed(location, place_span, borrow)
}
}
Control::Break
}
},
);
error_reported
}
fn mutate_place(
&mut self,
location: Location,
place_span: (Place<'tcx>, Span),
kind: AccessDepth,
mode: MutateMode,
flow_state: &Flows<'cx, 'tcx>,
) {
match mode {
MutateMode::WriteAndRead => {
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Update,
(place_span.0.as_ref(), place_span.1),
flow_state,
);
}
MutateMode::JustWrite => {
self.check_if_assigned_path_is_moved(location, place_span, flow_state);
}
}
if let Some(local) = place_span.0.as_local() {
if let Mutability::Not = self.body.local_decls[local].mutability {
self.check_if_reassignment_to_immutable_state(
location, local, place_span, flow_state,
);
return;
}
}
self.access_place(
location,
place_span,
(kind, Write(WriteKind::Mutate)),
LocalMutationIsAllowed::No,
flow_state,
);
}
fn consume_rvalue(
&mut self,
location: Location,
(rvalue, span): (&'cx Rvalue<'tcx>, Span),
flow_state: &Flows<'cx, 'tcx>,
) {
match *rvalue {
Rvalue::Ref(_ , bk, place) => {
let access_kind = match bk {
BorrowKind::Shallow => {
(Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk)))
}
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
BorrowKind::Unique | BorrowKind::Mut { .. } => {
let wk = WriteKind::MutableBorrow(bk);
if allow_two_phase_borrow(bk) {
(Deep, Reservation(wk))
} else {
(Deep, Write(wk))
}
}
};
self.access_place(
location,
(place, span),
access_kind,
LocalMutationIsAllowed::No,
flow_state,
);
let action = if bk == BorrowKind::Shallow {
InitializationRequiringAction::MatchOn
} else {
InitializationRequiringAction::Borrow
};
self.check_if_path_or_subpath_is_moved(
location,
action,
(place.as_ref(), span),
flow_state,
);
}
Rvalue::AddressOf(mutability, place) => {
let access_kind = match mutability {
Mutability::Mut => (
Deep,
Write(WriteKind::MutableBorrow(BorrowKind::Mut {
allow_two_phase_borrow: false,
})),
),
Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
};
self.access_place(
location,
(place, span),
access_kind,
LocalMutationIsAllowed::No,
flow_state,
);
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Borrow,
(place.as_ref(), span),
flow_state,
);
}
Rvalue::ThreadLocalRef(_) => {}
Rvalue::Use(ref operand)
| Rvalue::Repeat(ref operand, _)
| Rvalue::UnaryOp(_ , ref operand)
| Rvalue::Cast(_ , ref operand, _ ) => {
self.consume_operand(location, (operand, span), flow_state)
}
Rvalue::Len(place) | Rvalue::Discriminant(place) => {
let af = match *rvalue {
Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
Rvalue::Discriminant(..) => None,
_ => unreachable!(),
};
self.access_place(
location,
(place, span),
(Shallow(af), Read(ReadKind::Copy)),
LocalMutationIsAllowed::No,
flow_state,
);
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Use,
(place.as_ref(), span),
flow_state,
);
}
Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2)
| Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => {
self.consume_operand(location, (operand1, span), flow_state);
self.consume_operand(location, (operand2, span), flow_state);
}
Rvalue::NullaryOp(_op, _ty) => {
}
Rvalue::Aggregate(ref aggregate_kind, ref operands) => {
match **aggregate_kind {
AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) => {
let BorrowCheckResult { used_mut_upvars, .. } =
self.infcx.tcx.mir_borrowck(def_id.expect_local());
debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars);
for field in used_mut_upvars {
self.propagate_closure_used_mut_upvar(&operands[field.index()]);
}
}
AggregateKind::Adt(..)
| AggregateKind::Array(..)
| AggregateKind::Tuple { .. } => (),
}
for operand in operands {
self.consume_operand(location, (operand, span), flow_state);
}
}
}
}
fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) {
let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| {
if !place.projection.is_empty() {
if let Some(field) = this.is_upvar_field_projection(place.as_ref()) {
this.used_mut_upvars.push(field);
}
} else {
this.used_mut.insert(place.local);
}
};
match *operand {
Operand::Move(place) | Operand::Copy(place) => {
match place.as_local() {
Some(local) if !self.body.local_decls[local].is_user_variable() => {
if self.body.local_decls[local].ty.is_mutable_ptr() {
return;
}
let temp_mpi = self.move_data.rev_lookup.find_local(local);
let init = if let [init_index] = *self.move_data.init_path_map[temp_mpi] {
&self.move_data.inits[init_index]
} else {
bug!("temporary should be initialized exactly once")
};
let loc = match init.location {
InitLocation::Statement(stmt) => stmt,
_ => bug!("temporary initialized in arguments"),
};
let body = self.body;
let bbd = &body[loc.block];
let stmt = &bbd.statements[loc.statement_index];
debug!("temporary assigned in: stmt={:?}", stmt);
if let StatementKind::Assign(box (_, Rvalue::Ref(_, _, source))) = stmt.kind
{
propagate_closure_used_mut_place(self, source);
} else {
bug!(
"closures should only capture user variables \
or references to user variables"
);
}
}
_ => propagate_closure_used_mut_place(self, place),
}
}
Operand::Constant(..) => {}
}
}
fn consume_operand(
&mut self,
location: Location,
(operand, span): (&'cx Operand<'tcx>, Span),
flow_state: &Flows<'cx, 'tcx>,
) {
match *operand {
Operand::Copy(place) => {
self.access_place(
location,
(place, span),
(Deep, Read(ReadKind::Copy)),
LocalMutationIsAllowed::No,
flow_state,
);
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Use,
(place.as_ref(), span),
flow_state,
);
}
Operand::Move(place) => {
self.access_place(
location,
(place, span),
(Deep, Write(WriteKind::Move)),
LocalMutationIsAllowed::Yes,
flow_state,
);
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Use,
(place.as_ref(), span),
flow_state,
);
}
Operand::Constant(_) => {}
}
}
fn check_for_invalidation_at_exit(
&mut self,
location: Location,
borrow: &BorrowData<'tcx>,
span: Span,
) {
debug!("check_for_invalidation_at_exit({:?})", borrow);
let place = borrow.borrowed_place;
let mut root_place = PlaceRef { local: place.local, projection: &[] };
let (might_be_alive, will_be_dropped) =
if self.body.local_decls[root_place.local].is_ref_to_thread_local() {
root_place.projection = DEREF_PROJECTION;
(true, true)
} else {
(false, self.locals_are_invalidated_at_exit)
};
if !will_be_dropped {
debug!("place_is_invalidated_at_exit({:?}) - won't be dropped", place);
return;
}
let sd = if might_be_alive { Deep } else { Shallow(None) };
if places_conflict::borrow_conflicts_with_place(
self.infcx.tcx,
&self.body,
place,
borrow.kind,
root_place,
sd,
places_conflict::PlaceConflictBias::Overlap,
) {
debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
let span = self.infcx.tcx.sess.source_map().end_point(span);
self.report_borrowed_value_does_not_live_long_enough(
location,
borrow,
(place, span),
None,
)
}
}
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(
self.retrieve_borrow_spans(borrow).var_or_use(),
yield_span,
);
err.buffer(&mut self.errors_buffer);
}
}
fn check_activations(&mut self, location: Location, span: Span, flow_state: &Flows<'cx, 'tcx>) {
let borrow_set = self.borrow_set.clone();
for &borrow_index in borrow_set.activations_at_location(location) {
let borrow = &borrow_set[borrow_index];
assert!(match borrow.kind {
BorrowKind::Shared | BorrowKind::Shallow => false,
BorrowKind::Unique | BorrowKind::Mut { .. } => true,
});
self.access_place(
location,
(borrow.borrowed_place, span),
(Deep, Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index)),
LocalMutationIsAllowed::No,
flow_state,
);
}
}
fn check_if_reassignment_to_immutable_state(
&mut self,
location: Location,
local: Local,
place_span: (Place<'tcx>, Span),
flow_state: &Flows<'cx, 'tcx>,
) {
debug!("check_if_reassignment_to_immutable_state({:?})", local);
if let Some(init_index) = self.is_local_ever_initialized(local, flow_state) {
let init = &self.move_data.inits[init_index];
let span = init.span(&self.body);
self.report_illegal_reassignment(location, place_span, span, place_span.0);
}
}
fn check_if_full_path_is_moved(
&mut self,
location: Location,
desired_action: InitializationRequiringAction,
place_span: (PlaceRef<'tcx>, Span),
flow_state: &Flows<'cx, 'tcx>,
) {
let maybe_uninits = &flow_state.uninits;
debug!("check_if_full_path_is_moved place: {:?}", place_span.0);
let (prefix, mpi) = self.move_path_closest_to(place_span.0);
if maybe_uninits.contains(mpi) {
self.report_use_of_moved_or_uninitialized(
location,
desired_action,
(prefix, place_span.0, place_span.1),
mpi,
);
}
}
fn check_if_subslice_element_is_moved(
&mut self,
location: Location,
desired_action: InitializationRequiringAction,
place_span: (PlaceRef<'tcx>, Span),
maybe_uninits: &BitSet<MovePathIndex>,
from: u64,
to: u64,
) {
if let Some(mpi) = self.move_path_for_place(place_span.0) {
let move_paths = &self.move_data.move_paths;
let root_path = &move_paths[mpi];
for (child_mpi, child_move_path) in root_path.children(move_paths) {
let last_proj = child_move_path.place.projection.last().unwrap();
if let ProjectionElem::ConstantIndex { offset, from_end, .. } = last_proj {
debug_assert!(!from_end, "Array constant indexing shouldn't be `from_end`.");
if (from..to).contains(offset) {
let uninit_child =
self.move_data.find_in_move_path_or_its_descendants(child_mpi, |mpi| {
maybe_uninits.contains(mpi)
});
if let Some(uninit_child) = uninit_child {
self.report_use_of_moved_or_uninitialized(
location,
desired_action,
(place_span.0, place_span.0, place_span.1),
uninit_child,
);
return;
}
}
}
}
}
}
fn check_if_path_or_subpath_is_moved(
&mut self,
location: Location,
desired_action: InitializationRequiringAction,
place_span: (PlaceRef<'tcx>, Span),
flow_state: &Flows<'cx, 'tcx>,
) {
let maybe_uninits = &flow_state.uninits;
self.check_if_full_path_is_moved(location, desired_action, place_span, flow_state);
if let [base_proj @ .., ProjectionElem::Subslice { from, to, from_end: false }] =
place_span.0.projection
{
let place_ty =
Place::ty_from(place_span.0.local, base_proj, self.body(), self.infcx.tcx);
if let ty::Array(..) = place_ty.ty.kind() {
let array_place = PlaceRef { local: place_span.0.local, projection: base_proj };
self.check_if_subslice_element_is_moved(
location,
desired_action,
(array_place, place_span.1),
maybe_uninits,
*from,
*to,
);
return;
}
}
debug!("check_if_path_or_subpath_is_moved place: {:?}", place_span.0);
if let Some(mpi) = self.move_path_for_place(place_span.0) {
let uninit_mpi = self
.move_data
.find_in_move_path_or_its_descendants(mpi, |mpi| maybe_uninits.contains(mpi));
if let Some(uninit_mpi) = uninit_mpi {
self.report_use_of_moved_or_uninitialized(
location,
desired_action,
(place_span.0, place_span.0, place_span.1),
uninit_mpi,
);
return;
}
}
}
fn move_path_closest_to(&mut self, place: PlaceRef<'tcx>) -> (PlaceRef<'tcx>, MovePathIndex) {
match self.move_data.rev_lookup.find(place) {
LookupResult::Parent(Some(mpi)) | LookupResult::Exact(mpi) => {
(self.move_data.move_paths[mpi].place.as_ref(), mpi)
}
LookupResult::Parent(None) => panic!("should have move path for every Local"),
}
}
fn move_path_for_place(&mut self, place: PlaceRef<'tcx>) -> Option<MovePathIndex> {
match self.move_data.rev_lookup.find(place) {
LookupResult::Parent(_) => None,
LookupResult::Exact(mpi) => Some(mpi),
}
}
fn check_if_assigned_path_is_moved(
&mut self,
location: Location,
(place, span): (Place<'tcx>, Span),
flow_state: &Flows<'cx, 'tcx>,
) {
debug!("check_if_assigned_path_is_moved place: {:?}", place);
let mut cursor = &*place.projection.as_ref();
while let [proj_base @ .., elem] = cursor {
cursor = proj_base;
match elem {
ProjectionElem::Index(_) |
ProjectionElem::ConstantIndex { .. } |
ProjectionElem::Downcast(_, _) =>
{ }
ProjectionElem::Deref => {
self.check_if_full_path_is_moved(
location, InitializationRequiringAction::Use,
(PlaceRef {
local: place.local,
projection: proj_base,
}, span), flow_state);
break;
}
ProjectionElem::Subslice { .. } => {
panic!("we don't allow assignments to subslices, location: {:?}",
location);
}
ProjectionElem::Field(..) => {
let tcx = self.infcx.tcx;
let base_ty = Place::ty_from(place.local, proj_base, self.body(), tcx).ty;
match base_ty.kind() {
ty::Adt(def, _) if def.has_dtor(tcx) => {
self.check_if_path_or_subpath_is_moved(
location, InitializationRequiringAction::Assignment,
(PlaceRef {
local: place.local,
projection: proj_base,
}, span), flow_state);
break;
}
ty::Adt(..) | ty::Tuple(..) => {
check_parent_of_field(self, location, PlaceRef {
local: place.local,
projection: proj_base,
}, span, flow_state);
self.used_mut.insert(place.local);
}
_ => {}
}
}
}
}
fn check_parent_of_field<'cx, 'tcx>(
this: &mut MirBorrowckCtxt<'cx, 'tcx>,
location: Location,
base: PlaceRef<'tcx>,
span: Span,
flow_state: &Flows<'cx, 'tcx>,
) {
let maybe_uninits = &flow_state.uninits;
let mut shortest_uninit_seen = None;
for prefix in this.prefixes(base, PrefixSet::Shallow) {
let mpi = match this.move_path_for_place(prefix) {
Some(mpi) => mpi,
None => continue,
};
if maybe_uninits.contains(mpi) {
debug!(
"check_parent_of_field updating shortest_uninit_seen from {:?} to {:?}",
shortest_uninit_seen,
Some((prefix, mpi))
);
shortest_uninit_seen = Some((prefix, mpi));
} else {
debug!("check_parent_of_field {:?} is definitely initialized", (prefix, mpi));
}
}
if let Some((prefix, mpi)) = shortest_uninit_seen {
let tcx = this.infcx.tcx;
if let ty::Adt(def, _) =
Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind()
{
if def.is_union() {
if this.move_data.path_map[mpi].iter().any(|moi| {
this.move_data.moves[*moi].source.is_predecessor_of(location, this.body)
}) {
return;
}
}
}
this.report_use_of_moved_or_uninitialized(
location,
InitializationRequiringAction::PartialAssignment,
(prefix, base, span),
mpi,
);
}
}
}
fn check_access_permissions(
&mut self,
(place, span): (Place<'tcx>, Span),
kind: ReadOrWrite,
is_local_mutation_allowed: LocalMutationIsAllowed,
flow_state: &Flows<'cx, 'tcx>,
location: Location,
) -> bool {
debug!(
"check_access_permissions({:?}, {:?}, is_local_mutation_allowed: {:?})",
place, kind, is_local_mutation_allowed
);
let error_access;
let the_place_err;
match kind {
Reservation(WriteKind::MutableBorrow(
borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }),
))
| Write(WriteKind::MutableBorrow(
borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }),
)) => {
let is_local_mutation_allowed = match borrow_kind {
BorrowKind::Unique => LocalMutationIsAllowed::Yes,
BorrowKind::Mut { .. } => is_local_mutation_allowed,
BorrowKind::Shared | BorrowKind::Shallow => unreachable!(),
};
match self.is_mutable(place.as_ref(), is_local_mutation_allowed) {
Ok(root_place) => {
self.add_used_mut(root_place, flow_state);
return false;
}
Err(place_err) => {
error_access = AccessKind::MutableBorrow;
the_place_err = place_err;
}
}
}
Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => {
match self.is_mutable(place.as_ref(), is_local_mutation_allowed) {
Ok(root_place) => {
self.add_used_mut(root_place, flow_state);
return false;
}
Err(place_err) => {
error_access = AccessKind::Mutate;
the_place_err = place_err;
}
}
}
Reservation(
WriteKind::Move
| WriteKind::StorageDeadOrDrop
| WriteKind::MutableBorrow(BorrowKind::Shared)
| WriteKind::MutableBorrow(BorrowKind::Shallow),
)
| Write(
WriteKind::Move
| WriteKind::StorageDeadOrDrop
| WriteKind::MutableBorrow(BorrowKind::Shared)
| WriteKind::MutableBorrow(BorrowKind::Shallow),
) => {
if let (Err(_), true) = (
self.is_mutable(place.as_ref(), is_local_mutation_allowed),
self.errors_buffer.is_empty(),
) {
self.infcx.tcx.sess.delay_span_bug(
span,
&format!(
"Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
place, kind,
),
);
}
return false;
}
Activation(..) => {
return false;
}
Read(
ReadKind::Borrow(
BorrowKind::Unique
| BorrowKind::Mut { .. }
| BorrowKind::Shared
| BorrowKind::Shallow,
)
| ReadKind::Copy,
) => {
return false;
}
}
let previously_initialized =
self.is_local_ever_initialized(place.local, flow_state).is_some();
if previously_initialized {
self.report_mutability_error(place, span, the_place_err, error_access, location);
true
} else {
false
}
}
fn is_local_ever_initialized(
&self,
local: Local,
flow_state: &Flows<'cx, 'tcx>,
) -> Option<InitIndex> {
let mpi = self.move_data.rev_lookup.find_local(local);
let ii = &self.move_data.init_path_map[mpi];
for &index in ii {
if flow_state.ever_inits.contains(index) {
return Some(index);
}
}
None
}
fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'cx, 'tcx>) {
match root_place {
RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => {
if is_local_mutation_allowed != LocalMutationIsAllowed::Yes
&& self.is_local_ever_initialized(local, flow_state).is_some()
{
self.used_mut.insert(local);
}
}
RootPlace {
place_local: _,
place_projection: _,
is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
} => {}
RootPlace {
place_local,
place_projection: place_projection @ [.., _],
is_local_mutation_allowed: _,
} => {
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
local: place_local,
projection: place_projection,
}) {
self.used_mut_upvars.push(field);
}
}
}
}
fn is_mutable(
&self,
place: PlaceRef<'tcx>,
is_local_mutation_allowed: LocalMutationIsAllowed,
) -> Result<RootPlace<'tcx>, PlaceRef<'tcx>> {
match place {
PlaceRef { local, projection: [] } => {
let local = &self.body.local_decls[local];
match local.mutability {
Mutability::Not => match is_local_mutation_allowed {
LocalMutationIsAllowed::Yes => Ok(RootPlace {
place_local: place.local,
place_projection: place.projection,
is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
}),
LocalMutationIsAllowed::ExceptUpvars => Ok(RootPlace {
place_local: place.local,
place_projection: place.projection,
is_local_mutation_allowed: LocalMutationIsAllowed::ExceptUpvars,
}),
LocalMutationIsAllowed::No => Err(place),
},
Mutability::Mut => Ok(RootPlace {
place_local: place.local,
place_projection: place.projection,
is_local_mutation_allowed,
}),
}
}
PlaceRef { local: _, projection: [proj_base @ .., elem] } => {
match elem {
ProjectionElem::Deref => {
let base_ty =
Place::ty_from(place.local, proj_base, self.body(), self.infcx.tcx).ty;
match base_ty.kind() {
ty::Ref(_, _, mutbl) => {
match mutbl {
hir::Mutability::Not => Err(place),
hir::Mutability::Mut => {
let mode = match self.is_upvar_field_projection(place) {
Some(field) if self.upvars[field.index()].by_ref => {
is_local_mutation_allowed
}
_ => LocalMutationIsAllowed::Yes,
};
self.is_mutable(
PlaceRef { local: place.local, projection: proj_base },
mode,
)
}
}
}
ty::RawPtr(tnm) => {
match tnm.mutbl {
hir::Mutability::Not => Err(place),
hir::Mutability::Mut => Ok(RootPlace {
place_local: place.local,
place_projection: place.projection,
is_local_mutation_allowed,
}),
}
}
_ if base_ty.is_box() => self.is_mutable(
PlaceRef { local: place.local, projection: proj_base },
is_local_mutation_allowed,
),
_ => bug!("Deref of unexpected type: {:?}", base_ty),
}
}
ProjectionElem::Field(..)
| ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(..) => {
let upvar_field_projection = self.is_upvar_field_projection(place);
if let Some(field) = upvar_field_projection {
let upvar = &self.upvars[field.index()];
debug!(
"upvar.mutability={:?} local_mutation_is_allowed={:?} \
place={:?}",
upvar, is_local_mutation_allowed, place
);
match (upvar.mutability, is_local_mutation_allowed) {
(
Mutability::Not,
LocalMutationIsAllowed::No
| LocalMutationIsAllowed::ExceptUpvars,
) => Err(place),
(Mutability::Not, LocalMutationIsAllowed::Yes)
| (Mutability::Mut, _) => {
let _ = self.is_mutable(
PlaceRef { local: place.local, projection: proj_base },
is_local_mutation_allowed,
)?;
Ok(RootPlace {
place_local: place.local,
place_projection: place.projection,
is_local_mutation_allowed,
})
}
}
} else {
self.is_mutable(
PlaceRef { local: place.local, projection: proj_base },
is_local_mutation_allowed,
)
}
}
}
}
}
}
pub fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option<Field> {
path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body())
}
}
enum Overlap {
Arbitrary,
EqualOrDisjoint,
Disjoint,
}