Skip to content

Commit 4ca876b

Browse files
committed
Better account for FnOnce in move errors
``` error[E0382]: use of moved value: `blk` --> $DIR/once-cant-call-twice-on-heap.rs:8:5 | LL | fn foo<F:FnOnce()>(blk: F) { | --- move occurs because `blk` has type `F`, which does not implement the `Copy` trait LL | blk(); | ----- `blk` moved due to this call LL | blk(); | ^^^ value used here after move | note: `FnOnce` closures can only be called once --> $DIR/once-cant-call-twice-on-heap.rs:6:10 | LL | fn foo<F:FnOnce()>(blk: F) { | ^^^^^^^^ `F` is made to be an `FnOnce` closure here LL | blk(); | ----- this value implements `FnOnce`, which causes it to be moved when called ```
1 parent dfe28de commit 4ca876b

File tree

5 files changed

+88
-21
lines changed

5 files changed

+88
-21
lines changed

compiler/rustc_borrowck/messages.ftl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ borrowck_move_unsized =
8787
borrowck_moved_a_fn_once_in_call =
8888
this value implements `FnOnce`, which causes it to be moved when called
8989
90+
borrowck_moved_a_fn_once_in_call_call =
91+
`FnOnce` closures can only be called once
92+
93+
borrowck_moved_a_fn_once_in_call_def =
94+
`{$ty}` is made to be an `FnOnce` closure here
95+
9096
borrowck_moved_due_to_await =
9197
{$place_name} {$is_partial ->
9298
[true] partially moved

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
283283
Some(name) => format!("`{name}`"),
284284
None => "value".to_owned(),
285285
};
286-
if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, &note_msg) {
286+
if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, &note_msg)
287+
|| if let UseSpans::FnSelfUse { kind, .. } = use_spans
288+
&& let CallKind::FnCall { fn_trait_id, self_ty } = kind
289+
&& let ty::Param(_) = self_ty.kind()
290+
&& ty == self_ty
291+
&& Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait()
292+
{
293+
// this is a type parameter `T: FnOnce()`, don't suggest `T: FnOnce() + Clone`.
294+
true
295+
} else {
296+
false
297+
}
298+
{
287299
// Suppress the next suggestion since we don't want to put more bounds onto
288300
// something that already has `Fn`-like bounds (or is a closure), so we can't
289301
// restrict anyways.
@@ -1016,7 +1028,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
10161028
None
10171029
}
10181030
})
1019-
&& { true }
10201031
&& parent_binop == other_parent_binop
10211032
{
10221033
// Explicitly look for `expr += other_expr;` and avoid suggesting

compiler/rustc_borrowck/src/diagnostics/mod.rs

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::session_diagnostics::{
55
CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
66
};
77
use rustc_errors::{Applicability, Diag};
8+
use rustc_errors::{DiagCtxt, MultiSpan};
89
use rustc_hir as hir;
910
use rustc_hir::def::{CtorKind, Namespace};
1011
use rustc_hir::CoroutineKind;
@@ -29,6 +30,8 @@ use rustc_trait_selection::infer::InferCtxtExt;
2930
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
3031
use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions;
3132

33+
use crate::fluent_generated as fluent;
34+
3235
use super::borrow_set::BorrowData;
3336
use super::MirBorrowckCtxt;
3437

@@ -587,7 +590,7 @@ impl UseSpans<'_> {
587590
#[allow(rustc::diagnostic_outside_of_impl)]
588591
pub(super) fn args_subdiag(
589592
self,
590-
dcx: &rustc_errors::DiagCtxt,
593+
dcx: &DiagCtxt,
591594
err: &mut Diag<'_>,
592595
f: impl FnOnce(Span) -> CaptureArgLabel,
593596
) {
@@ -601,7 +604,7 @@ impl UseSpans<'_> {
601604
#[allow(rustc::diagnostic_outside_of_impl)]
602605
pub(super) fn var_path_only_subdiag(
603606
self,
604-
dcx: &rustc_errors::DiagCtxt,
607+
dcx: &DiagCtxt,
605608
err: &mut Diag<'_>,
606609
action: crate::InitializationRequiringAction,
607610
) {
@@ -639,7 +642,7 @@ impl UseSpans<'_> {
639642
#[allow(rustc::diagnostic_outside_of_impl)]
640643
pub(super) fn var_subdiag(
641644
self,
642-
dcx: &rustc_errors::DiagCtxt,
645+
dcx: &DiagCtxt,
643646
err: &mut Diag<'_>,
644647
kind: Option<rustc_middle::mir::BorrowKind>,
645648
f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause,
@@ -1034,7 +1037,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
10341037
.map(|n| format!("`{n}`"))
10351038
.unwrap_or_else(|| "value".to_owned());
10361039
match kind {
1037-
CallKind::FnCall { fn_trait_id, .. }
1040+
CallKind::FnCall { fn_trait_id, self_ty }
10381041
if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() =>
10391042
{
10401043
err.subdiagnostic(
@@ -1046,7 +1049,58 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
10461049
is_loop_message,
10471050
},
10481051
);
1049-
err.subdiagnostic(self.dcx(), CaptureReasonNote::FnOnceMoveInCall { var_span });
1052+
if let ty::Param(param_ty) = self_ty.kind()
1053+
&& let generics = self.infcx.tcx.generics_of(self.mir_def_id())
1054+
&& let param = generics.type_param(param_ty, self.infcx.tcx)
1055+
&& let Some(hir_generics) = self
1056+
.infcx
1057+
.tcx
1058+
.typeck_root_def_id(self.mir_def_id().to_def_id())
1059+
.as_local()
1060+
.and_then(|def_id| self.infcx.tcx.hir().get_generics(def_id))
1061+
&& let spans = hir_generics
1062+
.predicates
1063+
.iter()
1064+
.filter_map(|pred| match pred {
1065+
hir::WherePredicate::BoundPredicate(pred) => Some(pred),
1066+
_ => None,
1067+
})
1068+
.filter(|pred| {
1069+
if let Some((id, _)) = pred.bounded_ty.as_generic_param() {
1070+
id == param.def_id
1071+
} else {
1072+
false
1073+
}
1074+
})
1075+
.flat_map(|pred| pred.bounds)
1076+
.filter_map(|bound| {
1077+
if let Some(trait_ref) = bound.trait_ref()
1078+
&& let Some(trait_def_id) = trait_ref.trait_def_id()
1079+
&& trait_def_id == fn_trait_id
1080+
{
1081+
Some(bound.span())
1082+
} else {
1083+
None
1084+
}
1085+
})
1086+
.collect::<Vec<Span>>()
1087+
&& !spans.is_empty()
1088+
{
1089+
let mut span: MultiSpan = spans.clone().into();
1090+
for sp in spans {
1091+
span.push_span_label(sp, fluent::borrowck_moved_a_fn_once_in_call_def);
1092+
}
1093+
span.push_span_label(
1094+
fn_call_span,
1095+
fluent::borrowck_moved_a_fn_once_in_call,
1096+
);
1097+
err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call);
1098+
} else {
1099+
err.subdiagnostic(
1100+
self.dcx(),
1101+
CaptureReasonNote::FnOnceMoveInCall { var_span },
1102+
);
1103+
}
10501104
}
10511105
CallKind::Operator { self_arg, trait_id, .. } => {
10521106
let self_arg = self_arg.unwrap();

tests/ui/borrowck/borrowck-unboxed-closures.stderr

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,13 @@ LL | f(1, 2);
2929
LL | f(1, 2);
3030
| ^ value used here after move
3131
|
32-
note: this value implements `FnOnce`, which causes it to be moved when called
33-
--> $DIR/borrowck-unboxed-closures.rs:11:5
32+
note: `FnOnce` closures can only be called once
33+
--> $DIR/borrowck-unboxed-closures.rs:10:8
3434
|
35+
LL | fn c<F:FnOnce(isize, isize) -> isize>(f: F) {
36+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `F` is made to be an `FnOnce` closure here
3537
LL | f(1, 2);
36-
| ^
37-
help: consider further restricting this bound
38-
|
39-
LL | fn c<F:FnOnce(isize, isize) -> isize + Copy>(f: F) {
40-
| ++++++
38+
| ------- this value implements `FnOnce`, which causes it to be moved when called
4139

4240
error: aborting due to 3 previous errors
4341

tests/ui/once-cant-call-twice-on-heap.stderr

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,13 @@ LL | blk();
88
LL | blk();
99
| ^^^ value used here after move
1010
|
11-
note: this value implements `FnOnce`, which causes it to be moved when called
12-
--> $DIR/once-cant-call-twice-on-heap.rs:7:5
11+
note: `FnOnce` closures can only be called once
12+
--> $DIR/once-cant-call-twice-on-heap.rs:6:10
1313
|
14+
LL | fn foo<F:FnOnce()>(blk: F) {
15+
| ^^^^^^^^ `F` is made to be an `FnOnce` closure here
1416
LL | blk();
15-
| ^^^
16-
help: consider further restricting this bound
17-
|
18-
LL | fn foo<F:FnOnce() + Copy>(blk: F) {
19-
| ++++++
17+
| ----- this value implements `FnOnce`, which causes it to be moved when called
2018

2119
error: aborting due to 1 previous error
2220

0 commit comments

Comments
 (0)