diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 9b58334e65805a6822fa4446bf9271ff81dd037b..3e7cc0b1e3e9c2150b8235b1576b478f938901ee 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1367,9 +1367,10 @@ pub fn report_mismatched_types(&self, cause: &ObligationCause<'tcx>, expected: Ty<'tcx>, actual: Ty<'tcx>, - err: TypeError<'tcx>) { + err: TypeError<'tcx>) + -> DiagnosticBuilder<'tcx> { let trace = TypeTrace::types(cause, true, expected, 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, diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 3b7c46ef7fe175c890b46700bcc3bff19cf12101..638345608c2f5d9743595101ff8352a506d07d4d 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -543,6 +543,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>, } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 624201eaab69660218509dcd676f949d34bb6f75..ff50ee11b390e674a703b9cdd9837a22676da5af 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -481,7 +481,7 @@ pub fn check_match(&self, } else { (result_ty, arm_ty) }; - self.report_mismatched_types(&cause, expected, found, e); + self.report_mismatched_types(&cause, expected, found, e).emit(); self.tcx.types.err } }; diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index e72dba858c562160dbc42da65d4a53cc8f60109e..b4647df3f4f0a6bed7b874084131c44b06379522 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -131,10 +131,18 @@ fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option> { Some(self.fcx.resolve_type_vars_if_possible(&normalized.value)) } + /// Returns the final type, generating an error if it is an + /// unresolved inference variable. pub fn unambiguous_final_ty(&self) -> Ty<'tcx> { self.fcx.structurally_resolved_type(self.span, self.cur_ty) } + /// Returns the final type we ended up with, which may well be an + /// inference variable (we will resolve it first, if possible). + pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> { + self.fcx.resolve_type_vars_if_possible(&self.cur_ty) + } + pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I) where I: IntoIterator { diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index ef1c08bdab549e3b82a7c02237249c6ce4717b80..393d9341a08432151876eb73941c90d3e5d50964 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -14,8 +14,13 @@ use rustc::infer::{InferOk}; use rustc::traits::ObligationCause; -use syntax_pos::Span; +use syntax::ast; +use syntax_pos::{self, Span}; use rustc::hir; +use rustc::hir::def::Def; +use rustc::ty::{self, AssociatedItem}; + +use super::method::probe; impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Requires that the two types unify, and prints an error message if @@ -27,7 +32,7 @@ pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { self.register_predicates(obligations); }, Err(e) => { - self.report_mismatched_types(&cause, expected, actual, e); + self.report_mismatched_types(&cause, expected, actual, e).emit(); } } } @@ -46,7 +51,7 @@ pub fn demand_eqtype_with_origin(&self, self.register_predicates(obligations); }, Err(e) => { - self.report_mismatched_types(cause, expected, actual, e); + self.report_mismatched_types(cause, expected, actual, e).emit(); } } } @@ -57,7 +62,65 @@ pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty if let Err(e) = self.try_coerce(expr, checked_ty, expected) { let cause = self.misc(expr.span); let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); - self.report_mismatched_types(&cause, expected, expr_ty, e); + 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))); + }; + err.emit(); + } + } + + fn format_method_suggestion(&self, method: &AssociatedItem) -> String { + format!(".{}({})", + method.name, + if self.has_no_input_arg(method) { + "" + } else { + "..." + }) + } + + fn display_suggested_methods(&self, methods: &[AssociatedItem]) -> String { + methods.iter() + .take(5) + .map(|method| self.format_method_suggestion(&*method)) + .collect::>() + .join("\n - ") + } + + fn get_best_match(&self, methods: &[AssociatedItem]) -> String { + let no_argument_methods: Vec<_> = + methods.iter() + .filter(|ref x| self.has_no_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_no_input_arg(&self, method: &AssociatedItem) -> bool { + match method.def() { + Def::Method(def_id) => { + match self.tcx.item_type(def_id).sty { + ty::TypeVariants::TyFnDef(_, _, fty) => { + fty.sig.skip_binder().inputs().len() == 1 + } + _ => false, + } + } + _ => false, } } } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index b29eab780e0355df72e290dc5d79dfea719ac727..6353b45200bc977db61b89d3b985c86d27fb18eb 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -30,9 +30,11 @@ pub use self::suggest::AllTraitsVec; mod confirm; -mod probe; +pub mod probe; mod suggest; +use self::probe::IsSuggestion; + pub enum MethodError<'tcx> { // Did not find an applicable method, but we did find various near-misses that may work. NoMatch(NoMatchData<'tcx>), @@ -91,7 +93,8 @@ pub fn method_exists(&self, 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, IsSuggestion(false), + self_ty, call_expr_id) { Ok(..) => true, Err(NoMatch(..)) => false, Err(Ambiguity(..)) => true, @@ -130,7 +133,8 @@ pub fn lookup_method(&self, 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, IsSuggestion(false), + self_ty, call_expr.id)?; if let Some(import_id) = pick.import_id { self.tcx.used_trait_imports.borrow_mut().insert(import_id); @@ -328,7 +332,8 @@ pub fn resolve_ufcs(&self, expr_id: ast::NodeId) -> Result> { 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, IsSuggestion(false), + self_ty, expr_id)?; if let Some(import_id) = pick.import_id { self.tcx.used_trait_imports.borrow_mut().insert(import_id); diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 5cb0804b1bca19e1972fb953237cd821ac296b0b..06158366f5cda18dd7cbdf28cf337ca4aabffb36 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -16,12 +16,12 @@ use check::FnCtxt; use hir::def_id::DefId; use hir::def::Def; -use rustc::infer::InferOk; use rustc::ty::subst::{Subst, Substs}; use rustc::traits::{self, ObligationCause}; use rustc::ty::{self, Ty, ToPolyTraitRef, TraitRef, TypeFoldable}; use rustc::infer::type_variable::TypeVariableOrigin; use rustc::util::nodemap::FxHashSet; +use rustc::infer::{self, InferOk}; use syntax::ast; use syntax_pos::Span; use rustc::hir; @@ -32,11 +32,24 @@ use self::CandidateKind::*; pub use self::PickKind::*; +pub enum LookingFor<'tcx> { + /// looking for methods with the given name; this is the normal case + MethodName(ast::Name), + + /// looking for methods that return a given type; this is used to + /// assemble suggestions + ReturnType(Ty<'tcx>), +} + +/// Boolean flag used to indicate if this search is for a suggestion +/// or not. If true, we can allow ambiguity and so forth. +pub struct IsSuggestion(pub bool); + struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, span: Span, mode: Mode, - item_name: ast::Name, + looking_for: LookingFor<'tcx>, steps: Rc>>, opt_simplified_steps: Option>, inherent_candidates: Vec>, @@ -144,18 +157,72 @@ pub enum Mode { } impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { - pub fn probe_method(&self, - span: Span, - mode: Mode, - item_name: ast::Name, - self_ty: Ty<'tcx>, - scope_expr_id: ast::NodeId) - -> PickResult<'tcx> { + /// This is used to offer suggestions to users. It returns methods + /// that could have been called which have the desired return + /// type. Some effort is made to rule out methods that, if called, + /// would result in an error (basically, the same criteria we + /// would use to decide if a method is a plausible fit for + /// ambiguity purposes). + pub fn probe_for_return_type(&self, + span: Span, + mode: Mode, + return_type: Ty<'tcx>, + self_ty: Ty<'tcx>, + scope_expr_id: ast::NodeId) + -> Vec { + debug!("probe(self_ty={:?}, return_type={}, scope_expr_id={})", + self_ty, + return_type, + scope_expr_id); + let method_names = + self.probe_op(span, mode, LookingFor::ReturnType(return_type), IsSuggestion(true), + self_ty, scope_expr_id, + |probe_cx| Ok(probe_cx.candidate_method_names())) + .unwrap_or(vec![]); + method_names + .iter() + .flat_map(|&method_name| { + match self.probe_for_name(span, mode, method_name, IsSuggestion(true), self_ty, + scope_expr_id) { + Ok(pick) => Some(pick.item), + Err(_) => None, + } + }) + .collect() + } + + pub fn probe_for_name(&self, + span: Span, + mode: Mode, + item_name: ast::Name, + is_suggestion: IsSuggestion, + self_ty: Ty<'tcx>, + scope_expr_id: ast::NodeId) + -> PickResult<'tcx> { debug!("probe(self_ty={:?}, item_name={}, scope_expr_id={})", self_ty, item_name, scope_expr_id); + self.probe_op(span, + mode, + LookingFor::MethodName(item_name), + is_suggestion, + self_ty, + scope_expr_id, + |probe_cx| probe_cx.pick()) + } + fn probe_op(&'a self, + span: Span, + mode: Mode, + looking_for: LookingFor<'tcx>, + is_suggestion: IsSuggestion, + self_ty: Ty<'tcx>, + scope_expr_id: ast::NodeId, + op: OP) + -> Result> + where OP: FnOnce(ProbeContext<'a, 'gcx, 'tcx>) -> Result> + { // FIXME(#18741) -- right now, creating the steps involves evaluating the // `*` operator, which registers obligations that then escape into // the global fulfillment context and thus has global @@ -164,7 +231,7 @@ pub fn probe_method(&self, // think cause spurious errors. Really though this part should // take place in the `self.probe` below. let steps = if mode == Mode::MethodCall { - match self.create_steps(span, self_ty) { + match self.create_steps(span, self_ty, is_suggestion) { Some(steps) => steps, None => { return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(), @@ -207,14 +274,19 @@ pub fn probe_method(&self, // that we create during the probe process are removed later self.probe(|_| { let mut probe_cx = - ProbeContext::new(self, span, mode, item_name, steps, opt_simplified_steps); + ProbeContext::new(self, span, mode, looking_for, + steps, opt_simplified_steps); probe_cx.assemble_inherent_candidates(); probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?; - probe_cx.pick() + op(probe_cx) }) } - fn create_steps(&self, span: Span, self_ty: Ty<'tcx>) -> Option>> { + fn create_steps(&self, + span: Span, + self_ty: Ty<'tcx>, + is_suggestion: IsSuggestion) + -> Option>> { // FIXME: we don't need to create the entire steps in one pass let mut autoderef = self.autoderef(span, self_ty); @@ -228,8 +300,22 @@ fn create_steps(&self, span: Span, self_ty: Ty<'tcx>) -> Option { + // Ended in an inference variable. If we are doing + // a real method lookup, this is a hard error (it's an + // ambiguity and we can't make progress). + if !is_suggestion.0 { + let t = self.structurally_resolved_type(span, final_ty); + assert_eq!(t, self.tcx.types.err); + return None + } else { + // If we're just looking for suggestions, + // though, ambiguity is no big thing, we can + // just ignore it. + } + } ty::TyArray(elem_ty, _) => { let dereferences = steps.len() - 1; @@ -253,7 +339,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, span: Span, mode: Mode, - item_name: ast::Name, + looking_for: LookingFor<'tcx>, steps: Vec>, opt_simplified_steps: Option>) -> ProbeContext<'a, 'gcx, 'tcx> { @@ -261,7 +347,7 @@ fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, fcx: fcx, span: span, mode: mode, - item_name: item_name, + looking_for: looking_for, inherent_candidates: Vec::new(), extension_candidates: Vec::new(), impl_dups: FxHashSet(), @@ -410,44 +496,40 @@ fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId) { debug!("assemble_inherent_impl_probe {:?}", impl_def_id); - let item = match self.associated_item(impl_def_id) { - Some(m) => m, - None => { - return; - } // No method with correct name on this impl - }; + for item in self.impl_or_trait_item(impl_def_id) { + if !self.has_applicable_self(&item) { + // No receiver declared. Not a candidate. + self.record_static_candidate(ImplSource(impl_def_id)); + continue + } - if !self.has_applicable_self(&item) { - // No receiver declared. Not a candidate. - return self.record_static_candidate(ImplSource(impl_def_id)); - } + if !item.vis.is_accessible_from(self.body_id, &self.tcx.map) { + self.private_candidate = Some(item.def()); + continue + } - if !item.vis.is_accessible_from(self.body_id, &self.tcx.map) { - self.private_candidate = Some(item.def()); - return; - } + let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id); + let impl_ty = impl_ty.subst(self.tcx, impl_substs); - let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id); - let impl_ty = impl_ty.subst(self.tcx, impl_substs); - - // Determine the receiver type that the method itself expects. - let xform_self_ty = self.xform_self_ty(&item, impl_ty, impl_substs); - - // We can't use normalize_associated_types_in as it will pollute the - // fcx's fulfillment context after this probe is over. - let cause = traits::ObligationCause::misc(self.span, self.body_id); - let mut selcx = &mut traits::SelectionContext::new(self.fcx); - let traits::Normalized { value: xform_self_ty, obligations } = - traits::normalize(selcx, cause, &xform_self_ty); - debug!("assemble_inherent_impl_probe: xform_self_ty = {:?}", - xform_self_ty); - - self.inherent_candidates.push(Candidate { - xform_self_ty: xform_self_ty, - item: item, - kind: InherentImplCandidate(impl_substs, obligations), - import_id: self.import_id, - }); + // Determine the receiver type that the method itself expects. + let xform_self_ty = self.xform_self_ty(&item, impl_ty, impl_substs); + + // We can't use normalize_associated_types_in as it will pollute the + // fcx's fulfillment context after this probe is over. + let cause = traits::ObligationCause::misc(self.span, self.body_id); + let mut selcx = &mut traits::SelectionContext::new(self.fcx); + let traits::Normalized { value: xform_self_ty, obligations } = + traits::normalize(selcx, cause, &xform_self_ty); + debug!("assemble_inherent_impl_probe: xform_self_ty = {:?}", + xform_self_ty); + + self.inherent_candidates.push(Candidate { + xform_self_ty: xform_self_ty, + item: item, + kind: InherentImplCandidate(impl_substs, obligations), + import_id: self.import_id, + }); + } } fn assemble_inherent_candidates_from_object(&mut self, @@ -540,17 +622,12 @@ fn elaborate_bounds(&mut self, bounds: &[ty::PolyTraitRef<'tcx>], mut mk_cand let tcx = self.tcx; for bound_trait_ref in traits::transitive_bounds(tcx, bounds) { - let item = match self.associated_item(bound_trait_ref.def_id()) { - Some(v) => v, - None => { - continue; + for item in self.impl_or_trait_item(bound_trait_ref.def_id()) { + if !self.has_applicable_self(&item) { + self.record_static_candidate(TraitSource(bound_trait_ref.def_id())); + } else { + mk_cand(self, bound_trait_ref, item); } - }; - - if !self.has_applicable_self(&item) { - self.record_static_candidate(TraitSource(bound_trait_ref.def_id())); - } else { - mk_cand(self, bound_trait_ref, item); } } } @@ -584,36 +661,45 @@ fn assemble_extension_candidates_for_all_traits(&mut self) -> Result<(), MethodE Ok(()) } + pub fn matches_return_type(&self, method: &ty::AssociatedItem, + expected: ty::Ty<'tcx>) -> bool { + match method.def() { + Def::Method(def_id) => { + let fty = self.tcx.item_type(def_id).fn_sig(); + self.probe(|_| { + let substs = self.fresh_substs_for_item(self.span, method.def_id); + let output = fty.output().subst(self.tcx, substs); + let (output, _) = self.replace_late_bound_regions_with_fresh_var( + self.span, infer::FnCall, &output); + self.can_sub_types(output, expected).is_ok() + }) + } + _ => false, + } + } + fn assemble_extension_candidates_for_trait(&mut self, trait_def_id: DefId) -> Result<(), MethodError<'tcx>> { debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); - // Check whether `trait_def_id` defines a method with suitable name: - let maybe_item = self.tcx.associated_items(trait_def_id) - .find(|item| item.name == self.item_name); - let item = match maybe_item { - Some(i) => i, - None => { - return Ok(()); + for item in self.impl_or_trait_item(trait_def_id) { + // Check whether `trait_def_id` defines a method with suitable name: + if !self.has_applicable_self(&item) { + debug!("method has inapplicable self"); + self.record_static_candidate(TraitSource(trait_def_id)); + continue; } - }; - // Check whether `trait_def_id` defines a method with suitable name: - if !self.has_applicable_self(&item) { - debug!("method has inapplicable self"); - self.record_static_candidate(TraitSource(trait_def_id)); - return Ok(()); - } + self.assemble_extension_candidates_for_trait_impls(trait_def_id, item.clone()); - self.assemble_extension_candidates_for_trait_impls(trait_def_id, item.clone()); + self.assemble_closure_candidates(trait_def_id, item.clone())?; - self.assemble_closure_candidates(trait_def_id, item.clone())?; + self.assemble_projection_candidates(trait_def_id, item.clone()); - self.assemble_projection_candidates(trait_def_id, item.clone()); - - self.assemble_where_clause_candidates(trait_def_id, item.clone()); + self.assemble_where_clause_candidates(trait_def_id, item.clone()); + } Ok(()) } @@ -833,10 +919,30 @@ fn assemble_where_clause_candidates(&mut self, } } + fn candidate_method_names(&self) -> Vec { + let mut set = FxHashSet(); + let mut names: Vec<_> = + self.inherent_candidates + .iter() + .chain(&self.extension_candidates) + .map(|candidate| candidate.item.name) + .filter(|&name| set.insert(name)) + .collect(); + + // sort them by the name so we have a stable result + names.sort_by_key(|n| n.as_str()); + names + } + /////////////////////////////////////////////////////////////////////////// // THE ACTUAL SEARCH fn pick(mut self) -> PickResult<'tcx> { + assert!(match self.looking_for { + LookingFor::MethodName(_) => true, + LookingFor::ReturnType(_) => false, + }); + if let Some(r) = self.pick_core() { return r; } @@ -855,6 +961,7 @@ fn pick(mut self) -> PickResult<'tcx> { let out_of_scope_traits = match self.pick_core() { Some(Ok(p)) => vec![p.item.container.id()], + //Some(Ok(p)) => p.iter().map(|p| p.item.container().id()).collect(), Some(Err(MethodError::Ambiguity(v))) => { v.into_iter() .map(|source| { @@ -1257,10 +1364,20 @@ fn erase_late_bound_regions(&self, value: &ty::Binder) -> T self.tcx.erase_late_bound_regions(value) } - /// Find item with name `item_name` defined in impl/trait `def_id` - /// and return it, or `None`, if no such item was defined there. - fn associated_item(&self, def_id: DefId) -> Option { - self.fcx.associated_item(def_id, self.item_name) + /// Find the method with the appropriate name (or return type, as the case may be). + fn impl_or_trait_item(&self, def_id: DefId) -> Vec { + match self.looking_for { + LookingFor::MethodName(name) => { + self.fcx.associated_item(def_id, name).map_or(Vec::new(), |x| vec![x]) + } + LookingFor::ReturnType(return_ty) => { + self.tcx + .associated_items(def_id) + .map(|did| self.tcx.associated_item(did.def_id)) + .filter(|m| self.matches_return_type(m, return_ty)) + .collect() + } + } } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 58dff935a16b88388e3dce04cfca168b346058ba..3086721852deb82732883dc9de9e50057d11d718 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2986,7 +2986,7 @@ fn check_then_else(&self, } } Err(e) => { - self.report_mismatched_types(&cause, expected_ty, found_ty, e); + self.report_mismatched_types(&cause, expected_ty, found_ty, e).emit(); self.tcx.types.err } } @@ -3697,7 +3697,7 @@ fn check_expr_kind(&self, match result { Ok(ty) => ctxt.unified = ty, Err(err) => { - self.report_mismatched_types(&cause, ctxt.unified, e_ty, err); + self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit(); } } @@ -3880,7 +3880,7 @@ fn check_expr_kind(&self, match result { Ok(ty) => unified = ty, Err(e) => { - self.report_mismatched_types(&cause, unified, e_ty, e); + self.report_mismatched_types(&cause, unified, e_ty, e).emit(); } } } diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index f575d4d8bab7a5ddd51e48e549ba22e98808be0a..0e5a16987c12e408e9315cadd82ca3848872ae16 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -356,7 +356,7 @@ fn check_implementations_of_coerce_unsized(&self) { infcx.report_mismatched_types(&cause, mk_ptr(mt_b.ty), target, - ty::error::TypeError::Mutability); + ty::error::TypeError::Mutability).emit(); } (mt_a.ty, mt_b.ty, unsize_trait, None) }; diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 50d4c3cd0c994a8fd0e7080a6fc328a6b66f31ce..ec17813ed2a521fb479a0e012d1546890c31af33 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -185,7 +185,7 @@ fn require_same_types<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, true } Err(err) => { - infcx.report_mismatched_types(cause, expected, actual, err); + infcx.report_mismatched_types(cause, expected, actual, err).emit(); false } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 77c53542dcbec3233b3480e812d52feb91ce967b..e04cc11f15e14dc2deba84907cadc8a7b3b49942 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -304,6 +304,7 @@ pub fn new() -> Features { // Allows using `Self` and associated types in struct expressions and patterns. (active, more_struct_aliases, "1.14.0", Some(37544)), + // Allows #[link(..., cfg(..))] (active, link_cfg, "1.14.0", Some(37406)), @@ -314,6 +315,9 @@ pub fn new() -> Features { // Allows #[target_feature(...)] (active, target_feature, "1.15.0", None), + + // Allow safe suggestions for potential type conversions. + (active, safe_suggestion, "1.0.0", Some(37384)), ); declare_features! ( diff --git a/src/test/ui/span/coerce-suggestions.rs b/src/test/ui/span/coerce-suggestions.rs new file mode 100644 index 0000000000000000000000000000000000000000..3177e858ff4fd3b89ada820f849ce3a9889ffc66 --- /dev/null +++ b/src/test/ui/span/coerce-suggestions.rs @@ -0,0 +1,47 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_syntax)] + +fn test(_x: &mut String) {} +fn test2(_x: &mut i32) {} + +fn main() { + let x: usize = String::new(); + //~^ ERROR E0308 + //~| NOTE expected usize, found struct `std::string::String` + //~| NOTE expected type `usize` + //~| NOTE found type `std::string::String` + //~| HELP here are some functions which might fulfill your needs: + let x: &str = String::new(); + //~^ ERROR E0308 + //~| NOTE expected &str, found struct `std::string::String` + //~| NOTE expected type `&str` + //~| NOTE found type `std::string::String` + //~| HELP try with `&String::new()` + let y = String::new(); + test(&y); + //~^ ERROR E0308 + //~| 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 + //~| NOTE expected type `&mut i32` + //~| NOTE found type `&std::string::String` + let f; + f = box f; + //~^ ERROR E0308 + //~| NOTE cyclic type of infinite size + //~| NOTE expected type `_` + //~| NOTE found type `Box<_>` +} diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr new file mode 100644 index 0000000000000000000000000000000000000000..e316466150703c3dbf66ca61c049ab74a2d1c463 --- /dev/null +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -0,0 +1,55 @@ +error[E0308]: mismatched types + --> $DIR/coerce-suggestions.rs:17:20 + | +17 | let x: usize = String::new(); + | ^^^^^^^^^^^^^ expected usize, found struct `std::string::String` + | + = note: expected type `usize` + = note: found type `std::string::String` + = help: here are some functions which might fulfill your needs: + - .capacity() + - .len() + +error[E0308]: mismatched types + --> $DIR/coerce-suggestions.rs:23:19 + | +23 | let x: &str = String::new(); + | ^^^^^^^^^^^^^ expected &str, found struct `std::string::String` + | + = note: expected type `&str` + = note: found type `std::string::String` + = help: here are some functions which might fulfill your needs: + - .as_str() + - .trim() + - .trim_left() + - .trim_right() + +error[E0308]: mismatched types + --> $DIR/coerce-suggestions.rs:30:10 + | +30 | test(&y); + | ^^ types differ in mutability + | + = note: expected type `&mut std::string::String` + = note: found type `&std::string::String` + +error[E0308]: mismatched types + --> $DIR/coerce-suggestions.rs:36:11 + | +36 | test2(&y); + | ^^ types differ in mutability + | + = note: expected type `&mut i32` + = note: found type `&std::string::String` + +error[E0308]: mismatched types + --> $DIR/coerce-suggestions.rs:42:9 + | +42 | f = box f; + | ^^^^^ cyclic type of infinite size + | + = note: expected type `_` + = note: found type `Box<_>` + +error: aborting due to 5 previous errors +