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

Start of implementation of proposal for E0308 #37388

Closed
Closed
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
4 changes: 2 additions & 2 deletions src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1493,15 +1493,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
origin: TypeOrigin,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
err: TypeError<'tcx>) {
err: TypeError<'tcx>) -> DiagnosticBuilder<'tcx> {
let trace = TypeTrace {
origin: origin,
values: Types(ExpectedFound {
expected: expected,
found: actual
})
};
self.report_and_explain_type_error(trace, &err).emit();
self.report_and_explain_type_error(trace, &err)
}

pub fn report_conflicting_default_types(&self,
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ pub struct ProjectionTy<'tcx> {
pub struct BareFnTy<'tcx> {
pub unsafety: hir::Unsafety,
pub abi: abi::Abi,
/// Signature (inputs and output) of this function type.
pub sig: PolyFnSig<'tcx>,
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} else {
(result_ty, arm_ty)
};
self.report_mismatched_types(origin, expected, found, e);
self.report_mismatched_types(origin, expected, found, e).emit();
self.tcx.types.err
}
};
Expand Down
142 changes: 138 additions & 4 deletions src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ use check::FnCtxt;
use rustc::ty::Ty;
use rustc::infer::{InferOk, TypeOrigin};

use syntax_pos::Span;
use syntax::ast;
use syntax_pos::{self, Span};
use rustc::hir;
use rustc::ty::{self, ImplOrTraitItem};

use super::method::probe;

impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Requires that the two types unify, and prints an error message if
Expand All @@ -27,7 +31,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
assert!(obligations.is_empty());
},
Err(e) => {
self.report_mismatched_types(origin, expected, actual, e);
self.report_mismatched_types(origin, expected, actual, e).emit();
}
}
}
Expand All @@ -47,18 +51,148 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
assert!(obligations.is_empty());
},
Err(e) => {
self.report_mismatched_types(origin, expected, actual, e);
self.report_mismatched_types(origin, expected, actual, e).emit();
}
}
}

/// 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(_, expected_mutability),
&ty::TyRef(_, checked_mutability)) => {
// check if there is a mutability difference
if checked_mutability.mutbl == hir::Mutability::MutImmutable &&
checked_mutability.mutbl != expected_mutability.mutbl &&
self.can_sub_types(&checked_mutability.ty,
expected_mutability.ty).is_ok() {
if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
return Some(format!("try with `&mut {}`", &src.replace("&", "")));
}
}
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,
}
}

// Checks that the type of `expr` can be coerced to `expected`.
pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) {
let expected = self.resolve_type_vars_with_obligations(expected);
if let Err(e) = self.try_coerce(expr, checked_ty, expected) {
let origin = TypeOrigin::Misc(expr.span);
let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
self.report_mismatched_types(origin, expected, expr_ty, e);
let mode = probe::Mode::MethodCall;
let suggestions = if let Some(s) = self.check_ref(expr, checked_ty, expected) {
Some(s)
} else {
let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP,
mode,
expected,
checked_ty,
ast::DUMMY_NODE_ID);
if suggestions.len() > 0 {
Some(format!("here are some functions which \
might fulfill your needs:\n - {}",
self.get_best_match(&suggestions)))
} else {
None
}
};
let mut err = self.report_mismatched_types(origin, expected, expr_ty, e);
if let Some(suggestions) = suggestions {
err.help(&suggestions);
}
err.emit();
}
}

fn format_method_suggestion(&self, method: &ImplOrTraitItem<'tcx>) -> String {
format!(".{}({})",
method.name(),
if self.has_not_input_arg(method) {
""
} else {
"..."
})
}

fn display_suggested_methods(&self, methods: &[ImplOrTraitItem<'tcx>]) -> String {
methods.iter()
.take(5)
.map(|method| self.format_method_suggestion(&*method))
.collect::<Vec<String>>()
.join("\n - ")
}

fn get_best_match(&self, methods: &[ImplOrTraitItem<'tcx>]) -> String {
let no_argument_methods: Vec<_> =
methods.iter()
.filter(|ref x| self.has_not_input_arg(&*x))
.map(|x| x.clone())
.collect();
if no_argument_methods.len() > 0 {
self.display_suggested_methods(&no_argument_methods)
} else {
self.display_suggested_methods(&methods)
}
}

// This function checks if the method isn't static and takes other arguments than `self`.
fn has_not_input_arg(&self, method: &ImplOrTraitItem<'tcx>) -> bool {
match *method {
ImplOrTraitItem::MethodTraitItem(ref x) => {
x.fty.sig.skip_binder().inputs.len() == 1
}
_ => false,
}
}
}
8 changes: 4 additions & 4 deletions src/librustc_typeck/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub use self::CandidateSource::*;
pub use self::suggest::AllTraitsVec;

mod confirm;
mod probe;
pub mod probe;
mod suggest;

pub enum MethodError<'tcx> {
Expand Down Expand Up @@ -91,7 +91,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
allow_private: bool)
-> bool {
let mode = probe::Mode::MethodCall;
match self.probe_method(span, mode, method_name, self_ty, call_expr_id) {
match self.probe_for_name(span, mode, method_name, self_ty, call_expr_id) {
Ok(..) => true,
Err(NoMatch(..)) => false,
Err(Ambiguity(..)) => true,
Expand Down Expand Up @@ -130,7 +130,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {

let mode = probe::Mode::MethodCall;
let self_ty = self.resolve_type_vars_if_possible(&self_ty);
let pick = self.probe_method(span, mode, method_name, self_ty, call_expr.id)?;
let pick = self.probe_for_name(span, mode, method_name, self_ty, call_expr.id)?;

if let Some(import_id) = pick.import_id {
self.tcx.used_trait_imports.borrow_mut().insert(import_id);
Expand Down Expand Up @@ -353,7 +353,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr_id: ast::NodeId)
-> Result<Def, MethodError<'tcx>> {
let mode = probe::Mode::Path;
let pick = self.probe_method(span, mode, method_name, self_ty, expr_id)?;
let pick = self.probe_for_name(span, mode, method_name, self_ty, expr_id)?;

if let Some(import_id) = pick.import_id {
self.tcx.used_trait_imports.borrow_mut().insert(import_id);
Expand Down
Loading