diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index c63b081c73c48..68683ceaf3489 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -199,13 +199,16 @@ fn create_steps<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, -> Option>> { let mut steps = Vec::new(); - let (final_ty, dereferences, _) = check::autoderef(fcx, - span, - self_ty, - None, - UnresolvedTypeAction::Error, - NoPreference, - |t, d| { + let (final_ty, dereferences, _) = + check::autoderef_with_recursion_option(fcx, + span, + self_ty, + None, + UnresolvedTypeAction::Error, + NoPreference, + check::AutoderefRecursionOption + ::ReturnLastResolvedType, + |t, d| { steps.push(CandidateStep { self_ty: t, autoderefs: d, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 00482f2d53c2c..b4b4b90cd3d08 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2099,6 +2099,17 @@ pub enum UnresolvedTypeAction { Ignore } +/// Desired behaviour upon reaching the recursion limit. Used in +/// `autoderef_with_recursion_option`. Default is `ErrorGracefully` +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AutoderefRecursionOption { + /// Returns the last resolved type as it if were the valid end result + ReturnLastResolvedType, + /// Error gracefully upon reaching the limit.. This is the default behaviour + ErrorGracefully, +} + + /// Executes an autoderef loop for the type `t`. At each step, invokes `should_stop` to decide /// whether to terminate the loop. Returns the final type and number of derefs that it performed. /// @@ -2109,10 +2120,36 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, base_ty: Ty<'tcx>, opt_expr: Option<&hir::Expr>, unresolved_type_action: UnresolvedTypeAction, - mut lvalue_pref: LvaluePreference, - mut should_stop: F) + lvalue_pref: LvaluePreference, + should_stop: F) -> (Ty<'tcx>, usize, Option) where F: FnMut(Ty<'tcx>, usize) -> Option, +{ + // call the implementation with the default behaviour. + autoderef_with_recursion_option(fcx, + sp, + base_ty, + opt_expr, + unresolved_type_action, + lvalue_pref, + AutoderefRecursionOption::ErrorGracefully, + should_stop) +} + +/// Actual implementation of of `autoderef`. `autoderef` calls this method with +/// `AutoderefRecursionOption::ErrorGracefully` as default behaviour. Unless +/// specific behaviour is needed in case of reaching the recursion limit, +/// `autoderef` should be used instead. +pub fn autoderef_with_recursion_option<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, + sp: Span, + base_ty: Ty<'tcx>, + opt_expr: Option<&hir::Expr>, + unresolved_type_action: UnresolvedTypeAction, + mut lvalue_pref: LvaluePreference, + recursion_option: AutoderefRecursionOption, + mut should_stop: F) + -> (Ty<'tcx>, usize, Option) + where F: FnMut(Ty<'tcx>, usize) -> Option, { debug!("autoderef(base_ty={:?}, opt_expr={:?}, lvalue_pref={:?})", base_ty, @@ -2120,7 +2157,8 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, lvalue_pref); let mut t = base_ty; - for autoderefs in 0..fcx.tcx().sess.recursion_limit.get() { + let recursion_limit = fcx.tcx().sess.recursion_limit.get(); + for autoderefs in 0..recursion_limit { let resolved_t = match unresolved_type_action { UnresolvedTypeAction::Error => { structurally_resolved_type(fcx, sp, t) @@ -2163,6 +2201,7 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, try_overloaded_deref(fcx, sp, method_call, None, resolved_t, lvalue_pref) } }; + match mt { Some(mt) => { t = mt.ty; @@ -2174,11 +2213,21 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, } } - // We've reached the recursion limit, error gracefully. - span_err!(fcx.tcx().sess, sp, E0055, - "reached the recursion limit while auto-dereferencing {:?}", - base_ty); - (fcx.tcx().types.err, 0, None) + // Respect the desired behaviour regarding the recursion limit. + match recursion_option { + AutoderefRecursionOption::ReturnLastResolvedType => { + // Treat `t`, the last resolved type, as endresult + return (t, recursion_limit, None); + }, + AutoderefRecursionOption::ErrorGracefully => { + // We've reached the recursion limit, error gracefully. + // This is the default behaviour. + span_err!(fcx.tcx().sess, sp, E0055, + "reached the recursion limit while auto-dereferencing {:?}", + base_ty); + (fcx.tcx().types.err, 0, None) + } + } } fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, diff --git a/src/test/run-pass/issue-19509.rs b/src/test/run-pass/issue-19509.rs new file mode 100644 index 0000000000000..30c197fdd45e2 --- /dev/null +++ b/src/test/run-pass/issue-19509.rs @@ -0,0 +1,50 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ops::Deref; + +struct Foo { + inner: Bar, +} + +struct Bar; + +impl Foo { + pub fn foo_method(&self) { + } +} + +impl Bar { + pub fn bar_method(&self) { + } +} + +impl Deref for Foo { + type Target = Bar; + + fn deref<'a>(&'a self) -> &'a Self::Target { + &self.inner + } +} + +impl Deref for Bar { + type Target = Foo; + + fn deref<'a>(&'a self) -> &'a Self::Target { + panic!() + } +} + +fn main() { + let foo = Foo { inner: Bar, }; + let bar = Bar; + + foo.bar_method(); // should compile and execute +}