From ec5847a698bc3d64bd419a872d3c60e100999406 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Nov 2016 15:36:28 -0500 Subject: [PATCH] rewrite return type probing to use the "probe by name" path We now do two phases. First, we gather up the list of candidates with suitable return types and extract their names. Then we filter those to see which are applicable and we return that. It might be nice to do the "filter by return type" as a second step, but this is ok for now. --- src/librustc_typeck/check/demand.rs | 27 +++---- src/librustc_typeck/check/method/probe.rs | 94 +++++++++++++---------- 2 files changed, 63 insertions(+), 58 deletions(-) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index de9fb7549352..4704193da0c4 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -19,8 +19,6 @@ use syntax_pos::{self, Span}; use rustc::hir; use rustc::ty::{self, ImplOrTraitItem}; -use std::rc::Rc; - use super::method::probe; impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { @@ -138,17 +136,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mode = probe::Mode::MethodCall; let suggestions = if let Some(s) = self.check_ref(expr, checked_ty, expected) { Some(s) - } else if let Ok(methods) = self.probe_for_return_type(syntax_pos::DUMMY_SP, - mode, - expected, - checked_ty, - ast::DUMMY_NODE_ID) { - let suggestions: Vec<_> = - methods.iter() - .map(|ref x| { - Rc::new(x.item.clone()) - }) - .collect(); + } 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 - {}", @@ -156,8 +149,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } else { None } - } else { - None }; let mut err = self.report_mismatched_types(origin, expected, expr_ty, e); if let Some(suggestions) = suggestions { @@ -181,7 +172,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }) } - fn display_suggested_methods(&self, methods: &[Rc>]) -> String { + fn display_suggested_methods(&self, methods: &[ImplOrTraitItem<'tcx>]) -> String { methods.iter() .take(5) .map(|method| self.format_method_suggestion(&*method)) @@ -189,8 +180,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .join("\n - ") } - fn get_best_match(&self, methods: &[Rc>]) -> String { - let no_argument_methods: Vec>> = + 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()) diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index a233098a7516..0711390afbe3 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -150,19 +150,36 @@ pub enum Mode { } impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, '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) - -> PickResult<'tcx> { + -> Vec> { debug!("probe(self_ty={:?}, return_type={}, scope_expr_id={})", self_ty, return_type, scope_expr_id); - self.probe_op(span, mode, LookingFor::ReturnType(return_type), self_ty, scope_expr_id, - |probe_cx| probe_cx.pick()) + let method_names = + self.probe_op(span, mode, LookingFor::ReturnType(return_type), 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, self_ty, scope_expr_id) { + Ok(picks) => picks.into_iter().map(move |pick| pick.item).collect(), + Err(_) => vec![], + } + }) + .collect() } pub fn probe_for_name(&self, @@ -184,15 +201,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { |probe_cx| probe_cx.pick()) } - fn probe_op<'a,OP,R>(&'a self, - span: Span, - mode: Mode, - looking_for: LookingFor<'tcx>, - self_ty: Ty<'tcx>, - scope_expr_id: ast::NodeId, - op: OP) - -> R - where OP: FnOnce(&mut ProbeContext<'a, 'gcx, 'tcx>) -> R + fn probe_op(&'a self, + span: Span, + mode: Mode, + looking_for: LookingFor<'tcx>, + 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 @@ -249,7 +266,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { steps, opt_simplified_steps); probe_cx.assemble_inherent_candidates(); probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?; - op(&mut probe_cx) + op(probe_cx) }) } @@ -894,10 +911,30 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { } } + fn candidate_method_names(&self) -> Vec { + let mut set = FnvHashSet(); + 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(ret) = self.pick_core() { return ret; } @@ -959,33 +996,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { fn pick_core(&mut self) -> Option> { let steps = self.steps.clone(); - match self.looking_for { - LookingFor::MethodName(_) => { - // find the first step that works - steps.iter() - .filter_map(|step| self.pick_step(step)) - .next() - } - LookingFor::ReturnType(_) => { - // Normally, we stop at the first step where we find a positive match. - // But when we are scanning for methods with a suitable return type, - // these methods have distinct names and hence may not shadow one another - // (also, this is just for hints, so precision is less important). - let mut ret = Vec::new(); - - 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)) - } - } - } + // find the first step that works + steps.iter() + .filter_map(|step| self.pick_step(step)) + .next() } fn pick_step(&mut self, step: &CandidateStep<'tcx>) -> Option> {