Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 73 additions & 37 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use rustc_middle::mir::{
LocalKind, Location, Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement,
StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, find_self_call,
};
use rustc_middle::ty::print::Print;
use rustc_middle::ty::print::{Print, with_no_trimmed_paths};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex};
Expand Down Expand Up @@ -1374,12 +1374,21 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
&move_spans,
);

let func = tcx.def_path_str(method_did);
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
func,
place_name: place_name.clone(),
span: self_arg.span,
});
let func = with_no_trimmed_paths!(tcx.def_path_str(method_did));
if let Some((kind, _)) = desugaring {
err.subdiagnostic(CaptureReasonNote::DesugaringFuncTakeSelf {
func,
desugar_name: kind.name(),
place_name: place_name.clone(),
span: self_arg.span,
});
} else {
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
func,
place_name: place_name.clone(),
span: self_arg.span,
});
}
}
let parent_did = tcx.parent(method_did);
let parent_self_ty =
Expand All @@ -1400,30 +1409,46 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
var_span: var_span.shrink_to_hi(),
});
}
if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
if let Some((
kind @ (CallDesugaringKind::ForLoopIntoIter
| CallDesugaringKind::ForLoopIntoAsyncIter),
_,
)) = desugaring
{
let ty = moved_place.ty(self.body, tcx).ty;
let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
Some(def_id) => type_known_to_meet_bound_modulo_regions(
self.infcx,
self.infcx.param_env,
Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty),
def_id,
),
_ => false,
};
let def_id = kind.trait_def_id(tcx);
let suggest = type_known_to_meet_bound_modulo_regions(
self.infcx,
self.infcx.param_env,
Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty),
def_id,
);
if suggest {
err.subdiagnostic(CaptureReasonSuggest::IterateSlice {
ty,
span: move_span.shrink_to_lo(),
});
}

err.subdiagnostic(CaptureReasonLabel::ImplicitCall {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
match kind {
CallDesugaringKind::ForLoopIntoIter => {
err.subdiagnostic(CaptureReasonLabel::ImplicitCall {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
}
CallDesugaringKind::ForLoopIntoAsyncIter => {
err.subdiagnostic(CaptureReasonLabel::ImplicitAsyncCall {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
}
_ => {}
}
// If the moved place was a `&mut` ref, then we can
// suggest to reborrow it where it was moved, so it
// will still be valid by the time we get to the usage.
Expand Down Expand Up @@ -1451,20 +1476,31 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
}
}
} else {
if let Some((CallDesugaringKind::Await, _)) = desugaring {
err.subdiagnostic(CaptureReasonLabel::Await {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
} else {
err.subdiagnostic(CaptureReasonLabel::MethodCall {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
match desugaring {
Some((CallDesugaringKind::Await, _)) => {
err.subdiagnostic(CaptureReasonLabel::Await {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
}
Some((CallDesugaringKind::QuestionBranch, _)) => {
err.subdiagnostic(CaptureReasonLabel::QuestionMark {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
}
_ => {
err.subdiagnostic(CaptureReasonLabel::MethodCall {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
}
}
// Erase and shadow everything that could be passed to the new infcx.
let ty = moved_place.ty(self.body, tcx).ty;
Expand Down
43 changes: 43 additions & 0 deletions compiler/rustc_borrowck/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,22 @@ pub(crate) enum CaptureReasonLabel<'a> {
is_partial: bool,
is_loop_message: bool,
},
#[label(
"{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to the question mark {$is_loop_message ->
[true] operator, in previous iteration of loop
*[false] operator
}"
)]
QuestionMark {
#[primary_span]
fn_call_span: Span,
place_name: &'a str,
is_partial: bool,
is_loop_message: bool,
},
#[label(
"{$place_name} {$is_partial ->
[true] partially moved
Expand All @@ -405,6 +421,22 @@ pub(crate) enum CaptureReasonLabel<'a> {
is_partial: bool,
is_loop_message: bool,
},
#[label(
"{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to this implicit call to {$is_loop_message ->
[true] `.into_async_iter()`, in previous iteration of loop
*[false] `.into_async_iter()`
}"
)]
ImplicitAsyncCall {
#[primary_span]
fn_call_span: Span,
place_name: &'a str,
is_partial: bool,
is_loop_message: bool,
},
#[label(
"{$place_name} {$is_partial ->
[true] partially moved
Expand Down Expand Up @@ -498,6 +530,17 @@ pub(crate) enum CaptureReasonNote {
#[primary_span]
span: Span,
},
#[note(
"the {$desugar_name} is desugared into a call to `{$func}`, which takes ownership of the \
receiver `self`, which moves {$place_name}"
)]
DesugaringFuncTakeSelf {
desugar_name: &'static str,
func: String,
place_name: String,
#[primary_span]
span: Span,
},
}

#[derive(Subdiagnostic)]
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,9 @@ fn build_error_for_const_call<'tcx>(
// Don't point at the trait if this is a desugaring...
// FIXME(const_trait_impl): we could perhaps do this for `Iterator`.
match kind {
CallDesugaringKind::ForLoopIntoIter | CallDesugaringKind::ForLoopNext => {
CallDesugaringKind::ForLoopIntoIter
| CallDesugaringKind::ForLoopIntoAsyncIter
| CallDesugaringKind::ForLoopNext => {
error!(NonConstForLoopIntoIter)
}
CallDesugaringKind::QuestionBranch => {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ symbols! {
Input,
Int,
Into,
IntoAsyncIterator,
IntoFuture,
IntoIterator,
IntoIteratorItem,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use crate::traits::specialization_graph;
pub enum CallDesugaringKind {
/// for _ in x {} calls x.into_iter()
ForLoopIntoIter,
/// for await _ in x {} calls x.into_async_iter()
ForLoopIntoAsyncIter,
/// for _ in x {} calls iter.next()
ForLoopNext,
/// x? calls x.branch()
Expand All @@ -30,9 +32,22 @@ pub enum CallDesugaringKind {
}

impl CallDesugaringKind {
pub fn name(&self) -> &'static str {
match self {
CallDesugaringKind::ForLoopIntoIter => "`for` loop",
CallDesugaringKind::ForLoopIntoAsyncIter => "`for await` loop",
CallDesugaringKind::ForLoopNext => "`for` loop",
CallDesugaringKind::QuestionBranch => "question mark operator",
CallDesugaringKind::QuestionFromResidual => "question mark operator",
CallDesugaringKind::TryBlockFromOutput => "try block",
CallDesugaringKind::Await => "`await`",
}
}

pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId {
match self {
Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(),
Self::ForLoopIntoAsyncIter => tcx.get_diagnostic_item(sym::IntoAsyncIterator).unwrap(),
Self::ForLoopNext => tcx.require_lang_item(LangItem::Iterator, DUMMY_SP),
Self::QuestionBranch | Self::TryBlockFromOutput => {
tcx.require_lang_item(LangItem::Try, DUMMY_SP)
Expand Down Expand Up @@ -136,6 +151,10 @@ pub fn call_kind<'tcx>(
&& fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
{
Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0)))
} else if tcx.is_lang_item(method_did, LangItem::IntoAsyncIterIntoIter)
&& fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
{
Some((CallDesugaringKind::ForLoopIntoAsyncIter, method_args.type_at(0)))
} else if tcx.is_lang_item(method_did, LangItem::IteratorNext)
&& fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
{
Expand Down
1 change: 1 addition & 0 deletions library/core/src/async_iter/async_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ impl<T> Poll<Option<T>> {

/// Converts something into an async iterator
#[unstable(feature = "async_iterator", issue = "79024")]
#[rustc_diagnostic_item = "IntoAsyncIterator"]
pub trait IntoAsyncIterator {
/// The type of the item yielded by the iterator
type Item;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LL | x().await;
LL | x().await;
| ^ value used here after move
|
note: `async_call_once` takes ownership of the receiver `self`, which moves `x`
note: `std::ops::AsyncFnOnce::async_call_once` takes ownership of the receiver `self`, which moves `x`
--> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
help: you could `clone` the value and consume it, if the `NoCopy: Clone` trait bound could be satisfied
|
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/async-await/clone-suggestion.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ LL | f.await;
LL | f.await;
| ^ value used here after move
|
note: `into_future` takes ownership of the receiver `self`, which moves `f`
note: the `await` is desugared into a call to `std::future::IntoFuture::into_future`, which takes ownership of the receiver `self`, which moves `f`
--> $SRC_DIR/core/src/future/into_future.rs:LL:COL
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
Expand Down
8 changes: 2 additions & 6 deletions tests/ui/async-await/for-await-consumes-iter.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@ LL | let iter = core::async_iter::from_iter(0..3);
| ---- move occurs because `iter` has type `FromIter<std::ops::Range<i32>>`, which does not implement the `Copy` trait
LL | let mut count = 0;
LL | for await i in iter {
| ---- `iter` moved due to this method call
| ---- `iter` moved due to this implicit call to `.into_async_iter()`
...
LL | for await i in iter {
| ^^^^ value used here after move
|
note: `into_async_iter` takes ownership of the receiver `self`, which moves `iter`
note: the `for await` loop is desugared into a call to `std::async_iter::IntoAsyncIterator::into_async_iter`, which takes ownership of the receiver `self`, which moves `iter`
--> $SRC_DIR/core/src/async_iter/async_iter.rs:LL:COL
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
LL | for await i in iter.clone() {
| ++++++++

error: aborting due to 1 previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LL | for l in bad_letters {
LL | bad_letters.push('s');
| ^^^^^^^^^^^ value borrowed here after move
|
note: `into_iter` takes ownership of the receiver `self`, which moves `bad_letters`
note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `bad_letters`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider iterating over a slice of the `Vec<char>`'s content to avoid moving into the `for` loop
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | let _x = Rc::new(vec![1, 2]).into_iter();
| |
| move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait
|
note: `into_iter` takes ownership of the receiver `self`, which moves value
note: `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves value
--> $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
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume
|
LL | pub fn in_fn<F: Fn() -> Result<(), ()>>(f: F) -> Result<(), ()> {
| ^^^^^^^^^^^^^^^^^^^^^^
note: `into_iter` takes ownership of the receiver `self`, which moves `foos`
note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `foos`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL

error[E0507]: cannot move out of `foos`, a captured variable in an `FnMut` closure
Expand All @@ -41,7 +41,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume
|
LL | pub fn in_fn_mut<F: FnMut() -> Result<(), ()>>(mut f: F) -> Result<(), ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: `into_iter` takes ownership of the receiver `self`, which moves `foos`
note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `foos`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider creating a fresh reborrow of `foos` here
|
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/borrowck/issue-83760.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ LL | foo = Some(Struct);
LL | let _y = foo;
| ^^^ value used here after move
|
note: `Option::<T>::unwrap` takes ownership of the receiver `self`, which moves `foo`
note: `std::option::Option::<T>::unwrap` takes ownership of the receiver `self`, which moves `foo`
--> $SRC_DIR/core/src/option.rs:LL:COL
help: you could `clone` the value and consume it, if the `Struct: Clone` trait bound could be satisfied
|
Expand Down Expand Up @@ -61,7 +61,7 @@ LL | foo = Some(Struct2);
LL | } else if true {
LL | foo = Some(Struct2);
| ^^^^^^^^^^^^^^^^^^^
note: `Option::<T>::unwrap` takes ownership of the receiver `self`, which moves `foo`
note: `std::option::Option::<T>::unwrap` takes ownership of the receiver `self`, which moves `foo`
--> $SRC_DIR/core/src/option.rs:LL:COL
help: you could `clone` the value and consume it, if the `Struct2: Clone` trait bound could be satisfied
|
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/borrowck/issue-83924.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ LL | for n in v {
LL | for n in v {
| ^ value used here after move
|
note: `into_iter` takes ownership of the receiver `self`, which moves `v`
note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `v`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
help: consider creating a fresh reborrow of `v` here
|
Expand Down
17 changes: 17 additions & 0 deletions tests/ui/borrowck/moved-into-question-mark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// https://github.com/rust-lang/rust/issues/89567
use std::fs;
use std::io;

fn main() -> io::Result<()> {
for entry in fs::read_dir(".")? {
//~^ NOTE move occurs because `entry` has type `Result
let file_type = entry?.file_type()?;
//~^ NOTE `entry` moved due to the question mark operator
if file_type.is_dir() {
dbg!(entry?.file_name()); //~ ERROR use of moved value
//~^ NOTE value used here after move
//~| NOTE the question mark operator is desugared into a call to `std::ops::Try::branch`
}
}
Ok(())
}
Loading
Loading