Skip to content

Commit ec5847a

Browse files
nikomatsakisGuillaumeGomez
authored andcommitted
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.
1 parent 579e526 commit ec5847a

File tree

2 files changed

+63
-58
lines changed

2 files changed

+63
-58
lines changed

src/librustc_typeck/check/demand.rs

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ use syntax_pos::{self, Span};
1919
use rustc::hir;
2020
use rustc::ty::{self, ImplOrTraitItem};
2121

22-
use std::rc::Rc;
23-
2422
use super::method::probe;
2523

2624
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
@@ -138,26 +136,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
138136
let mode = probe::Mode::MethodCall;
139137
let suggestions = if let Some(s) = self.check_ref(expr, checked_ty, expected) {
140138
Some(s)
141-
} else if let Ok(methods) = self.probe_for_return_type(syntax_pos::DUMMY_SP,
142-
mode,
143-
expected,
144-
checked_ty,
145-
ast::DUMMY_NODE_ID) {
146-
let suggestions: Vec<_> =
147-
methods.iter()
148-
.map(|ref x| {
149-
Rc::new(x.item.clone())
150-
})
151-
.collect();
139+
} else {
140+
let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP,
141+
mode,
142+
expected,
143+
checked_ty,
144+
ast::DUMMY_NODE_ID);
152145
if suggestions.len() > 0 {
153146
Some(format!("here are some functions which \
154147
might fulfill your needs:\n - {}",
155148
self.get_best_match(&suggestions)))
156149
} else {
157150
None
158151
}
159-
} else {
160-
None
161152
};
162153
let mut err = self.report_mismatched_types(origin, expected, expr_ty, e);
163154
if let Some(suggestions) = suggestions {
@@ -181,16 +172,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
181172
})
182173
}
183174

184-
fn display_suggested_methods(&self, methods: &[Rc<ImplOrTraitItem<'tcx>>]) -> String {
175+
fn display_suggested_methods(&self, methods: &[ImplOrTraitItem<'tcx>]) -> String {
185176
methods.iter()
186177
.take(5)
187178
.map(|method| self.format_method_suggestion(&*method))
188179
.collect::<Vec<String>>()
189180
.join("\n - ")
190181
}
191182

192-
fn get_best_match(&self, methods: &[Rc<ImplOrTraitItem<'tcx>>]) -> String {
193-
let no_argument_methods: Vec<Rc<ImplOrTraitItem<'tcx>>> =
183+
fn get_best_match(&self, methods: &[ImplOrTraitItem<'tcx>]) -> String {
184+
let no_argument_methods: Vec<_> =
194185
methods.iter()
195186
.filter(|ref x| self.has_not_input_arg(&*x))
196187
.map(|x| x.clone())

src/librustc_typeck/check/method/probe.rs

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -150,19 +150,36 @@ pub enum Mode {
150150
}
151151

152152
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
153+
/// This is used to offer suggestions to users. It returns methods
154+
/// that could have been called which have the desired return
155+
/// type. Some effort is made to rule out methods that, if called,
156+
/// would result in an error (basically, the same criteria we
157+
/// would use to decide if a method is a plausible fit for
158+
/// ambiguity purposes).
153159
pub fn probe_for_return_type(&self,
154160
span: Span,
155161
mode: Mode,
156162
return_type: Ty<'tcx>,
157163
self_ty: Ty<'tcx>,
158164
scope_expr_id: ast::NodeId)
159-
-> PickResult<'tcx> {
165+
-> Vec<ty::ImplOrTraitItem<'tcx>> {
160166
debug!("probe(self_ty={:?}, return_type={}, scope_expr_id={})",
161167
self_ty,
162168
return_type,
163169
scope_expr_id);
164-
self.probe_op(span, mode, LookingFor::ReturnType(return_type), self_ty, scope_expr_id,
165-
|probe_cx| probe_cx.pick())
170+
let method_names =
171+
self.probe_op(span, mode, LookingFor::ReturnType(return_type), self_ty, scope_expr_id,
172+
|probe_cx| Ok(probe_cx.candidate_method_names()))
173+
.unwrap_or(vec![]);
174+
method_names
175+
.iter()
176+
.flat_map(|&method_name| {
177+
match self.probe_for_name(span, mode, method_name, self_ty, scope_expr_id) {
178+
Ok(picks) => picks.into_iter().map(move |pick| pick.item).collect(),
179+
Err(_) => vec![],
180+
}
181+
})
182+
.collect()
166183
}
167184

168185
pub fn probe_for_name(&self,
@@ -184,15 +201,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
184201
|probe_cx| probe_cx.pick())
185202
}
186203

187-
fn probe_op<'a,OP,R>(&'a self,
188-
span: Span,
189-
mode: Mode,
190-
looking_for: LookingFor<'tcx>,
191-
self_ty: Ty<'tcx>,
192-
scope_expr_id: ast::NodeId,
193-
op: OP)
194-
-> R
195-
where OP: FnOnce(&mut ProbeContext<'a, 'gcx, 'tcx>) -> R
204+
fn probe_op<OP,R>(&'a self,
205+
span: Span,
206+
mode: Mode,
207+
looking_for: LookingFor<'tcx>,
208+
self_ty: Ty<'tcx>,
209+
scope_expr_id: ast::NodeId,
210+
op: OP)
211+
-> Result<R, MethodError<'tcx>>
212+
where OP: FnOnce(ProbeContext<'a, 'gcx, 'tcx>) -> Result<R, MethodError<'tcx>>
196213
{
197214
// FIXME(#18741) -- right now, creating the steps involves evaluating the
198215
// `*` operator, which registers obligations that then escape into
@@ -249,7 +266,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
249266
steps, opt_simplified_steps);
250267
probe_cx.assemble_inherent_candidates();
251268
probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?;
252-
op(&mut probe_cx)
269+
op(probe_cx)
253270
})
254271
}
255272

@@ -894,10 +911,30 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
894911
}
895912
}
896913

914+
fn candidate_method_names(&self) -> Vec<ast::Name> {
915+
let mut set = FnvHashSet();
916+
let mut names: Vec<_> =
917+
self.inherent_candidates
918+
.iter()
919+
.chain(&self.extension_candidates)
920+
.map(|candidate| candidate.item.name())
921+
.filter(|&name| set.insert(name))
922+
.collect();
923+
924+
// sort them by the name so we have a stable result
925+
names.sort_by_key(|n| n.as_str());
926+
names
927+
}
928+
897929
///////////////////////////////////////////////////////////////////////////
898930
// THE ACTUAL SEARCH
899931

900932
fn pick(mut self) -> PickResult<'tcx> {
933+
assert!(match self.looking_for {
934+
LookingFor::MethodName(_) => true,
935+
LookingFor::ReturnType(_) => false,
936+
});
937+
901938
if let Some(ret) = self.pick_core() {
902939
return ret;
903940
}
@@ -959,33 +996,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
959996
fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
960997
let steps = self.steps.clone();
961998

962-
match self.looking_for {
963-
LookingFor::MethodName(_) => {
964-
// find the first step that works
965-
steps.iter()
966-
.filter_map(|step| self.pick_step(step))
967-
.next()
968-
}
969-
LookingFor::ReturnType(_) => {
970-
// Normally, we stop at the first step where we find a positive match.
971-
// But when we are scanning for methods with a suitable return type,
972-
// these methods have distinct names and hence may not shadow one another
973-
// (also, this is just for hints, so precision is less important).
974-
let mut ret = Vec::new();
975-
976-
for step in steps.iter() {
977-
match self.pick_step(step) {
978-
Some(Ok(mut elems)) => ret.append(&mut elems),
979-
_ => {}
980-
}
981-
}
982-
if ret.len() < 1 {
983-
None
984-
} else {
985-
Some(Ok(ret))
986-
}
987-
}
988-
}
999+
// find the first step that works
1000+
steps.iter()
1001+
.filter_map(|step| self.pick_step(step))
1002+
.next()
9891003
}
9901004

9911005
fn pick_step(&mut self, step: &CandidateStep<'tcx>) -> Option<PickResult<'tcx>> {

0 commit comments

Comments
 (0)