diff --git a/src/librustc/traits/query/method_autoderef.rs b/src/librustc/traits/query/method_autoderef.rs index e67497915d7c..175883eb2a73 100644 --- a/src/librustc/traits/query/method_autoderef.rs +++ b/src/librustc/traits/query/method_autoderef.rs @@ -30,7 +30,10 @@ pub struct MethodAutoderefStepsResult<'tcx> { /// The valid autoderef steps that could be find. pub steps: Lrc>>, /// If Some(T), a type autoderef reported an error on. - pub opt_bad_ty: Option>> + pub opt_bad_ty: Option>>, + /// If `true`, `steps` has been truncated due to reaching the + /// recursion limit. + pub reached_recursion_limit: bool, } #[derive(Debug)] @@ -44,7 +47,7 @@ impl_stable_hash_for!(struct MethodAutoderefBadTy<'tcx> { }); impl_stable_hash_for!(struct MethodAutoderefStepsResult<'tcx> { - steps, opt_bad_ty + reached_recursion_limit, steps, opt_bad_ty }); impl_stable_hash_for!(struct CandidateStep<'tcx> { diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index 3fce66a19d86..d240f45c7d9a 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -14,7 +14,7 @@ use super::method::MethodCallee; use rustc::infer::{InferCtxt, InferOk}; use rustc::session::DiagnosticMessageId; use rustc::traits::{self, TraitEngine}; -use rustc::ty::{self, Ty, TraitRef}; +use rustc::ty::{self, Ty, TyCtxt, TraitRef}; use rustc::ty::{ToPredicate, TypeFoldable}; use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref}; @@ -39,6 +39,8 @@ pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> { at_start: bool, include_raw_pointers: bool, span: Span, + silence_errors: bool, + reached_recursion_limit: bool } impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> { @@ -57,24 +59,10 @@ impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> { } if self.steps.len() >= *tcx.sess.recursion_limit.get() { - // We've reached the recursion limit, error gracefully. - let suggested_limit = *tcx.sess.recursion_limit.get() * 2; - let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", - self.cur_ty); - let error_id = (DiagnosticMessageId::ErrorId(55), Some(self.span), msg); - let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); - if fresh { - struct_span_err!(tcx.sess, - self.span, - E0055, - "reached the recursion limit while auto-dereferencing `{:?}`", - self.cur_ty) - .span_label(self.span, "deref recursion limit reached") - .help(&format!( - "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate", - suggested_limit)) - .emit(); + if !self.silence_errors { + report_autoderef_recursion_limit_error(tcx, self.span, self.cur_ty); } + self.reached_recursion_limit = true; return None; } @@ -123,6 +111,8 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { obligations: vec![], at_start: true, include_raw_pointers: false, + silence_errors: false, + reached_recursion_limit: false, span, } } @@ -240,6 +230,15 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { self } + pub fn silence_errors(mut self) -> Self { + self.silence_errors = true; + self + } + + pub fn reached_recursion_limit(&self) -> bool { + self.reached_recursion_limit + } + pub fn finalize(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) { fcx.register_predicates(self.into_obligations()); } @@ -249,6 +248,29 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { } } +pub fn report_autoderef_recursion_limit_error<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, span: Span, ty: Ty<'tcx>) +{ + // We've reached the recursion limit, error gracefully. + let suggested_limit = *tcx.sess.recursion_limit.get() * 2; + let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", + ty); + let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg); + let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); + if fresh { + struct_span_err!(tcx.sess, + span, + E0055, + "reached the recursion limit while auto-dereferencing `{:?}`", + ty) + .span_label(span, "deref recursion limit reached") + .help(&format!( + "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate", + suggested_limit)) + .emit(); + } +} + impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'gcx, 'tcx> { Autoderef::new(self, self.param_env, self.body_id, span, base_ty) diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 539c33cc14ae..29fd9f5f71e8 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -13,7 +13,7 @@ use super::NoMatchData; use super::{CandidateSource, ImplSource, TraitSource}; use super::suggest; -use check::autoderef::Autoderef; +use check::autoderef::{self, Autoderef}; use check::FnCtxt; use hir::def_id::DefId; use hir::def::Def; @@ -283,19 +283,35 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { from_unsafe_deref: false, unsize: false, }]), - opt_bad_ty: None + opt_bad_ty: None, + reached_recursion_limit: false } }) }; + // If our autoderef loop had reached the recursion limit, + // report an overflow error, but continue going on with + // the truncated autoderef list. + if steps.reached_recursion_limit { + self.probe(|_| { + let ty = &steps.steps.last().unwrap_or_else(|| { + span_bug!(span, "reached the recursion limit in 0 steps?") + }).self_ty; + let ty = self.probe_instantiate_query_response(span, &orig_values, ty) + .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); + autoderef::report_autoderef_recursion_limit_error(self.tcx, span, + ty.value); + }); + } + + // If we encountered an `_` type or an error type during autoderef, this is // ambiguous. - if let Some(autoderef_bad_ty) = &steps.opt_bad_ty { - let MethodAutoderefBadTy { reached_raw_pointer, ref ty } = **autoderef_bad_ty; + if let Some(bad_ty) = &steps.opt_bad_ty { if is_suggestion.0 { // Ambiguity was encountered during a suggestion. Just keep going. debug!("ProbeContext: encountered ambiguity in suggestion"); - } else if reached_raw_pointer && !self.tcx.features().arbitrary_self_types { + } else if bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types { // this case used to be allowed by the compiler, // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) @@ -314,10 +330,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Encountered a real ambiguity, so abort the lookup. If `ty` is not // an `Err`, report the right "type annotations needed" error pointing // to it. + let ty = &bad_ty.ty; let ty = self.probe_instantiate_query_response(span, &orig_values, ty) .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); - let t = self.structurally_resolved_type(span, ty.value); - assert_eq!(t, self.tcx.types.err); + let ty = self.structurally_resolved_type(span, ty.value); + assert_eq!(ty, self.tcx.types.err); return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(), Vec::new(), Vec::new(), @@ -365,7 +382,8 @@ fn method_autoderef_steps<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, let ParamEnvAnd { param_env, value: self_ty } = goal; let mut autoderef = Autoderef::new(infcx, param_env, ast::DUMMY_NODE_ID, DUMMY_SP, self_ty) - .include_raw_pointers(); + .include_raw_pointers() + .silence_errors(); let mut reached_raw_pointer = false; let mut steps: Vec<_> = autoderef.by_ref() .map(|(ty, d)| { @@ -416,7 +434,8 @@ fn method_autoderef_steps<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, MethodAutoderefStepsResult { steps: Lrc::new(steps), - opt_bad_ty: opt_bad_ty.map(Lrc::new) + opt_bad_ty: opt_bad_ty.map(Lrc::new), + reached_recursion_limit: autoderef.reached_recursion_limit() } }) }