Skip to content

Commit 6ea1fbb

Browse files
nikomatsakisGuillaumeGomez
authored andcommitted
recover from unresolved inference variable at end of autoderef
When we are scanning for suggestions, an unresolved inference variable is not a hard error.
1 parent 5d41be3 commit 6ea1fbb

File tree

3 files changed

+47
-23
lines changed

3 files changed

+47
-23
lines changed

src/librustc_typeck/check/autoderef.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,18 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
131131
Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
132132
}
133133

134+
/// Returns the final type, generating an error if it is an
135+
/// unresolved inference variable.
134136
pub fn unambiguous_final_ty(&self) -> Ty<'tcx> {
135137
self.fcx.structurally_resolved_type(self.span, self.cur_ty)
136138
}
137139

140+
/// Returns the final type we ended up with, which may well be an
141+
/// inference variable (we will resolve it first, if possible).
142+
pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> {
143+
self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
144+
}
145+
138146
pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
139147
where I: IntoIterator<Item = &'b hir::Expr>
140148
{

src/librustc_typeck/check/method/mod.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ mod confirm;
3333
pub mod probe;
3434
mod suggest;
3535

36+
use self::probe::IsSuggestion;
37+
3638
pub enum MethodError<'tcx> {
3739
// Did not find an applicable method, but we did find various near-misses that may work.
3840
NoMatch(NoMatchData<'tcx>),
@@ -91,7 +93,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
9193
allow_private: bool)
9294
-> bool {
9395
let mode = probe::Mode::MethodCall;
94-
match self.probe_for_name(span, mode, method_name, self_ty, call_expr_id) {
96+
match self.probe_for_name(span, mode, method_name, IsSuggestion(false),
97+
self_ty, call_expr_id) {
9598
Ok(..) => true,
9699
Err(NoMatch(..)) => false,
97100
Err(Ambiguity(..)) => true,
@@ -130,7 +133,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
130133

131134
let mode = probe::Mode::MethodCall;
132135
let self_ty = self.resolve_type_vars_if_possible(&self_ty);
133-
let pick = self.probe_for_name(span, mode, method_name, self_ty, call_expr.id)?;
136+
let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false),
137+
self_ty, call_expr.id)?;
134138

135139
if let Some(import_id) = pick.import_id {
136140
self.tcx.used_trait_imports.borrow_mut().insert(import_id);
@@ -328,7 +332,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
328332
expr_id: ast::NodeId)
329333
-> Result<Def, MethodError<'tcx>> {
330334
let mode = probe::Mode::Path;
331-
let pick = self.probe_for_name(span, mode, method_name, self_ty, expr_id)?;
335+
let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false),
336+
self_ty, expr_id)?;
332337

333338
if let Some(import_id) = pick.import_id {
334339
self.tcx.used_trait_imports.borrow_mut().insert(import_id);

src/librustc_typeck/check/method/probe.rs

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,17 @@ use self::CandidateKind::*;
3333
pub use self::PickKind::*;
3434

3535
pub enum LookingFor<'tcx> {
36+
/// looking for methods with the given name; this is the normal case
3637
MethodName(ast::Name),
38+
39+
/// looking for methods that return a given type; this is used to
40+
/// assemble suggestions
3741
ReturnType(Ty<'tcx>),
3842
}
3943

40-
impl<'tcx> LookingFor<'tcx> {
41-
pub fn is_method_name(&self) -> bool {
42-
match *self {
43-
LookingFor::MethodName(_) => true,
44-
_ => false,
45-
}
46-
}
47-
48-
pub fn is_return_type(&self) -> bool {
49-
match *self {
50-
LookingFor::ReturnType(_) => true,
51-
_ => false,
52-
}
53-
}
54-
}
44+
/// Boolean flag used to indicate if this search is for a suggestion
45+
/// or not. If true, we can allow ambiguity and so forth.
46+
pub struct IsSuggestion(pub bool);
5547

5648
struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
5749
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
@@ -183,13 +175,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
183175
return_type,
184176
scope_expr_id);
185177
let method_names =
186-
self.probe_op(span, mode, LookingFor::ReturnType(return_type), self_ty, scope_expr_id,
178+
self.probe_op(span, mode, LookingFor::ReturnType(return_type), IsSuggestion(true),
179+
self_ty, scope_expr_id,
187180
|probe_cx| Ok(probe_cx.candidate_method_names()))
188181
.unwrap_or(vec![]);
189182
method_names
190183
.iter()
191184
.flat_map(|&method_name| {
192-
match self.probe_for_name(span, mode, method_name, self_ty, scope_expr_id) {
185+
match self.probe_for_name(span, mode, method_name, IsSuggestion(true), self_ty, scope_expr_id) {
193186
Ok(pick) => Some(pick.item),
194187
Err(_) => None,
195188
}
@@ -201,6 +194,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
201194
span: Span,
202195
mode: Mode,
203196
item_name: ast::Name,
197+
is_suggestion: IsSuggestion,
204198
self_ty: Ty<'tcx>,
205199
scope_expr_id: ast::NodeId)
206200
-> PickResult<'tcx> {
@@ -211,6 +205,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
211205
self.probe_op(span,
212206
mode,
213207
LookingFor::MethodName(item_name),
208+
is_suggestion,
214209
self_ty,
215210
scope_expr_id,
216211
|probe_cx| probe_cx.pick())
@@ -220,6 +215,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
220215
span: Span,
221216
mode: Mode,
222217
looking_for: LookingFor<'tcx>,
218+
is_suggestion: IsSuggestion,
223219
self_ty: Ty<'tcx>,
224220
scope_expr_id: ast::NodeId,
225221
op: OP)
@@ -234,7 +230,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
234230
// think cause spurious errors. Really though this part should
235231
// take place in the `self.probe` below.
236232
let steps = if mode == Mode::MethodCall {
237-
match self.create_steps(span, self_ty) {
233+
match self.create_steps(span, self_ty, is_suggestion) {
238234
Some(steps) => steps,
239235
None => {
240236
return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(),
@@ -287,7 +283,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
287283

288284
fn create_steps(&self,
289285
span: Span,
290-
self_ty: Ty<'tcx>)
286+
self_ty: Ty<'tcx>,
287+
is_suggestion: IsSuggestion)
291288
-> Option<Vec<CandidateStep<'tcx>>> {
292289
// FIXME: we don't need to create the entire steps in one pass
293290

@@ -302,8 +299,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
302299
})
303300
.collect();
304301

305-
let final_ty = autoderef.unambiguous_final_ty();
302+
let final_ty = autoderef.maybe_ambiguous_final_ty();
306303
match final_ty.sty {
304+
ty::TyInfer(ty::TyVar(_)) => {
305+
// Ended in an inference variable. If we are doing
306+
// a real method lookup, this is a hard error (it's an
307+
// ambiguity and we can't make progress).
308+
if !is_suggestion.0 {
309+
let t = self.structurally_resolved_type(span, final_ty);
310+
assert_eq!(t, self.tcx.types.err);
311+
return None
312+
} else {
313+
// If we're just looking for suggestions,
314+
// though, ambiguity is no big thing, we can
315+
// just ignore it.
316+
}
317+
}
307318
ty::TyArray(elem_ty, _) => {
308319
let dereferences = steps.len() - 1;
309320

0 commit comments

Comments
 (0)