recover from unresolved inference variable at end of autoderef

When we are scanning for suggestions, an unresolved inference variable
is not a hard error.
This commit is contained in:
Niko Matsakis 2016-12-09 10:40:15 -05:00 committed by Guillaume Gomez
parent 5d41be3629
commit 6ea1fbb52b
3 changed files with 47 additions and 23 deletions

View file

@ -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>
{

View file

@ -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>),
@ -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,
@ -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);
@ -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);

View file

@ -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>,
@ -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,
}
@ -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> {
@ -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())
@ -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)
@ -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(),
@ -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
@ -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;