diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2025-07-20 02:15:45 +0000 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2025-07-21 16:21:23 +0000 |
| commit | 5082e6a300974459aec6dc73e76cc039c3a517c1 (patch) | |
| tree | c994ff732c6327c890a149009fddc40f041dccce /compiler | |
| parent | 690ae523e5c0ac9602db7bbe2b8094ee422e4d0f (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.rs | 79 |
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); } |
