Region + borrow checker support and tests for overloaded autoderef.

This commit is contained in:
Eduard Burtescu 2014-03-11 22:27:55 +02:00
parent feedd37653
commit 27c62449db
8 changed files with 540 additions and 24 deletions

View file

@ -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,

View file

@ -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) {
/*!

View file

@ -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() {}

View file

@ -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() {}

View file

@ -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
}

View file

@ -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});
}

View file

@ -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);
}

View file

@ -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);
}