Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ref suggestion #37658

Merged
merged 4 commits into from
Apr 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions src/librustc_typeck/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -722,6 +723,16 @@ 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 {
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);
self.probe(|_| coerce.coerce::<hir::Expr>(&[], source, target)).is_ok()
}

/// 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").
Expand Down
89 changes: 75 additions & 14 deletions src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@


use check::FnCtxt;
use rustc::ty::Ty;
use rustc::infer::{InferOk};
use rustc::infer::InferOk;
use rustc::traits::ObligationCause;

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;
Expand Down Expand Up @@ -80,18 +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 mode = probe::Mode::MethodCall;
let suggestions = 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 \
might fulfill your needs:\n{}",
self.get_best_match(&suggestions).join("\n")));
};
if let Some(suggestion) = self.check_ref(expr,
checked_ty,
expected) {
err.help(&suggestion);
} else {
let mode = probe::Mode::MethodCall;
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")));
}
}
err.emit();
}
}
Expand Down Expand Up @@ -140,4 +145,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<String> {
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.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 {
hir::Mutability::MutMutable => "&mut ",
hir::Mutability::MutImmutable => "&",
},
&src));
}
}
None
}
_ => None,
}
}
}
1 change: 0 additions & 1 deletion src/test/compile-fail/coercion-slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this a regression in error reporting?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have absolutely no clue from where this change comes since my code just added suggestion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably a stray side-effect? Try wrapping in probe if you haven't already. In fact, you probably want a version of try_coerce with no side-effects (call it probe_coerce).

}
2 changes: 1 addition & 1 deletion src/test/compile-fail/cross-borrow-trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl Trait for Foo {}

pub fn main() {
let x: Box<Trait> = Box::new(Foo);
let _y: &Trait = x; //~ ERROR mismatched types
let _y: &Trait = x; //~ ERROR E0308
//~| expected type `&Trait`
//~| found type `std::boxed::Box<Trait>`
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/issue-11374.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
1 change: 1 addition & 0 deletions src/test/compile-fail/issue-13058.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ fn check<'r, I: Iterator<Item=usize>, T: Itble<'r, usize, I>>(cont: &T) -> bool
fn main() {
check((3, 5));
//~^ ERROR mismatched types
//~| HELP try with `&(3, 5)`
}
1 change: 0 additions & 1 deletion src/test/ui/span/coerce-suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 5 additions & 9 deletions src/test/ui/span/coerce-suggestions.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 `_`
Expand Down