From 6d6d978baa3abb56ad56d481ab994698069626d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 3 Oct 2019 17:10:37 -0700 Subject: [PATCH 1/6] Note when a mutable trait object is needed --- src/librustc/traits/error_reporting.rs | 111 ++++++++++++++---- src/librustc_typeck/check/method/mod.rs | 64 ++++++---- src/librustc_typeck/check/method/suggest.rs | 11 +- src/test/ui/not-panic/not-panic-safe.stderr | 1 + .../ui/parser/lex-bad-char-literals-6.stderr | 2 + .../imm-ref-trait-object-literal.rs | 14 +++ .../imm-ref-trait-object-literal.stderr | 30 +++++ .../ui/suggestions/imm-ref-trait-object.rs | 8 ++ .../suggestions/imm-ref-trait-object.stderr | 10 ++ src/test/ui/suggestions/into-str.stderr | 1 + .../suggestions/mut-borrow-needed-by-trait.rs | 23 ++++ .../mut-borrow-needed-by-trait.stderr | 41 +++++++ .../suggestions/suggest-remove-refs-1.stderr | 1 + 13 files changed, 271 insertions(+), 46 deletions(-) create mode 100644 src/test/ui/suggestions/imm-ref-trait-object-literal.rs create mode 100644 src/test/ui/suggestions/imm-ref-trait-object-literal.stderr create mode 100644 src/test/ui/suggestions/imm-ref-trait-object.rs create mode 100644 src/test/ui/suggestions/imm-ref-trait-object.stderr create mode 100644 src/test/ui/suggestions/mut-borrow-needed-by-trait.rs create mode 100644 src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index a1c97d6c687..f85ba1459f5 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -453,21 +453,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - fn find_similar_impl_candidates(&self, - trait_ref: ty::PolyTraitRef<'tcx>) - -> Vec> - { - 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> { + 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 @@ -482,10 +478,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - fn report_similar_impl_candidates(&self, - impl_candidates: Vec>, - err: &mut DiagnosticBuilder<'_>) - { + fn report_similar_impl_candidates( + &self, + impl_candidates: Vec>, + err: &mut DiagnosticBuilder<'_>, + ) { if impl_candidates.is_empty() { return; } @@ -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 @@ -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() @@ -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>, + 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>, diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 2be31112721..4522be21cab 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -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), + IllegalSizedBound(Vec, bool), // Found a match, but the return type is wrong BadReturnType, @@ -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) diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 9820ede5b5c..a30b7faca0d 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -560,7 +560,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() { @@ -576,6 +576,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(); } diff --git a/src/test/ui/not-panic/not-panic-safe.stderr b/src/test/ui/not-panic/not-panic-safe.stderr index aa18b923044..2ae5ba523a7 100644 --- a/src/test/ui/not-panic/not-panic-safe.stderr +++ b/src/test/ui/not-panic/not-panic-safe.stderr @@ -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 diff --git a/src/test/ui/parser/lex-bad-char-literals-6.stderr b/src/test/ui/parser/lex-bad-char-literals-6.stderr index a7bbe05e94b..4792aacefe5 100644 --- a/src/test/ui/parser/lex-bad-char-literals-6.stderr +++ b/src/test/ui/parser/lex-bad-char-literals-6.stderr @@ -35,6 +35,7 @@ LL | if x == y {} | ^^ no implementation for `&str == char` | = help: the trait `std::cmp::PartialEq` is not implemented for `&str` + = note: `std::cmp::PartialEq` is implemented for `&mut str` error[E0308]: mismatched types --> $DIR/lex-bad-char-literals-6.rs:15:20 @@ -52,6 +53,7 @@ LL | if x == z {} | ^^ no implementation for `&str == char` | = help: the trait `std::cmp::PartialEq` is not implemented for `&str` + = note: `std::cmp::PartialEq` is implemented for `&mut str` error: aborting due to 6 previous errors diff --git a/src/test/ui/suggestions/imm-ref-trait-object-literal.rs b/src/test/ui/suggestions/imm-ref-trait-object-literal.rs new file mode 100644 index 00000000000..22ca6dde45e --- /dev/null +++ b/src/test/ui/suggestions/imm-ref-trait-object-literal.rs @@ -0,0 +1,14 @@ +trait Trait {} + +struct S; + +impl<'a> Trait for &'a mut S {} + +fn foo(_: 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 +} diff --git a/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr b/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr new file mode 100644 index 00000000000..ccaceefacd7 --- /dev/null +++ b/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr @@ -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) {} + | --- ----- 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) {} + | --- ----- 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`. diff --git a/src/test/ui/suggestions/imm-ref-trait-object.rs b/src/test/ui/suggestions/imm-ref-trait-object.rs new file mode 100644 index 00000000000..288d6c699f5 --- /dev/null +++ b/src/test/ui/suggestions/imm-ref-trait-object.rs @@ -0,0 +1,8 @@ +fn test(t: &dyn Iterator) -> u64 { + t.min().unwrap() //~ ERROR the `min` method cannot be invoked on a trait object +} + +fn main() { + let array = [0u64]; + test(&mut array.iter()); +} diff --git a/src/test/ui/suggestions/imm-ref-trait-object.stderr b/src/test/ui/suggestions/imm-ref-trait-object.stderr new file mode 100644 index 00000000000..e272d2a73f5 --- /dev/null +++ b/src/test/ui/suggestions/imm-ref-trait-object.stderr @@ -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` instead + +error: aborting due to previous error + diff --git a/src/test/ui/suggestions/into-str.stderr b/src/test/ui/suggestions/into-str.stderr index fb3e1096ad5..6af1267f26f 100644 --- a/src/test/ui/suggestions/into-str.stderr +++ b/src/test/ui/suggestions/into-str.stderr @@ -8,6 +8,7 @@ LL | foo(String::new()); | ^^^ the trait `std::convert::From` is not implemented for `&str` | = note: to coerce a `std::string::String` into a `&str`, use `&*` as a prefix + = note: `std::convert::From` 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 diff --git a/src/test/ui/suggestions/mut-borrow-needed-by-trait.rs b/src/test/ui/suggestions/mut-borrow-needed-by-trait.rs new file mode 100644 index 00000000000..dcef2ada63b --- /dev/null +++ b/src/test/ui/suggestions/mut-borrow-needed-by-trait.rs @@ -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 +} diff --git a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr new file mode 100644 index 00000000000..66dc5b66744 --- /dev/null +++ b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -0,0 +1,41 @@ +error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied + --> $DIR/mut-borrow-needed-by-trait.rs:17:29 + | +LL | let fp = BufWriter::new(fp); + | ^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` + | + = note: `std::io::Write` is implemented for `&mut dyn std::io::Write` + = note: required by `std::io::BufWriter::::new` + +error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied + --> $DIR/mut-borrow-needed-by-trait.rs:17:14 + | +LL | let fp = BufWriter::new(fp); + | ^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` + | + = note: `std::io::Write` is implemented for `&mut dyn std::io::Write` + = note: required by `std::io::BufWriter` + +error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied + --> $DIR/mut-borrow-needed-by-trait.rs:17:14 + | +LL | let fp = BufWriter::new(fp); + | ^^^^^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` + | + = note: `std::io::Write` is implemented for `&mut dyn std::io::Write` + = note: required by `std::io::BufWriter` + +error[E0599]: no method named `write_fmt` found for type `std::io::BufWriter<&dyn std::io::Write>` in the current scope + --> $DIR/mut-borrow-needed-by-trait.rs:22:5 + | +LL | writeln!(fp, "hello world").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `std::io::BufWriter<&dyn std::io::Write>` + | + = note: the method `write_fmt` exists but the following trait bounds were not satisfied: + `std::io::BufWriter<&dyn std::io::Write> : std::io::Write` + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/suggest-remove-refs-1.stderr b/src/test/ui/suggestions/suggest-remove-refs-1.stderr index bfc313cabdc..069b0a4db6c 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-1.stderr +++ b/src/test/ui/suggestions/suggest-remove-refs-1.stderr @@ -8,6 +8,7 @@ LL | for (i, n) in &v.iter().enumerate() { | help: consider removing 1 leading `&`-references | = help: the trait `std::iter::Iterator` is not implemented for `&std::iter::Enumerate>` + = note: `std::iter::Iterator` is implemented for `&mut std::iter::Enumerate>` = note: required by `std::iter::IntoIterator::into_iter` error: aborting due to previous error From acd6540a74e5e83ce6d24c696ad833b6f80027b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 3 Oct 2019 17:39:58 -0700 Subject: [PATCH 2/6] Tweak wording --- src/librustc/traits/error_reporting.rs | 3 ++- src/librustc_typeck/check/method/suggest.rs | 2 +- src/test/ui/not-panic/not-panic-safe.stderr | 2 +- src/test/ui/parser/lex-bad-char-literals-6.stderr | 4 ++-- src/test/ui/suggestions/imm-ref-trait-object.stderr | 2 +- src/test/ui/suggestions/into-str.stderr | 2 +- src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr | 6 +++--- src/test/ui/suggestions/suggest-remove-refs-1.stderr | 2 +- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index f85ba1459f5..ecd27fa9967 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1164,9 +1164,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); } else { err.note(&format!( - "`{}` is implemented for `{:?}`", + "`{}` is implemented for `{:?}`, but not for `{:?}`", trait_ref, trait_type, + trait_ref.skip_binder().self_ty(), )); } } diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index a30b7faca0d..0ffccc03e04 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -582,7 +582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Mutability::MutImmutable => self.tcx.mk_mut_ref(region, t_type), }; if needs_mut { - err.note(&format!("you need `{}` instead", trait_type)); + err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty)); } } err.emit(); diff --git a/src/test/ui/not-panic/not-panic-safe.stderr b/src/test/ui/not-panic/not-panic-safe.stderr index 2ae5ba523a7..2362ccd32de 100644 --- a/src/test/ui/not-panic/not-panic-safe.stderr +++ b/src/test/ui/not-panic/not-panic-safe.stderr @@ -8,7 +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` + = note: `std::panic::UnwindSafe` is implemented for `&i32`, but not for `&mut i32` error: aborting due to previous error diff --git a/src/test/ui/parser/lex-bad-char-literals-6.stderr b/src/test/ui/parser/lex-bad-char-literals-6.stderr index 4792aacefe5..dc6e1e6e972 100644 --- a/src/test/ui/parser/lex-bad-char-literals-6.stderr +++ b/src/test/ui/parser/lex-bad-char-literals-6.stderr @@ -35,7 +35,7 @@ LL | if x == y {} | ^^ no implementation for `&str == char` | = help: the trait `std::cmp::PartialEq` is not implemented for `&str` - = note: `std::cmp::PartialEq` is implemented for `&mut str` + = note: `std::cmp::PartialEq` is implemented for `&mut str`, but not for `&str` error[E0308]: mismatched types --> $DIR/lex-bad-char-literals-6.rs:15:20 @@ -53,7 +53,7 @@ LL | if x == z {} | ^^ no implementation for `&str == char` | = help: the trait `std::cmp::PartialEq` is not implemented for `&str` - = note: `std::cmp::PartialEq` is implemented for `&mut str` + = note: `std::cmp::PartialEq` is implemented for `&mut str`, but not for `&str` error: aborting due to 6 previous errors diff --git a/src/test/ui/suggestions/imm-ref-trait-object.stderr b/src/test/ui/suggestions/imm-ref-trait-object.stderr index e272d2a73f5..9185eaa65c0 100644 --- a/src/test/ui/suggestions/imm-ref-trait-object.stderr +++ b/src/test/ui/suggestions/imm-ref-trait-object.stderr @@ -4,7 +4,7 @@ error: the `min` method cannot be invoked on a trait object LL | t.min().unwrap() | ^^^ | - = note: you need `&mut dyn std::iter::Iterator` instead + = note: you need `&mut dyn std::iter::Iterator` instead of `&dyn std::iter::Iterator` error: aborting due to previous error diff --git a/src/test/ui/suggestions/into-str.stderr b/src/test/ui/suggestions/into-str.stderr index 6af1267f26f..a1e1f4d1357 100644 --- a/src/test/ui/suggestions/into-str.stderr +++ b/src/test/ui/suggestions/into-str.stderr @@ -8,7 +8,7 @@ LL | foo(String::new()); | ^^^ the trait `std::convert::From` is not implemented for `&str` | = note: to coerce a `std::string::String` into a `&str`, use `&*` as a prefix - = note: `std::convert::From` is implemented for `&mut str` + = note: `std::convert::From` is implemented for `&mut str`, but not for `&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 diff --git a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr index 66dc5b66744..daa8e1162d1 100644 --- a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr +++ b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -4,7 +4,7 @@ error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satis LL | let fp = BufWriter::new(fp); | ^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` | - = note: `std::io::Write` is implemented for `&mut dyn std::io::Write` + = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` = note: required by `std::io::BufWriter::::new` error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied @@ -13,7 +13,7 @@ error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satis LL | let fp = BufWriter::new(fp); | ^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` | - = note: `std::io::Write` is implemented for `&mut dyn std::io::Write` + = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` = note: required by `std::io::BufWriter` error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied @@ -22,7 +22,7 @@ error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satis LL | let fp = BufWriter::new(fp); | ^^^^^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` | - = note: `std::io::Write` is implemented for `&mut dyn std::io::Write` + = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` = note: required by `std::io::BufWriter` error[E0599]: no method named `write_fmt` found for type `std::io::BufWriter<&dyn std::io::Write>` in the current scope diff --git a/src/test/ui/suggestions/suggest-remove-refs-1.stderr b/src/test/ui/suggestions/suggest-remove-refs-1.stderr index 069b0a4db6c..04d17a16cc3 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-1.stderr +++ b/src/test/ui/suggestions/suggest-remove-refs-1.stderr @@ -8,7 +8,7 @@ LL | for (i, n) in &v.iter().enumerate() { | help: consider removing 1 leading `&`-references | = help: the trait `std::iter::Iterator` is not implemented for `&std::iter::Enumerate>` - = note: `std::iter::Iterator` is implemented for `&mut std::iter::Enumerate>` + = note: `std::iter::Iterator` is implemented for `&mut std::iter::Enumerate>`, but not for `&std::iter::Enumerate>` = note: required by `std::iter::IntoIterator::into_iter` error: aborting due to previous error From 722bb515e2b5f93aad4cfdbc3de664af9ac445c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 5 Oct 2019 10:41:24 -0700 Subject: [PATCH 3/6] Obligation must apply modulo regions --- src/librustc/traits/error_reporting.rs | 4 +++- src/librustc/traits/query/evaluate_obligation.rs | 2 +- src/test/ui/parser/lex-bad-char-literals-6.stderr | 2 -- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index ecd27fa9967..1e8f699b520 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1149,7 +1149,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { new_trait_ref.to_predicate(), ); - if self.predicate_may_hold(&new_obligation) { + if self.evaluate_obligation_no_overflow( + &new_obligation, + ).must_apply_modulo_regions() { let sp = self.tcx.sess.source_map() .span_take_while(span, |c| c.is_whitespace() || *c == '&'); if points_at_arg && diff --git a/src/librustc/traits/query/evaluate_obligation.rs b/src/librustc/traits/query/evaluate_obligation.rs index 17684df7e9b..0d426cab9b7 100644 --- a/src/librustc/traits/query/evaluate_obligation.rs +++ b/src/librustc/traits/query/evaluate_obligation.rs @@ -56,7 +56,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { // Helper function that canonicalizes and runs the query. If an // overflow results, we re-run it in the local context so we can // report a nice error. - fn evaluate_obligation_no_overflow( + crate fn evaluate_obligation_no_overflow( &self, obligation: &PredicateObligation<'tcx>, ) -> EvaluationResult { diff --git a/src/test/ui/parser/lex-bad-char-literals-6.stderr b/src/test/ui/parser/lex-bad-char-literals-6.stderr index dc6e1e6e972..a7bbe05e94b 100644 --- a/src/test/ui/parser/lex-bad-char-literals-6.stderr +++ b/src/test/ui/parser/lex-bad-char-literals-6.stderr @@ -35,7 +35,6 @@ LL | if x == y {} | ^^ no implementation for `&str == char` | = help: the trait `std::cmp::PartialEq` is not implemented for `&str` - = note: `std::cmp::PartialEq` is implemented for `&mut str`, but not for `&str` error[E0308]: mismatched types --> $DIR/lex-bad-char-literals-6.rs:15:20 @@ -53,7 +52,6 @@ LL | if x == z {} | ^^ no implementation for `&str == char` | = help: the trait `std::cmp::PartialEq` is not implemented for `&str` - = note: `std::cmp::PartialEq` is implemented for `&mut str`, but not for `&str` error: aborting due to 6 previous errors From 4fcaa4a283faa1c5b6b18f7c1dad002dbc1f7bea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 5 Oct 2019 11:37:21 -0700 Subject: [PATCH 4/6] review comments --- src/librustc/hir/mod.rs | 7 +++++ src/librustc_typeck/check/method/mod.rs | 8 +++--- src/librustc_typeck/check/method/suggest.rs | 30 +++++++++++---------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 30b05036688..3d473ec0bc7 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1053,6 +1053,13 @@ impl Mutability { MutImmutable => MutImmutable, } } + + pub fn not(self) -> Self { + match self { + MutMutable => MutImmutable, + MutImmutable => MutMutable, + } + } } #[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Hash, HashStable)] diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 4522be21cab..5851a074fa3 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -215,10 +215,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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), - }; + let trait_type = self.tcx.mk_ref(region, ty::TypeAndMut { + ty: t_type, + mutbl: mutability.not(), + }); match self.lookup_probe( span, segment.ident, diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 0ffccc03e04..c3ca58c834d 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -564,24 +564,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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() { - let help = format!("{an}other candidate{s} {were} found in the following \ - trait{s}, perhaps add a `use` for {one_of_them}:", - an = if candidates.len() == 1 {"an" } else { "" }, - s = pluralise!(candidates.len()), - were = if candidates.len() == 1 { "was" } else { "were" }, - one_of_them = if candidates.len() == 1 { - "it" - } else { - "one_of_them" - }); + let help = format!( + "{an}other candidate{s} {were} found in the following trait{s}, perhaps \ + add a `use` for {one_of_them}:", + an = if candidates.len() == 1 {"an" } else { "" }, + s = pluralise!(candidates.len()), + were = if candidates.len() == 1 { "was" } else { "were" }, + one_of_them = if candidates.len() == 1 { + "it" + } else { + "one_of_them" + }, + ); 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 { + let trait_type = self.tcx.mk_ref(region, ty::TypeAndMut { + ty: t_type, + mutbl: mutability.not(), + }); err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty)); } } From 2c6bcac535942533489bccdd39515babd0ebb15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 6 Oct 2019 18:38:15 -0700 Subject: [PATCH 5/6] review comments --- src/librustc/hir/mod.rs | 2 +- src/librustc_typeck/check/method/mod.rs | 2 +- src/librustc_typeck/check/method/suggest.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 3d473ec0bc7..6a43fa8da16 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1054,7 +1054,7 @@ impl Mutability { } } - pub fn not(self) -> Self { + pub fn invert(self) -> Self { match self { MutMutable => MutImmutable, MutImmutable => MutMutable, diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 5851a074fa3..434ead50e04 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -217,7 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Ref(region, t_type, mutability) = self_ty.kind { let trait_type = self.tcx.mk_ref(region, ty::TypeAndMut { ty: t_type, - mutbl: mutability.not(), + mutbl: mutability.invert(), }); match self.lookup_probe( span, diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index c3ca58c834d..c19ea578023 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -582,7 +582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if needs_mut { let trait_type = self.tcx.mk_ref(region, ty::TypeAndMut { ty: t_type, - mutbl: mutability.not(), + mutbl: mutability.invert(), }); err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty)); } From faf8a2af7aab765c832c9045f08421e42ddb0b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 9 Oct 2019 11:42:29 -0700 Subject: [PATCH 6/6] Only suggest change mut if vars are resolved --- src/librustc/traits/error_reporting.rs | 6 ++++++ src/test/ui/suggestions/suggest-remove-refs-1.stderr | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 1e8f699b520..9eb91569ed5 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1134,6 +1134,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { // Do not suggest removal of borrow from type arguments. return; } + let trait_ref = self.resolve_vars_if_possible(trait_ref); + if trait_ref.has_infer_types() { + // Do not ICE while trying to find if a reborrow would succeed on a trait with + // unresolved bindings. + return; + } if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind { let trait_type = match mutability { diff --git a/src/test/ui/suggestions/suggest-remove-refs-1.stderr b/src/test/ui/suggestions/suggest-remove-refs-1.stderr index 04d17a16cc3..bfc313cabdc 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-1.stderr +++ b/src/test/ui/suggestions/suggest-remove-refs-1.stderr @@ -8,7 +8,6 @@ LL | for (i, n) in &v.iter().enumerate() { | help: consider removing 1 leading `&`-references | = help: the trait `std::iter::Iterator` is not implemented for `&std::iter::Enumerate>` - = note: `std::iter::Iterator` is implemented for `&mut std::iter::Enumerate>`, but not for `&std::iter::Enumerate>` = note: required by `std::iter::IntoIterator::into_iter` error: aborting due to previous error