Skip to content

Commit 361791b

Browse files
committed
Auto merge of rust-lang#65456 - estebank:trait-bound-borrow, r=matthewjasper
Suggest borrowing when it would satisfy an unmet trait bound When there are multiple implementors for the same trait that is present in an unmet binding, modify the E0277 error to refer to the parent obligation and verify whether borrowing the argument being passed in would satisfy the unmet bound. If it would, suggest it. Fix rust-lang#56368.
2 parents 0f0c640 + 2fe8371 commit 361791b

11 files changed

+125
-29
lines changed

src/libcore/ops/function.rs

-4
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,6 @@ pub trait Fn<Args> : FnMut<Args> {
137137
#[rustc_paren_sugar]
138138
#[rustc_on_unimplemented(
139139
on(Args="()", note="wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}"),
140-
on(
141-
all(Args="(char,)", _Self="std::string::String"),
142-
note="borrowing the `{Self}` might fix the problem"
143-
),
144140
message="expected a `{FnMut}<{Args}>` closure, found `{Self}`",
145141
label="expected an `FnMut<{Args}>` closure, found `{Self}`",
146142
)]

src/librustc/traits/error_reporting.rs

+90-8
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use crate::ty::subst::Subst;
3333
use crate::ty::SubtypePredicate;
3434
use crate::util::nodemap::{FxHashMap, FxHashSet};
3535

36-
use errors::{Applicability, DiagnosticBuilder, pluralize};
36+
use errors::{Applicability, DiagnosticBuilder, pluralize, Style};
3737
use std::fmt;
3838
use syntax::ast;
3939
use syntax::symbol::{sym, kw};
@@ -713,20 +713,24 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
713713
}
714714
match obligation.predicate {
715715
ty::Predicate::Trait(ref trait_predicate) => {
716-
let trait_predicate =
717-
self.resolve_vars_if_possible(trait_predicate);
716+
let trait_predicate = self.resolve_vars_if_possible(trait_predicate);
718717

719718
if self.tcx.sess.has_errors() && trait_predicate.references_error() {
720719
return;
721720
}
722721
let trait_ref = trait_predicate.to_poly_trait_ref();
723-
let (post_message, pre_message) =
724-
self.get_parent_trait_ref(&obligation.cause.code)
725-
.map(|t| (format!(" in `{}`", t), format!("within `{}`, ", t)))
722+
let (
723+
post_message,
724+
pre_message,
725+
) = self.get_parent_trait_ref(&obligation.cause.code)
726+
.map(|t| (format!(" in `{}`", t), format!("within `{}`, ", t)))
726727
.unwrap_or_default();
727728

728-
let OnUnimplementedNote { message, label, note }
729-
= self.on_unimplemented_note(trait_ref, obligation);
729+
let OnUnimplementedNote {
730+
message,
731+
label,
732+
note,
733+
} = self.on_unimplemented_note(trait_ref, obligation);
730734
let have_alt_message = message.is_some() || label.is_some();
731735
let is_try = self.tcx.sess.source_map().span_to_snippet(span)
732736
.map(|s| &s == "?")
@@ -767,6 +771,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
767771
)
768772
};
769773

774+
if self.suggest_add_reference_to_arg(
775+
&obligation,
776+
&mut err,
777+
&trait_ref,
778+
points_at_arg,
779+
have_alt_message,
780+
) {
781+
self.note_obligation_cause(&mut err, obligation);
782+
err.emit();
783+
return;
784+
}
770785
if let Some(ref s) = label {
771786
// If it has a custom `#[rustc_on_unimplemented]`
772787
// error message, let's display it as the label!
@@ -1298,6 +1313,73 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
12981313
}
12991314
}
13001315

1316+
fn suggest_add_reference_to_arg(
1317+
&self,
1318+
obligation: &PredicateObligation<'tcx>,
1319+
err: &mut DiagnosticBuilder<'tcx>,
1320+
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
1321+
points_at_arg: bool,
1322+
has_custom_message: bool,
1323+
) -> bool {
1324+
if !points_at_arg {
1325+
return false;
1326+
}
1327+
1328+
let span = obligation.cause.span;
1329+
let param_env = obligation.param_env;
1330+
let trait_ref = trait_ref.skip_binder();
1331+
1332+
if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code {
1333+
// Try to apply the original trait binding obligation by borrowing.
1334+
let self_ty = trait_ref.self_ty();
1335+
let found = self_ty.to_string();
1336+
let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty);
1337+
let substs = self.tcx.mk_substs_trait(new_self_ty, &[]);
1338+
let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs);
1339+
let new_obligation = Obligation::new(
1340+
ObligationCause::dummy(),
1341+
param_env,
1342+
new_trait_ref.to_predicate(),
1343+
);
1344+
if self.predicate_must_hold_modulo_regions(&new_obligation) {
1345+
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
1346+
// We have a very specific type of error, where just borrowing this argument
1347+
// might solve the problem. In cases like this, the important part is the
1348+
// original type obligation, not the last one that failed, which is arbitrary.
1349+
// Because of this, we modify the error to refer to the original obligation and
1350+
// return early in the caller.
1351+
let msg = format!(
1352+
"the trait bound `{}: {}` is not satisfied",
1353+
found,
1354+
obligation.parent_trait_ref.skip_binder(),
1355+
);
1356+
if has_custom_message {
1357+
err.note(&msg);
1358+
} else {
1359+
err.message = vec![(msg, Style::NoStyle)];
1360+
}
1361+
if snippet.starts_with('&') {
1362+
// This is already a literal borrow and the obligation is failing
1363+
// somewhere else in the obligation chain. Do not suggest non-sense.
1364+
return false;
1365+
}
1366+
err.span_label(span, &format!(
1367+
"expected an implementor of trait `{}`",
1368+
obligation.parent_trait_ref.skip_binder(),
1369+
));
1370+
err.span_suggestion(
1371+
span,
1372+
"consider borrowing here",
1373+
format!("&{}", snippet),
1374+
Applicability::MaybeIncorrect,
1375+
);
1376+
return true;
1377+
}
1378+
}
1379+
}
1380+
false
1381+
}
1382+
13011383
/// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
13021384
/// suggest removing these references until we reach a type that implements the trait.
13031385
fn suggest_remove_reference(

src/librustc_errors/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub mod registry;
3838
mod styled_buffer;
3939
mod lock;
4040
pub mod json;
41+
pub use snippet::Style;
4142

4243
pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
4344

src/test/ui/derives/deriving-copyclone.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ fn main() {
2828
is_clone(B { a: 1, b: 2 });
2929

3030
// B<C> cannot be copied or cloned
31-
is_copy(B { a: 1, b: C }); //~ERROR Copy
32-
is_clone(B { a: 1, b: C }); //~ERROR Clone
31+
is_copy(B { a: 1, b: C }); //~ ERROR Copy
32+
is_clone(B { a: 1, b: C }); //~ ERROR Clone
3333

3434
// B<D> can be cloned but not copied
35-
is_copy(B { a: 1, b: D }); //~ERROR Copy
35+
is_copy(B { a: 1, b: D }); //~ ERROR Copy
3636
is_clone(B { a: 1, b: D });
3737
}

src/test/ui/derives/deriving-copyclone.stderr

+12-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ LL | fn is_copy<T: Copy>(_: T) {}
55
| ------- ---- required by this bound in `is_copy`
66
...
77
LL | is_copy(B { a: 1, b: C });
8-
| ^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `C`
8+
| ^^^^^^^^^^^^^^^^
9+
| |
10+
| expected an implementor of trait `std::marker::Copy`
11+
| help: consider borrowing here: `&B { a: 1, b: C }`
912
|
1013
= note: required because of the requirements on the impl of `std::marker::Copy` for `B<C>`
1114

@@ -16,7 +19,10 @@ LL | fn is_clone<T: Clone>(_: T) {}
1619
| -------- ----- required by this bound in `is_clone`
1720
...
1821
LL | is_clone(B { a: 1, b: C });
19-
| ^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `C`
22+
| ^^^^^^^^^^^^^^^^
23+
| |
24+
| expected an implementor of trait `std::clone::Clone`
25+
| help: consider borrowing here: `&B { a: 1, b: C }`
2026
|
2127
= note: required because of the requirements on the impl of `std::clone::Clone` for `B<C>`
2228

@@ -27,7 +33,10 @@ LL | fn is_copy<T: Copy>(_: T) {}
2733
| ------- ---- required by this bound in `is_copy`
2834
...
2935
LL | is_copy(B { a: 1, b: D });
30-
| ^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `D`
36+
| ^^^^^^^^^^^^^^^^
37+
| |
38+
| expected an implementor of trait `std::marker::Copy`
39+
| help: consider borrowing here: `&B { a: 1, b: D }`
3140
|
3241
= note: required because of the requirements on the impl of `std::marker::Copy` for `B<D>`
3342

src/test/ui/kindck/kindck-impl-type-params-2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ fn take_param<T:Foo>(foo: &T) { }
1111
fn main() {
1212
let x: Box<_> = box 3;
1313
take_param(&x);
14-
//~^ ERROR `std::boxed::Box<{integer}>: std::marker::Copy` is not satisfied
14+
//~^ ERROR the trait bound `std::boxed::Box<{integer}>: Foo` is not satisfied
1515
}

src/test/ui/kindck/kindck-impl-type-params-2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0277]: the trait bound `std::boxed::Box<{integer}>: std::marker::Copy` is not satisfied
1+
error[E0277]: the trait bound `std::boxed::Box<{integer}>: Foo` is not satisfied
22
--> $DIR/kindck-impl-type-params-2.rs:13:16
33
|
44
LL | fn take_param<T:Foo>(foo: &T) { }

src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0277]: the trait bound `std::boxed::Box<{integer}>: std::marker::Copy` is not satisfied
1+
error[E0277]: the trait bound `std::boxed::Box<{integer}>: Foo` is not satisfied
22
--> $DIR/kindck-inherited-copy-bound.rs:21:16
33
|
44
LL | fn take_param<T:Foo>(foo: &T) { }

src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0277]: the trait bound `std::boxed::Box<{integer}>: std::marker::Copy` is not satisfied
1+
error[E0277]: the trait bound `std::boxed::Box<{integer}>: Foo` is not satisfied
22
--> $DIR/kindck-inherited-copy-bound.rs:21:16
33
|
44
LL | fn take_param<T:Foo>(foo: &T) { }

src/test/ui/suggestions/issue-62843.stderr

+5-3
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ error[E0277]: expected a `std::ops::FnMut<(char,)>` closure, found `std::string:
22
--> $DIR/issue-62843.rs:4:32
33
|
44
LL | println!("{:?}", line.find(pattern));
5-
| ^^^^^^^ expected an `FnMut<(char,)>` closure, found `std::string::String`
5+
| ^^^^^^^
6+
| |
7+
| expected an implementor of trait `std::str::pattern::Pattern<'_>`
8+
| help: consider borrowing here: `&pattern`
69
|
7-
= help: the trait `std::ops::FnMut<(char,)>` is not implemented for `std::string::String`
8-
= note: borrowing the `std::string::String` might fix the problem
10+
= note: the trait bound `std::string::String: std::str::pattern::Pattern<'_>` is not satisfied
911
= note: required because of the requirements on the impl of `std::str::pattern::Pattern<'_>` for `std::string::String`
1012

1113
error: aborting due to previous error

src/test/ui/traits/traits-negative-impls.stderr

+10-4
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,12 @@ LL | fn is_send<T: Send>(_: T) {}
5050
| ------- ---- required by this bound in `is_send`
5151
...
5252
LL | is_send(Box::new(TestType));
53-
| ^^^^^^^^^^^^^^^^^^ `dummy2::TestType` cannot be sent between threads safely
53+
| ^^^^^^^^^^^^^^^^^^
54+
| |
55+
| expected an implementor of trait `std::marker::Send`
56+
| help: consider borrowing here: `&Box::new(TestType)`
5457
|
55-
= help: the trait `std::marker::Send` is not implemented for `dummy2::TestType`
58+
= note: the trait bound `dummy2::TestType: std::marker::Send` is not satisfied
5659
= note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<dummy2::TestType>`
5760
= note: required because it appears within the type `std::boxed::Box<dummy2::TestType>`
5861

@@ -77,9 +80,12 @@ LL | fn is_sync<T: Sync>(_: T) {}
7780
| ------- ---- required by this bound in `is_sync`
7881
...
7982
LL | is_sync(Outer2(TestType));
80-
| ^^^^^^^^^^^^^^^^ `main::TestType` cannot be sent between threads safely
83+
| ^^^^^^^^^^^^^^^^
84+
| |
85+
| expected an implementor of trait `std::marker::Sync`
86+
| help: consider borrowing here: `&Outer2(TestType)`
8187
|
82-
= help: the trait `std::marker::Send` is not implemented for `main::TestType`
88+
= note: the trait bound `main::TestType: std::marker::Sync` is not satisfied
8389
= note: required because of the requirements on the impl of `std::marker::Sync` for `Outer2<main::TestType>`
8490

8591
error: aborting due to 7 previous errors

0 commit comments

Comments
 (0)