Skip to content

Commit 2484e18

Browse files
committed
Add support for method calls
1 parent 1aa5668 commit 2484e18

File tree

3 files changed

+725
-55
lines changed

3 files changed

+725
-55
lines changed

compiler/rustc_borrowck/src/diagnostics/move_errors.rs

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_middle::bug;
99
use rustc_middle::mir::*;
1010
use rustc_middle::ty::{self, Ty, TyCtxt};
1111
use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
12+
use rustc_span::def_id::DefId;
1213
use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span};
1314
use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
1415
use rustc_trait_selection::infer::InferCtxtExt;
@@ -507,38 +508,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
507508
);
508509

509510
let closure_span = tcx.def_span(def_id);
510-
let mut clause_span = DUMMY_SP;
511-
let typck_result = self.infcx.tcx.typeck(self.mir_def_id());
512-
if let Some(closure_def_id) = def_id.as_local()
513-
&& let hir::Node::Expr(expr) = tcx.hir_node_by_def_id(closure_def_id)
514-
&& let hir::Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id)
515-
&& let hir::ExprKind::Call(callee, _) = parent.kind
516-
&& let Some(ty) = typck_result.node_type_opt(callee.hir_id)
517-
&& let ty::FnDef(fn_def_id, args) = ty.kind()
518-
&& let predicates = tcx.predicates_of(fn_def_id).instantiate(tcx, args)
519-
&& let Some((_, span)) =
520-
predicates.predicates.iter().zip(predicates.spans.iter()).find(
521-
|(pred, _)| match pred.as_trait_clause() {
522-
Some(clause)
523-
if let ty::Closure(clause_closure_def_id, _) =
524-
clause.self_ty().skip_binder().kind()
525-
&& clause_closure_def_id == def_id
526-
&& (tcx.lang_items().fn_mut_trait()
527-
== Some(clause.def_id())
528-
|| tcx.lang_items().fn_trait()
529-
== Some(clause.def_id())) =>
530-
{
531-
// Found `<TyOfCapturingClosure as FnMut>`
532-
true
533-
}
534-
_ => false,
535-
},
536-
)
537-
{
538-
// We point at the `Fn()` or `FnMut()` bound that coerced the closure, which
539-
// could be changed to `FnOnce()` to avoid the move error.
540-
clause_span = *span;
541-
}
542511

543512
self.cannot_move_out_of(span, &place_description)
544513
.with_span_label(upvar_span, "captured outer variable")
@@ -547,7 +516,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
547516
format!("captured by this `{closure_kind}` closure"),
548517
)
549518
.with_span_help(
550-
clause_span,
519+
self.get_closure_bound_clause_span(*def_id),
551520
"`Fn` and `FnMut` closures require captured values to be able to be \
552521
consumed multiple times, but an `FnOnce` consume them only once",
553522
)
@@ -599,6 +568,45 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
599568
err
600569
}
601570

571+
fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span {
572+
let tcx = self.infcx.tcx;
573+
let typeck_result = tcx.typeck(self.mir_def_id());
574+
let Some(closure_def_id) = def_id.as_local() else { return DUMMY_SP };
575+
let hir::Node::Expr(expr) = tcx.hir_node_by_def_id(closure_def_id) else { return DUMMY_SP };
576+
let hir::Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) else { return DUMMY_SP };
577+
let predicates = match parent.kind {
578+
hir::ExprKind::Call(callee, _) => {
579+
let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP };
580+
let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP };
581+
tcx.predicates_of(fn_def_id).instantiate(tcx, args)
582+
}
583+
hir::ExprKind::MethodCall(..) => {
584+
let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else {
585+
return DUMMY_SP;
586+
};
587+
let args = typeck_result.node_args(parent.hir_id);
588+
tcx.predicates_of(method).instantiate(tcx, args)
589+
}
590+
_ => return DUMMY_SP,
591+
};
592+
for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) {
593+
tracing::info!(?pred);
594+
tracing::info!(?span);
595+
if let Some(clause) = pred.as_trait_clause()
596+
&& let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind()
597+
&& *clause_closure_def_id == def_id
598+
&& (tcx.lang_items().fn_mut_trait() == Some(clause.def_id())
599+
|| tcx.lang_items().fn_trait() == Some(clause.def_id()))
600+
{
601+
// Found `<TyOfCapturingClosure as FnMut>`
602+
// We point at the `Fn()` or `FnMut()` bound that coerced the closure, which
603+
// could be changed to `FnOnce()` to avoid the move error.
604+
return *span;
605+
}
606+
}
607+
DUMMY_SP
608+
}
609+
602610
fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {
603611
match error {
604612
GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {

tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,32 @@ fn consume_fnmut<F: FnMut()>(_f: F) { }
3535
//~| HELP `Fn` and `FnMut` closures
3636
//~| HELP `Fn` and `FnMut` closures
3737

38+
trait T {
39+
fn consume_fn<F: Fn()>(_f: F) { }
40+
//~^ HELP `Fn` and `FnMut` closures
41+
//~| HELP `Fn` and `FnMut` closures
42+
//~| HELP `Fn` and `FnMut` closures
43+
//~| HELP `Fn` and `FnMut` closures
44+
//~| HELP `Fn` and `FnMut` closures
45+
//~| HELP `Fn` and `FnMut` closures
46+
//~| HELP `Fn` and `FnMut` closures
47+
//~| HELP `Fn` and `FnMut` closures
48+
//~| HELP `Fn` and `FnMut` closures
49+
//~| HELP `Fn` and `FnMut` closures
50+
fn method_consume_fn<F: Fn()>(&self, _f: F) { }
51+
//~^ HELP `Fn` and `FnMut` closures
52+
//~| HELP `Fn` and `FnMut` closures
53+
//~| HELP `Fn` and `FnMut` closures
54+
//~| HELP `Fn` and `FnMut` closures
55+
//~| HELP `Fn` and `FnMut` closures
56+
//~| HELP `Fn` and `FnMut` closures
57+
//~| HELP `Fn` and `FnMut` closures
58+
//~| HELP `Fn` and `FnMut` closures
59+
//~| HELP `Fn` and `FnMut` closures
60+
//~| HELP `Fn` and `FnMut` closures
61+
}
62+
impl T for () {}
63+
3864
pub fn main() { }
3965

4066
fn move_into_fn() {
@@ -94,6 +120,120 @@ fn move_into_fn() {
94120
});
95121
}
96122

123+
fn move_into_assoc_fn() {
124+
let e = Either::One(X(Y));
125+
let mut em = Either::One(X(Y));
126+
127+
let x = X(Y);
128+
129+
// move into Fn
130+
131+
<() as T>::consume_fn(|| {
132+
let X(_t) = x;
133+
//~^ ERROR cannot move
134+
//~| HELP consider borrowing here
135+
if let Either::One(_t) = e { }
136+
//~^ ERROR cannot move
137+
//~| HELP consider borrowing here
138+
while let Either::One(_t) = e { }
139+
//~^ ERROR cannot move
140+
//~| HELP consider borrowing here
141+
match e {
142+
//~^ ERROR cannot move
143+
//~| HELP consider borrowing here
144+
Either::One(_t)
145+
| Either::Two(_t) => (),
146+
}
147+
match e {
148+
//~^ ERROR cannot move
149+
//~| HELP consider borrowing here
150+
Either::One(_t) => (),
151+
Either::Two(ref _t) => (),
152+
// FIXME: should suggest removing `ref` too
153+
}
154+
155+
let X(mut _t) = x;
156+
//~^ ERROR cannot move
157+
//~| HELP consider borrowing here
158+
if let Either::One(mut _t) = em { }
159+
//~^ ERROR cannot move
160+
//~| HELP consider borrowing here
161+
while let Either::One(mut _t) = em { }
162+
//~^ ERROR cannot move
163+
//~| HELP consider borrowing here
164+
match em {
165+
//~^ ERROR cannot move
166+
//~| HELP consider borrowing here
167+
Either::One(mut _t)
168+
| Either::Two(mut _t) => (),
169+
}
170+
match em {
171+
//~^ ERROR cannot move
172+
//~| HELP consider borrowing here
173+
Either::One(mut _t) => (),
174+
Either::Two(ref _t) => (),
175+
// FIXME: should suggest removing `ref` too
176+
}
177+
});
178+
}
179+
180+
fn move_into_method() {
181+
let e = Either::One(X(Y));
182+
let mut em = Either::One(X(Y));
183+
184+
let x = X(Y);
185+
186+
// move into Fn
187+
188+
().method_consume_fn(|| {
189+
let X(_t) = x;
190+
//~^ ERROR cannot move
191+
//~| HELP consider borrowing here
192+
if let Either::One(_t) = e { }
193+
//~^ ERROR cannot move
194+
//~| HELP consider borrowing here
195+
while let Either::One(_t) = e { }
196+
//~^ ERROR cannot move
197+
//~| HELP consider borrowing here
198+
match e {
199+
//~^ ERROR cannot move
200+
//~| HELP consider borrowing here
201+
Either::One(_t)
202+
| Either::Two(_t) => (),
203+
}
204+
match e {
205+
//~^ ERROR cannot move
206+
//~| HELP consider borrowing here
207+
Either::One(_t) => (),
208+
Either::Two(ref _t) => (),
209+
// FIXME: should suggest removing `ref` too
210+
}
211+
212+
let X(mut _t) = x;
213+
//~^ ERROR cannot move
214+
//~| HELP consider borrowing here
215+
if let Either::One(mut _t) = em { }
216+
//~^ ERROR cannot move
217+
//~| HELP consider borrowing here
218+
while let Either::One(mut _t) = em { }
219+
//~^ ERROR cannot move
220+
//~| HELP consider borrowing here
221+
match em {
222+
//~^ ERROR cannot move
223+
//~| HELP consider borrowing here
224+
Either::One(mut _t)
225+
| Either::Two(mut _t) => (),
226+
}
227+
match em {
228+
//~^ ERROR cannot move
229+
//~| HELP consider borrowing here
230+
Either::One(mut _t) => (),
231+
Either::Two(ref _t) => (),
232+
// FIXME: should suggest removing `ref` too
233+
}
234+
});
235+
}
236+
97237
fn move_into_fnmut() {
98238
let e = Either::One(X(Y));
99239
let mut em = Either::One(X(Y));

0 commit comments

Comments
 (0)