--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use syntax_pos::Span;
+use rustc::mir::{BorrowKind, Field, Local, Location, Operand};
+use rustc::mir::{Place, ProjectionElem, Rvalue, StatementKind};
+use rustc::ty;
+use rustc_data_structures::indexed_vec::Idx;
+
+use super::{MirBorrowckCtxt, Context};
+use super::{InitializationRequiringAction, PrefixSet};
+use super::flow::FlowInProgress;
+use dataflow::{BorrowData, MovingOutStatements};
+use dataflow::move_paths::MovePathIndex;
+use util::borrowck_errors::{BorrowckErrors, Origin};
+
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+ pub(super) fn report_use_of_moved_or_uninitialized(
+ &mut self,
+ _context: Context,
+ desired_action: InitializationRequiringAction,
+ (place, span): (&Place<'tcx>, Span),
+ mpi: MovePathIndex,
+ curr_move_out: &FlowInProgress<MovingOutStatements<'_, 'gcx, 'tcx>>,
+ ) {
+ let mois = self.move_data.path_map[mpi]
+ .iter()
+ .filter(|moi| curr_move_out.contains(moi))
+ .collect::<Vec<_>>();
+
+ if mois.is_empty() {
+ let item_msg = match self.describe_place(place) {
+ Some(name) => format!("`{}`", name),
+ None => "value".to_owned(),
+ };
+ self.tcx
+ .cannot_act_on_uninitialized_variable(
+ span,
+ desired_action.as_noun(),
+ &self.describe_place(place).unwrap_or("_".to_owned()),
+ Origin::Mir,
+ )
+ .span_label(span, format!("use of possibly uninitialized {}", item_msg))
+ .emit();
+ } else {
+ let msg = ""; //FIXME: add "partially " or "collaterally "
+
+ let mut err = self.tcx.cannot_act_on_moved_value(
+ span,
+ desired_action.as_noun(),
+ msg,
+ &self.describe_place(place).unwrap_or("_".to_owned()),
+ Origin::Mir,
+ );
+
+ err.span_label(
+ span,
+ format!(
+ "value {} here after move",
+ desired_action.as_verb_in_past_tense()
+ ),
+ );
+ for moi in mois {
+ let move_msg = ""; //FIXME: add " (into closure)"
+ let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span;
+ if span == move_span {
+ err.span_label(
+ span,
+ format!("value moved{} here in previous iteration of loop", move_msg),
+ );
+ } else {
+ err.span_label(move_span, format!("value moved{} here", move_msg));
+ };
+ }
+ //FIXME: add note for closure
+ err.emit();
+ }
+ }
+
+ pub(super) fn report_move_out_while_borrowed(
+ &mut self,
+ _context: Context,
+ (place, span): (&Place<'tcx>, Span),
+ borrow: &BorrowData<'tcx>,
+ ) {
+ let value_msg = match self.describe_place(place) {
+ Some(name) => format!("`{}`", name),
+ None => "value".to_owned(),
+ };
+ let borrow_msg = match self.describe_place(&borrow.place) {
+ Some(name) => format!("`{}`", name),
+ None => "value".to_owned(),
+ };
+ self.tcx
+ .cannot_move_when_borrowed(
+ span,
+ &self.describe_place(place).unwrap_or("_".to_owned()),
+ Origin::Mir,
+ )
+ .span_label(
+ self.retrieve_borrow_span(borrow),
+ format!("borrow of {} occurs here", borrow_msg),
+ )
+ .span_label(span, format!("move out of {} occurs here", value_msg))
+ .emit();
+ }
+
+ pub(super) fn report_use_while_mutably_borrowed(
+ &mut self,
+ _context: Context,
+ (place, span): (&Place<'tcx>, Span),
+ borrow: &BorrowData<'tcx>,
+ ) {
+ let mut err = self.tcx.cannot_use_when_mutably_borrowed(
+ span,
+ &self.describe_place(place).unwrap_or("_".to_owned()),
+ self.retrieve_borrow_span(borrow),
+ &self.describe_place(&borrow.place).unwrap_or("_".to_owned()),
+ Origin::Mir,
+ );
+
+ err.emit();
+ }
+
+ /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
+ /// the local assigned at `location`.
+ /// This is done by searching in statements succeeding `location`
+ /// and originating from `maybe_closure_span`.
+ fn find_closure_span(
+ &self,
+ maybe_closure_span: Span,
+ location: Location,
+ ) -> Option<(Span, Span)> {
+ use rustc::hir::ExprClosure;
+ use rustc::mir::AggregateKind;
+
+ let local = if let StatementKind::Assign(Place::Local(local), _) =
+ self.mir[location.block].statements[location.statement_index].kind
+ {
+ local
+ } else {
+ return None;
+ };
+
+ for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
+ if maybe_closure_span != stmt.source_info.span {
+ break;
+ }
+
+ if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
+ if let AggregateKind::Closure(def_id, _) = **kind {
+ debug!("find_closure_span: found closure {:?}", places);
+
+ return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
+ let args_span = if let ExprClosure(_, _, _, span, _) =
+ self.tcx.hir.expect_expr(node_id).node
+ {
+ span
+ } else {
+ return None;
+ };
+
+ self.tcx
+ .with_freevars(node_id, |freevars| {
+ for (v, place) in freevars.iter().zip(places) {
+ match *place {
+ Operand::Copy(Place::Local(l)) |
+ Operand::Move(Place::Local(l)) if local == l =>
+ {
+ debug!(
+ "find_closure_span: found captured local {:?}",
+ l
+ );
+ return Some(v.span);
+ }
+ _ => {}
+ }
+ }
+ None
+ })
+ .map(|var_span| (args_span, var_span))
+ } else {
+ None
+ };
+ }
+ }
+ }
+
+ None
+ }
+
+ pub(super) fn report_conflicting_borrow(
+ &mut self,
+ context: Context,
+ (place, span): (&Place<'tcx>, Span),
+ gen_borrow_kind: BorrowKind,
+ issued_borrow: &BorrowData,
+ end_issued_loan_span: Option<Span>,
+ ) {
+ let issued_span = self.retrieve_borrow_span(issued_borrow);
+
+ let new_closure_span = self.find_closure_span(span, context.loc);
+ let span = new_closure_span.map(|(args, _)| args).unwrap_or(span);
+ let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location);
+ let issued_span = old_closure_span
+ .map(|(args, _)| args)
+ .unwrap_or(issued_span);
+
+ let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
+
+ // FIXME: supply non-"" `opt_via` when appropriate
+ let mut err = match (
+ gen_borrow_kind,
+ "immutable",
+ "mutable",
+ issued_borrow.kind,
+ "immutable",
+ "mutable",
+ ) {
+ (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) |
+ (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => self.tcx
+ .cannot_reborrow_already_borrowed(
+ span,
+ &desc_place,
+ "",
+ lft,
+ issued_span,
+ "it",
+ rgt,
+ "",
+ end_issued_loan_span,
+ Origin::Mir,
+ ),
+
+ (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => self.tcx
+ .cannot_mutably_borrow_multiply(
+ span,
+ &desc_place,
+ "",
+ issued_span,
+ "",
+ end_issued_loan_span,
+ Origin::Mir,
+ ),
+
+ (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => self.tcx
+ .cannot_uniquely_borrow_by_two_closures(
+ span,
+ &desc_place,
+ issued_span,
+ end_issued_loan_span,
+ Origin::Mir,
+ ),
+
+ (BorrowKind::Unique, _, _, _, _, _) => self.tcx.cannot_uniquely_borrow_by_one_closure(
+ span,
+ &desc_place,
+ "",
+ issued_span,
+ "it",
+ "",
+ end_issued_loan_span,
+ Origin::Mir,
+ ),
+
+ (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => self.tcx
+ .cannot_reborrow_already_uniquely_borrowed(
+ span,
+ &desc_place,
+ "",
+ lft,
+ issued_span,
+ "",
+ end_issued_loan_span,
+ Origin::Mir,
+ ),
+
+ (BorrowKind::Mut, _, lft, BorrowKind::Unique, _, _) => self.tcx
+ .cannot_reborrow_already_uniquely_borrowed(
+ span,
+ &desc_place,
+ "",
+ lft,
+ issued_span,
+ "",
+ end_issued_loan_span,
+ Origin::Mir,
+ ),
+
+ (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
+ };
+
+ if let Some((_, var_span)) = old_closure_span {
+ err.span_label(
+ var_span,
+ format!(
+ "previous borrow occurs due to use of `{}` in closure",
+ desc_place
+ ),
+ );
+ }
+
+ if let Some((_, var_span)) = new_closure_span {
+ err.span_label(
+ var_span,
+ format!("borrow occurs due to use of `{}` in closure", desc_place),
+ );
+ }
+
+ err.emit();
+ }
+
+ pub(super) fn report_borrowed_value_does_not_live_long_enough(
+ &mut self,
+ _: Context,
+ (place, span): (&Place<'tcx>, Span),
+ end_span: Option<Span>,
+ ) {
+ let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
+ let proper_span = match *root_place {
+ Place::Local(local) => self.mir.local_decls[local].source_info.span,
+ _ => span,
+ };
+ let mut err = self.tcx
+ .path_does_not_live_long_enough(span, "borrowed value", Origin::Mir);
+ err.span_label(proper_span, "temporary value created here");
+ err.span_label(span, "temporary value dropped here while still borrowed");
+ err.note("consider using a `let` binding to increase its lifetime");
+
+ if let Some(end) = end_span {
+ err.span_label(end, "temporary value needs to live until here");
+ }
+
+ err.emit();
+ }
+
+ pub(super) fn report_illegal_mutation_of_borrowed(
+ &mut self,
+ _: Context,
+ (place, span): (&Place<'tcx>, Span),
+ loan: &BorrowData,
+ ) {
+ let mut err = self.tcx.cannot_assign_to_borrowed(
+ span,
+ self.retrieve_borrow_span(loan),
+ &self.describe_place(place).unwrap_or("_".to_owned()),
+ Origin::Mir,
+ );
+
+ err.emit();
+ }
+
+ pub(super) fn report_illegal_reassignment(
+ &mut self,
+ _context: Context,
+ (place, span): (&Place<'tcx>, Span),
+ assigned_span: Span,
+ ) {
+ let mut err = self.tcx.cannot_reassign_immutable(
+ span,
+ &self.describe_place(place).unwrap_or("_".to_owned()),
+ Origin::Mir,
+ );
+ err.span_label(span, "cannot assign twice to immutable variable");
+ if span != assigned_span {
+ let value_msg = match self.describe_place(place) {
+ Some(name) => format!("`{}`", name),
+ None => "value".to_owned(),
+ };
+ err.span_label(assigned_span, format!("first assignment to {}", value_msg));
+ }
+ err.emit();
+ }
+}
+
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+ // End-user visible description of `place` if one can be found. If the
+ // place is a temporary for instance, None will be returned.
+ pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
+ let mut buf = String::new();
+ match self.append_place_to_string(place, &mut buf, false) {
+ Ok(()) => Some(buf),
+ Err(()) => None,
+ }
+ }
+
+ // Appends end-user visible description of `place` to `buf`.
+ fn append_place_to_string(
+ &self,
+ place: &Place<'tcx>,
+ buf: &mut String,
+ mut autoderef: bool,
+ ) -> Result<(), ()> {
+ match *place {
+ Place::Local(local) => {
+ self.append_local_to_string(local, buf)?;
+ }
+ Place::Static(ref static_) => {
+ buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
+ }
+ Place::Projection(ref proj) => {
+ match proj.elem {
+ ProjectionElem::Deref => {
+ if let Some(field) = self.is_upvar_field_projection(&proj.base) {
+ let var_index = field.index();
+ let name = self.mir.upvar_decls[var_index].debug_name.to_string();
+ if self.mir.upvar_decls[var_index].by_ref {
+ buf.push_str(&name);
+ } else {
+ buf.push_str(&format!("*{}", &name));
+ }
+ } else {
+ if autoderef {
+ self.append_place_to_string(&proj.base, buf, autoderef)?;
+ } else {
+ buf.push_str(&"*");
+ self.append_place_to_string(&proj.base, buf, autoderef)?;
+ }
+ }
+ }
+ ProjectionElem::Downcast(..) => {
+ self.append_place_to_string(&proj.base, buf, autoderef)?;
+ }
+ ProjectionElem::Field(field, _ty) => {
+ autoderef = true;
+
+ if let Some(field) = self.is_upvar_field_projection(place) {
+ let var_index = field.index();
+ let name = self.mir.upvar_decls[var_index].debug_name.to_string();
+ buf.push_str(&name);
+ } else {
+ let field_name = self.describe_field(&proj.base, field);
+ self.append_place_to_string(&proj.base, buf, autoderef)?;
+ buf.push_str(&format!(".{}", field_name));
+ }
+ }
+ ProjectionElem::Index(index) => {
+ autoderef = true;
+
+ self.append_place_to_string(&proj.base, buf, autoderef)?;
+ buf.push_str("[");
+ if let Err(_) = self.append_local_to_string(index, buf) {
+ buf.push_str("..");
+ }
+ buf.push_str("]");
+ }
+ ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
+ autoderef = true;
+ // Since it isn't possible to borrow an element on a particular index and
+ // then use another while the borrow is held, don't output indices details
+ // to avoid confusing the end-user
+ self.append_place_to_string(&proj.base, buf, autoderef)?;
+ buf.push_str(&"[..]");
+ }
+ };
+ }
+ }
+
+ Ok(())
+ }
+
+ // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
+ // a name, then `Err` is returned
+ fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
+ let local = &self.mir.local_decls[local_index];
+ match local.name {
+ Some(name) => {
+ buf.push_str(&format!("{}", name));
+ Ok(())
+ }
+ None => Err(()),
+ }
+ }
+
+ // End-user visible description of the `field`nth field of `base`
+ fn describe_field(&self, base: &Place, field: Field) -> String {
+ match *base {
+ Place::Local(local) => {
+ let local = &self.mir.local_decls[local];
+ self.describe_field_from_ty(&local.ty, field)
+ }
+ Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
+ Place::Projection(ref proj) => match proj.elem {
+ ProjectionElem::Deref => self.describe_field(&proj.base, field),
+ ProjectionElem::Downcast(def, variant_index) => {
+ format!("{}", def.variants[variant_index].fields[field.index()].name)
+ }
+ ProjectionElem::Field(_, field_type) => {
+ self.describe_field_from_ty(&field_type, field)
+ }
+ ProjectionElem::Index(..) |
+ ProjectionElem::ConstantIndex { .. } |
+ ProjectionElem::Subslice { .. } => {
+ format!("{}", self.describe_field(&proj.base, field))
+ }
+ },
+ }
+ }
+
+ // End-user visible description of the `field_index`nth field of `ty`
+ fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
+ if ty.is_box() {
+ // If the type is a box, the field is described from the boxed type
+ self.describe_field_from_ty(&ty.boxed_ty(), field)
+ } else {
+ match ty.sty {
+ ty::TyAdt(def, _) => if def.is_enum() {
+ format!("{}", field.index())
+ } else {
+ format!("{}", def.struct_variant().fields[field.index()].name)
+ },
+ ty::TyTuple(_, _) => format!("{}", field.index()),
+ ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => {
+ self.describe_field_from_ty(&tnm.ty, field)
+ }
+ ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field),
+ ty::TyClosure(closure_def_id, _) => {
+ // Convert the def-id into a node-id. node-ids are only valid for
+ // the local code in the current crate, so this returns an `Option` in case
+ // the closure comes from another crate. But in that case we wouldn't
+ // be borrowck'ing it, so we can just unwrap:
+ let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
+ let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
+
+ self.tcx.hir.name(freevar.var_id()).to_string()
+ }
+ _ => {
+ // Might need a revision when the fields in trait RFC is implemented
+ // (https://github.com/rust-lang/rfcs/pull/1546)
+ bug!(
+ "End-user description not implemented for field access on `{:?}`",
+ ty.sty
+ );
+ }
+ }
+ }
+ }
+
+ // Retrieve span of given borrow from the current MIR representation
+ fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
+ self.mir.source_info(borrow.location).span
+ }
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Manages the dataflow bits required for borrowck.
+//!
+//! FIXME: this might be better as a "generic" fixed-point combinator,
+//! but is not as ugly as it is right now.
+
+use rustc::mir::{BasicBlock, Location};
+use rustc_data_structures::indexed_set::{self, IdxSetBuf};
+use rustc_data_structures::indexed_vec::Idx;
+
+use dataflow::{BitDenotation, BlockSets, DataflowResults};
+use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
+use dataflow::{EverInitializedLvals, MovingOutStatements};
+use dataflow::Borrows;
+use dataflow::move_paths::{HasMoveData, MovePathIndex};
+use std::fmt;
+
+// (forced to be `pub` due to its use as an associated type below.)
+pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> {
+ pub borrows: FlowInProgress<Borrows<'b, 'gcx, 'tcx>>,
+ pub inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
+ pub uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
+ pub move_outs: FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>,
+ pub ever_inits: FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>,
+}
+
+pub struct FlowInProgress<BD>
+where
+ BD: BitDenotation,
+{
+ base_results: DataflowResults<BD>,
+ curr_state: IdxSetBuf<BD::Idx>,
+ stmt_gen: IdxSetBuf<BD::Idx>,
+ stmt_kill: IdxSetBuf<BD::Idx>,
+}
+
+impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
+ pub fn new(
+ borrows: FlowInProgress<Borrows<'b, 'gcx, 'tcx>>,
+ inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
+ uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
+ move_outs: FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>,
+ ever_inits: FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>,
+ ) -> Self {
+ InProgress {
+ borrows,
+ inits,
+ uninits,
+ move_outs,
+ ever_inits,
+ }
+ }
+
+ fn each_flow<XB, XI, XU, XM, XE>(
+ &mut self,
+ mut xform_borrows: XB,
+ mut xform_inits: XI,
+ mut xform_uninits: XU,
+ mut xform_move_outs: XM,
+ mut xform_ever_inits: XE,
+ ) where
+ XB: FnMut(&mut FlowInProgress<Borrows<'b, 'gcx, 'tcx>>),
+ XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>),
+ XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>),
+ XM: FnMut(&mut FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>),
+ XE: FnMut(&mut FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>),
+ {
+ xform_borrows(&mut self.borrows);
+ xform_inits(&mut self.inits);
+ xform_uninits(&mut self.uninits);
+ xform_move_outs(&mut self.move_outs);
+ xform_ever_inits(&mut self.ever_inits);
+ }
+
+ pub fn reset_to_entry_of(&mut self, bb: BasicBlock) {
+ self.each_flow(
+ |b| b.reset_to_entry_of(bb),
+ |i| i.reset_to_entry_of(bb),
+ |u| u.reset_to_entry_of(bb),
+ |m| m.reset_to_entry_of(bb),
+ |e| e.reset_to_entry_of(bb),
+ );
+ }
+
+ pub fn reconstruct_statement_effect(
+ &mut self,
+ location: Location,
+ ) {
+ self.each_flow(
+ |b| b.reconstruct_statement_effect(location),
+ |i| i.reconstruct_statement_effect(location),
+ |u| u.reconstruct_statement_effect(location),
+ |m| m.reconstruct_statement_effect(location),
+ |e| e.reconstruct_statement_effect(location),
+ );
+ }
+
+ pub fn apply_local_effect(&mut self, _location: Location) {
+ self.each_flow(
+ |b| b.apply_local_effect(),
+ |i| i.apply_local_effect(),
+ |u| u.apply_local_effect(),
+ |m| m.apply_local_effect(),
+ |e| e.apply_local_effect(),
+ );
+ }
+
+ pub fn reconstruct_terminator_effect(&mut self, location: Location) {
+ self.each_flow(
+ |b| b.reconstruct_terminator_effect(location),
+ |i| i.reconstruct_terminator_effect(location),
+ |u| u.reconstruct_terminator_effect(location),
+ |m| m.reconstruct_terminator_effect(location),
+ |e| e.reconstruct_terminator_effect(location),
+ );
+ }
+}
+
+impl<'b, 'gcx, 'tcx> fmt::Display for InProgress<'b, 'gcx, 'tcx> {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let mut s = String::new();
+
+ s.push_str("borrows in effect: [");
+ let mut saw_one = false;
+ self.borrows.each_state_bit(|borrow| {
+ if saw_one {
+ s.push_str(", ");
+ };
+ saw_one = true;
+ let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
+ s.push_str(&format!("{}", borrow_data));
+ });
+ s.push_str("] ");
+
+ s.push_str("borrows generated: [");
+ let mut saw_one = false;
+ self.borrows.each_gen_bit(|borrow| {
+ if saw_one {
+ s.push_str(", ");
+ };
+ saw_one = true;
+ let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
+ s.push_str(&format!("{}", borrow_data));
+ });
+ s.push_str("] ");
+
+ s.push_str("inits: [");
+ let mut saw_one = false;
+ self.inits.each_state_bit(|mpi_init| {
+ if saw_one {
+ s.push_str(", ");
+ };
+ saw_one = true;
+ let move_path = &self.inits.base_results.operator().move_data().move_paths[mpi_init];
+ s.push_str(&format!("{}", move_path));
+ });
+ s.push_str("] ");
+
+ s.push_str("uninits: [");
+ let mut saw_one = false;
+ self.uninits.each_state_bit(|mpi_uninit| {
+ if saw_one {
+ s.push_str(", ");
+ };
+ saw_one = true;
+ let move_path =
+ &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit];
+ s.push_str(&format!("{}", move_path));
+ });
+ s.push_str("] ");
+
+ s.push_str("move_out: [");
+ let mut saw_one = false;
+ self.move_outs.each_state_bit(|mpi_move_out| {
+ if saw_one {
+ s.push_str(", ");
+ };
+ saw_one = true;
+ let move_out = &self.move_outs.base_results.operator().move_data().moves[mpi_move_out];
+ s.push_str(&format!("{:?}", move_out));
+ });
+ s.push_str("] ");
+
+ s.push_str("ever_init: [");
+ let mut saw_one = false;
+ self.ever_inits.each_state_bit(|mpi_ever_init| {
+ if saw_one {
+ s.push_str(", ");
+ };
+ saw_one = true;
+ let ever_init =
+ &self.ever_inits.base_results.operator().move_data().inits[mpi_ever_init];
+ s.push_str(&format!("{:?}", ever_init));
+ });
+ s.push_str("]");
+
+ fmt::Display::fmt(&s, fmt)
+ }
+}
+
+impl<'tcx, T> FlowInProgress<T>
+where
+ T: HasMoveData<'tcx> + BitDenotation<Idx = MovePathIndex>,
+{
+ pub fn has_any_child_of(&self, mpi: T::Idx) -> Option<T::Idx> {
+ let move_data = self.base_results.operator().move_data();
+
+ let mut todo = vec![mpi];
+ let mut push_siblings = false; // don't look at siblings of original `mpi`.
+ while let Some(mpi) = todo.pop() {
+ if self.curr_state.contains(&mpi) {
+ return Some(mpi);
+ }
+ let move_path = &move_data.move_paths[mpi];
+ if let Some(child) = move_path.first_child {
+ todo.push(child);
+ }
+ if push_siblings {
+ if let Some(sibling) = move_path.next_sibling {
+ todo.push(sibling);
+ }
+ } else {
+ // after we've processed the original `mpi`, we should
+ // always traverse the siblings of any of its
+ // children.
+ push_siblings = true;
+ }
+ }
+ return None;
+ }
+}
+
+impl<BD> FlowInProgress<BD>
+where
+ BD: BitDenotation,
+{
+ pub fn each_state_bit<F>(&self, f: F)
+ where
+ F: FnMut(BD::Idx),
+ {
+ self.curr_state
+ .each_bit(self.base_results.operator().bits_per_block(), f)
+ }
+
+ fn each_gen_bit<F>(&self, f: F)
+ where
+ F: FnMut(BD::Idx),
+ {
+ self.stmt_gen
+ .each_bit(self.base_results.operator().bits_per_block(), f)
+ }
+
+ pub fn new(results: DataflowResults<BD>) -> Self {
+ let bits_per_block = results.sets().bits_per_block();
+ let curr_state = IdxSetBuf::new_empty(bits_per_block);
+ let stmt_gen = IdxSetBuf::new_empty(bits_per_block);
+ let stmt_kill = IdxSetBuf::new_empty(bits_per_block);
+ FlowInProgress {
+ base_results: results,
+ curr_state: curr_state,
+ stmt_gen: stmt_gen,
+ stmt_kill: stmt_kill,
+ }
+ }
+
+ pub fn operator(&self) -> &BD {
+ self.base_results.operator()
+ }
+
+ pub fn contains(&self, x: &BD::Idx) -> bool {
+ self.curr_state.contains(x)
+ }
+
+ pub fn reset_to_entry_of(&mut self, bb: BasicBlock) {
+ (*self.curr_state).clone_from(self.base_results.sets().on_entry_set_for(bb.index()));
+ }
+
+ pub fn reconstruct_statement_effect(&mut self, loc: Location) {
+ self.stmt_gen.reset_to_empty();
+ self.stmt_kill.reset_to_empty();
+ let mut ignored = IdxSetBuf::new_empty(0);
+ let mut sets = BlockSets {
+ on_entry: &mut ignored,
+ gen_set: &mut self.stmt_gen,
+ kill_set: &mut self.stmt_kill,
+ };
+ self.base_results
+ .operator()
+ .statement_effect(&mut sets, loc);
+ }
+
+ pub fn reconstruct_terminator_effect(&mut self, loc: Location) {
+ self.stmt_gen.reset_to_empty();
+ self.stmt_kill.reset_to_empty();
+ let mut ignored = IdxSetBuf::new_empty(0);
+ let mut sets = BlockSets {
+ on_entry: &mut ignored,
+ gen_set: &mut self.stmt_gen,
+ kill_set: &mut self.stmt_kill,
+ };
+ self.base_results
+ .operator()
+ .terminator_effect(&mut sets, loc);
+ }
+
+ pub fn apply_local_effect(&mut self) {
+ self.curr_state.union(&self.stmt_gen);
+ self.curr_state.subtract(&self.stmt_kill);
+ }
+
+ pub fn elems_incoming(&self) -> indexed_set::Elems<BD::Idx> {
+ let univ = self.base_results.sets().bits_per_block();
+ self.curr_state.elems(univ)
+ }
+
+ pub fn with_elems_outgoing<F>(&self, f: F)
+ where
+ F: FnOnce(indexed_set::Elems<BD::Idx>),
+ {
+ let mut curr_state = self.curr_state.clone();
+ curr_state.union(&self.stmt_gen);
+ curr_state.subtract(&self.stmt_kill);
+ let univ = self.base_results.sets().bits_per_block();
+ f(curr_state.elems(univ));
+ }
+}
use rustc::mir::ClosureRegionRequirements;
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::indexed_set::{self, IdxSetBuf};
+use rustc_data_structures::indexed_set::{IdxSetBuf};
use rustc_data_structures::indexed_vec::Idx;
use syntax::ast;
use dataflow::do_dataflow;
use dataflow::MoveDataParamEnv;
-use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer};
+use dataflow::DataflowResultsConsumer;
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
use dataflow::{EverInitializedLvals, MovingOutStatements};
use dataflow::{BorrowData, BorrowIndex, Borrows};
use dataflow::move_paths::{IllegalMoveOriginKind, MoveError};
-use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveOutIndex, MovePathIndex};
+use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex};
use util::borrowck_errors::{BorrowckErrors, Origin};
-use std::fmt;
use std::iter;
+use self::flow::{InProgress, FlowInProgress};
+use self::prefixes::PrefixSet;
use self::MutateMode::{JustWrite, WriteAndRead};
+mod error_reporting;
+mod flow;
+mod prefixes;
pub(crate) mod nll;
pub fn provide(providers: &mut Providers) {
};
let flow_inits = flow_inits; // remove mut
- let mut mbcx = MirBorrowckCtxt {
- tcx: tcx,
- mir: mir,
- node_id: id,
- move_data: &mdpe.move_data,
- param_env: param_env,
- locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) {
- hir::BodyOwnerKind::Const |
- hir::BodyOwnerKind::Static(_) => false,
- hir::BodyOwnerKind::Fn => true,
- },
- storage_dead_or_drop_error_reported: FxHashSet(),
- };
-
let flow_borrows = FlowInProgress::new(do_dataflow(
tcx,
mir,
flow_ever_inits,
);
+ let mut mbcx = MirBorrowckCtxt {
+ tcx: tcx,
+ mir: mir,
+ node_id: id,
+ move_data: &mdpe.move_data,
+ param_env: param_env,
+ locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) {
+ hir::BodyOwnerKind::Const |
+ hir::BodyOwnerKind::Static(_) => false,
+ hir::BodyOwnerKind::Fn => true,
+ },
+ storage_dead_or_drop_error_reported: FxHashSet(),
+ };
+
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
opt_closure_req
storage_dead_or_drop_error_reported: FxHashSet<Local>,
}
-// (forced to be `pub` due to its use as an associated type below.)
-pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> {
- borrows: FlowInProgress<Borrows<'b, 'gcx, 'tcx>>,
- inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
- uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
- move_outs: FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>,
- ever_inits: FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>,
-}
-
-struct FlowInProgress<BD>
-where
- BD: BitDenotation,
-{
- base_results: DataflowResults<BD>,
- curr_state: IdxSetBuf<BD::Idx>,
- stmt_gen: IdxSetBuf<BD::Idx>,
- stmt_kill: IdxSetBuf<BD::Idx>,
-}
-
// Check that:
// 1. assignments are always made to mutable locations (FIXME: does that still really go here?)
// 2. loans made in overlapping scopes do not conflict
}
fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
- flow_state.each_flow(
- |b| b.reset_to_entry_of(bb),
- |i| i.reset_to_entry_of(bb),
- |u| u.reset_to_entry_of(bb),
- |m| m.reset_to_entry_of(bb),
- |e| e.reset_to_entry_of(bb),
- );
+ flow_state.reset_to_entry_of(bb);
}
fn reconstruct_statement_effect(
location: Location,
flow_state: &mut Self::FlowState,
) {
- flow_state.each_flow(
- |b| b.reconstruct_statement_effect(location),
- |i| i.reconstruct_statement_effect(location),
- |u| u.reconstruct_statement_effect(location),
- |m| m.reconstruct_statement_effect(location),
- |e| e.reconstruct_statement_effect(location),
- );
+ flow_state.reconstruct_statement_effect(location);
}
- fn apply_local_effect(&mut self, _location: Location, flow_state: &mut Self::FlowState) {
- flow_state.each_flow(
- |b| b.apply_local_effect(),
- |i| i.apply_local_effect(),
- |u| u.apply_local_effect(),
- |m| m.apply_local_effect(),
- |e| e.apply_local_effect(),
- );
+ fn apply_local_effect(&mut self, location: Location, flow_state: &mut Self::FlowState) {
+ flow_state.apply_local_effect(location);
}
fn reconstruct_terminator_effect(
location: Location,
flow_state: &mut Self::FlowState,
) {
- flow_state.each_flow(
- |b| b.reconstruct_terminator_effect(location),
- |i| i.reconstruct_terminator_effect(location),
- |u| u.reconstruct_terminator_effect(location),
- |m| m.reconstruct_terminator_effect(location),
- |e| e.reconstruct_terminator_effect(location),
- );
+ flow_state.reconstruct_terminator_effect(location);
}
fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) {
// Often, the storage will already have been killed by an explicit
// StorageDead, but we don't always emit those (notably on unwind paths),
// so this "extra check" serves as a kind of backup.
- let domain = flow_state.borrows.base_results.operator();
+ let domain = flow_state.borrows.operator();
let data = domain.borrows();
flow_state.borrows.with_elems_outgoing(|borrows| {
for i in borrows {
ReadKind::Borrow(bk) => {
let end_issued_loan_span = flow_state
.borrows
- .base_results
.operator()
.opt_region_end_span(&borrow.region);
error_reported = true;
WriteKind::MutableBorrow(bk) => {
let end_issued_loan_span = flow_state
.borrows
- .base_results
.operator()
.opt_region_end_span(&borrow.region);
error_reported = true;
WriteKind::StorageDeadOrDrop => {
let end_span = flow_state
.borrows
- .base_results
.operator()
.opt_region_end_span(&borrow.region);
error_reported = true;
match self.move_path_closest_to(place) {
Ok(mpi) => for ii in &move_data.init_path_map[mpi] {
- if flow_state.ever_inits.curr_state.contains(ii) {
+ if flow_state.ever_inits.contains(ii) {
let first_assign_span = self.move_data.inits[*ii].span;
self.report_illegal_reassignment(context, (place, span), first_assign_span);
break;
let place = self.base_path(place_span.0);
let maybe_uninits = &flow_state.uninits;
- let curr_move_outs = &flow_state.move_outs.curr_state;
+ let curr_move_outs = &flow_state.move_outs;
// Bad scenarios:
//
debug!("check_if_path_is_moved part1 place: {:?}", place);
match self.move_path_closest_to(place) {
Ok(mpi) => {
- if maybe_uninits.curr_state.contains(&mpi) {
+ if maybe_uninits.contains(&mpi) {
self.report_use_of_moved_or_uninitialized(
context,
desired_action,
}
}
}
+
+
+ /// If this is a field projection, and the field is being projected from a closure type,
+ /// then returns the index of the field being projected. Note that this closure will always
+ /// be `self` in the current MIR, because that is the only time we directly access the fields
+ /// of a closure type.
+ fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> {
+ match *place {
+ Place::Projection(ref proj) => match proj.elem {
+ ProjectionElem::Field(field, _ty) => {
+ let is_projection_from_ty_closure = proj.base
+ .ty(self.mir, self.tcx)
+ .to_ty(self.tcx)
+ .is_closure();
+
+ if is_projection_from_ty_closure {
+ Some(field)
+ } else {
+ None
+ }
+ }
+ _ => None,
+ },
+ _ => None,
+ }
+ }
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
// FIXME: analogous code in check_loans first maps `place` to
// its base_path.
- let domain = flow_state.borrows.base_results.operator();
- let data = domain.borrows();
+ let data = flow_state.borrows.operator().borrows();
// check for loan restricting path P being used. Accounts for
// borrows of P, P.a.b, etc.
}
}
-use self::prefixes::PrefixSet;
-
-/// From the NLL RFC: "The deep [aka 'supporting'] prefixes for an
-/// place are formed by stripping away fields and derefs, except that
-/// we stop when we reach the deref of a shared reference. [...] "
-///
-/// "Shallow prefixes are found by stripping away fields, but stop at
-/// any dereference. So: writing a path like `a` is illegal if `a.b`
-/// is borrowed. But: writing `a` is legal if `*a` is borrowed,
-/// whether or not `a` is a shared or mutable reference. [...] "
-mod prefixes {
- use super::MirBorrowckCtxt;
-
- use rustc::hir;
- use rustc::ty::{self, TyCtxt};
- use rustc::mir::{Mir, Place, ProjectionElem};
-
- pub trait IsPrefixOf<'tcx> {
- fn is_prefix_of(&self, other: &Place<'tcx>) -> bool;
- }
-
- impl<'tcx> IsPrefixOf<'tcx> for Place<'tcx> {
- fn is_prefix_of(&self, other: &Place<'tcx>) -> bool {
- let mut cursor = other;
- loop {
- if self == cursor {
- return true;
- }
-
- match *cursor {
- Place::Local(_) | Place::Static(_) => return false,
- Place::Projection(ref proj) => {
- cursor = &proj.base;
- }
- }
- }
- }
- }
-
-
- pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
- mir: &'cx Mir<'tcx>,
- tcx: TyCtxt<'cx, 'gcx, 'tcx>,
- kind: PrefixSet,
- next: Option<&'cx Place<'tcx>>,
- }
-
- #[derive(Copy, Clone, PartialEq, Eq, Debug)]
- #[allow(dead_code)]
- pub(super) enum PrefixSet {
- /// Doesn't stop until it returns the base case (a Local or
- /// Static prefix).
- All,
- /// Stops at any dereference.
- Shallow,
- /// Stops at the deref of a shared reference.
- Supporting,
- }
-
- impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
- /// Returns an iterator over the prefixes of `place`
- /// (inclusive) from longest to smallest, potentially
- /// terminating the iteration early based on `kind`.
- pub(super) fn prefixes(
- &self,
- place: &'cx Place<'tcx>,
- kind: PrefixSet,
- ) -> Prefixes<'cx, 'gcx, 'tcx> {
- Prefixes {
- next: Some(place),
- kind,
- mir: self.mir,
- tcx: self.tcx,
- }
- }
- }
-
- impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> {
- type Item = &'cx Place<'tcx>;
- fn next(&mut self) -> Option<Self::Item> {
- let mut cursor = self.next?;
-
- // Post-processing `place`: Enqueue any remaining
- // work. Also, `place` may not be a prefix itself, but
- // may hold one further down (e.g. we never return
- // downcasts here, but may return a base of a downcast).
-
- 'cursor: loop {
- let proj = match *cursor {
- Place::Local(_) | // search yielded this leaf
- Place::Static(_) => {
- self.next = None;
- return Some(cursor);
- }
-
- Place::Projection(ref proj) => proj,
- };
-
- match proj.elem {
- ProjectionElem::Field(_ /*field*/, _ /*ty*/) => {
- // FIXME: add union handling
- self.next = Some(&proj.base);
- return Some(cursor);
- }
- ProjectionElem::Downcast(..) |
- ProjectionElem::Subslice { .. } |
- ProjectionElem::ConstantIndex { .. } |
- ProjectionElem::Index(_) => {
- cursor = &proj.base;
- continue 'cursor;
- }
- ProjectionElem::Deref => {
- // (handled below)
- }
- }
-
- assert_eq!(proj.elem, ProjectionElem::Deref);
-
- match self.kind {
- PrefixSet::Shallow => {
- // shallow prefixes are found by stripping away
- // fields, but stop at *any* dereference.
- // So we can just stop the traversal now.
- self.next = None;
- return Some(cursor);
- }
- PrefixSet::All => {
- // all prefixes: just blindly enqueue the base
- // of the projection
- self.next = Some(&proj.base);
- return Some(cursor);
- }
- PrefixSet::Supporting => {
- // fall through!
- }
- }
-
- assert_eq!(self.kind, PrefixSet::Supporting);
- // supporting prefixes: strip away fields and
- // derefs, except we stop at the deref of a shared
- // reference.
-
- let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
- match ty.sty {
- ty::TyRawPtr(_) |
- ty::TyRef(
- _, /*rgn*/
- ty::TypeAndMut {
- ty: _,
- mutbl: hir::MutImmutable,
- },
- ) => {
- // don't continue traversing over derefs of raw pointers or shared borrows.
- self.next = None;
- return Some(cursor);
- }
-
- ty::TyRef(
- _, /*rgn*/
- ty::TypeAndMut {
- ty: _,
- mutbl: hir::MutMutable,
- },
- ) => {
- self.next = Some(&proj.base);
- return Some(cursor);
- }
-
- ty::TyAdt(..) if ty.is_box() => {
- self.next = Some(&proj.base);
- return Some(cursor);
- }
-
- _ => panic!("unknown type fed to Projection Deref."),
- }
- }
- }
- }
-}
-
-impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
- fn report_use_of_moved_or_uninitialized(
- &mut self,
- _context: Context,
- desired_action: InitializationRequiringAction,
- (place, span): (&Place<'tcx>, Span),
- mpi: MovePathIndex,
- curr_move_out: &IdxSetBuf<MoveOutIndex>,
- ) {
- let mois = self.move_data.path_map[mpi]
- .iter()
- .filter(|moi| curr_move_out.contains(moi))
- .collect::<Vec<_>>();
-
- if mois.is_empty() {
- let item_msg = match self.describe_place(place) {
- Some(name) => format!("`{}`", name),
- None => "value".to_owned(),
- };
- self.tcx
- .cannot_act_on_uninitialized_variable(
- span,
- desired_action.as_noun(),
- &self.describe_place(place).unwrap_or("_".to_owned()),
- Origin::Mir,
- )
- .span_label(span, format!("use of possibly uninitialized {}", item_msg))
- .emit();
- } else {
- let msg = ""; //FIXME: add "partially " or "collaterally "
-
- let mut err = self.tcx.cannot_act_on_moved_value(
- span,
- desired_action.as_noun(),
- msg,
- &self.describe_place(place).unwrap_or("_".to_owned()),
- Origin::Mir,
- );
-
- err.span_label(
- span,
- format!(
- "value {} here after move",
- desired_action.as_verb_in_past_tense()
- ),
- );
- for moi in mois {
- let move_msg = ""; //FIXME: add " (into closure)"
- let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span;
- if span == move_span {
- err.span_label(
- span,
- format!("value moved{} here in previous iteration of loop", move_msg),
- );
- } else {
- err.span_label(move_span, format!("value moved{} here", move_msg));
- };
- }
- //FIXME: add note for closure
- err.emit();
- }
- }
-
- fn report_move_out_while_borrowed(
- &mut self,
- _context: Context,
- (place, span): (&Place<'tcx>, Span),
- borrow: &BorrowData<'tcx>,
- ) {
- let value_msg = match self.describe_place(place) {
- Some(name) => format!("`{}`", name),
- None => "value".to_owned(),
- };
- let borrow_msg = match self.describe_place(&borrow.place) {
- Some(name) => format!("`{}`", name),
- None => "value".to_owned(),
- };
- self.tcx
- .cannot_move_when_borrowed(
- span,
- &self.describe_place(place).unwrap_or("_".to_owned()),
- Origin::Mir,
- )
- .span_label(
- self.retrieve_borrow_span(borrow),
- format!("borrow of {} occurs here", borrow_msg),
- )
- .span_label(span, format!("move out of {} occurs here", value_msg))
- .emit();
- }
-
- fn report_use_while_mutably_borrowed(
- &mut self,
- _context: Context,
- (place, span): (&Place<'tcx>, Span),
- borrow: &BorrowData<'tcx>,
- ) {
- let mut err = self.tcx.cannot_use_when_mutably_borrowed(
- span,
- &self.describe_place(place).unwrap_or("_".to_owned()),
- self.retrieve_borrow_span(borrow),
- &self.describe_place(&borrow.place).unwrap_or("_".to_owned()),
- Origin::Mir,
- );
-
- err.emit();
- }
-
- /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
- /// the local assigned at `location`.
- /// This is done by searching in statements succeeding `location`
- /// and originating from `maybe_closure_span`.
- fn find_closure_span(
- &self,
- maybe_closure_span: Span,
- location: Location,
- ) -> Option<(Span, Span)> {
- use rustc::hir::ExprClosure;
- use rustc::mir::AggregateKind;
-
- let local = if let StatementKind::Assign(Place::Local(local), _) =
- self.mir[location.block].statements[location.statement_index].kind
- {
- local
- } else {
- return None;
- };
-
- for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
- if maybe_closure_span != stmt.source_info.span {
- break;
- }
-
- if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
- if let AggregateKind::Closure(def_id, _) = **kind {
- debug!("find_closure_span: found closure {:?}", places);
-
- return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
- let args_span = if let ExprClosure(_, _, _, span, _) =
- self.tcx.hir.expect_expr(node_id).node
- {
- span
- } else {
- return None;
- };
-
- self.tcx
- .with_freevars(node_id, |freevars| {
- for (v, place) in freevars.iter().zip(places) {
- match *place {
- Operand::Copy(Place::Local(l)) |
- Operand::Move(Place::Local(l)) if local == l =>
- {
- debug!(
- "find_closure_span: found captured local {:?}",
- l
- );
- return Some(v.span);
- }
- _ => {}
- }
- }
- None
- })
- .map(|var_span| (args_span, var_span))
- } else {
- None
- };
- }
- }
- }
-
- None
- }
-
- fn report_conflicting_borrow(
- &mut self,
- context: Context,
- (place, span): (&Place<'tcx>, Span),
- gen_borrow_kind: BorrowKind,
- issued_borrow: &BorrowData,
- end_issued_loan_span: Option<Span>,
- ) {
- let issued_span = self.retrieve_borrow_span(issued_borrow);
-
- let new_closure_span = self.find_closure_span(span, context.loc);
- let span = new_closure_span.map(|(args, _)| args).unwrap_or(span);
- let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location);
- let issued_span = old_closure_span
- .map(|(args, _)| args)
- .unwrap_or(issued_span);
-
- let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
-
- // FIXME: supply non-"" `opt_via` when appropriate
- let mut err = match (
- gen_borrow_kind,
- "immutable",
- "mutable",
- issued_borrow.kind,
- "immutable",
- "mutable",
- ) {
- (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) |
- (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => self.tcx
- .cannot_reborrow_already_borrowed(
- span,
- &desc_place,
- "",
- lft,
- issued_span,
- "it",
- rgt,
- "",
- end_issued_loan_span,
- Origin::Mir,
- ),
-
- (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => self.tcx
- .cannot_mutably_borrow_multiply(
- span,
- &desc_place,
- "",
- issued_span,
- "",
- end_issued_loan_span,
- Origin::Mir,
- ),
-
- (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => self.tcx
- .cannot_uniquely_borrow_by_two_closures(
- span,
- &desc_place,
- issued_span,
- end_issued_loan_span,
- Origin::Mir,
- ),
-
- (BorrowKind::Unique, _, _, _, _, _) => self.tcx.cannot_uniquely_borrow_by_one_closure(
- span,
- &desc_place,
- "",
- issued_span,
- "it",
- "",
- end_issued_loan_span,
- Origin::Mir,
- ),
-
- (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => self.tcx
- .cannot_reborrow_already_uniquely_borrowed(
- span,
- &desc_place,
- "",
- lft,
- issued_span,
- "",
- end_issued_loan_span,
- Origin::Mir,
- ),
-
- (BorrowKind::Mut, _, lft, BorrowKind::Unique, _, _) => self.tcx
- .cannot_reborrow_already_uniquely_borrowed(
- span,
- &desc_place,
- "",
- lft,
- issued_span,
- "",
- end_issued_loan_span,
- Origin::Mir,
- ),
-
- (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
- };
-
- if let Some((_, var_span)) = old_closure_span {
- err.span_label(
- var_span,
- format!(
- "previous borrow occurs due to use of `{}` in closure",
- desc_place
- ),
- );
- }
-
- if let Some((_, var_span)) = new_closure_span {
- err.span_label(
- var_span,
- format!("borrow occurs due to use of `{}` in closure", desc_place),
- );
- }
-
- err.emit();
- }
-
- fn report_borrowed_value_does_not_live_long_enough(
- &mut self,
- _: Context,
- (place, span): (&Place<'tcx>, Span),
- end_span: Option<Span>,
- ) {
- let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
- let proper_span = match *root_place {
- Place::Local(local) => self.mir.local_decls[local].source_info.span,
- _ => span,
- };
- let mut err = self.tcx
- .path_does_not_live_long_enough(span, "borrowed value", Origin::Mir);
- err.span_label(proper_span, "temporary value created here");
- err.span_label(span, "temporary value dropped here while still borrowed");
- err.note("consider using a `let` binding to increase its lifetime");
-
- if let Some(end) = end_span {
- err.span_label(end, "temporary value needs to live until here");
- }
-
- err.emit();
- }
-
- fn report_illegal_mutation_of_borrowed(
- &mut self,
- _: Context,
- (place, span): (&Place<'tcx>, Span),
- loan: &BorrowData,
- ) {
- let mut err = self.tcx.cannot_assign_to_borrowed(
- span,
- self.retrieve_borrow_span(loan),
- &self.describe_place(place).unwrap_or("_".to_owned()),
- Origin::Mir,
- );
-
- err.emit();
- }
-
- fn report_illegal_reassignment(
- &mut self,
- _context: Context,
- (place, span): (&Place<'tcx>, Span),
- assigned_span: Span,
- ) {
- let mut err = self.tcx.cannot_reassign_immutable(
- span,
- &self.describe_place(place).unwrap_or("_".to_owned()),
- Origin::Mir,
- );
- err.span_label(span, "cannot assign twice to immutable variable");
- if span != assigned_span {
- let value_msg = match self.describe_place(place) {
- Some(name) => format!("`{}`", name),
- None => "value".to_owned(),
- };
- err.span_label(assigned_span, format!("first assignment to {}", value_msg));
- }
- err.emit();
- }
-}
-
-impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
- // End-user visible description of `place` if one can be found. If the
- // place is a temporary for instance, None will be returned.
- fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
- let mut buf = String::new();
- match self.append_place_to_string(place, &mut buf, false) {
- Ok(()) => Some(buf),
- Err(()) => None,
- }
- }
-
- /// If this is a field projection, and the field is being projected from a closure type,
- /// then returns the index of the field being projected. Note that this closure will always
- /// be `self` in the current MIR, because that is the only time we directly access the fields
- /// of a closure type.
- fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> {
- match *place {
- Place::Projection(ref proj) => match proj.elem {
- ProjectionElem::Field(field, _ty) => {
- let is_projection_from_ty_closure = proj.base
- .ty(self.mir, self.tcx)
- .to_ty(self.tcx)
- .is_closure();
-
- if is_projection_from_ty_closure {
- Some(field)
- } else {
- None
- }
- }
- _ => None,
- },
- _ => None,
- }
- }
-
- // Appends end-user visible description of `place` to `buf`.
- fn append_place_to_string(
- &self,
- place: &Place<'tcx>,
- buf: &mut String,
- mut autoderef: bool,
- ) -> Result<(), ()> {
- match *place {
- Place::Local(local) => {
- self.append_local_to_string(local, buf)?;
- }
- Place::Static(ref static_) => {
- buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
- }
- Place::Projection(ref proj) => {
- match proj.elem {
- ProjectionElem::Deref => {
- if let Some(field) = self.is_upvar_field_projection(&proj.base) {
- let var_index = field.index();
- let name = self.mir.upvar_decls[var_index].debug_name.to_string();
- if self.mir.upvar_decls[var_index].by_ref {
- buf.push_str(&name);
- } else {
- buf.push_str(&format!("*{}", &name));
- }
- } else {
- if autoderef {
- self.append_place_to_string(&proj.base, buf, autoderef)?;
- } else {
- buf.push_str(&"*");
- self.append_place_to_string(&proj.base, buf, autoderef)?;
- }
- }
- }
- ProjectionElem::Downcast(..) => {
- self.append_place_to_string(&proj.base, buf, autoderef)?;
- }
- ProjectionElem::Field(field, _ty) => {
- autoderef = true;
-
- if let Some(field) = self.is_upvar_field_projection(place) {
- let var_index = field.index();
- let name = self.mir.upvar_decls[var_index].debug_name.to_string();
- buf.push_str(&name);
- } else {
- let field_name = self.describe_field(&proj.base, field);
- self.append_place_to_string(&proj.base, buf, autoderef)?;
- buf.push_str(&format!(".{}", field_name));
- }
- }
- ProjectionElem::Index(index) => {
- autoderef = true;
-
- self.append_place_to_string(&proj.base, buf, autoderef)?;
- buf.push_str("[");
- if let Err(_) = self.append_local_to_string(index, buf) {
- buf.push_str("..");
- }
- buf.push_str("]");
- }
- ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
- autoderef = true;
- // Since it isn't possible to borrow an element on a particular index and
- // then use another while the borrow is held, don't output indices details
- // to avoid confusing the end-user
- self.append_place_to_string(&proj.base, buf, autoderef)?;
- buf.push_str(&"[..]");
- }
- };
- }
- }
-
- Ok(())
- }
-
- // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
- // a name, then `Err` is returned
- fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
- let local = &self.mir.local_decls[local_index];
- match local.name {
- Some(name) => {
- buf.push_str(&format!("{}", name));
- Ok(())
- }
- None => Err(()),
- }
- }
-
- // End-user visible description of the `field`nth field of `base`
- fn describe_field(&self, base: &Place, field: Field) -> String {
- match *base {
- Place::Local(local) => {
- let local = &self.mir.local_decls[local];
- self.describe_field_from_ty(&local.ty, field)
- }
- Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
- Place::Projection(ref proj) => match proj.elem {
- ProjectionElem::Deref => self.describe_field(&proj.base, field),
- ProjectionElem::Downcast(def, variant_index) => {
- format!("{}", def.variants[variant_index].fields[field.index()].name)
- }
- ProjectionElem::Field(_, field_type) => {
- self.describe_field_from_ty(&field_type, field)
- }
- ProjectionElem::Index(..) |
- ProjectionElem::ConstantIndex { .. } |
- ProjectionElem::Subslice { .. } => {
- format!("{}", self.describe_field(&proj.base, field))
- }
- },
- }
- }
-
- // End-user visible description of the `field_index`nth field of `ty`
- fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
- if ty.is_box() {
- // If the type is a box, the field is described from the boxed type
- self.describe_field_from_ty(&ty.boxed_ty(), field)
- } else {
- match ty.sty {
- ty::TyAdt(def, _) => if def.is_enum() {
- format!("{}", field.index())
- } else {
- format!("{}", def.struct_variant().fields[field.index()].name)
- },
- ty::TyTuple(_, _) => format!("{}", field.index()),
- ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => {
- self.describe_field_from_ty(&tnm.ty, field)
- }
- ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field),
- ty::TyClosure(closure_def_id, _) => {
- // Convert the def-id into a node-id. node-ids are only valid for
- // the local code in the current crate, so this returns an `Option` in case
- // the closure comes from another crate. But in that case we wouldn't
- // be borrowck'ing it, so we can just unwrap:
- let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
- let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
-
- self.tcx.hir.name(freevar.var_id()).to_string()
- }
- _ => {
- // Might need a revision when the fields in trait RFC is implemented
- // (https://github.com/rust-lang/rfcs/pull/1546)
- bug!(
- "End-user description not implemented for field access on `{:?}`",
- ty.sty
- );
- }
- }
- }
- }
-
- // Retrieve span of given borrow from the current MIR representation
- fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
- self.mir.source_info(borrow.location).span
- }
-}
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
// FIXME (#16118): function intended to allow the borrow checker
}
}
}
-
-impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
- fn new(
- borrows: FlowInProgress<Borrows<'b, 'gcx, 'tcx>>,
- inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
- uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
- move_outs: FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>,
- ever_inits: FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>,
- ) -> Self {
- InProgress {
- borrows,
- inits,
- uninits,
- move_outs,
- ever_inits,
- }
- }
-
- fn each_flow<XB, XI, XU, XM, XE>(
- &mut self,
- mut xform_borrows: XB,
- mut xform_inits: XI,
- mut xform_uninits: XU,
- mut xform_move_outs: XM,
- mut xform_ever_inits: XE,
- ) where
- XB: FnMut(&mut FlowInProgress<Borrows<'b, 'gcx, 'tcx>>),
- XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>),
- XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>),
- XM: FnMut(&mut FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>),
- XE: FnMut(&mut FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>),
- {
- xform_borrows(&mut self.borrows);
- xform_inits(&mut self.inits);
- xform_uninits(&mut self.uninits);
- xform_move_outs(&mut self.move_outs);
- xform_ever_inits(&mut self.ever_inits);
- }
-}
-
-impl<'b, 'gcx, 'tcx> fmt::Display for InProgress<'b, 'gcx, 'tcx> {
- fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- let mut s = String::new();
-
- s.push_str("borrows in effect: [");
- let mut saw_one = false;
- self.borrows.each_state_bit(|borrow| {
- if saw_one {
- s.push_str(", ");
- };
- saw_one = true;
- let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
- s.push_str(&format!("{}", borrow_data));
- });
- s.push_str("] ");
-
- s.push_str("borrows generated: [");
- let mut saw_one = false;
- self.borrows.each_gen_bit(|borrow| {
- if saw_one {
- s.push_str(", ");
- };
- saw_one = true;
- let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
- s.push_str(&format!("{}", borrow_data));
- });
- s.push_str("] ");
-
- s.push_str("inits: [");
- let mut saw_one = false;
- self.inits.each_state_bit(|mpi_init| {
- if saw_one {
- s.push_str(", ");
- };
- saw_one = true;
- let move_path = &self.inits.base_results.operator().move_data().move_paths[mpi_init];
- s.push_str(&format!("{}", move_path));
- });
- s.push_str("] ");
-
- s.push_str("uninits: [");
- let mut saw_one = false;
- self.uninits.each_state_bit(|mpi_uninit| {
- if saw_one {
- s.push_str(", ");
- };
- saw_one = true;
- let move_path =
- &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit];
- s.push_str(&format!("{}", move_path));
- });
- s.push_str("] ");
-
- s.push_str("move_out: [");
- let mut saw_one = false;
- self.move_outs.each_state_bit(|mpi_move_out| {
- if saw_one {
- s.push_str(", ");
- };
- saw_one = true;
- let move_out = &self.move_outs.base_results.operator().move_data().moves[mpi_move_out];
- s.push_str(&format!("{:?}", move_out));
- });
- s.push_str("] ");
-
- s.push_str("ever_init: [");
- let mut saw_one = false;
- self.ever_inits.each_state_bit(|mpi_ever_init| {
- if saw_one {
- s.push_str(", ");
- };
- saw_one = true;
- let ever_init =
- &self.ever_inits.base_results.operator().move_data().inits[mpi_ever_init];
- s.push_str(&format!("{:?}", ever_init));
- });
- s.push_str("]");
-
- fmt::Display::fmt(&s, fmt)
- }
-}
-
-impl<'tcx, T> FlowInProgress<T>
-where
- T: HasMoveData<'tcx> + BitDenotation<Idx = MovePathIndex>,
-{
- fn has_any_child_of(&self, mpi: T::Idx) -> Option<T::Idx> {
- let move_data = self.base_results.operator().move_data();
-
- let mut todo = vec![mpi];
- let mut push_siblings = false; // don't look at siblings of original `mpi`.
- while let Some(mpi) = todo.pop() {
- if self.curr_state.contains(&mpi) {
- return Some(mpi);
- }
- let move_path = &move_data.move_paths[mpi];
- if let Some(child) = move_path.first_child {
- todo.push(child);
- }
- if push_siblings {
- if let Some(sibling) = move_path.next_sibling {
- todo.push(sibling);
- }
- } else {
- // after we've processed the original `mpi`, we should
- // always traverse the siblings of any of its
- // children.
- push_siblings = true;
- }
- }
- return None;
- }
-}
-
-impl<BD> FlowInProgress<BD>
-where
- BD: BitDenotation,
-{
- fn each_state_bit<F>(&self, f: F)
- where
- F: FnMut(BD::Idx),
- {
- self.curr_state
- .each_bit(self.base_results.operator().bits_per_block(), f)
- }
-
- fn each_gen_bit<F>(&self, f: F)
- where
- F: FnMut(BD::Idx),
- {
- self.stmt_gen
- .each_bit(self.base_results.operator().bits_per_block(), f)
- }
-
- fn new(results: DataflowResults<BD>) -> Self {
- let bits_per_block = results.sets().bits_per_block();
- let curr_state = IdxSetBuf::new_empty(bits_per_block);
- let stmt_gen = IdxSetBuf::new_empty(bits_per_block);
- let stmt_kill = IdxSetBuf::new_empty(bits_per_block);
- FlowInProgress {
- base_results: results,
- curr_state: curr_state,
- stmt_gen: stmt_gen,
- stmt_kill: stmt_kill,
- }
- }
-
- fn reset_to_entry_of(&mut self, bb: BasicBlock) {
- (*self.curr_state).clone_from(self.base_results.sets().on_entry_set_for(bb.index()));
- }
-
- fn reconstruct_statement_effect(&mut self, loc: Location) {
- self.stmt_gen.reset_to_empty();
- self.stmt_kill.reset_to_empty();
- let mut ignored = IdxSetBuf::new_empty(0);
- let mut sets = BlockSets {
- on_entry: &mut ignored,
- gen_set: &mut self.stmt_gen,
- kill_set: &mut self.stmt_kill,
- };
- self.base_results
- .operator()
- .statement_effect(&mut sets, loc);
- }
-
- fn reconstruct_terminator_effect(&mut self, loc: Location) {
- self.stmt_gen.reset_to_empty();
- self.stmt_kill.reset_to_empty();
- let mut ignored = IdxSetBuf::new_empty(0);
- let mut sets = BlockSets {
- on_entry: &mut ignored,
- gen_set: &mut self.stmt_gen,
- kill_set: &mut self.stmt_kill,
- };
- self.base_results
- .operator()
- .terminator_effect(&mut sets, loc);
- }
-
- fn apply_local_effect(&mut self) {
- self.curr_state.union(&self.stmt_gen);
- self.curr_state.subtract(&self.stmt_kill);
- }
-
- fn elems_incoming(&self) -> indexed_set::Elems<BD::Idx> {
- let univ = self.base_results.sets().bits_per_block();
- self.curr_state.elems(univ)
- }
-
- fn with_elems_outgoing<F>(&self, f: F)
- where
- F: FnOnce(indexed_set::Elems<BD::Idx>),
- {
- let mut curr_state = self.curr_state.clone();
- curr_state.union(&self.stmt_gen);
- curr_state.subtract(&self.stmt_kill);
- let univ = self.base_results.sets().bits_per_block();
- f(curr_state.elems(univ));
- }
-}