about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard Burtescu <edy.burt@gmail.com>2014-03-11 22:27:55 +0200
committerEduard Burtescu <edy.burt@gmail.com>2014-03-13 14:21:46 +0200
commit27c62449db9e752bcab39110aaaad63b2773e68b (patch)
treee02cec3603d4b52a4a82cef2ec2999b8d9b5e1e0
parentfeedd37653b32a97e2d10c12f2cf1b14c0058c19 (diff)
downloadrust-27c62449db9e752bcab39110aaaad63b2773e68b.tar.gz
rust-27c62449db9e752bcab39110aaaad63b2773e68b.zip
Region + borrow checker support and tests for overloaded autoderef.
-rw-r--r--src/librustc/middle/borrowck/gather_loans/mod.rs39
-rw-r--r--src/librustc/middle/typeck/check/regionck.rs79
-rw-r--r--src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs131
-rw-r--r--src/test/compile-fail/borrowck-borrow-overloaded-auto-deref.rs122
-rw-r--r--src/test/compile-fail/borrowck-move-out-of-overloaded-auto-deref.rs16
-rw-r--r--src/test/run-pass/overloaded-autoderef-count.rs82
-rw-r--r--src/test/run-pass/overloaded-autoderef-indexing.rs24
-rw-r--r--src/test/run-pass/overloaded-autoderef-order.rs71
8 files changed, 540 insertions, 24 deletions
diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs
index d2fcee79fc0..f6168feb2b8 100644
--- a/src/librustc/middle/borrowck/gather_loans/mod.rs
+++ b/src/librustc/middle/borrowck/gather_loans/mod.rs
@@ -326,6 +326,39 @@ impl<'a> GatherLoanCtxt<'a> {
         assert_eq!(id, popped);
     }
 
+    pub fn guarantee_autoderefs(&mut self,
+                                expr: &ast::Expr,
+                                autoderefs: uint) {
+        let method_map = self.bccx.method_map.borrow();
+        for i in range(0, autoderefs) {
+            match method_map.get().find(&MethodCall::autoderef(expr.id, i as u32)) {
+                Some(method) => {
+                    // Treat overloaded autoderefs as if an AutoRef adjustment
+                    // was applied on the base type, as that is always the case.
+                    let mut mc = self.bccx.mc();
+                    let cmt = match mc.cat_expr_autoderefd(expr, i) {
+                        Ok(v) => v,
+                        Err(()) => self.tcx().sess.span_bug(expr.span, "Err from mc")
+                    };
+                    let self_ty = *ty::ty_fn_args(method.ty).get(0);
+                    let (m, r) = match ty::get(self_ty).sty {
+                        ty::ty_rptr(r, ref m) => (m.mutbl, r),
+                        _ => self.tcx().sess.span_bug(expr.span,
+                                format!("bad overloaded deref type {}",
+                                    method.ty.repr(self.tcx())))
+                    };
+                    self.guarantee_valid(expr.id,
+                                         expr.span,
+                                         cmt,
+                                         m,
+                                         r,
+                                         AutoRef);
+                }
+                None => {}
+            }
+        }
+    }
+
     pub fn guarantee_adjustments(&mut self,
                                  expr: &ast::Expr,
                                  adjustment: &ty::AutoAdjustment) {
@@ -341,15 +374,17 @@ impl<'a> GatherLoanCtxt<'a> {
 
             ty::AutoDerefRef(
                 ty::AutoDerefRef {
-                    autoref: None, .. }) => {
+                    autoref: None, autoderefs }) => {
                 debug!("no autoref");
+                self.guarantee_autoderefs(expr, autoderefs);
                 return;
             }
 
             ty::AutoDerefRef(
                 ty::AutoDerefRef {
                     autoref: Some(ref autoref),
-                    autoderefs: autoderefs}) => {
+                    autoderefs}) => {
+                self.guarantee_autoderefs(expr, autoderefs);
                 let mut mc = self.bccx.mc();
                 let cmt = match mc.cat_expr_autoderefd(expr, autoderefs) {
                     Ok(v) => v,
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index 169a7cfc90e..aef1b20273a 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -141,6 +141,19 @@ use syntax::codemap::Span;
 use syntax::visit;
 use syntax::visit::Visitor;
 
+// If mem categorization results in an error, it's because the type
+// check failed (or will fail, when the error is uncovered and
+// reported during writeback). In this case, we just ignore this part
+// of the code and don't try to add any more region constraints.
+macro_rules! ignore_err(
+    ($inp: expr) => (
+        match $inp {
+            Ok(v) => v,
+            Err(()) => return
+        }
+    )
+)
+
 pub struct Rcx {
     fcx: @FnCtxt,
     errors_reported: uint,
@@ -395,7 +408,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
         match **adjustment {
             ty::AutoDerefRef(ty::AutoDerefRef {autoderefs, autoref: opt_autoref}) => {
                 let expr_ty = rcx.resolve_node_type(expr.id);
-                constrain_derefs(rcx, expr, autoderefs, expr_ty);
+                constrain_autoderefs(rcx, expr, autoderefs, expr_ty);
                 for autoref in opt_autoref.iter() {
                     link_autoref(rcx, expr, autoderefs, autoref);
 
@@ -494,7 +507,13 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
                 }
                 None => rcx.resolve_node_type(base.id)
             };
-            constrain_derefs(rcx, expr, 1, base_ty);
+            match ty::get(base_ty).sty {
+                ty::ty_rptr(r_ptr, _) => {
+                    mk_subregion_due_to_derefence(rcx, expr.span,
+                                                  ty::ReScope(expr.id), r_ptr);
+                }
+                _ => {}
+            }
 
             visit::walk_expr(rcx, expr, ());
         }
@@ -819,11 +838,10 @@ fn constrain_call(rcx: &mut Rcx,
         fn_sig.output);
 }
 
-fn constrain_derefs(rcx: &mut Rcx,
-                    deref_expr: &ast::Expr,
-                    derefs: uint,
-                    mut derefd_ty: ty::t)
-{
+fn constrain_autoderefs(rcx: &mut Rcx,
+                        deref_expr: &ast::Expr,
+                        derefs: uint,
+                        mut derefd_ty: ty::t) {
     /*!
      * Invoked on any dereference that occurs, whether explicitly
      * or through an auto-deref.  Checks that if this is a region
@@ -832,16 +850,46 @@ fn constrain_derefs(rcx: &mut Rcx,
      */
     let r_deref_expr = ty::ReScope(deref_expr.id);
     for i in range(0u, derefs) {
-        debug!("constrain_derefs(deref_expr=?, derefd_ty={}, derefs={:?}/{:?}",
+        debug!("constrain_autoderefs(deref_expr=?, derefd_ty={}, derefs={:?}/{:?}",
                rcx.fcx.infcx().ty_to_str(derefd_ty),
                i, derefs);
 
+        let method_call = MethodCall::autoderef(deref_expr.id, i as u32);
+        derefd_ty = match rcx.fcx.inh.method_map.get().find(&method_call) {
+            Some(method) => {
+                // Treat overloaded autoderefs as if an AutoRef adjustment
+                // was applied on the base type, as that is always the case.
+                let fn_sig = ty::ty_fn_sig(method.ty);
+                let self_ty = *fn_sig.inputs.get(0);
+                let (m, r) = match ty::get(self_ty).sty {
+                    ty::ty_rptr(r, ref m) => (m.mutbl, r),
+                    _ => rcx.tcx().sess.span_bug(deref_expr.span,
+                            format!("bad overloaded deref type {}",
+                                method.ty.repr(rcx.tcx())))
+                };
+                {
+                    let mut mc = mc::MemCategorizationContext { typer: &mut *rcx };
+                    let self_cmt = ignore_err!(mc.cat_expr_autoderefd(deref_expr, i));
+                    link_region(mc.typer, deref_expr.span, r, m, self_cmt);
+                }
+
+                // Specialized version of constrain_call.
+                constrain_regions_in_type(rcx, r_deref_expr,
+                                          infer::CallRcvr(deref_expr.span),
+                                          self_ty);
+                constrain_regions_in_type(rcx, r_deref_expr,
+                                          infer::CallReturn(deref_expr.span),
+                                          fn_sig.output);
+                fn_sig.output
+            }
+            None => derefd_ty
+        };
+
         match ty::get(derefd_ty).sty {
             ty::ty_rptr(r_ptr, _) => {
                 mk_subregion_due_to_derefence(rcx, deref_expr.span,
                                               r_deref_expr, r_ptr);
             }
-
             _ => {}
         }
 
@@ -965,19 +1013,6 @@ fn constrain_regions_in_type(
     return e == rcx.errors_reported;
 }
 
-// If mem categorization results in an error, it's because the type
-// check failed (or will fail, when the error is uncovered and
-// reported during writeback). In this case, we just ignore this part
-// of the code and don't try to add any more region constraints.
-macro_rules! ignore_err(
-    ($inp: expr) => (
-        match $inp {
-            Ok(v) => { v }
-            Err(()) => { return; }
-        }
-    )
-)
-
 fn link_addr_of(rcx: &mut Rcx, expr: &ast::Expr,
                mutability: ast::Mutability, base: &ast::Expr) {
     /*!
diff --git a/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs b/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs
new file mode 100644
index 00000000000..9800fd704ac
--- /dev/null
+++ b/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref-mut.rs
@@ -0,0 +1,131 @@
+// Copyright 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.
+
+// Test how overloaded deref interacts with borrows when DerefMut
+// is implemented.
+
+use std::ops::{Deref, DerefMut};
+
+struct Own<T> {
+    value: *mut T
+}
+
+impl<T> Deref<T> for Own<T> {
+    fn deref<'a>(&'a self) -> &'a T {
+        unsafe { &*self.value }
+    }
+}
+
+impl<T> DerefMut<T> for Own<T> {
+    fn deref_mut<'a>(&'a mut self) -> &'a mut T {
+        unsafe { &mut *self.value }
+    }
+}
+
+struct Point {
+    x: int,
+    y: int
+}
+
+impl Point {
+    fn get(&self) -> (int, int) {
+        (self.x, self.y)
+    }
+
+    fn set(&mut self, x: int, y: int) {
+        self.x = x;
+        self.y = y;
+    }
+
+    fn x_ref<'a>(&'a self) -> &'a int {
+        &self.x
+    }
+
+    fn y_mut<'a>(&'a mut self) -> &'a mut int {
+        &mut self.y
+    }
+}
+
+fn deref_imm_field(x: Own<Point>) {
+    let _i = &x.y;
+}
+
+fn deref_mut_field1(x: Own<Point>) {
+    let _i = &mut x.y; //~ ERROR cannot borrow
+}
+
+fn deref_mut_field2(mut x: Own<Point>) {
+    let _i = &mut x.y;
+}
+
+fn deref_extend_field<'a>(x: &'a Own<Point>) -> &'a int {
+    &x.y
+}
+
+fn deref_extend_mut_field1<'a>(x: &'a Own<Point>) -> &'a mut int {
+    &mut x.y //~ ERROR cannot borrow
+}
+
+fn deref_extend_mut_field2<'a>(x: &'a mut Own<Point>) -> &'a mut int {
+    &mut x.y
+}
+
+fn assign_field1<'a>(x: Own<Point>) {
+    x.y = 3; //~ ERROR cannot borrow
+}
+
+fn assign_field2<'a>(x: &'a Own<Point>) {
+    x.y = 3; //~ ERROR cannot assign
+}
+
+fn assign_field3<'a>(x: &'a mut Own<Point>) {
+    x.y = 3;
+}
+
+// FIXME(eddyb) #12825 This shouldn't attempt to call deref_mut.
+/*
+fn deref_imm_method(x: Own<Point>) {
+    let _i = x.get();
+}
+*/
+
+fn deref_mut_method1(x: Own<Point>) {
+    x.set(0, 0); //~ ERROR cannot borrow
+}
+
+fn deref_mut_method2(mut x: Own<Point>) {
+    x.set(0, 0);
+}
+
+fn deref_extend_method<'a>(x: &'a Own<Point>) -> &'a int {
+    x.x_ref()
+}
+
+fn deref_extend_mut_method1<'a>(x: &'a Own<Point>) -> &'a mut int {
+    x.y_mut() //~ ERROR cannot borrow
+}
+
+fn deref_extend_mut_method2<'a>(x: &'a mut Own<Point>) -> &'a mut int {
+    x.y_mut()
+}
+
+fn assign_method1<'a>(x: Own<Point>) {
+    *x.y_mut() = 3; //~ ERROR cannot borrow
+}
+
+fn assign_method2<'a>(x: &'a Own<Point>) {
+    *x.y_mut() = 3; //~ ERROR cannot borrow
+}
+
+fn assign_method3<'a>(x: &'a mut Own<Point>) {
+    *x.y_mut() = 3;
+}
+
+pub fn main() {}
\ No newline at end of file
diff --git a/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref.rs b/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref.rs
new file mode 100644
index 00000000000..de68dd311c6
--- /dev/null
+++ b/src/test/compile-fail/borrowck-borrow-overloaded-auto-deref.rs
@@ -0,0 +1,122 @@
+// Copyright 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.
+
+// Test how overloaded deref interacts with borrows when only
+// Deref and not DerefMut is implemented.
+
+use std::ops::Deref;
+
+struct Rc<T> {
+    value: *T
+}
+
+impl<T> Deref<T> for Rc<T> {
+    fn deref<'a>(&'a self) -> &'a T {
+        unsafe { &*self.value }
+    }
+}
+
+struct Point {
+    x: int,
+    y: int
+}
+
+impl Point {
+    fn get(&self) -> (int, int) {
+        (self.x, self.y)
+    }
+
+    fn set(&mut self, x: int, y: int) {
+        self.x = x;
+        self.y = y;
+    }
+
+    fn x_ref<'a>(&'a self) -> &'a int {
+        &self.x
+    }
+
+    fn y_mut<'a>(&'a mut self) -> &'a mut int {
+        &mut self.y
+    }
+}
+
+fn deref_imm_field(x: Rc<Point>) {
+    let _i = &x.y;
+}
+
+fn deref_mut_field1(x: Rc<Point>) {
+    let _i = &mut x.y; //~ ERROR cannot borrow
+}
+
+fn deref_mut_field2(mut x: Rc<Point>) {
+    let _i = &mut x.y; //~ ERROR cannot borrow
+}
+
+fn deref_extend_field<'a>(x: &'a Rc<Point>) -> &'a int {
+    &x.y
+}
+
+fn deref_extend_mut_field1<'a>(x: &'a Rc<Point>) -> &'a mut int {
+    &mut x.y //~ ERROR cannot borrow
+}
+
+fn deref_extend_mut_field2<'a>(x: &'a mut Rc<Point>) -> &'a mut int {
+    &mut x.y //~ ERROR cannot borrow
+}
+
+fn assign_field1<'a>(x: Rc<Point>) {
+    x.y = 3; //~ ERROR cannot assign
+}
+
+fn assign_field2<'a>(x: &'a Rc<Point>) {
+    x.y = 3; //~ ERROR cannot assign
+}
+
+fn assign_field3<'a>(x: &'a mut Rc<Point>) {
+    x.y = 3; //~ ERROR cannot assign
+}
+
+fn deref_imm_method(x: Rc<Point>) {
+    let _i = x.get();
+}
+
+fn deref_mut_method1(x: Rc<Point>) {
+    x.set(0, 0); //~ ERROR cannot borrow
+}
+
+fn deref_mut_method2(mut x: Rc<Point>) {
+    x.set(0, 0); //~ ERROR cannot borrow
+}
+
+fn deref_extend_method<'a>(x: &'a Rc<Point>) -> &'a int {
+    x.x_ref()
+}
+
+fn deref_extend_mut_method1<'a>(x: &'a Rc<Point>) -> &'a mut int {
+    x.y_mut() //~ ERROR cannot borrow
+}
+
+fn deref_extend_mut_method2<'a>(x: &'a mut Rc<Point>) -> &'a mut int {
+    x.y_mut() //~ ERROR cannot borrow
+}
+
+fn assign_method1<'a>(x: Rc<Point>) {
+    *x.y_mut() = 3; //~ ERROR cannot borrow
+}
+
+fn assign_method2<'a>(x: &'a Rc<Point>) {
+    *x.y_mut() = 3; //~ ERROR cannot borrow
+}
+
+fn assign_method3<'a>(x: &'a mut Rc<Point>) {
+    *x.y_mut() = 3; //~ ERROR cannot borrow
+}
+
+pub fn main() {}
diff --git a/src/test/compile-fail/borrowck-move-out-of-overloaded-auto-deref.rs b/src/test/compile-fail/borrowck-move-out-of-overloaded-auto-deref.rs
new file mode 100644
index 00000000000..1a96e5ef4b0
--- /dev/null
+++ b/src/test/compile-fail/borrowck-move-out-of-overloaded-auto-deref.rs
@@ -0,0 +1,16 @@
+// Copyright 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::rc::Rc;
+
+pub fn main() {
+    let _x = Rc::new(vec!(1, 2)).move_iter();
+    //~^ ERROR cannot move out of dereference of `&`-pointer
+}
diff --git a/src/test/run-pass/overloaded-autoderef-count.rs b/src/test/run-pass/overloaded-autoderef-count.rs
new file mode 100644
index 00000000000..10ee06473c8
--- /dev/null
+++ b/src/test/run-pass/overloaded-autoderef-count.rs
@@ -0,0 +1,82 @@
+// Copyright 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::cell::Cell;
+use std::ops::{Deref, DerefMut};
+use std::vec_ng::Vec;
+
+#[deriving(Eq)]
+struct DerefCounter<T> {
+    count_imm: Cell<uint>,
+    count_mut: uint,
+    value: T
+}
+
+impl<T> DerefCounter<T> {
+    fn new(value: T) -> DerefCounter<T> {
+        DerefCounter {
+            count_imm: Cell::new(0),
+            count_mut: 0,
+            value: value
+        }
+    }
+
+    fn counts(&self) -> (uint, uint) {
+        (self.count_imm.get(), self.count_mut)
+    }
+}
+
+impl<T> Deref<T> for DerefCounter<T> {
+    fn deref<'a>(&'a self) -> &'a T {
+        self.count_imm.set(self.count_imm.get() + 1);
+        &self.value
+    }
+}
+
+impl<T> DerefMut<T> for DerefCounter<T> {
+    fn deref_mut<'a>(&'a mut self) -> &'a mut T {
+        self.count_mut += 1;
+        &mut self.value
+    }
+}
+
+#[deriving(Eq, Show)]
+struct Point {
+    x: int,
+    y: int
+}
+
+impl Point {
+    fn get(&self) -> (int, int) {
+        (self.x, self.y)
+    }
+}
+
+pub fn main() {
+    let mut p = DerefCounter::new(Point {x: 0, y: 0});
+
+    let _ = p.x;
+    assert_eq!(p.counts(), (1, 0));
+
+    let _ = &p.x;
+    assert_eq!(p.counts(), (2, 0));
+
+    let _ = &mut p.y;
+    assert_eq!(p.counts(), (2, 1));
+
+    p.x += 3;
+    assert_eq!(p.counts(), (2, 2));
+
+    p.get();
+    assert_eq!(p.counts(), (2, 3));
+
+    // Check the final state.
+    assert_eq!(*p, Point {x: 3, y: 0});
+}
diff --git a/src/test/run-pass/overloaded-autoderef-indexing.rs b/src/test/run-pass/overloaded-autoderef-indexing.rs
new file mode 100644
index 00000000000..c8885a3090c
--- /dev/null
+++ b/src/test/run-pass/overloaded-autoderef-indexing.rs
@@ -0,0 +1,24 @@
+// Copyright 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.
+
+struct DerefArray<'a, T> {
+    inner: &'a [T]
+}
+
+impl<'a, T> Deref<&'a [T]> for DerefArray<'a, T> {
+    fn deref<'b>(&'b self) -> &'b &'a [T] {
+        &self.inner
+    }
+}
+
+pub fn main() {
+    let a = &[1, 2, 3];
+    assert_eq!(DerefArray {inner: a}[1], 2);
+}
diff --git a/src/test/run-pass/overloaded-autoderef-order.rs b/src/test/run-pass/overloaded-autoderef-order.rs
new file mode 100644
index 00000000000..9deeff77364
--- /dev/null
+++ b/src/test/run-pass/overloaded-autoderef-order.rs
@@ -0,0 +1,71 @@
+// Copyright 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::rc::Rc;
+
+struct DerefWrapper<X, Y> {
+    x: X,
+    y: Y
+}
+
+impl<X, Y> DerefWrapper<X, Y> {
+    fn get_x(self) -> X {
+        self.x
+    }
+}
+
+impl<X, Y> Deref<Y> for DerefWrapper<X, Y> {
+    fn deref<'a>(&'a self) -> &'a Y {
+        &self.y
+    }
+}
+
+mod priv_test {
+    pub struct DerefWrapperHideX<X, Y> {
+        priv x: X,
+        y: Y
+    }
+
+    impl<X, Y> DerefWrapperHideX<X, Y> {
+        pub fn new(x: X, y: Y) -> DerefWrapperHideX<X, Y> {
+            DerefWrapperHideX {
+                x: x,
+                y: y
+            }
+        }
+    }
+
+    impl<X, Y> Deref<Y> for DerefWrapperHideX<X, Y> {
+        fn deref<'a>(&'a self) -> &'a Y {
+            &self.y
+        }
+    }
+}
+
+pub fn main() {
+    let nested = DerefWrapper {x: true, y: DerefWrapper {x: 0, y: 1}};
+
+    // Use the first field that you can find.
+    assert_eq!(nested.x, true);
+    assert_eq!((*nested).x, 0);
+
+    // Same for methods, even though there are multiple
+    // candidates (at different nesting levels).
+    assert_eq!(nested.get_x(), true);
+    assert_eq!((*nested).get_x(), 0);
+
+    // Also go through multiple levels of indirection.
+    assert_eq!(Rc::new(nested).x, true);
+
+    let nested_priv = priv_test::DerefWrapperHideX::new(true, DerefWrapper {x: 0, y: 1});
+    // FIXME(eddyb) #12808 should skip private fields.
+    // assert_eq!(nested_priv.x, 0);
+    assert_eq!((*nested_priv).x, 0);
+}