about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-01-05 09:41:04 +0000
committerbors <bors@rust-lang.org>2019-01-05 09:41:04 +0000
commitcae623c5ce12df8f237264d8f2c31fdaa664c382 (patch)
tree3b3a5045b53393a47e7c6a1be4a62dbd303718f3
parent2fba17fc972f89fe9165fbed77d2f0ad6d3e2174 (diff)
parentfcad2094497519989327e372c09f9470bac7f957 (diff)
downloadrust-cae623c5ce12df8f237264d8f2c31fdaa664c382.tar.gz
rust-cae623c5ce12df8f237264d8f2c31fdaa664c382.zip
Auto merge of #57099 - davidtwco:issue-57098, r=nikomatsakis
NLL: Add closure cannot be moved note.

Fixes #57098.

This PR extends existing logic for checking whether a closure that
is `FnOnce` and therefore moves variables that it captures from the
environment has already been invoked when being invoked again.

Now, this logic will also check whether the closure is being moved after
previously being moved or invoked and add an appropriate note.

r? @pnkfelix
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs82
-rw-r--r--src/test/ui/not-copy-closure.nll.stderr6
2 files changed, 49 insertions, 39 deletions
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index ad8abe16415..474e67ac11a 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -123,7 +123,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 Origin::Mir,
             );
 
-            self.add_closure_invoked_twice_with_moved_variable_suggestion(
+            self.add_moved_or_invoked_closure_note(
                 context.loc,
                 used_place,
                 &mut err,
@@ -1329,7 +1329,8 @@ enum StorageDeadOrDrop<'tcx> {
 
 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
-    /// Adds a suggestion when a closure is invoked twice with a moved variable.
+    /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
+    /// is moved after being invoked.
     ///
     /// ```text
     /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
@@ -1339,30 +1340,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     /// LL |         for (key, value) in dict {
     ///    |                             ^^^^
     /// ```
-    pub(super) fn add_closure_invoked_twice_with_moved_variable_suggestion(
+    pub(super) fn add_moved_or_invoked_closure_note(
         &self,
         location: Location,
         place: &Place<'tcx>,
         diag: &mut DiagnosticBuilder<'_>,
     ) {
+        debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
         let mut target = place.local();
-        debug!(
-            "add_closure_invoked_twice_with_moved_variable_suggestion: location={:?} place={:?} \
-             target={:?}",
-             location, place, target,
-        );
         for stmt in &self.mir[location.block].statements[location.statement_index..] {
-            debug!(
-                "add_closure_invoked_twice_with_moved_variable_suggestion: stmt={:?} \
-                 target={:?}",
-                 stmt, target,
-            );
+            debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
             if let StatementKind::Assign(into, box Rvalue::Use(from)) = &stmt.kind {
-                debug!(
-                    "add_closure_invoked_twice_with_moved_variable_suggestion: into={:?} \
-                     from={:?}",
-                     into, from,
-                );
+                debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
                 match from {
                     Operand::Copy(ref place) |
                     Operand::Move(ref place) if target == place.local() =>
@@ -1372,12 +1361,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             }
         }
 
-
+        // Check if we are attempting to call a closure after it has been invoked.
         let terminator = self.mir[location.block].terminator();
-        debug!(
-            "add_closure_invoked_twice_with_moved_variable_suggestion: terminator={:?}",
-            terminator,
-        );
+        debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
         if let TerminatorKind::Call {
             func: Operand::Constant(box Constant {
                 literal: ty::LazyConst::Evaluated(ty::Const {
@@ -1389,7 +1375,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             args,
             ..
         } = &terminator.kind {
-            debug!("add_closure_invoked_twice_with_moved_variable_suggestion: id={:?}", id);
+            debug!("add_moved_or_invoked_closure_note: id={:?}", id);
             if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
                 let closure = match args.first() {
                     Some(Operand::Copy(ref place)) |
@@ -1397,33 +1383,51 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                         place.local().unwrap(),
                     _ => return,
                 };
-                debug!(
-                    "add_closure_invoked_twice_with_moved_variable_suggestion: closure={:?}",
-                     closure,
-                );
 
-                if let ty::TyKind::Closure(did, _substs) = self.mir.local_decls[closure].ty.sty {
-                    let node_id = match self.infcx.tcx.hir().as_local_node_id(did) {
-                        Some(node_id) => node_id,
-                        _ => return,
-                    };
+                debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
+                if let ty::TyKind::Closure(did, _) = self.mir.local_decls[closure].ty.sty {
+                    let node_id = self.infcx.tcx.hir().as_local_node_id(did).unwrap();
                     let hir_id = self.infcx.tcx.hir().node_to_hir_id(node_id);
 
-                    if let Some((
-                        span, name
-                    )) = self.infcx.tcx.typeck_tables_of(did).closure_kind_origins().get(hir_id) {
+                    if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
+                        .closure_kind_origins()
+                        .get(hir_id)
+                    {
                         diag.span_note(
                             *span,
                             &format!(
-                                "closure cannot be invoked more than once because it \
-                                 moves the variable `{}` out of its environment",
-                                 name,
+                                "closure cannot be invoked more than once because it moves the \
+                                 variable `{}` out of its environment",
+                                name,
                             ),
                         );
+                        return;
                     }
                 }
             }
         }
+
+        // Check if we are just moving a closure after it has been invoked.
+        if let Some(target) = target {
+            if let ty::TyKind::Closure(did, _) = self.mir.local_decls[target].ty.sty {
+                let node_id = self.infcx.tcx.hir().as_local_node_id(did).unwrap();
+                let hir_id = self.infcx.tcx.hir().node_to_hir_id(node_id);
+
+                if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
+                    .closure_kind_origins()
+                    .get(hir_id)
+                {
+                    diag.span_note(
+                        *span,
+                        &format!(
+                            "closure cannot be moved more than once as it is not `Copy` due to \
+                             moving the variable `{}` out of its environment",
+                             name
+                        ),
+                    );
+                }
+            }
+        }
     }
 
     /// End-user visible description of `place` if one can be found. If the
diff --git a/src/test/ui/not-copy-closure.nll.stderr b/src/test/ui/not-copy-closure.nll.stderr
index 2f295315a08..1a65bcf4473 100644
--- a/src/test/ui/not-copy-closure.nll.stderr
+++ b/src/test/ui/not-copy-closure.nll.stderr
@@ -5,6 +5,12 @@ LL |     let b = hello;
    |             ----- value moved here
 LL |     let c = hello; //~ ERROR use of moved value: `hello` [E0382]
    |             ^^^^^ value used here after move
+   |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `a` out of its environment
+  --> $DIR/not-copy-closure.rs:6:9
+   |
+LL |         a += 1;
+   |         ^
 
 error: aborting due to previous error