diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2016-12-09 10:40:15 -0500 |
|---|---|---|
| committer | Guillaume Gomez <guillaume1.gomez@gmail.com> | 2016-12-20 11:37:15 +0100 |
| commit | 6ea1fbb52bc2f82a78d7b592bdfed2b05e6fb666 (patch) | |
| tree | 80497f405af1af13cc61d6c46f4a3362a1811db9 /src | |
| parent | 5d41be3629067ddfd16fc672e75c2fcaf95c27ff (diff) | |
| download | rust-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.rs | 8 | ||||
| -rw-r--r-- | src/librustc_typeck/check/method/mod.rs | 11 | ||||
| -rw-r--r-- | src/librustc_typeck/check/method/probe.rs | 51 |
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; |
