Skip to content

Commit

Permalink
recover from unresolved inference variable at end of autoderef
Browse files Browse the repository at this point in the history
When we are scanning for suggestions, an unresolved inference variable
is not a hard error.
  • Loading branch information
nikomatsakis authored and GuillaumeGomez committed Dec 20, 2016
1 parent 5d41be3 commit 6ea1fbb
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 23 deletions.
8 changes: 8 additions & 0 deletions src/librustc_typeck/check/autoderef.rs
Expand Up @@ -131,10 +131,18 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
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<Item = &'b hir::Expr>
{
Expand Down
11 changes: 8 additions & 3 deletions src/librustc_typeck/check/method/mod.rs
Expand Up @@ -33,6 +33,8 @@ mod confirm;
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>),
Expand Down Expand Up @@ -91,7 +93,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
allow_private: bool)
-> bool {
let mode = probe::Mode::MethodCall;
match self.probe_for_name(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,
Expand Down Expand Up @@ -130,7 +133,8 @@ 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_for_name(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);
Expand Down Expand Up @@ -328,7 +332,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr_id: ast::NodeId)
-> Result<Def, MethodError<'tcx>> {
let mode = probe::Mode::Path;
let pick = self.probe_for_name(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);
Expand Down
51 changes: 31 additions & 20 deletions src/librustc_typeck/check/method/probe.rs
Expand Up @@ -33,25 +33,17 @@ 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>),
}

impl<'tcx> LookingFor<'tcx> {
pub fn is_method_name(&self) -> bool {
match *self {
LookingFor::MethodName(_) => true,
_ => false,
}
}

pub fn is_return_type(&self) -> bool {
match *self {
LookingFor::ReturnType(_) => true,
_ => false,
}
}
}
/// 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>,
Expand Down Expand Up @@ -183,13 +175,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
return_type,
scope_expr_id);
let method_names =
self.probe_op(span, mode, LookingFor::ReturnType(return_type), self_ty, scope_expr_id,
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, self_ty, scope_expr_id) {
match self.probe_for_name(span, mode, method_name, IsSuggestion(true), self_ty, scope_expr_id) {
Ok(pick) => Some(pick.item),
Err(_) => None,
}
Expand All @@ -201,6 +194,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
span: Span,
mode: Mode,
item_name: ast::Name,
is_suggestion: IsSuggestion,
self_ty: Ty<'tcx>,
scope_expr_id: ast::NodeId)
-> PickResult<'tcx> {
Expand All @@ -211,6 +205,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.probe_op(span,
mode,
LookingFor::MethodName(item_name),
is_suggestion,
self_ty,
scope_expr_id,
|probe_cx| probe_cx.pick())
Expand All @@ -220,6 +215,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
span: Span,
mode: Mode,
looking_for: LookingFor<'tcx>,
is_suggestion: IsSuggestion,
self_ty: Ty<'tcx>,
scope_expr_id: ast::NodeId,
op: OP)
Expand All @@ -234,7 +230,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// 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(),
Expand Down Expand Up @@ -287,7 +283,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {

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

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

let final_ty = autoderef.unambiguous_final_ty();
let final_ty = autoderef.maybe_ambiguous_final_ty();
match final_ty.sty {
ty::TyInfer(ty::TyVar(_)) => {
// 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;

Expand Down

0 comments on commit 6ea1fbb

Please sign in to comment.