about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-12-09 10:40:15 -0500
committerGuillaume Gomez <guillaume1.gomez@gmail.com>2016-12-20 11:37:15 +0100
commit6ea1fbb52bc2f82a78d7b592bdfed2b05e6fb666 (patch)
tree80497f405af1af13cc61d6c46f4a3362a1811db9 /src
parent5d41be3629067ddfd16fc672e75c2fcaf95c27ff (diff)
downloadrust-6ea1fbb52bc2f82a78d7b592bdfed2b05e6fb666.tar.gz
rust-6ea1fbb52bc2f82a78d7b592bdfed2b05e6fb666.zip
recover from unresolved inference variable at end of autoderef
When we are scanning for suggestions, an unresolved inference variable
is not a hard error.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/check/autoderef.rs8
-rw-r--r--src/librustc_typeck/check/method/mod.rs11
-rw-r--r--src/librustc_typeck/check/method/probe.rs51
3 files changed, 47 insertions, 23 deletions
diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs
index e72dba858c5..b4647df3f4f 100644
--- a/src/librustc_typeck/check/autoderef.rs
+++ b/src/librustc_typeck/check/autoderef.rs
@@ -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>
     {
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index 80437dcdbfe..6353b45200b 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -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);
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index e478638a5e0..c29c46d146e 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -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;