From 021d97d1c5e053176961970ccbcad9f2802e9c91 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 26 Oct 2016 23:53:47 +0200 Subject: [PATCH] Move suggestion list creation to coerce check --- src/librustc/infer/error_reporting.rs | 104 ++++++++++--- src/librustc_typeck/check/demand.rs | 89 ++++++++++- src/librustc_typeck/check/method/mod.rs | 28 +++- src/librustc_typeck/check/method/probe.rs | 171 +++++++++++++++++----- 4 files changed, 332 insertions(+), 60 deletions(-) diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs index 98cbd742b109..8fed2a820951 100644 --- a/src/librustc/infer/error_reporting.rs +++ b/src/librustc/infer/error_reporting.rs @@ -83,13 +83,14 @@ use hir::def_id::DefId; use infer; use middle::region; use traits::{ObligationCause, ObligationCauseCode}; -use ty::{self, ImplOrTraitItem, Ty, TyCtxt, TypeFoldable}; +use ty::{self, TyCtxt, TypeFoldable}; use ty::{Region, ReFree}; use ty::error::TypeError; use std::cell::{Cell, RefCell}; use std::char::from_u32; use std::fmt; +//use std::rc::Rc; use syntax::ast; use syntax::ptr::P; use syntax::symbol::Symbol; @@ -233,6 +234,22 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } +/*struct MethodInfo<'tcx> { + ast: Option, + id: DefId, + item: Rc>, +} + +impl<'tcx> MethodInfo<'tcx> { + fn new(ast: Option, id: DefId, item: Rc>) -> MethodInfo { + MethodInfo { + ast: ast, + id: id, + item: item, + } + } +}*/ + impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub fn report_region_errors(&self, errors: &Vec>) { @@ -583,36 +600,53 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } - if let Some((found, (expected_ty, _))) = self.get_ids(values) { + //if let Some((found, (expected_ty, expected))) = self.get_ids(values) { // look for expected with found id - self.tcx.populate_inherent_implementations_for_type_if_necessary(found); + /*self.tcx.populate_inherent_implementations_for_type_if_necessary(found); if let Some(impl_infos) = self.tcx.inherent_impls.borrow().get(&found) { - let mut methods: Vec<(Option, DefId, ImplOrTraitItem<'tcx>)> = Vec::new(); + let mut methods: Vec = Vec::new(); for impl_ in impl_infos { methods.append(&mut self.tcx .impl_or_trait_items(*impl_) .iter() - .map(|&did| (None, did, self.tcx.impl_or_trait_item(did))) - .filter(|&(_, _, ref x)| { - self.matches_return_type(x, &expected_ty) + .map(|&did| MethodInfo::new(None, did, Rc::new(self.tcx.impl_or_trait_item(did)))) + .filter(|ref x| { + self.matches_return_type(&*x.item, &expected_ty) }) .collect()); } - let safe_suggestions: Vec<_> = methods.iter() - .map(|&(_, ref id, ref x)| (self.find_attr(*id, "safe_suggestion"), id, x)) - .filter(|&(ref res, _, _)| res.is_some()) - .collect(); - if safe_suggestions.len() > 0 { - for (_, _, method) in safe_suggestions { - println!("safe ==> {:?}", method.name()); - } - } else { - for &(_, _, ref method) in methods.iter() { - println!("not safe ==> {:?}", method.name()); + for did in self.tcx.sess.cstore.implementations_of_trait(None) { + if did == found { + methods.append( + self.tcx.sess.cstore.impl_or_trait_items(did) + .iter() + .map(|&did| MethodInfo::new(None, did, Rc::new(self.tcx.impl_or_trait_item(did)))) + .filter(|ref x| { + self.matches_return_type(&*x.item, &expected_ty) + }) + .collect()); + ; } } - } - } + let safe_suggestions: Vec<_> = + methods.iter() + .map(|ref x| MethodInfo::new(self.find_attr(x.id, "safe_suggestion"), x.id, x.item.clone())) + .filter(|ref x| x.ast.is_some()) + .collect(); + if safe_suggestions.len() > 0 { + println!("safe"); + self.get_best_match(&safe_suggestions); + } else { + println!("not safe"); + self.get_best_match(&methods); + }*/ + /*let mode = probe::Mode::MethodCall; + if let Ok(ret) = self.probe_return(DUMMY_SP, mode, expected, found, DUMMY_NODE_ID) { + println!("got it"); + } else { + println!("sad..."); + }*/ + //} } diag.span_label(span, &terr); @@ -625,6 +659,23 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.tcx.note_and_explain_type_err(diag, terr, span); } + /*fn get_best_match(&self, methods: &[MethodInfo<'tcx>]) -> String { + let no_argument_methods: Vec<&MethodInfo> = + methods.iter() + .filter(|ref x| self.has_not_input_arg(&*x.item)) + .collect(); + if no_argument_methods.len() > 0 { + for ref method in no_argument_methods { + println!("best match ==> {:?}", method.item.name()); + } + } else { + for ref method in methods.iter() { + println!("not best ==> {:?}", method.item.name()); + } + } + String::new() + } + fn find_attr(&self, def_id: DefId, attr_name: &str) -> Option { for item in self.tcx.get_attrs(def_id).iter() { if item.check_name(attr_name) { @@ -632,7 +683,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } None - } + }*/ pub fn report_and_explain_type_error(&self, trace: TypeTrace<'tcx>, @@ -662,6 +713,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } + /*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, + } + } + fn matches_return_type(&self, method: &ImplOrTraitItem<'tcx>, expected: &ty::Ty<'tcx>) -> bool { match *method { ImplOrTraitItem::MethodTraitItem(ref x) => { @@ -714,7 +774,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } _ => None, } - } + }*/ fn expected_found_str>( &self, diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index ef1c08bdab54..db711e4a31db 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -14,8 +14,32 @@ use rustc::ty::Ty; 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::ty::{self, ImplOrTraitItem}; + +use hir::def_id::DefId; + +use std::rc::Rc; + +use super::method::probe; + +struct MethodInfo<'tcx> { + ast: Option, + id: DefId, + item: Rc>, +} + +impl<'tcx> MethodInfo<'tcx> { + fn new(ast: Option, id: DefId, item: Rc>) -> MethodInfo { + MethodInfo { + ast: ast, + id: id, + item: item, + } + } +} impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Requires that the two types unify, and prints an error message if @@ -57,7 +81,70 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { 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); + let mode = probe::Mode::MethodCall; + if let Ok(methods) = self.probe_return(syntax_pos::DUMMY_SP, mode, expected, + checked_ty, ast::DUMMY_NODE_ID) { + let suggestions: Vec<_> = + methods.iter() + .filter_map(|ref x| { + if let Some(id) = self.get_impl_id(&x.item) { + Some(MethodInfo::new(None, id, Rc::new(x.item.clone()))) + } else { + None + }}) + .collect(); + let safe_suggestions: Vec<_> = + suggestions.iter() + .map(|ref x| MethodInfo::new( + self.find_attr(x.id, "safe_suggestion"), + x.id, + x.item.clone())) + .filter(|ref x| x.ast.is_some()) + .collect(); + if safe_suggestions.len() > 0 { + self.get_best_match(&safe_suggestions); + } else { + self.get_best_match(&suggestions); + } + } self.report_mismatched_types(&cause, expected, expr_ty, e); } } + + fn get_best_match(&self, methods: &[MethodInfo<'tcx>]) -> String { + if methods.len() == 1 { + println!("unique match ==> {:?}", methods[0].item.name()); + return String::new(); + } + let no_argument_methods: Vec<&MethodInfo> = + methods.iter() + .filter(|ref x| self.has_not_input_arg(&*x.item)) + .collect(); + if no_argument_methods.len() > 0 { + for ref method in no_argument_methods { + println!("best match ==> {:?}", method.item.name()); + } + } else { + for ref method in methods.iter() { + println!("not best ==> {:?}", method.item.name()); + } + } + String::new() + } + + fn get_impl_id(&self, impl_: &ImplOrTraitItem<'tcx>) -> Option { + match *impl_ { + ty::ImplOrTraitItem::MethodTraitItem(ref m) => Some((*m).def_id), + _ => None, + } + } + + 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, + } + } } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index b29eab780e03..0af32c8fe6dc 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -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> { @@ -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_method(span, mode, method_name, self_ty, call_expr.id)?.remove(0); if let Some(import_id) = pick.import_id { self.tcx.used_trait_imports.borrow_mut().insert(import_id); @@ -328,7 +328,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr_id: ast::NodeId) -> Result> { let mode = probe::Mode::Path; - let pick = self.probe_method(span, mode, method_name, self_ty, expr_id)?; + let picks = self.probe_method(span, mode, method_name, self_ty, expr_id)?; + let pick = &picks[0]; if let Some(import_id) = pick.import_id { self.tcx.used_trait_imports.borrow_mut().insert(import_id); @@ -353,4 +354,25 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { -> Option { self.tcx.associated_items(def_id).find(|item| item.name == item_name) } + + fn matches_return_type(&self, method: &ty::ImplOrTraitItem<'tcx>, + expected: ty::Ty<'tcx>) -> bool { + match *method { + ty::ImplOrTraitItem::MethodTraitItem(ref x) => { + self.can_sub_types(x.fty.sig.skip_binder().output, expected).is_ok() + } + _ => false, + } + } + + pub fn impl_or_return_item(&self, + def_id: DefId, + return_type: ty::Ty<'tcx>) + -> Option> { + self.tcx + .impl_or_trait_items(def_id) + .iter() + .map(|&did| self.tcx.impl_or_trait_item(did)) + .find(|m| self.matches_return_type(m, return_type)) + } } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 5cb0804b1bca..32de37764f6e 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -32,11 +32,16 @@ use std::rc::Rc; use self::CandidateKind::*; pub use self::PickKind::*; +pub enum LookingFor<'tcx> { + MethodName(ast::Name), + ReturnType(Ty<'tcx>), +} + 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>, @@ -129,7 +134,7 @@ pub enum PickKind<'tcx> { ty::PolyTraitRef<'tcx>), } -pub type PickResult<'tcx> = Result, MethodError<'tcx>>; +pub type PickResult<'tcx> = Result>, MethodError<'tcx>>; #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum Mode { @@ -144,6 +149,20 @@ pub enum Mode { } impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { + pub fn probe_return(&self, + span: Span, + mode: Mode, + return_type: Ty<'tcx>, + self_ty: Ty<'tcx>, + scope_expr_id: ast::NodeId) + -> PickResult<'tcx> { + debug!("probe(self_ty={:?}, return_type={}, scope_expr_id={})", + self_ty, + return_type, + scope_expr_id); + self._probe(span, mode, LookingFor::ReturnType(return_type), self_ty, scope_expr_id) + } + pub fn probe_method(&self, span: Span, mode: Mode, @@ -155,6 +174,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self_ty, item_name, scope_expr_id); + self._probe(span, mode, LookingFor::MethodName(item_name), self_ty, scope_expr_id) + } + + fn _probe(&self, + span: Span, + mode: Mode, + looking_for: LookingFor<'tcx>, + self_ty: Ty<'tcx>, + scope_expr_id: ast::NodeId) + -> PickResult<'tcx> { // FIXME(#18741) -- right now, creating the steps involves evaluating the // `*` operator, which registers obligations that then escape into @@ -207,14 +236,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // 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() }) } - fn create_steps(&self, span: Span, self_ty: Ty<'tcx>) -> Option>> { + fn create_steps(&self, + span: Span, + self_ty: Ty<'tcx>) + -> Option>> { // FIXME: we don't need to create the entire steps in one pass let mut autoderef = self.autoderef(span, self_ty); @@ -247,13 +280,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Some(steps) } + + pub fn find_attr(&self, def_id: DefId, attr_name: &str) -> Option { + for item in self.tcx.get_attrs(def_id).iter() { + if item.check_name(attr_name) { + return Some(item.clone()); + } + } + None + } } 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 +303,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'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(), @@ -584,6 +626,16 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { Ok(()) } + fn matches_return_type(&self, method: &ty::ImplOrTraitItem<'tcx>, + expected: &ty::Ty<'tcx>) -> bool { + match *method { + ty::ImplOrTraitItem::MethodTraitItem(ref x) => { + self.can_sub_types(x.fty.sig.skip_binder().output, expected).is_ok() + } + _ => false, + } + } + fn assemble_extension_candidates_for_trait(&mut self, trait_def_id: DefId) -> Result<(), MethodError<'tcx>> { @@ -591,8 +643,19 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { 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 trait_items = self.tcx.associated_items(trait_def_id); + let maybe_item = match self.looking_for { + LookingFor::MethodName(item_name) => { + trait_items.iter() + .find(|item| item.name == item_name) + } + LookingFor::ReturnType(item_ty) => { + trait_items.iter() + .find(|item| { + self.matches_return_type(item, &item_ty) + }) + } + }; let item = match maybe_item { Some(i) => i, None => { @@ -837,8 +900,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { // THE ACTUAL SEARCH fn pick(mut self) -> PickResult<'tcx> { - if let Some(r) = self.pick_core() { - return r; + if let Some(ret) = self.pick_core() { + return ret; } let static_candidates = mem::replace(&mut self.static_candidates, vec![]); @@ -855,6 +918,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, '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| { @@ -896,9 +960,19 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { fn pick_core(&mut self) -> Option> { let steps = self.steps.clone(); + let mut ret = Vec::new(); - // find the first step that works - steps.iter().filter_map(|step| self.pick_step(step)).next() + for step in steps.iter() { + match self.pick_step(step) { + Some(Ok(mut elems)) => ret.append(&mut elems), + _ => {} + } + } + if ret.len() < 1 { + None + } else { + Some(Ok(ret)) + } } fn pick_step(&mut self, step: &CandidateStep<'tcx>) -> Option> { @@ -929,16 +1003,18 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { } self.pick_method(step.self_ty).map(|r| { - r.map(|mut pick| { - pick.autoderefs = step.autoderefs; + r.map(|mut picks| { + for pick in picks.iter_mut() { + pick.autoderefs = step.autoderefs; - // Insert a `&*` or `&mut *` if this is a reference type: - if let ty::TyRef(_, mt) = step.self_ty.sty { - pick.autoderefs += 1; - pick.autoref = Some(mt.mutbl); + // Insert a `&*` or `&mut *` if this is a reference type: + if let ty::TyRef(_, mt) = step.self_ty.sty { + pick.autoderefs += 1; + pick.autoref = Some(mt.mutbl); + } } - pick + picks }) }) } @@ -949,9 +1025,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { // In general, during probing we erase regions. See // `impl_self_ty()` for an explanation. let region = tcx.mk_region(ty::ReErased); + let mut res = Vec::new(); // Search through mutabilities in order to find one where pick works: - [hir::MutImmutable, hir::MutMutable] + for _ in [hir::MutImmutable, hir::MutMutable] .iter() .filter_map(|&m| { let autoref_ty = tcx.mk_ref(region, @@ -960,19 +1037,26 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { mutbl: m, }); self.pick_method(autoref_ty).map(|r| { - r.map(|mut pick| { - pick.autoderefs = step.autoderefs; - pick.autoref = Some(m); - pick.unsize = if step.unsize { - Some(step.self_ty) - } else { - None - }; - pick + r.map(|mut picks| { + for pick in picks.iter_mut() { + pick.autoderefs = step.autoderefs; + pick.autoref = Some(m); + pick.unsize = if step.unsize { + Some(step.self_ty) + } else { + None + }; + } + res.append(&mut picks); }) }) - }) - .nth(0) + }) {} + + if res.len() < 1 { + None + } else { + Some(Ok(res)) + } } fn pick_method(&mut self, self_ty: Ty<'tcx>) -> Option> { @@ -1002,7 +1086,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { probes: &[Candidate<'tcx>], possibly_unsatisfied_predicates: &mut Vec>) -> Option> { - let mut applicable_candidates: Vec<_> = probes.iter() + let applicable_candidates: Vec<_> = probes.iter() .filter(|&probe| self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) .collect(); @@ -1011,7 +1095,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { if applicable_candidates.len() > 1 { match self.collapse_candidates_to_trait_pick(&applicable_candidates[..]) { Some(pick) => { - return Some(Ok(pick)); + return Some(Ok(vec![pick])); } None => {} } @@ -1022,7 +1106,15 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { return Some(Err(MethodError::Ambiguity(sources))); } - applicable_candidates.pop().map(|probe| Ok(probe.to_unadjusted_pick())) + let ret: Vec<_> = applicable_candidates.iter() + .map(|probe| probe.to_unadjusted_pick()) + .collect(); + + if ret.len() < 1 { + None + } else { + Some(Ok(ret)) + } } fn consider_probe(&self, @@ -1262,6 +1354,17 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { fn associated_item(&self, def_id: DefId) -> Option { self.fcx.associated_item(def_id, self.item_name) } + + fn impl_or_trait_item(&self, def_id: DefId) -> Option> { + match self.looking_for { + LookingFor::MethodName(name) => { + self.fcx.impl_or_trait_item(def_id, name) + } + LookingFor::ReturnType(return_ty) => { + self.fcx.impl_or_return_item(def_id, return_ty) + } + } + } } impl<'tcx> Candidate<'tcx> {