From 852a49fd9cf3860aa018c1a2d66f644c6813d849 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 06:33:11 -0500 Subject: [PATCH 01/33] std -- replaces uses where const borrows would be required --- src/libstd/io/mod.rs | 38 ++++++----- src/libstd/unstable/finally.rs | 118 ++++++++++++++++++++++----------- src/libstd/vec.rs | 39 ++++++----- 3 files changed, 125 insertions(+), 70 deletions(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 1c1df691a52..54c0d98c798 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -277,7 +277,7 @@ use str::{StrSlice, OwnedStr}; use str; use to_str::ToStr; use uint; -use unstable::finally::Finally; +use unstable::finally::try_finally; use vec::{OwnedVector, MutableVector, ImmutableVector, OwnedCloneableVector}; use vec; @@ -473,25 +473,33 @@ pub trait Reader { /// pushed on to the vector, otherwise the amount `len` bytes couldn't be /// read (an error was encountered), and the error is returned. fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) -> IoResult<()> { + struct State<'a> { + buf: &'a mut ~[u8], + total_read: uint + } + let start_len = buf.len(); - let mut total_read = 0; + let mut s = State { buf: buf, total_read: 0 }; - buf.reserve_additional(len); - unsafe { buf.set_len(start_len + len); } + s.buf.reserve_additional(len); + unsafe { s.buf.set_len(start_len + len); } - (|| { - while total_read < len { - let len = buf.len(); - let slice = buf.mut_slice(start_len + total_read, len); - match self.read(slice) { - Ok(nread) => { - total_read += nread; + try_finally( + &mut s, (), + |s, _| { + while s.total_read < len { + let len = s.buf.len(); + let slice = s.buf.mut_slice(start_len + s.total_read, len); + match self.read(slice) { + Ok(nread) => { + s.total_read += nread; + } + Err(e) => return Err(e) } - Err(e) => return Err(e) } - } - Ok(()) - }).finally(|| unsafe { buf.set_len(start_len + total_read) }) + Ok(()) + }, + |s| unsafe { s.buf.set_len(start_len + s.total_read) }) } /// Reads `len` bytes and gives you back a new vector of length `len` diff --git a/src/libstd/unstable/finally.rs b/src/libstd/unstable/finally.rs index 6faf69d2bb9..433accecdbc 100644 --- a/src/libstd/unstable/finally.rs +++ b/src/libstd/unstable/finally.rs @@ -12,6 +12,11 @@ The Finally trait provides a method, `finally` on stack closures that emulates Java-style try/finally blocks. +Using the `finally` method is sometimes convenient, but the type rules +prohibit any shared, mutable state between the "try" case and the +"finally" case. For advanced cases, the `try_finally` function can +also be used. See that function for more details. + # Example ``` @@ -31,53 +36,89 @@ pub trait Finally { fn finally(&self, dtor: ||) -> T; } -macro_rules! finally_fn { - ($fnty:ty) => { - impl Finally for $fnty { - fn finally(&self, dtor: ||) -> T { - let _d = Finallyalizer { - dtor: dtor - }; - (*self)() - } - } - } -} - impl<'a,T> Finally for 'a || -> T { fn finally(&self, dtor: ||) -> T { - let _d = Finallyalizer { - dtor: dtor - }; - - (*self)() + try_finally(&mut (), (), + |_, _| (*self)(), + |_| dtor()) } } -finally_fn!(extern "Rust" fn() -> T) +impl Finally for fn() -> T { + fn finally(&self, dtor: ||) -> T { + try_finally(&mut (), (), + |_, _| (*self)(), + |_| dtor()) + } +} -struct Finallyalizer<'a> { - dtor: 'a || +/** + * The most general form of the `finally` functions. The function + * `try_fn` will be invoked first; whether or not it fails, the + * function `finally_fn` will be invoked next. The two parameters + * `mutate` and `drop` are used to thread state through the two + * closures. `mutate` is used for any shared, mutable state that both + * closures require access to; `drop` is used for any state that the + * `try_fn` requires ownership of. + * + * **WARNING:** While shared, mutable state between the try and finally + * function is often necessary, one must be very careful; the `try` + * function could have failed at any point, so the values of the shared + * state may be inconsistent. + * + * # Example + * + * ``` + * struct State<'a> { buffer: &'a mut [u8], len: uint } + * let mut state = State { buffer: buf, len: 0 }; + * try_finally( + * &mut state, (), + * |state, ()| { + * // use state.buffer, state.len + * } + * |state| { + * // use state.buffer, state.len to cleanup + * }) + * ``` + */ +pub fn try_finally(mutate: &mut T, + drop: U, + try_fn: |&mut T, U| -> R, + finally_fn: |&mut T|) + -> R { + let f = Finallyalizer { + mutate: mutate, + dtor: finally_fn, + }; + try_fn(&mut *f.mutate, drop) +} + +struct Finallyalizer<'a,A> { + mutate: &'a mut A, + dtor: 'a |&mut A| } #[unsafe_destructor] -impl<'a> Drop for Finallyalizer<'a> { +impl<'a,A> Drop for Finallyalizer<'a,A> { #[inline] fn drop(&mut self) { - (self.dtor)(); + (self.dtor)(self.mutate); } } #[test] fn test_success() { let mut i = 0; - (|| { - i = 10; - }).finally(|| { - assert!(!failing()); - assert_eq!(i, 10); - i = 20; - }); + try_finally( + &mut i, (), + |i, ()| { + *i = 10; + }, + |i| { + assert!(!failing()); + assert_eq!(*i, 10); + *i = 20; + }); assert_eq!(i, 20); } @@ -85,13 +126,16 @@ fn test_success() { #[should_fail] fn test_fail() { let mut i = 0; - (|| { - i = 10; - fail!(); - }).finally(|| { - assert!(failing()); - assert_eq!(i, 10); - }) + try_finally( + &mut i, (), + |i, ()| { + *i = 10; + fail!(); + }, + |i| { + assert!(failing()); + assert_eq!(*i, 10); + }) } #[test] diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index b58e0820cfd..bbb11d774b0 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -119,7 +119,8 @@ use mem; use mem::size_of; use kinds::marker; use uint; -use unstable::finally::Finally; +use unstable::finally::try_finally; +use unstable::intrinsics; use unstable::raw::{Repr, Slice, Vec}; /** @@ -132,15 +133,16 @@ pub fn from_fn(n_elts: uint, op: |uint| -> T) -> ~[T] { unsafe { let mut v = with_capacity(n_elts); let p = v.as_mut_ptr(); - let mut i: uint = 0u; - (|| { - while i < n_elts { - mem::move_val_init(&mut(*ptr::mut_offset(p, i as int)), op(i)); - i += 1u; - } - }).finally(|| { - v.set_len(i); - }); + let mut i = 0; + try_finally( + &mut i, (), + |i, ()| while *i < n_elts { + mem::move_val_init( + &mut(*ptr::mut_offset(p, *i as int)), + op(*i)); + *i += 1u; + }, + |i| v.set_len(*i)); v } } @@ -160,14 +162,15 @@ pub fn from_elem(n_elts: uint, t: T) -> ~[T] { let mut v = with_capacity(n_elts); let p = v.as_mut_ptr(); let mut i = 0u; - (|| { - while i < n_elts { - mem::move_val_init(&mut(*ptr::mut_offset(p, i as int)), t.clone()); - i += 1u; - } - }).finally(|| { - v.set_len(i); - }); + try_finally( + &mut i, (), + |i, ()| while *i < n_elts { + mem::move_val_init( + &mut(*ptr::mut_offset(p, *i as int)), + t.clone()); + *i += 1u; + }, + |i| v.set_len(*i)); v } } From 96139bf1d6eb00d60e8f6521dbf56fc23286723f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 06:38:20 -0500 Subject: [PATCH 02/33] remove antiquated reflect test rather than bring it up to date --- src/test/run-pass/reflect-visit-data.rs | 627 ------------------------ 1 file changed, 627 deletions(-) delete mode 100644 src/test/run-pass/reflect-visit-data.rs diff --git a/src/test/run-pass/reflect-visit-data.rs b/src/test/run-pass/reflect-visit-data.rs deleted file mode 100644 index bc491cc9b7f..00000000000 --- a/src/test/run-pass/reflect-visit-data.rs +++ /dev/null @@ -1,627 +0,0 @@ -// 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. - -// ignore-fast - -#[feature(managed_boxes)]; - -use std::cell::RefCell; -use std::libc::c_void; -use std::ptr; -use std::mem; -use std::unstable::intrinsics::{TyDesc, get_tydesc, visit_tydesc, TyVisitor, Disr, Opaque}; -use std::unstable::raw::Vec; - -#[doc = "High-level interfaces to `std::unstable::intrinsics::visit_ty` reflection system."] - -/// Trait for visitor that wishes to reflect on data. -trait movable_ptr { - fn move_ptr(&mut self, adjustment: |*c_void| -> *c_void); -} - -/// Helper function for alignment calculation. -#[inline(always)] -fn align(size: uint, align: uint) -> uint { - ((size + align) - 1u) & !(align - 1u) -} - -struct ptr_visit_adaptor(Inner); - -impl ptr_visit_adaptor { - fn inner<'a>(&'a mut self) -> &'a mut V { - let ptr_visit_adaptor(ref mut i) = *self; - &mut i.inner - } -} - -impl ptr_visit_adaptor { - - #[inline(always)] - pub fn bump(&mut self, sz: uint) { - self.inner().move_ptr(|p| ((p as uint) + sz) as *c_void) - } - - #[inline(always)] - pub fn align(&mut self, a: uint) { - self.inner().move_ptr(|p| align(p as uint, a) as *c_void) - } - - #[inline(always)] - pub fn align_to(&mut self) { - self.align(mem::min_align_of::()); - } - - #[inline(always)] - pub fn bump_past(&mut self) { - self.bump(mem::size_of::()); - } - -} - -impl TyVisitor for ptr_visit_adaptor { - - fn visit_bot(&mut self) -> bool { - self.align_to::<()>(); - if ! self.inner().visit_bot() { return false; } - self.bump_past::<()>(); - true - } - - fn visit_nil(&mut self) -> bool { - self.align_to::<()>(); - if ! self.inner().visit_nil() { return false; } - self.bump_past::<()>(); - true - } - - fn visit_bool(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_bool() { return false; } - self.bump_past::(); - true - } - - fn visit_int(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_int() { return false; } - self.bump_past::(); - true - } - - fn visit_i8(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_i8() { return false; } - self.bump_past::(); - true - } - - fn visit_i16(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_i16() { return false; } - self.bump_past::(); - true - } - - fn visit_i32(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_i32() { return false; } - self.bump_past::(); - true - } - - fn visit_i64(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_i64() { return false; } - self.bump_past::(); - true - } - - fn visit_uint(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_uint() { return false; } - self.bump_past::(); - true - } - - fn visit_u8(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_u8() { return false; } - self.bump_past::(); - true - } - - fn visit_u16(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_u16() { return false; } - self.bump_past::(); - true - } - - fn visit_u32(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_u32() { return false; } - self.bump_past::(); - true - } - - fn visit_u64(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_u64() { return false; } - self.bump_past::(); - true - } - - fn visit_f32(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_f32() { return false; } - self.bump_past::(); - true - } - - fn visit_f64(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_f64() { return false; } - self.bump_past::(); - true - } - - fn visit_char(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_char() { return false; } - self.bump_past::(); - true - } - - fn visit_estr_box(&mut self) -> bool { - true - } - - fn visit_estr_uniq(&mut self) -> bool { - self.align_to::<~str>(); - if ! self.inner().visit_estr_uniq() { return false; } - self.bump_past::<~str>(); - true - } - - fn visit_estr_slice(&mut self) -> bool { - self.align_to::<&'static str>(); - if ! self.inner().visit_estr_slice() { return false; } - self.bump_past::<&'static str>(); - true - } - - fn visit_estr_fixed(&mut self, n: uint, - sz: uint, - align: uint) -> bool { - self.align(align); - if ! self.inner().visit_estr_fixed(n, sz, align) { return false; } - self.bump(sz); - true - } - - fn visit_box(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<@u8>(); - if ! self.inner().visit_box(mtbl, inner) { return false; } - self.bump_past::<@u8>(); - true - } - - fn visit_uniq(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<~u8>(); - if ! self.inner().visit_uniq(mtbl, inner) { return false; } - self.bump_past::<~u8>(); - true - } - - fn visit_ptr(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<*u8>(); - if ! self.inner().visit_ptr(mtbl, inner) { return false; } - self.bump_past::<*u8>(); - true - } - - fn visit_rptr(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<&'static u8>(); - if ! self.inner().visit_rptr(mtbl, inner) { return false; } - self.bump_past::<&'static u8>(); - true - } - - fn visit_unboxed_vec(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::>(); - // FIXME (#3732): Inner really has to move its own pointers on this one. - // or else possibly we could have some weird interface wherein we - // read-off a word from inner's pointers, but the read-word has to - // always be the same in all sub-pointers? Dubious. - if ! self.inner().visit_vec(mtbl, inner) { return false; } - true - } - - fn visit_vec(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<~[u8]>(); - if ! self.inner().visit_vec(mtbl, inner) { return false; } - self.bump_past::<~[u8]>(); - true - } - - fn visit_evec_box(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - true - } - - fn visit_evec_uniq(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<~[u8]>(); - if ! self.inner().visit_evec_uniq(mtbl, inner) { return false; } - self.bump_past::<~[u8]>(); - true - } - - fn visit_evec_slice(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<&'static [u8]>(); - if ! self.inner().visit_evec_slice(mtbl, inner) { return false; } - self.bump_past::<&'static [u8]>(); - true - } - - fn visit_evec_fixed(&mut self, n: uint, sz: uint, align: uint, - mtbl: uint, inner: *TyDesc) -> bool { - self.align(align); - if ! self.inner().visit_evec_fixed(n, sz, align, mtbl, inner) { - return false; - } - self.bump(sz); - true - } - - fn visit_enter_rec(&mut self, n_fields: uint, sz: uint, align: uint) -> bool { - self.align(align); - if ! self.inner().visit_enter_rec(n_fields, sz, align) { return false; } - true - } - - fn visit_rec_field(&mut self, i: uint, name: &str, - mtbl: uint, inner: *TyDesc) -> bool { - if ! self.inner().visit_rec_field(i, name, mtbl, inner) { return false; } - true - } - - fn visit_leave_rec(&mut self, n_fields: uint, sz: uint, align: uint) -> bool { - if ! self.inner().visit_leave_rec(n_fields, sz, align) { return false; } - true - } - - fn visit_enter_class(&mut self, name: &str, named_fields: bool, n_fields: uint, sz: uint, - align: uint) -> bool { - self.align(align); - if ! self.inner().visit_enter_class(name, named_fields, n_fields, sz, align) { - return false; - } - true - } - - fn visit_class_field(&mut self, i: uint, name: &str, named: bool, - mtbl: uint, inner: *TyDesc) -> bool { - if ! self.inner().visit_class_field(i, name, named, mtbl, inner) { - return false; - } - true - } - - fn visit_leave_class(&mut self, name: &str, named_fields: bool, n_fields: uint, sz: uint, - align: uint) -> bool { - if ! self.inner().visit_leave_class(name, named_fields, n_fields, sz, align) { - return false; - } - true - } - - fn visit_enter_tup(&mut self, n_fields: uint, sz: uint, align: uint) -> bool { - self.align(align); - if ! self.inner().visit_enter_tup(n_fields, sz, align) { return false; } - true - } - - fn visit_tup_field(&mut self, i: uint, inner: *TyDesc) -> bool { - if ! self.inner().visit_tup_field(i, inner) { return false; } - true - } - - fn visit_leave_tup(&mut self, n_fields: uint, sz: uint, align: uint) -> bool { - if ! self.inner().visit_leave_tup(n_fields, sz, align) { return false; } - true - } - - fn visit_enter_fn(&mut self, purity: uint, proto: uint, - n_inputs: uint, retstyle: uint) -> bool { - if ! self.inner().visit_enter_fn(purity, proto, n_inputs, retstyle) { - return false - } - true - } - - fn visit_fn_input(&mut self, i: uint, mode: uint, inner: *TyDesc) -> bool { - if ! self.inner().visit_fn_input(i, mode, inner) { return false; } - true - } - - fn visit_fn_output(&mut self, retstyle: uint, variadic: bool, inner: *TyDesc) -> bool { - if ! self.inner().visit_fn_output(retstyle, variadic, inner) { return false; } - true - } - - fn visit_leave_fn(&mut self, purity: uint, proto: uint, - n_inputs: uint, retstyle: uint) -> bool { - if ! self.inner().visit_leave_fn(purity, proto, n_inputs, retstyle) { - return false; - } - true - } - - fn visit_enter_enum(&mut self, n_variants: uint, - get_disr: extern unsafe fn(ptr: *Opaque) -> Disr, - sz: uint, align: uint) - -> bool { - self.align(align); - if ! self.inner().visit_enter_enum(n_variants, get_disr, sz, align) { return false; } - true - } - - fn visit_enter_enum_variant(&mut self, variant: uint, - disr_val: Disr, - n_fields: uint, - name: &str) -> bool { - if ! self.inner().visit_enter_enum_variant(variant, disr_val, - n_fields, name) { - return false; - } - true - } - - fn visit_enum_variant_field(&mut self, i: uint, offset: uint, inner: *TyDesc) -> bool { - if ! self.inner().visit_enum_variant_field(i, offset, inner) { return false; } - true - } - - fn visit_leave_enum_variant(&mut self, variant: uint, - disr_val: Disr, - n_fields: uint, - name: &str) -> bool { - if ! self.inner().visit_leave_enum_variant(variant, disr_val, - n_fields, name) { - return false; - } - true - } - - fn visit_leave_enum(&mut self, n_variants: uint, - get_disr: extern unsafe fn(ptr: *Opaque) -> Disr, - sz: uint, align: uint) - -> bool { - if ! self.inner().visit_leave_enum(n_variants, get_disr, sz, align) { return false; } - true - } - - fn visit_trait(&mut self, name: &str) -> bool { - self.align_to::<~TyVisitor>(); - if ! self.inner().visit_trait(name) { return false; } - self.bump_past::<~TyVisitor>(); - true - } - - fn visit_param(&mut self, i: uint) -> bool { - if ! self.inner().visit_param(i) { return false; } - true - } - - fn visit_self(&mut self) -> bool { - self.align_to::<&'static u8>(); - if ! self.inner().visit_self() { return false; } - self.align_to::<&'static u8>(); - true - } - - fn visit_type(&mut self) -> bool { - if ! self.inner().visit_type() { return false; } - true - } -} - -struct my_visitor(@RefCell); - -#[deriving(Clone)] -struct Stuff { - ptr1: *c_void, - ptr2: *c_void, - vals: ~[~str] -} - -impl my_visitor { - pub fn get(&mut self, f: |T|) { - unsafe { - let my_visitor(s) = *self; - f((*((*s).get().ptr1 as *T)).clone()); - } - } - - pub fn visit_inner(&mut self, inner: *TyDesc) -> bool { - unsafe { - let my_visitor(s) = *self; - let u = my_visitor(s); - let mut v = ptr_visit_adaptor::(Inner {inner: u}); - visit_tydesc(inner, &mut v as &mut TyVisitor); - true - } - } -} - -struct Inner { inner: V } - -impl movable_ptr for my_visitor { - fn move_ptr(&mut self, adjustment: |*c_void| -> *c_void) { - let my_visitor(s) = *self; - let mut this = s.borrow_mut(); - this.get().ptr1 = adjustment(this.get().ptr1); - this.get().ptr2 = adjustment(this.get().ptr2); - } -} - -impl TyVisitor for my_visitor { - - fn visit_bot(&mut self) -> bool { true } - fn visit_nil(&mut self) -> bool { true } - fn visit_bool(&mut self) -> bool { - self.get::(|b| { - let my_visitor(s) = *self; - let mut this = s.borrow_mut(); - this.get().vals.push(b.to_str()); - }); - true - } - fn visit_int(&mut self) -> bool { - self.get::(|i| { - let my_visitor(s) = *self; - let mut this = s.borrow_mut(); - this.get().vals.push(i.to_str()); - }); - true - } - fn visit_i8(&mut self) -> bool { true } - fn visit_i16(&mut self) -> bool { true } - fn visit_i32(&mut self) -> bool { true } - fn visit_i64(&mut self) -> bool { true } - - fn visit_uint(&mut self) -> bool { true } - fn visit_u8(&mut self) -> bool { true } - fn visit_u16(&mut self) -> bool { true } - fn visit_u32(&mut self) -> bool { true } - fn visit_u64(&mut self) -> bool { true } - - fn visit_f32(&mut self) -> bool { true } - fn visit_f64(&mut self) -> bool { true } - - fn visit_char(&mut self) -> bool { true } - - fn visit_estr_box(&mut self) -> bool { true } - fn visit_estr_uniq(&mut self) -> bool { true } - fn visit_estr_slice(&mut self) -> bool { true } - fn visit_estr_fixed(&mut self, _n: uint, _sz: uint, - _align: uint) -> bool { true } - - fn visit_box(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_uniq(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_ptr(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_rptr(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - - fn visit_vec(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_unboxed_vec(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_evec_box(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_evec_uniq(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_evec_slice(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_evec_fixed(&mut self, _n: uint, _sz: uint, _align: uint, - _mtbl: uint, _inner: *TyDesc) -> bool { true } - - fn visit_enter_rec(&mut self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - fn visit_rec_field(&mut self, _i: uint, _name: &str, - _mtbl: uint, inner: *TyDesc) -> bool { - error!("rec field!"); - self.visit_inner(inner) - } - fn visit_leave_rec(&mut self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - - fn visit_enter_class(&mut self, _name: &str, _named_fields: bool, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - fn visit_class_field(&mut self, _i: uint, _name: &str, _named: bool, - _mtbl: uint, inner: *TyDesc) -> bool { - self.visit_inner(inner) - } - fn visit_leave_class(&mut self, _name: &str, _named_fields: bool, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - - fn visit_enter_tup(&mut self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - fn visit_tup_field(&mut self, _i: uint, inner: *TyDesc) -> bool { - error!("tup field!"); - self.visit_inner(inner) - } - fn visit_leave_tup(&mut self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - - fn visit_enter_enum(&mut self, _n_variants: uint, - _get_disr: extern unsafe fn(ptr: *Opaque) -> Disr, - _sz: uint, _align: uint) -> bool { - // FIXME (#3732): this needs to rewind between enum variants, or something. - true - } - fn visit_enter_enum_variant(&mut self, _variant: uint, - _disr_val: Disr, - _n_fields: uint, - _name: &str) -> bool { true } - fn visit_enum_variant_field(&mut self, _i: uint, _offset: uint, inner: *TyDesc) -> bool { - self.visit_inner(inner) - } - fn visit_leave_enum_variant(&mut self, _variant: uint, - _disr_val: Disr, - _n_fields: uint, - _name: &str) -> bool { true } - fn visit_leave_enum(&mut self, _n_variants: uint, - _get_disr: extern unsafe fn(ptr: *Opaque) -> Disr, - _sz: uint, _align: uint) -> bool { true } - - fn visit_enter_fn(&mut self, _purity: uint, _proto: uint, - _n_inputs: uint, _retstyle: uint) -> bool { true } - fn visit_fn_input(&mut self, _i: uint, _mode: uint, _inner: *TyDesc) -> bool { - true - } - fn visit_fn_output(&mut self, _retstyle: uint, _variadic: bool, _inner: *TyDesc) -> bool { - true - } - fn visit_leave_fn(&mut self, _purity: uint, _proto: uint, - _n_inputs: uint, _retstyle: uint) -> bool { true } - - - fn visit_trait(&mut self, _name: &str) -> bool { true } - fn visit_param(&mut self, _i: uint) -> bool { true } - fn visit_self(&mut self) -> bool { true } - fn visit_type(&mut self) -> bool { true } -} - -fn get_tydesc_for(_t: T) -> *TyDesc { - unsafe { - get_tydesc::() - } -} - -struct Triple { x: int, y: int, z: int } - -pub fn main() { - unsafe { - let r = (1,2,3,true,false, Triple {x:5,y:4,z:3}, (12,)); - let p = ptr::to_unsafe_ptr(&r) as *c_void; - let u = my_visitor(@RefCell::new(Stuff {ptr1: p, - ptr2: p, - vals: ~[]})); - let mut v = ptr_visit_adaptor(Inner {inner: u}); - let td = get_tydesc_for(r); - error!("tydesc sz: {}, align: {}", - (*td).size, (*td).align); - visit_tydesc(td, &mut v as &mut TyVisitor); - - let my_visitor(m) = u; - let mut ub = m.borrow_mut(); - let r = ub.get().vals.clone(); - for s in r.iter() { - println!("val: {}", *s); - } - error!("{:?}", ub.get().vals.clone()); - assert_eq!(ub.get().vals.clone(), - ~[ ~"1", ~"2", ~"3", ~"true", ~"false", ~"5", ~"4", ~"3", ~"12"]); - } -} From 6f571a63a6e966ae59e0d078542c04734eba58ac Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 06:38:53 -0500 Subject: [PATCH 03/33] libglob -- patch closure where const borrow would have helped --- src/libglob/lib.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libglob/lib.rs b/src/libglob/lib.rs index c6ecb7697da..25634b1808d 100644 --- a/src/libglob/lib.rs +++ b/src/libglob/lib.rs @@ -28,6 +28,7 @@ #[crate_type = "dylib"]; #[license = "MIT/ASL2"]; +use std::cell::Cell; use std::{os, path}; use std::io::fs; use std::path::is_sep; @@ -342,22 +343,24 @@ impl Pattern { } fn matches_from(&self, - mut prev_char: Option, + prev_char: Option, mut file: &str, i: uint, options: MatchOptions) -> MatchResult { + let prev_char = Cell::new(prev_char); + let require_literal = |c| { (options.require_literal_separator && is_sep(c)) || (options.require_literal_leading_dot && c == '.' - && is_sep(prev_char.unwrap_or('/'))) + && is_sep(prev_char.get().unwrap_or('/'))) }; for (ti, token) in self.tokens.slice_from(i).iter().enumerate() { match *token { AnySequence => { loop { - match self.matches_from(prev_char, file, i + ti + 1, options) { + match self.matches_from(prev_char.get(), file, i + ti + 1, options) { SubPatternDoesntMatch => (), // keep trying m => return m, } @@ -370,7 +373,7 @@ impl Pattern { if require_literal(c) { return SubPatternDoesntMatch; } - prev_char = Some(c); + prev_char.set(Some(c)); file = next; } } @@ -400,7 +403,7 @@ impl Pattern { if !matches { return SubPatternDoesntMatch; } - prev_char = Some(c); + prev_char.set(Some(c)); file = next; } } From 0f5baad6ee5191991d18271aabebaf3fc6124424 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 06:52:01 -0500 Subject: [PATCH 04/33] container -- update example to contain scope of closure borrow --- src/doc/guide-container.md | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/doc/guide-container.md b/src/doc/guide-container.md index 1c79be1eb82..ebad650a534 100644 --- a/src/doc/guide-container.md +++ b/src/doc/guide-container.md @@ -181,19 +181,25 @@ never call its underlying iterator again once `None` has been returned: ~~~ let xs = [1,2,3,4,5]; let mut calls = 0; -let it = xs.iter().scan((), |_, x| { - calls += 1; - if *x < 3 { Some(x) } else { None }}); -// the iterator will only yield 1 and 2 before returning None -// If we were to call it 5 times, calls would end up as 5, despite only 2 values -// being yielded (and therefore 3 unique calls being made). The fuse() adaptor -// can fix this. -let mut it = it.fuse(); -it.next(); -it.next(); -it.next(); -it.next(); -it.next(); + +{ + let it = xs.iter().scan((), |_, x| { + calls += 1; + if *x < 3 { Some(x) } else { None }}); + + // the iterator will only yield 1 and 2 before returning None + // If we were to call it 5 times, calls would end up as 5, despite + // only 2 values being yielded (and therefore 3 unique calls being + // made). The fuse() adaptor can fix this. + + let mut it = it.fuse(); + it.next(); + it.next(); + it.next(); + it.next(); + it.next(); +} + assert_eq!(calls, 3); ~~~ From 0e005ab84892aa201f720cc60d2aef002bfd7a4a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 9 Feb 2014 14:08:18 -0500 Subject: [PATCH 05/33] to_str -- update to contain scope of closure --- src/libsyntax/ext/deriving/to_str.rs | 41 ++++++++++++++-------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/libsyntax/ext/deriving/to_str.rs b/src/libsyntax/ext/deriving/to_str.rs index 186f1254493..e5145fb15f7 100644 --- a/src/libsyntax/ext/deriving/to_str.rs +++ b/src/libsyntax/ext/deriving/to_str.rs @@ -67,31 +67,32 @@ fn to_str_substructure(cx: &mut ExtCtxt, span: Span, substr: &Substructure) let mut stmts = ~[cx.stmt_let(span, true, buf, init)]; let push_str = cx.ident_of("push_str"); - let push = |s: @Expr| { - let ebuf = cx.expr_ident(span, buf); - let call = cx.expr_method_call(span, ebuf, push_str, ~[s]); - stmts.push(cx.stmt_expr(call)); - }; + { + let push = |s: @Expr| { + let ebuf = cx.expr_ident(span, buf); + let call = cx.expr_method_call(span, ebuf, push_str, ~[s]); + stmts.push(cx.stmt_expr(call)); + }; - for (i, &FieldInfo {name, span, self_, .. }) in fields.iter().enumerate() { - if i > 0 { - push(cx.expr_str(span, InternedString::new(", "))); - } - match name { - None => {} - Some(id) => { - let interned_id = token::get_ident(id.name); - let name = interned_id.get() + ": "; - push(cx.expr_str(span, - token::intern_and_get_ident(name))); + for (i, &FieldInfo {name, span, self_, .. }) in fields.iter().enumerate() { + if i > 0 { + push(cx.expr_str(span, InternedString::new(", "))); } + match name { + None => {} + Some(id) => { + let interned_id = token::get_ident(id.name); + let name = interned_id.get() + ": "; + push(cx.expr_str(span, + token::intern_and_get_ident(name))); + } + } + push(cx.expr_method_call(span, self_, to_str, ~[])); } - push(cx.expr_method_call(span, self_, to_str, ~[])); + push(cx.expr_str(span, end)); } - push(cx.expr_str(span, end)); - cx.expr_block(cx.block(span, stmts, Some(cx.expr_ident(span, - buf)))) + cx.expr_block(cx.block(span, stmts, Some(cx.expr_ident(span, buf)))) } }; From f7e5d8418c0e6dcd17efc61f7f5eb641dce8653d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:42:48 -0500 Subject: [PATCH 06/33] ty -- minor refactorings, helper methods --- src/librustc/middle/ty.rs | 47 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 7b2aee9274a..5b58ec57c63 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -45,6 +45,7 @@ use syntax::attr; use syntax::attr::AttrMetaMethods; use syntax::codemap::Span; use syntax::parse::token; +use syntax::parse::token::InternedString; use syntax::{ast, ast_map}; use syntax::opt_vec::OptVec; use syntax::opt_vec; @@ -2668,8 +2669,13 @@ pub fn node_id_to_trait_ref(cx: ctxt, id: ast::NodeId) -> @ty::TraitRef { } } +pub fn try_node_id_to_type(cx: ctxt, id: ast::NodeId) -> Option { + let node_types = cx.node_types.borrow(); + node_types.get().find_copy(&(id as uint)) +} + pub fn node_id_to_type(cx: ctxt, id: ast::NodeId) -> t { - match node_id_to_type_opt(cx, id) { + match try_node_id_to_type(cx, id) { Some(t) => t, None => cx.sess.bug( format!("node_id_to_type: no type for node `{}`", @@ -2883,6 +2889,45 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: &ast::Expr) -> t { adjust_ty(cx, expr.span, unadjusted_ty, adjustment) } +pub fn expr_span(cx: ctxt, id: NodeId) -> Span { + match cx.items.find(id) { + Some(ast_map::NodeExpr(e)) => { + e.span + } + Some(f) => { + cx.sess.bug(format!("Node id {} is not an expr: {:?}", + id, f)); + } + None => { + cx.sess.bug(format!("Node id {} is not present \ + in the node map", id)); + } + } +} + +pub fn local_var_name_str(cx: ctxt, id: NodeId) -> InternedString { + match cx.items.find(id) { + Some(ast_map::NodeLocal(pat)) => { + match pat.node { + ast::PatIdent(_, ref path, _) => { + let ident = ast_util::path_to_ident(path); + token::get_ident(ident.name) + } + _ => { + cx.sess.bug( + format!("Variable id {} maps to {:?}, not local", + id, pat)); + } + } + } + r => { + cx.sess.bug( + format!("Variable id {} maps to {:?}, not local", + id, r)); + } + } +} + pub fn adjust_ty(cx: ctxt, span: Span, unadjusted_ty: ty::t, From 64c9b5c3aeb037f8113948da2c098bb8c86b9d8a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:43:05 -0500 Subject: [PATCH 07/33] trans/datum -- move mutable variable into closure --- src/librustc/middle/trans/datum.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index e7ebc2ef526..329301efa5e 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -480,9 +480,9 @@ impl Datum { * no cleanup scheduled). */ - let mut bcx = bcx; self.match_kind( |l| { + let mut bcx = bcx; match l.appropriate_rvalue_mode(bcx.ccx()) { ByRef => { let scratch = rvalue_scratch_datum(bcx, l.ty, name); From 7286e35c6b840763012db2ca4b0655b8344f02ba Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:43:48 -0500 Subject: [PATCH 08/33] error_reporting -- explain reborrowed upvar constraints in a hopefully useful way --- .../middle/typeck/infer/error_reporting.rs | 41 ++++++++++++++++--- src/librustc/middle/typeck/infer/mod.rs | 17 ++++++-- .../typeck/infer/region_inference/mod.rs | 6 ++- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 657b75a44ed..3a3f24a2e2d 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -237,6 +237,24 @@ impl ErrorReporting for InferCtxt { sup, ""); } + infer::ReborrowUpvar(span, ref upvar_id) => { + self.tcx.sess.span_err( + span, + format!("lifetime of borrowed pointer outlives \ + lifetime of captured variable `{}`...", + ty::local_var_name_str(self.tcx, upvar_id.var_id).get().to_str())); + note_and_explain_region( + self.tcx, + "...the borrowed pointer is valid for ", + sub, + "..."); + note_and_explain_region( + self.tcx, + format!("...but `{}` is only valid for ", + ty::local_var_name_str(self.tcx, upvar_id.var_id).get().to_str()), + sup, + ""); + } infer::InfStackClosure(span) => { self.tcx.sess.span_err( span, @@ -272,10 +290,12 @@ impl ErrorReporting for InferCtxt { sup, ""); } - infer::FreeVariable(span) => { + infer::FreeVariable(span, id) => { self.tcx.sess.span_err( span, - "captured variable does not outlive the enclosing closure"); + format!("captured variable `{}` does not \ + outlive the enclosing closure", + ty::local_var_name_str(self.tcx, id).get().to_str())); note_and_explain_region( self.tcx, "captured variable is valid for ", @@ -473,6 +493,10 @@ impl ErrorReportingHelpers for InferCtxt { infer::BoundRegionInCoherence(..) => { format!(" for coherence check") } + infer::UpvarRegion(ref upvar_id, _) => { + format!(" for capture of `{}` by closure", + ty::local_var_name_str(self.tcx, upvar_id.var_id).get().to_str()) + } }; self.tcx.sess.span_err( @@ -533,6 +557,12 @@ impl ErrorReportingHelpers for InferCtxt { "...so that reference does not outlive \ borrowed content"); } + infer::ReborrowUpvar(span, ref upvar_id) => { + self.tcx.sess.span_note( + span, + format!("...so that closure can access `{}`", + ty::local_var_name_str(self.tcx, upvar_id.var_id).get().to_str())) + } infer::InfStackClosure(span) => { self.tcx.sess.span_note( span, @@ -549,11 +579,12 @@ impl ErrorReportingHelpers for InferCtxt { "...so that pointer is not dereferenced \ outside its lifetime"); } - infer::FreeVariable(span) => { + infer::FreeVariable(span, id) => { self.tcx.sess.span_note( span, - "...so that captured variable does not outlive the \ - enclosing closure"); + format!("...so that captured variable `{}` \ + does not outlive the enclosing closure", + ty::local_var_name_str(self.tcx, id).get().to_str())); } infer::IndexSlice(span) => { self.tcx.sess.span_note( diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 496170c3e47..deec4100617 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -160,7 +160,7 @@ pub enum SubregionOrigin { DerefPointer(Span), // Closure bound must not outlive captured free variables - FreeVariable(Span), + FreeVariable(Span, ast::NodeId), // Index into slice must be within its lifetime IndexSlice(Span), @@ -172,6 +172,9 @@ pub enum SubregionOrigin { // Creating a pointer `b` to contents of another reference Reborrow(Span), + // Creating a pointer `b` to contents of an upvar + ReborrowUpvar(Span, ty::UpvarId), + // (&'a &'b T) where a >= b ReferenceOutlivesReferent(ty::t, Span), @@ -225,6 +228,8 @@ pub enum RegionVariableOrigin { // when doing subtyping/lub/glb computations BoundRegionInFnType(Span, ty::BoundRegion), + UpvarRegion(ty::UpvarId, Span), + BoundRegionInTypeOrImpl(Span), BoundRegionInCoherence, @@ -876,10 +881,11 @@ impl SubregionOrigin { InfStackClosure(a) => a, InvokeClosure(a) => a, DerefPointer(a) => a, - FreeVariable(a) => a, + FreeVariable(a, _) => a, IndexSlice(a) => a, RelateObjectBound(a) => a, Reborrow(a) => a, + ReborrowUpvar(a, _) => a, ReferenceOutlivesReferent(_, a) => a, BindingTypeIsNotValidAtDecl(a) => a, CallRcvr(a) => a, @@ -898,10 +904,11 @@ impl Repr for SubregionOrigin { InfStackClosure(a) => format!("InfStackClosure({})", a.repr(tcx)), InvokeClosure(a) => format!("InvokeClosure({})", a.repr(tcx)), DerefPointer(a) => format!("DerefPointer({})", a.repr(tcx)), - FreeVariable(a) => format!("FreeVariable({})", a.repr(tcx)), + FreeVariable(a, b) => format!("FreeVariable({}, {})", a.repr(tcx), b), IndexSlice(a) => format!("IndexSlice({})", a.repr(tcx)), RelateObjectBound(a) => format!("RelateObjectBound({})", a.repr(tcx)), Reborrow(a) => format!("Reborrow({})", a.repr(tcx)), + ReborrowUpvar(a, b) => format!("ReborrowUpvar({},{:?})", a.repr(tcx), b), ReferenceOutlivesReferent(_, a) => format!("ReferenceOutlivesReferent({})", a.repr(tcx)), BindingTypeIsNotValidAtDecl(a) => @@ -928,6 +935,7 @@ impl RegionVariableOrigin { BoundRegionInFnType(a, _) => a, BoundRegionInTypeOrImpl(a) => a, BoundRegionInCoherence => codemap::DUMMY_SP, + UpvarRegion(_, a) => a } } } @@ -948,6 +956,9 @@ impl Repr for RegionVariableOrigin { BoundRegionInTypeOrImpl(a) => format!("bound_regionInTypeOrImpl({})", a.repr(tcx)), BoundRegionInCoherence => format!("bound_regionInCoherence"), + UpvarRegion(a, b) => format!("UpvarRegion({}, {})", + a.repr(tcx), + b.repr(tcx)), } } } diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/typeck/infer/region_inference/mod.rs index 2bc0d7d6419..bbd9d8e1c4d 100644 --- a/src/librustc/middle/typeck/infer/region_inference/mod.rs +++ b/src/librustc/middle/typeck/infer/region_inference/mod.rs @@ -270,7 +270,11 @@ impl RegionVarBindings { // cannot add constraints once regions are resolved assert!(self.values_are_none()); - debug!("RegionVarBindings: make_subregion({:?}, {:?})", sub, sup); + debug!("RegionVarBindings: make_subregion({}, {}) due to {}", + sub.repr(self.tcx), + sup.repr(self.tcx), + origin.repr(self.tcx)); + match (sub, sup) { (ReEarlyBound(..), _) | (ReLateBound(..), _) | From 949e1c7935776ddb3e67ade590e9df58a5894cf8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:44:56 -0500 Subject: [PATCH 09/33] metadata -- remove tiny convenience closure that was causing conflicting mutable borrows --- src/librustc/metadata/encoder.rs | 19 +++++++++---------- src/librustc/metadata/tydecode.rs | 3 ++- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 4abfedb8722..ea4d6d3b252 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -924,7 +924,6 @@ fn encode_info_for_item(ecx: &EncodeContext, pos: ebml_w.writer.tell().unwrap(), }); } - let add_to_index: || = || add_to_index(item, ebml_w, index); debug!("encoding info for item at {}", ecx.tcx.sess.codemap.span_to_str(item.span)); @@ -932,7 +931,7 @@ fn encode_info_for_item(ecx: &EncodeContext, let def_id = local_def(item.id); match item.node { ItemStatic(_, m, _) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); if m == ast::MutMutable { @@ -959,7 +958,7 @@ fn encode_info_for_item(ecx: &EncodeContext, ebml_w.end_tag(); } ItemFn(_, purity, _, ref generics, _) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, purity_fn_family(purity)); @@ -977,7 +976,7 @@ fn encode_info_for_item(ecx: &EncodeContext, ebml_w.end_tag(); } ItemMod(ref m) => { - add_to_index(); + add_to_index(item, ebml_w, index); encode_info_for_mod(ecx, ebml_w, m, @@ -987,7 +986,7 @@ fn encode_info_for_item(ecx: &EncodeContext, item.vis); } ItemForeignMod(ref fm) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, 'n'); @@ -1004,7 +1003,7 @@ fn encode_info_for_item(ecx: &EncodeContext, ebml_w.end_tag(); } ItemTy(..) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, 'y'); @@ -1015,7 +1014,7 @@ fn encode_info_for_item(ecx: &EncodeContext, ebml_w.end_tag(); } ItemEnum(ref enum_definition, ref generics) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); @@ -1053,7 +1052,7 @@ fn encode_info_for_item(ecx: &EncodeContext, struct_def.fields, index); /* Index the class*/ - add_to_index(); + add_to_index(item, ebml_w, index); /* Now, make an item for the class itself */ ebml_w.start_tag(tag_items_data_item); @@ -1106,7 +1105,7 @@ fn encode_info_for_item(ecx: &EncodeContext, let impls = tcx.impls.borrow(); let imp = impls.get().get(&def_id); - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, 'i'); @@ -1170,7 +1169,7 @@ fn encode_info_for_item(ecx: &EncodeContext, } } ItemTrait(_, ref super_traits, ref ms) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, 'I'); diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 42e4d986837..00e189cdc79 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -96,8 +96,9 @@ pub fn parse_ident(st: &mut PState, last: char) -> ast::Ident { } fn parse_ident_(st: &mut PState, is_last: |char| -> bool) -> ast::Ident { + let tcx = st.tcx; scan(st, is_last, |bytes| { - st.tcx.sess.ident_of(str::from_utf8(bytes).unwrap()) + tcx.sess.ident_of(str::from_utf8(bytes).unwrap()) }) } From b2b4c79b179413d4a5e1d706844b11dfca8226c8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:45:18 -0500 Subject: [PATCH 10/33] resolve -- rewrite conflict closure into method --- src/librustc/middle/resolve.rs | 91 +++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index a941c1318ca..9623f6f0cbc 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -2567,52 +2567,15 @@ impl Resolver { } } - let merge_import_resolution = |name, name_bindings: @NameBindings| { - let dest_import_resolution; - let mut import_resolutions = module_.import_resolutions - .borrow_mut(); - match import_resolutions.get().find(&name) { - None => { - // Create a new import resolution from this child. - dest_import_resolution = - @ImportResolution::new(id, is_public); - import_resolutions.get().insert(name, - dest_import_resolution); - } - Some(&existing_import_resolution) => { - dest_import_resolution = existing_import_resolution; - } - } - - debug!("(resolving glob import) writing resolution `{}` in `{}` \ - to `{}`", - token::get_ident(name).get().to_str(), - self.module_to_str(containing_module), - self.module_to_str(module_)); - - // Merge the child item into the import resolution. - if name_bindings.defined_in_public_namespace(ValueNS) { - debug!("(resolving glob import) ... for value target"); - dest_import_resolution.value_target.set( - Some(Target::new(containing_module, name_bindings))); - dest_import_resolution.value_id.set(id); - } - if name_bindings.defined_in_public_namespace(TypeNS) { - debug!("(resolving glob import) ... for type target"); - dest_import_resolution.type_target.set( - Some(Target::new(containing_module, name_bindings))); - dest_import_resolution.type_id.set(id); - } - dest_import_resolution.is_public.set(is_public); - }; - // Add all children from the containing module. self.populate_module_if_necessary(containing_module); { let children = containing_module.children.borrow(); for (&name, name_bindings) in children.get().iter() { - merge_import_resolution(name, *name_bindings); + self.merge_import_resolution(module_, containing_module, + id, is_public, + name, *name_bindings); } } @@ -2623,7 +2586,9 @@ impl Resolver { for (&name, module) in external_module_children.get().iter() { let name_bindings = @Resolver::create_name_bindings_from_module(*module); - merge_import_resolution(name, name_bindings); + self.merge_import_resolution(module_, containing_module, + id, is_public, + name, name_bindings); } } @@ -2641,6 +2606,50 @@ impl Resolver { return Success(()); } + fn merge_import_resolution(&mut self, + module_: @Module, + containing_module: @Module, + id: NodeId, + is_public: bool, + name: Name, + name_bindings: @NameBindings) { + let dest_import_resolution; + let mut import_resolutions = module_.import_resolutions.borrow_mut(); + match import_resolutions.get().find(&name) { + None => { + // Create a new import resolution from this child. + dest_import_resolution = + @ImportResolution::new(id, is_public); + import_resolutions.get().insert(name, + dest_import_resolution); + } + Some(&existing_import_resolution) => { + dest_import_resolution = existing_import_resolution; + } + } + + debug!("(resolving glob import) writing resolution `{}` in `{}` \ + to `{}`", + token::get_ident(name).get().to_str(), + self.module_to_str(containing_module), + self.module_to_str(module_)); + + // Merge the child item into the import resolution. + if name_bindings.defined_in_public_namespace(ValueNS) { + debug!("(resolving glob import) ... for value target"); + dest_import_resolution.value_target.set( + Some(Target::new(containing_module, name_bindings))); + dest_import_resolution.value_id.set(id); + } + if name_bindings.defined_in_public_namespace(TypeNS) { + debug!("(resolving glob import) ... for type target"); + dest_import_resolution.type_target.set( + Some(Target::new(containing_module, name_bindings))); + dest_import_resolution.type_id.set(id); + } + dest_import_resolution.is_public.set(is_public); + } + /// Resolves the given module path from the given root `module_`. fn resolve_module_path_from_root(&mut self, module_: @Module, From ca65c00ef2e1fb7373c97085315703887ee4d53c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:45:46 -0500 Subject: [PATCH 11/33] syntax/ext/format -- rewrite conflicting closures into methods --- src/libsyntax/ext/format.rs | 243 +++++++++++++++++++----------------- 1 file changed, 127 insertions(+), 116 deletions(-) diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index 4bc3b804c7f..35d2adbead5 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -21,6 +21,7 @@ use rsparse = parse; use std::fmt::parse; use std::hashmap::{HashMap, HashSet}; use std::vec; +use std::cell::RefCell; #[deriving(Eq)] enum ArgumentType { @@ -367,157 +368,167 @@ impl<'a> Context<'a> { return ~[unnamed, allow_dead_code]; } - /// Translate a `parse::Piece` to a static `rt::Piece` - fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr { - let sp = self.fmtsp; - let parsepath = |s: &str| { - ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), - self.ecx.ident_of("parse"), self.ecx.ident_of(s)] - }; - let rtpath = |s: &str| { - ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), - self.ecx.ident_of("rt"), self.ecx.ident_of(s)] - }; - let ctpath = |s: &str| { - ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), - self.ecx.ident_of("parse"), self.ecx.ident_of(s)] - }; - let none = self.ecx.path_global(sp, ~[ + fn parsepath(&self, s: &str) -> ~[ast::Ident] { + ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), + self.ecx.ident_of("parse"), self.ecx.ident_of(s)] + } + + fn rtpath(&self, s: &str) -> ~[ast::Ident] { + ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), + self.ecx.ident_of("rt"), self.ecx.ident_of(s)] + } + + fn ctpath(&self, s: &str) -> ~[ast::Ident] { + ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), + self.ecx.ident_of("parse"), self.ecx.ident_of(s)] + } + + fn none(&self) -> @ast::Expr { + let none = self.ecx.path_global(self.fmtsp, ~[ self.ecx.ident_of("std"), self.ecx.ident_of("option"), self.ecx.ident_of("None")]); - let none = self.ecx.expr_path(none); - let some = |e: @ast::Expr| { - let p = self.ecx.path_global(sp, ~[ + self.ecx.expr_path(none) + } + + fn some(&self, e: @ast::Expr) -> @ast::Expr { + let p = self.ecx.path_global(self.fmtsp, ~[ self.ecx.ident_of("std"), self.ecx.ident_of("option"), self.ecx.ident_of("Some")]); - let p = self.ecx.expr_path(p); - self.ecx.expr_call(sp, p, ~[e]) - }; - let trans_count = |c: parse::Count| { - match c { - parse::CountIs(i) => { - self.ecx.expr_call_global(sp, rtpath("CountIs"), - ~[self.ecx.expr_uint(sp, i)]) - } - parse::CountIsParam(i) => { - self.ecx.expr_call_global(sp, rtpath("CountIsParam"), - ~[self.ecx.expr_uint(sp, i)]) - } - parse::CountImplied => { - let path = self.ecx.path_global(sp, rtpath("CountImplied")); - self.ecx.expr_path(path) - } - parse::CountIsNextParam => { - let path = self.ecx.path_global(sp, rtpath("CountIsNextParam")); - self.ecx.expr_path(path) - } - parse::CountIsName(n) => { - let i = match self.name_positions.find_equiv(&n) { - Some(&i) => i, - None => 0, // error already emitted elsewhere - }; - let i = i + self.args.len(); - self.ecx.expr_call_global(sp, rtpath("CountIsParam"), - ~[self.ecx.expr_uint(sp, i)]) - } + let p = self.ecx.expr_path(p); + self.ecx.expr_call(self.fmtsp, p, ~[e]) + } + + fn trans_count(&self, c: parse::Count) -> @ast::Expr { + let sp = self.fmtsp; + match c { + parse::CountIs(i) => { + self.ecx.expr_call_global(sp, self.rtpath("CountIs"), + ~[self.ecx.expr_uint(sp, i)]) } - }; - let trans_method = |method: &parse::Method| { - let method = match *method { - parse::Select(ref arms, ref default) => { - let arms = arms.iter().map(|arm| { - let p = self.ecx.path_global(sp, rtpath("SelectArm")); + parse::CountIsParam(i) => { + self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"), + ~[self.ecx.expr_uint(sp, i)]) + } + parse::CountImplied => { + let path = self.ecx.path_global(sp, self.rtpath("CountImplied")); + self.ecx.expr_path(path) + } + parse::CountIsNextParam => { + let path = self.ecx.path_global(sp, self.rtpath("CountIsNextParam")); + self.ecx.expr_path(path) + } + parse::CountIsName(n) => { + let i = match self.name_positions.find_equiv(&n) { + Some(&i) => i, + None => 0, // error already emitted elsewhere + }; + let i = i + self.args.len(); + self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"), + ~[self.ecx.expr_uint(sp, i)]) + } + } + } + + fn trans_method(&mut self, method: &parse::Method) -> @ast::Expr { + let sp = self.fmtsp; + let method = match *method { + parse::Select(ref arms, ref default) => { + let arms = arms.iter().map(|arm| { + let p = self.ecx.path_global(sp, self.rtpath("SelectArm")); let result = arm.result.iter().map(|p| { self.trans_piece(p) }).collect(); let s = token::intern_and_get_ident(arm.selector); let selector = self.ecx.expr_str(sp, s); self.ecx.expr_struct(sp, p, ~[ - self.ecx.field_imm(sp, - self.ecx.ident_of("selector"), - selector), - self.ecx.field_imm(sp, self.ecx.ident_of("result"), - self.ecx.expr_vec_slice(sp, result)), - ]) + self.ecx.field_imm(sp, + self.ecx.ident_of("selector"), + selector), + self.ecx.field_imm(sp, self.ecx.ident_of("result"), + self.ecx.expr_vec_slice(sp, result)), + ]) }).collect(); - let default = default.iter().map(|p| { + let default = default.iter().map(|p| { self.trans_piece(p) }).collect(); - self.ecx.expr_call_global(sp, rtpath("Select"), ~[ + self.ecx.expr_call_global(sp, self.rtpath("Select"), ~[ self.ecx.expr_vec_slice(sp, arms), self.ecx.expr_vec_slice(sp, default), - ]) - } - parse::Plural(offset, ref arms, ref default) => { - let offset = match offset { - Some(i) => { some(self.ecx.expr_uint(sp, i)) } - None => { none.clone() } - }; - let arms = arms.iter().map(|arm| { - let p = self.ecx.path_global(sp, rtpath("PluralArm")); + ]) + } + parse::Plural(offset, ref arms, ref default) => { + let offset = match offset { + Some(i) => { self.some(self.ecx.expr_uint(sp, i)) } + None => { self.none() } + }; + let arms = arms.iter().map(|arm| { + let p = self.ecx.path_global(sp, self.rtpath("PluralArm")); let result = arm.result.iter().map(|p| { - self.trans_piece(p) - }).collect(); + self.trans_piece(p) + }).collect(); let (lr, selarg) = match arm.selector { parse::Keyword(t) => { - let p = ctpath(format!("{:?}", t)); + let p = self.ctpath(format!("{:?}", t)); let p = self.ecx.path_global(sp, p); - (rtpath("Keyword"), self.ecx.expr_path(p)) + (self.rtpath("Keyword"), self.ecx.expr_path(p)) } parse::Literal(i) => { - (rtpath("Literal"), self.ecx.expr_uint(sp, i)) + (self.rtpath("Literal"), self.ecx.expr_uint(sp, i)) } }; let selector = self.ecx.expr_call_global(sp, - lr, ~[selarg]); + lr, ~[selarg]); self.ecx.expr_struct(sp, p, ~[ - self.ecx.field_imm(sp, - self.ecx.ident_of("selector"), - selector), - self.ecx.field_imm(sp, self.ecx.ident_of("result"), - self.ecx.expr_vec_slice(sp, result)), - ]) + self.ecx.field_imm(sp, + self.ecx.ident_of("selector"), + selector), + self.ecx.field_imm(sp, self.ecx.ident_of("result"), + self.ecx.expr_vec_slice(sp, result)), + ]) }).collect(); - let default = default.iter().map(|p| { + let default = default.iter().map(|p| { self.trans_piece(p) }).collect(); - self.ecx.expr_call_global(sp, rtpath("Plural"), ~[ + self.ecx.expr_call_global(sp, self.rtpath("Plural"), ~[ offset, self.ecx.expr_vec_slice(sp, arms), self.ecx.expr_vec_slice(sp, default), - ]) - } - }; - let life = self.ecx.lifetime(sp, self.ecx.ident_of("static")); - let ty = self.ecx.ty_path(self.ecx.path_all( + ]) + } + }; + let life = self.ecx.lifetime(sp, self.ecx.ident_of("static")); + let ty = self.ecx.ty_path(self.ecx.path_all( sp, true, - rtpath("Method"), + self.rtpath("Method"), opt_vec::with(life), ~[] - ), None); - let st = ast::ItemStatic(ty, ast::MutImmutable, method); - let static_name = self.ecx.ident_of(format!("__STATIC_METHOD_{}", - self.method_statics.len())); - let item = self.ecx.item(sp, static_name, self.static_attrs(), st); - self.method_statics.push(item); - self.ecx.expr_ident(sp, static_name) - }; + ), None); + let st = ast::ItemStatic(ty, ast::MutImmutable, method); + let static_name = self.ecx.ident_of(format!("__STATIC_METHOD_{}", + self.method_statics.len())); + let item = self.ecx.item(sp, static_name, self.static_attrs(), st); + self.method_statics.push(item); + self.ecx.expr_ident(sp, static_name) + } + /// Translate a `parse::Piece` to a static `rt::Piece` + fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr { + let sp = self.fmtsp; match *piece { parse::String(s) => { let s = token::intern_and_get_ident(s); self.ecx.expr_call_global(sp, - rtpath("String"), + self.rtpath("String"), ~[ self.ecx.expr_str(sp, s) ]) } parse::CurrentArgument => { let nil = self.ecx.expr_lit(sp, ast::LitNil); - self.ecx.expr_call_global(sp, rtpath("CurrentArgument"), ~[nil]) + self.ecx.expr_call_global(sp, self.rtpath("CurrentArgument"), ~[nil]) } parse::Argument(ref arg) => { // Translate the position @@ -525,11 +536,11 @@ impl<'a> Context<'a> { // These two have a direct mapping parse::ArgumentNext => { let path = self.ecx.path_global(sp, - rtpath("ArgumentNext")); + self.rtpath("ArgumentNext")); self.ecx.expr_path(path) } parse::ArgumentIs(i) => { - self.ecx.expr_call_global(sp, rtpath("ArgumentIs"), + self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"), ~[self.ecx.expr_uint(sp, i)]) } // Named arguments are converted to positional arguments at @@ -540,7 +551,7 @@ impl<'a> Context<'a> { None => 0, // error already emitted elsewhere }; let i = i + self.args.len(); - self.ecx.expr_call_global(sp, rtpath("ArgumentIs"), + self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"), ~[self.ecx.expr_uint(sp, i)]) } }; @@ -550,20 +561,20 @@ impl<'a> Context<'a> { let fill = self.ecx.expr_lit(sp, ast::LitChar(fill as u32)); let align = match arg.format.align { parse::AlignLeft => { - self.ecx.path_global(sp, parsepath("AlignLeft")) + self.ecx.path_global(sp, self.parsepath("AlignLeft")) } parse::AlignRight => { - self.ecx.path_global(sp, parsepath("AlignRight")) + self.ecx.path_global(sp, self.parsepath("AlignRight")) } parse::AlignUnknown => { - self.ecx.path_global(sp, parsepath("AlignUnknown")) + self.ecx.path_global(sp, self.parsepath("AlignUnknown")) } }; let align = self.ecx.expr_path(align); let flags = self.ecx.expr_uint(sp, arg.format.flags); - let prec = trans_count(arg.format.precision); - let width = trans_count(arg.format.width); - let path = self.ecx.path_global(sp, rtpath("FormatSpec")); + let prec = self.trans_count(arg.format.precision); + let width = self.trans_count(arg.format.width); + let path = self.ecx.path_global(sp, self.rtpath("FormatSpec")); let fmt = self.ecx.expr_struct(sp, path, ~[ self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill), self.ecx.field_imm(sp, self.ecx.ident_of("align"), align), @@ -574,19 +585,19 @@ impl<'a> Context<'a> { // Translate the method (if any) let method = match arg.method { - None => { none.clone() } + None => { self.none() } Some(ref m) => { - let m = trans_method(*m); - some(self.ecx.expr_addr_of(sp, m)) + let m = self.trans_method(*m); + self.some(self.ecx.expr_addr_of(sp, m)) } }; - let path = self.ecx.path_global(sp, rtpath("Argument")); + let path = self.ecx.path_global(sp, self.rtpath("Argument")); let s = self.ecx.expr_struct(sp, path, ~[ self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos), self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt), self.ecx.field_imm(sp, self.ecx.ident_of("method"), method), ]); - self.ecx.expr_call_global(sp, rtpath("Argument"), ~[s]) + self.ecx.expr_call_global(sp, self.rtpath("Argument"), ~[s]) } } } From 7ba5bef86e9939accea4c4a12c4c10e5723a773c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:46:33 -0500 Subject: [PATCH 12/33] syntax/fold -- remove conflicting (and rather pointless) closures --- src/libsyntax/fold.rs | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 8fbaea7ac1e..52ff3798f1b 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -75,14 +75,12 @@ pub trait Folder { } fn fold_struct_field(&mut self, sf: &StructField) -> StructField { - let fold_attribute = |x| fold_attribute_(x, self); - Spanned { node: ast::StructField_ { kind: sf.node.kind, id: self.new_id(sf.node.id), ty: self.fold_ty(sf.node.ty), - attrs: sf.node.attrs.map(|e| fold_attribute(*e)) + attrs: sf.node.attrs.map(|e| fold_attribute_(*e, self)) }, span: self.new_span(sf.span) } @@ -225,8 +223,7 @@ pub trait Folder { } } - let fold_attribute = |x| fold_attribute_(x, self); - let attrs = v.node.attrs.map(|x| fold_attribute(*x)); + let attrs = v.node.attrs.map(|x| fold_attribute_(*x, self)); let de = match v.node.disr_expr { Some(e) => Some(self.fold_expr(e)), @@ -323,8 +320,7 @@ fn fold_meta_item_(mi: @MetaItem, fld: &mut T) -> @MetaItem { match mi.node { MetaWord(ref id) => MetaWord((*id).clone()), MetaList(ref id, ref mis) => { - let fold_meta_item = |x| fold_meta_item_(x, fld); - MetaList((*id).clone(), mis.map(|e| fold_meta_item(*e))) + MetaList((*id).clone(), mis.map(|e| fold_meta_item_(*e, fld))) } MetaNameValue(ref id, ref s) => { MetaNameValue((*id).clone(), (*s).clone()) @@ -604,23 +600,18 @@ pub fn noop_fold_mod(m: &Mod, folder: &mut T) -> Mod { } pub fn noop_fold_crate(c: Crate, folder: &mut T) -> Crate { - let fold_meta_item = |x| fold_meta_item_(x, folder); - let fold_attribute = |x| fold_attribute_(x, folder); - Crate { module: folder.fold_mod(&c.module), - attrs: c.attrs.map(|x| fold_attribute(*x)), - config: c.config.map(|x| fold_meta_item(*x)), + attrs: c.attrs.map(|x| fold_attribute_(*x, folder)), + config: c.config.map(|x| fold_meta_item_(*x, folder)), span: folder.new_span(c.span), } } pub fn noop_fold_item(i: &Item, folder: &mut T) -> SmallVector<@Item> { - let fold_attribute = |x| fold_attribute_(x, folder); - SmallVector::one(@Item { ident: folder.fold_ident(i.ident), - attrs: i.attrs.map(|e| fold_attribute(*e)), + attrs: i.attrs.map(|e| fold_attribute_(*e, folder)), id: folder.new_id(i.id), node: folder.fold_item_underscore(&i.node), vis: i.vis, @@ -711,8 +702,6 @@ pub fn noop_fold_pat(p: @Pat, folder: &mut T) -> @Pat { } pub fn noop_fold_expr(e: @Expr, folder: &mut T) -> @Expr { - let fold_field = |x| fold_field_(x, folder); - let node = match e.node { ExprVstore(e, v) => { ExprVstore(folder.fold_expr(e), v) @@ -824,7 +813,7 @@ pub fn noop_fold_expr(e: @Expr, folder: &mut T) -> @Expr { ExprMac(ref mac) => ExprMac(folder.fold_mac(mac)), ExprStruct(ref path, ref fields, maybe_expr) => { ExprStruct(folder.fold_path(path), - fields.map(|x| fold_field(*x)), + fields.map(|x| fold_field_(*x, folder)), maybe_expr.map(|x| folder.fold_expr(x))) }, ExprParen(ex) => ExprParen(folder.fold_expr(ex)) From 8b760fd844fd43e331388a4d5c73304356058295 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:48:31 -0500 Subject: [PATCH 13/33] vec -- introduce local var to make clear what subportion is being borrowed --- src/libstd/vec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index bbb11d774b0..2acafecf957 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -120,7 +120,6 @@ use mem::size_of; use kinds::marker; use uint; use unstable::finally::try_finally; -use unstable::intrinsics; use unstable::raw::{Repr, Slice, Vec}; /** @@ -297,7 +296,8 @@ impl<'a, T> Iterator<&'a [T]> for RevSplits<'a, T> { return Some(self.v); } - match self.v.iter().rposition(|x| (self.pred)(x)) { + let pred = &mut self.pred; + match self.v.iter().rposition(|x| (*pred)(x)) { None => { self.finished = true; Some(self.v) From 95c53c049c9ced13c325c9aade22affbf5d97fa3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:48:56 -0500 Subject: [PATCH 14/33] back/link -- introduce block to clarify scope of closure --- src/librustc/back/link.rs | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index d6abc7b6954..099f376aded 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -361,21 +361,23 @@ pub mod write { let mut llvm_c_strs = ~[]; let mut llvm_args = ~[]; - let add = |arg: &str| { - let s = arg.to_c_str(); - llvm_args.push(s.with_ref(|p| p)); - llvm_c_strs.push(s); - }; - add("rustc"); // fake program name - add("-arm-enable-ehabi"); - add("-arm-enable-ehabi-descriptors"); - if vectorize_loop { add("-vectorize-loops"); } - if vectorize_slp { add("-vectorize-slp"); } - if sess.time_llvm_passes() { add("-time-passes"); } - if sess.print_llvm_passes() { add("-debug-pass=Structure"); } + { + let add = |arg: &str| { + let s = arg.to_c_str(); + llvm_args.push(s.with_ref(|p| p)); + llvm_c_strs.push(s); + }; + add("rustc"); // fake program name + add("-arm-enable-ehabi"); + add("-arm-enable-ehabi-descriptors"); + if vectorize_loop { add("-vectorize-loops"); } + if vectorize_slp { add("-vectorize-slp"); } + if sess.time_llvm_passes() { add("-time-passes"); } + if sess.print_llvm_passes() { add("-debug-pass=Structure"); } - for arg in sess.opts.cg.llvm_args.iter() { - add(*arg); + for arg in sess.opts.cg.llvm_args.iter() { + add(*arg); + } } INIT.doit(|| { @@ -631,7 +633,7 @@ pub fn mangle(sess: Session, ss: ast_map::Path, let mut n = ~"_ZN"; // _Z == Begin name-sequence, N == nested - let push = |s: &str| { + let push = |n: &mut ~str, s: &str| { let sani = sanitize(s); n.push_str(format!("{}{}", sani.len(), sani)); }; @@ -640,7 +642,7 @@ pub fn mangle(sess: Session, ss: ast_map::Path, for s in ss.iter() { match *s { PathName(s) | PathMod(s) | PathPrettyName(s, _) => { - push(sess.str_of(s)) + push(&mut n, sess.str_of(s)) } } } @@ -665,10 +667,10 @@ pub fn mangle(sess: Session, ss: ast_map::Path, } } if hash.len() > 0 { - push(hash); + push(&mut n, hash); } match vers { - Some(s) => push(s), + Some(s) => push(&mut n, s), None => {} } From 42cd820c62c531f20d763281c7cc2bdaa1e0a89a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:51:18 -0500 Subject: [PATCH 15/33] ppaux -- add Repr implementations --- src/librustc/util/ppaux.rs | 46 +++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 5a9c3fd031d..afac501835d 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -565,11 +565,26 @@ impl Repr for Option { fn repr(&self, tcx: ctxt) -> ~str { match self { &None => ~"None", - &Some(ref t) => format!("Some({})", t.repr(tcx)) + &Some(ref t) => t.repr(tcx), } } } +impl Repr for Result { + fn repr(&self, tcx: ctxt) -> ~str { + match self { + &Ok(ref t) => t.repr(tcx), + &Err(ref u) => format!("Err({})", u.repr(tcx)) + } + } +} + +impl Repr for () { + fn repr(&self, _tcx: ctxt) -> ~str { + ~"()" + } +} + impl Repr for @T { fn repr(&self, tcx: ctxt) -> ~str { (&**self).repr(tcx) @@ -1021,3 +1036,32 @@ impl UserString for AbiSet { self.to_str() } } + +impl Repr for ty::UpvarId { + fn repr(&self, tcx: ctxt) -> ~str { + format!("UpvarId({};`{}`;{})", + self.var_id, + ty::local_var_name_str(tcx, self.var_id), + self.closure_expr_id) + } +} + +impl Repr for ast::Mutability { + fn repr(&self, _tcx: ctxt) -> ~str { + format!("{:?}", *self) + } +} + +impl Repr for ty::BorrowKind { + fn repr(&self, _tcx: ctxt) -> ~str { + format!("{:?}", *self) + } +} + +impl Repr for ty::UpvarBorrow { + fn repr(&self, tcx: ctxt) -> ~str { + format!("UpvarBorrow({}, {})", + self.kind.repr(tcx), + self.region.repr(tcx)) + } +} From c9c8049cda14b76223fd16922f1da79aae458a02 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:51:44 -0500 Subject: [PATCH 16/33] io -- introduce local to avoid conflicting borrow --- src/libstd/io/net/udp.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs index ae99101e179..aeec36a932c 100644 --- a/src/libstd/io/net/udp.rs +++ b/src/libstd/io/net/udp.rs @@ -83,7 +83,8 @@ impl Reader for UdpStream { impl Writer for UdpStream { fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.as_socket(|sock| sock.sendto(buf, self.connectedTo)) + let connectedTo = self.connectedTo; + self.as_socket(|sock| sock.sendto(buf, connectedTo)) } } From 8dff89c238a22b18b18c83f6ccafd94f66507f70 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:52:10 -0500 Subject: [PATCH 17/33] librustdoc -- move closure to clarify scope --- src/librustdoc/fold.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 83af47d585e..fee27bd5b31 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -21,7 +21,6 @@ pub trait DocFolder { fn fold_item_recur(&mut self, item: Item) -> Option { let Item { attrs, name, source, visibility, id, inner } = item; let inner = inner; - let c = |x| self.fold_item(x); let inner = match inner { StructItem(mut i) => { let mut foo = ~[]; swap(&mut foo, &mut i.fields); @@ -72,6 +71,7 @@ pub trait DocFolder { StructVariant(mut j) => { let mut foo = ~[]; swap(&mut foo, &mut j.fields); let num_fields = foo.len(); + let c = |x| self.fold_item(x); j.fields.extend(&mut foo.move_iter().filter_map(c)); j.fields_stripped |= num_fields != j.fields.len(); VariantItem(Variant {kind: StructVariant(j), ..i2}) From b0ac40a243b1385ecf0bc25505fa3e65f32013c3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:52:41 -0500 Subject: [PATCH 18/33] sha2 -- introduce locals to clarify which subportions are being borrowed --- src/librustc/util/sha2.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/librustc/util/sha2.rs b/src/librustc/util/sha2.rs index 1b3f5ec947d..116ec6bba29 100644 --- a/src/librustc/util/sha2.rs +++ b/src/librustc/util/sha2.rs @@ -453,7 +453,8 @@ impl Engine256 { assert!(!self.finished) // Assumes that input.len() can be converted to u64 without overflow self.length_bits = add_bytes_to_bits(self.length_bits, input.len() as u64); - self.buffer.input(input, |input: &[u8]| { self.state.process_block(input) }); + let self_state = &mut self.state; + self.buffer.input(input, |input: &[u8]| { self_state.process_block(input) }); } fn finish(&mut self) { @@ -461,10 +462,11 @@ impl Engine256 { return; } - self.buffer.standard_padding(8, |input: &[u8]| { self.state.process_block(input) }); + let self_state = &mut self.state; + self.buffer.standard_padding(8, |input: &[u8]| { self_state.process_block(input) }); write_u32_be(self.buffer.next(4), (self.length_bits >> 32) as u32 ); write_u32_be(self.buffer.next(4), self.length_bits as u32); - self.state.process_block(self.buffer.full_buffer()); + self_state.process_block(self.buffer.full_buffer()); self.finished = true; } From e3ca1c2fcac616d796c648b87bddd5acfb11bdda Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 17:00:45 -0500 Subject: [PATCH 19/33] str -- borrow fields of self for use in closure since self.iter is borrowed --- src/libstd/str.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 3225bb3a678..bc5991c6eeb 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -625,15 +625,17 @@ impl<'a> Iterator for Normalizations<'a> { if !self.sorted { for ch in self.iter { + let buffer = &mut self.buffer; + let sorted = &mut self.sorted; decomposer(ch, |d| { let class = canonical_combining_class(d); - if class == 0 && !self.sorted { - canonical_sort(self.buffer); - self.sorted = true; + if class == 0 && !*sorted { + canonical_sort(*buffer); + *sorted = true; } - self.buffer.push((d, class)); + buffer.push((d, class)); }); - if self.sorted { break } + if *sorted { break } } } From ec6d122826cf00ace0dbbcb82f36dc92b2a1df83 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 17:28:07 -0500 Subject: [PATCH 20/33] libsyntax -- combine two iter ops into one so that `fld` does not need to be mutably shared between them both --- src/libsyntax/ext/expand.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 69611829c7c..d146cd4dae3 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -709,14 +709,15 @@ pub fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P { // expand the elements of a block. pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P { let new_view_items = b.view_items.map(|x| fld.fold_view_item(x)); - let new_stmts = b.stmts.iter() - .map(|x| { + let new_stmts = + b.stmts.iter().flat_map(|x| { + let renamed_stmt = { let pending_renames = &mut fld.extsbox.info().pending_renames; let mut rename_fld = renames_to_fold(pending_renames); rename_fld.fold_stmt(*x).expect_one("rename_fold didn't return one value") - }) - .flat_map(|x| fld.fold_stmt(x).move_iter()) - .collect(); + }; + fld.fold_stmt(renamed_stmt).move_iter() + }).collect(); let new_expr = b.expr.map(|x| { let expr = { let pending_renames = &mut fld.extsbox.info().pending_renames; From 7ffa67ce924352d4ac726bec48e4e554b5bb0f8c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 19:56:27 -0500 Subject: [PATCH 21/33] front -- collapse iterator actions that require access to the same &mut state --- src/librustc/front/config.rs | 50 ++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/librustc/front/config.rs b/src/librustc/front/config.rs index f87c05ddab8..4fd72a4bbfc 100644 --- a/src/librustc/front/config.rs +++ b/src/librustc/front/config.rs @@ -58,8 +58,10 @@ fn filter_view_item<'r>(cx: &Context, view_item: &'r ast::ViewItem) } fn fold_mod(cx: &mut Context, m: &ast::Mod) -> ast::Mod { - let filtered_items = m.items.iter() + let filtered_items: ~[&@ast::Item] = m.items.iter() .filter(|&a| item_in_cfg(cx, *a)) + .collect(); + let flattened_items = filtered_items.move_iter() .flat_map(|&x| cx.fold_item(x).move_iter()) .collect(); let filtered_view_items = m.view_items.iter().filter_map(|a| { @@ -67,7 +69,7 @@ fn fold_mod(cx: &mut Context, m: &ast::Mod) -> ast::Mod { }).collect(); ast::Mod { view_items: filtered_view_items, - items: filtered_items + items: flattened_items } } @@ -113,23 +115,26 @@ fn fold_item_underscore(cx: &mut Context, item: &ast::Item_) -> ast::Item_ { ast::ItemStruct(fold_struct(cx, def), generics.clone()) } ast::ItemEnum(ref def, ref generics) => { - let mut variants = def.variants.iter().map(|c| c.clone()).filter(|m| { - (cx.in_cfg)(m.node.attrs) - }).map(|v| { - match v.node.kind { - ast::TupleVariantKind(..) => v, - ast::StructVariantKind(def) => { - let def = fold_struct(cx, def); - @codemap::Spanned { - node: ast::Variant_ { - kind: ast::StructVariantKind(def), - ..v.node.clone() - }, - ..*v - } + let mut variants = def.variants.iter().map(|c| c.clone()). + filter_map(|v| { + if !(cx.in_cfg)(v.node.attrs) { + None + } else { + Some(match v.node.kind { + ast::TupleVariantKind(..) => v, + ast::StructVariantKind(def) => { + let def = fold_struct(cx, def); + @codemap::Spanned { + node: ast::Variant_ { + kind: ast::StructVariantKind(def), + ..v.node.clone() + }, + ..*v + } + } + }) } - } - }); + }); ast::ItemEnum(ast::EnumDef { variants: variants.collect(), }, generics.clone()) @@ -165,10 +170,11 @@ fn retain_stmt(cx: &Context, stmt: @ast::Stmt) -> bool { } fn fold_block(cx: &mut Context, b: ast::P) -> ast::P { - let resulting_stmts = b.stmts.iter() - .filter(|&a| retain_stmt(cx, *a)) - .flat_map(|&stmt| cx.fold_stmt(stmt).move_iter()) - .collect(); + let resulting_stmts: ~[&@ast::Stmt] = + b.stmts.iter().filter(|&a| retain_stmt(cx, *a)).collect(); + let resulting_stmts = resulting_stmts.move_iter() + .flat_map(|&stmt| cx.fold_stmt(stmt).move_iter()) + .collect(); let filtered_view_items = b.view_items.iter().filter_map(|a| { filter_view_item(cx, a).map(|x| cx.fold_view_item(x)) }).collect(); From c7560387afc9660b4ea6a15eef1f387182c6ae51 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 9 Feb 2014 07:37:03 -0500 Subject: [PATCH 22/33] libgetopts -- fix unsafe sharing in closures --- src/libgetopts/lib.rs | 48 ++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/libgetopts/lib.rs b/src/libgetopts/lib.rs index e5e7c50d2ce..34e09ac1913 100644 --- a/src/libgetopts/lib.rs +++ b/src/libgetopts/lib.rs @@ -775,14 +775,13 @@ fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool) let mut lim = lim; let mut cont = true; - let slice: || = || { cont = it(ss.slice(slice_start, last_end)) }; // if the limit is larger than the string, lower it to save cycles if lim >= fake_i { lim = fake_i; } - let machine: |(uint, char)| -> bool = |(i, c)| { + let machine: |&mut bool, (uint, char)| -> bool = |cont, (i, c)| { let whitespace = if ::std::char::is_whitespace(c) { Ws } else { Cr }; let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim }; @@ -794,24 +793,49 @@ fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool) (B, Cr, OverLim) if (i - last_start + 1) > lim => fail!("word starting with {} longer than limit!", ss.slice(last_start, i + 1)), - (B, Cr, OverLim) => { slice(); slice_start = last_start; B } - (B, Ws, UnderLim) => { last_end = i; C } - (B, Ws, OverLim) => { last_end = i; slice(); A } + (B, Cr, OverLim) => { + *cont = it(ss.slice(slice_start, last_end)); + slice_start = last_start; + B + } + (B, Ws, UnderLim) => { + last_end = i; + C + } + (B, Ws, OverLim) => { + last_end = i; + *cont = it(ss.slice(slice_start, last_end)); + A + } - (C, Cr, UnderLim) => { last_start = i; B } - (C, Cr, OverLim) => { slice(); slice_start = i; last_start = i; last_end = i; B } - (C, Ws, OverLim) => { slice(); A } - (C, Ws, UnderLim) => { C } + (C, Cr, UnderLim) => { + last_start = i; + B + } + (C, Cr, OverLim) => { + *cont = it(ss.slice(slice_start, last_end)); + slice_start = i; + last_start = i; + last_end = i; + B + } + (C, Ws, OverLim) => { + *cont = it(ss.slice(slice_start, last_end)); + A + } + (C, Ws, UnderLim) => { + C + } }; - cont + *cont }; - ss.char_indices().advance(|x| machine(x)); + ss.char_indices().advance(|x| machine(&mut cont, x)); // Let the automaton 'run out' by supplying trailing whitespace while cont && match state { B | C => true, A => false } { - machine((fake_i, ' ')); + machine(&mut cont, (fake_i, ' ')); fake_i += 1; } return cont; From 56c5d4cec385ce8196ecbee0e67cf4a928c06170 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 9 Feb 2014 07:37:33 -0500 Subject: [PATCH 23/33] libsyntax -- fix unsafe sharing in closures --- src/libsyntax/ext/deriving/generic.rs | 10 ++++++---- src/libsyntax/ext/deriving/rand.rs | 11 ++++++----- src/libsyntax/ext/format.rs | 1 - 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index 47be3067284..9d290c93c64 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -580,10 +580,12 @@ impl<'a> MethodDef<'a> { ast::SelfStatic => None, _ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable)) }; - let args = arg_types.move_iter().map(|(name, ty)| { - cx.arg(trait_.span, name, ty) - }); - let args = self_arg.move_iter().chain(args).collect(); + let args = { + let args = arg_types.move_iter().map(|(name, ty)| { + cx.arg(trait_.span, name, ty) + }); + self_arg.move_iter().chain(args).collect() + }; let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident); diff --git a/src/libsyntax/ext/deriving/rand.rs b/src/libsyntax/ext/deriving/rand.rs index a40317286c9..ef7bd7c2bcd 100644 --- a/src/libsyntax/ext/deriving/rand.rs +++ b/src/libsyntax/ext/deriving/rand.rs @@ -60,7 +60,7 @@ fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) cx.ident_of("Rand"), cx.ident_of("rand") ]; - let rand_call = |span| { + let rand_call = |cx: &mut ExtCtxt, span| { cx.expr_call_global(span, rand_ident.clone(), ~[ rng[0] ]) @@ -111,7 +111,7 @@ fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) let i_expr = cx.expr_uint(v_span, i); let pat = cx.pat_lit(v_span, i_expr); - let thing = rand_thing(cx, v_span, ident, summary, |sp| rand_call(sp)); + let thing = rand_thing(cx, v_span, ident, summary, |cx, sp| rand_call(cx, sp)); cx.arm(v_span, ~[ pat ], thing) }).collect::<~[ast::Arm]>(); @@ -130,20 +130,21 @@ fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) trait_span: Span, ctor_ident: Ident, summary: &StaticFields, - rand_call: |Span| -> @Expr) + rand_call: |&mut ExtCtxt, Span| -> @Expr) -> @Expr { match *summary { Unnamed(ref fields) => { if fields.is_empty() { cx.expr_ident(trait_span, ctor_ident) } else { - let exprs = fields.map(|span| rand_call(*span)); + let exprs = fields.map(|span| rand_call(cx, *span)); cx.expr_call_ident(trait_span, ctor_ident, exprs) } } Named(ref fields) => { let rand_fields = fields.map(|&(ident, span)| { - cx.field_imm(span, ident, rand_call(span)) + let e = rand_call(cx, span); + cx.field_imm(span, ident, e) }); cx.expr_struct_ident(trait_span, ctor_ident, rand_fields) } diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index 35d2adbead5..3eacce5eb1d 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -21,7 +21,6 @@ use rsparse = parse; use std::fmt::parse; use std::hashmap::{HashMap, HashSet}; use std::vec; -use std::cell::RefCell; #[deriving(Eq)] enum ArgumentType { From 844eab194063ad6500abe01e7f256140bdba28a3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 9 Feb 2014 07:37:44 -0500 Subject: [PATCH 24/33] librustuv -- fix unsafe sharing in rustuv --- src/librustuv/net.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustuv/net.rs b/src/librustuv/net.rs index 87fadbba176..551e2c9faf7 100644 --- a/src/librustuv/net.rs +++ b/src/librustuv/net.rs @@ -510,8 +510,9 @@ impl rtio::RtioUdpSocket for UdpWatcher { buf: Some(slice_to_uv_buf(buf)), result: None, }; + let handle = self.handle; wait_until_woken_after(&mut cx.task, || { - unsafe { uvll::set_data_for_uv_handle(self.handle, &cx) } + unsafe { uvll::set_data_for_uv_handle(handle, &cx) } }); match cx.result.take_unwrap() { (n, _) if n < 0 => From 807def022a6540dde6727b9a438ea3b0eaafd9b5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 10:09:46 -0500 Subject: [PATCH 25/33] region -- Improve comments in region.rs --- src/librustc/middle/region.rs | 54 ++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 232b35bb82a..fcda7cd79e4 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -36,19 +36,42 @@ use syntax::ast_util::{stmt_id}; /** The region maps encode information about region relationships. -- `scope_map` maps from: - - an expression to the expression or block encoding the maximum - (static) lifetime of a value produced by that expression. This is - generally the innermost call, statement, match, or block. - - a variable or binding id to the block in which that variable is declared. -- `free_region_map` maps from: - - a free region `a` to a list of free regions `bs` such that - `a <= b for all b in bs` +- `scope_map` maps from a scope id to the enclosing scope id; this is + usually corresponding to the lexical nesting, though in the case of + closures the parent scope is the innermost conditinal expression or repeating + block + +- `var_map` maps from a variable or binding id to the block in which + that variable is declared. + +- `free_region_map` maps from a free region `a` to a list of free + regions `bs` such that `a <= b for all b in bs` - the free region map is populated during type check as we check each function. See the function `relate_free_regions` for more information. -- `temporary_scopes` includes scopes where cleanups for temporaries occur. - These are statements and loop/fn bodies. + +- `rvalue_scopes` includes entries for those expressions whose cleanup + scope is larger than the default. The map goes from the expression + id to the cleanup scope id. For rvalues not present in this table, + the appropriate cleanup scope is the innermost enclosing statement, + conditional expression, or repeating block (see `terminating_scopes`). + +- `terminating_scopes` is a set containing the ids of each statement, + or conditional/repeating expression. These scopes are calling "terminating + scopes" because, when attempting to find the scope of a temporary, by + default we search up the enclosing scopes until we encounter the + terminating scope. A conditional/repeating + expression is one which is not guaranteed to execute exactly once + upon entering the parent scope. This could be because the expression + only executes conditionally, such as the expression `b` in `a && b`, + or because the expression may execute many times, such as a loop + body. The reason that we distinguish such expressions is that, upon + exiting the parent scope, we cannot statically know how many times + the expression executed, and thus if the expression creates + temporaries we cannot know statically how many such temporaries we + would have to cleanup. Therefore we ensure that the temporaries never + outlast the conditional/repeating expression, preventing the need + for dynamic checks and/or arbitrary amounts of stack space. */ pub struct RegionMaps { priv scope_map: RefCell>, @@ -840,7 +863,16 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor, visit::FkItemFn(..) | visit::FkMethod(..) => { Context {parent: None, var_parent: None, ..cx} } - visit::FkFnBlock(..) => cx + visit::FkFnBlock(..) => { + // FIXME(#3696) -- at present we are place the closure body + // within the region hierarchy exactly where it appears lexically. + // This is wrong because the closure may live longer + // than the enclosing expression. We should probably fix this, + // but the correct fix is a bit subtle, and I am also not sure + // that the present approach is unsound -- it may not permit + // any illegal programs. See issue for more details. + cx + } }; visitor.visit_block(body, body_cx); } From b1962a2b2e52f16ad817339e798f90fac973c2b1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:37:19 -0500 Subject: [PATCH 26/33] add upvar_borrow_map to tcx and fcx in typeck --- src/librustc/middle/ty.rs | 144 +++++++++++++++++- src/librustc/middle/typeck/check/mod.rs | 2 + src/librustc/middle/typeck/check/writeback.rs | 36 +++++ 3 files changed, 181 insertions(+), 1 deletion(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 5b58ec57c63..7c4cb396b3e 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -351,6 +351,9 @@ pub struct ctxt_ { // is used for lazy resolution of traits. populated_external_traits: RefCell>, + // Borrows + upvar_borrow_map: RefCell, + // These two caches are used by const_eval when decoding external statics // and variants that are found. extern_const_statics: RefCell>>, @@ -494,6 +497,120 @@ pub enum Region { ReEmpty, } +/** + * Upvars do not get their own node-id. Instead, we use the pair of + * the original var id (that is, the root variable that is referenced + * by the upvar) and the id of the closure expression. + */ +#[deriving(Clone, Eq, IterBytes)] +pub struct UpvarId { + var_id: ast::NodeId, + closure_expr_id: ast::NodeId, +} + +#[deriving(Clone, Eq, IterBytes)] +pub enum BorrowKind { + /// Data must be immutable and is aliasable. + ImmBorrow, + + /// Data must be immutable but not aliasable. This kind of borrow + /// cannot currently be expressed by the user and is used only in + /// implicit closure bindings. It is needed when you the closure + /// is borrowing or mutating a mutable referent, e.g.: + /// + /// let x: &mut int = ...; + /// let y = || *x += 5; + /// + /// If we were to try to translate this closure into a more explicit + /// form, we'd encounter an error with the code as written: + /// + /// struct Env { x: & &mut int } + /// let x: &mut int = ...; + /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// This is then illegal because you cannot mutate a `&mut` found + /// in an aliasable location. To solve, you'd have to translate with + /// an `&mut` borrow: + /// + /// struct Env { x: & &mut int } + /// let x: &mut int = ...; + /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// Now the assignment to `**env.x` is legal, but creating a + /// mutable pointer to `x` is not because `x` is not mutable. We + /// could fix this by declaring `x` as `let mut x`. This is ok in + /// user code, if awkward, but extra weird for closures, since the + /// borrow is hidden. + /// + /// So we introduce a "unique imm" borrow -- the referent is + /// immutable, but not aliasable. This solves the problem. For + /// simplicity, we don't give users the way to express this + /// borrow, it's just used when translating closures. + UniqueImmBorrow, + + /// Data is mutable and not aliasable. + MutBorrow +} + +/** + * Information describing the borrowing of an upvar. This is computed + * during `typeck`, specifically by `regionck`. The general idea is + * that the compiler analyses treat closures like: + * + * let closure: &'e fn() = || { + * x = 1; // upvar x is assigned to + * use(y); // upvar y is read + * foo(&z); // upvar z is borrowed immutably + * }; + * + * as if they were "desugared" to something loosely like: + * + * struct Vars<'x,'y,'z> { x: &'x mut int, + * y: &'y const int, + * z: &'z int } + * let closure: &'e fn() = { + * fn f(env: &Vars) { + * *env.x = 1; + * use(*env.y); + * foo(env.z); + * } + * let env: &'e mut Vars<'x,'y,'z> = &mut Vars { x: &'x mut x, + * y: &'y const y, + * z: &'z z }; + * (env, f) + * }; + * + * This is basically what happens at runtime. The closure is basically + * an existentially quantified version of the `(env, f)` pair. + * + * This data structure indicates the region and mutability of a single + * one of the `x...z` borrows. + * + * It may not be obvious why each borrowed variable gets its own + * lifetime (in the desugared version of the example, these are indicated + * by the lifetime parameters `'x`, `'y`, and `'z` in the `Vars` definition). + * Each such lifetime must encompass the lifetime `'e` of the closure itself, + * but need not be identical to it. The reason that this makes sense: + * + * - Callers are only permitted to invoke the closure, and hence to + * use the pointers, within the lifetime `'e`, so clearly `'e` must + * be a sublifetime of `'x...'z`. + * - The closure creator knows which upvars were borrowed by the closure + * and thus `x...z` will be reserved for `'x...'z` respectively. + * - Through mutation, the borrowed upvars can actually escape the + * the closure, so sometimes it is necessary for them to be larger + * than the closure lifetime itself. + */ +#[deriving(Eq, Clone)] +pub struct UpvarBorrow { + kind: BorrowKind, + region: ty::Region, +} + +pub type UpvarBorrowMap = HashMap; + impl Region { pub fn is_bound(&self) -> bool { match self { @@ -999,7 +1116,7 @@ pub fn mk_ctxt(s: session::Session, impl_vtables: RefCell::new(HashMap::new()), populated_external_types: RefCell::new(HashSet::new()), populated_external_traits: RefCell::new(HashSet::new()), - + upvar_borrow_map: RefCell::new(HashMap::new()), extern_const_statics: RefCell::new(HashMap::new()), extern_const_variants: RefCell::new(HashMap::new()), } @@ -5100,3 +5217,28 @@ impl substs { } } } + +impl BorrowKind { + pub fn from_mutbl(m: ast::Mutability) -> BorrowKind { + match m { + ast::MutMutable => MutBorrow, + ast::MutImmutable => ImmBorrow, + } + } + + pub fn to_user_str(&self) -> &'static str { + match *self { + MutBorrow => "mutable", + ImmBorrow => "immutable", + UniqueImmBorrow => "uniquely immutable", + } + } + + pub fn to_short_str(&self) -> &'static str { + match *self { + MutBorrow => "mut", + ImmBorrow => "imm", + UniqueImmBorrow => "own", + } + } +} diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index e2e7a58f523..9eec804dd2e 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -164,6 +164,7 @@ pub struct Inherited { adjustments: RefCell>, method_map: method_map, vtable_map: vtable_map, + upvar_borrow_map: RefCell, } #[deriving(Clone)] @@ -266,6 +267,7 @@ impl Inherited { adjustments: RefCell::new(HashMap::new()), method_map: @RefCell::new(HashMap::new()), vtable_map: @RefCell::new(HashMap::new()), + upvar_borrow_map: RefCell::new(HashMap::new()), } } } diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index 48b1acd3f9b..84801355990 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -378,10 +378,45 @@ impl Visitor<()> for WbCtxt { fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {} } +fn resolve_upvar_borrow_map(wbcx: &mut WbCtxt) { + if !wbcx.success { + return; + } + + let fcx = wbcx.fcx; + let tcx = fcx.tcx(); + let upvar_borrow_map = fcx.inh.upvar_borrow_map.borrow(); + for (upvar_id, upvar_borrow) in upvar_borrow_map.get().iter() { + let r = upvar_borrow.region; + match resolve_region(fcx.infcx(), r, resolve_all | force_all) { + Ok(r) => { + let new_upvar_borrow = ty::UpvarBorrow { + kind: upvar_borrow.kind, + region: r + }; + debug!("Upvar borrow for {} resolved to {}", + upvar_id.repr(tcx), new_upvar_borrow.repr(tcx)); + let mut tcx_upvar_borrow_map = tcx.upvar_borrow_map.borrow_mut(); + tcx_upvar_borrow_map.get().insert(*upvar_id, new_upvar_borrow); + } + Err(e) => { + let span = ty::expr_span(tcx, upvar_id.closure_expr_id); + fcx.ccx.tcx.sess.span_err( + span, format!("cannot resolve lifetime for \ + captured variable `{}`: {}", + ty::local_var_name_str(tcx, upvar_id.var_id).get().to_str(), + infer::fixup_err_to_str(e))); + wbcx.success = false; + } + }; + } +} + pub fn resolve_type_vars_in_expr(fcx: @FnCtxt, e: &ast::Expr) -> bool { let mut wbcx = WbCtxt { fcx: fcx, success: true }; let wbcx = &mut wbcx; wbcx.visit_expr(e, ()); + resolve_upvar_borrow_map(wbcx); return wbcx.success; } @@ -397,5 +432,6 @@ pub fn resolve_type_vars_in_fn(fcx: @FnCtxt, decl: &ast::FnDecl, resolve_type_vars_for_node(wbcx, arg.pat.span, arg.pat.id); } } + resolve_upvar_borrow_map(wbcx); return wbcx.success; } From d45dd2754e58d12ee5a9cabc4bb19ac8db18cecc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:41:29 -0500 Subject: [PATCH 27/33] regionck -- rewrite in terms of mem_categorization, compute upvar borrow kinds --- src/librustc/middle/typeck/check/regionck.rs | 1332 ++++++++++-------- 1 file changed, 717 insertions(+), 615 deletions(-) diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index ac15d52ff13..7647de9a3ad 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -12,23 +12,115 @@ The region check is a final pass that runs over the AST after we have inferred the type constraints but before we have actually finalized -the types. Its purpose is to embed some final region constraints. -The reason that this is not done earlier is that sometimes we don't -know whether a given type will be a region pointer or not until this -phase. +the types. Its purpose is to embed a variety of region constraints. +Inserting these constraints as a separate pass is good because (1) it +localizes the code that has to do with region inference and (2) often +we cannot know what constraints are needed until the basic types have +been inferred. -In particular, we ensure that, if the type of an expression or -variable is `&'r T`, then the expression or variable must occur within -the region scope `r`. Note that in some cases `r` may still be a -region variable, so this gives us a chance to influence the value for -`r` that we infer to ensure we choose a value large enough to enclose -all uses. There is a lengthy comment in visit_node() that explains -this point a bit better. +### Interaction with the borrow checker + +In general, the job of the borrowck module (which runs later) is to +check that all soundness criteria are met, given a particular set of +regions. The job of *this* module is to anticipate the needs of the +borrow checker and infer regions that will satisfy its requirements. +It is generally true that the inference doesn't need to be sound, +meaning that if there is a bug and we inferred bad regions, the borrow +checker should catch it. This is not entirely true though; for +example, the borrow checker doesn't check subtyping, and it doesn't +check that region pointers are always live when they are used. It +might be worthwhile to fix this so that borrowck serves as a kind of +verification step -- that would add confidence in the overall +correctness of the compiler, at the cost of duplicating some type +checks and effort. + +### Inferring the duration of borrows, automatic and otherwise + +Whenever we introduce a borrowed pointer, for example as the result of +a borrow expression `let x = &data`, the lifetime of the pointer `x` +is always specified as a region inferencr variable. `regionck` has the +job of adding constraints such that this inference variable is as +narrow as possible while still accommodating all uses (that is, every +dereference of the resulting pointer must be within the lifetime). + +#### Reborrows + +Generally speaking, `regionck` does NOT try to ensure that the data +`data` will outlive the pointer `x`. That is the job of borrowck. The +one exception is when "re-borrowing" the contents of another borrowed +pointer. For example, imagine you have a borrowed pointer `b` with +lifetime L1 and you have an expression `&*b`. The result of this +expression will be another borrowed pointer with lifetime L2 (which is +an inference variable). The borrow checker is going to enforce the +constraint that L2 < L1, because otherwise you are re-borrowing data +for a lifetime larger than the original loan. However, without the +routines in this module, the region inferencer would not know of this +dependency and thus it might infer the lifetime of L2 to be greater +than L1 (issue #3148). + +There are a number of troublesome scenarios in the tests +`region-dependent-*.rs`, but here is one example: + + struct Foo { i: int } + struct Bar { foo: Foo } + fn get_i(x: &'a Bar) -> &'a int { + let foo = &x.foo; // Lifetime L1 + &foo.i // Lifetime L2 + } + +Note that this comes up either with `&` expressions, `ref` +bindings, and `autorefs`, which are the three ways to introduce +a borrow. + +The key point here is that when you are borrowing a value that +is "guaranteed" by a borrowed pointer, you must link the +lifetime of that borrowed pointer (L1, here) to the lifetime of +the borrow itself (L2). What do I mean by "guaranteed" by a +borrowed pointer? I mean any data that is reached by first +dereferencing a borrowed pointer and then either traversing +interior offsets or owned pointers. We say that the guarantor +of such data it the region of the borrowed pointer that was +traversed. This is essentially the same as the ownership +relation, except that a borrowed pointer never owns its +contents. + +### Inferring borrow kinds for upvars + +Whenever there is a closure expression, we need to determine how each +upvar is used. We do this by initially assigning each upvar an +immutable "borrow kind" (see `ty::BorrowKind` for details) and then +"escalating" the kind as needed. The borrow kind proceeds according to +the following lattice: + + ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow + +So, for example, if we see an assignment `x = 5` to an upvar `x`, we +will promote it's borrow kind to mutable borrow. If we see an `&mut x` +we'll do the same. Naturally, this applies not just to the upvar, but +to everything owned by `x`, so the result is the same for something +like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a +struct). These adjustments are performed in +`adjust_upvar_borrow_kind()` (you can trace backwards through the code +from there). + +The fact that we are inferring borrow kinds as we go results in a +semi-hacky interaction with mem-categorization. In particular, +mem-categorization will query the current borrow kind as it +categorizes, and we'll return the *current* value, but this may get +adjusted later. Therefore, in this module, we genreally ignore the +borrow kind (and derived mutabilities) that are returned from +mem-categorization, since they may be inaccurate. (Another option +would be to use a unification scheme, where instead of returning a +concrete borrow kind like `ty::ImmBorrow`, we return a +`ty::InferBorrow(upvar_id)` or something like that, but this would +then mean that all later passes would have to check for these figments +and report an error, and it just seems like more mess in the end.) */ -use middle::freevars::get_freevars; +use middle::freevars; +use mc = middle::mem_categorization; use middle::ty::{ReScope}; use middle::ty; use middle::typeck::astconv::AstConv; @@ -43,6 +135,7 @@ use util::ppaux::{ty_to_str, region_to_str, Repr}; use syntax::ast::{ManagedSigil, OwnedSigil, BorrowedSigil}; use syntax::ast::{DefArg, DefBinding, DefLocal, DefUpvar}; use syntax::ast; +use syntax::ast_util; use syntax::codemap::Span; use syntax::visit; use syntax::visit::Visitor; @@ -149,6 +242,36 @@ impl Rcx { } } +impl<'a> mc::Typer for &'a mut Rcx { + fn tcx(&self) -> ty::ctxt { + self.fcx.tcx() + } + + fn node_ty(&mut self, id: ast::NodeId) -> mc::McResult { + let t = self.resolve_node_type(id); + if ty::type_is_error(t) {Err(())} else {Ok(t)} + } + + fn adjustment(&mut self, id: ast::NodeId) -> Option<@ty::AutoAdjustment> { + let adjustments = self.fcx.inh.adjustments.borrow(); + adjustments.get().find_copy(&id) + } + + fn is_method_call(&mut self, id: ast::NodeId) -> bool { + let method_map = self.fcx.inh.method_map.borrow(); + method_map.get().contains_key(&id) + } + + fn temporary_scope(&mut self, id: ast::NodeId) -> Option { + self.tcx().region_maps.temporary_scope(id) + } + + fn upvar_borrow(&mut self, id: ty::UpvarId) -> ty::UpvarBorrow { + let upvar_borrow_map = self.fcx.inh.upvar_borrow_map.borrow(); + upvar_borrow_map.get().get_copy(&id) + } +} + pub fn regionck_expr(fcx: @FnCtxt, e: &ast::Expr) { let mut rcx = Rcx { fcx: fcx, errors_reported: 0, repeating_scope: e.id }; @@ -213,7 +336,7 @@ fn visit_arm(rcx: &mut Rcx, arm: &ast::Arm) { fn visit_local(rcx: &mut Rcx, l: &ast::Local) { // see above constrain_bindings_in_pat(l.pat, rcx); - guarantor::for_local(rcx, l); + link_local(rcx, l); visit::walk_local(rcx, l, ()); } @@ -273,7 +396,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { let expr_ty = rcx.resolve_node_type(expr.id); constrain_derefs(rcx, expr, autoderefs, expr_ty); for autoref in opt_autoref.iter() { - guarantor::for_autoref(rcx, expr, autoderefs, autoref); + link_autoref(rcx, expr, autoderefs, autoref); // Require that the resulting region encompasses // the current node. @@ -323,8 +446,22 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { visit::walk_expr(rcx, expr, ()); } + ast::ExprAssign(lhs, _) => { + adjust_borrow_kind_for_assignment_lhs(rcx, lhs); + visit::walk_expr(rcx, expr, ()); + } + + ast::ExprAssignOp(callee_id, _, lhs, rhs) => { + if has_method_map { + constrain_call(rcx, callee_id, expr, Some(lhs), [rhs], true); + } + + adjust_borrow_kind_for_assignment_lhs(rcx, lhs); + + visit::walk_expr(rcx, expr, ()); + } + ast::ExprIndex(callee_id, lhs, rhs) | - ast::ExprAssignOp(callee_id, _, lhs, rhs) | ast::ExprBinary(callee_id, _, lhs, rhs) if has_method_map => { // As `expr_method_call`, but the call is via an // overloaded op. Note that we (sadly) currently use an @@ -388,8 +525,8 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { visit::walk_expr(rcx, expr, ()); } - ast::ExprAddrOf(_, base) => { - guarantor::for_addr_of(rcx, expr, base); + ast::ExprAddrOf(m, base) => { + link_addr_of(rcx, expr, m, base); // Require that when you write a `&expr` expression, the // resulting pointer has a lifetime that encompasses the @@ -405,13 +542,13 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { } ast::ExprMatch(discr, ref arms) => { - guarantor::for_match(rcx, discr, *arms); + link_match(rcx, discr, *arms); visit::walk_expr(rcx, expr, ()); } - ast::ExprFnBlock(..) | ast::ExprProc(..) => { - check_expr_fn_block(rcx, expr); + ast::ExprFnBlock(_, ref body) | ast::ExprProc(_, ref body) => { + check_expr_fn_block(rcx, expr, &**body); } ast::ExprLoop(body, _) => { @@ -437,43 +574,136 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { } fn check_expr_fn_block(rcx: &mut Rcx, - expr: &ast::Expr) { + expr: &ast::Expr, + body: &ast::Block) { let tcx = rcx.fcx.tcx(); - match expr.node { - ast::ExprFnBlock(_, ref body) | ast::ExprProc(_, ref body) => { - let function_type = rcx.resolve_node_type(expr.id); - match ty::get(function_type).sty { - ty::ty_closure( - ty::ClosureTy { - sigil: ast::BorrowedSigil, region: region, ..}) => { - if get_freevars(tcx, expr.id).is_empty() { - // No free variables means that the environment - // will be NULL at runtime and hence the closure - // has static lifetime. - } else { - // Otherwise, the closure must not outlive the - // variables it closes over, nor can it - // outlive the innermost repeating scope - // (since otherwise that would require - // infinite stack). - constrain_free_variables(rcx, region, expr); - let repeating_scope = ty::ReScope(rcx.repeating_scope); - rcx.fcx.mk_subr(true, infer::InfStackClosure(expr.span), - region, repeating_scope); - } - } - _ => () + let function_type = rcx.resolve_node_type(expr.id); + match ty::get(function_type).sty { + ty::ty_closure(ty::ClosureTy { + sigil: ast::BorrowedSigil, region: region, ..}) => { + let freevars = freevars::get_freevars(tcx, expr.id); + if freevars.is_empty() { + // No free variables means that the environment + // will be NULL at runtime and hence the closure + // has static lifetime. + } else { + // Closure must not outlive the variables it closes over. + constrain_free_variables(rcx, region, expr, freevars); + + // Closure cannot outlive the appropriate temporary scope. + let s = rcx.repeating_scope; + rcx.fcx.mk_subr(true, infer::InfStackClosure(expr.span), + region, ty::ReScope(s)); } - - let repeating_scope = rcx.set_repeating_scope(body.id); - visit::walk_expr(rcx, expr, ()); - rcx.set_repeating_scope(repeating_scope); } + _ => () + } - _ => { - tcx.sess.span_bug( - expr.span, - "expected expr_fn_block"); + let repeating_scope = rcx.set_repeating_scope(body.id); + visit::walk_expr(rcx, expr, ()); + rcx.set_repeating_scope(repeating_scope); + + match ty::get(function_type).sty { + ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil, ..}) => { + let freevars = freevars::get_freevars(tcx, expr.id); + propagate_upupvar_borrow_kind(rcx, expr, freevars); + } + _ => () + } + + fn constrain_free_variables(rcx: &mut Rcx, + region: ty::Region, + expr: &ast::Expr, + freevars: freevars::freevar_info) { + /*! + * Make sure that all free variables referenced inside the closure + * outlive the closure itself. Also, create an entry in the + * upvar_borrows map with a region. + */ + + let tcx = rcx.fcx.ccx.tcx; + let infcx = rcx.fcx.infcx(); + debug!("constrain_free_variables({}, {})", + region.repr(tcx), expr.repr(tcx)); + for freevar in freevars.iter() { + debug!("freevar def is {:?}", freevar.def); + + // Identify the variable being closed over and its node-id. + let def = freevar.def; + let def_id = ast_util::def_id_of_def(def); + assert!(def_id.crate == ast::LOCAL_CRATE); + let upvar_id = ty::UpvarId { var_id: def_id.node, + closure_expr_id: expr.id }; + + // Create a region variable to represent this borrow. This borrow + // must outlive the region on the closure. + let origin = infer::UpvarRegion(upvar_id, expr.span); + let freevar_region = infcx.next_region_var(origin); + rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span, def_id.node), + region, freevar_region); + + // Create a UpvarBorrow entry. Note that we begin with a + // const borrow_kind, but change it to either mut or + // immutable as dictated by the uses. + let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, + region: freevar_region }; + let mut upvar_borrow_map = rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + upvar_borrow_map.get().insert(upvar_id, upvar_borrow); + + // Guarantee that the closure does not outlive the variable itself. + let en_region = region_of_def(rcx.fcx, def); + debug!("en_region = {}", en_region.repr(tcx)); + rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span, def_id.node), + region, en_region); + } + } + + fn propagate_upupvar_borrow_kind(rcx: &mut Rcx, + expr: &ast::Expr, + freevars: freevars::freevar_info) { + let tcx = rcx.fcx.ccx.tcx; + debug!("propagate_upupvar_borrow_kind({})", expr.repr(tcx)); + for freevar in freevars.iter() { + // Because of the semi-hokey way that we are doing + // borrow_kind inference, we need to check for + // indirect dependencies, like so: + // + // let mut x = 0; + // outer_call(|| { + // inner_call(|| { + // x = 1; + // }); + // }); + // + // Here, the `inner_call` is basically "reborrowing" the + // outer pointer. With no other changes, `inner_call` + // would infer that it requires a mutable borrow, but + // `outer_call` would infer that a const borrow is + // sufficient. This is because we haven't linked the + // borrow_kind of the borrow that occurs in the inner + // closure to the borrow_kind of the borrow in the outer + // closure. Note that regions *are* naturally linked + // because we have a proper inference scheme there. + // + // Anyway, for borrow_kind, we basically go back over now + // after checking the inner closure (and hence + // determining the final borrow_kind) and propagate that as + // a constraint on the outer closure. + match freevar.def { + ast::DefUpvar(var_id, _, outer_closure_id, _) => { + // thing being captured is itself an upvar: + let outer_upvar_id = ty::UpvarId { + var_id: var_id, + closure_expr_id: outer_closure_id }; + let inner_upvar_id = ty::UpvarId { + var_id: var_id, + closure_expr_id: expr.id }; + link_upvar_borrow_kind(rcx, + inner_upvar_id, + outer_upvar_id); + } + _ => {} + } } } } @@ -554,7 +784,7 @@ fn constrain_call(rcx: &mut Rcx, // result. modes are going away and the "DerefArgs" code // should be ported to use adjustments if implicitly_ref_args { - guarantor::for_by_ref(rcx, arg_expr, callee_scope); + link_by_ref(rcx, arg_expr, callee_scope); } } @@ -564,7 +794,7 @@ fn constrain_call(rcx: &mut Rcx, constrain_regions_in_type_of_node( rcx, r.id, callee_region, infer::CallRcvr(r.span)); if implicitly_ref_args { - guarantor::for_by_ref(rcx, r, callee_scope); + link_by_ref(rcx, r, callee_scope); } } @@ -644,27 +874,6 @@ fn constrain_index(rcx: &mut Rcx, } } -fn constrain_free_variables(rcx: &mut Rcx, - region: ty::Region, - expr: &ast::Expr) { - /*! - * Make sure that all free variables referenced inside the closure - * outlive the closure itself. - */ - - let tcx = rcx.fcx.ccx.tcx; - debug!("constrain_free_variables({}, {})", - region.repr(tcx), expr.repr(tcx)); - for freevar in get_freevars(tcx, expr.id).iter() { - debug!("freevar def is {:?}", freevar.def); - let def = freevar.def; - let def_region = region_of_def(rcx.fcx, def); - debug!("def_region = {}", def_region.repr(tcx)); - rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span), - region, def_region); - } -} - fn constrain_regions_in_type_of_node( rcx: &mut Rcx, id: ast::NodeId, @@ -744,576 +953,469 @@ fn constrain_regions_in_type( return e == rcx.errors_reported; } -pub mod guarantor { +// 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) { /*! - * The routines in this module are aiming to deal with the case - * where a the contents of a reference are re-borrowed. - * Imagine you have a reference `b` with lifetime L1 and - * you have an expression `&*b`. The result of this borrow will - * be another reference with lifetime L2 (which is an - * inference variable). The borrow checker is going to enforce - * the constraint that L2 < L1, because otherwise you are - * re-borrowing data for a lifetime larger than the original loan. - * However, without the routines in this module, the region - * inferencer would not know of this dependency and thus it might - * infer the lifetime of L2 to be greater than L1 (issue #3148). - * - * There are a number of troublesome scenarios in the tests - * `region-dependent-*.rs`, but here is one example: - * - * struct Foo { i: int } - * struct Bar { foo: Foo } - * fn get_i(x: &'a Bar) -> &'a int { - * let foo = &x.foo; // Lifetime L1 - * &foo.i // Lifetime L2 - * } - * - * Note that this comes up either with `&` expressions, `ref` - * bindings, and `autorefs`, which are the three ways to introduce - * a borrow. - * - * The key point here is that when you are borrowing a value that - * is "guaranteed" by a reference, you must link the - * lifetime of that reference (L1, here) to the lifetime of - * the borrow itself (L2). What do I mean by "guaranteed" by a - * reference? I mean any data that is reached by first - * dereferencing a reference and then either traversing - * interior offsets or owned pointers. We say that the guarantor - * of such data it the region of the reference that was - * traversed. This is essentially the same as the ownership - * relation, except that a reference never owns its - * contents. - * - * NB: I really wanted to use the `mem_categorization` code here - * but I cannot because final type resolution hasn't happened yet, - * and `mem_categorization` requires that all types be known. - * So this is very similar logic to what you would find there, - * but more special purpose. + * Computes the guarantor for an expression `&base` and then + * ensures that the lifetime of the resulting pointer is linked + * to the lifetime of its guarantor (if any). */ - use middle::typeck::astconv::AstConv; - use middle::typeck::check::regionck::Rcx; - use middle::typeck::check::regionck::mk_subregion_due_to_derefence; - use middle::typeck::infer; - use middle::ty; - use syntax::ast; - use syntax::codemap::Span; - use util::ppaux::{ty_to_str, Repr}; + debug!("link_addr_of(base=?)"); - pub fn for_addr_of(rcx: &mut Rcx, expr: &ast::Expr, base: &ast::Expr) { - /*! - * Computes the guarantor for an expression `&base` and then - * ensures that the lifetime of the resulting pointer is linked - * to the lifetime of its guarantor (if any). - */ + let cmt = { + let mut mc = mc::MemCategorizationContext { typer: &mut *rcx }; + ignore_err!(mc.cat_expr(base)) + }; + link_region_from_node_type(rcx, expr.span, expr.id, mutability, cmt); +} - debug!("guarantor::for_addr_of(base=?)"); +fn link_local(rcx: &mut Rcx, local: &ast::Local) { + /*! + * Computes the guarantors for any ref bindings in a `let` and + * then ensures that the lifetime of the resulting pointer is + * linked to the lifetime of the initialization expression. + */ - let guarantor = guarantor(rcx, base); - link(rcx, expr.span, expr.id, guarantor); - } + debug!("regionck::for_local()"); + let init_expr = match local.init { + None => { return; } + Some(expr) => expr, + }; + let mut mc = mc::MemCategorizationContext { typer: rcx }; + let discr_cmt = ignore_err!(mc.cat_expr(init_expr)); + link_pattern(&mut mc, discr_cmt, local.pat); +} - pub fn for_match(rcx: &mut Rcx, discr: &ast::Expr, arms: &[ast::Arm]) { - /*! - * Computes the guarantors for any ref bindings in a match and - * then ensures that the lifetime of the resulting pointer is - * linked to the lifetime of its guarantor (if any). - */ +fn link_match(rcx: &mut Rcx, discr: &ast::Expr, arms: &[ast::Arm]) { + /*! + * Computes the guarantors for any ref bindings in a match and + * then ensures that the lifetime of the resulting pointer is + * linked to the lifetime of its guarantor (if any). + */ - debug!("regionck::for_match()"); - let discr_guarantor = guarantor(rcx, discr); - debug!("discr_guarantor={}", discr_guarantor.repr(rcx.tcx())); - for arm in arms.iter() { - for pat in arm.pats.iter() { - link_ref_bindings_in_pat(rcx, *pat, discr_guarantor); - } + debug!("regionck::for_match()"); + let mut mc = mc::MemCategorizationContext { typer: rcx }; + let discr_cmt = ignore_err!(mc.cat_expr(discr)); + debug!("discr_cmt={}", discr_cmt.repr(mc.typer.tcx())); + for arm in arms.iter() { + for &root_pat in arm.pats.iter() { + link_pattern(&mut mc, discr_cmt, root_pat); } } +} - pub fn for_local(rcx: &mut Rcx, local: &ast::Local) { - /*! - * Link the lifetimes of any ref bindings in a let - * pattern to the lifetimes in the initializer. - * - * For example, given something like this: - * - * let &Foo(ref x) = ...; - * - * this would ensure that the lifetime 'a of the - * region pointer being matched must be >= the lifetime - * of the ref binding. - */ +fn link_pattern(mc: &mut mc::MemCategorizationContext<&mut Rcx>, + discr_cmt: mc::cmt, + root_pat: @ast::Pat) { + /*! + * Link lifetimes of any ref bindings in `root_pat` to + * the pointers found in the discriminant, if needed. + */ - debug!("regionck::for_match()"); - let init_expr = match local.init { - None => { return; } - Some(e) => e - }; - let init_guarantor = guarantor(rcx, init_expr); - debug!("init_guarantor={}", init_guarantor.repr(rcx.tcx())); - link_ref_bindings_in_pat(rcx, local.pat, init_guarantor); - } + let _ = mc.cat_pattern(discr_cmt, root_pat, |mc, sub_cmt, sub_pat| { + match sub_pat.node { + // `ref x` pattern + ast::PatIdent(ast::BindByRef(mutbl), _, _) => { + link_region_from_node_type( + mc.typer, sub_pat.span, sub_pat.id, + mutbl, sub_cmt); + } - pub fn for_autoref(rcx: &mut Rcx, - expr: &ast::Expr, - autoderefs: uint, - autoref: &ty::AutoRef) { - /*! - * Computes the guarantor for an expression that has an - * autoref adjustment and links it to the lifetime of the - * autoref. This is only important when auto re-borrowing - * region pointers. - */ - - debug!("guarantor::for_autoref(autoref={:?})", autoref); - - let mut expr_ct = categorize_unadjusted(rcx, expr); - debug!(" unadjusted cat={:?}", expr_ct.cat); - expr_ct = apply_autoderefs( - rcx, expr, autoderefs, expr_ct); - - match *autoref { - ty::AutoPtr(r, _) => { - // In this case, we are implicitly adding an `&`. - maybe_make_subregion(rcx, expr, r, expr_ct.cat.guarantor); - } - - ty::AutoBorrowVec(r, _) | - ty::AutoBorrowVecRef(r, _) | - ty::AutoBorrowFn(r) | - ty::AutoBorrowObj(r, _) => { - // In each of these cases, what is being borrowed is - // not the (autoderef'd) expr itself but rather the - // contents of the autoderef'd expression (i.e., what - // the pointer points at). - maybe_make_subregion(rcx, expr, r, - guarantor_of_deref(&expr_ct.cat)); - } - - ty::AutoUnsafe(_) => {} - } - - fn maybe_make_subregion( - rcx: &mut Rcx, - expr: &ast::Expr, - sub_region: ty::Region, - sup_region: Option) - { - for r in sup_region.iter() { - rcx.fcx.mk_subr(true, infer::Reborrow(expr.span), - sub_region, *r); - } - } - } - - pub fn for_by_ref(rcx: &mut Rcx, - expr: &ast::Expr, - callee_scope: ast::NodeId) { - /*! - * Computes the guarantor for cases where the `expr` is - * being passed by implicit reference and must outlive - * `callee_scope`. - */ - - let tcx = rcx.tcx(); - debug!("guarantor::for_by_ref(expr={}, callee_scope={:?})", - expr.repr(tcx), callee_scope); - let expr_cat = categorize(rcx, expr); - debug!("guarantor::for_by_ref(expr={:?}, callee_scope={:?}) category={:?}", - expr.id, callee_scope, expr_cat); - let minimum_lifetime = ty::ReScope(callee_scope); - for guarantor in expr_cat.guarantor.iter() { - mk_subregion_due_to_derefence(rcx, expr.span, - minimum_lifetime, *guarantor); - } - } - - fn link( - rcx: &mut Rcx, - span: Span, - id: ast::NodeId, - guarantor: Option) { - /*! - * - * Links the lifetime of the reference resulting from a borrow - * to the lifetime of its guarantor (if any). - */ - - debug!("link(id={:?}, guarantor={:?})", id, guarantor); - - let bound = match guarantor { - None => { - // If guarantor is None, then the value being borrowed - // is not guaranteed by a region pointer, so there are - // no lifetimes to link. - return; - } - Some(r) => { r } - }; - - // this routine is used for the result of ref bindings and & - // expressions, both of which always yield a region variable, so - // mk_subr should never fail. - let rptr_ty = rcx.resolve_node_type(id); - if !ty::type_is_bot(rptr_ty) { - let tcx = rcx.fcx.ccx.tcx; - debug!("rptr_ty={}", ty_to_str(tcx, rptr_ty)); - let r = ty::ty_region(tcx, span, rptr_ty); - rcx.fcx.mk_subr(true, infer::Reborrow(span), r, bound); - } - } - - /// Categorizes types based on what kind of pointer they are. - /// Note that we don't bother to distinguish between rptrs (&T) - /// and slices (&[T], &str)---they are all just `BorrowedPointer`. - enum PointerCategorization { - NotPointer, - OwnedPointer, - BorrowedPointer(ty::Region), - OtherPointer - } - - /// Guarantor of an expression paired with the - /// PointerCategorization` of its type. - struct ExprCategorization { - guarantor: Option, - pointer: PointerCategorization - } - - /// ExprCategorization paired with the full type of the expr - struct ExprCategorizationType { - cat: ExprCategorization, - ty: ty::t - } - - fn guarantor(rcx: &mut Rcx, expr: &ast::Expr) -> Option { - /*! - * - * Computes the guarantor of `expr`, or None if `expr` is - * not guaranteed by any region. Here `expr` is some expression - * whose address is being taken (e.g., there is an expression - * `&expr`). - */ - - debug!("guarantor()"); - match expr.node { - ast::ExprUnary(_, ast::UnDeref, b) => { - let cat = categorize(rcx, b); - guarantor_of_deref(&cat) - } - ast::ExprField(b, _, _) => { - categorize(rcx, b).guarantor - } - ast::ExprIndex(_, b, _) => { - let cat = categorize(rcx, b); - guarantor_of_deref(&cat) - } - - ast::ExprParen(e) => { - guarantor(rcx, e) - } - - // Either a variable or constant and hence resides - // in constant memory or on the stack frame. Either way, - // not guaranteed by a region pointer. - ast::ExprPath(..) => None, - - // All of these expressions are rvalues and hence their - // value is not guaranteed by a region pointer. - ast::ExprInlineAsm(..) | - ast::ExprMac(..) | - ast::ExprLit(_) | - ast::ExprUnary(..) | - ast::ExprAddrOf(..) | - ast::ExprBinary(..) | - ast::ExprVstore(..) | - ast::ExprBox(..) | - ast::ExprBreak(..) | - ast::ExprAgain(..) | - ast::ExprRet(..) | - ast::ExprLogLevel | - ast::ExprWhile(..) | - ast::ExprLoop(..) | - ast::ExprAssign(..) | - ast::ExprAssignOp(..) | - ast::ExprCast(..) | - ast::ExprCall(..) | - ast::ExprMethodCall(..) | - ast::ExprStruct(..) | - ast::ExprTup(..) | - ast::ExprIf(..) | - ast::ExprMatch(..) | - ast::ExprFnBlock(..) | - ast::ExprProc(..) | - ast::ExprBlock(..) | - ast::ExprRepeat(..) | - ast::ExprVec(..) => { - assert!(!ty::expr_is_lval( - rcx.fcx.tcx(), rcx.fcx.inh.method_map, expr)); - None - } - ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop"), - } - } - - fn categorize(rcx: &mut Rcx, expr: &ast::Expr) -> ExprCategorization { - debug!("categorize()"); - - let mut expr_ct = categorize_unadjusted(rcx, expr); - debug!("before adjustments, cat={:?}", expr_ct.cat); - - let adjustments = rcx.fcx.inh.adjustments.borrow(); - match adjustments.get().find(&expr.id) { - Some(adjustment) => { - match **adjustment { - ty::AutoAddEnv(..) => { - // This is basically an rvalue, not a pointer, no regions - // involved. - expr_ct.cat = ExprCategorization { - guarantor: None, - pointer: NotPointer - }; + // `[_, ..slice, _]` pattern + ast::PatVec(_, Some(slice_pat), _) => { + match mc.cat_slice_pattern(sub_cmt, slice_pat) { + Ok((slice_cmt, slice_mutbl, slice_r)) => { + link_region(mc.typer, sub_pat.span, slice_r, + slice_mutbl, slice_cmt); + } + Err(()) => {} } + } + _ => {} + } + }); +} - ty::AutoObject(ast::BorrowedSigil, - Some(region), - _, - _, - _, - _) => { - expr_ct.cat = ExprCategorization { - guarantor: None, - pointer: BorrowedPointer(region) - }; - } +fn link_autoref(rcx: &mut Rcx, + expr: &ast::Expr, + autoderefs: uint, + autoref: &ty::AutoRef) { + /*! + * Link lifetime of borrowed pointer resulting from autoref + * to lifetimes in the value being autoref'd. + */ - ty::AutoObject(ast::OwnedSigil, _, _, _, _, _) => { - expr_ct.cat = ExprCategorization { - guarantor: None, - pointer: OwnedPointer - }; - } + debug!("link_autoref(autoref={:?})", autoref); + let mut mc = mc::MemCategorizationContext { typer: rcx }; + let expr_cmt = ignore_err!(mc.cat_expr_autoderefd(expr, autoderefs)); + debug!("expr_cmt={}", expr_cmt.repr(mc.typer.tcx())); - ty::AutoObject(ast::ManagedSigil, _, _, _, _, _) => { - expr_ct.cat = ExprCategorization { - guarantor: None, - pointer: OtherPointer - }; - } - ty::AutoDerefRef(ref adjustment) => { - debug!("adjustment={:?}", adjustment); + match *autoref { + ty::AutoPtr(r, m) => { + link_region(mc.typer, expr.span, r, m, expr_cmt); + } - expr_ct = apply_autoderefs( - rcx, expr, adjustment.autoderefs, expr_ct); + ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => { + let cmt_index = mc.cat_index(expr, expr_cmt, autoderefs+1); + link_region(mc.typer, expr.span, r, m, cmt_index); + } - match adjustment.autoref { + ty::AutoBorrowFn(r) => { + let cmt_deref = mc.cat_deref_fn_or_obj(expr, expr_cmt, 0); + link_region(mc.typer, expr.span, r, ast::MutImmutable, cmt_deref); + } + + ty::AutoBorrowObj(r, m) => { + let cmt_deref = mc.cat_deref_fn_or_obj(expr, expr_cmt, 0); + link_region(mc.typer, expr.span, r, m, cmt_deref); + } + + ty::AutoUnsafe(_) => {} + } +} + +fn link_by_ref(rcx: &mut Rcx, + expr: &ast::Expr, + callee_scope: ast::NodeId) { + /*! + * Computes the guarantor for cases where the `expr` is + * being passed by implicit reference and must outlive + * `callee_scope`. + */ + + let tcx = rcx.tcx(); + debug!("link_by_ref(expr={}, callee_scope={})", + expr.repr(tcx), callee_scope); + let mut mc = mc::MemCategorizationContext { typer: rcx }; + let expr_cmt = ignore_err!(mc.cat_expr(expr)); + let region_min = ty::ReScope(callee_scope); + link_region(mc.typer, expr.span, region_min, ast::MutImmutable, expr_cmt); +} + +fn link_region_from_node_type(rcx: &mut Rcx, + span: Span, + id: ast::NodeId, + mutbl: ast::Mutability, + cmt_borrowed: mc::cmt) { + /*! + * Like `link_region()`, except that the region is + * extracted from the type of `id`, which must be some + * region-typed thing. + */ + + let rptr_ty = rcx.resolve_node_type(id); + if !ty::type_is_bot(rptr_ty) && !ty::type_is_error(rptr_ty) { + let tcx = rcx.fcx.ccx.tcx; + debug!("rptr_ty={}", ty_to_str(tcx, rptr_ty)); + let r = ty::ty_region(tcx, span, rptr_ty); + link_region(rcx, span, r, mutbl, cmt_borrowed); + } +} + +fn link_region(rcx: &mut Rcx, + span: Span, + region_min: ty::Region, + mutbl: ast::Mutability, + cmt_borrowed: mc::cmt) { + /*! + * Informs the inference engine that a borrow of `cmt` + * must have mutability `mutbl` and lifetime `region_min`. + * If `cmt` is a deref of a region pointer with + * lifetime `r_borrowed`, this will add the constraint that + * `region_min <= r_borrowed`. + */ + + // Iterate through all the things that must be live at least + // for the lifetime `region_min` for the borrow to be valid: + let mut cmt_borrowed = cmt_borrowed; + loop { + debug!("link_region(region_min={}, mutbl={}, cmt_borrowed={})", + region_min.repr(rcx.tcx()), + mutbl.repr(rcx.tcx()), + cmt_borrowed.repr(rcx.tcx())); + match cmt_borrowed.cat { + mc::cat_deref(base, _, mc::BorrowedPtr(_, r_borrowed)) => { + // References to an upvar `x` are translated to + // `*x`, since that is what happens in the + // underlying machine. We detect such references + // and treat them slightly differently, both to + // offer better error messages and because we need + // to infer the kind of borrow (mut, const, etc) + // to use for each upvar. + let cause = match base.cat { + mc::cat_upvar(ref upvar_id, _) => { + let mut upvar_borrow_map = + rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + match upvar_borrow_map.get().find_mut(upvar_id) { + Some(upvar_borrow) => { + debug!("link_region: {} <= {}", + region_min.repr(rcx.tcx()), + upvar_borrow.region.repr(rcx.tcx())); + adjust_upvar_borrow_kind_for_loan( + *upvar_id, + upvar_borrow, + mutbl); + infer::ReborrowUpvar(span, *upvar_id) + } None => { - } - Some(ty::AutoUnsafe(_)) => { - expr_ct.cat.guarantor = None; - expr_ct.cat.pointer = OtherPointer; - debug!("autoref, cat={:?}", expr_ct.cat); - } - Some(ty::AutoPtr(r, _)) | - Some(ty::AutoBorrowVec(r, _)) | - Some(ty::AutoBorrowVecRef(r, _)) | - Some(ty::AutoBorrowFn(r)) | - Some(ty::AutoBorrowObj(r, _)) => { - // If there is an autoref, then the result of - // this expression will be some sort of - // reference. - expr_ct.cat.guarantor = None; - expr_ct.cat.pointer = BorrowedPointer(r); - debug!("autoref, cat={:?}", expr_ct.cat); + rcx.tcx().sess.span_bug( + span, + format!("Illegal upvar id: {}", + upvar_id.repr(rcx.tcx()))); } } } - _ => fail!("invalid or unhandled adjustment"), - } - } - - None => {} - } - - debug!("result={:?}", expr_ct.cat); - return expr_ct.cat; - } - - fn categorize_unadjusted(rcx: &mut Rcx, - expr: &ast::Expr) - -> ExprCategorizationType { - debug!("categorize_unadjusted()"); - - let guarantor = { - let method_map = rcx.fcx.inh.method_map.borrow(); - if method_map.get().contains_key(&expr.id) { - None - } else { - guarantor(rcx, expr) - } - }; - - let expr_ty = rcx.resolve_node_type(expr.id); - ExprCategorizationType { - cat: ExprCategorization { - guarantor: guarantor, - pointer: pointer_categorize(expr_ty) - }, - ty: expr_ty - } - } - - fn apply_autoderefs( - rcx: &mut Rcx, - expr: &ast::Expr, - autoderefs: uint, - ct: ExprCategorizationType) - -> ExprCategorizationType { - let mut ct = ct; - let tcx = rcx.fcx.ccx.tcx; - - if ty::type_is_error(ct.ty) { - ct.cat.pointer = NotPointer; - return ct; - } - - for _ in range(0u, autoderefs) { - ct.cat.guarantor = guarantor_of_deref(&ct.cat); - - match ty::deref(ct.ty, true) { - Some(mt) => { - ct.ty = mt.ty; - ct.cat.pointer = pointer_categorize(ct.ty); - } - None => { - tcx.sess.span_bug( - expr.span, - format!("autoderef but type not derefable: {}", - ty_to_str(tcx, ct.ty))); - } - } - - debug!("autoderef, cat={:?}", ct.cat); - } - return ct; - } - - fn pointer_categorize(ty: ty::t) -> PointerCategorization { - match ty::get(ty).sty { - ty::ty_rptr(r, _) | - ty::ty_vec(_, ty::vstore_slice(r)) | - ty::ty_trait(_, _, ty::RegionTraitStore(r), _, _) | - ty::ty_str(ty::vstore_slice(r)) => { - BorrowedPointer(r) - } - ty::ty_uniq(..) | - ty::ty_str(ty::vstore_uniq) | - ty::ty_trait(_, _, ty::UniqTraitStore, _, _) | - ty::ty_vec(_, ty::vstore_uniq) => { - OwnedPointer - } - ty::ty_box(..) | ty::ty_ptr(..) => { - OtherPointer - } - ty::ty_closure(ref closure_ty) => { - match closure_ty.sigil { - ast::BorrowedSigil => BorrowedPointer(closure_ty.region), - ast::OwnedSigil => OwnedPointer, - ast::ManagedSigil => OtherPointer, - } - } - _ => { - NotPointer - } - } - } - - fn guarantor_of_deref(cat: &ExprCategorization) -> Option { - match cat.pointer { - NotPointer => cat.guarantor, - BorrowedPointer(r) => Some(r), - OwnedPointer => cat.guarantor, - OtherPointer => None - } - } - - fn link_ref_bindings_in_pat( - rcx: &mut Rcx, - pat: &ast::Pat, - guarantor: Option) { - /*! - * - * Descends through the pattern, tracking the guarantor - * of the value being matched. When a ref binding is encountered, - * links the lifetime of that ref binding to the lifetime of - * the guarantor. We begin with the guarantor of the - * discriminant but of course as we go we may pass through - * other pointers. - */ - - debug!("link_ref_bindings_in_pat(pat={}, guarantor={:?})", - rcx.fcx.pat_to_str(pat), guarantor); - - match pat.node { - ast::PatWild | ast::PatWildMulti => {} - ast::PatIdent(ast::BindByRef(_), _, opt_p) => { - link(rcx, pat.span, pat.id, guarantor); - - for p in opt_p.iter() { - link_ref_bindings_in_pat(rcx, *p, guarantor); - } - } - ast::PatIdent(_, _, opt_p) => { - for p in opt_p.iter() { - link_ref_bindings_in_pat(rcx, *p, guarantor); - } - } - ast::PatEnum(_, None) => {} - ast::PatEnum(_, Some(ref pats)) => { - link_ref_bindings_in_pats(rcx, pats, guarantor); - } - ast::PatStruct(_, ref fpats, _) => { - for fpat in fpats.iter() { - link_ref_bindings_in_pat(rcx, fpat.pat, guarantor); - } - } - ast::PatTup(ref ps) => { - link_ref_bindings_in_pats(rcx, ps, guarantor) - } - ast::PatUniq(p) => { - link_ref_bindings_in_pat(rcx, p, guarantor) - } - ast::PatRegion(p) => { - let rptr_ty = rcx.resolve_node_type(pat.id); - let r = ty::ty_region(rcx.fcx.tcx(), pat.span, rptr_ty); - link_ref_bindings_in_pat(rcx, p, Some(r)); - } - ast::PatLit(..) => {} - ast::PatRange(..) => {} - ast::PatVec(ref before, ref slice, ref after) => { - let vec_ty = rcx.resolve_node_type(pat.id); - let vstore = ty::ty_vstore(vec_ty); - let guarantor1 = match vstore { - ty::vstore_fixed(_) | ty::vstore_uniq => guarantor, - ty::vstore_slice(r) => Some(r), + _ => { + infer::Reborrow(span) + } }; - link_ref_bindings_in_pats(rcx, before, guarantor1); - for &p in slice.iter() { - link_ref_bindings_in_pat(rcx, p, guarantor); + debug!("link_region: {} <= {}", + region_min.repr(rcx.tcx()), + r_borrowed.repr(rcx.tcx())); + rcx.fcx.mk_subr(true, cause, region_min, r_borrowed); + + if mutbl == ast::MutMutable { + // If this is a mutable borrow, then the thing + // being borrowed will have to be unique. + // In user code, this means it must be an `&mut` + // borrow, but for an upvar, we might opt + // for an immutable-unique borrow. + adjust_upvar_borrow_kind_for_unique(rcx, base); } - link_ref_bindings_in_pats(rcx, after, guarantor1); + + // Borrowing an `&mut` pointee for `region_min` is + // only valid if the pointer resides in a unique + // location which is itself valid for + // `region_min`. We don't care about the unique + // part, but we may need to influence the + // inference to ensure that the location remains + // valid. + // + // FIXME(#8624) fixing borrowck will require this + // if m == ast::m_mutbl { + // cmt_borrowed = cmt_base; + // } else { + // return; + // } + return; + } + mc::cat_discr(cmt_base, _) | + mc::cat_downcast(cmt_base) | + mc::cat_deref(cmt_base, _, mc::OwnedPtr) | + mc::cat_interior(cmt_base, _) => { + // Interior or owned data requires its base to be valid + cmt_borrowed = cmt_base; + } + mc::cat_deref(_, _, mc::GcPtr(..)) | + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_static_item | + mc::cat_copied_upvar(..) | + mc::cat_local(..) | + mc::cat_arg(..) | + mc::cat_upvar(..) | + mc::cat_rvalue(..) => { + // These are all "base cases" with independent lifetimes + // that are not subject to inference + return; } } } +} - fn link_ref_bindings_in_pats(rcx: &mut Rcx, - pats: &~[@ast::Pat], - guarantor: Option) { - for pat in pats.iter() { - link_ref_bindings_in_pat(rcx, *pat, guarantor); +fn adjust_borrow_kind_for_assignment_lhs(rcx: &mut Rcx, + lhs: &ast::Expr) { + /*! + * Adjusts the inferred borrow_kind as needed to account + * for upvars that are assigned to in an assignment + * expression. + */ + + let mut mc = mc::MemCategorizationContext { typer: rcx }; + let cmt = ignore_err!(mc.cat_expr(lhs)); + adjust_upvar_borrow_kind_for_mut(mc.typer, cmt); +} + +fn adjust_upvar_borrow_kind_for_mut(rcx: &mut Rcx, + cmt: mc::cmt) { + let mut cmt = cmt; + loop { + debug!("adjust_upvar_borrow_kind_for_mut(cmt={})", + cmt.repr(rcx.tcx())); + + match cmt.cat { + mc::cat_deref(base, _, mc::OwnedPtr) | + mc::cat_interior(base, _) | + mc::cat_downcast(base) | + mc::cat_discr(base, _) => { + // Interior or owned data is mutable if base is + // mutable, so iterate to the base. + cmt = base; + continue; + } + + mc::cat_deref(base, _, mc::BorrowedPtr(..)) => { + match base.cat { + mc::cat_upvar(ref upvar_id, _) => { + // if this is an implicit deref of an + // upvar, then we need to modify the + // borrow_kind of the upvar to make sure it + // is inferred to mutable if necessary + let mut upvar_borrow_map = + rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + let ub = upvar_borrow_map.get().get_mut(upvar_id); + return adjust_upvar_borrow_kind(*upvar_id, ub, ty::MutBorrow); + } + + _ => { + // assignment to deref of an `&mut` + // borrowed pointer implies that the + // pointer itself must be unique, but not + // necessarily *mutable* + return adjust_upvar_borrow_kind_for_unique(rcx, base); + } + } + } + + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_deref(_, _, mc::GcPtr) | + mc::cat_static_item | + mc::cat_rvalue(_) | + mc::cat_copied_upvar(_) | + mc::cat_local(_) | + mc::cat_arg(_) | + mc::cat_upvar(..) => { + return; + } + } + } +} + +fn adjust_upvar_borrow_kind_for_unique(rcx: &mut Rcx, + cmt: mc::cmt) { + let mut cmt = cmt; + loop { + debug!("adjust_upvar_borrow_kind_for_unique(cmt={})", + cmt.repr(rcx.tcx())); + + match cmt.cat { + mc::cat_deref(base, _, mc::OwnedPtr) | + mc::cat_interior(base, _) | + mc::cat_downcast(base) | + mc::cat_discr(base, _) => { + // Interior or owned data is unique if base is + // unique. + cmt = base; + continue; + } + + mc::cat_deref(base, _, mc::BorrowedPtr(..)) => { + match base.cat { + mc::cat_upvar(ref upvar_id, _) => { + // if this is an implicit deref of an + // upvar, then we need to modify the + // borrow_kind of the upvar to make sure it + // is inferred to unique if necessary + let mut upvar_borrow_map = + rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + let ub = upvar_borrow_map.get().get_mut(upvar_id); + return adjust_upvar_borrow_kind(*upvar_id, ub, ty::UniqueImmBorrow); + } + + _ => { + // for a borrowed pointer to be unique, its + // base must be unique + return adjust_upvar_borrow_kind_for_unique(rcx, base); + } + } + } + + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_deref(_, _, mc::GcPtr) | + mc::cat_static_item | + mc::cat_rvalue(_) | + mc::cat_copied_upvar(_) | + mc::cat_local(_) | + mc::cat_arg(_) | + mc::cat_upvar(..) => { + return; + } + } + } +} + +fn link_upvar_borrow_kind(rcx: &mut Rcx, + inner_upvar_id: ty::UpvarId, + outer_upvar_id: ty::UpvarId) { + /*! + * Indicates that the borrow_kind of `outer_upvar_id` must + * permit a reborrowing with the borrow_kind of `inner_upvar_id` + */ + + debug!("link_upvar_borrow_kind: inner_upvar_id={:?} outer_upvar_id={:?}", + inner_upvar_id, outer_upvar_id); + + let mut upvar_borrow_map = rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + let inner_borrow = upvar_borrow_map.get().get_copy(&inner_upvar_id); + match upvar_borrow_map.get().find_mut(&outer_upvar_id) { + Some(outer_borrow) => { + adjust_upvar_borrow_kind(outer_upvar_id, outer_borrow, inner_borrow.kind); + } + None => { /* outer closure is not a stack closure */ } + } +} + +fn adjust_upvar_borrow_kind_for_loan(upvar_id: ty::UpvarId, + upvar_borrow: &mut ty::UpvarBorrow, + mutbl: ast::Mutability) { + debug!("adjust_upvar_borrow_kind_for_loan: upvar_id={:?} kind={:?} -> {:?}", + upvar_id, upvar_borrow.kind, mutbl); + + adjust_upvar_borrow_kind(upvar_id, upvar_borrow, + ty::BorrowKind::from_mutbl(mutbl)) +} + +fn adjust_upvar_borrow_kind(upvar_id: ty::UpvarId, + upvar_borrow: &mut ty::UpvarBorrow, + kind: ty::BorrowKind) { + /*! + * We infer the borrow_kind with which to borrow upvars in a stack + * closure. The borrow_kind basically follows a lattice of + * `imm < unique-imm < mut`, moving from left to right as needed (but never + * right to left). Here the argument `mutbl` is the borrow_kind that + * is required by some particular use. + */ + + debug!("adjust_upvar_borrow_kind: id={:?} kind=({:?} -> {:?})", + upvar_id, upvar_borrow.kind, kind); + + match (upvar_borrow.kind, kind) { + // Take RHS: + (ty::ImmBorrow, ty::UniqueImmBorrow) | + (ty::ImmBorrow, ty::MutBorrow) | + (ty::UniqueImmBorrow, ty::MutBorrow) => { + upvar_borrow.kind = kind; + } + // Take LHS: + (ty::ImmBorrow, ty::ImmBorrow) | + (ty::UniqueImmBorrow, ty::ImmBorrow) | + (ty::UniqueImmBorrow, ty::UniqueImmBorrow) | + (ty::MutBorrow, _) => { } } - } From db38192daf8eb20b17ddad22d2029dec0df2b203 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:41:58 -0500 Subject: [PATCH 28/33] mem_categorization -- parameterize over TYPER interface, treat upvar refs as deref'd borrowed pointers --- src/librustc/middle/mem_categorization.rs | 623 +++++++++++++--------- 1 file changed, 380 insertions(+), 243 deletions(-) diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 93f439b653c..b0e31a8e443 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -22,7 +22,7 @@ * forms): * * E = rvalue // some computed rvalue - * | x // address of a local variable, arg, or upvar + * | x // address of a local variable or argument * | *E // deref of a ptr * | E.comp // access to an interior component * @@ -44,13 +44,25 @@ * themselves. For example, auto-derefs are explicit. Also, an index a[b] is * decomposed into two operations: a derefence to reach the array data and * then an index to jump forward to the relevant item. + * + * ## By-reference upvars + * + * One part of the translation which may be non-obvious is that we translate + * closure upvars into the dereference of a borrow pointer; this more closely + * resembles the runtime translation. So, for example, if we had: + * + * let mut x = 3; + * let y = 5; + * let inc = || x += y; + * + * Then when we categorize `x` (*within* the closure) we would yield a + * result of `*x'`, effectively, where `x'` is a `cat_upvar` reference + * tied to `x`. The type of `x'` will be a borrowed pointer. */ use middle::ty; -use middle::typeck; use util::ppaux::{ty_to_str, region_ptr_to_str, Repr}; -use util::common::indenter; use syntax::ast::{MutImmutable, MutMutable}; use syntax::ast; @@ -63,15 +75,15 @@ pub enum categorization { cat_rvalue(ty::Region), // temporary val, argument is its scope cat_static_item, cat_copied_upvar(CopiedUpvar), // upvar copied into @fn or ~fn env - cat_stack_upvar(cmt), // by ref upvar from || + cat_upvar(ty::UpvarId, ty::UpvarBorrow), // by ref upvar from stack closure cat_local(ast::NodeId), // local variable cat_arg(ast::NodeId), // formal argument cat_deref(cmt, uint, PointerKind), // deref of a ptr cat_interior(cmt, InteriorKind), // something interior: field, tuple, etc - cat_downcast(cmt), // selects a particular enum variant (..) + cat_downcast(cmt), // selects a particular enum variant (*1) cat_discr(cmt, ast::NodeId), // match discriminant (see preserve()) - // (..) downcast is only required if the enum has more than one variant + // (*1) downcast is only required if the enum has more than one variant } #[deriving(Eq)] @@ -83,10 +95,10 @@ pub struct CopiedUpvar { // different kinds of pointers: #[deriving(Eq, IterBytes)] pub enum PointerKind { - uniq_ptr, - gc_ptr, - region_ptr(ast::Mutability, ty::Region), - unsafe_ptr(ast::Mutability) + OwnedPtr, + GcPtr, + BorrowedPtr(ty::BorrowKind, ty::Region), + UnsafePtr(ast::Mutability), } // We use the term "interior" to mean "something reachable from the @@ -114,7 +126,7 @@ pub enum ElementKind { pub enum MutabilityCategory { McImmutable, // Immutable. McDeclared, // Directly declared as mutable. - McInherited // Inherited from the fact that owner is mutable. + McInherited, // Inherited from the fact that owner is mutable. } // `cmt`: "Category, Mutability, and Type". @@ -159,30 +171,32 @@ pub fn opt_deref_kind(t: ty::t) -> Option { ty::ty_vec(_, ty::vstore_uniq) | ty::ty_str(ty::vstore_uniq) | ty::ty_closure(ty::ClosureTy {sigil: ast::OwnedSigil, ..}) => { - Some(deref_ptr(uniq_ptr)) + Some(deref_ptr(OwnedPtr)) } ty::ty_rptr(r, mt) | ty::ty_vec(mt, ty::vstore_slice(r)) => { - Some(deref_ptr(region_ptr(mt.mutbl, r))) + let kind = ty::BorrowKind::from_mutbl(mt.mutbl); + Some(deref_ptr(BorrowedPtr(kind, r))) } ty::ty_trait(_, _, ty::RegionTraitStore(r), m, _) => { - Some(deref_ptr(region_ptr(m, r))) + let kind = ty::BorrowKind::from_mutbl(m); + Some(deref_ptr(BorrowedPtr(kind, r))) } ty::ty_str(ty::vstore_slice(r)) | ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil, region: r, ..}) => { - Some(deref_ptr(region_ptr(ast::MutImmutable, r))) + Some(deref_ptr(BorrowedPtr(ty::ImmBorrow, r))) } - ty::ty_box(_) => { - Some(deref_ptr(gc_ptr)) + ty::ty_box(..) => { + Some(deref_ptr(GcPtr)) } ty::ty_ptr(ref mt) => { - Some(deref_ptr(unsafe_ptr(mt.mutbl))) + Some(deref_ptr(UnsafePtr(mt.mutbl))) } ty::ty_enum(..) | @@ -210,53 +224,7 @@ pub fn deref_kind(tcx: ty::ctxt, t: ty::t) -> deref_kind { } } -pub fn cat_expr(tcx: ty::ctxt, - method_map: typeck::method_map, - expr: &ast::Expr) - -> cmt { - let mcx = &mem_categorization_ctxt { - tcx: tcx, method_map: method_map - }; - return mcx.cat_expr(expr); -} - -pub fn cat_expr_unadjusted(tcx: ty::ctxt, - method_map: typeck::method_map, - expr: &ast::Expr) - -> cmt { - let mcx = &mem_categorization_ctxt { - tcx: tcx, method_map: method_map - }; - return mcx.cat_expr_unadjusted(expr); -} - -pub fn cat_expr_autoderefd( - tcx: ty::ctxt, - method_map: typeck::method_map, - expr: &ast::Expr, - autoderefs: uint) -> cmt -{ - let mcx = &mem_categorization_ctxt { - tcx: tcx, method_map: method_map - }; - return mcx.cat_expr_autoderefd(expr, autoderefs); -} - -pub fn cat_def( - tcx: ty::ctxt, - method_map: typeck::method_map, - expr_id: ast::NodeId, - expr_span: Span, - expr_ty: ty::t, - def: ast::Def) -> cmt { - - let mcx = &mem_categorization_ctxt { - tcx: tcx, method_map: method_map - }; - return mcx.cat_def(expr_id, expr_span, expr_ty, def); -} - -pub trait ast_node { +trait ast_node { fn id(&self) -> ast::NodeId; fn span(&self) -> Span; } @@ -271,9 +239,37 @@ impl ast_node for ast::Pat { fn span(&self) -> Span { self.span } } -pub struct mem_categorization_ctxt { - tcx: ty::ctxt, - method_map: typeck::method_map, +pub struct MemCategorizationContext { + typer: TYPER +} + +pub type McResult = Result; + +/** + * The `Typer` trait provides the interface fro the mem-categorization + * module to the results of the type check. It can be used to query + * the type assigned to an expression node, to inquire after adjustments, + * and so on. + * + * This interface is needed because mem-categorization is used from + * two places: `regionck` and `borrowck`. `regionck` executes before + * type inference is complete, and hence derives types and so on from + * intermediate tables. This also implies that type errors can occur, + * and hence `node_ty()` and friends return a `Result` type -- any + * error will propagate back up through the mem-categorization + * routines. + * + * In the borrow checker, in contrast, type checking is complete and we + * know that no errors have occurred, so we simply consult the tcx and we + * can be sure that only `Ok` results will occur. + */ +pub trait Typer { + fn tcx(&self) -> ty::ctxt; + fn node_ty(&mut self, id: ast::NodeId) -> McResult; + fn adjustment(&mut self, node_id: ast::NodeId) -> Option<@ty::AutoAdjustment>; + fn is_method_call(&mut self, id: ast::NodeId) -> bool; + fn temporary_scope(&mut self, rvalue_id: ast::NodeId) -> Option; + fn upvar_borrow(&mut self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow; } impl ToStr for MutabilityCategory { @@ -290,18 +286,45 @@ impl MutabilityCategory { } } + pub fn from_borrow_kind(borrow_kind: ty::BorrowKind) -> MutabilityCategory { + match borrow_kind { + ty::ImmBorrow => McImmutable, + ty::UniqueImmBorrow => McImmutable, + ty::MutBorrow => McDeclared, + } + } + + pub fn from_pointer_kind(base_mutbl: MutabilityCategory, + ptr: PointerKind) -> MutabilityCategory { + match ptr { + OwnedPtr => { + base_mutbl.inherit() + } + BorrowedPtr(borrow_kind, _) => { + MutabilityCategory::from_borrow_kind(borrow_kind) + } + GcPtr => { + McImmutable + } + UnsafePtr(m) => { + MutabilityCategory::from_mutbl(m) + } + } + } + pub fn inherit(&self) -> MutabilityCategory { match *self { McImmutable => McImmutable, McDeclared => McInherited, - McInherited => McInherited + McInherited => McInherited, } } pub fn is_mutable(&self) -> bool { match *self { McImmutable => false, - McDeclared | McInherited => true + McInherited => true, + McDeclared => true, } } @@ -320,55 +343,78 @@ impl MutabilityCategory { } } -impl mem_categorization_ctxt { - pub fn expr_ty(&self, expr: &ast::Expr) -> ty::t { - ty::expr_ty(self.tcx, expr) +macro_rules! if_ok( + ($inp: expr) => ( + match $inp { + Ok(v) => { v } + Err(e) => { return Err(e); } + } + ) +) + +impl MemCategorizationContext { + fn tcx(&self) -> ty::ctxt { + self.typer.tcx() } - pub fn pat_ty(&self, pat: &ast::Pat) -> ty::t { - ty::node_id_to_type(self.tcx, pat.id) + fn adjustment(&mut self, id: ast::NodeId) -> Option<@ty::AutoAdjustment> { + self.typer.adjustment(id) } - pub fn cat_expr(&self, expr: &ast::Expr) -> cmt { - let adjustments = self.tcx.adjustments.borrow(); - match adjustments.get().find(&expr.id) { + fn expr_ty(&mut self, expr: &ast::Expr) -> McResult { + self.typer.node_ty(expr.id) + } + + fn expr_ty_adjusted(&mut self, expr: &ast::Expr) -> McResult { + let unadjusted_ty = if_ok!(self.expr_ty(expr)); + let adjustment = self.adjustment(expr.id); + Ok(ty::adjust_ty(self.tcx(), expr.span, unadjusted_ty, adjustment)) + } + + fn node_ty(&mut self, id: ast::NodeId) -> McResult { + self.typer.node_ty(id) + } + + fn pat_ty(&mut self, pat: @ast::Pat) -> McResult { + self.typer.node_ty(pat.id) + } + + pub fn cat_expr(&mut self, expr: &ast::Expr) -> McResult { + match self.adjustment(expr.id) { None => { // No adjustments. self.cat_expr_unadjusted(expr) } Some(adjustment) => { - match **adjustment { + match *adjustment { ty::AutoObject(..) => { // Implicity casts a concrete object to trait object // so just patch up the type - let expr_ty = ty::expr_ty_adjusted(self.tcx, expr); - @cmt_ { - ty: expr_ty, - ..*self.cat_expr_unadjusted(expr) - } + let expr_ty = if_ok!(self.expr_ty_adjusted(expr)); + let expr_cmt = if_ok!(self.cat_expr_unadjusted(expr)); + Ok(@cmt_ {ty: expr_ty, ..*expr_cmt}) } ty::AutoAddEnv(..) => { // Convert a bare fn to a closure by adding NULL env. // Result is an rvalue. - let expr_ty = ty::expr_ty_adjusted(self.tcx, expr); - self.cat_rvalue_node(expr.id(), expr.span(), expr_ty) + let expr_ty = if_ok!(self.expr_ty_adjusted(expr)); + Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } - ty::AutoDerefRef(ty::AutoDerefRef { - autoref: Some(_), - ..}) => { + ty::AutoDerefRef( + ty::AutoDerefRef { + autoref: Some(_), ..}) => { // Equivalent to &*expr or something similar. // Result is an rvalue. - let expr_ty = ty::expr_ty_adjusted(self.tcx, expr); - self.cat_rvalue_node(expr.id(), expr.span(), expr_ty) + let expr_ty = if_ok!(self.expr_ty_adjusted(expr)); + Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } - ty::AutoDerefRef(ty::AutoDerefRef { - autoref: None, - autoderefs: autoderefs - }) => { + ty::AutoDerefRef( + ty::AutoDerefRef { + autoref: None, autoderefs: autoderefs}) => { // Equivalent to *expr or something similar. self.cat_expr_autoderefd(expr, autoderefs) } @@ -377,53 +423,51 @@ impl mem_categorization_ctxt { } } - pub fn cat_expr_autoderefd(&self, expr: &ast::Expr, autoderefs: uint) - -> cmt { - let mut cmt = self.cat_expr_unadjusted(expr); + pub fn cat_expr_autoderefd(&mut self, expr: &ast::Expr, autoderefs: uint) + -> McResult { + let mut cmt = if_ok!(self.cat_expr_unadjusted(expr)); for deref in range(1u, autoderefs + 1) { cmt = self.cat_deref(expr, cmt, deref); } - return cmt; + return Ok(cmt); } - pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> cmt { + pub fn cat_expr_unadjusted(&mut self, expr: &ast::Expr) -> McResult { debug!("cat_expr: id={} expr={}", - expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr())); + expr.id, + expr.repr(self.tcx())); - let expr_ty = self.expr_ty(expr); + let expr_ty = if_ok!(self.expr_ty(expr)); match expr.node { ast::ExprUnary(_, ast::UnDeref, e_base) => { - let method_map = self.method_map.borrow(); - if method_map.get().contains_key(&expr.id) { - return self.cat_rvalue_node(expr.id(), expr.span(), expr_ty); + if self.typer.is_method_call(expr.id) { + return Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)); } - let base_cmt = self.cat_expr(e_base); - self.cat_deref(expr, base_cmt, 0) + let base_cmt = if_ok!(self.cat_expr(e_base)); + Ok(self.cat_deref(expr, base_cmt, 0)) } ast::ExprField(base, f_name, _) => { // Method calls are now a special syntactic form, // so `a.b` should always be a field. - let method_map = self.method_map.borrow(); - assert!(!method_map.get().contains_key(&expr.id)); + assert!(!self.typer.is_method_call(expr.id)); - let base_cmt = self.cat_expr(base); - self.cat_field(expr, base_cmt, f_name, self.expr_ty(expr)) + let base_cmt = if_ok!(self.cat_expr(base)); + Ok(self.cat_field(expr, base_cmt, f_name, expr_ty)) } ast::ExprIndex(_, base, _) => { - let method_map = self.method_map.borrow(); - if method_map.get().contains_key(&expr.id) { - return self.cat_rvalue_node(expr.id(), expr.span(), expr_ty); + if self.typer.is_method_call(expr.id) { + return Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)); } - let base_cmt = self.cat_expr(base); - self.cat_index(expr, base_cmt, 0) + let base_cmt = if_ok!(self.cat_expr(base)); + Ok(self.cat_index(expr, base_cmt, 0)) } ast::ExprPath(_) => { - let def_map = self.tcx.def_map.borrow(); + let def_map = self.tcx().def_map.borrow(); let def = def_map.get().get_copy(&expr.id); self.cat_def(expr.id, expr.span, expr_ty, def) } @@ -441,49 +485,48 @@ impl mem_categorization_ctxt { ast::ExprLit(..) | ast::ExprBreak(..) | ast::ExprMac(..) | ast::ExprAgain(..) | ast::ExprStruct(..) | ast::ExprRepeat(..) | ast::ExprInlineAsm(..) | ast::ExprBox(..) => { - return self.cat_rvalue_node(expr.id(), expr.span(), expr_ty); + Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop") } } - pub fn cat_def(&self, + pub fn cat_def(&mut self, id: ast::NodeId, span: Span, expr_ty: ty::t, def: ast::Def) - -> cmt { + -> McResult { debug!("cat_def: id={} expr={}", - id, ty_to_str(self.tcx, expr_ty)); - + id, expr_ty.repr(self.tcx())); match def { ast::DefStruct(..) | ast::DefVariant(..) => { - self.cat_rvalue_node(id, span, expr_ty) + Ok(self.cat_rvalue_node(id, span, expr_ty)) } ast::DefFn(..) | ast::DefStaticMethod(..) | ast::DefMod(_) | ast::DefForeignMod(_) | ast::DefStatic(_, false) | ast::DefUse(_) | ast::DefTrait(_) | ast::DefTy(_) | ast::DefPrimTy(_) | ast::DefTyParam(..) | ast::DefTyParamBinder(..) | ast::DefRegion(_) | ast::DefLabel(_) | ast::DefSelfTy(..) | ast::DefMethod(..) => { - @cmt_ { + Ok(@cmt_ { id:id, span:span, cat:cat_static_item, mutbl: McImmutable, ty:expr_ty - } + }) } ast::DefStatic(_, true) => { - @cmt_ { + Ok(@cmt_ { id:id, span:span, cat:cat_static_item, mutbl: McDeclared, ty:expr_ty - } + }) } ast::DefArg(vid, binding_mode) => { @@ -495,17 +538,17 @@ impl mem_categorization_ctxt { ast::BindByValue(ast::MutMutable) => McDeclared, _ => McImmutable }; - @cmt_ { + Ok(@cmt_ { id: id, span: span, cat: cat_arg(vid), mutbl: m, ty:expr_ty - } + }) } - ast::DefUpvar(upvar_id, inner, fn_node_id, _) => { - let ty = ty::node_id_to_type(self.tcx, fn_node_id); + ast::DefUpvar(var_id, _, fn_node_id, _) => { + let ty = if_ok!(self.node_ty(fn_node_id)); match ty::get(ty).sty { ty::ty_closure(ref closure_ty) => { // Decide whether to use implicit reference or by copy/move @@ -523,33 +566,25 @@ impl mem_categorization_ctxt { }; if var_is_refd { - let upvar_cmt = - self.cat_def(id, span, expr_ty, *inner); - @cmt_ { - id:id, - span:span, - cat:cat_stack_upvar(upvar_cmt), - mutbl:upvar_cmt.mutbl.inherit(), - ty:upvar_cmt.ty - } + self.cat_upvar(id, span, var_id, fn_node_id) } else { // FIXME #2152 allow mutation of moved upvars - @cmt_ { + Ok(@cmt_ { id:id, span:span, cat:cat_copied_upvar(CopiedUpvar { - upvar_id: upvar_id, + upvar_id: var_id, onceness: closure_ty.onceness}), mutbl:McImmutable, ty:expr_ty - } + }) } } _ => { - self.tcx.sess.span_bug( + self.tcx().sess.span_bug( span, - format!("upvar of non-closure {:?} - {}", - fn_node_id, ty.repr(self.tcx))); + format!("Upvar of non-closure {} - {}", + fn_node_id, ty.repr(self.tcx()))); } } } @@ -562,19 +597,73 @@ impl mem_categorization_ctxt { _ => McImmutable }; - @cmt_ { + Ok(@cmt_ { id: id, span: span, cat: cat_local(vid), mutbl: m, ty: expr_ty - } + }) } } } - pub fn cat_rvalue_node(&self, id: ast::NodeId, span: Span, expr_ty: ty::t) -> cmt { - match self.tcx.region_maps.temporary_scope(id) { + fn cat_upvar(&mut self, + id: ast::NodeId, + span: Span, + var_id: ast::NodeId, + fn_node_id: ast::NodeId) + -> McResult { + /*! + * Upvars through a closure are in fact indirect + * references. That is, when a closure refers to a + * variable from a parent stack frame like `x = 10`, + * that is equivalent to `*x_ = 10` where `x_` is a + * borrowed pointer (`&mut x`) created when the closure + * was created and store in the environment. This + * equivalence is expose in the mem-categorization. + */ + + let upvar_id = ty::UpvarId { var_id: var_id, + closure_expr_id: fn_node_id }; + + let upvar_borrow = self.typer.upvar_borrow(upvar_id); + + let var_ty = if_ok!(self.node_ty(var_id)); + + // We can't actually represent the types of all upvars + // as user-describable types, since upvars support const + // and unique-imm borrows! Therefore, we cheat, and just + // give err type. Nobody should be inspecting this type anyhow. + let upvar_ty = ty::mk_err(); + + let base_cmt = @cmt_ { + id:id, + span:span, + cat:cat_upvar(upvar_id, upvar_borrow), + mutbl:McImmutable, + ty:upvar_ty, + }; + + let ptr = BorrowedPtr(upvar_borrow.kind, upvar_borrow.region); + + let deref_cmt = @cmt_ { + id:id, + span:span, + cat:cat_deref(base_cmt, 0, ptr), + mutbl:MutabilityCategory::from_borrow_kind(upvar_borrow.kind), + ty:var_ty, + }; + + Ok(deref_cmt) + } + + pub fn cat_rvalue_node(&mut self, + id: ast::NodeId, + span: Span, + expr_ty: ty::t) + -> cmt { + match self.typer.temporary_scope(id) { Some(scope) => { self.cat_rvalue(id, span, ty::ReScope(scope), expr_ty) } @@ -584,7 +673,7 @@ impl mem_categorization_ctxt { } } - pub fn cat_rvalue(&self, + pub fn cat_rvalue(&mut self, cmt_id: ast::NodeId, span: Span, temp_scope: ty::Region, @@ -602,7 +691,7 @@ impl mem_categorization_ctxt { /// component is inherited from the base it is a part of. For /// example, a record field is mutable if it is declared mutable /// or if the container is mutable. - pub fn inherited_mutability(&self, + pub fn inherited_mutability(&mut self, base_m: MutabilityCategory, interior_m: ast::Mutability) -> MutabilityCategory { @@ -612,7 +701,7 @@ impl mem_categorization_ctxt { } } - pub fn cat_field(&self, + pub fn cat_field(&mut self, node: &N, base_cmt: cmt, f_name: ast::Ident, @@ -627,7 +716,7 @@ impl mem_categorization_ctxt { } } - pub fn cat_deref_fn_or_obj(&self, + pub fn cat_deref_fn_or_obj(&mut self, node: &N, base_cmt: cmt, deref_cnt: uint) @@ -638,11 +727,11 @@ impl mem_categorization_ctxt { // know what type lies at the other end, so we just call it // `()` (the empty tuple). - let opaque_ty = ty::mk_tup(self.tcx, ~[]); + let opaque_ty = ty::mk_tup(self.tcx(), ~[]); return self.cat_deref_common(node, base_cmt, deref_cnt, opaque_ty); } - pub fn cat_deref(&self, + pub fn cat_deref(&mut self, node: &N, base_cmt: cmt, deref_cnt: uint) @@ -650,37 +739,28 @@ impl mem_categorization_ctxt { let mt = match ty::deref(base_cmt.ty, true) { Some(mt) => mt, None => { - self.tcx.sess.span_bug( + self.tcx().sess.span_bug( node.span(), - format!("explicit deref of non-derefable type: {}", - ty_to_str(self.tcx, base_cmt.ty))); + format!("Explicit deref of non-derefable type: {}", + base_cmt.ty.repr(self.tcx()))); } }; return self.cat_deref_common(node, base_cmt, deref_cnt, mt.ty); } - pub fn cat_deref_common(&self, + pub fn cat_deref_common(&mut self, node: &N, base_cmt: cmt, deref_cnt: uint, deref_ty: ty::t) -> cmt { - match deref_kind(self.tcx, base_cmt.ty) { + match deref_kind(self.tcx(), base_cmt.ty) { deref_ptr(ptr) => { // for unique ptrs, we inherit mutability from the // owning reference. - let m = match ptr { - uniq_ptr => { - base_cmt.mutbl.inherit() - } - gc_ptr => { - McImmutable - } - region_ptr(m, _) | unsafe_ptr(m) => { - MutabilityCategory::from_mutbl(m) - } - }; + let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, + ptr); @cmt_ { id:node.id(), @@ -704,7 +784,7 @@ impl mem_categorization_ctxt { } } - pub fn cat_index(&self, + pub fn cat_index(&mut self, elt: &N, base_cmt: cmt, derefs: uint) @@ -743,28 +823,18 @@ impl mem_categorization_ctxt { let element_ty = match ty::index(base_cmt.ty) { Some(ref mt) => mt.ty, None => { - self.tcx.sess.span_bug( + self.tcx().sess.span_bug( elt.span(), - format!("explicit index of non-index type `{}`", - ty_to_str(self.tcx, base_cmt.ty))); + format!("Explicit index of non-index type `{}`", + base_cmt.ty.repr(self.tcx()))); } }; - return match deref_kind(self.tcx, base_cmt.ty) { + return match deref_kind(self.tcx(), base_cmt.ty) { deref_ptr(ptr) => { // for unique ptrs, we inherit mutability from the // owning reference. - let m = match ptr { - uniq_ptr => { - base_cmt.mutbl.inherit() - } - gc_ptr => { - McImmutable - } - region_ptr(m, _) | unsafe_ptr(m) => { - MutabilityCategory::from_mutbl(m) - } - }; + let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr); // the deref is explicit in the resulting cmt let deref_cmt = @cmt_ { @@ -775,7 +845,7 @@ impl mem_categorization_ctxt { ty:element_ty }; - interior(elt, deref_cmt, base_cmt.ty, m, element_ty) + interior(elt, deref_cmt, base_cmt.ty, m.inherit(), element_ty) } deref_interior(_) => { @@ -801,7 +871,57 @@ impl mem_categorization_ctxt { } } - pub fn cat_imm_interior(&self, + pub fn cat_slice_pattern(&mut self, + vec_cmt: cmt, + slice_pat: @ast::Pat) + -> McResult<(cmt, ast::Mutability, ty::Region)> { + /*! + * Given a pattern P like: `[_, ..Q, _]`, where `vec_cmt` is + * the cmt for `P`, `slice_pat` is the pattern `Q`, returns: + * - a cmt for `Q` + * - the mutability and region of the slice `Q` + * + * These last two bits of info happen to be things that + * borrowck needs. + */ + + let slice_ty = if_ok!(self.node_ty(slice_pat.id)); + let (slice_mutbl, slice_r) = vec_slice_info(self.tcx(), + slice_pat, + slice_ty); + let cmt_slice = self.cat_index(slice_pat, vec_cmt, 0); + return Ok((cmt_slice, slice_mutbl, slice_r)); + + fn vec_slice_info(tcx: ty::ctxt, + pat: @ast::Pat, + slice_ty: ty::t) + -> (ast::Mutability, ty::Region) { + /*! + * In a pattern like [a, b, ..c], normally `c` has slice type, + * but if you have [a, b, ..ref c], then the type of `ref c` + * will be `&&[]`, so to extract the slice details we have + * to recurse through rptrs. + */ + + match ty::get(slice_ty).sty { + ty::ty_vec(slice_mt, ty::vstore_slice(slice_r)) => { + (slice_mt.mutbl, slice_r) + } + + ty::ty_rptr(_, ref mt) => { + vec_slice_info(tcx, pat, mt.ty) + } + + _ => { + tcx.sess.span_bug( + pat.span, + format!("Type of slice pattern is not a slice")); + } + } + } + } + + pub fn cat_imm_interior(&mut self, node: &N, base_cmt: cmt, interior_ty: ty::t, @@ -816,7 +936,7 @@ impl mem_categorization_ctxt { } } - pub fn cat_downcast(&self, + pub fn cat_downcast(&mut self, node: &N, base_cmt: cmt, downcast_ty: ty::t) @@ -830,10 +950,13 @@ impl mem_categorization_ctxt { } } - pub fn cat_pattern(&self, + pub fn cat_pattern(&mut self, cmt: cmt, - pat: &ast::Pat, - op: |cmt, &ast::Pat|) { + pat: @ast::Pat, + op: |&mut MemCategorizationContext, + cmt, + @ast::Pat|) + -> McResult<()> { // Here, `cmt` is the categorization for the value being // matched and pat is the pattern it is being matched against. // @@ -846,7 +969,7 @@ impl mem_categorization_ctxt { // we can be sure that the binding will remain valid for the // duration of the arm. // - // (..) There is subtlety concerning the correspondence between + // (*2) There is subtlety concerning the correspondence between // pattern ids and types as compared to *expression* ids and // types. This is explained briefly. on the definition of the // type `cmt`, so go off and read what it says there, then @@ -856,7 +979,8 @@ impl mem_categorization_ctxt { // In general, the id of the cmt should be the node that // "produces" the value---patterns aren't executable code // exactly, but I consider them to "execute" when they match a - // value. So if you have something like: + // value, and I consider them to produce the value that was + // matched. So if you have something like: // // let x = @@3; // match x { @@ -878,13 +1002,12 @@ impl mem_categorization_ctxt { // step out of sync again. So you'll see below that we always // get the type of the *subpattern* and use that. - let tcx = self.tcx; + let tcx = self.tcx(); debug!("cat_pattern: id={} pat={} cmt={}", pat.id, pprust::pat_to_str(pat, tcx.sess.intr()), cmt.repr(tcx)); - let _i = indenter(); - op(cmt, pat); + op(self, cmt, pat); match pat.node { ast::PatWild | ast::PatWildMulti => { @@ -895,13 +1018,13 @@ impl mem_categorization_ctxt { // variant(..) } ast::PatEnum(_, Some(ref subpats)) => { - let def_map = self.tcx.def_map.borrow(); + let def_map = self.tcx().def_map.borrow(); match def_map.get().find(&pat.id) { Some(&ast::DefVariant(enum_did, _, _)) => { // variant(x, y, z) let downcast_cmt = { - if ty::enum_is_univariant(tcx, enum_did) { + if ty::enum_is_univariant(self.tcx(), enum_did) { cmt // univariant, no downcast needed } else { self.cat_downcast(pat, cmt, cmt.ty) @@ -909,34 +1032,34 @@ impl mem_categorization_ctxt { }; for (i, &subpat) in subpats.iter().enumerate() { - let subpat_ty = self.pat_ty(subpat); // see (..) + let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2) let subcmt = self.cat_imm_interior( pat, downcast_cmt, subpat_ty, InteriorField(PositionalField(i))); - self.cat_pattern(subcmt, subpat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(subcmt, subpat, |x,y,z| op(x,y,z))); } } Some(&ast::DefFn(..)) | Some(&ast::DefStruct(..)) => { for (i, &subpat) in subpats.iter().enumerate() { - let subpat_ty = self.pat_ty(subpat); // see (..) + let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2) let cmt_field = self.cat_imm_interior( pat, cmt, subpat_ty, InteriorField(PositionalField(i))); - self.cat_pattern(cmt_field, subpat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(cmt_field, subpat, |x,y,z| op(x,y,z))); } } Some(&ast::DefStatic(..)) => { for &subpat in subpats.iter() { - self.cat_pattern(cmt, subpat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(cmt, subpat, |x,y,z| op(x,y,z))); } } _ => { - self.tcx.sess.span_bug( + self.tcx().sess.span_bug( pat.span, "enum pattern didn't resolve to enum or struct"); } @@ -944,7 +1067,7 @@ impl mem_categorization_ctxt { } ast::PatIdent(_, _, Some(subpat)) => { - self.cat_pattern(cmt, subpat, op); + if_ok!(self.cat_pattern(cmt, subpat, op)); } ast::PatIdent(_, _, None) => { @@ -954,42 +1077,42 @@ impl mem_categorization_ctxt { ast::PatStruct(_, ref field_pats, _) => { // {f1: p1, ..., fN: pN} for fp in field_pats.iter() { - let field_ty = self.pat_ty(fp.pat); // see (..) + let field_ty = if_ok!(self.pat_ty(fp.pat)); // see (*2) let cmt_field = self.cat_field(pat, cmt, fp.ident, field_ty); - self.cat_pattern(cmt_field, fp.pat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(cmt_field, fp.pat, |x,y,z| op(x,y,z))); } } ast::PatTup(ref subpats) => { // (p1, ..., pN) for (i, &subpat) in subpats.iter().enumerate() { - let subpat_ty = self.pat_ty(subpat); // see (..) + let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2) let subcmt = self.cat_imm_interior( pat, cmt, subpat_ty, InteriorField(PositionalField(i))); - self.cat_pattern(subcmt, subpat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(subcmt, subpat, |x,y,z| op(x,y,z))); } } ast::PatUniq(subpat) | ast::PatRegion(subpat) => { // @p1, ~p1 let subcmt = self.cat_deref(pat, cmt, 0); - self.cat_pattern(subcmt, subpat, op); + if_ok!(self.cat_pattern(subcmt, subpat, op)); } ast::PatVec(ref before, slice, ref after) => { let elt_cmt = self.cat_index(pat, cmt, 0); for &before_pat in before.iter() { - self.cat_pattern(elt_cmt, before_pat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(elt_cmt, before_pat, |x,y,z| op(x,y,z))); } for &slice_pat in slice.iter() { - let slice_ty = self.pat_ty(slice_pat); + let slice_ty = if_ok!(self.pat_ty(slice_pat)); let slice_cmt = self.cat_rvalue_node(pat.id(), pat.span(), slice_ty); - self.cat_pattern(slice_cmt, slice_pat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(slice_cmt, slice_pat, |x,y,z| op(x,y,z))); } for &after_pat in after.iter() { - self.cat_pattern(elt_cmt, after_pat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(elt_cmt, after_pat, |x,y,z| op(x,y,z))); } } @@ -997,9 +1120,11 @@ impl mem_categorization_ctxt { /*always ok*/ } } + + Ok(()) } - pub fn mut_to_str(&self, mutbl: ast::Mutability) -> ~str { + pub fn mut_to_str(&mut self, mutbl: ast::Mutability) -> ~str { match mutbl { MutMutable => ~"mutable", MutImmutable => ~"immutable" @@ -1023,8 +1148,15 @@ impl mem_categorization_ctxt { cat_arg(..) => { ~"argument" } - cat_deref(_, _, pk) => { - format!("dereference of {} pointer", ptr_sigil(pk)) + cat_deref(base, _, pk) => { + match base.cat { + cat_upvar(..) => { + format!("captured outer variable") + } + _ => { + format!("dereference of {} pointer", ptr_sigil(pk)) + } + } } cat_interior(_, InteriorField(NamedField(_))) => { ~"field" @@ -1041,7 +1173,7 @@ impl mem_categorization_ctxt { cat_interior(_, InteriorElement(OtherElement)) => { ~"indexed content" } - cat_stack_upvar(_) => { + cat_upvar(..) => { ~"captured outer variable" } cat_discr(cmt, _) => { @@ -1054,7 +1186,7 @@ impl mem_categorization_ctxt { } pub fn region_to_str(&self, r: ty::Region) -> ~str { - region_ptr_to_str(self.tcx, r) + region_ptr_to_str(self.tcx(), r) } } @@ -1099,7 +1231,7 @@ pub fn field_mutbl(tcx: ty::ctxt, pub enum AliasableReason { AliasableManaged, - AliasableBorrowed(ast::Mutability), + AliasableBorrowed, AliasableOther, AliasableStatic, AliasableStaticMut, @@ -1117,16 +1249,16 @@ impl cmt_ { cat_copied_upvar(..) | cat_local(..) | cat_arg(..) | - cat_deref(_, _, unsafe_ptr(..)) | - cat_deref(_, _, gc_ptr) | - cat_deref(_, _, region_ptr(..)) => { + cat_deref(_, _, UnsafePtr(..)) | + cat_deref(_, _, GcPtr(..)) | + cat_deref(_, _, BorrowedPtr(..)) | + cat_upvar(..) => { @self } cat_downcast(b) | - cat_stack_upvar(b) | cat_discr(b, _) | cat_interior(b, _) | - cat_deref(b, _, uniq_ptr) => { + cat_deref(b, _, OwnedPtr) => { b.guarantor() } } @@ -1143,10 +1275,10 @@ impl cmt_ { // aliased and eventually recused. match self.cat { - cat_deref(b, _, region_ptr(MutMutable, _)) | + cat_deref(b, _, BorrowedPtr(ty::MutBorrow, _)) | + cat_deref(b, _, BorrowedPtr(ty::UniqueImmBorrow, _)) | cat_downcast(b) | - cat_stack_upvar(b) | - cat_deref(b, _, uniq_ptr) | + cat_deref(b, _, OwnedPtr) | cat_interior(b, _) | cat_discr(b, _) => { // Aliasability depends on base cmt @@ -1156,8 +1288,9 @@ impl cmt_ { cat_copied_upvar(CopiedUpvar {onceness: ast::Once, ..}) | cat_rvalue(..) | cat_local(..) | + cat_upvar(..) | cat_arg(_) | - cat_deref(_, _, unsafe_ptr(..)) => { // yes, it's aliasable, but... + cat_deref(_, _, UnsafePtr(..)) => { // yes, it's aliasable, but... None } @@ -1173,12 +1306,12 @@ impl cmt_ { } } - cat_deref(_, _, gc_ptr) => { + cat_deref(_, _, GcPtr) => { Some(AliasableManaged) } - cat_deref(_, _, region_ptr(m @ MutImmutable, _)) => { - Some(AliasableBorrowed(m)) + cat_deref(_, _, BorrowedPtr(ty::ImmBorrow, _)) => { + Some(AliasableBorrowed) } } } @@ -1201,12 +1334,15 @@ impl Repr for categorization { cat_rvalue(..) | cat_copied_upvar(..) | cat_local(..) | + cat_upvar(..) | cat_arg(..) => { format!("{:?}", *self) } cat_deref(cmt, derefs, ptr) => { - format!("{}->({}, {})", cmt.cat.repr(tcx), - ptr_sigil(ptr), derefs) + format!("{}-{}{}->", + cmt.cat.repr(tcx), + ptr_sigil(ptr), + derefs) } cat_interior(cmt, interior) => { format!("{}.{}", @@ -1216,7 +1352,6 @@ impl Repr for categorization { cat_downcast(cmt) => { format!("{}->(enum)", cmt.cat.repr(tcx)) } - cat_stack_upvar(cmt) | cat_discr(cmt, _) => { cmt.cat.repr(tcx) } @@ -1224,12 +1359,14 @@ impl Repr for categorization { } } -pub fn ptr_sigil(ptr: PointerKind) -> ~str { +pub fn ptr_sigil(ptr: PointerKind) -> &'static str { match ptr { - uniq_ptr => ~"~", - gc_ptr => ~"@", - region_ptr(_, _) => ~"&", - unsafe_ptr(_) => ~"*" + OwnedPtr => "~", + GcPtr => "@", + BorrowedPtr(ty::ImmBorrow, _) => "&", + BorrowedPtr(ty::MutBorrow, _) => "&mut", + BorrowedPtr(ty::UniqueImmBorrow, _) => "&unique", + UnsafePtr(_) => "*" } } From 6b8b75142997b12baf374ef6c4721bec5df62849 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 9 Feb 2014 14:08:03 -0500 Subject: [PATCH 29/33] borrowck -- treak borrows from closures like other borrows --- src/librustc/middle/borrowck/check_loans.rs | 301 +++++++++------ .../borrowck/gather_loans/gather_moves.rs | 68 ++-- .../middle/borrowck/gather_loans/lifetime.rs | 46 +-- .../middle/borrowck/gather_loans/mod.rs | 296 ++++++++------- .../borrowck/gather_loans/restrictions.rs | 30 +- src/librustc/middle/borrowck/mod.rs | 356 ++++++++++-------- src/librustc/middle/borrowck/move_data.rs | 10 - 7 files changed, 631 insertions(+), 476 deletions(-) diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index cfd2c0719b0..cb1a803c35a 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -52,9 +52,11 @@ impl<'a> Visitor<()> for CheckLoanCtxt<'a> { fn visit_pat(&mut self, p: &ast::Pat, _: ()) { check_loans_in_pat(self, p); } - fn visit_fn(&mut self, fk: &visit::FnKind, fd: &ast::FnDecl, - b: &ast::Block, s: Span, n: ast::NodeId, _: ()) { - check_loans_in_fn(self, fk, fd, b, s, n); + fn visit_fn(&mut self, _fk: &visit::FnKind, _fd: &ast::FnDecl, + _b: &ast::Block, _s: Span, _n: ast::NodeId, _: ()) { + // Don't process nested items or closures here, + // the outer loop will take care of it. + return; } // FIXME(#10894) should continue recursing @@ -218,9 +220,19 @@ impl<'a> CheckLoanCtxt<'a> { loan2.repr(self.tcx())); // Restrictions that would cause the new loan to be illegal: - let illegal_if = match loan2.mutbl { - MutableMutability => RESTR_FREEZE | RESTR_CLAIM, - ImmutableMutability => RESTR_FREEZE, + let illegal_if = match loan2.kind { + // Look for restrictions against mutation. These are + // generated by all other borrows. + ty::MutBorrow => RESTR_MUTATE, + + // Look for restrictions against freezing (immutable borrows). + // These are generated by `&mut` borrows. + ty::ImmBorrow => RESTR_FREEZE, + + // No matter how the data is borrowed (as `&`, as `&mut`, + // or as `&unique imm`) it will always generate a + // restriction against mutating the data. So look for those. + ty::UniqueImmBorrow => RESTR_MUTATE, }; debug!("illegal_if={:?}", illegal_if); @@ -228,47 +240,107 @@ impl<'a> CheckLoanCtxt<'a> { if !restr.set.intersects(illegal_if) { continue; } if restr.loan_path != loan2.loan_path { continue; } - match (new_loan.mutbl, old_loan.mutbl) { - (_, MutableMutability) => { - let var = self.bccx.loan_path_to_str(new_loan.loan_path); + let old_pronoun = if new_loan.loan_path == old_loan.loan_path { + ~"it" + } else { + format!("`{}`", + self.bccx.loan_path_to_str(old_loan.loan_path)) + }; + + match (new_loan.kind, old_loan.kind) { + (ty::MutBorrow, ty::MutBorrow) => { self.bccx.span_err( new_loan.span, - format!("cannot borrow `{}` because it is already \ - borrowed as mutable", var)); - self.bccx.span_note( - old_loan.span, - format!("previous borrow of `{0}` as mutable occurs \ - here; the mutable borrow prevents subsequent \ - moves, borrows, or modification of `{0}` \ - until the borrow ends", var)); + format!("cannot borrow `{}` as mutable \ + more than once at a time", + self.bccx.loan_path_to_str(new_loan.loan_path))); } - (_, mutability) => { + (ty::UniqueImmBorrow, _) => { + self.bccx.span_err( + new_loan.span, + format!("closure requires unique access to `{}` \ + but {} is already borrowed", + self.bccx.loan_path_to_str(new_loan.loan_path), + old_pronoun)); + } + + (_, ty::UniqueImmBorrow) => { self.bccx.span_err( new_loan.span, format!("cannot borrow `{}` as {} because \ - it is already borrowed as {}", - self.bccx.loan_path_to_str(new_loan.loan_path), - self.bccx.mut_to_str(new_loan.mutbl), - self.bccx.mut_to_str(old_loan.mutbl))); + previous closure requires unique access", + self.bccx.loan_path_to_str(new_loan.loan_path), + new_loan.kind.to_user_str())); + } - let var = self.bccx.loan_path_to_str(new_loan.loan_path); - let mut note = format!("previous borrow of `{}` occurs \ - here", var); - if mutability == ImmutableMutability { - note.push_str(format!("; the immutable borrow prevents \ - subsequent moves or mutable - borrows of `{}` until the - borrow ends", var)); - } - self.bccx.span_note(old_loan.span, note); + (_, _) => { + self.bccx.span_err( + new_loan.span, + format!("cannot borrow `{}` as {} because \ + {} is also borrowed as {}", + self.bccx.loan_path_to_str(new_loan.loan_path), + new_loan.kind.to_user_str(), + old_pronoun, + old_loan.kind.to_user_str())); } } + match new_loan.cause { + ClosureCapture(span) => { + self.bccx.span_note( + span, + format!("borrow occurs due to use of `{}` in closure", + self.bccx.loan_path_to_str(new_loan.loan_path))); + } + _ => { } + } + + let rule_summary = match old_loan.kind { + ty::MutBorrow => { + format!("the mutable borrow prevents subsequent \ + moves, borrows, or modification of `{0}` \ + until the borrow ends", + self.bccx.loan_path_to_str(old_loan.loan_path)) + } + + ty::ImmBorrow => { + format!("the immutable borrow prevents subsequent \ + moves or mutable borrows of `{0}` \ + until the borrow ends", + self.bccx.loan_path_to_str(old_loan.loan_path)) + } + + ty::UniqueImmBorrow => { + format!("the unique capture prevents subsequent \ + moves or borrows of `{0}` \ + until the borrow ends", + self.bccx.loan_path_to_str(old_loan.loan_path)) + } + }; + + let borrow_summary = match old_loan.cause { + ClosureCapture(_) => { + format!("previous borrow of `{}` occurs here due to \ + use in closure", + self.bccx.loan_path_to_str(old_loan.loan_path)) + } + + AddrOf | AutoRef | RefBinding => { + format!("previous borrow of `{}` occurs here", + self.bccx.loan_path_to_str(old_loan.loan_path)) + } + }; + + self.bccx.span_note( + old_loan.span, + format!("{}; {}", borrow_summary, rule_summary)); + let old_loan_span = ast_map::node_span(self.tcx().items, old_loan.kill_scope); self.bccx.span_end_note(old_loan_span, "previous borrow ends here"); + return false; } @@ -349,11 +421,23 @@ impl<'a> CheckLoanCtxt<'a> { } // Otherwise, just a plain error. - self.bccx.span_err( - expr.span, - format!("cannot assign to {} {}", - cmt.mutbl.to_user_str(), - self.bccx.cmt_to_str(cmt))); + match opt_loan_path(cmt) { + Some(lp) => { + self.bccx.span_err( + expr.span, + format!("cannot assign to {} {} `{}`", + cmt.mutbl.to_user_str(), + self.bccx.cmt_to_str(cmt), + self.bccx.loan_path_to_str(lp))); + } + None => { + self.bccx.span_err( + expr.span, + format!("cannot assign to {} {}", + cmt.mutbl.to_user_str(), + self.bccx.cmt_to_str(cmt))); + } + } return; fn mark_variable_as_used_mut(this: &CheckLoanCtxt, @@ -377,11 +461,11 @@ impl<'a> CheckLoanCtxt<'a> { return; } - mc::cat_stack_upvar(b) => { - cmt = b; + mc::cat_upvar(..) => { + return; } - mc::cat_deref(_, _, mc::gc_ptr) => { + mc::cat_deref(_, _, mc::GcPtr) => { assert_eq!(cmt.mutbl, mc::McImmutable); return; } @@ -389,25 +473,22 @@ impl<'a> CheckLoanCtxt<'a> { mc::cat_rvalue(..) | mc::cat_static_item | mc::cat_copied_upvar(..) | - mc::cat_deref(_, _, mc::unsafe_ptr(..)) | - mc::cat_deref(_, _, mc::region_ptr(..)) => { + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_deref(_, _, mc::BorrowedPtr(..)) => { assert_eq!(cmt.mutbl, mc::McDeclared); return; } mc::cat_discr(b, _) | - mc::cat_deref(b, _, mc::uniq_ptr) => { + mc::cat_deref(b, _, mc::OwnedPtr) => { assert_eq!(cmt.mutbl, mc::McInherited); cmt = b; } mc::cat_downcast(b) | mc::cat_interior(b, _) => { - if cmt.mutbl == mc::McInherited { - cmt = b; - } else { - return; // field declared as mutable or some such - } + assert_eq!(cmt.mutbl, mc::McInherited); + cmt = b; } } } @@ -422,7 +503,7 @@ impl<'a> CheckLoanCtxt<'a> { debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})", cmt.repr(this.tcx()), guarantor.repr(this.tcx())); match guarantor.cat { - mc::cat_deref(b, _, mc::region_ptr(ast::MutMutable, _)) => { + mc::cat_deref(b, _, mc::BorrowedPtr(ty::MutBorrow, _)) => { // Statically prohibit writes to `&mut` when aliasable check_for_aliasability_violation(this, expr, b); @@ -557,7 +638,7 @@ impl<'a> CheckLoanCtxt<'a> { // with inherited mutability and with `&mut` // pointers. LpExtend(lp_base, mc::McInherited, _) | - LpExtend(lp_base, _, LpDeref(mc::region_ptr(ast::MutMutable, _))) => { + LpExtend(lp_base, _, LpDeref(mc::BorrowedPtr(ty::MutBorrow, _))) => { loan_path = lp_base; } @@ -572,9 +653,7 @@ impl<'a> CheckLoanCtxt<'a> { // Check for a non-const loan of `loan_path` let cont = this.each_in_scope_loan(expr.id, |loan| { if loan.loan_path == loan_path { - this.report_illegal_mutation(expr, - full_loan_path, - loan); + this.report_illegal_mutation(expr, full_loan_path, loan); false } else { true @@ -603,9 +682,10 @@ impl<'a> CheckLoanCtxt<'a> { fn check_move_out_from_expr(&self, expr: &ast::Expr) { match expr.node { ast::ExprFnBlock(..) | ast::ExprProc(..) => { - // moves due to capture clauses are checked - // in `check_loans_in_fn`, so that we can - // give a better error message + // Moves due to captures are checked in + // check_captured_variables() because it allows + // us to give a more precise error message with + // a more precise span. } _ => { self.check_move_out_from_id(expr.id, expr.span) @@ -621,18 +701,59 @@ impl<'a> CheckLoanCtxt<'a> { self.bccx.span_err( span, format!("cannot move out of `{}` \ - because it is borrowed", + because it is borrowed", self.bccx.loan_path_to_str(move_path))); self.bccx.span_note( loan_span, format!("borrow of `{}` occurs here", - self.bccx.loan_path_to_str(loan_path))); + self.bccx.loan_path_to_str(loan_path))); } } true }); } + fn check_captured_variables(&self, + closure_id: ast::NodeId, + span: Span) { + let capture_map = self.bccx.capture_map.borrow(); + let cap_vars = capture_map.get().get(&closure_id); + for cap_var in cap_vars.borrow().iter() { + let var_id = ast_util::def_id_of_def(cap_var.def).node; + let var_path = @LpVar(var_id); + self.check_if_path_is_moved(closure_id, span, + MovedInCapture, var_path); + match cap_var.mode { + moves::CapRef | moves::CapCopy => {} + moves::CapMove => { + check_by_move_capture(self, closure_id, cap_var, var_path); + } + } + } + return; + + fn check_by_move_capture(this: &CheckLoanCtxt, + closure_id: ast::NodeId, + cap_var: &moves::CaptureVar, + move_path: @LoanPath) { + let move_err = this.analyze_move_out_from(closure_id, move_path); + match move_err { + MoveOk => {} + MoveWhileBorrowed(loan_path, loan_span) => { + this.bccx.span_err( + cap_var.span, + format!("cannot move `{}` into closure \ + because it is borrowed", + this.bccx.loan_path_to_str(move_path))); + this.bccx.span_note( + loan_span, + format!("borrow of `{}` occurs here", + this.bccx.loan_path_to_str(loan_path))); + } + } + } + } + pub fn analyze_move_out_from(&self, expr_id: ast::NodeId, mut move_path: @LoanPath) @@ -681,67 +802,6 @@ impl<'a> CheckLoanCtxt<'a> { } } -fn check_loans_in_fn<'a>(this: &mut CheckLoanCtxt<'a>, - fk: &visit::FnKind, - decl: &ast::FnDecl, - body: &ast::Block, - sp: Span, - id: ast::NodeId) { - match *fk { - visit::FkItemFn(..) | visit::FkMethod(..) => { - // Don't process nested items. - return; - } - - visit::FkFnBlock(..) => { - check_captured_variables(this, id, sp); - } - } - - visit::walk_fn(this, fk, decl, body, sp, id, ()); - - fn check_captured_variables(this: &CheckLoanCtxt, - closure_id: ast::NodeId, - span: Span) { - let capture_map = this.bccx.capture_map.borrow(); - let cap_vars = capture_map.get().get(&closure_id); - for cap_var in cap_vars.borrow().iter() { - let var_id = ast_util::def_id_of_def(cap_var.def).node; - let var_path = @LpVar(var_id); - this.check_if_path_is_moved(closure_id, span, - MovedInCapture, var_path); - match cap_var.mode { - moves::CapRef | moves::CapCopy => {} - moves::CapMove => { - check_by_move_capture(this, closure_id, cap_var, var_path); - } - } - } - return; - - fn check_by_move_capture(this: &CheckLoanCtxt, - closure_id: ast::NodeId, - cap_var: &moves::CaptureVar, - move_path: @LoanPath) { - let move_err = this.analyze_move_out_from(closure_id, move_path); - match move_err { - MoveOk => {} - MoveWhileBorrowed(loan_path, loan_span) => { - this.bccx.span_err( - cap_var.span, - format!("cannot move `{}` into closure \ - because it is borrowed", - this.bccx.loan_path_to_str(move_path))); - this.bccx.span_note( - loan_span, - format!("borrow of `{}` occurs here", - this.bccx.loan_path_to_str(loan_path))); - } - } - } - } -} - fn check_loans_in_local<'a>(this: &mut CheckLoanCtxt<'a>, local: &ast::Local) { visit::walk_local(this, local, ()); @@ -769,6 +829,9 @@ fn check_loans_in_expr<'a>(this: &mut CheckLoanCtxt<'a>, } } } + ast::ExprFnBlock(..) | ast::ExprProc(..) => { + this.check_captured_variables(expr.id, expr.span) + } ast::ExprAssign(dest, _) | ast::ExprAssignOp(_, _, dest, _) => { this.check_assignment(dest); diff --git a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs index 0d9b4b0b171..49b12a6db1f 100644 --- a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs @@ -18,9 +18,8 @@ use middle::borrowck::move_data::*; use middle::moves; use middle::ty; use syntax::ast; -use syntax::ast_util; use syntax::codemap::Span; -use util::ppaux::{UserString}; +use util::ppaux::{Repr, UserString}; pub fn gather_decl(bccx: &BorrowckCtxt, move_data: &MoveData, @@ -35,33 +34,14 @@ pub fn gather_move_from_expr(bccx: &BorrowckCtxt, move_data: &MoveData, move_expr: &ast::Expr, cmt: mc::cmt) { - gather_move_from_expr_or_pat(bccx, move_data, move_expr.id, MoveExpr, cmt); + gather_move(bccx, move_data, move_expr.id, MoveExpr, cmt); } pub fn gather_move_from_pat(bccx: &BorrowckCtxt, move_data: &MoveData, move_pat: &ast::Pat, cmt: mc::cmt) { - gather_move_from_expr_or_pat(bccx, move_data, move_pat.id, MovePat, cmt); -} - -fn gather_move_from_expr_or_pat(bccx: &BorrowckCtxt, - move_data: &MoveData, - move_id: ast::NodeId, - move_kind: MoveKind, - cmt: mc::cmt) { - if !check_is_legal_to_move_from(bccx, cmt, cmt) { - return; - } - - match opt_loan_path(cmt) { - Some(loan_path) => { - move_data.add_move(bccx.tcx, loan_path, move_id, move_kind); - } - None => { - // move from rvalue or unsafe pointer, hence ok - } - } + gather_move(bccx, move_data, move_pat.id, MovePat, cmt); } pub fn gather_captures(bccx: &BorrowckCtxt, @@ -72,16 +52,38 @@ pub fn gather_captures(bccx: &BorrowckCtxt, for captured_var in captured_vars.borrow().iter() { match captured_var.mode { moves::CapMove => { - let fvar_id = ast_util::def_id_of_def(captured_var.def).node; - let loan_path = @LpVar(fvar_id); - move_data.add_move(bccx.tcx, loan_path, closure_expr.id, - Captured); + let cmt = bccx.cat_captured_var(closure_expr.id, + closure_expr.span, + captured_var); + gather_move(bccx, move_data, closure_expr.id, Captured, cmt); } moves::CapCopy | moves::CapRef => {} } } } +fn gather_move(bccx: &BorrowckCtxt, + move_data: &MoveData, + move_id: ast::NodeId, + move_kind: MoveKind, + cmt: mc::cmt) { + debug!("gather_move(move_id={}, cmt={})", + move_id, cmt.repr(bccx.tcx)); + + if !check_is_legal_to_move_from(bccx, cmt, cmt) { + return; + } + + match opt_loan_path(cmt) { + Some(loan_path) => { + move_data.add_move(bccx.tcx, loan_path, move_id, move_kind); + } + None => { + // move from rvalue or unsafe pointer, hence ok + } + } +} + pub fn gather_assignment(bccx: &BorrowckCtxt, move_data: &MoveData, assignment_id: ast::NodeId, @@ -99,15 +101,15 @@ fn check_is_legal_to_move_from(bccx: &BorrowckCtxt, cmt0: mc::cmt, cmt: mc::cmt) -> bool { match cmt.cat { - mc::cat_deref(_, _, mc::region_ptr(..)) | - mc::cat_deref(_, _, mc::gc_ptr) | - mc::cat_deref(_, _, mc::unsafe_ptr(..)) | - mc::cat_stack_upvar(..) | + mc::cat_deref(_, _, mc::BorrowedPtr(..)) | + mc::cat_deref(_, _, mc::GcPtr) | + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_upvar(..) | mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Many, .. }) => { bccx.span_err( cmt0.span, format!("cannot move out of {}", - bccx.cmt_to_str(cmt))); + bccx.cmt_to_str(cmt))); false } @@ -158,7 +160,7 @@ fn check_is_legal_to_move_from(bccx: &BorrowckCtxt, } } - mc::cat_deref(b, _, mc::uniq_ptr) | + mc::cat_deref(b, _, mc::OwnedPtr) | mc::cat_discr(b, _) => { check_is_legal_to_move_from(bccx, cmt0, b) } diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs index e151f398458..c47affac683 100644 --- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs +++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs @@ -26,16 +26,19 @@ pub fn guarantee_lifetime(bccx: &BorrowckCtxt, item_scope_id: ast::NodeId, root_scope_id: ast::NodeId, span: Span, + cause: LoanCause, cmt: mc::cmt, loan_region: ty::Region, - loan_mutbl: LoanMutability) -> R { + loan_kind: ty::BorrowKind) + -> Result<(),()> { debug!("guarantee_lifetime(cmt={}, loan_region={})", cmt.repr(bccx.tcx), loan_region.repr(bccx.tcx)); let ctxt = GuaranteeLifetimeContext {bccx: bccx, item_scope_id: item_scope_id, span: span, + cause: cause, loan_region: loan_region, - loan_mutbl: loan_mutbl, + loan_kind: loan_kind, cmt_original: cmt, root_scope_id: root_scope_id}; ctxt.check(cmt, None) @@ -55,8 +58,9 @@ struct GuaranteeLifetimeContext<'a> { root_scope_id: ast::NodeId, span: Span, + cause: LoanCause, loan_region: ty::Region, - loan_mutbl: LoanMutability, + loan_kind: ty::BorrowKind, cmt_original: mc::cmt } @@ -76,21 +80,18 @@ impl<'a> GuaranteeLifetimeContext<'a> { mc::cat_copied_upvar(..) | // L-Local mc::cat_local(..) | // L-Local mc::cat_arg(..) | // L-Local - mc::cat_deref(_, _, mc::region_ptr(..)) | // L-Deref-Borrowed - mc::cat_deref(_, _, mc::unsafe_ptr(..)) => { + mc::cat_upvar(..) | + mc::cat_deref(_, _, mc::BorrowedPtr(..)) | // L-Deref-Borrowed + mc::cat_deref(_, _, mc::UnsafePtr(..)) => { let scope = self.scope(cmt); self.check_scope(scope) } - mc::cat_stack_upvar(cmt) => { - self.check(cmt, discr_scope) - } - mc::cat_static_item => { Ok(()) } - mc::cat_deref(base, derefs, mc::gc_ptr) => { + mc::cat_deref(base, derefs, mc::GcPtr) => { let base_scope = self.scope(base); // L-Deref-Managed-Imm-User-Root @@ -112,7 +113,7 @@ impl<'a> GuaranteeLifetimeContext<'a> { } mc::cat_downcast(base) | - mc::cat_deref(base, _, mc::uniq_ptr) | // L-Deref-Send + mc::cat_deref(base, _, mc::OwnedPtr) | // L-Deref-Send mc::cat_interior(base, _) => { // L-Field self.check(base, discr_scope) } @@ -269,12 +270,12 @@ impl<'a> GuaranteeLifetimeContext<'a> { mc::cat_rvalue(..) | mc::cat_static_item | mc::cat_copied_upvar(..) | - mc::cat_deref(..) => { + mc::cat_deref(..) | + mc::cat_upvar(..) => { false } r @ mc::cat_downcast(..) | r @ mc::cat_interior(..) | - r @ mc::cat_stack_upvar(..) | r @ mc::cat_discr(..) => { self.tcx().sess.span_bug( cmt.span, @@ -294,6 +295,7 @@ impl<'a> GuaranteeLifetimeContext<'a> { mc::cat_rvalue(temp_scope) => { temp_scope } + mc::cat_upvar(..) | mc::cat_copied_upvar(_) => { ty::ReScope(self.item_scope_id) } @@ -304,17 +306,16 @@ impl<'a> GuaranteeLifetimeContext<'a> { mc::cat_arg(local_id) => { ty::ReScope(self.bccx.tcx.region_maps.var_scope(local_id)) } - mc::cat_deref(_, _, mc::unsafe_ptr(..)) => { + mc::cat_deref(_, _, mc::UnsafePtr(..)) => { ty::ReStatic } - mc::cat_deref(_, _, mc::region_ptr(_, r)) => { + mc::cat_deref(_, _, mc::BorrowedPtr(_, r)) => { r } mc::cat_downcast(cmt) | - mc::cat_deref(cmt, _, mc::uniq_ptr) | - mc::cat_deref(cmt, _, mc::gc_ptr) | + mc::cat_deref(cmt, _, mc::OwnedPtr) | + mc::cat_deref(cmt, _, mc::GcPtr) | mc::cat_interior(cmt, _) | - mc::cat_stack_upvar(cmt) | mc::cat_discr(cmt, _) => { self.scope(cmt) } @@ -322,10 +323,9 @@ impl<'a> GuaranteeLifetimeContext<'a> { } fn report_error(&self, code: bckerr_code) { - self.bccx.report(BckError { - cmt: self.cmt_original, - span: self.span, - code: code - }); + self.bccx.report(BckError { cmt: self.cmt_original, + span: self.span, + cause: self.cause, + code: code }); } } diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index 957073ac392..c6a77988bce 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -16,10 +16,10 @@ // their associated scopes. In phase two, checking loans, we will then make // sure that all of these loans are honored. - use middle::borrowck::*; use middle::borrowck::move_data::MoveData; use mc = middle::mem_categorization; +use middle::moves; use middle::pat_util; use middle::ty::{ty_region}; use middle::ty; @@ -28,6 +28,7 @@ use util::ppaux::{Repr}; use std::cell::RefCell; use syntax::ast; +use syntax::ast_util; use syntax::ast_util::IdRange; use syntax::codemap::Span; use syntax::print::pprust; @@ -127,22 +128,15 @@ fn add_pat_to_id_range(this: &mut GatherLoanCtxt, visit::walk_pat(this, p, ()); } -fn gather_loans_in_fn(this: &mut GatherLoanCtxt, fk: &FnKind, - decl: &ast::FnDecl, body: &ast::Block, - sp: Span, id: ast::NodeId) { - match fk { - &visit::FkItemFn(..) | &visit::FkMethod(..) => { - fail!("cannot occur, due to visit_item override"); - } - - // Visit closures as part of the containing item. - &visit::FkFnBlock(..) => { - this.push_repeating_id(body.id); - visit::walk_fn(this, fk, decl, body, sp, id, ()); - this.pop_repeating_id(body.id); - this.gather_fn_arg_patterns(decl, body); - } - } +fn gather_loans_in_fn(_v: &mut GatherLoanCtxt, + _fk: &FnKind, + _decl: &ast::FnDecl, + _body: &ast::Block, + _sp: Span, + _id: ast::NodeId) { + // Do not visit closures or fn items here, the outer loop in + // borrowck/mod will visit them for us in turn. + return; } fn gather_loans_in_block(this: &mut GatherLoanCtxt, @@ -232,8 +226,9 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt, this.guarantee_valid(ex.id, ex.span, base_cmt, - LoanMutability::from_ast_mutability(mutbl), - scope_r); + mutbl, + scope_r, + AddrOf); } visit::walk_expr(this, ex, ()); } @@ -278,8 +273,9 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt, this.guarantee_valid(arg.id, arg.span, arg_cmt, - ImmutableMutability, - scope_r); + ast::MutImmutable, + scope_r, + AutoRef); visit::walk_expr(this, ex, ()); } @@ -305,6 +301,7 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt, ast::ExprFnBlock(..) | ast::ExprProc(..) => { gather_moves::gather_captures(this.bccx, &this.move_data, ex); + this.guarantee_captures(ex); visit::walk_expr(this, ex, ()); } @@ -367,49 +364,48 @@ impl<'a> GatherLoanCtxt<'a> { ty::AutoDerefRef { autoref: Some(ref autoref), autoderefs: autoderefs}) => { - let mcx = &mc::mem_categorization_ctxt { - tcx: self.tcx(), - method_map: self.bccx.method_map}; - let cmt = mcx.cat_expr_autoderefd(expr, autoderefs); + let mut mc = self.bccx.mc(); + let cmt = match mc.cat_expr_autoderefd(expr, autoderefs) { + Ok(v) => v, + Err(()) => self.tcx().sess.span_bug(expr.span, "Err from mc") + }; debug!("after autoderef, cmt={}", cmt.repr(self.tcx())); match *autoref { ty::AutoPtr(r, m) => { - let loan_mutability = - LoanMutability::from_ast_mutability(m); self.guarantee_valid(expr.id, expr.span, cmt, - loan_mutability, - r) + m, + r, + AutoRef) } ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => { - let cmt_index = mcx.cat_index(expr, cmt, autoderefs+1); - let loan_mutability = - LoanMutability::from_ast_mutability(m); + let cmt_index = mc.cat_index(expr, cmt, autoderefs+1); self.guarantee_valid(expr.id, expr.span, cmt_index, - loan_mutability, - r) + m, + r, + AutoRef) } ty::AutoBorrowFn(r) => { - let cmt_deref = mcx.cat_deref_fn_or_obj(expr, cmt, 0); + let cmt_deref = mc.cat_deref_fn_or_obj(expr, cmt, 0); self.guarantee_valid(expr.id, expr.span, cmt_deref, - ImmutableMutability, - r) + ast::MutImmutable, + r, + AutoRef) } ty::AutoBorrowObj(r, m) => { - let cmt_deref = mcx.cat_deref_fn_or_obj(expr, cmt, 0); - let loan_mutability = - LoanMutability::from_ast_mutability(m); + let cmt_deref = mc.cat_deref_fn_or_obj(expr, cmt, 0); self.guarantee_valid(expr.id, expr.span, cmt_deref, - loan_mutability, - r) + m, + r, + AutoRef) } ty::AutoUnsafe(_) => {} } @@ -421,22 +417,71 @@ impl<'a> GatherLoanCtxt<'a> { } } - // Guarantees that addr_of(cmt) will be valid for the duration of - // `static_scope_r`, or reports an error. This may entail taking - // out loans, which will be added to the `req_loan_map`. This can - // also entail "rooting" GC'd pointers, which means ensuring - // dynamically that they are not freed. + fn guarantee_captures(&mut self, + closure_expr: &ast::Expr) { + let capture_map = self.bccx.capture_map.borrow(); + let captured_vars = capture_map.get().get(&closure_expr.id); + for captured_var in captured_vars.borrow().iter() { + match captured_var.mode { + moves::CapCopy | moves::CapMove => { continue; } + moves::CapRef => { } + } + + let var_id = ast_util::def_id_of_def(captured_var.def).node; + let var_cmt = self.bccx.cat_captured_var(closure_expr.id, + closure_expr.span, + captured_var); + + // Lookup the kind of borrow the callee requires + let upvar_id = ty::UpvarId { var_id: var_id, + closure_expr_id: closure_expr.id }; + let upvar_borrow_map = self.tcx().upvar_borrow_map.borrow(); + let upvar_borrow = upvar_borrow_map.get().get_copy(&upvar_id); + + self.guarantee_valid_kind(closure_expr.id, + closure_expr.span, + var_cmt, + upvar_borrow.kind, + upvar_borrow.region, + ClosureCapture(captured_var.span)); + } + } + pub fn guarantee_valid(&mut self, borrow_id: ast::NodeId, borrow_span: Span, cmt: mc::cmt, - req_mutbl: LoanMutability, - loan_region: ty::Region) { + req_mutbl: ast::Mutability, + loan_region: ty::Region, + cause: LoanCause) { + self.guarantee_valid_kind(borrow_id, + borrow_span, + cmt, + ty::BorrowKind::from_mutbl(req_mutbl), + loan_region, + cause); + } + + fn guarantee_valid_kind(&mut self, + borrow_id: ast::NodeId, + borrow_span: Span, + cmt: mc::cmt, + req_kind: ty::BorrowKind, + loan_region: ty::Region, + cause: LoanCause) { + /*! + * Guarantees that `addr_of(cmt)` will be valid for the duration of + * `static_scope_r`, or reports an error. This may entail taking + * out loans, which will be added to the `req_loan_map`. This can + * also entail "rooting" GC'd pointers, which means ensuring + * dynamically that they are not freed. + */ + debug!("guarantee_valid(borrow_id={:?}, cmt={}, \ req_mutbl={:?}, loan_region={:?})", borrow_id, cmt.repr(self.tcx()), - req_mutbl, + req_kind, loan_region); // a loan for the empty region can never be dereferenced, so @@ -450,26 +495,28 @@ impl<'a> GatherLoanCtxt<'a> { // Check that the lifetime of the borrow does not exceed // the lifetime of the data being borrowed. if lifetime::guarantee_lifetime(self.bccx, self.item_ub, root_ub, - borrow_span, cmt, loan_region, - req_mutbl).is_err() { + borrow_span, cause, cmt, loan_region, + req_kind).is_err() { return; // reported an error, no sense in reporting more. } // Check that we don't allow mutable borrows of non-mutable data. - if check_mutability(self.bccx, borrow_span, cmt, req_mutbl).is_err() { + if check_mutability(self.bccx, borrow_span, cause, + cmt, req_kind).is_err() { return; // reported an error, no sense in reporting more. } // Check that we don't allow mutable borrows of aliasable data. - if check_aliasability(self.bccx, borrow_span, cmt, req_mutbl).is_err() { + if check_aliasability(self.bccx, borrow_span, cause, + cmt, req_kind).is_err() { return; // reported an error, no sense in reporting more. } // Compute the restrictions that are required to enforce the // loan is safe. let restr = restrictions::compute_restrictions( - self.bccx, borrow_span, - cmt, loan_region, self.restriction_set(req_mutbl)); + self.bccx, borrow_span, cause, + cmt, loan_region, self.restriction_set(req_kind)); // Create the loan record (if needed). let loan = match restr { @@ -512,7 +559,7 @@ impl<'a> GatherLoanCtxt<'a> { let kill_scope = self.compute_kill_scope(loan_scope, loan_path); debug!("kill_scope = {:?}", kill_scope); - if req_mutbl == MutableMutability { + if req_kind == ty::MutBorrow { self.mark_loan_path_as_mutated(loan_path); } @@ -521,11 +568,12 @@ impl<'a> GatherLoanCtxt<'a> { index: all_loans.get().len(), loan_path: loan_path, cmt: cmt, - mutbl: req_mutbl, + kind: req_kind, gen_scope: gen_scope, kill_scope: kill_scope, span: borrow_span, - restrictions: restrictions + restrictions: restrictions, + cause: cause, } } }; @@ -568,23 +616,33 @@ impl<'a> GatherLoanCtxt<'a> { fn check_mutability(bccx: &BorrowckCtxt, borrow_span: Span, + cause: LoanCause, cmt: mc::cmt, - req_mutbl: LoanMutability) -> Result<(),()> { + req_kind: ty::BorrowKind) + -> Result<(),()> { //! Implements the M-* rules in doc.rs. - match req_mutbl { - ImmutableMutability => { - // both imm and mut data can be lent as imm; - // for mutable data, this is a freeze - Ok(()) + match req_kind { + ty::UniqueImmBorrow | ty::ImmBorrow => { + match cmt.mutbl { + // I am intentionally leaving this here to help + // refactoring if, in the future, we should add new + // kinds of mutability. + mc::McImmutable | mc::McDeclared | mc::McInherited => { + // both imm and mut data can be lent as imm; + // for mutable data, this is a freeze + Ok(()) + } + } } - MutableMutability => { + ty::MutBorrow => { // Only mutable data can be lent as mutable. if !cmt.mutbl.is_mutable() { - Err(bccx.report(BckError {span: borrow_span, - cmt: cmt, - code: err_mutbl(req_mutbl)})) + Err(bccx.report(BckError { span: borrow_span, + cause: cause, + cmt: cmt, + code: err_mutbl })) } else { Ok(()) } @@ -594,18 +652,18 @@ impl<'a> GatherLoanCtxt<'a> { fn check_aliasability(bccx: &BorrowckCtxt, borrow_span: Span, + loan_cause: LoanCause, cmt: mc::cmt, - req_mutbl: LoanMutability) -> Result<(),()> { + req_kind: ty::BorrowKind) + -> Result<(),()> { //! Implements the A-* rules in doc.rs. - match req_mutbl { - ImmutableMutability => { - // both imm and mut data can be lent as imm; - // for mutable data, this is a freeze + match req_kind { + ty::ImmBorrow => { Ok(()) } - MutableMutability => { + ty::UniqueImmBorrow | ty::MutBorrow => { // Check for those cases where we cannot control // the aliasing and make sure that we are not // being asked to. @@ -620,11 +678,11 @@ impl<'a> GatherLoanCtxt<'a> { // unsafe. At your own peril and all that. Ok(()) } - Some(cause) => { + Some(alias_cause) => { bccx.report_aliasability_violation( borrow_span, - BorrowViolation, - cause); + BorrowViolation(loan_cause), + alias_cause); Err(()) } } @@ -633,11 +691,18 @@ impl<'a> GatherLoanCtxt<'a> { } } - pub fn restriction_set(&self, req_mutbl: LoanMutability) - -> RestrictionSet { - match req_mutbl { - ImmutableMutability => RESTR_MUTATE | RESTR_CLAIM, - MutableMutability => RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE, + fn restriction_set(&self, req_kind: ty::BorrowKind) -> RestrictionSet { + match req_kind { + // If borrowing data as immutable, no mutation allowed: + ty::ImmBorrow => RESTR_MUTATE, + + // If borrowing data as mutable, no mutation nor other + // borrows allowed: + ty::MutBorrow => RESTR_MUTATE | RESTR_FREEZE, + + // If borrowing data as unique imm, no mutation nor other + // borrows allowed: + ty::UniqueImmBorrow => RESTR_MUTATE | RESTR_FREEZE, } } @@ -719,11 +784,11 @@ impl<'a> GatherLoanCtxt<'a> { * `gather_pat()`. */ - let mc_ctxt = self.bccx.mc_ctxt(); + let mut mc = self.bccx.mc(); for arg in decl.inputs.iter() { let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id); - let arg_cmt = mc_ctxt.cat_rvalue( + let arg_cmt = mc.cat_rvalue( arg.id, arg.pat.span, ty::ReScope(body.id), // Args live only as long as the fn body. @@ -735,7 +800,7 @@ impl<'a> GatherLoanCtxt<'a> { fn gather_pat(&mut self, discr_cmt: mc::cmt, - root_pat: &ast::Pat, + root_pat: @ast::Pat, arm_match_ids: Option<(ast::NodeId, ast::NodeId)>) { /*! * Walks patterns, examining the bindings to determine if they @@ -774,13 +839,12 @@ impl<'a> GatherLoanCtxt<'a> { } } }; - let loan_mutability = - LoanMutability::from_ast_mutability(mutbl); self.guarantee_valid(pat.id, pat.span, cmt_discr, - loan_mutability, - scope_r); + mutbl, + scope_r, + RefBinding); } ast::BindByValue(_) => { // No borrows here, but there may be moves @@ -797,14 +861,15 @@ impl<'a> GatherLoanCtxt<'a> { // original vector. This is effectively a borrow of // the elements of the vector being matched. - let slice_ty = ty::node_id_to_type(self.tcx(), - slice_pat.id); - let (slice_mutbl, slice_r) = - self.vec_slice_info(slice_pat, slice_ty); - let mcx = self.bccx.mc_ctxt(); - let cmt_index = mcx.cat_index(slice_pat, cmt, 0); - let slice_loan_mutability = - LoanMutability::from_ast_mutability(slice_mutbl); + let (slice_cmt, slice_borrow_kind, slice_r) = { + match self.bccx.mc().cat_slice_pattern(cmt, slice_pat) { + Ok(v) => v, + Err(()) => { + self.tcx().sess.span_bug(slice_pat.span, + "Err from mc") + } + } + }; // Note: We declare here that the borrow occurs upon // entering the `[...]` pattern. This implies that @@ -823,11 +888,9 @@ impl<'a> GatherLoanCtxt<'a> { // trans do the right thing, and it would only work // for `~` vectors. It seems simpler to just require // that people call `vec.pop()` or `vec.unshift()`. - self.guarantee_valid(pat.id, - pat.span, - cmt_index, - slice_loan_mutability, - slice_r); + self.guarantee_valid(pat.id, pat.span, + slice_cmt, slice_borrow_kind, slice_r, + RefBinding); } _ => {} @@ -835,33 +898,6 @@ impl<'a> GatherLoanCtxt<'a> { }) } - pub fn vec_slice_info(&self, pat: &ast::Pat, slice_ty: ty::t) - -> (ast::Mutability, ty::Region) { - /*! - * - * In a pattern like [a, b, ..c], normally `c` has slice type, - * but if you have [a, b, ..ref c], then the type of `ref c` - * will be `&&[]`, so to extract the slice details we have - * to recurse through rptrs. - */ - - match ty::get(slice_ty).sty { - ty::ty_vec(slice_mt, ty::vstore_slice(slice_r)) => { - (slice_mt.mutbl, slice_r) - } - - ty::ty_rptr(_, ref mt) => { - self.vec_slice_info(pat, mt.ty) - } - - _ => { - self.tcx().sess.span_bug( - pat.span, - format!("type of slice pattern is not a slice")); - } - } - } - pub fn pat_is_binding(&self, pat: &ast::Pat) -> bool { pat_util::pat_is_binding(self.bccx.tcx.def_map, pat) } diff --git a/src/librustc/middle/borrowck/gather_loans/restrictions.rs b/src/librustc/middle/borrowck/gather_loans/restrictions.rs index 60e0baa3534..575119ba690 100644 --- a/src/librustc/middle/borrowck/gather_loans/restrictions.rs +++ b/src/librustc/middle/borrowck/gather_loans/restrictions.rs @@ -16,8 +16,8 @@ use std::vec; use middle::borrowck::*; use mc = middle::mem_categorization; use middle::ty; -use syntax::ast::{MutImmutable, MutMutable}; use syntax::codemap::Span; +use util::ppaux::Repr; pub enum RestrictionResult { Safe, @@ -26,12 +26,14 @@ pub enum RestrictionResult { pub fn compute_restrictions(bccx: &BorrowckCtxt, span: Span, + cause: LoanCause, cmt: mc::cmt, loan_region: ty::Region, restr: RestrictionSet) -> RestrictionResult { let ctxt = RestrictionsContext { bccx: bccx, span: span, + cause: cause, cmt_original: cmt, loan_region: loan_region, }; @@ -47,12 +49,17 @@ struct RestrictionsContext<'a> { span: Span, cmt_original: mc::cmt, loan_region: ty::Region, + cause: LoanCause, } impl<'a> RestrictionsContext<'a> { fn restrict(&self, cmt: mc::cmt, restrictions: RestrictionSet) -> RestrictionResult { + debug!("restrict(cmt={}, restrictions={})", + cmt.repr(self.bccx.tcx), + restrictions.repr(self.bccx.tcx)); + match cmt.cat { mc::cat_rvalue(..) => { // Effectively, rvalues are stored into a @@ -64,7 +71,8 @@ impl<'a> RestrictionsContext<'a> { } mc::cat_local(local_id) | - mc::cat_arg(local_id) => { + mc::cat_arg(local_id) | + mc::cat_upvar(ty::UpvarId {var_id: local_id, ..}, _) => { // R-Variable let lp = @LpVar(local_id); SafeIf(lp, ~[Restriction {loan_path: lp, @@ -77,7 +85,7 @@ impl<'a> RestrictionsContext<'a> { // could cause the type of the memory to change. self.restrict( cmt_base, - restrictions | RESTR_MUTATE | RESTR_CLAIM) + restrictions | RESTR_MUTATE) } mc::cat_interior(cmt_base, i) => { @@ -90,7 +98,7 @@ impl<'a> RestrictionsContext<'a> { self.extend(result, cmt.mutbl, LpInterior(i), restrictions) } - mc::cat_deref(cmt_base, _, pk @ mc::uniq_ptr) => { + mc::cat_deref(cmt_base, _, pk @ mc::OwnedPtr) => { // R-Deref-Send-Pointer // // When we borrow the interior of an owned pointer, we @@ -98,7 +106,7 @@ impl<'a> RestrictionsContext<'a> { // would cause the unique pointer to be freed. let result = self.restrict( cmt_base, - restrictions | RESTR_MUTATE | RESTR_CLAIM); + restrictions | RESTR_MUTATE); self.extend(result, cmt.mutbl, LpDeref(pk), restrictions) } @@ -107,12 +115,14 @@ impl<'a> RestrictionsContext<'a> { Safe } - mc::cat_deref(cmt_base, _, mc::region_ptr(MutImmutable, lt)) => { + mc::cat_deref(cmt_base, _, mc::BorrowedPtr(ty::ImmBorrow, lt)) | + mc::cat_deref(cmt_base, _, mc::BorrowedPtr(ty::UniqueImmBorrow, lt)) => { // R-Deref-Imm-Borrowed if !self.bccx.is_subregion_of(self.loan_region, lt) { self.bccx.report( BckError { span: self.span, + cause: self.cause, cmt: cmt_base, code: err_borrowed_pointer_too_short( self.loan_region, lt, restrictions)}); @@ -121,17 +131,18 @@ impl<'a> RestrictionsContext<'a> { Safe } - mc::cat_deref(_, _, mc::gc_ptr) => { + mc::cat_deref(_, _, mc::GcPtr) => { // R-Deref-Imm-Managed Safe } - mc::cat_deref(cmt_base, _, pk @ mc::region_ptr(MutMutable, lt)) => { + mc::cat_deref(cmt_base, _, pk @ mc::BorrowedPtr(ty::MutBorrow, lt)) => { // R-Deref-Mut-Borrowed if !self.bccx.is_subregion_of(self.loan_region, lt) { self.bccx.report( BckError { span: self.span, + cause: self.cause, cmt: cmt_base, code: err_borrowed_pointer_too_short( self.loan_region, lt, restrictions)}); @@ -142,12 +153,11 @@ impl<'a> RestrictionsContext<'a> { self.extend(result, cmt.mutbl, LpDeref(pk), restrictions) } - mc::cat_deref(_, _, mc::unsafe_ptr(..)) => { + mc::cat_deref(_, _, mc::UnsafePtr(..)) => { // We are very trusting when working with unsafe pointers. Safe } - mc::cat_stack_upvar(cmt_base) | mc::cat_discr(cmt_base, _) => { self.restrict(cmt_base, restrictions) } diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 6a3e2fc63b0..acc8ece85f8 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -120,41 +120,32 @@ fn borrowck_fn(this: &mut BorrowckCtxt, body: &ast::Block, sp: Span, id: ast::NodeId) { - match fk { - &visit::FkFnBlock(..) => { - // Closures are checked as part of their containing fn item. - } + debug!("borrowck_fn(id={})", id); - &visit::FkItemFn(..) | &visit::FkMethod(..) => { - debug!("borrowck_fn(id={:?})", id); - - // Check the body of fn items. - let (id_range, all_loans, move_data) = - gather_loans::gather_loans(this, decl, body); - - let all_loans = all_loans.borrow(); - let mut loan_dfcx = DataFlowContext::new(this.tcx, - this.method_map, - LoanDataFlowOperator, - id_range, - all_loans.get().len()); - for (loan_idx, loan) in all_loans.get().iter().enumerate() { - loan_dfcx.add_gen(loan.gen_scope, loan_idx); - loan_dfcx.add_kill(loan.kill_scope, loan_idx); - } - - loan_dfcx.propagate(body); - - let flowed_moves = move_data::FlowedMoveData::new(move_data, - this.tcx, - this.method_map, - id_range, - body); - - check_loans::check_loans(this, &loan_dfcx, flowed_moves, - *all_loans.get(), body); - } + // Check the body of fn items. + let (id_range, all_loans, move_data) = + gather_loans::gather_loans(this, decl, body); + let all_loans = all_loans.borrow(); + let mut loan_dfcx = + DataFlowContext::new(this.tcx, + this.method_map, + LoanDataFlowOperator, + id_range, + all_loans.get().len()); + for (loan_idx, loan) in all_loans.get().iter().enumerate() { + loan_dfcx.add_gen(loan.gen_scope, loan_idx); + loan_dfcx.add_kill(loan.kill_scope, loan_idx); } + loan_dfcx.propagate(body); + + let flowed_moves = move_data::FlowedMoveData::new(move_data, + this.tcx, + this.method_map, + id_range, + body); + + check_loans::check_loans(this, &loan_dfcx, flowed_moves, + *all_loans.get(), body); visit::walk_fn(this, fk, decl, body, sp, id, ()); } @@ -211,41 +202,25 @@ pub enum PartialTotal { /////////////////////////////////////////////////////////////////////////// // Loans and loan paths -#[deriving(Clone, Eq)] -pub enum LoanMutability { - ImmutableMutability, - MutableMutability, -} - -impl LoanMutability { - pub fn from_ast_mutability(ast_mutability: ast::Mutability) - -> LoanMutability { - match ast_mutability { - ast::MutImmutable => ImmutableMutability, - ast::MutMutable => MutableMutability, - } - } -} - -impl ToStr for LoanMutability { - fn to_str(&self) -> ~str { - match *self { - ImmutableMutability => ~"immutable", - MutableMutability => ~"mutable", - } - } -} - /// Record of a loan that was issued. pub struct Loan { index: uint, loan_path: @LoanPath, cmt: mc::cmt, - mutbl: LoanMutability, + kind: ty::BorrowKind, restrictions: ~[Restriction], gen_scope: ast::NodeId, kill_scope: ast::NodeId, span: Span, + cause: LoanCause, +} + +#[deriving(Eq)] +pub enum LoanCause { + ClosureCapture(Span), + AddrOf, + AutoRef, + RefBinding, } #[deriving(Eq, IterBytes)] @@ -283,7 +258,9 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> { None } - mc::cat_local(id) | mc::cat_arg(id) => { + mc::cat_local(id) | + mc::cat_arg(id) | + mc::cat_upvar(ty::UpvarId {var_id: id, ..}, _) => { Some(@LpVar(id)) } @@ -300,7 +277,6 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> { } mc::cat_downcast(cmt_base) | - mc::cat_stack_upvar(cmt_base) | mc::cat_discr(cmt_base, _) => { opt_loan_path(cmt_base) } @@ -313,8 +289,7 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> { // Borrowing an lvalue often results in *restrictions* that limit what // can be done with this lvalue during the scope of the loan: // -// - `RESTR_MUTATE`: The lvalue may not be modified. -// - `RESTR_CLAIM`: `&mut` borrows of the lvalue are forbidden. +// - `RESTR_MUTATE`: The lvalue may not be modified or `&mut` borrowed. // - `RESTR_FREEZE`: `&` borrows of the lvalue are forbidden. // // In addition, no value which is restricted may be moved. Therefore, @@ -333,8 +308,7 @@ pub struct RestrictionSet { pub static RESTR_EMPTY: RestrictionSet = RestrictionSet {bits: 0b0000}; pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b0001}; -pub static RESTR_CLAIM: RestrictionSet = RestrictionSet {bits: 0b0010}; -pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0100}; +pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0010}; impl RestrictionSet { pub fn intersects(&self, restr: RestrictionSet) -> bool { @@ -358,6 +332,12 @@ impl BitAnd for RestrictionSet { } } +impl Repr for RestrictionSet { + fn repr(&self, _tcx: ty::ctxt) -> ~str { + format!("RestrictionSet(0x{:x})", self.bits as uint) + } +} + /////////////////////////////////////////////////////////////////////////// // Rooting of managed boxes // @@ -393,10 +373,9 @@ pub fn root_map() -> root_map { // Errors that can occur #[deriving(Eq)] pub enum bckerr_code { - err_mutbl(LoanMutability), + err_mutbl, err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope err_out_of_scope(ty::Region, ty::Region), // superscope, subscope - err_freeze_aliasable_const, err_borrowed_pointer_too_short( ty::Region, ty::Region, RestrictionSet), // loan, ptr } @@ -406,13 +385,14 @@ pub enum bckerr_code { #[deriving(Eq)] pub struct BckError { span: Span, + cause: LoanCause, cmt: mc::cmt, code: bckerr_code } pub enum AliasableViolationKind { MutabilityViolation, - BorrowViolation + BorrowViolation(LoanCause) } pub enum MovedValueUseKind { @@ -439,29 +419,55 @@ impl BorrowckCtxt { moves_map.get().contains(&id) } + pub fn mc(&self) -> mc::MemCategorizationContext { + mc::MemCategorizationContext { + typer: TcxTyper { + tcx: self.tcx, + method_map: self.method_map + } + } + } + pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt { - mc::cat_expr(self.tcx, self.method_map, expr) + match self.mc().cat_expr(expr) { + Ok(c) => c, + Err(()) => { + self.tcx.sess.span_bug(expr.span, "error in mem categorization"); + } + } } pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> mc::cmt { - mc::cat_expr_unadjusted(self.tcx, self.method_map, expr) + match self.mc().cat_expr_unadjusted(expr) { + Ok(c) => c, + Err(()) => { + self.tcx.sess.span_bug(expr.span, "error in mem categorization"); + } + } } pub fn cat_expr_autoderefd(&self, expr: &ast::Expr, adj: &ty::AutoAdjustment) -> mc::cmt { - match *adj { + let r = match *adj { ty::AutoAddEnv(..) | ty::AutoObject(..) => { // no autoderefs - mc::cat_expr_unadjusted(self.tcx, self.method_map, expr) + self.mc().cat_expr_unadjusted(expr) } ty::AutoDerefRef( ty::AutoDerefRef { autoderefs: autoderefs, ..}) => { - mc::cat_expr_autoderefd(self.tcx, self.method_map, expr, - autoderefs) + self.mc().cat_expr_autoderefd(expr, autoderefs) + } + }; + + match r { + Ok(c) => c, + Err(()) => { + self.tcx.sess.span_bug(expr.span, + "error in mem categorization"); } } } @@ -472,7 +478,23 @@ impl BorrowckCtxt { ty: ty::t, def: ast::Def) -> mc::cmt { - mc::cat_def(self.tcx, self.method_map, id, span, ty, def) + match self.mc().cat_def(id, span, ty, def) { + Ok(c) => c, + Err(()) => { + self.tcx.sess.span_bug(span, "error in mem categorization"); + } + } + } + + pub fn cat_captured_var(&self, + id: ast::NodeId, + span: Span, + captured_var: &moves::CaptureVar) -> mc::cmt { + // Create the cmt for the variable being borrowed, from the + // caller's perspective + let var_id = ast_util::def_id_of_def(captured_var.def).node; + let var_ty = ty::node_id_to_type(self.tcx, var_id); + self.cat_def(id, span, var_ty, captured_var.def) } pub fn cat_discr(&self, cmt: mc::cmt, match_id: ast::NodeId) -> mc::cmt { @@ -481,17 +503,12 @@ impl BorrowckCtxt { ..*cmt} } - pub fn mc_ctxt(&self) -> mc::mem_categorization_ctxt { - mc::mem_categorization_ctxt {tcx: self.tcx, - method_map: self.method_map} - } - pub fn cat_pattern(&self, cmt: mc::cmt, - pat: &ast::Pat, + pat: @ast::Pat, op: |mc::cmt, &ast::Pat|) { - let mc = self.mc_ctxt(); - mc.cat_pattern(cmt, pat, op); + let r = self.mc().cat_pattern(cmt, pat, |_,x,y| op(x,y)); + assert!(r.is_ok()); } pub fn report(&self, err: BckError) { @@ -622,24 +639,35 @@ impl BorrowckCtxt { pub fn bckerr_to_str(&self, err: BckError) -> ~str { match err.code { - err_mutbl(lk) => { - format!("cannot borrow {} {} as {}", - err.cmt.mutbl.to_user_str(), - self.cmt_to_str(err.cmt), - self.mut_to_str(lk)) + err_mutbl => { + let descr = match opt_loan_path(err.cmt) { + None => format!("{} {}", + err.cmt.mutbl.to_user_str(), + self.cmt_to_str(err.cmt)), + Some(lp) => format!("{} {} `{}`", + err.cmt.mutbl.to_user_str(), + self.cmt_to_str(err.cmt), + self.loan_path_to_str(lp)), + }; + + match err.cause { + ClosureCapture(_) => { + format!("closure cannot assign to {}", descr) + } + AddrOf | RefBinding | AutoRef => { + format!("cannot borrow {} as mutable", descr) + } + } } err_out_of_root_scope(..) => { format!("cannot root managed value long enough") } err_out_of_scope(..) => { - format!("borrowed value does not live long enough") - } - err_freeze_aliasable_const => { - // Means that the user borrowed a ~T or enum value - // residing in &const or @const pointer. Terrible - // error message, but then &const and @const are - // supposed to be going away. - format!("unsafe borrow of aliasable, const value") + let msg = match opt_loan_path(err.cmt) { + None => format!("borrowed value"), + Some(lp) => format!("`{}`", self.loan_path_to_str(lp)), + }; + format!("{} does not live long enough", msg) } err_borrowed_pointer_too_short(..) => { let descr = match opt_loan_path(err.cmt) { @@ -659,8 +687,24 @@ impl BorrowckCtxt { kind: AliasableViolationKind, cause: mc::AliasableReason) { let prefix = match kind { - MutabilityViolation => "cannot assign to data", - BorrowViolation => "cannot borrow data mutably" + MutabilityViolation => { + "cannot assign to data" + } + BorrowViolation(ClosureCapture(_)) => { + // I don't think we can get aliasability violations + // with closure captures, so no need to come up with a + // good error message. The reason this cannot happen + // is because we only capture local variables in + // closures, and those are never aliasable. + self.tcx.sess.span_bug( + span, + "aliasability violation with closure"); + } + BorrowViolation(AddrOf) | + BorrowViolation(AutoRef) | + BorrowViolation(RefBinding) => { + "cannot borrow data mutably" + } }; match cause { @@ -680,7 +724,7 @@ impl BorrowckCtxt { span, format!("{} in a `@` pointer", prefix)); } - mc::AliasableBorrowed(_) => { + mc::AliasableBorrowed => { self.tcx.sess.span_err( span, format!("{} in a `&` reference", prefix)); @@ -691,7 +735,7 @@ impl BorrowckCtxt { pub fn note_and_explain_bckerr(&self, err: BckError) { let code = err.code; match code { - err_mutbl(..) | err_freeze_aliasable_const(..) => {} + err_mutbl(..) => { } err_out_of_root_scope(super_scope, sub_scope) => { note_and_explain_region( @@ -738,52 +782,16 @@ impl BorrowckCtxt { } } - pub fn append_loan_path_to_str_from_interior(&self, - loan_path: &LoanPath, - out: &mut ~str) { - match *loan_path { - LpExtend(_, _, LpDeref(_)) => { - out.push_char('('); - self.append_loan_path_to_str(loan_path, out); - out.push_char(')'); - } - LpExtend(_, _, LpInterior(_)) | - LpVar(_) => { - self.append_loan_path_to_str(loan_path, out); - } - } - } - pub fn append_loan_path_to_str(&self, loan_path: &LoanPath, out: &mut ~str) { match *loan_path { LpVar(id) => { - match self.tcx.items.find(id) { - Some(ast_map::NodeLocal(pat)) => { - match pat.node { - ast::PatIdent(_, ref path, _) => { - let ident = ast_util::path_to_ident(path); - let string = token::get_ident(ident.name); - out.push_str(string.get()); - } - _ => { - self.tcx.sess.bug( - format!("loan path LpVar({:?}) maps to {:?}, not local", - id, pat)); - } - } - } - r => { - self.tcx.sess.bug( - format!("loan path LpVar({:?}) maps to {:?}, not local", - id, r)); - } - } + out.push_str(ty::local_var_name_str(self.tcx, id).get()); } LpExtend(lp_base, _, LpInterior(mc::InteriorField(fname))) => { - self.append_loan_path_to_str_from_interior(lp_base, out); + self.append_autoderefd_loan_path_to_str(lp_base, out); match fname { mc::NamedField(ref fname) => { let string = token::get_ident(*fname); @@ -798,8 +806,8 @@ impl BorrowckCtxt { } LpExtend(lp_base, _, LpInterior(mc::InteriorElement(_))) => { - self.append_loan_path_to_str_from_interior(lp_base, out); - out.push_str("[]"); + self.append_autoderefd_loan_path_to_str(lp_base, out); + out.push_str("[..]"); } LpExtend(lp_base, _, LpDeref(_)) => { @@ -809,6 +817,23 @@ impl BorrowckCtxt { } } + pub fn append_autoderefd_loan_path_to_str(&self, + loan_path: &LoanPath, + out: &mut ~str) { + match *loan_path { + LpExtend(lp_base, _, LpDeref(_)) => { + // For a path like `(*x).f` or `(*x)[3]`, autoderef + // rules would normally allow users to omit the `*x`. + // So just serialize such paths to `x.f` or x[3]` respectively. + self.append_autoderefd_loan_path_to_str(lp_base, out) + } + + LpVar(..) | LpExtend(_, _, LpInterior(..)) => { + self.append_loan_path_to_str(loan_path, out) + } + } + } + pub fn loan_path_to_str(&self, loan_path: &LoanPath) -> ~str { let mut result = ~""; self.append_loan_path_to_str(loan_path, &mut result); @@ -816,13 +841,11 @@ impl BorrowckCtxt { } pub fn cmt_to_str(&self, cmt: mc::cmt) -> ~str { - let mc = &mc::mem_categorization_ctxt {tcx: self.tcx, - method_map: self.method_map}; - mc.cmt_to_str(cmt) + self.mc().cmt_to_str(cmt) } - pub fn mut_to_str(&self, mutbl: LoanMutability) -> ~str { - mutbl.to_str() + pub fn mut_to_str(&self, mutbl: ast::Mutability) -> ~str { + self.mc().mut_to_str(mutbl) } pub fn mut_to_keyword(&self, mutbl: ast::Mutability) -> &'static str { @@ -843,11 +866,6 @@ impl DataFlowOperator for LoanDataFlowOperator { fn join(&self, succ: uint, pred: uint) -> uint { succ | pred // loans from both preds are in scope } - - #[inline] - fn walk_closures(&self) -> bool { - true - } } impl Repr for Loan { @@ -855,7 +873,7 @@ impl Repr for Loan { format!("Loan_{:?}({}, {:?}, {:?}-{:?}, {})", self.index, self.loan_path.repr(tcx), - self.mutbl, + self.kind, self.gen_scope, self.kill_scope, self.restrictions.repr(tcx)) @@ -890,3 +908,39 @@ impl Repr for LoanPath { } } } + +/////////////////////////////////////////////////////////////////////////// + +struct TcxTyper { + tcx: ty::ctxt, + method_map: typeck::method_map, +} + +impl mc::Typer for TcxTyper { + fn tcx(&self) -> ty::ctxt { + self.tcx + } + + fn node_ty(&mut self, id: ast::NodeId) -> mc::McResult { + Ok(ty::node_id_to_type(self.tcx, id)) + } + + fn adjustment(&mut self, id: ast::NodeId) -> Option<@ty::AutoAdjustment> { + let adjustments = self.tcx.adjustments.borrow(); + adjustments.get().find_copy(&id) + } + + fn is_method_call(&mut self, id: ast::NodeId) -> bool { + let method_map = self.method_map.borrow(); + method_map.get().contains_key(&id) + } + + fn temporary_scope(&mut self, id: ast::NodeId) -> Option { + self.tcx.region_maps.temporary_scope(id) + } + + fn upvar_borrow(&mut self, id: ty::UpvarId) -> ty::UpvarBorrow { + let upvar_borrow_map = self.tcx.upvar_borrow_map.borrow(); + upvar_borrow_map.get().get_copy(&id) + } +} diff --git a/src/librustc/middle/borrowck/move_data.rs b/src/librustc/middle/borrowck/move_data.rs index 75549c5944d..34efcacc44b 100644 --- a/src/librustc/middle/borrowck/move_data.rs +++ b/src/librustc/middle/borrowck/move_data.rs @@ -722,11 +722,6 @@ impl DataFlowOperator for MoveDataFlowOperator { fn join(&self, succ: uint, pred: uint) -> uint { succ | pred // moves from both preds are in scope } - - #[inline] - fn walk_closures(&self) -> bool { - true - } } impl DataFlowOperator for AssignDataFlowOperator { @@ -739,9 +734,4 @@ impl DataFlowOperator for AssignDataFlowOperator { fn join(&self, succ: uint, pred: uint) -> uint { succ | pred // moves from both preds are in scope } - - #[inline] - fn walk_closures(&self) -> bool { - true - } } From 1bd7b182c5edeb33c8164cfc99d1f89ad849057e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Feb 2014 14:36:13 -0500 Subject: [PATCH 30/33] dataflow -- do not consider the interprocedural case --- src/librustc/middle/dataflow.rs | 143 +++++--------------------------- 1 file changed, 23 insertions(+), 120 deletions(-) diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index e4b648dd43c..5af5aa63e1d 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -17,7 +17,6 @@ */ -use std::cast; use std::io; use std::uint; use std::vec; @@ -72,9 +71,6 @@ pub trait DataFlowOperator { /// Joins two predecessor bits together, typically either `|` or `&` fn join(&self, succ: uint, pred: uint) -> uint; - - /// True if we should propagate through closures - fn walk_closures(&self) -> bool; } struct PropagationContext<'a, O> { @@ -373,8 +369,8 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { blk: &ast::Block, in_out: &mut [uint], loop_scopes: &mut ~[LoopScope]) { - debug!("DataFlowContext::walk_block(blk.id={:?}, in_out={})", - blk.id, bits_to_str(reslice(in_out))); + debug!("DataFlowContext::walk_block(blk.id={}, in_out={})", + blk.id, bits_to_str(in_out)); self.merge_with_entry_set(blk.id, in_out); @@ -425,99 +421,12 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { in_out: &mut [uint], loop_scopes: &mut ~[LoopScope]) { debug!("DataFlowContext::walk_expr(expr={}, in_out={})", - expr.repr(self.dfcx.tcx), bits_to_str(reslice(in_out))); + expr.repr(self.dfcx.tcx), bits_to_str(in_out)); self.merge_with_entry_set(expr.id, in_out); match expr.node { - ast::ExprFnBlock(ref decl, body) | - ast::ExprProc(ref decl, body) => { - if self.dfcx.oper.walk_closures() { - // In the absence of once fns, we must assume that - // every function body will execute more than - // once. Thus we treat every function body like a - // loop. - // - // What is subtle and a bit tricky, also, is how - // to deal with the "output" bits---that is, what - // do we consider to be the successor of a - // function body, given that it could be called - // from any point within its lifetime? What we do - // is to add their effects immediately as of the - // point of creation. Of course we have to ensure - // that this is sound for the analyses which make - // use of dataflow. - // - // In the case of the initedness checker (which - // does not currently use dataflow, but I hope to - // convert at some point), we will simply not walk - // closures at all, so it's a moot point. - // - // In the case of the borrow checker, this means - // the loans which would be created by calling a - // function come into effect immediately when the - // function is created. This is guaranteed to be - // earlier than the point at which the loan - // actually comes into scope (which is the point - // at which the closure is *called*). Because - // loans persist until the scope of the loans is - // exited, it is always a safe approximation to - // have a loan begin earlier than it actually will - // at runtime, so this should be sound. - // - // We stil have to be careful in the region - // checker and borrow checker to treat function - // bodies like loops, which implies some - // limitations. For example, a closure cannot root - // a managed box for longer than its body. - // - // General control flow looks like this: - // - // +- (expr) <----------+ - // | | | - // | v | - // | (body) -----------+--> (exit) - // | | | - // | + (break/loop) -+ - // | | - // +--------------------+ - // - // This is a bit more conservative than a loop. - // Note that we must assume that even after a - // `break` occurs (e.g., in a `for` loop) that the - // closure may be reinvoked. - // - // One difference from other loops is that `loop` - // and `break` statements which target a closure - // both simply add to the `break_bits`. - - // func_bits represents the state when the function - // returns - let mut func_bits = reslice(in_out).to_owned(); - - loop_scopes.push(LoopScope { - loop_id: expr.id, - break_bits: reslice(in_out).to_owned() - }); - for input in decl.inputs.iter() { - self.walk_pat(input.pat, func_bits, loop_scopes); - } - self.walk_block(body, func_bits, loop_scopes); - - // add the bits from any early return via `break`, - // `continue`, or `return` into `func_bits` - let loop_scope = loop_scopes.pop().unwrap(); - join_bits(&self.dfcx.oper, loop_scope.break_bits, func_bits); - - // add `func_bits` to the entry bits for `expr`, - // since we must assume the function may be called - // more than once - self.add_to_entry_set(expr.id, reslice(func_bits)); - - // the final exit bits include whatever was present - // in the original, joined with the bits from the function - join_bits(&self.dfcx.oper, func_bits, in_out); - } + ast::ExprFnBlock(..) | ast::ExprProc(..) => { } ast::ExprIf(cond, then, els) => { @@ -536,7 +445,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { // self.walk_expr(cond, in_out, loop_scopes); - let mut then_bits = reslice(in_out).to_owned(); + let mut then_bits = in_out.to_owned(); self.walk_block(then, then_bits, loop_scopes); self.walk_opt_expr(els, in_out, loop_scopes); @@ -558,10 +467,10 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { self.walk_expr(cond, in_out, loop_scopes); - let mut body_bits = reslice(in_out).to_owned(); + let mut body_bits = in_out.to_owned(); loop_scopes.push(LoopScope { loop_id: expr.id, - break_bits: reslice(in_out).to_owned() + break_bits: in_out.to_owned() }); self.walk_block(blk, body_bits, loop_scopes); self.add_to_entry_set(expr.id, body_bits); @@ -581,11 +490,11 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { // <--+ (break) // - let mut body_bits = reslice(in_out).to_owned(); + let mut body_bits = in_out.to_owned(); self.reset(in_out); loop_scopes.push(LoopScope { loop_id: expr.id, - break_bits: reslice(in_out).to_owned() + break_bits: in_out.to_owned() }); self.walk_block(blk, body_bits, loop_scopes); self.add_to_entry_set(expr.id, body_bits); @@ -609,7 +518,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { // self.walk_expr(discr, in_out, loop_scopes); - let mut guards = reslice(in_out).to_owned(); + let mut guards = in_out.to_owned(); // We know that exactly one arm will be taken, so we // can start out with a blank slate and just union @@ -622,7 +531,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { // determine the bits for the body and then union // them into `in_out`, which reflects all bodies to date - let mut body = reslice(guards).to_owned(); + let mut body = guards.to_owned(); self.walk_pat_alternatives(arm.pats, body, loop_scopes); self.walk_block(arm.body, body, loop_scopes); join_bits(&self.dfcx.oper, body, in_out); @@ -643,7 +552,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { ast::ExprAgain(label) => { let scope = self.find_scope(expr, label, loop_scopes); self.pop_scopes(expr, scope, in_out); - self.add_to_entry_set(scope.loop_id, reslice(in_out)); + self.add_to_entry_set(scope.loop_id, in_out); self.reset(in_out); } @@ -693,7 +602,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { ast::ExprBinary(_, op, l, r) if ast_util::lazy_binop(op) => { self.walk_expr(l, in_out, loop_scopes); - let temp = reslice(in_out).to_owned(); + let temp = in_out.to_owned(); self.walk_expr(r, in_out, loop_scopes); join_bits(&self.dfcx.oper, temp, in_out); } @@ -756,7 +665,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { debug!("pop_scopes(from_expr={}, to_scope={:?}, in_out={})", from_expr.repr(tcx), to_scope.loop_id, - bits_to_str(reslice(in_out))); + bits_to_str(in_out)); let mut id = from_expr.id; while id != to_scope.loop_id { @@ -781,11 +690,11 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { in_out: &mut [uint]) { self.pop_scopes(from_expr, to_scope, in_out); self.dfcx.apply_kill(from_expr.id, in_out); - join_bits(&self.dfcx.oper, reslice(in_out), to_scope.break_bits); - debug!("break_from_to(from_expr={}, to_scope={:?}) final break_bits={}", + join_bits(&self.dfcx.oper, in_out, to_scope.break_bits); + debug!("break_from_to(from_expr={}, to_scope={}) final break_bits={}", from_expr.repr(self.tcx()), to_scope.loop_id, - bits_to_str(reslice(in_out))); + bits_to_str(in_out)); } fn walk_exprs(&mut self, @@ -830,10 +739,10 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { in_out: &mut [uint], _loop_scopes: &mut ~[LoopScope]) { debug!("DataFlowContext::walk_pat(pat={}, in_out={})", - pat.repr(self.dfcx.tcx), bits_to_str(reslice(in_out))); + pat.repr(self.dfcx.tcx), bits_to_str(in_out)); ast_util::walk_pat(pat, |p| { - debug!(" p.id={:?} in_out={}", p.id, bits_to_str(reslice(in_out))); + debug!(" p.id={} in_out={}", p.id, bits_to_str(in_out)); self.merge_with_entry_set(p.id, in_out); self.dfcx.apply_gen_kill(p.id, in_out); true @@ -852,7 +761,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { // In the general case, the patterns in `pats` are // alternatives, so we must treat this like an N-way select // statement. - let initial_state = reslice(in_out).to_owned(); + let initial_state = in_out.to_owned(); for &pat in pats.iter() { let mut temp = initial_state.clone(); self.walk_pat(pat, temp, loop_scopes); @@ -929,8 +838,8 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { let (start, end) = self.dfcx.compute_id_range(id); let changed = { // FIXME(#5074) awkward construction let on_entry = self.dfcx.on_entry.mut_slice(start, end); - let changed = join_bits(&self.dfcx.oper, reslice(pred_bits), on_entry); - copy_bits(reslice(on_entry), pred_bits); + let changed = join_bits(&self.dfcx.oper, pred_bits, on_entry); + copy_bits(on_entry, pred_bits); changed }; if changed { @@ -942,7 +851,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { } fn mut_bits_to_str(words: &mut [uint]) -> ~str { - bits_to_str(reslice(words)) + bits_to_str(words) } fn bits_to_str(words: &[uint]) -> ~str { @@ -1007,9 +916,3 @@ fn bit_str(bit: uint) -> ~str { format!("[{}:{}-{:02x}]", bit, byte, lobits) } -fn reslice<'a>(v: &'a mut [uint]) -> &'a [uint] { - // bFIXME(#5074) this function should not be necessary at all - unsafe { - cast::transmute(v) - } -} From 3805c5416e0df7c6a3214e980a145a34119f4ad5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 10 Feb 2014 07:44:03 -0500 Subject: [PATCH 31/33] test -- update tests with new error messages --- .../arc-rw-read-mode-shouldnt-escape.rs | 2 +- .../compile-fail/borrowck-assign-comp-idx.rs | 6 +++--- .../compile-fail/borrowck-autoref-3261.rs | 19 ++++++++++--------- .../borrowck-borrow-mut-object-twice.rs | 2 +- .../borrowck-insert-during-each.rs | 7 ++++--- .../borrowck-loan-blocks-move-cc.rs | 2 ++ .../borrowck-loan-blocks-mut-uniq.rs | 11 ++++++----- src/test/compile-fail/borrowck-loan-rcvr.rs | 4 ++-- .../compile-fail/borrowck-loan-vec-content.rs | 8 +++++--- .../compile-fail/borrowck-move-by-capture.rs | 6 ++---- .../compile-fail/borrowck-object-lifetime.rs | 2 +- .../borrowck-vec-pattern-move-tail.rs | 2 +- .../borrowck-vec-pattern-nesting.rs | 4 ++-- .../borrowck-vec-pattern-tail-element-loan.rs | 2 +- src/test/compile-fail/issue-3154.rs | 2 +- src/test/compile-fail/issue-4335.rs | 2 +- .../kindck-owned-trait-contains.rs | 2 +- .../moves-based-on-type-access-to-field.rs | 4 ++-- src/test/compile-fail/mut-cant-alias.rs | 2 +- .../compile-fail/mut-ptr-cant-outlive-ref.rs | 2 +- .../regionck-closure-lifetimes.rs | 6 +++--- src/test/compile-fail/regions-addr-of-arg.rs | 4 ++-- src/test/compile-fail/regions-addr-of-self.rs | 3 +-- .../regions-addr-of-upvar-self.rs | 3 +-- src/test/compile-fail/regions-bounds.rs | 4 ++-- .../compile-fail/regions-creating-enums3.rs | 2 +- .../compile-fail/regions-creating-enums4.rs | 2 +- .../regions-escape-loop-via-variable.rs | 2 +- .../regions-escape-loop-via-vec.rs | 2 +- .../regions-free-region-ordering-callee.rs | 4 ++-- .../regions-free-region-ordering-caller1.rs | 2 +- .../regions-free-region-ordering-incorrect.rs | 2 +- src/test/compile-fail/regions-freevar.rs | 3 +-- .../compile-fail/regions-glb-free-free.rs | 2 +- .../regions-infer-at-fn-not-param.rs | 2 +- src/test/compile-fail/regions-infer-call-3.rs | 2 +- .../compile-fail/regions-infer-not-param.rs | 4 ++-- .../regions-infer-paramd-indirect.rs | 2 +- src/test/compile-fail/regions-nested-fns-2.rs | 7 ++++--- src/test/compile-fail/regions-nested-fns.rs | 4 ++-- .../compile-fail/regions-ret-borrowed-1.rs | 2 +- src/test/compile-fail/regions-ret-borrowed.rs | 2 +- .../compile-fail/regions-steal-closure.rs | 2 +- src/test/compile-fail/regions-trait-3.rs | 2 +- .../sync-rwlock-read-mode-shouldnt-escape.rs | 2 +- .../trait-coercion-generic-regions.rs | 2 +- src/test/compile-fail/vec-mut-iter-borrow.rs | 2 +- src/test/run-pass/lambda-infer-unresolved.rs | 3 ++- src/test/run-pass/regions-copy-closure.rs | 7 +++++-- 49 files changed, 92 insertions(+), 85 deletions(-) diff --git a/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs b/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs index dfdbd882acf..653f37d96d4 100644 --- a/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs +++ b/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs @@ -15,7 +15,7 @@ fn main() { let mut y = None; x.write_downgrade(|write_mode| { y = Some(x.downgrade(write_mode)); - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer }); y.unwrap(); // Adding this line causes a method unification failure instead diff --git a/src/test/compile-fail/borrowck-assign-comp-idx.rs b/src/test/compile-fail/borrowck-assign-comp-idx.rs index b50a657eae7..dec248a3015 100644 --- a/src/test/compile-fail/borrowck-assign-comp-idx.rs +++ b/src/test/compile-fail/borrowck-assign-comp-idx.rs @@ -32,9 +32,9 @@ fn b() { let mut p = ~[1]; - borrow(p, || { - p[0] = 5; //~ ERROR cannot assign to - }); + borrow( + p, + || p[0] = 5); //~ ERROR cannot borrow `p` as mutable } fn c() { diff --git a/src/test/compile-fail/borrowck-autoref-3261.rs b/src/test/compile-fail/borrowck-autoref-3261.rs index b8735fb2d3c..29016a2f44f 100644 --- a/src/test/compile-fail/borrowck-autoref-3261.rs +++ b/src/test/compile-fail/borrowck-autoref-3261.rs @@ -21,13 +21,14 @@ impl X { fn main() { let mut x = X(Right(main)); - (&mut x).with(|opt| { - match opt { - &Right(ref f) => { - x = X(Left((0,0))); //~ ERROR cannot assign to `x` - (*f)() - }, - _ => fail!() - } - }) + (&mut x).with( + |opt| { //~ ERROR cannot borrow `x` as mutable more than once at a time + match opt { + &Right(ref f) => { + x = X(Left((0,0))); + (*f)() + }, + _ => fail!() + } + }) } diff --git a/src/test/compile-fail/borrowck-borrow-mut-object-twice.rs b/src/test/compile-fail/borrowck-borrow-mut-object-twice.rs index c338aac2ddf..34b9c31fdd8 100644 --- a/src/test/compile-fail/borrowck-borrow-mut-object-twice.rs +++ b/src/test/compile-fail/borrowck-borrow-mut-object-twice.rs @@ -18,7 +18,7 @@ trait Foo { fn test(x: &mut Foo) { let _y = x.f1(); - x.f2(); //~ ERROR cannot borrow `*x` because it is already borrowed as mutable + x.f2(); //~ ERROR cannot borrow `*x` as mutable } fn main() {} diff --git a/src/test/compile-fail/borrowck-insert-during-each.rs b/src/test/compile-fail/borrowck-insert-during-each.rs index 94ed47b01e1..38ff840ada4 100644 --- a/src/test/compile-fail/borrowck-insert-during-each.rs +++ b/src/test/compile-fail/borrowck-insert-during-each.rs @@ -23,9 +23,10 @@ impl Foo { } fn bar(f: &mut Foo) { - f.foo(|a| { - f.n.insert(*a); //~ ERROR cannot borrow - }) + f.foo( + |a| { //~ ERROR closure requires unique access to `f` + f.n.insert(*a); + }) } fn main() { diff --git a/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs b/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs index c193288468a..18fd4111018 100644 --- a/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs +++ b/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs @@ -21,7 +21,9 @@ fn box_imm() { info!("v={}", *v); //~^ ERROR cannot move `v` into closure }); +} +fn box_imm_explicit() { let v = ~3; let _w = &v; task::spawn(proc() { diff --git a/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs b/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs index a54476abb26..6a0d3ef82fb 100644 --- a/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs +++ b/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs @@ -14,11 +14,12 @@ fn borrow(v: &int, f: |x: &int|) { fn box_imm() { let mut v = ~3; - borrow(v, |w| { - v = ~4; //~ ERROR cannot assign to `v` because it is borrowed - assert_eq!(*v, 3); - assert_eq!(*w, 4); - }) + borrow(v, + |w| { //~ ERROR cannot borrow `v` as mutable + v = ~4; + assert_eq!(*v, 3); + assert_eq!(*w, 4); + }) } fn main() { diff --git a/src/test/compile-fail/borrowck-loan-rcvr.rs b/src/test/compile-fail/borrowck-loan-rcvr.rs index a0071938ce4..dbeeb521306 100644 --- a/src/test/compile-fail/borrowck-loan-rcvr.rs +++ b/src/test/compile-fail/borrowck-loan-rcvr.rs @@ -32,8 +32,8 @@ fn a() { p.impurem(); // But in this case we do not honor the loan: - p.blockm(|| { - p.x = 10; //~ ERROR cannot assign + p.blockm(|| { //~ ERROR cannot borrow `p` as mutable + p.x = 10; }) } diff --git a/src/test/compile-fail/borrowck-loan-vec-content.rs b/src/test/compile-fail/borrowck-loan-vec-content.rs index 6527ddfa2ec..0e721d7107f 100644 --- a/src/test/compile-fail/borrowck-loan-vec-content.rs +++ b/src/test/compile-fail/borrowck-loan-vec-content.rs @@ -23,9 +23,11 @@ fn has_mut_vec_and_does_not_try_to_change_it() { fn has_mut_vec_but_tries_to_change_it() { let mut v = ~[1, 2, 3]; - takes_imm_elt(&v[0], || { - v[1] = 4; //~ ERROR cannot assign - }) + takes_imm_elt( + &v[0], + || { //~ ERROR cannot borrow `v` as mutable + v[1] = 4; + }) } fn main() { diff --git a/src/test/compile-fail/borrowck-move-by-capture.rs b/src/test/compile-fail/borrowck-move-by-capture.rs index 5af1f8312aa..f3869e5c9fd 100644 --- a/src/test/compile-fail/borrowck-move-by-capture.rs +++ b/src/test/compile-fail/borrowck-move-by-capture.rs @@ -9,10 +9,8 @@ // except according to those terms. pub fn main() { - // FIXME(#2202) - Due to the way that borrowck treats closures, - // you get two error reports here. let bar = ~3; - let _g = || { //~ ERROR capture of moved value - let _h: proc() -> int = proc() *bar; //~ ERROR capture of moved value + let _g = || { + let _h: proc() -> int = proc() *bar; //~ ERROR cannot move out of captured outer variable }; } diff --git a/src/test/compile-fail/borrowck-object-lifetime.rs b/src/test/compile-fail/borrowck-object-lifetime.rs index daeb5b72857..92b77d8243e 100644 --- a/src/test/compile-fail/borrowck-object-lifetime.rs +++ b/src/test/compile-fail/borrowck-object-lifetime.rs @@ -17,7 +17,7 @@ fn borrowed_receiver<'a>(x: &'a Foo) -> &'a () { } fn owned_receiver(x: ~Foo) -> &() { - x.borrowed() //~ ERROR borrowed value does not live long enough + x.borrowed() //~ ERROR `*x` does not live long enough } fn mut_owned_receiver(mut x: ~Foo) { diff --git a/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs index 909af7da960..cca8ed93388 100644 --- a/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs @@ -14,6 +14,6 @@ fn main() { [1, 2, ..tail] => tail, _ => unreachable!() }; - a[0] = 0; //~ ERROR cannot assign to `a[]` because it is borrowed + a[0] = 0; //~ ERROR cannot assign to `a[..]` because it is borrowed t[0]; } diff --git a/src/test/compile-fail/borrowck-vec-pattern-nesting.rs b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs index be66dcf372e..cb1a7d393a8 100644 --- a/src/test/compile-fail/borrowck-vec-pattern-nesting.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs @@ -12,7 +12,7 @@ fn a() { let mut vec = ~[~1, ~2, ~3]; match vec { [~ref _a] => { - vec[0] = ~4; //~ ERROR cannot assign to `(*vec)[]` because it is borrowed + vec[0] = ~4; //~ ERROR cannot assign } _ => fail!("foo") } @@ -22,7 +22,7 @@ fn b() { let mut vec = ~[~1, ~2, ~3]; match vec { [.._b] => { - vec[0] = ~4; //~ ERROR cannot assign to `(*vec)[]` because it is borrowed + vec[0] = ~4; //~ ERROR cannot assign } } } diff --git a/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs index cf20d57ac58..b471d40a950 100644 --- a/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs @@ -11,7 +11,7 @@ fn a() -> &int { let vec = ~[1, 2, 3, 4]; let tail = match vec { - [_a, ..tail] => &tail[0], //~ ERROR borrowed value does not live long enough + [_a, ..tail] => &tail[0], //~ ERROR `vec[..]` does not live long enough _ => fail!("foo") }; tail diff --git a/src/test/compile-fail/issue-3154.rs b/src/test/compile-fail/issue-3154.rs index 1cfed882d6c..dcb705856d9 100644 --- a/src/test/compile-fail/issue-3154.rs +++ b/src/test/compile-fail/issue-3154.rs @@ -13,7 +13,7 @@ struct thing<'a, Q> { } fn thing(x: &Q) -> thing { - thing{ x: x } //~ ERROR cannot infer an appropriate lifetime + thing{ x: x } //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/issue-4335.rs b/src/test/compile-fail/issue-4335.rs index 4cfa543a93f..8e4aa799d1f 100644 --- a/src/test/compile-fail/issue-4335.rs +++ b/src/test/compile-fail/issue-4335.rs @@ -11,7 +11,7 @@ fn id(t: T) -> T { t } fn f<'r, T>(v: &'r T) -> 'r || -> T { - id(|| *v) //~ ERROR cannot infer an appropriate lifetime + id(|| *v) //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/kindck-owned-trait-contains.rs b/src/test/compile-fail/kindck-owned-trait-contains.rs index 5fe9b13f83b..ed1725f3240 100644 --- a/src/test/compile-fail/kindck-owned-trait-contains.rs +++ b/src/test/compile-fail/kindck-owned-trait-contains.rs @@ -24,7 +24,7 @@ fn main() { let y = { let tmp0 = 3; - let tmp1 = &tmp0; //~ ERROR borrowed value does not live long enough + let tmp1 = &tmp0; //~ ERROR `tmp0` does not live long enough repeater(tmp1) }; assert!(3 == *(y.get())); diff --git a/src/test/compile-fail/moves-based-on-type-access-to-field.rs b/src/test/compile-fail/moves-based-on-type-access-to-field.rs index f07d4fcf70c..1557b290c2c 100644 --- a/src/test/compile-fail/moves-based-on-type-access-to-field.rs +++ b/src/test/compile-fail/moves-based-on-type-access-to-field.rs @@ -17,13 +17,13 @@ fn touch(_a: &A) {} fn f10() { let x = Foo { f: ~"hi", y: 3 }; - consume(x.f); //~ NOTE `x.f` moved here + consume(x.f); touch(&x.y); //~ ERROR use of partially moved value: `x` } fn f20() { let x = ~[~"hi"]; - consume(x[0]); //~ NOTE `(*x)[]` moved here + consume(x[0]); touch(&x[0]); //~ ERROR use of partially moved value: `x` } diff --git a/src/test/compile-fail/mut-cant-alias.rs b/src/test/compile-fail/mut-cant-alias.rs index 5b8079b832e..e3e2ace71ad 100644 --- a/src/test/compile-fail/mut-cant-alias.rs +++ b/src/test/compile-fail/mut-cant-alias.rs @@ -14,5 +14,5 @@ fn main() { let m = RefCell::new(0); let mut b = m.borrow_mut(); let b1 = b.get(); - let b2 = b.get(); //~ ERROR cannot borrow `b` because it is already borrowed as mutable + let b2 = b.get(); //~ ERROR cannot borrow } diff --git a/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs b/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs index ca276700e8b..2e5cf1b504b 100644 --- a/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs +++ b/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs @@ -15,6 +15,6 @@ fn main() { let p; { let b = m.borrow(); - p = b.get(); //~ ERROR borrowed value does not live long enough + p = b.get(); //~ ERROR `b` does not live long enough } } diff --git a/src/test/compile-fail/regionck-closure-lifetimes.rs b/src/test/compile-fail/regionck-closure-lifetimes.rs index f66b17d68c7..ec51f2dc212 100644 --- a/src/test/compile-fail/regionck-closure-lifetimes.rs +++ b/src/test/compile-fail/regionck-closure-lifetimes.rs @@ -18,7 +18,7 @@ fn env<'a>(_: &'a uint, blk: |p: 'a |||) { let mut state = 0; let statep = &mut state; - blk(|| *statep = 1); //~ ERROR cannot infer an appropriate lifetime + blk(|| *statep = 1); //~ ERROR cannot infer } fn no_env_no_for<'a>(_: &'a uint, blk: |p: 'a |||) { @@ -40,7 +40,7 @@ fn repeating_loop() { let state = 0; loop { - closure = || state; //~ ERROR cannot infer an appropriate lifetime + closure = || state; //~ ERROR cannot infer break; } @@ -56,7 +56,7 @@ fn repeating_while() { let state = 0; while true { - closure = || state; //~ ERROR cannot infer an appropriate lifetime + closure = || state; //~ ERROR cannot infer break; } diff --git a/src/test/compile-fail/regions-addr-of-arg.rs b/src/test/compile-fail/regions-addr-of-arg.rs index ff13548b494..3e568180b53 100644 --- a/src/test/compile-fail/regions-addr-of-arg.rs +++ b/src/test/compile-fail/regions-addr-of-arg.rs @@ -12,7 +12,7 @@ // bounded by the current function call. fn foo(a: int) { - let _p: &'static int = &a; //~ ERROR borrowed value does not live long enough + let _p: &'static int = &a; //~ ERROR `a` does not live long enough } fn bar(a: int) { @@ -20,7 +20,7 @@ fn bar(a: int) { } fn zed<'a>(a: int) -> &'a int { - &a //~ ERROR borrowed value does not live long enough + &a //~ ERROR `a` does not live long enough } fn main() { diff --git a/src/test/compile-fail/regions-addr-of-self.rs b/src/test/compile-fail/regions-addr-of-self.rs index 2cd96735a07..ce89b66cd5b 100644 --- a/src/test/compile-fail/regions-addr-of-self.rs +++ b/src/test/compile-fail/regions-addr-of-self.rs @@ -14,8 +14,7 @@ struct dog { impl dog { pub fn chase_cat(&mut self) { - let p: &'static mut uint = &mut self.cats_chased; - //~^ ERROR cannot infer an appropriate lifetime + let p: &'static mut uint = &mut self.cats_chased; //~ ERROR cannot infer *p += 1u; } diff --git a/src/test/compile-fail/regions-addr-of-upvar-self.rs b/src/test/compile-fail/regions-addr-of-upvar-self.rs index c8fe60a2490..7a146c043c8 100644 --- a/src/test/compile-fail/regions-addr-of-upvar-self.rs +++ b/src/test/compile-fail/regions-addr-of-upvar-self.rs @@ -17,8 +17,7 @@ struct dog { impl dog { pub fn chase_cat(&mut self) { let _f = || { - let p: &'static mut uint = &mut self.food; - //~^ ERROR cannot infer an appropriate lifetime + let p: &'static mut uint = &mut self.food; //~ ERROR cannot infer *p = 3u; }; } diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs index f74244c4984..5ef043634fb 100644 --- a/src/test/compile-fail/regions-bounds.rs +++ b/src/test/compile-fail/regions-bounds.rs @@ -17,12 +17,12 @@ struct a_class<'a> { x:&'a int } fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> { return e; //~ ERROR mismatched types: expected `an_enum<'b>` but found `an_enum<'a>` - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer } fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> { return e; //~ ERROR mismatched types: expected `a_class<'b>` but found `a_class<'a>` - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer } fn main() { } diff --git a/src/test/compile-fail/regions-creating-enums3.rs b/src/test/compile-fail/regions-creating-enums3.rs index 9e36ecc2b75..2c3f39795a4 100644 --- a/src/test/compile-fail/regions-creating-enums3.rs +++ b/src/test/compile-fail/regions-creating-enums3.rs @@ -14,7 +14,7 @@ enum ast<'a> { } fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> { - add(x, y) //~ ERROR cannot infer an appropriate lifetime + add(x, y) //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/regions-creating-enums4.rs b/src/test/compile-fail/regions-creating-enums4.rs index 7683b678b2b..0cd5a975960 100644 --- a/src/test/compile-fail/regions-creating-enums4.rs +++ b/src/test/compile-fail/regions-creating-enums4.rs @@ -14,7 +14,7 @@ enum ast<'a> { } fn mk_add_bad2<'a>(x: &'a ast<'a>, y: &'a ast<'a>, z: &ast) -> ast { - add(x, y) //~ ERROR cannot infer an appropriate lifetime + add(x, y) //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/regions-escape-loop-via-variable.rs b/src/test/compile-fail/regions-escape-loop-via-variable.rs index 19bd0bf9747..f588655d1af 100644 --- a/src/test/compile-fail/regions-escape-loop-via-variable.rs +++ b/src/test/compile-fail/regions-escape-loop-via-variable.rs @@ -18,6 +18,6 @@ fn main() { loop { let x = 1 + *p; - p = &x; //~ ERROR borrowed value does not live long enough + p = &x; //~ ERROR `x` does not live long enough } } diff --git a/src/test/compile-fail/regions-escape-loop-via-vec.rs b/src/test/compile-fail/regions-escape-loop-via-vec.rs index 92e2cd73dfb..ccfcc52945d 100644 --- a/src/test/compile-fail/regions-escape-loop-via-vec.rs +++ b/src/test/compile-fail/regions-escape-loop-via-vec.rs @@ -14,7 +14,7 @@ fn broken() { let mut _y = ~[&mut x]; while x < 10 { let mut z = x; - _y.push(&mut z); //~ ERROR borrowed value does not live long enough + _y.push(&mut z); //~ ERROR `z` does not live long enough x += 1; //~ ERROR cannot assign } } diff --git a/src/test/compile-fail/regions-free-region-ordering-callee.rs b/src/test/compile-fail/regions-free-region-ordering-callee.rs index 94c617b2182..9762e5c4690 100644 --- a/src/test/compile-fail/regions-free-region-ordering-callee.rs +++ b/src/test/compile-fail/regions-free-region-ordering-callee.rs @@ -20,13 +20,13 @@ fn ordering1<'a, 'b>(x: &'a &'b uint) -> &'a uint { fn ordering2<'a, 'b>(x: &'a &'b uint, y: &'a uint) -> &'b uint { // However, it is not safe to assume that 'b <= 'a - &*y //~ ERROR cannot infer an appropriate lifetime + &*y //~ ERROR cannot infer } fn ordering3<'a, 'b>(x: &'a uint, y: &'b uint) -> &'a &'b uint { // Do not infer an ordering from the return value. let z: &'b uint = &*x; - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer fail!(); } diff --git a/src/test/compile-fail/regions-free-region-ordering-caller1.rs b/src/test/compile-fail/regions-free-region-ordering-caller1.rs index 1408f75be89..b117a1a6476 100644 --- a/src/test/compile-fail/regions-free-region-ordering-caller1.rs +++ b/src/test/compile-fail/regions-free-region-ordering-caller1.rs @@ -18,7 +18,7 @@ fn call1<'a>(x: &'a uint) { let y: uint = 3; let z: &'a & uint = &(&y); //~^ ERROR borrowed value does not live long enough - //~^^ ERROR borrowed value does not live long enough + //~^^ ERROR `y` does not live long enough } fn main() {} diff --git a/src/test/compile-fail/regions-free-region-ordering-incorrect.rs b/src/test/compile-fail/regions-free-region-ordering-incorrect.rs index 54352092794..6f6b6761735 100644 --- a/src/test/compile-fail/regions-free-region-ordering-incorrect.rs +++ b/src/test/compile-fail/regions-free-region-ordering-incorrect.rs @@ -24,7 +24,7 @@ impl<'b, T> Node<'b, T> { fn get<'a>(&'a self) -> &'b T { match self.next { Some(ref next) => next.get(), - None => &self.val //~ ERROR cannot infer an appropriate lifetime + None => &self.val //~ ERROR cannot infer } } } diff --git a/src/test/compile-fail/regions-freevar.rs b/src/test/compile-fail/regions-freevar.rs index 940a7f9afbb..af460dbdd78 100644 --- a/src/test/compile-fail/regions-freevar.rs +++ b/src/test/compile-fail/regions-freevar.rs @@ -12,8 +12,7 @@ fn wants_static_fn(_x: 'static ||) {} fn main() { let i = 3; - wants_static_fn(|| { - //~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements + wants_static_fn(|| { //~ ERROR cannot infer info!("i={}", i); }) } diff --git a/src/test/compile-fail/regions-glb-free-free.rs b/src/test/compile-fail/regions-glb-free-free.rs index 8f6754b34bc..1aafd9057c2 100644 --- a/src/test/compile-fail/regions-glb-free-free.rs +++ b/src/test/compile-fail/regions-glb-free-free.rs @@ -24,7 +24,7 @@ mod argparse { impl<'a> Flag<'a> { pub fn set_desc(self, s: &str) -> Flag<'a> { - Flag { //~ ERROR cannot infer an appropriate lifetime + Flag { //~ ERROR cannot infer name: self.name, desc: s, max_count: self.max_count, diff --git a/src/test/compile-fail/regions-infer-at-fn-not-param.rs b/src/test/compile-fail/regions-infer-at-fn-not-param.rs index 46de570eaf4..ad6d1b2742d 100644 --- a/src/test/compile-fail/regions-infer-at-fn-not-param.rs +++ b/src/test/compile-fail/regions-infer-at-fn-not-param.rs @@ -22,7 +22,7 @@ struct not_parameterized2 { fn take1(p: parameterized1) -> parameterized1 { p } //~^ ERROR mismatched types -//~^^ ERROR cannot infer an appropriate lifetime +//~^^ ERROR cannot infer fn take3(p: not_parameterized1) -> not_parameterized1 { p } fn take4(p: not_parameterized2) -> not_parameterized2 { p } diff --git a/src/test/compile-fail/regions-infer-call-3.rs b/src/test/compile-fail/regions-infer-call-3.rs index bb7c487005f..66f958c7893 100644 --- a/src/test/compile-fail/regions-infer-call-3.rs +++ b/src/test/compile-fail/regions-infer-call-3.rs @@ -16,7 +16,7 @@ fn with(f: |x: &int| -> T) -> T { fn manip<'a>(x: &'a int) -> int { let z = with(|y| { select(x, y) }); - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer *z } diff --git a/src/test/compile-fail/regions-infer-not-param.rs b/src/test/compile-fail/regions-infer-not-param.rs index b5fce9e21bd..6596a1d8c23 100644 --- a/src/test/compile-fail/regions-infer-not-param.rs +++ b/src/test/compile-fail/regions-infer-not-param.rs @@ -23,11 +23,11 @@ struct indirect2<'a> { } fn take_direct(p: direct) -> direct { p } //~ ERROR mismatched types -//~^ ERROR cannot infer an appropriate lifetime +//~^ ERROR cannot infer fn take_indirect1(p: indirect1) -> indirect1 { p } fn take_indirect2(p: indirect2) -> indirect2 { p } //~ ERROR mismatched types -//~^ ERROR cannot infer an appropriate lifetime +//~^ ERROR cannot infer fn main() {} diff --git a/src/test/compile-fail/regions-infer-paramd-indirect.rs b/src/test/compile-fail/regions-infer-paramd-indirect.rs index 63d3338cc89..e2f4f791652 100644 --- a/src/test/compile-fail/regions-infer-paramd-indirect.rs +++ b/src/test/compile-fail/regions-infer-paramd-indirect.rs @@ -32,7 +32,7 @@ impl<'a> set_f<'a> for c<'a> { fn set_f_bad(&self, b: @b) { self.f = b; //~ ERROR mismatched types: expected `@@&'a int` but found `@@&int` - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer } } diff --git a/src/test/compile-fail/regions-nested-fns-2.rs b/src/test/compile-fail/regions-nested-fns-2.rs index 8e9a7546541..60eae9ce80a 100644 --- a/src/test/compile-fail/regions-nested-fns-2.rs +++ b/src/test/compile-fail/regions-nested-fns-2.rs @@ -12,9 +12,10 @@ fn ignore(_f: <'z>|&'z int| -> &'z int) {} fn nested() { let y = 3; - ignore(|z| { - if false { &y } else { z } //~ ERROR borrowed value does not live long enough - }); + ignore( + |z| { //~ ERROR `y` does not live long enough + if false { &y } else { z } + }); } fn main() {} diff --git a/src/test/compile-fail/regions-nested-fns.rs b/src/test/compile-fail/regions-nested-fns.rs index 11610f422a0..c66e5616b84 100644 --- a/src/test/compile-fail/regions-nested-fns.rs +++ b/src/test/compile-fail/regions-nested-fns.rs @@ -12,7 +12,7 @@ fn ignore(t: T) {} fn nested<'x>(x: &'x int) { let y = 3; - let mut ay = &y; //~ ERROR cannot infer an appropriate lifetime + let mut ay = &y; //~ ERROR cannot infer ignore::< <'z>|&'z int|>(|z| { ay = x; @@ -22,7 +22,7 @@ fn nested<'x>(x: &'x int) { ignore::< <'z>|&'z int| -> &'z int>(|z| { if false { return x; } //~ ERROR mismatched types - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer if false { return ay; } return z; }); diff --git a/src/test/compile-fail/regions-ret-borrowed-1.rs b/src/test/compile-fail/regions-ret-borrowed-1.rs index 7eb5fa3c60b..0c335b9d557 100644 --- a/src/test/compile-fail/regions-ret-borrowed-1.rs +++ b/src/test/compile-fail/regions-ret-borrowed-1.rs @@ -19,7 +19,7 @@ fn with(f: <'a>|x: &'a int| -> R) -> R { fn return_it<'a>() -> &'a int { with(|o| o) //~ ERROR mismatched types //~^ ERROR lifetime of return value does not outlive the function call - //~^^ ERROR cannot infer an appropriate lifetime + //~^^ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/regions-ret-borrowed.rs b/src/test/compile-fail/regions-ret-borrowed.rs index 2f6f2f44cda..469421751df 100644 --- a/src/test/compile-fail/regions-ret-borrowed.rs +++ b/src/test/compile-fail/regions-ret-borrowed.rs @@ -22,7 +22,7 @@ fn with(f: |x: &int| -> R) -> R { fn return_it() -> &int { with(|o| o) //~ ERROR mismatched types //~^ ERROR lifetime of return value does not outlive the function call - //~^^ ERROR cannot infer an appropriate lifetime + //~^^ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/regions-steal-closure.rs b/src/test/compile-fail/regions-steal-closure.rs index 32fde3747c1..f80e5616bd5 100644 --- a/src/test/compile-fail/regions-steal-closure.rs +++ b/src/test/compile-fail/regions-steal-closure.rs @@ -19,7 +19,7 @@ fn box_it<'r>(x: 'r ||) -> closure_box<'r> { fn main() { let cl_box = { let mut i = 3; - box_it(|| i += 1) //~ ERROR cannot infer an appropriate lifetime + box_it(|| i += 1) //~ ERROR cannot infer }; (cl_box.cl)(); } diff --git a/src/test/compile-fail/regions-trait-3.rs b/src/test/compile-fail/regions-trait-3.rs index 52d80f49aa6..9222fde7789 100644 --- a/src/test/compile-fail/regions-trait-3.rs +++ b/src/test/compile-fail/regions-trait-3.rs @@ -38,7 +38,7 @@ impl get_ctxt for Foo<'a> { } fn make_gc2<'a,'b>(foo: Foo<'a>) -> @get_ctxt<'b> { - return @foo as @get_ctxt; //~ ERROR cannot infer an appropriate lifetime + return @foo as @get_ctxt; //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/sync-rwlock-read-mode-shouldnt-escape.rs b/src/test/compile-fail/sync-rwlock-read-mode-shouldnt-escape.rs index 0201f9dd51c..cb6c11537c6 100644 --- a/src/test/compile-fail/sync-rwlock-read-mode-shouldnt-escape.rs +++ b/src/test/compile-fail/sync-rwlock-read-mode-shouldnt-escape.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: cannot infer an appropriate lifetime +// error-pattern: cannot infer extern mod sync; use sync::RWLock; fn main() { diff --git a/src/test/compile-fail/trait-coercion-generic-regions.rs b/src/test/compile-fail/trait-coercion-generic-regions.rs index 2aeebc0f1a8..76c877dcca8 100644 --- a/src/test/compile-fail/trait-coercion-generic-regions.rs +++ b/src/test/compile-fail/trait-coercion-generic-regions.rs @@ -24,7 +24,7 @@ impl Trait<&'static str> for Struct { fn main() { let person = ~"Fred"; - let person: &str = person; //~ ERROR borrowed value does not live long enough + let person: &str = person; //~ ERROR `person[..]` does not live long enough let s: ~Trait<&'static str> = ~Struct { person: person }; } diff --git a/src/test/compile-fail/vec-mut-iter-borrow.rs b/src/test/compile-fail/vec-mut-iter-borrow.rs index 21ffc1ae7f9..72dbd82e947 100644 --- a/src/test/compile-fail/vec-mut-iter-borrow.rs +++ b/src/test/compile-fail/vec-mut-iter-borrow.rs @@ -12,6 +12,6 @@ fn main() { let mut xs = ~[1, 2, 3, 4]; for x in xs.mut_iter() { - xs.push(1) //~ ERROR cannot borrow `xs` because it is already borrowed as mutable + xs.push(1) //~ ERROR cannot borrow `xs` } } diff --git a/src/test/run-pass/lambda-infer-unresolved.rs b/src/test/run-pass/lambda-infer-unresolved.rs index 65f95f78ea8..59baf63d284 100644 --- a/src/test/run-pass/lambda-infer-unresolved.rs +++ b/src/test/run-pass/lambda-infer-unresolved.rs @@ -16,5 +16,6 @@ struct Refs { refs: ~[int], n: int } pub fn main() { let mut e = Refs{refs: ~[], n: 0}; let _f: || = || error!("{}", e.n); - e.refs.push(1); + let x: &[int] = e.refs; + assert_eq!(x.len(), 0); } diff --git a/src/test/run-pass/regions-copy-closure.rs b/src/test/run-pass/regions-copy-closure.rs index 718394e943f..55cb5c62684 100644 --- a/src/test/run-pass/regions-copy-closure.rs +++ b/src/test/run-pass/regions-copy-closure.rs @@ -18,8 +18,11 @@ fn box_it<'r>(x: 'r ||) -> closure_box<'r> { pub fn main() { let mut i = 3; - let cl_box = box_it(|| i += 1); assert_eq!(i, 3); - (cl_box.cl)(); + { + let cl = || i += 1; + let cl_box = box_it(cl); + (cl_box.cl)(); + } assert_eq!(i, 4); } From c9e3cb678d556926c491d977550fbd805892d4af Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 10 Feb 2014 07:44:21 -0500 Subject: [PATCH 32/33] test -- add new tests specifically examining closure borrows --- .../borrowck-closures-mut-and-imm.rs | 79 +++++++++++++++++++ .../borrowck-closures-mut-of-imm.rs | 31 ++++++++ .../compile-fail/borrowck-closures-two-mut.rs | 56 +++++++++++++ .../compile-fail/borrowck-closures-unique.rs | 50 ++++++++++++ .../borrowck-closures-use-after-free.rs | 31 ++++++++ .../run-pass/borrowck-closures-two-imm.rs | 49 ++++++++++++ 6 files changed, 296 insertions(+) create mode 100644 src/test/compile-fail/borrowck-closures-mut-and-imm.rs create mode 100644 src/test/compile-fail/borrowck-closures-mut-of-imm.rs create mode 100644 src/test/compile-fail/borrowck-closures-two-mut.rs create mode 100644 src/test/compile-fail/borrowck-closures-unique.rs create mode 100644 src/test/compile-fail/borrowck-closures-use-after-free.rs create mode 100644 src/test/run-pass/borrowck-closures-two-imm.rs diff --git a/src/test/compile-fail/borrowck-closures-mut-and-imm.rs b/src/test/compile-fail/borrowck-closures-mut-and-imm.rs new file mode 100644 index 00000000000..006f475b29d --- /dev/null +++ b/src/test/compile-fail/borrowck-closures-mut-and-imm.rs @@ -0,0 +1,79 @@ +// 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. + +// Tests that two closures cannot simultaneously have mutable +// and immutable access to the variable. Issue #6801. + +fn get(x: &int) -> int { + *x +} + +fn set(x: &mut int) { + *x = 4; +} + +fn a() { + let mut x = 3; + let c1 = || x = 4; + let c2 = || x * 5; //~ ERROR cannot borrow `x` +} + +fn b() { + let mut x = 3; + let c1 = || set(&mut x); + let c2 = || get(&x); //~ ERROR cannot borrow `x` +} + +fn c() { + let mut x = 3; + let c1 = || set(&mut x); + let c2 = || x * 5; //~ ERROR cannot borrow `x` +} + +fn d() { + let mut x = 3; + let c2 = || x * 5; + x = 5; //~ ERROR cannot assign +} + +fn e() { + let mut x = 3; + let c1 = || get(&x); + x = 5; //~ ERROR cannot assign +} + +fn f() { + let mut x = ~3; + let c1 = || get(&*x); + *x = 5; //~ ERROR cannot assign +} + +fn g() { + struct Foo { + f: ~int + } + + let mut x = ~Foo { f: ~3 }; + let c1 = || get(&*x.f); + *x.f = 5; //~ ERROR cannot assign to `*x.f` +} + +fn h() { + struct Foo { + f: ~int + } + + let mut x = ~Foo { f: ~3 }; + let c1 = || get(&*x.f); + let c2 = || *x.f = 5; //~ ERROR cannot borrow `x` as mutable +} + +fn main() { +} diff --git a/src/test/compile-fail/borrowck-closures-mut-of-imm.rs b/src/test/compile-fail/borrowck-closures-mut-of-imm.rs new file mode 100644 index 00000000000..cdfb569762d --- /dev/null +++ b/src/test/compile-fail/borrowck-closures-mut-of-imm.rs @@ -0,0 +1,31 @@ +// 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. + +// Tests that two closures cannot simultaneously have mutable +// and immutable access to the variable. Issue #6801. + +fn get(x: &int) -> int { + *x +} + +fn set(x: &mut int) { + *x = 4; +} + +fn a(x: &int) { + let c1 = || set(&mut *x); + //~^ ERROR cannot borrow + let c2 = || set(&mut *x); + //~^ ERROR closure requires unique access to `x` + //~^^ ERROR cannot borrow +} + +fn main() { +} diff --git a/src/test/compile-fail/borrowck-closures-two-mut.rs b/src/test/compile-fail/borrowck-closures-two-mut.rs new file mode 100644 index 00000000000..570249aed44 --- /dev/null +++ b/src/test/compile-fail/borrowck-closures-two-mut.rs @@ -0,0 +1,56 @@ +// 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. + +// Tests that two closures cannot simultaneously have mutable +// access to the variable, whether that mutable access be used +// for direct assignment or for taking mutable ref. Issue #6801. + +fn a() { + let mut x = 3; + let c1 = || x = 4; + let c2 = || x = 5; //~ ERROR cannot borrow `x` as mutable more than once +} + +fn set(x: &mut int) { + *x = 4; +} + +fn b() { + let mut x = 3; + let c1 = || set(&mut x); + let c2 = || set(&mut x); //~ ERROR cannot borrow `x` as mutable more than once +} + +fn c() { + let mut x = 3; + let c1 = || x = 5; + let c2 = || set(&mut x); //~ ERROR cannot borrow `x` as mutable more than once +} + +fn d() { + let mut x = 3; + let c1 = || x = 5; + let c2 = || { let _y = || set(&mut x); }; // (nested closure) + //~^ ERROR cannot borrow `x` as mutable more than once +} + +fn g() { + struct Foo { + f: ~int + } + + let mut x = ~Foo { f: ~3 }; + let c1 = || set(&mut *x.f); + let c2 = || set(&mut *x.f); + //~^ ERROR cannot borrow `x` as mutable more than once +} + +fn main() { +} diff --git a/src/test/compile-fail/borrowck-closures-unique.rs b/src/test/compile-fail/borrowck-closures-unique.rs new file mode 100644 index 00000000000..80d942e58d1 --- /dev/null +++ b/src/test/compile-fail/borrowck-closures-unique.rs @@ -0,0 +1,50 @@ +// 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. + +// Tests that a closure which requires mutable access to the referent +// of an `&mut` requires a "unique" borrow -- that is, the variable to +// be borrowed (here, `x`) will not be borrowed *mutably*, but +// may be *immutable*, but we cannot allow +// multiple borrows. + +fn get(x: &int) -> int { + *x +} + +fn set(x: &mut int) -> int { + *x +} + +fn a(x: &mut int) { + let c1 = || get(x); + let c2 = || get(x); +} + +fn b(x: &mut int) { + let c1 = || get(x); + let c2 = || set(x); //~ ERROR closure requires unique access to `x` +} + +fn c(x: &mut int) { + let c1 = || get(x); + let c2 = || { get(x); set(x); }; //~ ERROR closure requires unique access to `x` +} + +fn d(x: &mut int) { + let c1 = || set(x); + let c2 = || set(x); //~ ERROR closure requires unique access to `x` +} + +fn e(x: &mut int) { + let c1: || = || x = fail!(); //~ ERROR closure cannot assign to immutable argument `x` +} + +fn main() { +} diff --git a/src/test/compile-fail/borrowck-closures-use-after-free.rs b/src/test/compile-fail/borrowck-closures-use-after-free.rs new file mode 100644 index 00000000000..38c13b1fce9 --- /dev/null +++ b/src/test/compile-fail/borrowck-closures-use-after-free.rs @@ -0,0 +1,31 @@ +// 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. + +// Tests that a closure which mutates a local variable +// cannot also be supplied a borrowed version of that +// variable's contents. Issue #11192. + +struct Foo { + x: int +} + +impl Drop for Foo { + fn drop(&mut self) { + println!("drop {}", self.x); + } +} + +fn main() { + let mut ptr = ~Foo { x: 0 }; + let test = |foo: &Foo| { + ptr = ~Foo { x: ptr.x + 1 }; + }; + test(ptr); //~ ERROR cannot borrow `*ptr` +} diff --git a/src/test/run-pass/borrowck-closures-two-imm.rs b/src/test/run-pass/borrowck-closures-two-imm.rs new file mode 100644 index 00000000000..3bd12b03041 --- /dev/null +++ b/src/test/run-pass/borrowck-closures-two-imm.rs @@ -0,0 +1,49 @@ +// 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. + +// Tests that two closures can simultaneously have immutable +// access to the variable, whether that immutable access be used +// for direct reads or for taking immutable ref. Also check +// that the main function can read the variable too while +// the closures are in scope. Issue #6801. + +fn a() -> int { + let mut x = 3; + x += 1; + let c1 = || x * 4; + let c2 = || x * 5; + c1() * c2() * x +} + +fn get(x: &int) -> int { + *x * 4 +} + +fn b() -> int { + let mut x = 3; + x += 1; + let c1 = || get(&x); + let c2 = || get(&x); + c1() * c2() * x +} + +fn c() -> int { + let mut x = 3; + x += 1; + let c1 = || x * 5; + let c2 = || get(&x); + c1() * c2() * x +} + +pub fn main() { + assert_eq!(a(), 1280); + assert_eq!(b(), 1024); + assert_eq!(c(), 1280); +} From 484f0f11e6e49c530cd0351e76989ec6706fa2ce Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Feb 2014 16:55:03 -0500 Subject: [PATCH 33/33] Correct nits from @pcwalton --- src/librustc/middle/mem_categorization.rs | 4 ++-- src/librustc/middle/typeck/check/regionck.rs | 24 +++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index b0e31a8e443..efd19cf73c0 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -48,7 +48,7 @@ * ## By-reference upvars * * One part of the translation which may be non-obvious is that we translate - * closure upvars into the dereference of a borrow pointer; this more closely + * closure upvars into the dereference of a borrowed pointer; this more closely * resembles the runtime translation. So, for example, if we had: * * let mut x = 3; @@ -246,7 +246,7 @@ pub struct MemCategorizationContext { pub type McResult = Result; /** - * The `Typer` trait provides the interface fro the mem-categorization + * The `Typer` trait provides the interface for the mem-categorization * module to the results of the type check. It can be used to query * the type assigned to an expression node, to inquire after adjustments, * and so on. diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 7647de9a3ad..d3a0da4bbfd 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -38,7 +38,7 @@ checks and effort. Whenever we introduce a borrowed pointer, for example as the result of a borrow expression `let x = &data`, the lifetime of the pointer `x` -is always specified as a region inferencr variable. `regionck` has the +is always specified as a region inference variable. `regionck` has the job of adding constraints such that this inference variable is as narrow as possible while still accommodating all uses (that is, every dereference of the resulting pointer must be within the lifetime). @@ -95,7 +95,7 @@ the following lattice: ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow So, for example, if we see an assignment `x = 5` to an upvar `x`, we -will promote it's borrow kind to mutable borrow. If we see an `&mut x` +will promote its borrow kind to mutable borrow. If we see an `&mut x` we'll do the same. Naturally, this applies not just to the upvar, but to everything owned by `x`, so the result is the same for something like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a @@ -107,7 +107,7 @@ The fact that we are inferring borrow kinds as we go results in a semi-hacky interaction with mem-categorization. In particular, mem-categorization will query the current borrow kind as it categorizes, and we'll return the *current* value, but this may get -adjusted later. Therefore, in this module, we genreally ignore the +adjusted later. Therefore, in this module, we generally ignore the borrow kind (and derived mutabilities) that are returned from mem-categorization, since they may be inaccurate. (Another option would be to use a unification scheme, where instead of returning a @@ -698,9 +698,9 @@ fn check_expr_fn_block(rcx: &mut Rcx, let inner_upvar_id = ty::UpvarId { var_id: var_id, closure_expr_id: expr.id }; - link_upvar_borrow_kind(rcx, - inner_upvar_id, - outer_upvar_id); + link_upvar_borrow_kind_for_nested_closures(rcx, + inner_upvar_id, + outer_upvar_id); } _ => {} } @@ -1114,7 +1114,7 @@ fn link_region_from_node_type(rcx: &mut Rcx, /*! * Like `link_region()`, except that the region is * extracted from the type of `id`, which must be some - * region-typed thing. + * reference (`&T`, `&str`, etc). */ let rptr_ty = rcx.resolve_node_type(id); @@ -1359,12 +1359,14 @@ fn adjust_upvar_borrow_kind_for_unique(rcx: &mut Rcx, } } -fn link_upvar_borrow_kind(rcx: &mut Rcx, - inner_upvar_id: ty::UpvarId, - outer_upvar_id: ty::UpvarId) { +fn link_upvar_borrow_kind_for_nested_closures(rcx: &mut Rcx, + inner_upvar_id: ty::UpvarId, + outer_upvar_id: ty::UpvarId) { /*! * Indicates that the borrow_kind of `outer_upvar_id` must - * permit a reborrowing with the borrow_kind of `inner_upvar_id` + * permit a reborrowing with the borrow_kind of `inner_upvar_id`. + * This occurs in nested closures, see comment above at the call to + * this function. */ debug!("link_upvar_borrow_kind: inner_upvar_id={:?} outer_upvar_id={:?}",