Skip to content
17 changes: 10 additions & 7 deletions src/librustc_typeck/check/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,16 @@ fn create_steps<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-> Option<Vec<CandidateStep<'tcx>>> {
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,
Expand Down
65 changes: 57 additions & 8 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2099,6 +2099,17 @@ pub enum UnresolvedTypeAction {
Ignore
}

/// Desired behaviour upon reaching the recursion limit. Used in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yay docs!

/// `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.
///
Expand All @@ -2109,18 +2120,45 @@ 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<T>)
where F: FnMut(Ty<'tcx>, usize) -> Option<T>,
{
// 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<T>)
where F: FnMut(Ty<'tcx>, usize) -> Option<T>,
{
debug!("autoderef(base_ty={:?}, opt_expr={:?}, lvalue_pref={:?})",
base_ty,
opt_expr,
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)
Expand Down Expand Up @@ -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;
Expand All @@ -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>,
Expand Down
50 changes: 50 additions & 0 deletions src/test/run-pass/issue-19509.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
}