diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index d2fcee79fc03..f6168feb2b84 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 169a7cfc90e7..aef1b20273a2 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 000000000000..9800fd704ac7 --- /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 or the MIT license +// , 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 { + value: *mut T +} + +impl Deref for Own { + fn deref<'a>(&'a self) -> &'a T { + unsafe { &*self.value } + } +} + +impl DerefMut for Own { + 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) { + let _i = &x.y; +} + +fn deref_mut_field1(x: Own) { + let _i = &mut x.y; //~ ERROR cannot borrow +} + +fn deref_mut_field2(mut x: Own) { + let _i = &mut x.y; +} + +fn deref_extend_field<'a>(x: &'a Own) -> &'a int { + &x.y +} + +fn deref_extend_mut_field1<'a>(x: &'a Own) -> &'a mut int { + &mut x.y //~ ERROR cannot borrow +} + +fn deref_extend_mut_field2<'a>(x: &'a mut Own) -> &'a mut int { + &mut x.y +} + +fn assign_field1<'a>(x: Own) { + x.y = 3; //~ ERROR cannot borrow +} + +fn assign_field2<'a>(x: &'a Own) { + x.y = 3; //~ ERROR cannot assign +} + +fn assign_field3<'a>(x: &'a mut Own) { + x.y = 3; +} + +// FIXME(eddyb) #12825 This shouldn't attempt to call deref_mut. +/* +fn deref_imm_method(x: Own) { + let _i = x.get(); +} +*/ + +fn deref_mut_method1(x: Own) { + x.set(0, 0); //~ ERROR cannot borrow +} + +fn deref_mut_method2(mut x: Own) { + x.set(0, 0); +} + +fn deref_extend_method<'a>(x: &'a Own) -> &'a int { + x.x_ref() +} + +fn deref_extend_mut_method1<'a>(x: &'a Own) -> &'a mut int { + x.y_mut() //~ ERROR cannot borrow +} + +fn deref_extend_mut_method2<'a>(x: &'a mut Own) -> &'a mut int { + x.y_mut() +} + +fn assign_method1<'a>(x: Own) { + *x.y_mut() = 3; //~ ERROR cannot borrow +} + +fn assign_method2<'a>(x: &'a Own) { + *x.y_mut() = 3; //~ ERROR cannot borrow +} + +fn assign_method3<'a>(x: &'a mut Own) { + *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 000000000000..de68dd311c65 --- /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 or the MIT license +// , 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 { + value: *T +} + +impl Deref for Rc { + 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) { + let _i = &x.y; +} + +fn deref_mut_field1(x: Rc) { + let _i = &mut x.y; //~ ERROR cannot borrow +} + +fn deref_mut_field2(mut x: Rc) { + let _i = &mut x.y; //~ ERROR cannot borrow +} + +fn deref_extend_field<'a>(x: &'a Rc) -> &'a int { + &x.y +} + +fn deref_extend_mut_field1<'a>(x: &'a Rc) -> &'a mut int { + &mut x.y //~ ERROR cannot borrow +} + +fn deref_extend_mut_field2<'a>(x: &'a mut Rc) -> &'a mut int { + &mut x.y //~ ERROR cannot borrow +} + +fn assign_field1<'a>(x: Rc) { + x.y = 3; //~ ERROR cannot assign +} + +fn assign_field2<'a>(x: &'a Rc) { + x.y = 3; //~ ERROR cannot assign +} + +fn assign_field3<'a>(x: &'a mut Rc) { + x.y = 3; //~ ERROR cannot assign +} + +fn deref_imm_method(x: Rc) { + let _i = x.get(); +} + +fn deref_mut_method1(x: Rc) { + x.set(0, 0); //~ ERROR cannot borrow +} + +fn deref_mut_method2(mut x: Rc) { + x.set(0, 0); //~ ERROR cannot borrow +} + +fn deref_extend_method<'a>(x: &'a Rc) -> &'a int { + x.x_ref() +} + +fn deref_extend_mut_method1<'a>(x: &'a Rc) -> &'a mut int { + x.y_mut() //~ ERROR cannot borrow +} + +fn deref_extend_mut_method2<'a>(x: &'a mut Rc) -> &'a mut int { + x.y_mut() //~ ERROR cannot borrow +} + +fn assign_method1<'a>(x: Rc) { + *x.y_mut() = 3; //~ ERROR cannot borrow +} + +fn assign_method2<'a>(x: &'a Rc) { + *x.y_mut() = 3; //~ ERROR cannot borrow +} + +fn assign_method3<'a>(x: &'a mut Rc) { + *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 000000000000..1a96e5ef4b0c --- /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 or the MIT license +// , 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 000000000000..10ee06473c80 --- /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 or the MIT license +// , 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 { + count_imm: Cell, + count_mut: uint, + value: T +} + +impl DerefCounter { + fn new(value: T) -> DerefCounter { + DerefCounter { + count_imm: Cell::new(0), + count_mut: 0, + value: value + } + } + + fn counts(&self) -> (uint, uint) { + (self.count_imm.get(), self.count_mut) + } +} + +impl Deref for DerefCounter { + fn deref<'a>(&'a self) -> &'a T { + self.count_imm.set(self.count_imm.get() + 1); + &self.value + } +} + +impl DerefMut for DerefCounter { + 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 000000000000..c8885a3090c0 --- /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 or the MIT license +// , 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 000000000000..9deeff773642 --- /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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::rc::Rc; + +struct DerefWrapper { + x: X, + y: Y +} + +impl DerefWrapper { + fn get_x(self) -> X { + self.x + } +} + +impl Deref for DerefWrapper { + fn deref<'a>(&'a self) -> &'a Y { + &self.y + } +} + +mod priv_test { + pub struct DerefWrapperHideX { + priv x: X, + y: Y + } + + impl DerefWrapperHideX { + pub fn new(x: X, y: Y) -> DerefWrapperHideX { + DerefWrapperHideX { + x: x, + y: y + } + } + } + + impl Deref for DerefWrapperHideX { + 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); +}