diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index b55843ac527df..5991845d265b2 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -127,14 +127,14 @@ impl<'tcx> Place<'tcx> { /// of a closure type. pub fn is_upvar_field_projection<'cx, 'gcx>(&self, mir: &'cx Mir<'tcx>, tcx: &TyCtxt<'cx, 'gcx, 'tcx>) -> Option { - let place = if let Place::Projection(ref proj) = self { + let (place, by_ref) = if let Place::Projection(ref proj) = self { if let ProjectionElem::Deref = proj.elem { - &proj.base + (&proj.base, true) } else { - self + (self, false) } } else { - self + (self, false) }; match place { @@ -142,14 +142,16 @@ impl<'tcx> Place<'tcx> { ProjectionElem::Field(field, _ty) => { let base_ty = proj.base.ty(mir, *tcx).to_ty(*tcx); - if base_ty.is_closure() || base_ty.is_generator() { + if (base_ty.is_closure() || base_ty.is_generator()) && + (!by_ref || mir.upvar_decls[field.index()].by_ref) + { Some(field) } else { None } }, _ => None, - }, + } _ => None, } } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 967a8d9033096..f96b9b8082fa7 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -151,7 +151,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( let location_table = &LocationTable::new(mir); let mut errors_buffer = Vec::new(); - let (move_data, move_errors): (MoveData<'tcx>, Option>>) = + let (move_data, move_errors): (MoveData<'tcx>, Option, MoveError<'tcx>)>>) = match MoveData::gather_moves(mir, tcx) { Ok(move_data) => (move_data, None), Err((move_data, move_errors)) => (move_data, Some(move_errors)), diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs index 4d988fef450b8..d3524e841b223 100644 --- a/src/librustc_mir/borrow_check/move_errors.rs +++ b/src/librustc_mir/borrow_check/move_errors.rs @@ -11,11 +11,12 @@ use rustc::hir; use rustc::mir::*; use rustc::ty; -use rustc_data_structures::indexed_vec::Idx; use rustc_errors::DiagnosticBuilder; +use rustc_data_structures::indexed_vec::Idx; use syntax_pos::Span; use borrow_check::MirBorrowckCtxt; +use borrow_check::prefixes::PrefixSet; use dataflow::move_paths::{IllegalMoveOrigin, IllegalMoveOriginKind}; use dataflow::move_paths::{LookupResult, MoveError, MovePathIndex}; use util::borrowck_errors::{BorrowckErrors, Origin}; @@ -38,6 +39,7 @@ enum GroupedMoveError<'tcx> { // Match place can't be moved from // e.g. match x[0] { s => (), } where x: &[String] MovesFromMatchPlace { + original_path: Place<'tcx>, span: Span, move_from: Place<'tcx>, kind: IllegalMoveOriginKind<'tcx>, @@ -46,6 +48,7 @@ enum GroupedMoveError<'tcx> { // Part of a pattern can't be moved from, // e.g. match &String::new() { &x => (), } MovesFromPattern { + original_path: Place<'tcx>, span: Span, move_from: MovePathIndex, kind: IllegalMoveOriginKind<'tcx>, @@ -53,23 +56,27 @@ enum GroupedMoveError<'tcx> { }, // Everything that isn't from pattern matching. OtherIllegalMove { + original_path: Place<'tcx>, span: Span, kind: IllegalMoveOriginKind<'tcx>, }, } impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { - pub(crate) fn report_move_errors(&mut self, move_errors: Vec>) { + pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) { let grouped_errors = self.group_move_errors(move_errors); for error in grouped_errors { self.report(error); } } - fn group_move_errors(&self, errors: Vec>) -> Vec> { + fn group_move_errors( + &self, + errors: Vec<(Place<'tcx>, MoveError<'tcx>)> + ) -> Vec> { let mut grouped_errors = Vec::new(); - for error in errors { - self.append_to_grouped_errors(&mut grouped_errors, error); + for (original_path, error) in errors { + self.append_to_grouped_errors(&mut grouped_errors, original_path, error); } grouped_errors } @@ -77,6 +84,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { fn append_to_grouped_errors( &self, grouped_errors: &mut Vec>, + original_path: Place<'tcx>, error: MoveError<'tcx>, ) { match error { @@ -116,6 +124,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { self.append_binding_error( grouped_errors, kind, + original_path, move_from, *local, opt_match_place, @@ -127,6 +136,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } grouped_errors.push(GroupedMoveError::OtherIllegalMove { span: stmt_source_info.span, + original_path, kind, }); } @@ -137,6 +147,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { &self, grouped_errors: &mut Vec>, kind: IllegalMoveOriginKind<'tcx>, + original_path: Place<'tcx>, move_from: &Place<'tcx>, bind_to: Local, match_place: &Option>, @@ -176,6 +187,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { grouped_errors.push(GroupedMoveError::MovesFromMatchPlace { span, move_from: match_place.clone(), + original_path, kind, binds_to, }); @@ -206,6 +218,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { grouped_errors.push(GroupedMoveError::MovesFromPattern { span: match_span, move_from: mpi, + original_path, kind, binds_to: vec![bind_to], }); @@ -215,12 +228,23 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { fn report(&mut self, error: GroupedMoveError<'tcx>) { let (mut err, err_span) = { - let (span, kind): (Span, &IllegalMoveOriginKind) = match error { - GroupedMoveError::MovesFromMatchPlace { span, ref kind, .. } - | GroupedMoveError::MovesFromPattern { span, ref kind, .. } - | GroupedMoveError::OtherIllegalMove { span, ref kind } => (span, kind), - }; + let (span, original_path, kind): (Span, &Place<'tcx>, &IllegalMoveOriginKind) = + match error { + GroupedMoveError::MovesFromMatchPlace { + span, + ref original_path, + ref kind, + .. + } | + GroupedMoveError::MovesFromPattern { span, ref original_path, ref kind, .. } | + GroupedMoveError::OtherIllegalMove { span, ref original_path, ref kind } => { + (span, original_path, kind) + }, + }; let origin = Origin::Mir; + debug!("report: original_path={:?} span={:?}, kind={:?} \ + original_path.is_upvar_field_projection={:?}", original_path, span, kind, + original_path.is_upvar_field_projection(self.mir, &self.tcx)); ( match kind { IllegalMoveOriginKind::Static => { @@ -231,22 +255,17 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { // borrow to provide feedback about why this // was a move rather than a copy. let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx); + let is_upvar_field_projection = + self.prefixes(&original_path, PrefixSet::All) + .any(|p| p.is_upvar_field_projection(self.mir, &self.tcx) + .is_some()); match ty.sty { ty::TyArray(..) | ty::TySlice(..) => self .tcx .cannot_move_out_of_interior_noncopy(span, ty, None, origin), ty::TyClosure(def_id, closure_substs) - if !self.mir.upvar_decls.is_empty() - && { - match place { - Place::Projection(ref proj) => { - proj.base == Place::Local(Local::new(1)) - } - Place::Promoted(_) | - Place::Local(_) | Place::Static(_) => unreachable!(), - } - } => - { + if !self.mir.upvar_decls.is_empty() && is_upvar_field_projection + => { let closure_kind_ty = closure_substs.closure_kind_ty(def_id, self.tcx); let closure_kind = closure_kind_ty.to_opt_closure_kind(); @@ -262,7 +281,28 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } None => bug!("closure kind not inferred by borrowck"), }; - self.tcx.cannot_move_out_of(span, place_description, origin) + debug!("report: closure_kind_ty={:?} closure_kind={:?} \ + place_description={:?}", closure_kind_ty, closure_kind, + place_description); + + let mut diag = self.tcx.cannot_move_out_of( + span, place_description, origin); + + for prefix in self.prefixes(&original_path, PrefixSet::All) { + if let Some(field) = prefix.is_upvar_field_projection( + self.mir, &self.tcx) { + let upvar_decl = &self.mir.upvar_decls[field.index()]; + let upvar_hir_id = + upvar_decl.var_hir_id.assert_crate_local(); + let upvar_node_id = + self.tcx.hir.hir_to_node_id(upvar_hir_id); + let upvar_span = self.tcx.hir.span(upvar_node_id); + diag.span_label(upvar_span, "captured outer variable"); + break; + } + } + + diag } _ => self .tcx diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs index e8862320ddf3f..283cccd51174a 100644 --- a/src/librustc_mir/borrow_check/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/mutability_errors.rs @@ -36,10 +36,18 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { error_access: AccessKind, location: Location, ) { + debug!( + "report_mutability_error(\ + access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\ + )", + access_place, span, the_place_err, error_access, location, + ); + let mut err; let item_msg; let reason; let access_place_desc = self.describe_place(access_place); + debug!("report_mutability_error: access_place_desc={:?}", access_place_desc); match the_place_err { Place::Local(local) => { @@ -63,7 +71,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { )); item_msg = format!("`{}`", access_place_desc.unwrap()); - if self.is_upvar(access_place) { + if access_place.is_upvar_field_projection(self.mir, &self.tcx).is_some() { reason = ", as it is not declared as mutable".to_string(); } else { let name = self.mir.upvar_decls[upvar_index.index()].debug_name; @@ -82,7 +90,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { the_place_err.ty(self.mir, self.tcx).to_ty(self.tcx) )); - reason = if self.is_upvar(access_place) { + reason = if access_place.is_upvar_field_projection(self.mir, + &self.tcx).is_some() { ", as it is a captured variable in a `Fn` closure".to_string() } else { ", as `Fn` closures cannot mutate their captured variables".to_string() @@ -155,6 +164,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { }) => bug!("Unexpected immutable place."), } + debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason); + // `act` and `acted_on` are strings that let us abstract over // the verbs used in some diagnostic messages. let act; @@ -199,6 +210,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { } }; + debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on); + match the_place_err { // We want to suggest users use `let mut` for local (user // variable) mutations... @@ -382,31 +395,6 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { err.buffer(&mut self.errors_buffer); } - - // Does this place refer to what the user sees as an upvar - fn is_upvar(&self, place: &Place<'tcx>) -> bool { - match *place { - Place::Projection(box Projection { - ref base, - elem: ProjectionElem::Field(_, _), - }) => { - let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx); - is_closure_or_generator(base_ty) - } - Place::Projection(box Projection { - base: - Place::Projection(box Projection { - ref base, - elem: ProjectionElem::Field(upvar_index, _), - }), - elem: ProjectionElem::Deref, - }) => { - let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx); - is_closure_or_generator(base_ty) && self.mir.upvar_decls[upvar_index.index()].by_ref - } - _ => false, - } - } } fn suggest_ampmut_self<'cx, 'gcx, 'tcx>( diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 44e46e90549a7..60030f2702055 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -27,7 +27,7 @@ struct MoveDataBuilder<'a, 'gcx: 'tcx, 'tcx: 'a> { mir: &'a Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>, data: MoveData<'tcx>, - errors: Vec>, + errors: Vec<(Place<'tcx>, MoveError<'tcx>)>, } impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { @@ -186,7 +186,9 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { } impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { - fn finalize(self) -> Result, (MoveData<'tcx>, Vec>)> { + fn finalize( + self + ) -> Result, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)> { debug!("{}", { debug!("moves for {:?}:", self.mir.span); for (j, mo) in self.data.moves.iter_enumerated() { @@ -207,9 +209,10 @@ impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { } } -pub(super) fn gather_moves<'a, 'gcx, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) - -> Result, - (MoveData<'tcx>, Vec>)> { +pub(super) fn gather_moves<'a, 'gcx, 'tcx>( + mir: &Mir<'tcx>, + tcx: TyCtxt<'a, 'gcx, 'tcx> +) -> Result, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)> { let mut builder = MoveDataBuilder::new(mir, tcx); builder.gather_args(); @@ -407,7 +410,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { let path = match self.move_path_for(place) { Ok(path) | Err(MoveError::UnionMove { path }) => path, Err(error @ MoveError::IllegalMove { .. }) => { - self.builder.errors.push(error); + self.builder.errors.push((place.clone(), error)); return; } }; diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 64bfd36b7eeb6..7b4cbdf7131b0 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -313,7 +313,7 @@ impl<'tcx> MoveError<'tcx> { impl<'a, 'gcx, 'tcx> MoveData<'tcx> { pub fn gather_moves(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) - -> Result>)> { + -> Result, MoveError<'tcx>)>)> { builder::gather_moves(mir, tcx) } } diff --git a/src/test/ui/borrowck/borrowck-in-static.nll.stderr b/src/test/ui/borrowck/borrowck-in-static.nll.stderr index 05a022a726cd9..45fa1764f7027 100644 --- a/src/test/ui/borrowck/borrowck-in-static.nll.stderr +++ b/src/test/ui/borrowck/borrowck-in-static.nll.stderr @@ -1,6 +1,8 @@ error[E0507]: cannot move out of captured variable in an `Fn` closure --> $DIR/borrowck-in-static.rs:15:17 | +LL | let x = Box::new(0); + | - captured outer variable LL | Box::new(|| x) //~ ERROR cannot move out of captured outer variable | ^ cannot move out of captured variable in an `Fn` closure diff --git a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.nll.stderr b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.nll.stderr index 07a9f374b2c39..0eb5fc8c32435 100644 --- a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.nll.stderr +++ b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.nll.stderr @@ -1,6 +1,9 @@ error[E0507]: cannot move out of captured variable in an `Fn` closure --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:21:9 | +LL | let y = vec![format!("World")]; + | - captured outer variable +LL | call(|| { LL | y.into_iter(); | ^ cannot move out of captured variable in an `Fn` closure diff --git a/src/test/ui/issue-4335.nll.stderr b/src/test/ui/issue-4335.nll.stderr index 4ccd24fa45921..a9345e86f7248 100644 --- a/src/test/ui/issue-4335.nll.stderr +++ b/src/test/ui/issue-4335.nll.stderr @@ -1,6 +1,8 @@ error[E0507]: cannot move out of captured variable in an `FnMut` closure --> $DIR/issue-4335.rs:16:20 | +LL | fn f<'r, T>(v: &'r T) -> Box T + 'r> { + | - captured outer variable LL | id(Box::new(|| *v)) | ^^ cannot move out of captured variable in an `FnMut` closure diff --git a/src/test/ui/nll/issue-52663-span-decl-captured-variable.rs b/src/test/ui/nll/issue-52663-span-decl-captured-variable.rs new file mode 100644 index 0000000000000..dc40b0c44fd02 --- /dev/null +++ b/src/test/ui/nll/issue-52663-span-decl-captured-variable.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(nll)] + +fn expect_fn(f: F) where F : Fn() { + f(); +} + +fn main() { + { + let x = (vec![22], vec![44]); + expect_fn(|| drop(x.0)); + //~^ ERROR cannot move out of captured variable in an `Fn` closure [E0507] + } +} diff --git a/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr b/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr new file mode 100644 index 0000000000000..51f19565855a7 --- /dev/null +++ b/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -0,0 +1,11 @@ +error[E0507]: cannot move out of captured variable in an `Fn` closure + --> $DIR/issue-52663-span-decl-captured-variable.rs:20:26 + | +LL | let x = (vec![22], vec![44]); + | - captured outer variable +LL | expect_fn(|| drop(x.0)); + | ^^^ cannot move out of captured variable in an `Fn` closure + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0507`. diff --git a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr index c7dbc043cdaee..a05a3911aa771 100644 --- a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr +++ b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr @@ -31,6 +31,9 @@ LL | f.f.call_mut(()) error[E0507]: cannot move out of captured variable in an `FnMut` closure --> $DIR/borrowck-call-is-borrow-issue-12224.rs:66:13 | +LL | let mut f = move |g: Box, b: isize| { + | ----- captured outer variable +... LL | foo(f); | ^ cannot move out of captured variable in an `FnMut` closure