diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 270430f40df..a6dbbee79a4 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1682,7 +1682,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { { if let InferTables::InProgress(tables) = self.tables { if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { - return tables.borrow().closure_kinds.get(&id).cloned(); + return tables.borrow() + .closure_kinds + .get(&id) + .cloned() + .map(|(kind, _)| kind); } } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 7316d45dc21..e3d7aeb22e1 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -58,6 +58,7 @@ use syntax::abi; use syntax::ast::{self, Name, NodeId}; use syntax::attr; use syntax::symbol::{Symbol, keywords}; +use syntax_pos::Span; use hir; @@ -229,8 +230,9 @@ pub struct TypeckTables<'tcx> { /// Records the type of each closure. pub closure_tys: NodeMap>, - /// Records the kind of each closure. - pub closure_kinds: NodeMap, + /// Records the kind of each closure and the span and name of the variable + /// that caused the closure to be this kind. + pub closure_kinds: NodeMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>, /// For each fn, records the "liberated" types of its arguments /// and return type. Liberated means that all bound regions diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 3248731d1ca..9b084acc193 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -39,8 +39,6 @@ use rustc::middle::free_region::RegionRelations; use rustc::ty::{self, TyCtxt}; use rustc::ty::maps::Providers; -use syntax_pos::DUMMY_SP; - use std::fmt; use std::rc::Rc; use std::hash::{Hash, Hasher}; @@ -587,9 +585,15 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { verb, msg, nl); let need_note = match lp.ty.sty { ty::TypeVariants::TyClosure(id, _) => { - if let Ok(ty::ClosureKind::FnOnce) = - ty::queries::closure_kind::try_get(self.tcx, DUMMY_SP, id) { - err.help("closure was moved because it only implements `FnOnce`"); + let node_id = self.tcx.hir.as_local_node_id(id).unwrap(); + if let Some(&(ty::ClosureKind::FnOnce, Some((span, name)))) = + self.tables.closure_kinds.get(&node_id) + { + err.span_note(span, &format!( + "closure cannot be invoked more than once because \ + it moves the variable `{}` out of its environment", + name + )); false } else { true diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 4c3d5c8aaca..c2e8269aafe 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -103,7 +103,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.tables.borrow_mut().closure_tys.insert(expr.id, sig); match opt_kind { Some(kind) => { - self.tables.borrow_mut().closure_kinds.insert(expr.id, kind); + self.tables.borrow_mut().closure_kinds.insert(expr.id, (kind, None)); } None => {} } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index b32eb9ac5fb..1a1a9361a89 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -814,7 +814,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { let closure_kinds = &self.tables.borrow().closure_kinds; let closure_kind = match closure_kinds.get(&closure_id) { - Some(&k) => k, + Some(&(k, _)) => k, None => { return Err(MethodError::ClosureAmbiguity(trait_def_id)); } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index b5c2780e9a7..3a6fd51693e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -702,7 +702,7 @@ fn closure_kind<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::ClosureKind { let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); - tcx.typeck_tables_of(def_id).closure_kinds[&node_id] + tcx.typeck_tables_of(def_id).closure_kinds[&node_id].0 } fn adt_destructor<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 9bfc5f3f0ea..114290c52d1 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -74,7 +74,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { struct SeedBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, - temp_closure_kinds: NodeMap, + temp_closure_kinds: NodeMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>, } impl<'a, 'gcx, 'tcx> Visitor<'gcx> for SeedBorrowKind<'a, 'gcx, 'tcx> { @@ -107,7 +107,7 @@ impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> { capture_clause: hir::CaptureClause) { if !self.fcx.tables.borrow().closure_kinds.contains_key(&expr.id) { - self.temp_closure_kinds.insert(expr.id, ty::ClosureKind::Fn); + self.temp_closure_kinds.insert(expr.id, (ty::ClosureKind::Fn, None)); debug!("check_closure: adding closure {:?} as Fn", expr.id); } @@ -143,12 +143,12 @@ impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> { struct AdjustBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, - temp_closure_kinds: NodeMap, + temp_closure_kinds: NodeMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>, } impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, - temp_closure_kinds: NodeMap) + temp_closure_kinds: NodeMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>) -> AdjustBorrowKind<'a, 'gcx, 'tcx> { AdjustBorrowKind { fcx: fcx, temp_closure_kinds: temp_closure_kinds } } @@ -211,8 +211,8 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { // If we are also inferred the closure kind here, update the // main table and process any deferred resolutions. - if let Some(&kind) = self.temp_closure_kinds.get(&id) { - self.fcx.tables.borrow_mut().closure_kinds.insert(id, kind); + if let Some(&(kind, context)) = self.temp_closure_kinds.get(&id) { + self.fcx.tables.borrow_mut().closure_kinds.insert(id, (kind, context)); let closure_def_id = self.fcx.tcx.hir.local_def_id(id); debug!("closure_kind({:?}) = {:?}", closure_def_id, kind); @@ -272,6 +272,8 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { euv::Move(_) => { } } + let tcx = self.fcx.tcx; + // watch out for a move of the deref of a borrowed pointer; // for that to be legal, the upvar would have to be borrowed // by value instead @@ -289,7 +291,9 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { // to move out of an upvar, this must be a FnOnce closure self.adjust_closure_kind(upvar_id.closure_expr_id, - ty::ClosureKind::FnOnce); + ty::ClosureKind::FnOnce, + guarantor.span, + tcx.hir.name(upvar_id.var_id)); let upvar_capture_map = &mut self.fcx.tables.borrow_mut().upvar_capture_map; @@ -303,7 +307,9 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { // to be a FnOnce closure to permit moves out // of the environment. self.adjust_closure_kind(upvar_id.closure_expr_id, - ty::ClosureKind::FnOnce); + ty::ClosureKind::FnOnce, + guarantor.span, + tcx.hir.name(upvar_id.var_id)); } mc::NoteNone => { } @@ -331,7 +337,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { Categorization::Deref(base, _, mc::BorrowedPtr(..)) | Categorization::Deref(base, _, mc::Implicit(..)) => { - if !self.try_adjust_upvar_deref(&cmt.note, ty::MutBorrow) { + if !self.try_adjust_upvar_deref(cmt, ty::MutBorrow) { // assignment to deref of an `&mut` // borrowed pointer implies that the // pointer itself must be unique, but not @@ -365,7 +371,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { Categorization::Deref(base, _, mc::BorrowedPtr(..)) | Categorization::Deref(base, _, mc::Implicit(..)) => { - if !self.try_adjust_upvar_deref(&cmt.note, ty::UniqueImmBorrow) { + if !self.try_adjust_upvar_deref(cmt, ty::UniqueImmBorrow) { // for a borrowed pointer to be unique, its // base must be unique self.adjust_upvar_borrow_kind_for_unique(base); @@ -382,7 +388,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { } fn try_adjust_upvar_deref(&mut self, - note: &mc::Note, + cmt: mc::cmt<'tcx>, borrow_kind: ty::BorrowKind) -> bool { @@ -394,7 +400,9 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { ty::ImmBorrow => false, }); - match *note { + let tcx = self.fcx.tcx; + + match cmt.note { mc::NoteUpvarRef(upvar_id) => { // if this is an implicit deref of an // upvar, then we need to modify the @@ -407,7 +415,10 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { } // also need to be in an FnMut closure since this is not an ImmBorrow - self.adjust_closure_kind(upvar_id.closure_expr_id, ty::ClosureKind::FnMut); + self.adjust_closure_kind(upvar_id.closure_expr_id, + ty::ClosureKind::FnMut, + cmt.span, + tcx.hir.name(upvar_id.var_id)); true } @@ -415,7 +426,10 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { // this kind of deref occurs in a `move` closure, or // for a by-value upvar; in either case, to mutate an // upvar, we need to be an FnMut closure - self.adjust_closure_kind(upvar_id.closure_expr_id, ty::ClosureKind::FnMut); + self.adjust_closure_kind(upvar_id.closure_expr_id, + ty::ClosureKind::FnMut, + cmt.span, + tcx.hir.name(upvar_id.var_id)); true } @@ -462,11 +476,13 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { fn adjust_closure_kind(&mut self, closure_id: ast::NodeId, - new_kind: ty::ClosureKind) { - debug!("adjust_closure_kind(closure_id={}, new_kind={:?})", - closure_id, new_kind); + new_kind: ty::ClosureKind, + upvar_span: Span, + var_name: ast::Name) { + debug!("adjust_closure_kind(closure_id={}, new_kind={:?}, upvar_span={:?}, var_name={})", + closure_id, new_kind, upvar_span, var_name); - if let Some(&existing_kind) = self.temp_closure_kinds.get(&closure_id) { + if let Some(&(existing_kind, _)) = self.temp_closure_kinds.get(&closure_id) { debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}", closure_id, existing_kind, new_kind); @@ -482,7 +498,10 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { // new kind is stronger than the old kind - self.temp_closure_kinds.insert(closure_id, new_kind); + self.temp_closure_kinds.insert( + closure_id, + (new_kind, Some((upvar_span, var_name))) + ); } } } diff --git a/src/test/ui/fn_once-moved.rs b/src/test/ui/fn_once-moved.rs index 781d3885eae..409964082f2 100644 --- a/src/test/ui/fn_once-moved.rs +++ b/src/test/ui/fn_once-moved.rs @@ -20,5 +20,6 @@ fn main() { debug_dump_dict(); debug_dump_dict(); //~^ ERROR use of moved value: `debug_dump_dict` - //~| NOTE closure was moved because it only implements `FnOnce` + //~| NOTE closure cannot be invoked more than once because it moves the + //~| variable `dict` out of its environment } diff --git a/src/test/ui/fn_once-moved.stderr b/src/test/ui/fn_once-moved.stderr index 91c89e55c54..27b7d91d1d4 100644 --- a/src/test/ui/fn_once-moved.stderr +++ b/src/test/ui/fn_once-moved.stderr @@ -6,7 +6,11 @@ error[E0382]: use of moved value: `debug_dump_dict` 21 | debug_dump_dict(); | ^^^^^^^^^^^^^^^ value used here after move | - = help: closure was moved because it only implements `FnOnce` +note: closure cannot be invoked more than once because it moves the variable `dict` out of its environment + --> $DIR/fn_once-moved.rs:16:29 + | +16 | for (key, value) in dict { + | ^^^^ error: aborting due to previous error(s)