Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a5d9310
remove 'as_str' when it's already a str type.
Sep 24, 2019
bb3c030
add test files
Sep 24, 2019
4cb86c9
comment fixes
Sep 25, 2019
5abc5e9
add FIXME and use 'span_label'
Oct 1, 2019
51482d0
fix unit tests
Oct 1, 2019
11c2d43
typo fix in the code
Oct 2, 2019
55ecb79
Note when a mutable trait object is needed
estebank Oct 4, 2019
f5e372a
Tweak wording
estebank Oct 4, 2019
4414068
Correctly estimate required space for string
AnthonyMikh Oct 5, 2019
35a3b58
Obligation must apply modulo regions
estebank Oct 5, 2019
169b040
review comments
estebank Oct 5, 2019
0b0aeac
Suggest dereferencing boolean reference when used in 'if' or 'while'
XiangQingW Oct 6, 2019
8a164ac
Suggest dereferencing boolean reference when used in 'if' or 'while'
XiangQingW Oct 6, 2019
bbb69d1
Suggest dereferencing boolean reference when used in 'if' or 'while'
XiangQingW Oct 6, 2019
c5e0d6e
Add long error explanation for E0566
GuillaumeGomez Oct 6, 2019
57cb881
Update ui tests
GuillaumeGomez Oct 6, 2019
8baf7bf
Update reference
tmandry Oct 6, 2019
6efcb02
review comments
estebank Oct 7, 2019
c100831
Rollup merge of #64739 - guanqun:remove-as-str, r=estebank
Centril Oct 7, 2019
fa961fa
Rollup merge of #65077 - estebank:mut-trait-expected, r=nikomatsakis
Centril Oct 7, 2019
a3d0c90
Rollup merge of #65120 - AnthonyMikh:fix_65119, r=estebank
Centril Oct 7, 2019
934f3c2
Rollup merge of #65150 - XiangQingW:master, r=estebank
Centril Oct 7, 2019
00315c3
Rollup merge of #65164 - GuillaumeGomez:long-err-explanation-E0566, r…
Centril Oct 7, 2019
ca3f01a
Rollup merge of #65173 - tmandry:reffy-ref, r=tmandry
Centril Oct 7, 2019
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
Prev Previous commit
Next Next commit
Note when a mutable trait object is needed
  • Loading branch information
estebank committed Oct 4, 2019
commit 55ecb79123a3e9e91444a07e2e42d417e88c0b31
111 changes: 90 additions & 21 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,21 +453,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}

fn find_similar_impl_candidates(&self,
trait_ref: ty::PolyTraitRef<'tcx>)
-> Vec<ty::TraitRef<'tcx>>
{
let simp = fast_reject::simplify_type(self.tcx,
trait_ref.skip_binder().self_ty(),
true);
fn find_similar_impl_candidates(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> Vec<ty::TraitRef<'tcx>> {
let simp = fast_reject::simplify_type(self.tcx, trait_ref.skip_binder().self_ty(), true);
let all_impls = self.tcx.all_impls(trait_ref.def_id());

match simp {
Some(simp) => all_impls.iter().filter_map(|&def_id| {
let imp = self.tcx.impl_trait_ref(def_id).unwrap();
let imp_simp = fast_reject::simplify_type(self.tcx,
imp.self_ty(),
true);
let imp_simp = fast_reject::simplify_type(self.tcx, imp.self_ty(), true);
if let Some(imp_simp) = imp_simp {
if simp != imp_simp {
return None
Expand All @@ -482,10 +478,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}

fn report_similar_impl_candidates(&self,
impl_candidates: Vec<ty::TraitRef<'tcx>>,
err: &mut DiagnosticBuilder<'_>)
{
fn report_similar_impl_candidates(
&self,
impl_candidates: Vec<ty::TraitRef<'tcx>>,
err: &mut DiagnosticBuilder<'_>,
) {
if impl_candidates.is_empty() {
return;
}
Expand Down Expand Up @@ -720,10 +717,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// which is somewhat confusing.
err.help(&format!("consider adding a `where {}` bound",
trait_ref.to_predicate()));
} else if !have_alt_message {
// Can't show anything else useful, try to find similar impls.
let impl_candidates = self.find_similar_impl_candidates(trait_ref);
self.report_similar_impl_candidates(impl_candidates, &mut err);
} else {
if !have_alt_message {
// Can't show anything else useful, try to find similar impls.
let impl_candidates = self.find_similar_impl_candidates(trait_ref);
self.report_similar_impl_candidates(impl_candidates, &mut err);
}
self.suggest_change_mut(
&obligation,
&mut err,
&trait_ref,
points_at_arg,
);
}

// If this error is due to `!: Trait` not implemented but `(): Trait` is
Expand Down Expand Up @@ -1081,9 +1086,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {

let substs = self.tcx.mk_substs_trait(trait_type, &[]);
let new_trait_ref = ty::TraitRef::new(trait_ref.def_id, substs);
let new_obligation = Obligation::new(ObligationCause::dummy(),
obligation.param_env,
new_trait_ref.to_predicate());
let new_obligation = Obligation::new(
ObligationCause::dummy(),
obligation.param_env,
new_trait_ref.to_predicate(),
);

if self.predicate_may_hold(&new_obligation) {
let sp = self.tcx.sess.source_map()
Expand All @@ -1105,6 +1112,68 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}

/// Check if the trait bound is implemented for a different mutability and note it in the
/// final error.
fn suggest_change_mut(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut DiagnosticBuilder<'tcx>,
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
points_at_arg: bool,
) {
let span = obligation.cause.span;
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
let refs_number = snippet.chars()
.filter(|c| !c.is_whitespace())
.take_while(|c| *c == '&')
.count();
if let Some('\'') = snippet.chars()
.filter(|c| !c.is_whitespace())
.skip(refs_number)
.next()
{ // Do not suggest removal of borrow from type arguments.
return;
}

if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind {
let trait_type = match mutability {
hir::Mutability::MutMutable => self.tcx.mk_imm_ref(region, t_type),
hir::Mutability::MutImmutable => self.tcx.mk_mut_ref(region, t_type),
};

let substs = self.tcx.mk_substs_trait(&trait_type, &[]);
let new_trait_ref = ty::TraitRef::new(trait_ref.skip_binder().def_id, substs);
let new_obligation = Obligation::new(
ObligationCause::dummy(),
obligation.param_env,
new_trait_ref.to_predicate(),
);

if self.predicate_may_hold(&new_obligation) {
let sp = self.tcx.sess.source_map()
.span_take_while(span, |c| c.is_whitespace() || *c == '&');
if points_at_arg &&
mutability == hir::Mutability::MutImmutable &&
refs_number > 0
{
err.span_suggestion(
sp,
"consider changing this borrow's mutability",
"&mut ".to_string(),
Applicability::MachineApplicable,
);
} else {
err.note(&format!(
"`{}` is implemented for `{:?}`",
trait_ref,
trait_type,
));
}
}
}
}
}

fn suggest_semicolon_removal(
&self,
obligation: &PredicateObligation<'tcx>,
Expand Down
64 changes: 40 additions & 24 deletions src/librustc_typeck/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub enum MethodError<'tcx> {

// Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
// forgotten to import a trait.
IllegalSizedBound(Vec<DefId>),
IllegalSizedBound(Vec<DefId>, bool),

// Found a match, but the return type is wrong
BadReturnType,
Expand Down Expand Up @@ -213,33 +213,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
segment,
);

let mut needs_mut = false;
if let ty::Ref(region, t_type, mutability) = self_ty.kind {
let trait_type = match mutability {
hir::Mutability::MutMutable => self.tcx.mk_imm_ref(region, t_type),
hir::Mutability::MutImmutable => self.tcx.mk_mut_ref(region, t_type),
};
match self.lookup_probe(
span,
segment.ident,
trait_type,
call_expr,
ProbeScope::TraitsInScope
) {
Ok(ref new_pick) if *new_pick != pick => {
needs_mut = true;
}
_ => {}
}
}

if result.illegal_sized_bound {
// We probe again, taking all traits into account (not only those in scope).
let candidates =
match self.lookup_probe(span,
segment.ident,
self_ty,
call_expr,
ProbeScope::AllTraits) {

// If we find a different result the caller probably forgot to import a trait.
Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
Err(Ambiguity(ref sources)) => {
sources.iter()
.filter_map(|source| {
match *source {
// Note: this cannot come from an inherent impl,
// because the first probing succeeded.
ImplSource(def) => self.tcx.trait_id_of_impl(def),
TraitSource(_) => None,
}
})
.collect()
let candidates = match self.lookup_probe(
span,
segment.ident,
self_ty,
call_expr,
ProbeScope::AllTraits,
) {
// If we find a different result the caller probably forgot to import a trait.
Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
Err(Ambiguity(ref sources)) => sources.iter().filter_map(|source| {
match *source {
// Note: this cannot come from an inherent impl,
// because the first probing succeeded.
ImplSource(def) => self.tcx.trait_id_of_impl(def),
TraitSource(_) => None,
}
_ => Vec::new(),
};
}).collect(),
_ => Vec::new(),
};

return Err(IllegalSizedBound(candidates));
return Err(IllegalSizedBound(candidates, needs_mut));
}

Ok(result.callee)
Expand Down
11 changes: 10 additions & 1 deletion src/librustc_typeck/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.emit();
}

MethodError::IllegalSizedBound(candidates) => {
MethodError::IllegalSizedBound(candidates, needs_mut) => {
let msg = format!("the `{}` method cannot be invoked on a trait object", item_name);
let mut err = self.sess().struct_span_err(span, &msg);
if !candidates.is_empty() {
Expand All @@ -569,6 +569,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
self.suggest_use_candidates(&mut err, help, candidates);
}
if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind {
let trait_type = match mutability {
hir::Mutability::MutMutable => self.tcx.mk_imm_ref(region, t_type),
hir::Mutability::MutImmutable => self.tcx.mk_mut_ref(region, t_type),
};
if needs_mut {
err.note(&format!("you need `{}` instead", trait_type));
}
}
err.emit();
}

Expand Down
1 change: 1 addition & 0 deletions src/test/ui/not-panic/not-panic-safe.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ LL | assert::<&mut i32>();
| ^^^^^^^^^^^^^^^^^^ `&mut i32` may not be safely transferred across an unwind boundary
|
= help: the trait `std::panic::UnwindSafe` is not implemented for `&mut i32`
= note: `std::panic::UnwindSafe` is implemented for `&i32`

error: aborting due to previous error

Expand Down
2 changes: 2 additions & 0 deletions src/test/ui/parser/lex-bad-char-literals-6.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ LL | if x == y {}
| ^^ no implementation for `&str == char`
|
= help: the trait `std::cmp::PartialEq<char>` is not implemented for `&str`
= note: `std::cmp::PartialEq<char>` is implemented for `&mut str`

error[E0308]: mismatched types
--> $DIR/lex-bad-char-literals-6.rs:15:20
Expand All @@ -52,6 +53,7 @@ LL | if x == z {}
| ^^ no implementation for `&str == char`
|
= help: the trait `std::cmp::PartialEq<char>` is not implemented for `&str`
= note: `std::cmp::PartialEq<char>` is implemented for `&mut str`

error: aborting due to 6 previous errors

Expand Down
14 changes: 14 additions & 0 deletions src/test/ui/suggestions/imm-ref-trait-object-literal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
trait Trait {}

struct S;

impl<'a> Trait for &'a mut S {}

fn foo<X: Trait>(_: X) {}


fn main() {
let s = S;
foo(&s); //~ ERROR the trait bound `&S: Trait` is not satisfied
foo(s); //~ ERROR the trait bound `S: Trait` is not satisfied
}
30 changes: 30 additions & 0 deletions src/test/ui/suggestions/imm-ref-trait-object-literal.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
error[E0277]: the trait bound `&S: Trait` is not satisfied
--> $DIR/imm-ref-trait-object-literal.rs:12:7
|
LL | fn foo<X: Trait>(_: X) {}
| --- ----- required by this bound in `foo`
...
LL | foo(&s);
| -^
| |
| the trait `Trait` is not implemented for `&S`
| help: consider changing this borrow's mutability: `&mut`
|
= help: the following implementations were found:
<&'a mut S as Trait>

error[E0277]: the trait bound `S: Trait` is not satisfied
--> $DIR/imm-ref-trait-object-literal.rs:13:7
|
LL | fn foo<X: Trait>(_: X) {}
| --- ----- required by this bound in `foo`
...
LL | foo(s);
| ^ the trait `Trait` is not implemented for `S`
|
= help: the following implementations were found:
<&'a mut S as Trait>

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0277`.
8 changes: 8 additions & 0 deletions src/test/ui/suggestions/imm-ref-trait-object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn test(t: &dyn Iterator<Item=&u64>) -> u64 {
t.min().unwrap() //~ ERROR the `min` method cannot be invoked on a trait object
}

fn main() {
let array = [0u64];
test(&mut array.iter());
}
10 changes: 10 additions & 0 deletions src/test/ui/suggestions/imm-ref-trait-object.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: the `min` method cannot be invoked on a trait object
--> $DIR/imm-ref-trait-object.rs:2:8
|
LL | t.min().unwrap()
| ^^^
|
= note: you need `&mut dyn std::iter::Iterator<Item = &u64>` instead

error: aborting due to previous error

1 change: 1 addition & 0 deletions src/test/ui/suggestions/into-str.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ LL | foo(String::new());
| ^^^ the trait `std::convert::From<std::string::String>` is not implemented for `&str`
|
= note: to coerce a `std::string::String` into a `&str`, use `&*` as a prefix
= note: `std::convert::From<std::string::String>` is implemented for `&mut str`
= note: required because of the requirements on the impl of `std::convert::Into<&str>` for `std::string::String`

error: aborting due to previous error
Expand Down
23 changes: 23 additions & 0 deletions src/test/ui/suggestions/mut-borrow-needed-by-trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use std::env::args;
use std::fs::File;
use std::io::{stdout, Write, BufWriter};

fn main() {
let mut args = args();
let _ = args.next();
let dest = args.next();

let h1; let h2; let h3;

let fp: &dyn Write = match dest {
Some(path) => { h1 = File::create(path).unwrap(); &h1 },
None => { h2 = stdout(); h3 = h2.lock(); &h3 }
};

let fp = BufWriter::new(fp);
//~^ ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
//~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
//~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied

writeln!(fp, "hello world").unwrap(); //~ ERROR no method named `write_fmt` found for type
}
Loading