From d360091e799b14a81ced54a67bc67ae0b0bc3afe Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 8 Nov 2016 22:54:31 +0100 Subject: [PATCH 1/4] Add suggestion for & coercions --- src/librustc_typeck/check/demand.rs | 77 ++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index e922c7447ff85..2933f35abfb6a 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -11,8 +11,9 @@ use check::FnCtxt; use rustc::ty::Ty; -use rustc::infer::{InferOk}; +use rustc::infer::{InferOk, TypeOrigin}; use rustc::traits::ObligationCause; +use rustc::ty; use syntax::ast; use syntax_pos::{self, Span}; @@ -80,12 +81,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) { let cause = self.misc(expr.span); let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); - let mode = probe::Mode::MethodCall; - let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, - mode, - expected, - checked_ty, - ast::DUMMY_NODE_ID); + let suggestions = if let Some(suggestions) = self.check_ref(expr, + checked_ty, + expected) { + suggestions + } else { + let mode = probe::Mode::MethodCall; + self.probe_for_return_type(syntax_pos::DUMMY_SP, + mode, + expected, + checked_ty, + ast::DUMMY_NODE_ID) + } let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); if suggestions.len() > 0 { err.help(&format!("here are some functions which \ @@ -140,4 +147,60 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => false, } } + + /// This function is used to determine potential "simple" improvements or users' errors and + /// provide them useful help. For example: + /// + /// ``` + /// fn some_fn(s: &str) {} + /// + /// let x = "hey!".to_owned(); + /// some_fn(x); // error + /// ``` + /// + /// No need to find every potential function which could make a coercion to transform a + /// `String` into a `&str` since a `&` would do the trick! + /// + /// In addition of this check, it also checks between references mutability state. If the + /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with + /// `&mut`!". + fn check_ref(&self, + expr: &hir::Expr, + checked_ty: Ty<'tcx>, + expected: Ty<'tcx>) + -> Option { + match (&expected.sty, &checked_ty.sty) { + (&ty::TyRef(_, _), &ty::TyRef(_, _)) => None, + (&ty::TyRef(_, mutability), _) => { + // Check if it can work when put into a ref. For example: + // + // ``` + // fn bar(x: &mut i32) {} + // + // let x = 0u32; + // bar(&x); // error, expected &mut + // ``` + let ref_ty = match mutability.mutbl { + hir::Mutability::MutMutable => self.tcx.mk_mut_ref( + self.tcx.mk_region(ty::ReStatic), + checked_ty), + hir::Mutability::MutImmutable => self.tcx.mk_imm_ref( + self.tcx.mk_region(ty::ReStatic), + checked_ty), + }; + if self.try_coerce(expr, ref_ty, expected).is_ok() { + if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) { + return Some(format!("try with `{}{}`", + match mutability.mutbl { + hir::Mutability::MutMutable => "&mut ", + hir::Mutability::MutImmutable => "&", + }, + &src)); + } + } + None + } + _ => None, + } + } } From 8fe3a9a8f123fa759d35000bfbfb623167a76409 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 8 Nov 2016 22:55:58 +0100 Subject: [PATCH 2/4] Update tests --- src/test/compile-fail/coercion-slice.rs | 1 - src/test/compile-fail/cross-borrow-trait.rs | 2 ++ src/test/compile-fail/dst-bad-coercions.rs | 2 ++ src/test/compile-fail/issue-13058.rs | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/coercion-slice.rs b/src/test/compile-fail/coercion-slice.rs index 6b468ff96620d..7c5a4e0c3c6f6 100644 --- a/src/test/compile-fail/coercion-slice.rs +++ b/src/test/compile-fail/coercion-slice.rs @@ -14,6 +14,5 @@ fn main() { let _: &[i32] = [0]; //~^ ERROR mismatched types //~| expected type `&[i32]` - //~| found type `[{integer}; 1]` //~| expected &[i32], found array of 1 elements } diff --git a/src/test/compile-fail/cross-borrow-trait.rs b/src/test/compile-fail/cross-borrow-trait.rs index e5afccb9cf394..ee67a30fa1c37 100644 --- a/src/test/compile-fail/cross-borrow-trait.rs +++ b/src/test/compile-fail/cross-borrow-trait.rs @@ -20,4 +20,6 @@ pub fn main() { let _y: &Trait = x; //~ ERROR mismatched types //~| expected type `&Trait` //~| found type `std::boxed::Box` + //~| expected &Trait, found box + //~| ERROR the trait bound `Box: Trait` is not satisfied } diff --git a/src/test/compile-fail/dst-bad-coercions.rs b/src/test/compile-fail/dst-bad-coercions.rs index 883c16b089581..ff2e2d619a5ec 100644 --- a/src/test/compile-fail/dst-bad-coercions.rs +++ b/src/test/compile-fail/dst-bad-coercions.rs @@ -23,11 +23,13 @@ pub fn main() { let x: *const S = &S; let y: &S = x; //~ ERROR mismatched types let y: &T = x; //~ ERROR mismatched types + //~^ ERROR the trait bound `*const S: T` is not satisfied // Test that we cannot convert from *-ptr to &S and &T (mut version) let x: *mut S = &mut S; let y: &S = x; //~ ERROR mismatched types let y: &T = x; //~ ERROR mismatched types + //~^ ERROR the trait bound `*mut S: T` is not satisfied // Test that we cannot convert an immutable ptr to a mutable one using *-ptrs let x: &mut T = &S; //~ ERROR mismatched types diff --git a/src/test/compile-fail/issue-13058.rs b/src/test/compile-fail/issue-13058.rs index 408c6d411de90..ed1634441498b 100644 --- a/src/test/compile-fail/issue-13058.rs +++ b/src/test/compile-fail/issue-13058.rs @@ -35,4 +35,5 @@ fn check<'r, I: Iterator, T: Itble<'r, usize, I>>(cont: &T) -> bool fn main() { check((3, 5)); //~^ ERROR mismatched types +//~| HELP try with `&(3, 5)` } From 7d3284ebc18f3d4087d71352421726b2240a7c20 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 10 Nov 2016 00:25:45 +0100 Subject: [PATCH 3/4] Create a new method to run coercion inside probe --- src/librustc_typeck/check/coercion.rs | 30 ++++++++++++++-- src/librustc_typeck/check/demand.rs | 38 ++++++++++----------- src/test/compile-fail/cross-borrow-trait.rs | 4 +-- src/test/compile-fail/dst-bad-coercions.rs | 2 -- src/test/compile-fail/issue-11374.rs | 2 +- 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index c6a1f6cfc0d7f..a769b55c520ba 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -65,8 +65,8 @@ use check::{Diverges, FnCtxt}; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::infer::{Coercion, InferResult, InferOk, TypeTrace}; -use rustc::infer::type_variable::TypeVariableOrigin; -use rustc::traits::{self, ObligationCause, ObligationCauseCode}; +use rustc::infer::type_variable::{TypeVariableOrigin}; +use rustc::traits::{self, /*FulfillmentContext,*/ ObligationCause, ObligationCauseCode}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::ty::{self, LvaluePreference, TypeAndMut, Ty, ClosureSubsts}; @@ -78,6 +78,7 @@ use errors::DiagnosticBuilder; use syntax::abi; use syntax::feature_gate; use syntax::ptr::P; +use syntax_pos; use std::collections::VecDeque; use std::ops::Deref; @@ -722,6 +723,31 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Ok(target) } + /// Same as `try_coerce()`, but without side-effects. + pub fn can_coerce(&self, + expr_ty: Ty<'tcx>, + target: Ty<'tcx>) + -> bool { + // FIXME: This is a hack, but coercion wasn't made to be run + // in a probe. It leaks obligations and bounds and things out + // into the environment. For now we just save-and-restore the + // fulfillment context. + /*let saved_fulfillment_cx = + mem::replace( + &mut *self.inh.fulfillment_cx.borrow_mut(), + FulfillmentContext::new());*/ + let source = self.resolve_type_vars_with_obligations(expr_ty); + debug!("coercion::can({:?} -> {:?})", source, target); + + let cause = self.cause(syntax_pos::DUMMY_SP, ObligationCauseCode::ExprAssignable); + let coerce = Coerce::new(self, cause); + let result = self.probe(|_| coerce.coerce::(&[], source, target)).is_ok(); + + //*self.inh.fulfillment_cx.borrow_mut() = saved_fulfillment_cx; + + result + } + /// Given some expressions, their known unified type and another expression, /// tries to unify the types, potentially inserting coercions on any of the /// provided expressions and returns their LUB (aka "common supertype"). diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 2933f35abfb6a..0fd98232becfa 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -10,16 +10,14 @@ use check::FnCtxt; -use rustc::ty::Ty; -use rustc::infer::{InferOk, TypeOrigin}; +use rustc::infer::InferOk; use rustc::traits::ObligationCause; -use rustc::ty; use syntax::ast; use syntax_pos::{self, Span}; use rustc::hir; use rustc::hir::def::Def; -use rustc::ty::{self, AssociatedItem}; +use rustc::ty::{self, Ty, AssociatedItem}; use errors::DiagnosticBuilder; use super::method::probe; @@ -81,24 +79,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) { let cause = self.misc(expr.span); let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); - let suggestions = if let Some(suggestions) = self.check_ref(expr, - checked_ty, - expected) { - suggestions + let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); + if let Some(suggestion) = self.check_ref(expr, + checked_ty, + expected) { + err.help(&suggestion); } else { let mode = probe::Mode::MethodCall; - self.probe_for_return_type(syntax_pos::DUMMY_SP, - mode, - expected, - checked_ty, - ast::DUMMY_NODE_ID) + let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, + mode, + expected, + checked_ty, + ast::DUMMY_NODE_ID); + if suggestions.len() > 0 { + err.help(&format!("here are some functions which \ + might fulfill your needs:\n - {}", + self.get_best_match(&suggestions).join("\n"))); + } } - let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); - if suggestions.len() > 0 { - err.help(&format!("here are some functions which \ - might fulfill your needs:\n{}", - self.get_best_match(&suggestions).join("\n"))); - }; err.emit(); } } @@ -188,7 +186,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.tcx.mk_region(ty::ReStatic), checked_ty), }; - if self.try_coerce(expr, ref_ty, expected).is_ok() { + if self.can_coerce(ref_ty, expected) { if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) { return Some(format!("try with `{}{}`", match mutability.mutbl { diff --git a/src/test/compile-fail/cross-borrow-trait.rs b/src/test/compile-fail/cross-borrow-trait.rs index ee67a30fa1c37..847a82c082651 100644 --- a/src/test/compile-fail/cross-borrow-trait.rs +++ b/src/test/compile-fail/cross-borrow-trait.rs @@ -17,9 +17,7 @@ impl Trait for Foo {} pub fn main() { let x: Box = Box::new(Foo); - let _y: &Trait = x; //~ ERROR mismatched types + let _y: &Trait = x; //~ ERROR E0308 //~| expected type `&Trait` //~| found type `std::boxed::Box` - //~| expected &Trait, found box - //~| ERROR the trait bound `Box: Trait` is not satisfied } diff --git a/src/test/compile-fail/dst-bad-coercions.rs b/src/test/compile-fail/dst-bad-coercions.rs index ff2e2d619a5ec..883c16b089581 100644 --- a/src/test/compile-fail/dst-bad-coercions.rs +++ b/src/test/compile-fail/dst-bad-coercions.rs @@ -23,13 +23,11 @@ pub fn main() { let x: *const S = &S; let y: &S = x; //~ ERROR mismatched types let y: &T = x; //~ ERROR mismatched types - //~^ ERROR the trait bound `*const S: T` is not satisfied // Test that we cannot convert from *-ptr to &S and &T (mut version) let x: *mut S = &mut S; let y: &S = x; //~ ERROR mismatched types let y: &T = x; //~ ERROR mismatched types - //~^ ERROR the trait bound `*mut S: T` is not satisfied // Test that we cannot convert an immutable ptr to a mutable one using *-ptrs let x: &mut T = &S; //~ ERROR mismatched types diff --git a/src/test/compile-fail/issue-11374.rs b/src/test/compile-fail/issue-11374.rs index f78786a2889da..1e444a6bebf9b 100644 --- a/src/test/compile-fail/issue-11374.rs +++ b/src/test/compile-fail/issue-11374.rs @@ -33,5 +33,5 @@ pub fn for_stdin<'a>() -> Container<'a> { fn main() { let mut c = for_stdin(); let mut v = Vec::new(); - c.read_to(v); //~ ERROR mismatched types + c.read_to(v); //~ ERROR E0308 } From 7ce1eb77c77ce0a56efc7d11ac8003e6a95208d0 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 21 Apr 2017 13:28:31 +0200 Subject: [PATCH 4/4] Update ui test --- src/librustc_typeck/check/coercion.rs | 23 ++++------------------ src/librustc_typeck/check/demand.rs | 2 +- src/test/ui/span/coerce-suggestions.rs | 1 - src/test/ui/span/coerce-suggestions.stderr | 14 +++++-------- 4 files changed, 10 insertions(+), 30 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index a769b55c520ba..d21b5f739bd7b 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -65,8 +65,8 @@ use check::{Diverges, FnCtxt}; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::infer::{Coercion, InferResult, InferOk, TypeTrace}; -use rustc::infer::type_variable::{TypeVariableOrigin}; -use rustc::traits::{self, /*FulfillmentContext,*/ ObligationCause, ObligationCauseCode}; +use rustc::infer::type_variable::TypeVariableOrigin; +use rustc::traits::{self, ObligationCause, ObligationCauseCode}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::ty::{self, LvaluePreference, TypeAndMut, Ty, ClosureSubsts}; @@ -724,28 +724,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } /// Same as `try_coerce()`, but without side-effects. - pub fn can_coerce(&self, - expr_ty: Ty<'tcx>, - target: Ty<'tcx>) - -> bool { - // FIXME: This is a hack, but coercion wasn't made to be run - // in a probe. It leaks obligations and bounds and things out - // into the environment. For now we just save-and-restore the - // fulfillment context. - /*let saved_fulfillment_cx = - mem::replace( - &mut *self.inh.fulfillment_cx.borrow_mut(), - FulfillmentContext::new());*/ + pub fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool { let source = self.resolve_type_vars_with_obligations(expr_ty); debug!("coercion::can({:?} -> {:?})", source, target); let cause = self.cause(syntax_pos::DUMMY_SP, ObligationCauseCode::ExprAssignable); let coerce = Coerce::new(self, cause); - let result = self.probe(|_| coerce.coerce::(&[], source, target)).is_ok(); - - //*self.inh.fulfillment_cx.borrow_mut() = saved_fulfillment_cx; - - result + self.probe(|_| coerce.coerce::(&[], source, target)).is_ok() } /// Given some expressions, their known unified type and another expression, diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 0fd98232becfa..4cc3f2dacdfe9 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -93,7 +93,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ast::DUMMY_NODE_ID); if suggestions.len() > 0 { err.help(&format!("here are some functions which \ - might fulfill your needs:\n - {}", + might fulfill your needs:\n{}", self.get_best_match(&suggestions).join("\n"))); } } diff --git a/src/test/ui/span/coerce-suggestions.rs b/src/test/ui/span/coerce-suggestions.rs index 3177e858ff4fd..bc3122bf71c0e 100644 --- a/src/test/ui/span/coerce-suggestions.rs +++ b/src/test/ui/span/coerce-suggestions.rs @@ -32,7 +32,6 @@ fn main() { //~| NOTE types differ in mutability //~| NOTE expected type `&mut std::string::String` //~| NOTE found type `&std::string::String` - //~| HELP try with `&mut y` test2(&y); //~^ ERROR E0308 //~| NOTE types differ in mutability diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index 6a70b8ff851d7..220b2f471da9a 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -18,11 +18,7 @@ error[E0308]: mismatched types | = note: expected type `&str` found type `std::string::String` - = help: here are some functions which might fulfill your needs: - - .as_str() - - .trim() - - .trim_left() - - .trim_right() + = help: try with `&String::new()` error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:30:10 @@ -34,18 +30,18 @@ error[E0308]: mismatched types found type `&std::string::String` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:36:11 + --> $DIR/coerce-suggestions.rs:35:11 | -36 | test2(&y); +35 | test2(&y); | ^^ types differ in mutability | = note: expected type `&mut i32` found type `&std::string::String` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:42:9 + --> $DIR/coerce-suggestions.rs:41:9 | -42 | f = box f; +41 | f = box f; | ^^^^^ cyclic type of infinite size | = note: expected type `_`