Skip to content

Commit 48d07d1

Browse files
committed
Suggest borrowing if a trait implementation is found for &/&mut <type>
1 parent 94ecdfd commit 48d07d1

File tree

6 files changed

+154
-17
lines changed

6 files changed

+154
-17
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -686,17 +686,42 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
686686
return false;
687687
}
688688

689+
// Blacklist traits for which it would be nonsensical to suggest borrowing.
690+
// For instance, immutable references are always Copy, so suggesting to
691+
// borrow would always succeed, but it's probably not what the user wanted.
692+
let blacklist: Vec<_> = [
693+
LangItem::Copy,
694+
LangItem::Clone,
695+
LangItem::Pin,
696+
LangItem::Unpin,
697+
LangItem::Sized,
698+
LangItem::Send,
699+
]
700+
.iter()
701+
.filter_map(|lang_item| self.tcx.lang_items().require(*lang_item).ok())
702+
.collect();
703+
689704
let span = obligation.cause.span;
690705
let param_env = obligation.param_env;
691706
let trait_ref = trait_ref.skip_binder();
692707

693-
if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code {
694-
// Try to apply the original trait binding obligation by borrowing.
695-
let self_ty = trait_ref.self_ty();
696-
let found = self_ty.to_string();
697-
let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty);
698-
let substs = self.tcx.mk_substs_trait(new_self_ty, &[]);
699-
let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs);
708+
let found_ty = trait_ref.self_ty();
709+
let found_ty_str = found_ty.to_string();
710+
let imm_borrowed_found_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, found_ty);
711+
let imm_substs = self.tcx.mk_substs_trait(imm_borrowed_found_ty, &[]);
712+
let mut_borrowed_found_ty = self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, found_ty);
713+
let mut_substs = self.tcx.mk_substs_trait(mut_borrowed_found_ty, &[]);
714+
715+
// Try to apply the original trait binding obligation by borrowing.
716+
let mut try_borrowing = |new_trait_ref: ty::TraitRef<'tcx>,
717+
expected_trait_ref: ty::TraitRef<'tcx>,
718+
mtbl: bool,
719+
blacklist: &[DefId]|
720+
-> bool {
721+
if blacklist.contains(&expected_trait_ref.def_id) {
722+
return false;
723+
}
724+
700725
let new_obligation = Obligation::new(
701726
ObligationCause::dummy(),
702727
param_env,
@@ -713,8 +738,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
713738

714739
let msg = format!(
715740
"the trait bound `{}: {}` is not satisfied",
716-
found,
717-
obligation.parent_trait_ref.skip_binder().print_only_trait_path(),
741+
found_ty_str,
742+
expected_trait_ref.print_only_trait_path(),
718743
);
719744
if has_custom_message {
720745
err.note(&msg);
@@ -730,7 +755,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
730755
span,
731756
&format!(
732757
"expected an implementor of trait `{}`",
733-
obligation.parent_trait_ref.skip_binder().print_only_trait_path(),
758+
expected_trait_ref.print_only_trait_path(),
734759
),
735760
);
736761

@@ -745,16 +770,52 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
745770

746771
err.span_suggestion(
747772
span,
748-
"consider borrowing here",
749-
format!("&{}", snippet),
773+
&format!(
774+
"consider borrowing{} here",
775+
if mtbl { " mutably" } else { "" }
776+
),
777+
format!("&{}{}", if mtbl { "mut " } else { "" }, snippet),
750778
Applicability::MaybeIncorrect,
751779
);
752780
}
753781
return true;
754782
}
755783
}
784+
return false;
785+
};
786+
787+
if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code {
788+
let expected_trait_ref = obligation.parent_trait_ref.skip_binder();
789+
let new_imm_trait_ref =
790+
ty::TraitRef::new(obligation.parent_trait_ref.def_id(), imm_substs);
791+
let new_mut_trait_ref =
792+
ty::TraitRef::new(obligation.parent_trait_ref.def_id(), mut_substs);
793+
if try_borrowing(new_imm_trait_ref, expected_trait_ref, false, &[]) {
794+
return true;
795+
} else {
796+
return try_borrowing(new_mut_trait_ref, expected_trait_ref, true, &[]);
797+
}
798+
} else if let ObligationCauseCode::BindingObligation(_, _)
799+
| ObligationCauseCode::ItemObligation(_) = &obligation.cause.code
800+
{
801+
if try_borrowing(
802+
ty::TraitRef::new(trait_ref.def_id, imm_substs),
803+
trait_ref,
804+
false,
805+
&blacklist[..],
806+
) {
807+
return true;
808+
} else {
809+
return try_borrowing(
810+
ty::TraitRef::new(trait_ref.def_id, mut_substs),
811+
trait_ref,
812+
true,
813+
&blacklist[..],
814+
);
815+
}
816+
} else {
817+
false
756818
}
757-
false
758819
}
759820

760821
/// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,

src/test/ui/suggestions/imm-ref-trait-object-literal.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ LL | fn foo<X: Trait>(_: X) {}
2121
| ----- required by this bound in `foo`
2222
...
2323
LL | foo(s);
24-
| ^ the trait `Trait` is not implemented for `S`
25-
|
26-
= help: the following implementations were found:
27-
<&'a mut S as Trait>
24+
| ^
25+
| |
26+
| expected an implementor of trait `Trait`
27+
| help: consider borrowing mutably here: `&mut s`
2828

2929
error: aborting due to 2 previous errors
3030

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// A slight variation of issue-84973.rs. Here, a mutable borrow is
2+
// required (and the obligation kind is different).
3+
4+
trait Tr {}
5+
impl Tr for &mut i32 {}
6+
7+
fn foo<T: Tr>(i: T) {}
8+
9+
fn main() {
10+
let a: i32 = 32;
11+
foo(a);
12+
//~^ ERROR: the trait bound `i32: Tr` is not satisfied [E0277]
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0277]: the trait bound `i32: Tr` is not satisfied
2+
--> $DIR/issue-84973-2.rs:11:9
3+
|
4+
LL | fn foo<T: Tr>(i: T) {}
5+
| -- required by this bound in `foo`
6+
...
7+
LL | foo(a);
8+
| ^
9+
| |
10+
| expected an implementor of trait `Tr`
11+
| help: consider borrowing mutably here: `&mut a`
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Checks whether borrowing is suggested when a trait bound is not satisfied
2+
// for found type `T`, but is for `&/&mut T`.
3+
4+
fn main() {
5+
let f = Fancy{};
6+
let o = Other::new(f);
7+
//~^ ERROR: the trait bound `Fancy: SomeTrait` is not satisfied [E0277]
8+
}
9+
10+
struct Fancy {}
11+
12+
impl <'a> SomeTrait for &'a Fancy {
13+
}
14+
15+
trait SomeTrait {}
16+
17+
struct Other<'a, G> {
18+
a: &'a str,
19+
g: G,
20+
}
21+
22+
// Broadly copied from https://docs.rs/petgraph/0.5.1/src/petgraph/dot.rs.html#70
23+
impl<'a, G> Other<'a, G>
24+
where
25+
G: SomeTrait,
26+
{
27+
pub fn new(g: G) -> Self {
28+
Other {
29+
a: "hi",
30+
g: g,
31+
}
32+
}
33+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0277]: the trait bound `Fancy: SomeTrait` is not satisfied
2+
--> $DIR/issue-84973.rs:6:24
3+
|
4+
LL | let o = Other::new(f);
5+
| ^
6+
| |
7+
| expected an implementor of trait `SomeTrait`
8+
| help: consider borrowing here: `&f`
9+
...
10+
LL | pub fn new(g: G) -> Self {
11+
| ------------------------ required by `Other::<'a, G>::new`
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)