let location_table = &LocationTable::new(mir);
let mut errors_buffer = Vec::new();
- let (move_data, move_errors): (MoveData<'tcx>, Option<Vec<MoveError<'tcx>>>) =
+ let (move_data, move_errors): (MoveData<'tcx>, Option<Vec<(Place<'tcx>, MoveError<'tcx>)>>) =
match MoveData::gather_moves(mir, tcx) {
Ok(move_data) => (move_data, None),
Err((move_data, move_errors)) => (move_data, Some(move_errors)),
}
let span = local_decl.source_info.span;
- let mut_span = tcx.sess.codemap().span_until_non_whitespace(span);
+ let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
let mut err = tcx.struct_span_lint_node(
UNUSED_MUT,
No,
}
-struct AccessErrorsReported {
- mutability_error: bool,
- #[allow(dead_code)]
- conflict_error: bool,
-}
-
#[derive(Copy, Clone)]
enum InitializationRequiringAction {
Update,
// individual fields instead. This way if `foo` has a
// destructor but `bar` does not, we will only check for
// borrows of `x.foo` and not `x.bar`. See #47703.
- ty::TyAdt(def, substs) if def.is_struct() && !def.has_dtor(self.tcx) => {
+ ty::Adt(def, substs) if def.is_struct() && !def.has_dtor(self.tcx) => {
def.all_fields()
.map(|field| field.ty(gcx, substs))
.enumerate()
.for_each(|field| drop_field(self, field));
}
// Same as above, but for tuples.
- ty::TyTuple(tys) => {
+ ty::Tuple(tys) => {
tys.iter()
.cloned()
.enumerate()
}
// Closures also have disjoint fields, but they are only
// directly accessed in the body of the closure.
- ty::TyClosure(def, substs)
+ ty::Closure(def, substs)
if *drop_place == Place::Local(Local::new(1))
&& !self.mir.upvar_decls.is_empty() =>
{
}
// Generators also have disjoint fields, but they are only
// directly accessed in the body of the generator.
- ty::TyGenerator(def, substs, _)
+ ty::Generator(def, substs, _)
if *drop_place == Place::Local(Local::new(1))
&& !self.mir.upvar_decls.is_empty() =>
{
// the base case below, we would have a Deep Write due to
// the box being `needs_drop`, and that Deep Write would
// touch `&mut` data in the box.
- ty::TyAdt(def, _) if def.is_box() => {
+ ty::Adt(def, _) if def.is_box() => {
// When/if we add a `&own T` type, this action would
// be like running the destructor of the `&own T`.
// (And the owner of backing storage referenced by the
kind: (ShallowOrDeep, ReadOrWrite),
is_local_mutation_allowed: LocalMutationIsAllowed,
flow_state: &Flows<'cx, 'gcx, 'tcx>,
- ) -> AccessErrorsReported {
+ ) {
let (sd, rw) = kind;
if let Activation(_, borrow_index) = rw {
place: {:?} borrow_index: {:?}",
place_span.0, borrow_index
);
- return AccessErrorsReported {
- mutability_error: false,
- conflict_error: true,
- };
+ return;
}
}
- if self
+ // Check is_empty() first because it's the common case, and doing that
+ // way we avoid the clone() call.
+ if !self.access_place_error_reported.is_empty() &&
+ self
.access_place_error_reported
.contains(&(place_span.0.clone(), place_span.1))
{
"access_place: suppressing error place_span=`{:?}` kind=`{:?}`",
place_span, kind
);
- return AccessErrorsReported {
- mutability_error: false,
- conflict_error: true,
- };
+ return;
}
let mutability_error =
self.access_place_error_reported
.insert((place_span.0.clone(), place_span.1));
}
-
- AccessErrorsReported {
- mutability_error,
- conflict_error,
- }
}
fn check_access_for_conflict(
}
}
- let errors_reported = self.access_place(
+ // Special case: you can assign a immutable local variable
+ // (e.g., `x = ...`) so long as it has never been initialized
+ // before (at this point in the flow).
+ if let &Place::Local(local) = place_span.0 {
+ if let Mutability::Not = self.mir.local_decls[local].mutability {
+ // check for reassignments to immutable local variables
+ self.check_if_reassignment_to_immutable_state(
+ context,
+ local,
+ place_span,
+ flow_state,
+ );
+ return;
+ }
+ }
+
+ // Otherwise, use the normal access permission rules.
+ self.access_place(
context,
place_span,
(kind, Write(WriteKind::Mutate)),
- // We want immutable upvars to cause an "assignment to immutable var"
- // error, not an "reassignment of immutable var" error, because the
- // latter can't find a good previous assignment span.
- //
- // There's probably a better way to do this.
- LocalMutationIsAllowed::ExceptUpvars,
+ LocalMutationIsAllowed::No,
flow_state,
);
-
- if !errors_reported.mutability_error {
- // check for reassignments to immutable local variables
- self.check_if_reassignment_to_immutable_state(context, place_span, flow_state);
- }
}
fn consume_rvalue(
// FIXME: allow thread-locals to borrow other thread locals?
let (might_be_alive, will_be_dropped) = match root_place {
Place::Promoted(_) => (true, false),
- Place::Static(statik) => {
+ Place::Static(_) => {
// Thread-locals might be dropped after the function exits, but
// "true" statics will never be.
- let is_thread_local = self
- .tcx
- .get_attrs(statik.def_id)
- .iter()
- .any(|attr| attr.check_name("thread_local"));
-
+ let is_thread_local = self.is_place_thread_local(&root_place);
(true, is_thread_local)
}
Place::Local(_) => {
debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
// FIXME: should be talking about the region lifetime instead
// of just a span here.
- let span = self.tcx.sess.codemap().end_point(span);
+ let span = self.tcx.sess.source_map().end_point(span);
self.report_borrowed_value_does_not_live_long_enough(
context,
borrow,
fn check_if_reassignment_to_immutable_state(
&mut self,
context: Context,
- (place, span): (&Place<'tcx>, Span),
+ local: Local,
+ place_span: (&Place<'tcx>, Span),
flow_state: &Flows<'cx, 'gcx, 'tcx>,
) {
- debug!("check_if_reassignment_to_immutable_state({:?})", place);
- // determine if this path has a non-mut owner (and thus needs checking).
- let err_place = match self.is_mutable(place, LocalMutationIsAllowed::No) {
- Ok(..) => return,
- Err(place) => place,
- };
- debug!(
- "check_if_reassignment_to_immutable_state({:?}) - is an imm local",
- place
- );
-
- for i in flow_state.ever_inits.iter_incoming() {
- let init = self.move_data.inits[i];
- let init_place = &self.move_data.move_paths[init.path].place;
- if places_conflict::places_conflict(self.tcx, self.mir, &init_place, place, Deep) {
- self.report_illegal_reassignment(context, (place, span), init.span, err_place);
- break;
- }
+ debug!("check_if_reassignment_to_immutable_state({:?})", local);
+
+ // Check if any of the initializiations of `local` have happened yet:
+ let mpi = self.move_data.rev_lookup.find_local(local);
+ let init_indices = &self.move_data.init_path_map[mpi];
+ let first_init_index = init_indices.iter().find(|ii| flow_state.ever_inits.contains(ii));
+ if let Some(&init_index) = first_init_index {
+ // And, if so, report an error.
+ let init = &self.move_data.inits[init_index];
+ self.report_illegal_reassignment(context, place_span, init.span, place_span.0);
}
}
// be already initialized
let tcx = self.tcx;
match base.ty(self.mir, tcx).to_ty(tcx).sty {
- ty::TyAdt(def, _) if def.has_dtor(tcx) => {
+ ty::Adt(def, _) if def.has_dtor(tcx) => {
// FIXME: analogous code in
// check_loans.rs first maps
// Check the kind of deref to decide
match base_ty.sty {
- ty::TyRef(_, _, mutbl) => {
+ ty::Ref(_, _, mutbl) => {
match mutbl {
// Shared borrowed data is never mutable
hir::MutImmutable => Err(place),
}
}
}
- ty::TyRawPtr(tnm) => {
+ ty::RawPtr(tnm) => {
match tnm.mutbl {
// `*const` raw pointers are not mutable
hir::MutImmutable => return Err(place),