Skip to content

Commit c7f9d74

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 5c63884 commit c7f9d74

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
@@ -282,7 +282,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
282282
Some(name) => format!("`{name}`"),
283283
None => "value".to_owned(),
284284
};
285-
if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, &note_msg) {
285+
if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, &note_msg)
286+
|| if let UseSpans::FnSelfUse { kind, .. } = use_spans
287+
&& let CallKind::FnCall { fn_trait_id, self_ty } = kind
288+
&& let ty::Param(_) = self_ty.kind()
289+
&& ty == self_ty
290+
&& Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait()
291+
{
292+
// this is a type parameter `T: FnOnce()`, don't suggest `T: FnOnce() + Clone`.
293+
true
294+
} else {
295+
false
296+
}
297+
{
286298
// Suppress the next suggestion since we don't want to put more bounds onto
287299
// something that already has `Fn`-like bounds (or is a closure), so we can't
288300
// restrict anyways.
@@ -776,7 +788,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
776788
None
777789
}
778790
})
779-
&& { true }
780791
&& parent_binop == other_parent_binop
781792
{
782793
// 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
@@ -6,6 +6,7 @@ use crate::session_diagnostics::{
66
};
77
use itertools::Itertools;
88
use rustc_errors::{Applicability, Diag};
9+
use rustc_errors::{DiagCtxt, MultiSpan};
910
use rustc_hir as hir;
1011
use rustc_hir::def::{CtorKind, Namespace};
1112
use rustc_hir::CoroutineKind;
@@ -30,6 +31,8 @@ use rustc_trait_selection::infer::InferCtxtExt;
3031
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
3132
use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions;
3233

34+
use crate::fluent_generated as fluent;
35+
3336
use super::borrow_set::BorrowData;
3437
use super::MirBorrowckCtxt;
3538

@@ -589,7 +592,7 @@ impl UseSpans<'_> {
589592
#[allow(rustc::diagnostic_outside_of_impl)]
590593
pub(super) fn args_subdiag(
591594
self,
592-
dcx: &rustc_errors::DiagCtxt,
595+
dcx: &DiagCtxt,
593596
err: &mut Diag<'_>,
594597
f: impl FnOnce(Span) -> CaptureArgLabel,
595598
) {
@@ -603,7 +606,7 @@ impl UseSpans<'_> {
603606
#[allow(rustc::diagnostic_outside_of_impl)]
604607
pub(super) fn var_path_only_subdiag(
605608
self,
606-
dcx: &rustc_errors::DiagCtxt,
609+
dcx: &DiagCtxt,
607610
err: &mut Diag<'_>,
608611
action: crate::InitializationRequiringAction,
609612
) {
@@ -641,7 +644,7 @@ impl UseSpans<'_> {
641644
#[allow(rustc::diagnostic_outside_of_impl)]
642645
pub(super) fn var_subdiag(
643646
self,
644-
dcx: &rustc_errors::DiagCtxt,
647+
dcx: &DiagCtxt,
645648
err: &mut Diag<'_>,
646649
kind: Option<rustc_middle::mir::BorrowKind>,
647650
f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause,
@@ -1036,7 +1039,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
10361039
.map(|n| format!("`{n}`"))
10371040
.unwrap_or_else(|| "value".to_owned());
10381041
match kind {
1039-
CallKind::FnCall { fn_trait_id, .. }
1042+
CallKind::FnCall { fn_trait_id, self_ty }
10401043
if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() =>
10411044
{
10421045
err.subdiagnostic(
@@ -1048,7 +1051,58 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
10481051
is_loop_message,
10491052
},
10501053
);
1051-
err.subdiagnostic(self.dcx(), CaptureReasonNote::FnOnceMoveInCall { var_span });
1054+
if let ty::Param(param_ty) = self_ty.kind()
1055+
&& let generics = self.infcx.tcx.generics_of(self.mir_def_id())
1056+
&& let param = generics.type_param(param_ty, self.infcx.tcx)
1057+
&& let Some(hir_generics) = self
1058+
.infcx
1059+
.tcx
1060+
.typeck_root_def_id(self.mir_def_id().to_def_id())
1061+
.as_local()
1062+
.and_then(|def_id| self.infcx.tcx.hir().get_generics(def_id))
1063+
&& let spans = hir_generics
1064+
.predicates
1065+
.iter()
1066+
.filter_map(|pred| match pred {
1067+
hir::WherePredicate::BoundPredicate(pred) => Some(pred),
1068+
_ => None,
1069+
})
1070+
.filter(|pred| {
1071+
if let Some((id, _)) = pred.bounded_ty.as_generic_param() {
1072+
id == param.def_id
1073+
} else {
1074+
false
1075+
}
1076+
})
1077+
.flat_map(|pred| pred.bounds)
1078+
.filter_map(|bound| {
1079+
if let Some(trait_ref) = bound.trait_ref()
1080+
&& let Some(trait_def_id) = trait_ref.trait_def_id()
1081+
&& trait_def_id == fn_trait_id
1082+
{
1083+
Some(bound.span())
1084+
} else {
1085+
None
1086+
}
1087+
})
1088+
.collect::<Vec<Span>>()
1089+
&& !spans.is_empty()
1090+
{
1091+
let mut span: MultiSpan = spans.clone().into();
1092+
for sp in spans {
1093+
span.push_span_label(sp, fluent::borrowck_moved_a_fn_once_in_call_def);
1094+
}
1095+
span.push_span_label(
1096+
fn_call_span,
1097+
fluent::borrowck_moved_a_fn_once_in_call,
1098+
);
1099+
err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call);
1100+
} else {
1101+
err.subdiagnostic(
1102+
self.dcx(),
1103+
CaptureReasonNote::FnOnceMoveInCall { var_span },
1104+
);
1105+
}
10521106
}
10531107
CallKind::Operator { self_arg, trait_id, .. } => {
10541108
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)