diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 989fbb066f7..6b5c1d1a20f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -6,7 +6,7 @@ use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::GeneratorKind; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{LateBoundRegionConversionTime, TyCtxtInferExt}; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::{ AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand, @@ -18,7 +18,10 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; use rustc_span::def_id::LocalDefId; use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::VariantIdx; -use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::{ + type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause, +}; use super::borrow_set::BorrowData; use super::MirBorrowckCtxt; @@ -1131,13 +1134,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place_name, partially_str, loop_message ), ); - let ty = tcx.erase_regions(moved_place.ty(self.body, self.infcx.tcx).ty); + let infcx = tcx.infer_ctxt().build(); + let ty = infcx.freshen(moved_place.ty(self.body, tcx).ty); if let ty::Adt(def, substs) = ty.kind() - && Some(def.did()) == self.infcx.tcx.lang_items().pin_type() + && Some(def.did()) == tcx.lang_items().pin_type() && let ty::Ref(_, _, hir::Mutability::Mut) = substs.type_at(0).kind() - // FIXME: this is a hack because we can't call `can_eq` - && ty.to_string() == - tcx.fn_sig(method_did).input(0).skip_binder().to_string() + && let self_ty = infcx.freshen( + infcx.replace_bound_vars_with_fresh_vars( + fn_call_span, + LateBoundRegionConversionTime::FnCall, + tcx.fn_sig(method_did).input(0), + ) + ) + && infcx.can_eq(self.param_env, ty, self_ty).is_ok() { err.span_suggestion_verbose( fn_call_span.shrink_to_lo(), @@ -1146,28 +1155,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Applicability::MaybeIncorrect, ); } - if let Some(clone_trait) = tcx.lang_items().clone_trait() { - // We can't use `predicate_may_hold` or `can_eq` without ICEs in - // borrowck because of the inference context, so we do a poor-man's - // version here. - for impl_def_id in tcx.all_impls(clone_trait) { - if let Some(def_id) = impl_def_id.as_local() - && let hir_id = tcx.hir().local_def_id_to_hir_id(def_id) - && let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(_), - .. - }) = tcx.hir().get(hir_id) - && tcx.type_of(impl_def_id) == ty - { - err.span_suggestion_verbose( - fn_call_span.shrink_to_lo(), - "you can `clone` the value and consume it, but this might \ - not be your desired behavior", - "clone().".to_string(), - Applicability::MaybeIncorrect, - ); - } - } + if let Some(clone_trait) = tcx.lang_items().clone_trait() + && let trait_ref = tcx.mk_trait_ref(clone_trait, [ty]) + && let o = Obligation::new( + tcx, + ObligationCause::dummy(), + self.param_env, + ty::Binder::dummy(trait_ref), + ) + && infcx.predicate_must_hold_modulo_regions(&o) + { + err.span_suggestion_verbose( + fn_call_span.shrink_to_lo(), + "you can `clone` the value and consume it, but this might not be \ + your desired behavior", + "clone().".to_string(), + Applicability::MaybeIncorrect, + ); } } // Avoid pointing to the same function in multiple different diff --git a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr index ecf5382e863..87135f0bb43 100644 --- a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr @@ -9,6 +9,10 @@ LL | let _x = Rc::new(vec![1, 2]).into_iter(); | note: `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 + | +LL | let _x = Rc::new(vec![1, 2]).clone().into_iter(); + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed new file mode 100644 index 00000000000..b0c5376105b --- /dev/null +++ b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed @@ -0,0 +1,15 @@ +// run-rustfix +// Test that a by-ref `FnMut` closure gets an error when it tries to +// consume a value. + +fn call(f: F) where F : Fn() { + f(); +} + +fn main() { + let y = vec![format!("World")]; + call(|| { + y.clone().into_iter(); + //~^ ERROR cannot move out of `y`, a captured variable in an `Fn` closure + }); +} diff --git a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.rs b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.rs index d54b09c5da9..4666b8a3373 100644 --- a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.rs +++ b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.rs @@ -1,3 +1,4 @@ +// run-rustfix // Test that a by-ref `FnMut` closure gets an error when it tries to // consume a value. diff --git a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index b1367c65218..f033d53bf8e 100644 --- a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `y`, a captured variable in an `Fn` closure - --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:11:9 + --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:12:9 | LL | let y = vec![format!("World")]; | - captured outer variable @@ -12,6 +12,10 @@ LL | y.into_iter(); | note: `into_iter` takes ownership of the receiver `self`, which moves `y` --> $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 + | +LL | y.clone().into_iter(); + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/tab_3.stderr b/src/test/ui/codemap_tests/tab_3.stderr index e0e369124a4..6f87a2a367e 100644 --- a/src/test/ui/codemap_tests/tab_3.stderr +++ b/src/test/ui/codemap_tests/tab_3.stderr @@ -16,6 +16,10 @@ help: consider cloning the value if the performance cost is acceptable | LL | some_vec.clone().into_iter(); | ++++++++ +help: you can `clone` the value and consume it, but this might not be your desired behavior + | +LL | some_vec.clone().into_iter(); + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/moves/move-fn-self-receiver.stderr b/src/test/ui/moves/move-fn-self-receiver.stderr index b3f95ee192a..106fd31a088 100644 --- a/src/test/ui/moves/move-fn-self-receiver.stderr +++ b/src/test/ui/moves/move-fn-self-receiver.stderr @@ -9,6 +9,10 @@ LL | val.0; note: `into_iter` takes ownership of the receiver `self`, which moves `val.0` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL = note: move occurs because `val.0` has type `Vec`, which does not implement the `Copy` trait +help: you can `clone` the value and consume it, but this might not be your desired behavior + | +LL | val.0.clone().into_iter().next(); + | ++++++++ error[E0382]: use of moved value: `foo` --> $DIR/move-fn-self-receiver.rs:34:5 @@ -97,6 +101,10 @@ help: consider cloning the value if the performance cost is acceptable | LL | rc_foo.clone().use_rc_self(); | ++++++++ +help: you can `clone` the value and consume it, but this might not be your desired behavior + | +LL | rc_foo.clone().use_rc_self(); + | ++++++++ error[E0382]: use of moved value: `foo_add` --> $DIR/move-fn-self-receiver.rs:59:5 @@ -140,6 +148,10 @@ help: consider cloning the value if the performance cost is acceptable | LL | for _val in explicit_into_iter.clone().into_iter() {} | ++++++++ +help: you can `clone` the value and consume it, but this might not be your desired behavior + | +LL | for _val in explicit_into_iter.clone().into_iter() {} + | ++++++++ error[E0382]: use of moved value: `container` --> $DIR/move-fn-self-receiver.rs:71:5 diff --git a/src/test/ui/moves/moves-based-on-type-access-to-field.stderr b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr index 0b1a623a013..b51ba16cca1 100644 --- a/src/test/ui/moves/moves-based-on-type-access-to-field.stderr +++ b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr @@ -14,6 +14,10 @@ help: consider cloning the value if the performance cost is acceptable | LL | consume(x.clone().into_iter().next().unwrap()); | ++++++++ +help: you can `clone` the value and consume it, but this might not be your desired behavior + | +LL | consume(x.clone().into_iter().next().unwrap()); + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/moves/moves-based-on-type-exprs.stderr b/src/test/ui/moves/moves-based-on-type-exprs.stderr index ae76889f104..8458d9cad5f 100644 --- a/src/test/ui/moves/moves-based-on-type-exprs.stderr +++ b/src/test/ui/moves/moves-based-on-type-exprs.stderr @@ -166,6 +166,10 @@ help: consider cloning the value if the performance cost is acceptable | LL | let _y = x.clone().into_iter().next().unwrap(); | ++++++++ +help: you can `clone` the value and consume it, but this might not be your desired behavior + | +LL | let _y = x.clone().into_iter().next().unwrap(); + | ++++++++ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:83:11 @@ -183,6 +187,10 @@ help: consider cloning the value if the performance cost is acceptable | LL | let _y = [x.clone().into_iter().next().unwrap(); 1]; | ++++++++ +help: you can `clone` the value and consume it, but this might not be your desired behavior + | +LL | let _y = [x.clone().into_iter().next().unwrap(); 1]; + | ++++++++ error: aborting due to 11 previous errors diff --git a/src/test/ui/suggestions/option-content-move.stderr b/src/test/ui/suggestions/option-content-move.stderr index 3e0271d0257..474a72093c6 100644 --- a/src/test/ui/suggestions/option-content-move.stderr +++ b/src/test/ui/suggestions/option-content-move.stderr @@ -9,6 +9,10 @@ LL | if selection.1.unwrap().contains(selection.0) { | note: `Option::::unwrap` takes ownership of the receiver `self`, which moves `selection.1` --> $SRC_DIR/core/src/option.rs:LL:COL +help: you can `clone` the value and consume it, but this might not be your desired behavior + | +LL | if selection.1.clone().unwrap().contains(selection.0) { + | ++++++++ error[E0507]: cannot move out of `selection.1` which is behind a shared reference --> $DIR/option-content-move.rs:27:20 @@ -21,6 +25,10 @@ LL | if selection.1.unwrap().contains(selection.0) { | note: `Result::::unwrap` takes ownership of the receiver `self`, which moves `selection.1` --> $SRC_DIR/core/src/result.rs:LL:COL +help: you can `clone` the value and consume it, but this might not be your desired behavior + | +LL | if selection.1.clone().unwrap().contains(selection.0) { + | ++++++++ error: aborting due to 2 previous errors