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:
parent
5d41be3629
commit
6ea1fbb52b
3 changed files with 47 additions and 23 deletions
|
|
@ -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>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue