about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2025-07-20 02:15:45 +0000
committerEsteban Küber <esteban@kuber.com.ar>2025-07-21 16:21:23 +0000
commit5082e6a300974459aec6dc73e76cc039c3a517c1 (patch)
treec994ff732c6327c890a149009fddc40f041dccce /compiler
parent690ae523e5c0ac9602db7bbe2b8094ee422e4d0f (diff)
Generalize logic pointing at binding moved into closure
Account not only for `fn` parameters when moving non-`Copy` values into closure, but also for let bindings.

```
error[E0507]: cannot move out of `bar`, a captured variable in an `FnMut` closure
  --> $DIR/borrowck-move-by-capture.rs:9:29
   |
LL |     let bar: Box<_> = Box::new(3);
   |         ---  ------ move occurs because `bar` has type `Box<isize>`, which does not implement the `Copy` trait
   |         |
   |         captured outer variable
LL |     let _g = to_fn_mut(|| {
   |                        -- captured by this `FnMut` closure
LL |         let _h = to_fn_once(move || -> isize { *bar });
   |                             ^^^^^^^^^^^^^^^^   ---- variable moved due to use in closure
   |                             |
   |                             `bar` is moved here
   |
help: consider cloning the value before moving it into the closure
   |
LL ~         let value = bar.clone();
LL ~         let _h = to_fn_once(move || -> isize { value });
   |
```

```
error[E0507]: cannot move out of `y`, a captured variable in an `Fn` closure
  --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:12:9
   |
LL |     let y = vec![format!("World")];
   |         -   ---------------------- move occurs because `y` has type `Vec<String>`, which does not implement the `Copy` trait
   |         |
   |         captured outer variable
LL |     call(|| {
   |          -- captured by this `Fn` closure
LL |         y.into_iter();
   |         ^ ----------- `y` moved due to this method call
   |         |
   |         `y` is moved here
   |
note: `into_iter` takes ownership of the receiver `self`, which moves `y`
  --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: you can `clone` the value and consume it, but this might not be your desired behavior
   |
LL |         <Vec<String> as Clone>::clone(&y).into_iter();
   |         +++++++++++++++++++++++++++++++ +
help: consider cloning the value if the performance cost is acceptable
   |
LL |         y.clone().into_iter();
   |          ++++++++
```
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs79
1 files changed, 55 insertions, 24 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 28024e6fece..56f7fe68854 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -623,30 +623,51 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     && let Some(upvar_field) = self
                         .prefixes(original_path.as_ref(), PrefixSet::All)
                         .find_map(|p| self.is_upvar_field_projection(p))
-                    && let upvar = &self.upvars[upvar_field.index()]
-                    && let upvar_hir_id = upvar.get_root_variable()
-                    && let hir::Node::Param(param) = self.infcx.tcx.parent_hir_node(upvar_hir_id)
                 {
-                    // Instead of pointing at the path where we access the value within a closure,
-                    // we point at the type on the parameter from the definition of the outer
-                    // function:
-                    //
-                    // error[E0507]: cannot move out of `foo`, a captured
-                    //               variable in an `Fn` closure
-                    //   --> file.rs:14:25
-                    //    |
-                    // 13 | fn do_stuff(foo: Option<Foo>) {
-                    //    |             ---  ----------- move occurs because `foo` has type
-                    //    |             |                `Option<Foo>`, which does not implement
-                    //    |             |                the `Copy` trait
-                    //    |             captured outer variable
-                    // 14 |     require_fn_trait(|| async {
-                    //    |                      -- ^^^^^ `foo` is moved here
-                    //    |                      |
-                    //    |                      captured by this `Fn` closure
-                    // 15 |         if foo.map_or(false, |f| f.foo()) {
-                    //    |            --- variable moved due to use in coroutine
-                    use_span = param.ty_span;
+                    let upvar = &self.upvars[upvar_field.index()];
+                    let upvar_hir_id = upvar.get_root_variable();
+                    use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) {
+                        hir::Node::Param(param) => {
+                            // Instead of pointing at the path where we access the value within a
+                            // closure, we point at the type on the parameter from the definition
+                            // of the outer function:
+                            //
+                            // error[E0507]: cannot move out of `foo`, a captured
+                            //               variable in an `Fn` closure
+                            //   --> file.rs:14:25
+                            //    |
+                            // 13 | fn do_stuff(foo: Option<Foo>) {
+                            //    |             ---  ----------- move occurs because `foo` has type
+                            //    |             |                `Option<Foo>`, which does not
+                            //    |             |                implement the `Copy` trait
+                            //    |             captured outer variable
+                            // 14 |     require_fn_trait(|| async {
+                            //    |                      -- ^^^^^ `foo` is moved here
+                            //    |                      |
+                            //    |                      captured by this `Fn` closure
+                            // 15 |         if foo.map_or(false, |f| f.foo()) {
+                            //    |            --- variable moved due to use in coroutine
+                            param.ty_span
+                        }
+                        hir::Node::LetStmt(stmt) => match (stmt.ty, stmt.init) {
+                            // 13 | fn do_stuff(foo: Option<Foo>) {
+                            // 14 |    let foo: Option<Foo> = foo;
+                            //    |        ---  ----------- move occurs because `foo` has type
+                            //    |        |                `Option<Foo>`, which does not implement
+                            //    |        |                the `Copy` trait
+                            //    |        captured outer variable
+                            (Some(ty), _) => ty.span,
+                            // 13 | fn do_stuff(bar: Option<Foo>) {
+                            // 14 |    let foo = bar;
+                            //    |        ---   --- move occurs because `foo` has type
+                            //    |        |         `Option<Foo>`, which does not implement the
+                            //    |        |         `Copy` trait
+                            //    |        captured outer variable
+                            (None, Some(init)) => init.span,
+                            (None, None) => use_span,
+                        },
+                        _ => use_span,
+                    };
                 }
 
                 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
@@ -656,12 +677,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     span: use_span,
                 });
 
+                let mut pointed_at_span = false;
                 use_spans.args_subdiag(err, |args_span| {
+                    if args_span == span || args_span == use_span {
+                        pointed_at_span = true;
+                    }
                     crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
-                        place: place_desc,
+                        place: place_desc.clone(),
                         args_span,
                     }
                 });
+                if !pointed_at_span && use_span != span {
+                    err.subdiagnostic(crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
+                        place: place_desc,
+                        args_span: span,
+                    });
+                }
 
                 self.add_note_for_packed_struct_derive(err, original_path.local);
             }