diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 9d1f409d69c..01fa3a50d81 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -714,22 +714,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let mut_substs = self.tcx.mk_substs_trait(mut_borrowed_found_ty, &[]); // Try to apply the original trait binding obligation by borrowing. - let mut try_borrowing = |new_trait_ref: ty::TraitRef<'tcx>, + let mut try_borrowing = |new_imm_trait_ref: ty::TraitRef<'tcx>, + new_mut_trait_ref: ty::TraitRef<'tcx>, expected_trait_ref: ty::TraitRef<'tcx>, - mtbl: bool, blacklist: &[DefId]| -> bool { if blacklist.contains(&expected_trait_ref.def_id) { return false; } - let new_obligation = Obligation::new( + let imm_result = self.predicate_must_hold_modulo_regions(&Obligation::new( ObligationCause::dummy(), param_env, - ty::Binder::dummy(new_trait_ref).without_const().to_predicate(self.tcx), - ); + ty::Binder::dummy(new_imm_trait_ref).without_const().to_predicate(self.tcx), + )); - if self.predicate_must_hold_modulo_regions(&new_obligation) { + let mut_result = self.predicate_must_hold_modulo_regions(&Obligation::new( + ObligationCause::dummy(), + param_env, + ty::Binder::dummy(new_mut_trait_ref).without_const().to_predicate(self.tcx), + )); + + if imm_result || mut_result { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { // We have a very specific type of error, where just borrowing this argument // might solve the problem. In cases like this, the important part is the @@ -773,15 +779,24 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // } // ``` - err.span_suggestion( - span, - &format!( - "consider{} borrowing here", - if mtbl { " mutably" } else { "" } - ), - format!("&{}{}", if mtbl { "mut " } else { "" }, snippet), - Applicability::MaybeIncorrect, - ); + if imm_result && mut_result { + err.span_suggestions( + span, + "consider borrowing here", + [format!("&{}", snippet), format!("&mut {}", snippet)].into_iter(), + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion( + span, + &format!( + "consider{} borrowing here", + if mut_result { " mutably" } else { "" } + ), + format!("&{}{}", if mut_result { "mut " } else { "" }, snippet), + Applicability::MaybeIncorrect, + ); + } } return true; } @@ -795,29 +810,16 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ty::TraitRef::new(obligation.parent_trait_ref.def_id(), imm_substs); let new_mut_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), mut_substs); - if try_borrowing(new_imm_trait_ref, expected_trait_ref, false, &[]) { - return true; - } else { - return try_borrowing(new_mut_trait_ref, expected_trait_ref, true, &[]); - } + return try_borrowing(new_imm_trait_ref, new_mut_trait_ref, expected_trait_ref, &[]); } else if let ObligationCauseCode::BindingObligation(_, _) | ObligationCauseCode::ItemObligation(_) = &*code { - if try_borrowing( + return try_borrowing( ty::TraitRef::new(trait_ref.def_id, imm_substs), + ty::TraitRef::new(trait_ref.def_id, mut_substs), trait_ref, - false, &never_suggest_borrow[..], - ) { - return true; - } else { - return try_borrowing( - ty::TraitRef::new(trait_ref.def_id, mut_substs), - trait_ref, - true, - &never_suggest_borrow[..], - ); - } + ); } else { false } diff --git a/src/test/ui/suggestions/suggest-both-imm-and-mut-trait-implementation.rs b/src/test/ui/suggestions/suggest-both-imm-and-mut-trait-implementation.rs new file mode 100644 index 00000000000..0a4f0b489fc --- /dev/null +++ b/src/test/ui/suggestions/suggest-both-imm-and-mut-trait-implementation.rs @@ -0,0 +1,13 @@ +trait Trait {} + +struct S; + +impl Trait for &S {} +impl Trait for &mut S {} + +fn foo(_: X) {} + +fn main() { + let s = S; + foo(s); //~ ERROR the trait bound `S: Trait` is not satisfied +} diff --git a/src/test/ui/suggestions/suggest-both-imm-and-mut-trait-implementation.stderr b/src/test/ui/suggestions/suggest-both-imm-and-mut-trait-implementation.stderr new file mode 100644 index 00000000000..420f5e65cad --- /dev/null +++ b/src/test/ui/suggestions/suggest-both-imm-and-mut-trait-implementation.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `S: Trait` is not satisfied + --> $DIR/suggest-both-imm-and-mut-trait-implementation.rs:12:9 + | +LL | foo(s); + | --- ^ expected an implementor of trait `Trait` + | | + | required by a bound introduced by this call + | +note: required by a bound in `foo` + --> $DIR/suggest-both-imm-and-mut-trait-implementation.rs:8:11 + | +LL | fn foo(_: X) {} + | ^^^^^ required by this bound in `foo` +help: consider borrowing here + | +LL | foo(&s); + | ~~ +LL | foo(&mut s); + | ~~~~~~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.