Fix #64008.
let err = FnMutError {
span: *span,
ty_err: match output_ty.kind() {
- ty::Closure(_, _) => FnMutReturnTypeErr::ReturnClosure { span: *span },
ty::Generator(def, ..) if self.infcx.tcx.generator_is_async(*def) => {
FnMutReturnTypeErr::ReturnAsyncBlock { span: *span }
}
+ _ if output_ty.contains_closure() => {
+ FnMutReturnTypeErr::ReturnClosure { span: *span }
+ }
_ => FnMutReturnTypeErr::ReturnRef { span: *span },
},
};
fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) {
let map = self.infcx.tcx.hir();
let body_id = map.body_owned_by(self.mir_def_id());
- let expr = &map.body(body_id).value;
+ let expr = &map.body(body_id).value.peel_blocks();
let mut closure_span = None::<rustc_span::Span>;
match expr.kind {
hir::ExprKind::MethodCall(.., args, _) => {
}
}
}
- hir::ExprKind::Block(blk, _) => {
- if let Some(expr) = blk.expr {
- // only when the block is a closure
- if let hir::ExprKind::Closure(hir::Closure {
- capture_clause: hir::CaptureBy::Ref,
- body,
- ..
- }) = expr.kind
- {
- let body = map.body(*body);
- if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
- closure_span = Some(expr.span.shrink_to_lo());
- }
- }
+ hir::ExprKind::Closure(hir::Closure {
+ capture_clause: hir::CaptureBy::Ref,
+ body,
+ ..
+ }) => {
+ let body = map.body(*body);
+ if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
+ closure_span = Some(expr.span.shrink_to_lo());
}
}
_ => {}
cf.is_break()
}
+ /// Checks whether a type recursively contains any closure
+ ///
+ /// Example: `Option<[closure@file.rs:4:20]>` returns true
+ pub fn contains_closure(self) -> bool {
+ struct ContainsClosureVisitor;
+
+ impl<'tcx> TypeVisitor<'tcx> for ContainsClosureVisitor {
+ type BreakTy = ();
+
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if let ty::Closure(_, _) = t.kind() {
+ ControlFlow::Break(())
+ } else {
+ t.super_visit_with(self)
+ }
+ }
+ }
+
+ let cf = self.visit_with(&mut ContainsClosureVisitor);
+ cf.is_break()
+ }
+
/// Returns the type and mutability of `*ty`.
///
/// The parameter `explicit` indicates if this is an *explicit* dereference.
--- /dev/null
+// run-rustfix
+#![allow(dead_code, path_statements)]
+fn foo1(s: &str) -> impl Iterator<Item = String> + '_ {
+ None.into_iter()
+ .flat_map(move |()| s.chars().map(move |c| format!("{}{}", c, s)))
+ //~^ ERROR captured variable cannot escape `FnMut` closure body
+ //~| HELP consider adding 'move' keyword before the nested closure
+}
+
+fn foo2(s: &str) -> impl Sized + '_ {
+ move |()| s.chars().map(move |c| format!("{}{}", c, s))
+ //~^ ERROR lifetime may not live long enough
+ //~| HELP consider adding 'move' keyword before the nested closure
+}
+
+pub struct X;
+pub fn foo3<'a>(
+ bar: &'a X,
+) -> impl Iterator<Item = ()> + 'a {
+ Some(()).iter().flat_map(move |()| {
+ Some(()).iter().map(move |()| { bar; }) //~ ERROR captured variable cannot escape
+ //~^ HELP consider adding 'move' keyword before the nested closure
+ })
+}
+
+fn main() {}
+// run-rustfix
+#![allow(dead_code, path_statements)]
fn foo1(s: &str) -> impl Iterator<Item = String> + '_ {
None.into_iter()
.flat_map(move |()| s.chars().map(|c| format!("{}{}", c, s)))
//~| HELP consider adding 'move' keyword before the nested closure
}
+pub struct X;
+pub fn foo3<'a>(
+ bar: &'a X,
+) -> impl Iterator<Item = ()> + 'a {
+ Some(()).iter().flat_map(move |()| {
+ Some(()).iter().map(|()| { bar; }) //~ ERROR captured variable cannot escape
+ //~^ HELP consider adding 'move' keyword before the nested closure
+ })
+}
+
fn main() {}
error: captured variable cannot escape `FnMut` closure body
- --> $DIR/issue-95079-missing-move-in-nested-closure.rs:3:29
+ --> $DIR/issue-95079-missing-move-in-nested-closure.rs:5:29
|
LL | fn foo1(s: &str) -> impl Iterator<Item = String> + '_ {
| - variable defined here
LL | .flat_map(move |()| s.chars().map(|c| format!("{}{}", c, s)))
| - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | |
- | | returns a reference to a captured variable which escapes the closure body
+ | | returns a closure that contains a reference to a captured variable, which then escapes the closure body
| | variable captured here
| inferred to be a `FnMut` closure
|
| ++++
error: lifetime may not live long enough
- --> $DIR/issue-95079-missing-move-in-nested-closure.rs:9:15
+ --> $DIR/issue-95079-missing-move-in-nested-closure.rs:11:15
|
LL | move |()| s.chars().map(|c| format!("{}{}", c, s))
| --------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
- | | return type of closure `Map<Chars<'_>, [closure@$DIR/issue-95079-missing-move-in-nested-closure.rs:9:29: 9:32]>` contains a lifetime `'2`
+ | | return type of closure `Map<Chars<'_>, [closure@$DIR/issue-95079-missing-move-in-nested-closure.rs:11:29: 11:32]>` contains a lifetime `'2`
| lifetime `'1` represents this closure's body
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
LL | move |()| s.chars().map(move |c| format!("{}{}", c, s))
| ++++
-error: aborting due to 2 previous errors
+error: captured variable cannot escape `FnMut` closure body
+ --> $DIR/issue-95079-missing-move-in-nested-closure.rs:21:9
+ |
+LL | bar: &'a X,
+ | --- variable defined here
+LL | ) -> impl Iterator<Item = ()> + 'a {
+LL | Some(()).iter().flat_map(move |()| {
+ | - inferred to be a `FnMut` closure
+LL | Some(()).iter().map(|()| { bar; })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^^
+ | | |
+ | | variable captured here
+ | returns a closure that contains a reference to a captured variable, which then escapes the closure body
+ |
+ = note: `FnMut` closures only have access to their captured variables while they are executing...
+ = note: ...therefore, they cannot allow references to captured variables to escape
+help: consider adding 'move' keyword before the nested closure
+ |
+LL | Some(()).iter().map(move |()| { bar; })
+ | ++++
+
+error: aborting due to 3 previous errors