From 202b8dcdc420d8b109fbd5260ea2e2be0a5b7faf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 17 Apr 2013 18:05:17 -0400 Subject: [PATCH 01/46] adapt to snapshot --- src/libcore/container.rs | 36 ----- src/libcore/hashmap.rs | 168 ------------------------ src/libcore/option.rs | 104 --------------- src/libcore/reflect.rs | 33 +---- src/libcore/repr.rs | 110 +--------------- src/libcore/result.rs | 7 - src/libcore/rt/mod.rs | 21 --- src/libcore/rt/rtio.rs | 5 - src/libcore/rt/uvio.rs | 16 --- src/libcore/trie.rs | 95 -------------- src/libcore/tuple.rs | 28 ---- src/libcore/unstable/lang.rs | 26 ---- src/libcore/vec.rs | 217 ------------------------------- src/librustc/middle/astencode.rs | 104 --------------- src/librustc/rustc.rc | 1 + src/libstd/arena.rs | 56 -------- src/libstd/deque.rs | 122 ----------------- src/libstd/ebml.rs | 18 --- src/libstd/future.rs | 29 ----- src/libstd/json.rs | 48 ------- src/libstd/priority_queue.rs | 16 --- src/libstd/serialize.rs | 10 -- src/libstd/smallintmap.rs | 77 ----------- src/libsyntax/ext/base.rs | 11 -- src/libsyntax/opt_vec.rs | 9 -- 25 files changed, 3 insertions(+), 1364 deletions(-) diff --git a/src/libcore/container.rs b/src/libcore/container.rs index 88c78aebfc5c7..00ea4a9322111 100644 --- a/src/libcore/container.rs +++ b/src/libcore/container.rs @@ -25,42 +25,6 @@ pub trait Mutable: Container { fn clear(&mut self); } -#[cfg(stage0)] -pub trait Map: Mutable { - /// Return true if the map contains a value for the specified key - fn contains_key(&self, key: &K) -> bool; - - // Visits all keys and values - fn each(&self, f: &fn(&K, &V) -> bool); - - /// Visit all keys - fn each_key(&self, f: &fn(&K) -> bool); - - /// Visit all values - fn each_value(&self, f: &fn(&V) -> bool); - - /// Iterate over the map and mutate the contained values - fn mutate_values(&mut self, f: &fn(&K, &mut V) -> bool); - - /// Return a reference to the value corresponding to the key - fn find(&self, key: &K) -> Option<&'self V>; - - /// Return a mutable reference to the value corresponding to the key - fn find_mut(&mut self, key: &K) -> Option<&'self mut V>; - - /// Insert a key-value pair into the map. An existing value for a - /// key is replaced by the new value. Return true if the key did - /// not already exist in the map. - fn insert(&mut self, key: K, value: V) -> bool; - - /// Remove a key-value pair from the map. Return true if the key - /// was present in the map, otherwise false. - fn remove(&mut self, key: &K) -> bool; -} - -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] pub trait Map: Mutable { /// Return true if the map contains a value for the specified key fn contains_key(&self, key: &K) -> bool; diff --git a/src/libcore/hashmap.rs b/src/libcore/hashmap.rs index 41f4f34dc1971..ad1994a92d2bb 100644 --- a/src/libcore/hashmap.rs +++ b/src/libcore/hashmap.rs @@ -184,18 +184,6 @@ priv impl HashMap { } } - #[cfg(stage0)] - #[inline(always)] - fn value_for_bucket(&self, idx: uint) -> &'self V { - match self.buckets[idx] { - Some(ref bkt) => &bkt.value, - None => fail!(~"HashMap::find: internal logic error"), - } - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] #[inline(always)] fn value_for_bucket<'a>(&'a self, idx: uint) -> &'a V { match self.buckets[idx] { @@ -204,18 +192,6 @@ priv impl HashMap { } } - #[cfg(stage0)] - #[inline(always)] - fn mut_value_for_bucket(&mut self, idx: uint) -> &'self mut V { - match self.buckets[idx] { - Some(ref mut bkt) => &mut bkt.value, - None => unreachable() - } - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] #[inline(always)] fn mut_value_for_bucket<'a>(&'a mut self, idx: uint) -> &'a mut V { match self.buckets[idx] { @@ -329,21 +305,6 @@ impl Map for HashMap { } /// Visit all key-value pairs - #[cfg(stage0)] - fn each(&self, blk: &fn(&'self K, &'self V) -> bool) { - for uint::range(0, self.buckets.len()) |i| { - for self.buckets[i].each |bucket| { - if !blk(&bucket.key, &bucket.value) { - return; - } - } - } - } - - /// Visit all key-value pairs - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn each<'a>(&'a self, blk: &fn(&'a K, &'a V) -> bool) { for uint::range(0, self.buckets.len()) |i| { for self.buckets[i].each |bucket| { @@ -360,15 +321,6 @@ impl Map for HashMap { } /// Visit all values - #[cfg(stage0)] - fn each_value(&self, blk: &fn(v: &V) -> bool) { - self.each(|_, v| blk(v)) - } - - /// Visit all values - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn each_value<'a>(&'a self, blk: &fn(v: &'a V) -> bool) { self.each(|_, v| blk(v)) } @@ -386,18 +338,6 @@ impl Map for HashMap { } /// Return a reference to the value corresponding to the key - #[cfg(stage0)] - fn find(&self, k: &K) -> Option<&'self V> { - match self.bucket_for_key(k) { - FoundEntry(idx) => Some(self.value_for_bucket(idx)), - TableFull | FoundHole(_) => None, - } - } - - /// Return a reference to the value corresponding to the key - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn find<'a>(&'a self, k: &K) -> Option<&'a V> { match self.bucket_for_key(k) { FoundEntry(idx) => Some(self.value_for_bucket(idx)), @@ -406,21 +346,6 @@ impl Map for HashMap { } /// Return a mutable reference to the value corresponding to the key - #[cfg(stage0)] - fn find_mut(&mut self, k: &K) -> Option<&'self mut V> { - let idx = match self.bucket_for_key(k) { - FoundEntry(idx) => idx, - TableFull | FoundHole(_) => return None - }; - unsafe { // FIXME(#4903)---requires flow-sensitive borrow checker - Some(::cast::transmute_mut_region(self.mut_value_for_bucket(idx))) - } - } - - /// Return a mutable reference to the value corresponding to the key - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn find_mut<'a>(&'a mut self, k: &K) -> Option<&'a mut V> { let idx = match self.bucket_for_key(k) { FoundEntry(idx) => idx, @@ -503,40 +428,6 @@ pub impl HashMap { /// Return the value corresponding to the key in the map, or insert /// and return the value if it doesn't exist. - #[cfg(stage0)] - fn find_or_insert(&mut self, k: K, v: V) -> &'self V { - if self.size >= self.resize_at { - // n.b.: We could also do this after searching, so - // that we do not resize if this call to insert is - // simply going to update a key in place. My sense - // though is that it's worse to have to search through - // buckets to find the right spot twice than to just - // resize in this corner case. - self.expand(); - } - - let hash = k.hash_keyed(self.k0, self.k1) as uint; - let idx = match self.bucket_for_key_with_hash(hash, &k) { - TableFull => fail!(~"Internal logic error"), - FoundEntry(idx) => idx, - FoundHole(idx) => { - self.buckets[idx] = Some(Bucket{hash: hash, key: k, - value: v}); - self.size += 1; - idx - }, - }; - - unsafe { // FIXME(#4903)---requires flow-sensitive borrow checker - ::cast::transmute_region(self.value_for_bucket(idx)) - } - } - - /// Return the value corresponding to the key in the map, or insert - /// and return the value if it doesn't exist. - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn find_or_insert<'a>(&'a mut self, k: K, v: V) -> &'a V { if self.size >= self.resize_at { // n.b.: We could also do this after searching, so @@ -567,41 +458,6 @@ pub impl HashMap { /// Return the value corresponding to the key in the map, or create, /// insert, and return a new value if it doesn't exist. - #[cfg(stage0)] - fn find_or_insert_with(&mut self, k: K, f: &fn(&K) -> V) -> &'self V { - if self.size >= self.resize_at { - // n.b.: We could also do this after searching, so - // that we do not resize if this call to insert is - // simply going to update a key in place. My sense - // though is that it's worse to have to search through - // buckets to find the right spot twice than to just - // resize in this corner case. - self.expand(); - } - - let hash = k.hash_keyed(self.k0, self.k1) as uint; - let idx = match self.bucket_for_key_with_hash(hash, &k) { - TableFull => fail!(~"Internal logic error"), - FoundEntry(idx) => idx, - FoundHole(idx) => { - let v = f(&k); - self.buckets[idx] = Some(Bucket{hash: hash, key: k, - value: v}); - self.size += 1; - idx - }, - }; - - unsafe { // FIXME(#4903)---requires flow-sensitive borrow checker - ::cast::transmute_region(self.value_for_bucket(idx)) - } - } - - /// Return the value corresponding to the key in the map, or create, - /// insert, and return a new value if it doesn't exist. - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn find_or_insert_with<'a>(&'a mut self, k: K, f: &fn(&K) -> V) -> &'a V { if self.size >= self.resize_at { // n.b.: We could also do this after searching, so @@ -647,17 +503,6 @@ pub impl HashMap { } } - #[cfg(stage0)] - fn get(&self, k: &K) -> &'self V { - match self.find(k) { - Some(v) => v, - None => fail!(fmt!("No entry found for key: %?", k)), - } - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn get<'a>(&'a self, k: &K) -> &'a V { match self.find(k) { Some(v) => v, @@ -676,19 +521,6 @@ pub impl HashMap { /// Return the value corresponding to the key in the map, using /// equivalence - #[cfg(stage0)] - fn find_equiv>(&self, k: &Q) -> Option<&'self V> { - match self.bucket_for_key_equiv(k) { - FoundEntry(idx) => Some(self.value_for_bucket(idx)), - TableFull | FoundHole(_) => None, - } - } - - /// Return the value corresponding to the key in the map, using - /// equivalence - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn find_equiv<'a, Q:Hash + Equiv>(&'a self, k: &Q) -> Option<&'a V> { match self.bucket_for_key_equiv(k) { FoundEntry(idx) => Some(self.value_for_bucket(idx)), diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 17192b4257b16..5abf376ddde65 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -100,16 +100,6 @@ impl> Add, Option> for Option { impl BaseIter for Option { /// Performs an operation on the contained value by reference - #[cfg(stage0)] - #[inline(always)] - fn each(&self, f: &fn(x: &'self T) -> bool) { - match *self { None => (), Some(ref t) => { f(t); } } - } - - /// Performs an operation on the contained value by reference - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] #[inline(always)] fn each<'a>(&'a self, f: &fn(x: &'a T) -> bool) { match *self { None => (), Some(ref t) => { f(t); } } @@ -122,15 +112,6 @@ impl BaseIter for Option { } impl MutableIter for Option { - #[cfg(stage0)] - #[inline(always)] - fn each_mut(&mut self, f: &fn(&'self mut T) -> bool) { - match *self { None => (), Some(ref mut t) => { f(t); } } - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] #[inline(always)] fn each_mut<'a>(&'a mut self, f: &fn(&'a mut T) -> bool) { match *self { None => (), Some(ref mut t) => { f(t); } } @@ -200,35 +181,12 @@ pub impl Option { * Update an optional value by optionally running its content by reference * through a function that returns an option. */ - #[cfg(stage0)] - #[inline(always)] - fn chain_ref(&self, f: &fn(x: &'self T) -> Option) -> Option { - match *self { Some(ref x) => f(x), None => None } - } - - /** - * Update an optional value by optionally running its content by reference - * through a function that returns an option. - */ - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] #[inline(always)] fn chain_ref<'a, U>(&'a self, f: &fn(x: &'a T) -> Option) -> Option { match *self { Some(ref x) => f(x), None => None } } /// Maps a `some` value from one type to another by reference - #[cfg(stage0)] - #[inline(always)] - fn map(&self, f: &fn(&'self T) -> U) -> Option { - match *self { Some(ref x) => Some(f(x)), None => None } - } - - /// Maps a `some` value from one type to another by reference - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] #[inline(always)] fn map<'a, U>(&self, f: &fn(&'a T) -> U) -> Option { match *self { Some(ref x) => Some(f(x)), None => None } @@ -242,16 +200,6 @@ pub impl Option { } /// Applies a function to the contained value or returns a default - #[cfg(stage0)] - #[inline(always)] - fn map_default(&self, def: U, f: &fn(&'self T) -> U) -> U { - match *self { None => def, Some(ref t) => f(t) } - } - - /// Applies a function to the contained value or returns a default - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] #[inline(always)] fn map_default<'a, U>(&'a self, def: U, f: &fn(&'a T) -> U) -> U { match *self { None => def, Some(ref t) => f(t) } @@ -295,32 +243,6 @@ pub impl Option { case explicitly. */ #[inline(always)] - #[cfg(stage0)] - fn get_ref(&self) -> &'self T { - match *self { - Some(ref x) => x, - None => fail!(~"option::get_ref none") - } - } - - /** - Gets an immutable reference to the value inside an option. - - # Failure - - Fails if the value equals `None` - - # Safety note - - In general, because this function may fail, its use is discouraged - (calling `get` on `None` is akin to dereferencing a null pointer). - Instead, prefer to use pattern matching and handle the `None` - case explicitly. - */ - #[inline(always)] - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn get_ref<'a>(&'a self) -> &'a T { match *self { Some(ref x) => x, @@ -343,32 +265,6 @@ pub impl Option { case explicitly. */ #[inline(always)] - #[cfg(stage0)] - fn get_mut_ref(&mut self) -> &'self mut T { - match *self { - Some(ref mut x) => x, - None => fail!(~"option::get_mut_ref none") - } - } - - /** - Gets a mutable reference to the value inside an option. - - # Failure - - Fails if the value equals `None` - - # Safety note - - In general, because this function may fail, its use is discouraged - (calling `get` on `None` is akin to dereferencing a null pointer). - Instead, prefer to use pattern matching and handle the `None` - case explicitly. - */ - #[inline(always)] - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn get_mut_ref<'a>(&'a mut self) -> &'a mut T { match *self { Some(ref mut x) => x, diff --git a/src/libcore/reflect.rs b/src/libcore/reflect.rs index 9a0526b4351ba..47de360f58995 100644 --- a/src/libcore/reflect.rs +++ b/src/libcore/reflect.rs @@ -15,7 +15,7 @@ Runtime type reflection */ use intrinsic::{TyDesc, TyVisitor}; -#[cfg(not(stage0))] use intrinsic::Opaque; +use intrinsic::Opaque; use libc::c_void; use sys; use vec; @@ -394,17 +394,6 @@ impl TyVisitor for MovePtrAdaptor { true } - #[cfg(stage0)] - fn visit_enter_enum(&self, n_variants: uint, sz: uint, align: uint) - -> bool { - self.align(align); - if ! self.inner.visit_enter_enum(n_variants, sz, align) { - return false; - } - true - } - - #[cfg(not(stage0))] fn visit_enter_enum(&self, n_variants: uint, get_disr: extern unsafe fn(ptr: *Opaque) -> int, sz: uint, align: uint) @@ -428,15 +417,6 @@ impl TyVisitor for MovePtrAdaptor { true } - #[cfg(stage0)] - fn visit_enum_variant_field(&self, i: uint, inner: *TyDesc) -> bool { - unsafe { self.align((*inner).align); } - if ! self.inner.visit_enum_variant_field(i, inner) { return false; } - unsafe { self.bump((*inner).size); } - true - } - - #[cfg(not(stage0))] fn visit_enum_variant_field(&self, i: uint, offset: uint, inner: *TyDesc) -> bool { self.inner.push_ptr(); self.bump(offset); @@ -457,17 +437,6 @@ impl TyVisitor for MovePtrAdaptor { true } - #[cfg(stage0)] - fn visit_leave_enum(&self, n_variants: uint, sz: uint, align: uint) - -> bool { - if ! self.inner.visit_leave_enum(n_variants, sz, align) { - return false; - } - self.bump(sz); - true - } - - #[cfg(not(stage0))] fn visit_leave_enum(&self, n_variants: uint, get_disr: extern unsafe fn(ptr: *Opaque) -> int, sz: uint, align: uint) -> bool { diff --git a/src/libcore/repr.rs b/src/libcore/repr.rs index 03e44e00d8831..2d6412b0cc832 100644 --- a/src/libcore/repr.rs +++ b/src/libcore/repr.rs @@ -18,7 +18,7 @@ use cast::transmute; use char; use intrinsic; use intrinsic::{TyDesc, TyVisitor, visit_tydesc}; -#[cfg(not(stage0))] use intrinsic::Opaque; +use intrinsic::Opaque; use io::{Writer, WriterUtil}; use libc::c_void; use managed; @@ -138,14 +138,6 @@ impl Repr for char { // New implementation using reflect::MovePtr -#[cfg(stage0)] -enum VariantState { - Degenerate, - TagMatch, - TagMismatch, -} - -#[cfg(not(stage0))] enum VariantState { SearchingFor(int), Matched, @@ -190,18 +182,6 @@ pub impl ReprVisitor { true } - #[cfg(stage0)] #[inline(always)] - fn bump(&self, sz: uint) { - do self.move_ptr() |p| { - ((p as uint) + sz) as *c_void - }; - } - - #[cfg(stage0)] #[inline(always)] - fn bump_past(&self) { - self.bump(sys::size_of::()); - } - #[inline(always)] fn visit_inner(&self, inner: *TyDesc) -> bool { self.visit_ptr_inner(self.ptr, inner) @@ -467,18 +447,6 @@ impl TyVisitor for ReprVisitor { true } - #[cfg(stage0)] - fn visit_enter_enum(&self, n_variants: uint, - _sz: uint, _align: uint) -> bool { - if n_variants == 1 { - self.var_stk.push(Degenerate) - } else { - self.var_stk.push(TagMatch) - } - true - } - - #[cfg(not(stage0))] fn visit_enter_enum(&self, _n_variants: uint, get_disr: extern unsafe fn(ptr: *Opaque) -> int, _sz: uint, _align: uint) -> bool { @@ -487,40 +455,6 @@ impl TyVisitor for ReprVisitor { true } - #[cfg(stage0)] - fn visit_enter_enum_variant(&self, _variant: uint, - disr_val: int, - n_fields: uint, - name: &str) -> bool { - let mut write = false; - match self.var_stk.pop() { - Degenerate => { - write = true; - self.var_stk.push(Degenerate); - } - TagMatch | TagMismatch => { - do self.get::() |t| { - if disr_val == *t { - write = true; - self.var_stk.push(TagMatch); - } else { - self.var_stk.push(TagMismatch); - } - }; - self.bump_past::(); - } - } - - if write { - self.writer.write_str(name); - if n_fields > 0 { - self.writer.write_char('('); - } - } - true - } - - #[cfg(not(stage0))] fn visit_enter_enum_variant(&self, _variant: uint, disr_val: int, n_fields: uint, @@ -549,23 +483,6 @@ impl TyVisitor for ReprVisitor { true } - #[cfg(stage0)] - fn visit_enum_variant_field(&self, i: uint, inner: *TyDesc) -> bool { - match self.var_stk[vec::uniq_len(&const self.var_stk) - 1] { - Degenerate | TagMatch => { - if i != 0 { - self.writer.write_str(", "); - } - if ! self.visit_inner(inner) { - return false; - } - } - TagMismatch => () - } - true - } - - #[cfg(not(stage0))] fn visit_enum_variant_field(&self, i: uint, _offset: uint, inner: *TyDesc) -> bool { match self.var_stk[vec::uniq_len(&const self.var_stk) - 1] { Matched => { @@ -581,23 +498,6 @@ impl TyVisitor for ReprVisitor { true } - #[cfg(stage0)] - fn visit_leave_enum_variant(&self, _variant: uint, - _disr_val: int, - n_fields: uint, - _name: &str) -> bool { - match self.var_stk[vec::uniq_len(&const self.var_stk) - 1] { - Degenerate | TagMatch => { - if n_fields > 0 { - self.writer.write_char(')'); - } - } - TagMismatch => () - } - true - } - - #[cfg(not(stage0))] fn visit_leave_enum_variant(&self, _variant: uint, _disr_val: int, n_fields: uint, @@ -613,14 +513,6 @@ impl TyVisitor for ReprVisitor { true } - #[cfg(stage0)] - fn visit_leave_enum(&self, _n_variants: uint, - _sz: uint, _align: uint) -> bool { - self.var_stk.pop(); - true - } - - #[cfg(not(stage0))] fn visit_leave_enum(&self, _n_variants: uint, _get_disr: extern unsafe fn(ptr: *Opaque) -> int, _sz: uint, _align: uint) -> bool { diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 9171c5167bc7b..17cc07c660d1a 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -226,13 +226,6 @@ pub fn map_err(res: &Result, op: &fn(&E) -> F) } pub impl Result { - #[cfg(stage0)] - #[inline(always)] - fn get_ref(&self) -> &'self T { get_ref(self) } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] #[inline(always)] fn get_ref<'a>(&'a self) -> &'a T { get_ref(self) } diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index e93e0c6fc6cc9..e77ec82637e4b 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -49,27 +49,6 @@ mod context; mod thread; pub mod env; -#[cfg(stage0)] -pub fn start(main: *u8, _argc: int, _argv: *c_char, _crate_map: *u8) -> int { - use self::sched::{Scheduler, Task}; - use self::uvio::UvEventLoop; - - let loop_ = ~UvEventLoop::new(); - let mut sched = ~Scheduler::new(loop_); - let main_task = ~do Task::new(&mut sched.stack_pool) { - // XXX: Can't call a C function pointer from Rust yet - unsafe { rust_call_nullary_fn(main) }; - }; - sched.task_queue.push_back(main_task); - sched.run(); - return 0; - - extern { - fn rust_call_nullary_fn(f: *u8); - } -} - -#[cfg(not(stage0))] pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int { use self::sched::{Scheduler, Task}; use self::uvio::UvEventLoop; diff --git a/src/libcore/rt/rtio.rs b/src/libcore/rt/rtio.rs index 66eb79ba6ae4e..fd64438c61b46 100644 --- a/src/libcore/rt/rtio.rs +++ b/src/libcore/rt/rtio.rs @@ -24,11 +24,6 @@ pub trait EventLoop { fn run(&mut self); fn callback(&mut self, ~fn()); /// The asynchronous I/O services. Not all event loops may provide one - #[cfg(stage0)] - fn io(&mut self) -> Option<&'self mut IoFactoryObject>; - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject>; } diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index abdd8d6619a8a..fba91dd7d8ab9 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -69,14 +69,6 @@ impl EventLoop for UvEventLoop { } } - #[cfg(stage0)] - fn io(&mut self) -> Option<&'self mut IoFactoryObject> { - Some(&mut self.uvio) - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject> { Some(&mut self.uvio) } @@ -99,14 +91,6 @@ fn test_callback_run_once() { pub struct UvIoFactory(Loop); pub impl UvIoFactory { - #[cfg(stage0)] - fn uv_loop(&mut self) -> &'self mut Loop { - match self { &UvIoFactory(ref mut ptr) => ptr } - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn uv_loop<'a>(&'a mut self) -> &'a mut Loop { match self { &UvIoFactory(ref mut ptr) => ptr } } diff --git a/src/libcore/trie.rs b/src/libcore/trie.rs index f4e9ddbdd90a1..f0756be994432 100644 --- a/src/libcore/trie.rs +++ b/src/libcore/trie.rs @@ -56,16 +56,6 @@ impl Map for TrieMap { /// Visit all key-value pairs in order #[inline(always)] - #[cfg(stage0)] - fn each(&self, f: &fn(&uint, &'self T) -> bool) { - self.root.each(f); - } - - /// Visit all key-value pairs in order - #[inline(always)] - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn each<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) { self.root.each(f); } @@ -78,16 +68,6 @@ impl Map for TrieMap { /// Visit all values in order #[inline(always)] - #[cfg(stage0)] - fn each_value(&self, f: &fn(&T) -> bool) { - self.each(|_, v| f(v)) - } - - /// Visit all values in order - #[inline(always)] - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn each_value<'a>(&'a self, f: &fn(&'a T) -> bool) { self.each(|_, v| f(v)) } @@ -99,31 +79,6 @@ impl Map for TrieMap { } /// Return a reference to the value corresponding to the key - #[cfg(stage0)] - #[inline(hint)] - fn find(&self, key: &uint) -> Option<&'self T> { - let mut node: &'self TrieNode = &self.root; - let mut idx = 0; - loop { - match node.children[chunk(*key, idx)] { - Internal(ref x) => node = &**x, - External(stored, ref value) => { - if stored == *key { - return Some(value) - } else { - return None - } - } - Nothing => return None - } - idx += 1; - } - } - - /// Return a reference to the value corresponding to the key - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] #[inline(hint)] fn find<'a>(&'a self, key: &uint) -> Option<&'a T> { let mut node: &'a TrieNode = &self.root; @@ -145,16 +100,6 @@ impl Map for TrieMap { } /// Return a mutable reference to the value corresponding to the key - #[cfg(stage0)] - #[inline(always)] - fn find_mut(&mut self, key: &uint) -> Option<&'self mut T> { - find_mut(&mut self.root.children[chunk(*key, 0)], *key, 1) - } - - /// Return a mutable reference to the value corresponding to the key - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] #[inline(always)] fn find_mut<'a>(&'a mut self, key: &uint) -> Option<&'a mut T> { find_mut(&mut self.root.children[chunk(*key, 0)], *key, 1) @@ -193,16 +138,6 @@ pub impl TrieMap { /// Visit all key-value pairs in reverse order #[inline(always)] - #[cfg(stage0)] - fn each_reverse(&self, f: &fn(&uint, &'self T) -> bool) { - self.root.each_reverse(f); - } - - /// Visit all key-value pairs in reverse order - #[inline(always)] - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn each_reverse<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) { self.root.each_reverse(f); } @@ -298,21 +233,6 @@ impl TrieNode { } impl TrieNode { - #[cfg(stage0)] - fn each(&self, f: &fn(&uint, &'self T) -> bool) -> bool { - for uint::range(0, self.children.len()) |idx| { - match self.children[idx] { - Internal(ref x) => if !x.each(f) { return false }, - External(k, ref v) => if !f(&k, v) { return false }, - Nothing => () - } - } - true - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn each<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool { for uint::range(0, self.children.len()) |idx| { match self.children[idx] { @@ -324,21 +244,6 @@ impl TrieNode { true } - #[cfg(stage0)] - fn each_reverse(&self, f: &fn(&uint, &'self T) -> bool) -> bool { - for uint::range_rev(self.children.len(), 0) |idx| { - match self.children[idx - 1] { - Internal(ref x) => if !x.each_reverse(f) { return false }, - External(k, ref v) => if !f(&k, v) { return false }, - Nothing => () - } - } - true - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn each_reverse<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool { for uint::range_rev(self.children.len(), 0) |idx| { match self.children[idx - 1] { diff --git a/src/libcore/tuple.rs b/src/libcore/tuple.rs index a2b6f0eb1a714..6da22657906dd 100644 --- a/src/libcore/tuple.rs +++ b/src/libcore/tuple.rs @@ -56,39 +56,11 @@ impl Clone for (T, U) { } } -#[cfg(stage0)] -pub trait ImmutableTuple { - fn first_ref(&self) -> &'self T; - fn second_ref(&self) -> &'self U; -} - -#[cfg(stage0)] -impl ImmutableTuple for (T, U) { - #[inline(always)] - fn first_ref(&self) -> &'self T { - match *self { - (ref t, _) => t, - } - } - #[inline(always)] - fn second_ref(&self) -> &'self U { - match *self { - (_, ref u) => u, - } - } -} - -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] pub trait ImmutableTuple { fn first_ref<'a>(&'a self) -> &'a T; fn second_ref<'a>(&'a self) -> &'a U; } -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl ImmutableTuple for (T, U) { #[inline(always)] fn first_ref<'a>(&'a self) -> &'a T { diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index 611862a79e7e0..de0542afc399a 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -135,32 +135,6 @@ pub unsafe fn strdup_uniq(ptr: *c_uchar, len: uint) -> ~str { } #[lang="start"] -#[cfg(stage0)] -pub fn start(main: *u8, argc: int, argv: *c_char, - crate_map: *u8) -> int { - use libc::getenv; - use rt::start; - - unsafe { - let use_old_rt = do str::as_c_str("RUST_NEWRT") |s| { - getenv(s).is_null() - }; - if use_old_rt { - return rust_start(main as *c_void, argc as c_int, argv, - crate_map as *c_void) as int; - } else { - return start(main, argc, argv, crate_map); - } - } - - extern { - fn rust_start(main: *c_void, argc: c_int, argv: *c_char, - crate_map: *c_void) -> c_int; - } -} - -#[lang="start"] -#[cfg(not(stage0))] pub fn start(main: *u8, argc: int, argv: **c_char, crate_map: *u8) -> int { use libc::getenv; diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 86767dc5bad85..94f866643532c 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -1566,16 +1566,6 @@ pub fn each_permutation(v: &[T], put: &fn(ts: &[T]) -> bool) { } } -// see doc below -#[cfg(stage0)] // XXX: lifetimes! -pub fn windowed(n: uint, v: &[T], it: &fn(&[T]) -> bool) { - assert!(1u <= n); - if n > v.len() { return; } - for uint::range(0, v.len() - n + 1) |i| { - if !it(v.slice(i, i+n)) { return } - } -} - /** * Iterate over all contiguous windows of length `n` of the vector `v`. * @@ -1590,9 +1580,6 @@ pub fn windowed(n: uint, v: &[T], it: &fn(&[T]) -> bool) { * ~~~ * */ -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] pub fn windowed<'r, T>(n: uint, v: &'r [T], it: &fn(&'r [T]) -> bool) { assert!(1u <= n); if n > v.len() { return; } @@ -1854,150 +1841,6 @@ impl<'self,T:Copy> CopyableVector for &'self const [T] { } } -#[cfg(stage0)] -pub trait ImmutableVector { - fn slice(&self, start: uint, end: uint) -> &'self [T]; - fn head(&self) -> &'self T; - fn head_opt(&self) -> Option<&'self T>; - fn tail(&self) -> &'self [T]; - fn tailn(&self, n: uint) -> &'self [T]; - fn init(&self) -> &'self [T]; - fn initn(&self, n: uint) -> &'self [T]; - fn last(&self) -> &'self T; - fn last_opt(&self) -> Option<&'self T>; - fn each_reverse(&self, blk: &fn(&T) -> bool); - fn eachi_reverse(&self, blk: &fn(uint, &T) -> bool); - fn foldr(&self, z: U, p: &fn(t: &T, u: U) -> U) -> U; - fn map(&self, f: &fn(t: &T) -> U) -> ~[U]; - fn mapi(&self, f: &fn(uint, t: &T) -> U) -> ~[U]; - fn map_r(&self, f: &fn(x: &T) -> U) -> ~[U]; - fn alli(&self, f: &fn(uint, t: &T) -> bool) -> bool; - fn flat_map(&self, f: &fn(t: &T) -> ~[U]) -> ~[U]; - fn filter_mapped(&self, f: &fn(t: &T) -> Option) -> ~[U]; - unsafe fn unsafe_ref(&self, index: uint) -> *T; -} - -/// Extension methods for vectors -#[cfg(stage0)] -impl<'self,T> ImmutableVector for &'self [T] { - /// Return a slice that points into another slice. - #[inline] - fn slice(&self, start: uint, end: uint) -> &'self [T] { - slice(*self, start, end) - } - - /// Returns the first element of a vector, failing if the vector is empty. - #[inline] - fn head(&self) -> &'self T { head(*self) } - - /// Returns the first element of a vector - #[inline] - fn head_opt(&self) -> Option<&'self T> { head_opt(*self) } - - /// Returns all but the first element of a vector - #[inline] - fn tail(&self) -> &'self [T] { tail(*self) } - - /// Returns all but the first `n' elements of a vector - #[inline] - fn tailn(&self, n: uint) -> &'self [T] { tailn(*self, n) } - - /// Returns all but the last elemnt of a vector - #[inline] - fn init(&self) -> &'self [T] { init(*self) } - - /// Returns all but the last `n' elemnts of a vector - #[inline] - fn initn(&self, n: uint) -> &'self [T] { initn(*self, n) } - - /// Returns the last element of a `v`, failing if the vector is empty. - #[inline] - fn last(&self) -> &'self T { last(*self) } - - /// Returns the last element of a `v`, failing if the vector is empty. - #[inline] - fn last_opt(&self) -> Option<&'self T> { last_opt(*self) } - - /// Iterates over a vector's elements in reverse. - #[inline] - fn each_reverse(&self, blk: &fn(&T) -> bool) { - each_reverse(*self, blk) - } - - /// Iterates over a vector's elements and indices in reverse. - #[inline] - fn eachi_reverse(&self, blk: &fn(uint, &T) -> bool) { - eachi_reverse(*self, blk) - } - - /// Reduce a vector from right to left - #[inline] - fn foldr(&self, z: U, p: &fn(t: &T, u: U) -> U) -> U { - foldr(*self, z, p) - } - - /// Apply a function to each element of a vector and return the results - #[inline] - fn map(&self, f: &fn(t: &T) -> U) -> ~[U] { map(*self, f) } - - /** - * Apply a function to the index and value of each element in the vector - * and return the results - */ - fn mapi(&self, f: &fn(uint, t: &T) -> U) -> ~[U] { - mapi(*self, f) - } - - #[inline] - fn map_r(&self, f: &fn(x: &T) -> U) -> ~[U] { - let mut r = ~[]; - let mut i = 0; - while i < self.len() { - r.push(f(&self[i])); - i += 1; - } - r - } - - /** - * Returns true if the function returns true for all elements. - * - * If the vector is empty, true is returned. - */ - fn alli(&self, f: &fn(uint, t: &T) -> bool) -> bool { - alli(*self, f) - } - /** - * Apply a function to each element of a vector and return a concatenation - * of each result vector - */ - #[inline] - fn flat_map(&self, f: &fn(t: &T) -> ~[U]) -> ~[U] { - flat_map(*self, f) - } - /** - * Apply a function to each element of a vector and return the results - * - * If function `f` returns `none` then that element is excluded from - * the resulting vector. - */ - #[inline] - fn filter_mapped(&self, f: &fn(t: &T) -> Option) -> ~[U] { - filter_mapped(*self, f) - } - - /// Returns a pointer to the element at the given index, without doing - /// bounds checking. - #[inline(always)] - unsafe fn unsafe_ref(&self, index: uint) -> *T { - let (ptr, _): (*T, uint) = transmute(*self); - ptr.offset(index) - } -} - -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] pub trait ImmutableVector<'self, T> { fn slice(&self, start: uint, end: uint) -> &'self [T]; fn iter(self) -> VecIterator<'self, T>; @@ -2022,9 +1865,6 @@ pub trait ImmutableVector<'self, T> { } /// Extension methods for vectors -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl<'self,T> ImmutableVector<'self, T> for &'self [T] { /// Return a slice that points into another slice. #[inline] @@ -2634,17 +2474,6 @@ pub mod bytes { // ___________________________________________________________________________ // ITERATION TRAIT METHODS -#[cfg(stage0)] -impl<'self,A> old_iter::BaseIter for &'self [A] { - #[inline(always)] - fn each(&self, blk: &fn(v: &'self A) -> bool) { each(*self, blk) } - #[inline(always)] - fn size_hint(&self) -> Option { Some(self.len()) } -} - -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl<'self,A> old_iter::BaseIter for &'self [A] { #[inline(always)] fn each<'a>(&'a self, blk: &fn(v: &'a A) -> bool) { each(*self, blk) } @@ -2653,18 +2482,6 @@ impl<'self,A> old_iter::BaseIter for &'self [A] { } // FIXME(#4148): This should be redundant -#[cfg(stage0)] -impl old_iter::BaseIter for ~[A] { - #[inline(always)] - fn each(&self, blk: &fn(v: &'self A) -> bool) { each(*self, blk) } - #[inline(always)] - fn size_hint(&self) -> Option { Some(self.len()) } -} - -// FIXME(#4148): This should be redundant -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl old_iter::BaseIter for ~[A] { #[inline(always)] fn each<'a>(&'a self, blk: &fn(v: &'a A) -> bool) { each(*self, blk) } @@ -2673,18 +2490,6 @@ impl old_iter::BaseIter for ~[A] { } // FIXME(#4148): This should be redundant -#[cfg(stage0)] -impl old_iter::BaseIter for @[A] { - #[inline(always)] - fn each(&self, blk: &fn(v: &'self A) -> bool) { each(*self, blk) } - #[inline(always)] - fn size_hint(&self) -> Option { Some(self.len()) } -} - -// FIXME(#4148): This should be redundant -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl old_iter::BaseIter for @[A] { #[inline(always)] fn each<'a>(&'a self, blk: &fn(v: &'a A) -> bool) { each(*self, blk) } @@ -2692,17 +2497,6 @@ impl old_iter::BaseIter for @[A] { fn size_hint(&self) -> Option { Some(self.len()) } } -#[cfg(stage0)] -impl<'self,A> old_iter::MutableIter for &'self mut [A] { - #[inline(always)] - fn each_mut(&mut self, blk: &fn(v: &'self mut A) -> bool) { - each_mut(*self, blk) - } -} - -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl<'self,A> old_iter::MutableIter for &'self mut [A] { #[inline(always)] fn each_mut<'a>(&'a mut self, blk: &fn(v: &'a mut A) -> bool) { @@ -2711,17 +2505,6 @@ impl<'self,A> old_iter::MutableIter for &'self mut [A] { } // FIXME(#4148): This should be redundant -#[cfg(stage0)] -impl old_iter::MutableIter for ~[A] { - #[inline(always)] - fn each_mut(&mut self, blk: &fn(v: &'self mut A) -> bool) { - each_mut(*self, blk) - } -} - -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl old_iter::MutableIter for ~[A] { #[inline(always)] fn each_mut<'a>(&'a mut self, blk: &fn(v: &'a mut A) -> bool) { diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index c7c9c110586c7..c65521228fa87 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -557,29 +557,6 @@ trait read_method_map_entry_helper { -> method_map_entry; } -#[cfg(stage0)] -fn encode_method_map_entry(ecx: @e::EncodeContext, - ebml_w: &writer::Encoder, - mme: method_map_entry) { - do ebml_w.emit_struct("method_map_entry", 3) { - do ebml_w.emit_field(~"self_arg", 0u) { - ebml_w.emit_arg(ecx, mme.self_arg); - } - do ebml_w.emit_field(~"explicit_self", 2u) { - mme.explicit_self.encode(ebml_w); - } - do ebml_w.emit_field(~"origin", 1u) { - mme.origin.encode(ebml_w); - } - do ebml_w.emit_field(~"self_mode", 3) { - mme.self_mode.encode(ebml_w); - } - } -} - -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] fn encode_method_map_entry(ecx: @e::EncodeContext, ebml_w: &writer::Encoder, mme: method_map_entry) { @@ -600,34 +577,6 @@ fn encode_method_map_entry(ecx: @e::EncodeContext, } impl read_method_map_entry_helper for reader::Decoder { - #[cfg(stage0)] - fn read_method_map_entry(&self, xcx: @ExtendedDecodeContext) - -> method_map_entry { - do self.read_struct("method_map_entry", 3) { - method_map_entry { - self_arg: self.read_field(~"self_arg", 0u, || { - self.read_arg(xcx) - }), - explicit_self: self.read_field(~"explicit_self", 2u, || { - let self_type: ast::self_ty_ = Decodable::decode(self); - self_type - }), - origin: self.read_field(~"origin", 1u, || { - let method_origin: method_origin = - Decodable::decode(self); - method_origin.tr(xcx) - }), - self_mode: self.read_field(~"self_mode", 3, || { - let self_mode: ty::SelfMode = Decodable::decode(self); - self_mode - }), - } - } - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn read_method_map_entry(&self, xcx: @ExtendedDecodeContext) -> method_map_entry { do self.read_struct("method_map_entry", 3) { @@ -841,33 +790,6 @@ impl ebml_writer_helpers for writer::Encoder { } } - #[cfg(stage0)] - fn emit_tpbt(&self, ecx: @e::EncodeContext, - tpbt: ty::ty_param_bounds_and_ty) { - do self.emit_struct("ty_param_bounds_and_ty", 2) { - do self.emit_field(~"generics", 0) { - do self.emit_struct("Generics", 2) { - do self.emit_field(~"type_param_defs", 0) { - do self.emit_from_vec(*tpbt.generics.type_param_defs) - |type_param_def| - { - self.emit_type_param_def(ecx, type_param_def); - } - } - do self.emit_field(~"region_param", 1) { - tpbt.generics.region_param.encode(self); - } - } - } - do self.emit_field(~"ty", 1) { - self.emit_ty(ecx, tpbt.ty); - } - } - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn emit_tpbt(&self, ecx: @e::EncodeContext, tpbt: ty::ty_param_bounds_and_ty) { do self.emit_struct("ty_param_bounds_and_ty", 2) { @@ -1126,32 +1048,6 @@ impl ebml_decoder_decoder_helpers for reader::Decoder { } } - #[cfg(stage0)] - fn read_ty_param_bounds_and_ty(&self, xcx: @ExtendedDecodeContext) - -> ty::ty_param_bounds_and_ty - { - do self.read_struct("ty_param_bounds_and_ty", 2) { - ty::ty_param_bounds_and_ty { - generics: do self.read_struct("Generics", 2) { - ty::Generics { - type_param_defs: self.read_field("type_param_defs", 0, || { - @self.read_to_vec(|| self.read_type_param_def(xcx)) - }), - region_param: self.read_field(~"region_param", 1, || { - Decodable::decode(self) - }) - } - }, - ty: self.read_field(~"ty", 1, || { - self.read_ty(xcx) - }) - } - } - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn read_ty_param_bounds_and_ty(&self, xcx: @ExtendedDecodeContext) -> ty::ty_param_bounds_and_ty { diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc index 54c51cf2e487a..9e34d5c6177e6 100644 --- a/src/librustc/rustc.rc +++ b/src/librustc/rustc.rc @@ -85,6 +85,7 @@ pub mod middle { pub mod lint; #[path = "borrowck/mod.rs"] pub mod borrowck; + pub mod dataflow; pub mod mem_categorization; pub mod liveness; pub mod kind; diff --git a/src/libstd/arena.rs b/src/libstd/arena.rs index 8e2c734504512..e714af5fa1890 100644 --- a/src/libstd/arena.rs +++ b/src/libstd/arena.rs @@ -201,21 +201,6 @@ pub impl Arena { } #[inline(always)] - #[cfg(stage0)] - priv fn alloc_pod(&self, op: &fn() -> T) -> &'self T { - unsafe { - let tydesc = sys::get_type_desc::(); - let ptr = self.alloc_pod_inner((*tydesc).size, (*tydesc).align); - let ptr: *mut T = transmute(ptr); - rusti::move_val_init(&mut (*ptr), op()); - return transmute(ptr); - } - } - - #[inline(always)] - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] priv fn alloc_pod<'a, T>(&'a self, op: &fn() -> T) -> &'a T { unsafe { let tydesc = sys::get_type_desc::(); @@ -261,31 +246,6 @@ pub impl Arena { } #[inline(always)] - #[cfg(stage0)] - priv fn alloc_nonpod(&self, op: &fn() -> T) -> &'self T { - unsafe { - let tydesc = sys::get_type_desc::(); - let (ty_ptr, ptr) = - self.alloc_nonpod_inner((*tydesc).size, (*tydesc).align); - let ty_ptr: *mut uint = transmute(ty_ptr); - let ptr: *mut T = transmute(ptr); - // Write in our tydesc along with a bit indicating that it - // has *not* been initialized yet. - *ty_ptr = transmute(tydesc); - // Actually initialize it - rusti::move_val_init(&mut(*ptr), op()); - // Now that we are done, update the tydesc to indicate that - // the object is there. - *ty_ptr = bitpack_tydesc_ptr(tydesc, true); - - return transmute(ptr); - } - } - - #[inline(always)] - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] priv fn alloc_nonpod<'a, T>(&'a self, op: &fn() -> T) -> &'a T { unsafe { let tydesc = sys::get_type_desc::(); @@ -308,22 +268,6 @@ pub impl Arena { // The external interface #[inline(always)] - #[cfg(stage0)] - fn alloc(&self, op: &fn() -> T) -> &'self T { - unsafe { - if !rusti::needs_drop::() { - self.alloc_pod(op) - } else { - self.alloc_nonpod(op) - } - } - } - - // The external interface - #[inline(always)] - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn alloc<'a, T>(&'a self, op: &fn() -> T) -> &'a T { unsafe { if !rusti::needs_drop::() { diff --git a/src/libstd/deque.rs b/src/libstd/deque.rs index 5d52bb7c0810b..64d708e0b2e89 100644 --- a/src/libstd/deque.rs +++ b/src/libstd/deque.rs @@ -37,128 +37,6 @@ impl Mutable for Deque { } } -#[cfg(stage0)] -pub impl Deque { - /// Create an empty Deque - fn new() -> Deque { - Deque{nelts: 0, lo: 0, hi: 0, - elts: vec::from_fn(initial_capacity, |_| None)} - } - - /// Return a reference to the first element in the deque - /// - /// Fails if the deque is empty - #[cfg(stage0)] - fn peek_front(&self) -> &'self T { get(self.elts, self.lo) } - - /// Return a reference to the first element in the deque - /// - /// Fails if the deque is empty - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] - fn peek_front<'a>(&'a self) -> &'a T { get(self.elts, self.lo) } - - /// Return a reference to the last element in the deque - /// - /// Fails if the deque is empty - #[cfg(stage0)] - fn peek_back(&self) -> &'self T { get(self.elts, self.hi - 1u) } - - /// Return a reference to the last element in the deque - /// - /// Fails if the deque is empty - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] - fn peek_back<'a>(&'a self) -> &'a T { get(self.elts, self.hi - 1u) } - - /// Retrieve an element in the deque by index - /// - /// Fails if there is no element with the given index - #[cfg(stage0)] - fn get(&self, i: int) -> &'self T { - let idx = (self.lo + (i as uint)) % self.elts.len(); - get(self.elts, idx) - } - - /// Retrieve an element in the deque by index - /// - /// Fails if there is no element with the given index - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] - fn get<'a>(&'a self, i: int) -> &'a T { - let idx = (self.lo + (i as uint)) % self.elts.len(); - get(self.elts, idx) - } - - /// Iterate over the elements in the deque - fn each(&self, f: &fn(&T) -> bool) { - self.eachi(|_i, e| f(e)) - } - - /// Iterate over the elements in the deque by index - fn eachi(&self, f: &fn(uint, &T) -> bool) { - for uint::range(0, self.nelts) |i| { - if !f(i, self.get(i as int)) { return; } - } - } - - /// Remove and return the first element in the deque - /// - /// Fails if the deque is empty - fn pop_front(&mut self) -> T { - let result = self.elts[self.lo].swap_unwrap(); - self.lo = (self.lo + 1u) % self.elts.len(); - self.nelts -= 1u; - result - } - - /// Remove and return the last element in the deque - /// - /// Fails if the deque is empty - fn pop_back(&mut self) -> T { - if self.hi == 0u { - self.hi = self.elts.len() - 1u; - } else { self.hi -= 1u; } - let result = self.elts[self.hi].swap_unwrap(); - self.elts[self.hi] = None; - self.nelts -= 1u; - result - } - - /// Prepend an element to the deque - fn add_front(&mut self, t: T) { - let oldlo = self.lo; - if self.lo == 0u { - self.lo = self.elts.len() - 1u; - } else { self.lo -= 1u; } - if self.lo == self.hi { - self.elts = grow(self.nelts, oldlo, self.elts); - self.lo = self.elts.len() - 1u; - self.hi = self.nelts; - } - self.elts[self.lo] = Some(t); - self.nelts += 1u; - } - - /// Append an element to the deque - fn add_back(&mut self, t: T) { - if self.lo == self.hi && self.nelts != 0u { - self.elts = grow(self.nelts, self.lo, self.elts); - self.lo = 0u; - self.hi = self.nelts; - } - self.elts[self.hi] = Some(t); - self.hi = (self.hi + 1u) % self.elts.len(); - self.nelts += 1u; - } -} - -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] pub impl Deque { /// Create an empty Deque fn new() -> Deque { diff --git a/src/libstd/ebml.rs b/src/libstd/ebml.rs index 2598e96a141e2..9b89036eee51b 100644 --- a/src/libstd/ebml.rs +++ b/src/libstd/ebml.rs @@ -400,16 +400,6 @@ pub mod reader { f() } - #[cfg(stage0)] - fn read_field(&self, name: &str, idx: uint, f: &fn() -> T) -> T { - debug!("read_field(name=%?, idx=%u)", name, idx); - self._check_label(name); - f() - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn read_struct_field(&self, name: &str, idx: uint, f: &fn() -> T) -> T { debug!("read_struct_field(name=%?, idx=%u)", name, idx); self._check_label(name); @@ -714,14 +704,6 @@ pub mod writer { } fn emit_struct(&self, _name: &str, _len: uint, f: &fn()) { f() } - #[cfg(stage0)] - fn emit_field(&self, name: &str, _idx: uint, f: &fn()) { - self._emit_label(name); - f() - } - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn emit_struct_field(&self, name: &str, _idx: uint, f: &fn()) { self._emit_label(name); f() diff --git a/src/libstd/future.rs b/src/libstd/future.rs index f59abfa81ca10..c3fc16bdf70ba 100644 --- a/src/libstd/future.rs +++ b/src/libstd/future.rs @@ -54,35 +54,6 @@ pub impl Future { } pub impl Future { - #[cfg(stage0)] - fn get_ref(&self) -> &'self A { - /*! - * Executes the future's closure and then returns a borrowed - * pointer to the result. The borrowed pointer lasts as long as - * the future. - */ - unsafe { - match self.state { - Forced(ref mut v) => { return cast::transmute(v); } - Evaluating => fail!(~"Recursive forcing of future!"), - Pending(_) => {} - } - - let mut state = Evaluating; - self.state <-> state; - match state { - Forced(_) | Evaluating => fail!(~"Logic error."), - Pending(f) => { - self.state = Forced(f()); - self.get_ref() - } - } - } - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn get_ref<'a>(&'a self) -> &'a A { /*! * Executes the future's closure and then returns a borrowed diff --git a/src/libstd/json.rs b/src/libstd/json.rs index 7353bec7333c5..5d5b0bd952f18 100644 --- a/src/libstd/json.rs +++ b/src/libstd/json.rs @@ -143,16 +143,6 @@ impl serialize::Encoder for Encoder { f(); self.wr.write_char('}'); } - #[cfg(stage0)] - fn emit_field(&self, name: &str, idx: uint, f: &fn()) { - if idx != 0 { self.wr.write_char(','); } - self.wr.write_str(escape_str(name)); - self.wr.write_char(':'); - f(); - } - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn emit_struct_field(&self, name: &str, idx: uint, f: &fn()) { if idx != 0 { self.wr.write_char(','); } self.wr.write_str(escape_str(name)); @@ -289,21 +279,6 @@ impl serialize::Encoder for PrettyEncoder { self.wr.write_char('}'); } } - #[cfg(stage0)] - fn emit_field(&self, name: &str, idx: uint, f: &fn()) { - if idx == 0 { - self.wr.write_char('\n'); - } else { - self.wr.write_str(",\n"); - } - self.wr.write_str(spaces(self.indent)); - self.wr.write_str(escape_str(name)); - self.wr.write_str(": "); - f(); - } - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn emit_struct_field(&self, name: &str, idx: uint, f: &fn()) { if idx == 0 { self.wr.write_char('\n'); @@ -901,29 +876,6 @@ impl serialize::Decoder for Decoder { value } - #[cfg(stage0)] - fn read_field(&self, name: &str, idx: uint, f: &fn() -> T) -> T { - debug!("read_field(name=%?, idx=%u)", name, idx); - match self.stack.pop() { - Object(obj) => { - let mut obj = obj; - let value = match obj.pop(&name.to_owned()) { - None => fail!(fmt!("no such field: %s", name)), - Some(json) => { - self.stack.push(json); - f() - } - }; - self.stack.push(Object(obj)); - value - } - value => fail!(fmt!("not an object: %?", value)) - } - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn read_struct_field(&self, name: &str, idx: uint, f: &fn() -> T) -> T { debug!("read_struct_field(name=%?, idx=%u)", name, idx); match self.stack.pop() { diff --git a/src/libstd/priority_queue.rs b/src/libstd/priority_queue.rs index 47af3576c9062..33fe1cfff8e59 100644 --- a/src/libstd/priority_queue.rs +++ b/src/libstd/priority_queue.rs @@ -45,25 +45,9 @@ impl Mutable for PriorityQueue { pub impl PriorityQueue { /// Returns the greatest item in the queue - fails if empty - #[cfg(stage0)] - fn top(&self) -> &'self T { &self.data[0] } - - /// Returns the greatest item in the queue - fails if empty - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn top<'a>(&'a self) -> &'a T { &self.data[0] } /// Returns the greatest item in the queue - None if empty - #[cfg(stage0)] - fn maybe_top(&self) -> Option<&'self T> { - if self.is_empty() { None } else { Some(self.top()) } - } - - /// Returns the greatest item in the queue - None if empty - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn maybe_top<'a>(&'a self) -> Option<&'a T> { if self.is_empty() { None } else { Some(self.top()) } } diff --git a/src/libstd/serialize.rs b/src/libstd/serialize.rs index 1ad581ba993e4..032df4c819cdd 100644 --- a/src/libstd/serialize.rs +++ b/src/libstd/serialize.rs @@ -55,11 +55,6 @@ pub trait Encoder { fn emit_enum_struct_variant_field(&self, f_name: &str, f_idx: uint, f: &fn()); fn emit_struct(&self, name: &str, len: uint, f: &fn()); - #[cfg(stage0)] - fn emit_field(&self, f_name: &str, f_idx: uint, f: &fn()); - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn emit_struct_field(&self, f_name: &str, f_idx: uint, f: &fn()); fn emit_tuple(&self, len: uint, f: &fn()); @@ -111,11 +106,6 @@ pub trait Decoder { fn read_enum_struct_variant_field(&self, &f_name: &str, f_idx: uint, f: &fn() -> T) -> T; fn read_struct(&self, s_name: &str, len: uint, f: &fn() -> T) -> T; - #[cfg(stage0)] - fn read_field(&self, f_name: &str, f_idx: uint, f: &fn() -> T) -> T; - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn read_struct_field(&self, f_name: &str, f_idx: uint, f: &fn() -> T) -> T; fn read_tuple(&self, f: &fn(uint) -> T) -> T; diff --git a/src/libstd/smallintmap.rs b/src/libstd/smallintmap.rs index fb17d4e50900c..1b72300a178ba 100644 --- a/src/libstd/smallintmap.rs +++ b/src/libstd/smallintmap.rs @@ -50,20 +50,6 @@ impl Map for SmallIntMap { } /// Visit all key-value pairs in order - #[cfg(stage0)] - fn each(&self, it: &fn(&uint, &'self V) -> bool) { - for uint::range(0, self.v.len()) |i| { - match self.v[i] { - Some(ref elt) => if !it(&i, elt) { break }, - None => () - } - } - } - - /// Visit all key-value pairs in order - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn each<'a>(&'a self, it: &fn(&uint, &'a V) -> bool) { for uint::range(0, self.v.len()) |i| { match self.v[i] { @@ -79,15 +65,6 @@ impl Map for SmallIntMap { } /// Visit all values in order - #[cfg(stage0)] - fn each_value(&self, blk: &fn(value: &V) -> bool) { - self.each(|_, v| blk(v)) - } - - /// Visit all values in order - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn each_value<'a>(&'a self, blk: &fn(value: &'a V) -> bool) { self.each(|_, v| blk(v)) } @@ -103,22 +80,6 @@ impl Map for SmallIntMap { } /// Return a reference to the value corresponding to the key - #[cfg(stage0)] - fn find(&self, key: &uint) -> Option<&'self V> { - if *key < self.v.len() { - match self.v[*key] { - Some(ref value) => Some(value), - None => None - } - } else { - None - } - } - - /// Return a reference to the value corresponding to the key - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn find<'a>(&'a self, key: &uint) -> Option<&'a V> { if *key < self.v.len() { match self.v[*key] { @@ -131,22 +92,6 @@ impl Map for SmallIntMap { } /// Return a mutable reference to the value corresponding to the key - #[cfg(stage0)] - fn find_mut(&mut self, key: &uint) -> Option<&'self mut V> { - if *key < self.v.len() { - match self.v[*key] { - Some(ref mut value) => Some(value), - None => None - } - } else { - None - } - } - - /// Return a mutable reference to the value corresponding to the key - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn find_mut<'a>(&'a mut self, key: &uint) -> Option<&'a mut V> { if *key < self.v.len() { match self.v[*key] { @@ -188,20 +133,6 @@ pub impl SmallIntMap { fn new() -> SmallIntMap { SmallIntMap{v: ~[]} } /// Visit all key-value pairs in reverse order - #[cfg(stage0)] - fn each_reverse(&self, it: &fn(uint, &'self V) -> bool) { - for uint::range_rev(self.v.len(), 0) |i| { - match self.v[i - 1] { - Some(ref elt) => if !it(i - 1, elt) { break }, - None => () - } - } - } - - /// Visit all key-value pairs in reverse order - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn each_reverse<'a>(&'a self, it: &fn(uint, &'a V) -> bool) { for uint::range_rev(self.v.len(), 0) |i| { match self.v[i - 1] { @@ -211,14 +142,6 @@ pub impl SmallIntMap { } } - #[cfg(stage0)] - fn get(&self, key: &uint) -> &'self V { - self.find(key).expect("key not present") - } - - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn get<'a>(&'a self, key: &uint) -> &'a V { self.find(key).expect("key not present") } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 2d6d74b5c1e32..5bad9ecae3ed7 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -451,17 +451,6 @@ impl MapChain{ // ugh: can't get this to compile with mut because of the // lack of flow sensitivity. - #[cfg(stage0)] - fn get_map(&self) -> &'self HashMap { - match *self { - BaseMapChain (~ref map) => map, - ConsMapChain (~ref map,_) => map - } - } - - // ugh: can't get this to compile with mut because of the - // lack of flow sensitivity. - #[cfg(not(stage0))] fn get_map<'a>(&'a self) -> &'a HashMap { match *self { BaseMapChain (~ref map) => map, diff --git a/src/libsyntax/opt_vec.rs b/src/libsyntax/opt_vec.rs index 6cf7bba600ec0..600ab964e5238 100644 --- a/src/libsyntax/opt_vec.rs +++ b/src/libsyntax/opt_vec.rs @@ -61,15 +61,6 @@ impl OptVec { } } - #[cfg(stage0)] - fn get(&self, i: uint) -> &'self T { - match *self { - Empty => fail!(fmt!("Invalid index %u", i)), - Vec(ref v) => &v[i] - } - } - - #[cfg(not(stage0))] fn get<'a>(&'a self, i: uint) -> &'a T { match *self { Empty => fail!(fmt!("Invalid index %u", i)), From b5a7e8b35322869b1cf51bd1b35a86e9e721da54 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 22 Apr 2013 18:09:23 -0400 Subject: [PATCH 02/46] desnapshot --- src/libcore/core.rc | 3 --- src/libcore/num/f32.rs | 16 ++-------------- src/libcore/num/f64.rs | 12 ++---------- src/libcore/num/float.rs | 15 ++------------- src/libcore/num/int-template.rs | 14 -------------- src/libcore/num/num.rs | 28 ++-------------------------- src/libcore/num/strconv.rs | 9 --------- src/libcore/num/uint-template.rs | 14 -------------- src/libcore/ops.rs | 12 ------------ src/libcore/prelude.rs | 3 --- src/libcore/repr.rs | 1 - src/libcore/rt/io/mod.rs | 3 --- src/libstd/std.rc | 3 --- 13 files changed, 8 insertions(+), 125 deletions(-) diff --git a/src/libcore/core.rc b/src/libcore/core.rc index f9a56f613d542..f6e4056f3d0fc 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -75,9 +75,6 @@ they contained the following prologue: pub use kinds::{Const, Copy, Owned, Durable}; pub use ops::{Drop}; -#[cfg(stage0)] -pub use ops::{Add, Sub, Mul, Div, Modulo, Neg, Not}; -#[cfg(not(stage0))] pub use ops::{Add, Sub, Mul, Quot, Rem, Neg, Not}; pub use ops::{BitAnd, BitOr, BitXor}; pub use ops::{Shl, Shr, Index}; diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index e687f482fa98c..6398127a5faff 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -278,24 +278,12 @@ impl Mul for f32 { #[inline(always)] fn mul(&self, other: &f32) -> f32 { *self * *other } } - -#[cfg(stage0,notest)] -impl Div for f32 { - #[inline(always)] - fn div(&self, other: &f32) -> f32 { *self / *other } -} -#[cfg(not(stage0),notest)] +#[cfg(notest)] impl Quot for f32 { #[inline(always)] fn quot(&self, other: &f32) -> f32 { *self / *other } } - -#[cfg(stage0,notest)] -impl Modulo for f32 { - #[inline(always)] - fn modulo(&self, other: &f32) -> f32 { *self % *other } -} -#[cfg(not(stage0),notest)] +#[cfg(notest)] impl Rem for f32 { #[inline(always)] fn rem(&self, other: &f32) -> f32 { *self % *other } diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index d00e6ae2c0d79..013f3c5095e2a 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -296,20 +296,12 @@ impl Sub for f64 { impl Mul for f64 { fn mul(&self, other: &f64) -> f64 { *self * *other } } -#[cfg(stage0,notest)] -impl Div for f64 { - fn div(&self, other: &f64) -> f64 { *self / *other } -} -#[cfg(not(stage0),notest)] +#[cfg(notest)] impl Quot for f64 { #[inline(always)] fn quot(&self, other: &f64) -> f64 { *self / *other } } -#[cfg(stage0,notest)] -impl Modulo for f64 { - fn modulo(&self, other: &f64) -> f64 { *self % *other } -} -#[cfg(not(stage0),notest)] +#[cfg(notest)] impl Rem for f64 { #[inline(always)] fn rem(&self, other: &f64) -> f64 { *self % *other } diff --git a/src/libcore/num/float.rs b/src/libcore/num/float.rs index 3aa8848cdbed2..496ad4ec176b7 100644 --- a/src/libcore/num/float.rs +++ b/src/libcore/num/float.rs @@ -691,23 +691,12 @@ impl Mul for float { #[inline(always)] fn mul(&self, other: &float) -> float { *self * *other } } - -#[cfg(stage0,notest)] -impl Div for float { - #[inline(always)] - fn div(&self, other: &float) -> float { *self / *other } -} -#[cfg(not(stage0),notest)] +#[cfg(notest)] impl Quot for float { #[inline(always)] fn quot(&self, other: &float) -> float { *self / *other } } -#[cfg(stage0,notest)] -impl Modulo for float { - #[inline(always)] - fn modulo(&self, other: &float) -> float { *self % *other } -} -#[cfg(not(stage0),notest)] +#[cfg(notest)] impl Rem for float { #[inline(always)] fn rem(&self, other: &float) -> float { *self % *other } diff --git a/src/libcore/num/int-template.rs b/src/libcore/num/int-template.rs index ec38a32c039d6..85489755fc7a9 100644 --- a/src/libcore/num/int-template.rs +++ b/src/libcore/num/int-template.rs @@ -200,13 +200,6 @@ impl Mul for T { #[inline(always)] fn mul(&self, other: &T) -> T { *self * *other } } - -#[cfg(stage0,notest)] -impl Div for T { - #[inline(always)] - fn div(&self, other: &T) -> T { *self / *other } -} -#[cfg(not(stage0),notest)] impl Quot for T { /// /// Returns the integer quotient, truncated towards 0. As this behaviour reflects @@ -229,13 +222,6 @@ impl Quot for T { #[inline(always)] fn quot(&self, other: &T) -> T { *self / *other } } - -#[cfg(stage0,notest)] -impl Modulo for T { - #[inline(always)] - fn modulo(&self, other: &T) -> T { *self % *other } -} -#[cfg(not(stage0),notest)] impl Rem for T { /// /// Returns the integer remainder after division, satisfying: diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index 3e43ebfef1222..de7597fa821d6 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -10,13 +10,6 @@ //! An interface for numeric types use cmp::{Eq, Ord}; -#[cfg(stage0)] -use ops::{Add, Sub, Mul, Neg}; -#[cfg(stage0)] -use Quot = ops::Div; -#[cfg(stage0)] -use Rem = ops::Modulo; -#[cfg(not(stage0))] use ops::{Add, Sub, Mul, Quot, Rem, Neg}; use ops::{Not, BitAnd, BitOr, BitXor, Shl, Shr}; use option::Option; @@ -391,25 +384,8 @@ pub fn pow_with_uint+Mul>( total } -/// Helper function for testing numeric operations -#[cfg(stage0,test)] -pub fn test_num(ten: T, two: T) { - assert_eq!(ten.add(&two), cast(12)); - assert_eq!(ten.sub(&two), cast(8)); - assert_eq!(ten.mul(&two), cast(20)); - assert_eq!(ten.div(&two), cast(5)); - assert_eq!(ten.modulo(&two), cast(0)); - - assert_eq!(ten.add(&two), ten + two); - assert_eq!(ten.sub(&two), ten - two); - assert_eq!(ten.mul(&two), ten * two); - assert_eq!(ten.div(&two), ten / two); - assert_eq!(ten.modulo(&two), ten % two); -} -#[cfg(stage1,test)] -#[cfg(stage2,test)] -#[cfg(stage3,test)] -pub fn test_num(ten: T, two: T) { +#[cfg(test)] +fn test_num(ten: T, two: T) { assert_eq!(ten.add(&two), cast(12)); assert_eq!(ten.sub(&two), cast(8)); assert_eq!(ten.mul(&two), cast(20)); diff --git a/src/libcore/num/strconv.rs b/src/libcore/num/strconv.rs index 2f3cd92dac09e..004789e7fc1ca 100644 --- a/src/libcore/num/strconv.rs +++ b/src/libcore/num/strconv.rs @@ -9,15 +9,6 @@ // except according to those terms. use core::cmp::{Ord, Eq}; -#[cfg(stage0)] -use ops::{Add, Sub, Mul, Neg}; -#[cfg(stage0)] -use Quot = ops::Div; -#[cfg(stage0)] -use Rem = ops::Modulo; -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] use ops::{Add, Sub, Mul, Quot, Rem, Neg}; use option::{None, Option, Some}; use char; diff --git a/src/libcore/num/uint-template.rs b/src/libcore/num/uint-template.rs index 3dfdd22c42dc1..f975226cde63b 100644 --- a/src/libcore/num/uint-template.rs +++ b/src/libcore/num/uint-template.rs @@ -165,24 +165,10 @@ impl Mul for T { #[inline(always)] fn mul(&self, other: &T) -> T { *self * *other } } - -#[cfg(stage0,notest)] -impl Div for T { - #[inline(always)] - fn div(&self, other: &T) -> T { *self / *other } -} -#[cfg(not(stage0),notest)] impl Quot for T { #[inline(always)] fn quot(&self, other: &T) -> T { *self / *other } } - -#[cfg(stage0,notest)] -impl Modulo for T { - #[inline(always)] - fn modulo(&self, other: &T) -> T { *self % *other } -} -#[cfg(not(stage0),notest)] impl Rem for T { #[inline(always)] fn rem(&self, other: &T) -> T { *self % *other } diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 1aa7aada05c88..18dcf34e49bfb 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -30,24 +30,12 @@ pub trait Mul { fn mul(&self, rhs: &RHS) -> Result; } -#[lang="div"] -#[cfg(stage0)] -pub trait Div { - fn div(&self, rhs: &RHS) -> Result; -} #[lang="quot"] -#[cfg(not(stage0))] pub trait Quot { fn quot(&self, rhs: &RHS) -> Result; } -#[lang="modulo"] -#[cfg(stage0)] -pub trait Modulo { - fn modulo(&self, rhs: &RHS) -> Result; -} #[lang="rem"] -#[cfg(not(stage0))] pub trait Rem { fn rem(&self, rhs: &RHS) -> Result; } diff --git a/src/libcore/prelude.rs b/src/libcore/prelude.rs index 9a2e480ce6e54..318725d2822a5 100644 --- a/src/libcore/prelude.rs +++ b/src/libcore/prelude.rs @@ -14,9 +14,6 @@ pub use either::{Either, Left, Right}; pub use kinds::{Const, Copy, Owned, Durable}; -#[cfg(stage0)] -pub use ops::{Add, Sub, Mul, Div, Modulo, Neg, Not}; -#[cfg(not(stage0))] pub use ops::{Add, Sub, Mul, Quot, Rem, Neg, Not}; pub use ops::{BitAnd, BitOr, BitXor}; pub use ops::{Drop}; diff --git a/src/libcore/repr.rs b/src/libcore/repr.rs index 2d6412b0cc832..abcb727809eea 100644 --- a/src/libcore/repr.rs +++ b/src/libcore/repr.rs @@ -23,7 +23,6 @@ use io::{Writer, WriterUtil}; use libc::c_void; use managed; use ptr; -#[cfg(stage0)] use sys; use reflect; use reflect::{MovePtr, align}; use to_str::ToStr; diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index b035532144c44..cb00b02d9d1b6 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -125,18 +125,15 @@ pub mod file; pub mod net; /// Readers and Writers for memory buffers and strings. -#[cfg(not(stage0))] // XXX Using unsnapshotted features pub mod mem; /// Non-blocking access to stdin, stdout, stderr pub mod stdio; /// Basic stream compression. XXX: Belongs with other flate code -#[cfg(not(stage0))] // XXX Using unsnapshotted features pub mod flate; /// Interop between byte streams and pipes. Not sure where it belongs -#[cfg(not(stage0))] // XXX " pub mod comm_adapters; /// Extension traits diff --git a/src/libstd/std.rc b/src/libstd/std.rc index 0a5348d79760e..40db9f89d0fd7 100644 --- a/src/libstd/std.rc +++ b/src/libstd/std.rc @@ -91,13 +91,10 @@ pub mod cmp; pub mod base64; pub mod rl; pub mod workcache; -#[cfg(not(stage0))] #[path="num/bigint.rs"] pub mod bigint; -#[cfg(not(stage0))] #[path="num/rational.rs"] pub mod rational; -#[cfg(not(stage0))] #[path="num/complex.rs"] pub mod complex; pub mod stats; From a896440ca1b93cc5dc6edd827ea2ae89602bfa9e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 15 Mar 2013 15:24:24 -0400 Subject: [PATCH 03/46] new borrow checker (mass squash) --- src/libcore/cleanup.rs | 40 +- src/libcore/io.rs | 14 +- src/libcore/rt/sched/mod.rs | 2 +- src/libcore/str.rs | 12 - src/libcore/to_bytes.rs | 2 +- src/libcore/unstable/lang.rs | 47 +- src/libcore/vec.rs | 11 +- src/librustc/driver/driver.rs | 21 +- src/librustc/driver/session.rs | 8 + src/librustc/front/test.rs | 5 +- src/librustc/metadata/decoder.rs | 4 +- src/librustc/metadata/tydecode.rs | 3 + src/librustc/metadata/tyencode.rs | 48 +- src/librustc/middle/astencode.rs | 43 +- src/librustc/middle/borrowck/check_loans.rs | 1063 ++++++++--------- src/librustc/middle/borrowck/doc.rs | 750 ++++++++++++ src/librustc/middle/borrowck/gather_loans.rs | 643 ---------- .../middle/borrowck/gather_loans/lifetime.rs | 322 +++++ .../middle/borrowck/gather_loans/mod.rs | 710 +++++++++++ .../borrowck/gather_loans/restrictions.rs | 251 ++++ src/librustc/middle/borrowck/loan.rs | 312 ----- src/librustc/middle/borrowck/mod.rs | 861 ++++++------- src/librustc/middle/borrowck/preserve.rs | 409 ------- src/librustc/middle/const_eval.rs | 2 - src/librustc/middle/dataflow.rs | 1009 ++++++++++++++++ src/librustc/middle/kind.rs | 8 +- src/librustc/middle/lang_items.rs | 66 +- src/librustc/middle/liveness.rs | 105 +- src/librustc/middle/mem_categorization.rs | 546 ++++----- src/librustc/middle/moves.rs | 28 +- src/librustc/middle/region.rs | 318 ++--- src/librustc/middle/resolve.rs | 12 +- src/librustc/middle/trans/_match.rs | 7 +- src/librustc/middle/trans/base.rs | 109 +- src/librustc/middle/trans/callee.rs | 2 - src/librustc/middle/trans/common.rs | 9 +- src/librustc/middle/trans/consts.rs | 11 +- src/librustc/middle/trans/datum.rs | 7 +- src/librustc/middle/trans/expr.rs | 54 +- src/librustc/middle/trans/inline.rs | 39 +- src/librustc/middle/trans/meth.rs | 5 + src/librustc/middle/trans/monomorphize.rs | 4 + src/librustc/middle/trans/reachable.rs | 58 +- src/librustc/middle/ty.rs | 100 +- src/librustc/middle/typeck/check/_match.rs | 6 +- src/librustc/middle/typeck/check/method.rs | 47 +- src/librustc/middle/typeck/check/mod.rs | 54 +- src/librustc/middle/typeck/check/regionck.rs | 571 +++++---- .../middle/typeck/check/regionmanip.rs | 3 +- src/librustc/middle/typeck/check/writeback.rs | 29 +- src/librustc/middle/typeck/coherence.rs | 12 +- src/librustc/middle/typeck/infer/coercion.rs | 48 +- src/librustc/middle/typeck/infer/mod.rs | 81 +- .../middle/typeck/infer/region_inference.rs | 253 ++-- src/librustc/middle/typeck/infer/unify.rs | 35 +- src/librustc/util/ppaux.rs | 17 +- src/libstd/arc.rs | 12 +- src/libstd/bitv.rs | 14 +- src/libstd/net_tcp.rs | 6 +- src/libstd/serialize.rs | 15 - src/libstd/sort.rs | 70 +- src/libstd/std.rc | 1 - src/libsyntax/ast_map.rs | 69 +- src/libsyntax/ast_util.rs | 24 +- src/libsyntax/codemap.rs | 4 +- src/libsyntax/ext/base.rs | 108 +- src/libsyntax/ext/expand.rs | 3 + src/libsyntax/ext/pipes/liveness.rs | 4 +- src/libsyntax/ext/pipes/proto.rs | 18 +- src/libsyntax/print/pp.rs | 4 +- src/libsyntax/print/pprust.rs | 8 +- src/libsyntax/util/interner.rs | 4 +- src/libsyntax/visit.rs | 6 + .../compile-fail/access-mode-in-closures.rs | 2 +- .../arc-rw-read-mode-shouldnt-escape.rs | 1 + .../arc-rw-write-mode-shouldnt-escape.rs | 1 + .../attempted-access-non-fatal.rs | 4 +- .../compile-fail/borrowck-addr-of-upvar.rs | 4 +- .../compile-fail/borrowck-assign-comp-idx.rs | 10 +- src/test/compile-fail/borrowck-assign-comp.rs | 18 +- .../borrowck-assign-to-constants.rs | 4 +- .../compile-fail/borrowck-assign-to-enum.rs | 2 +- .../borrowck-assign-to-subfield.rs | 2 +- ... => borrowck-auto-mut-ref-to-immut-var.rs} | 10 +- .../compile-fail/borrowck-autoref-3261.rs | 4 +- .../borrowck-bad-nested-calls-free.rs | 43 + .../borrowck-bad-nested-calls-move.rs | 43 + .../borrowck-borrow-from-owned-ptr.rs | 70 +- .../borrowck-borrow-from-stack-variable.rs | 67 +- .../borrowck-borrowed-uniq-rvalue-2.rs | 3 +- .../borrowck-borrowed-uniq-rvalue.rs | 2 +- ...borrowck-call-method-from-mut-aliasable.rs | 8 +- ...k-imm-ref-to-mut-rec-field-issue-3162-c.rs | 4 +- .../borrowck-insert-during-each.rs | 4 +- .../compile-fail/borrowck-issue-2657-1.rs | 4 +- .../compile-fail/borrowck-issue-2657-2.rs | 2 +- .../compile-fail/borrowck-lend-flow-if.rs | 52 + .../compile-fail/borrowck-lend-flow-loop.rs | 164 +++ .../compile-fail/borrowck-lend-flow-match.rs | 60 + src/test/compile-fail/borrowck-lend-flow.rs | 93 +- .../borrowck-loan-blocks-move-cc.rs | 8 +- .../compile-fail/borrowck-loan-blocks-move.rs | 4 +- .../borrowck-loan-blocks-mut-uniq.rs | 4 +- ...borrowck-loan-local-as-both-mut-and-imm.rs | 4 +- .../borrowck-loan-rcvr-overloaded-op.rs | 6 +- src/test/compile-fail/borrowck-loan-rcvr.rs | 24 +- .../compile-fail/borrowck-loan-vec-content.rs | 4 +- .../compile-fail/borrowck-move-by-capture.rs | 2 +- .../borrowck-mut-addr-of-imm-var.rs | 2 +- .../compile-fail/borrowck-mut-boxed-vec.rs | 4 +- .../compile-fail/borrowck-mut-deref-comp.rs | 4 +- .../borrowck-mut-slice-of-imm-vec.rs | 2 +- .../borrowck-no-cycle-in-exchange-heap.rs | 4 +- .../borrowck-pat-by-value-binding.rs | 11 +- src/test/compile-fail/borrowck-pat-enum.rs | 14 +- .../borrowck-pat-reassign-binding.rs | 11 +- ...borrowck-pat-reassign-sometimes-binding.rs | 26 - .../borrowck-reborrow-from-mut.rs | 22 +- .../compile-fail/borrowck-ref-into-rvalue.rs | 8 +- .../compile-fail/borrowck-ref-mut-of-imm.rs | 2 +- .../compile-fail/borrowck-unary-move-2.rs | 2 +- src/test/compile-fail/borrowck-unary-move.rs | 4 +- .../compile-fail/borrowck-uniq-via-box.rs | 55 - .../compile-fail/borrowck-uniq-via-lend.rs | 8 +- .../compile-fail/borrowck-uniq-via-ref.rs | 7 +- .../borrowck-vec-pattern-element-loan.rs | 12 +- .../borrowck-vec-pattern-loan-from-mut.rs | 2 +- .../borrowck-vec-pattern-move-tail.rs | 5 +- .../borrowck-vec-pattern-nesting.rs | 4 +- .../borrowck-vec-pattern-tail-element-loan.rs | 4 +- .../borrowck-wg-borrow-mut-to-imm-fail-2.rs | 4 +- .../borrowck-wg-borrow-mut-to-imm-fail-3.rs | 4 +- .../borrowck-wg-borrow-mut-to-imm-fail.rs | 4 +- .../compile-fail/borrowck-wg-move-base-2.rs | 2 +- src/test/compile-fail/fn-variance-3.rs | 2 +- .../compile-fail/immut-function-arguments.rs | 4 +- src/test/compile-fail/index_message.rs | 2 +- src/test/compile-fail/issue-1896-1.rs | 4 +- src/test/compile-fail/issue-2149.rs | 1 - src/test/compile-fail/issue-2151.rs | 3 +- src/test/compile-fail/issue-2590.rs | 2 +- src/test/compile-fail/issue-3044.rs | 1 - src/test/compile-fail/issue-511.rs | 2 +- .../kindck-owned-trait-contains.rs | 3 + src/test/compile-fail/lambda-mutate-nested.rs | 2 +- src/test/compile-fail/lambda-mutate.rs | 2 +- .../moves-based-on-type-block-bad.rs | 2 +- ...type-move-out-of-closure-env-issue-1965.rs | 2 +- .../compile-fail/mutable-class-fields-2.rs | 3 +- src/test/compile-fail/mutable-class-fields.rs | 5 +- .../compile-fail/mutable-huh-ptr-assign.rs | 2 +- src/test/compile-fail/regions-addr-of-arg.rs | 2 +- .../compile-fail/regions-creating-enums.rs | 4 +- .../compile-fail/regions-creating-enums4.rs | 3 +- .../compile-fail/regions-escape-bound-fn.rs | 2 +- .../regions-escape-loop-via-variable.rs | 2 +- .../regions-escape-loop-via-vec.rs | 4 +- .../regions-escape-via-trait-or-not.rs | 7 +- .../regions-infer-borrow-scope-too-big.rs | 2 +- .../regions-infer-borrow-scope-within-loop.rs | 2 +- src/test/compile-fail/regions-nested-fns-2.rs | 2 +- src/test/compile-fail/regions-nested-fns.rs | 2 +- .../compile-fail/regions-ret-borrowed-1.rs | 1 + src/test/compile-fail/regions-ret-borrowed.rs | 1 + src/test/compile-fail/regions-ret.rs | 2 +- .../regions-var-type-out-of-scope.rs | 2 +- src/test/compile-fail/swap-no-lval.rs | 4 +- .../compile-fail/writing-to-immutable-vec.rs | 6 +- .../borrowck-nested-calls.rs} | 24 +- .../run-pass/coerce-reborrow-mut-vec-rcvr.rs | 6 +- src/test/run-pass/issue-2735-2.rs | 18 +- src/test/run-pass/issue-2735-3.rs | 18 +- 172 files changed, 6449 insertions(+), 4277 deletions(-) create mode 100644 src/librustc/middle/borrowck/doc.rs delete mode 100644 src/librustc/middle/borrowck/gather_loans.rs create mode 100644 src/librustc/middle/borrowck/gather_loans/lifetime.rs create mode 100644 src/librustc/middle/borrowck/gather_loans/mod.rs create mode 100644 src/librustc/middle/borrowck/gather_loans/restrictions.rs delete mode 100644 src/librustc/middle/borrowck/loan.rs delete mode 100644 src/librustc/middle/borrowck/preserve.rs create mode 100644 src/librustc/middle/dataflow.rs rename src/test/compile-fail/{auto-ref-borrowck-failure.rs => borrowck-auto-mut-ref-to-immut-var.rs} (81%) create mode 100644 src/test/compile-fail/borrowck-bad-nested-calls-free.rs create mode 100644 src/test/compile-fail/borrowck-bad-nested-calls-move.rs create mode 100644 src/test/compile-fail/borrowck-lend-flow-if.rs create mode 100644 src/test/compile-fail/borrowck-lend-flow-loop.rs create mode 100644 src/test/compile-fail/borrowck-lend-flow-match.rs delete mode 100644 src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs delete mode 100644 src/test/compile-fail/borrowck-uniq-via-box.rs rename src/test/{compile-fail/issue-4500.rs => run-pass/borrowck-nested-calls.rs} (51%) diff --git a/src/libcore/cleanup.rs b/src/libcore/cleanup.rs index a07c6b4811b6c..1dfe1b22dc4fb 100644 --- a/src/libcore/cleanup.rs +++ b/src/libcore/cleanup.rs @@ -126,14 +126,17 @@ struct AnnihilateStats { n_bytes_freed: uint } -unsafe fn each_live_alloc(f: &fn(box: *mut BoxRepr, uniq: bool) -> bool) { +unsafe fn each_live_alloc(read_next_before: bool, + f: &fn(box: *mut BoxRepr, uniq: bool) -> bool) { + //! Walks the internal list of allocations + use managed; let task: *Task = transmute(rustrt::rust_get_task()); let box = (*task).boxed_region.live_allocs; let mut box: *mut BoxRepr = transmute(copy box); while box != mut_null() { - let next = transmute(copy (*box).header.next); + let next_before = transmute(copy (*box).header.next); let uniq = (*box).header.ref_count == managed::raw::RC_MANAGED_UNIQUE; @@ -141,7 +144,11 @@ unsafe fn each_live_alloc(f: &fn(box: *mut BoxRepr, uniq: bool) -> bool) { break } - box = next + if read_next_before { + box = next_before; + } else { + box = transmute(copy (*box).header.next); + } } } @@ -159,7 +166,7 @@ fn debug_mem() -> bool { #[cfg(notest)] #[lang="annihilate"] pub unsafe fn annihilate() { - use unstable::lang::local_free; + use unstable::lang::{local_free, debug_ptr}; use io::WriterUtil; use io; use libc; @@ -173,27 +180,46 @@ pub unsafe fn annihilate() { }; // Pass 1: Make all boxes immortal. - for each_live_alloc |box, uniq| { + // + // In this pass, nothing gets freed, so it does not matter whether + // we read the next field before or after the callback. + for each_live_alloc(true) |box, uniq| { stats.n_total_boxes += 1; if uniq { + debug_ptr("Managed-uniq: ", &*box); stats.n_unique_boxes += 1; } else { + debug_ptr("Immortalizing: ", &*box); (*box).header.ref_count = managed::raw::RC_IMMORTAL; } } // Pass 2: Drop all boxes. - for each_live_alloc |box, uniq| { + // + // In this pass, unique-managed boxes may get freed, but not + // managed boxes, so we must read the `next` field *after* the + // callback, as the original value may have been freed. + for each_live_alloc(false) |box, uniq| { if !uniq { + debug_ptr("Invoking tydesc/glue on: ", &*box); let tydesc: *TypeDesc = transmute(copy (*box).header.type_desc); let drop_glue: DropGlue = transmute(((*tydesc).drop_glue, 0)); + debug_ptr("Box data: ", &(*box).data); + debug_ptr("Type descriptor: ", tydesc); drop_glue(to_unsafe_ptr(&tydesc), transmute(&(*box).data)); + debug_ptr("Dropped ", &*box); } } // Pass 3: Free all boxes. - for each_live_alloc |box, uniq| { + // + // In this pass, managed boxes may get freed (but not + // unique-managed boxes, though I think that none of those are + // left), so we must read the `next` field before, since it will + // not be valid after. + for each_live_alloc(true) |box, uniq| { if !uniq { + debug_ptr("About to free: ", &*box); stats.n_bytes_freed += (*((*box).header.type_desc)).size + sys::size_of::(); diff --git a/src/libcore/io.rs b/src/libcore/io.rs index 35ffd88c8f477..217ea1a9982c1 100644 --- a/src/libcore/io.rs +++ b/src/libcore/io.rs @@ -1022,7 +1022,7 @@ pub enum WriterType { Screen, File } pub trait Writer { /// Write all of the given bytes. - fn write(&self, v: &const [u8]); + fn write(&self, v: &[u8]); /// Move the current position within the stream. The second parameter /// determines the position that the first parameter is relative to. @@ -1039,7 +1039,7 @@ pub trait Writer { } impl Writer for @Writer { - fn write(&self, v: &const [u8]) { self.write(v) } + fn write(&self, v: &[u8]) { self.write(v) } fn seek(&self, a: int, b: SeekStyle) { self.seek(a, b) } fn tell(&self) -> uint { self.tell() } fn flush(&self) -> int { self.flush() } @@ -1047,7 +1047,7 @@ impl Writer for @Writer { } impl Writer for Wrapper { - fn write(&self, bs: &const [u8]) { self.base.write(bs); } + fn write(&self, bs: &[u8]) { self.base.write(bs); } fn seek(&self, off: int, style: SeekStyle) { self.base.seek(off, style); } fn tell(&self) -> uint { self.base.tell() } fn flush(&self) -> int { self.base.flush() } @@ -1055,7 +1055,7 @@ impl Writer for Wrapper { } impl Writer for *libc::FILE { - fn write(&self, v: &const [u8]) { + fn write(&self, v: &[u8]) { unsafe { do vec::as_const_buf(v) |vbuf, len| { let nout = libc::fwrite(vbuf as *c_void, @@ -1105,7 +1105,7 @@ pub fn FILE_writer(f: *libc::FILE, cleanup: bool) -> @Writer { } impl Writer for fd_t { - fn write(&self, v: &const [u8]) { + fn write(&self, v: &[u8]) { unsafe { let mut count = 0u; do vec::as_const_buf(v) |vbuf, len| { @@ -1262,7 +1262,7 @@ pub fn u64_to_be_bytes(n: u64, size: uint, } } -pub fn u64_from_be_bytes(data: &const [u8], +pub fn u64_from_be_bytes(data: &[u8], start: uint, size: uint) -> u64 { @@ -1497,7 +1497,7 @@ pub struct BytesWriter { } impl Writer for BytesWriter { - fn write(&self, v: &const [u8]) { + fn write(&self, v: &[u8]) { let v_len = v.len(); let bytes_len = vec::uniq_len(&const self.bytes); diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs index 28946281628b1..a2132676c1a03 100644 --- a/src/libcore/rt/sched/mod.rs +++ b/src/libcore/rt/sched/mod.rs @@ -304,7 +304,7 @@ pub impl Scheduler { unsafe { let last_task = transmute::, Option<&mut Task>>(last_task); let last_task_context = match last_task { - Some(ref t) => Some(&mut t.saved_context), None => None + Some(t) => Some(&mut t.saved_context), None => None }; let next_task_context = match self.current_task { Some(ref mut t) => Some(&mut t.saved_context), None => None diff --git a/src/libcore/str.rs b/src/libcore/str.rs index 064bffa00561f..f4430ca669fb1 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -2356,9 +2356,6 @@ pub trait StrSlice<'self> { fn any(&self, it: &fn(char) -> bool) -> bool; fn contains<'a>(&self, needle: &'a str) -> bool; fn contains_char(&self, needle: char) -> bool; - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] fn char_iter(&self) -> StrCharIterator<'self>; fn each(&self, it: &fn(u8) -> bool); fn eachi(&self, it: &fn(uint, u8) -> bool); @@ -2420,9 +2417,6 @@ impl<'self> StrSlice<'self> for &'self str { contains_char(*self, needle) } - #[cfg(stage1)] - #[cfg(stage2)] - #[cfg(stage3)] #[inline] fn char_iter(&self) -> StrCharIterator<'self> { StrCharIterator { @@ -2615,17 +2609,11 @@ impl Clone for ~str { } } -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] pub struct StrCharIterator<'self> { priv index: uint, priv string: &'self str, } -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl<'self> Iterator for StrCharIterator<'self> { #[inline] fn next(&mut self) -> Option { diff --git a/src/libcore/to_bytes.rs b/src/libcore/to_bytes.rs index 7b4b6994e50a5..63dcf0f44dc3b 100644 --- a/src/libcore/to_bytes.rs +++ b/src/libcore/to_bytes.rs @@ -19,7 +19,7 @@ use io::Writer; use option::{None, Option, Some}; use str; -pub type Cb<'self> = &'self fn(buf: &const [u8]) -> bool; +pub type Cb<'self> = &'self fn(buf: &[u8]) -> bool; /** * A trait to implement in order to make a type hashable; diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index de0542afc399a..cf71b01aeaeeb 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -11,7 +11,7 @@ //! Runtime calls emitted by the compiler. use cast::transmute; -use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int}; +use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int, STDERR_FILENO}; use managed::raw::BoxRepr; use str; use sys; @@ -74,7 +74,44 @@ pub fn fail_borrowed() { #[lang="exchange_malloc"] #[inline(always)] pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char { - transmute(exchange_alloc::malloc(transmute(td), transmute(size))) + let result = transmute(exchange_alloc::malloc(transmute(td), transmute(size))); + debug_ptr("exchange_malloc: ", result); + return result; +} + +/// Because this code is so perf. sensitive, use a static constant so that +/// debug printouts are compiled out most of the time. +static ENABLE_DEBUG_PTR: bool = false; + +#[inline] +pub fn debug_ptr(tag: &'static str, p: *T) { + //! A useful debugging function that prints a pointer + tag + newline + //! without allocating memory. + + if ENABLE_DEBUG_PTR && ::rt::env::get().debug_mem { + debug_ptr_slow(tag, p); + } + + fn debug_ptr_slow(tag: &'static str, p: *T) { + use io; + let dbg = STDERR_FILENO as io::fd_t; + let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'a', 'b', 'c', 'd', 'e', 'f']; + dbg.write_str(tag); + + static uint_nibbles: uint = ::uint::bytes << 1; + let mut buffer = [0_u8, ..uint_nibbles+1]; + let mut i = p as uint; + let mut c = uint_nibbles; + while c > 0 { + c -= 1; + buffer[c] = letters[i & 0xF] as u8; + i >>= 4; + } + dbg.write(buffer.slice(0, uint_nibbles)); + + dbg.write_str("\n"); + } } // NB: Calls to free CANNOT be allowed to fail, as throwing an exception from @@ -83,13 +120,16 @@ pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char { #[lang="exchange_free"] #[inline(always)] pub unsafe fn exchange_free(ptr: *c_char) { + debug_ptr("exchange_free: ", ptr); exchange_alloc::free(transmute(ptr)) } #[lang="malloc"] #[inline(always)] pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char { - return rustrt::rust_upcall_malloc_noswitch(td, size); + let result = rustrt::rust_upcall_malloc_noswitch(td, size); + debug_ptr("local_malloc: ", result); + return result; } // NB: Calls to free CANNOT be allowed to fail, as throwing an exception from @@ -98,6 +138,7 @@ pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char { #[lang="free"] #[inline(always)] pub unsafe fn local_free(ptr: *c_char) { + debug_ptr("local_free: ", ptr); rustrt::rust_upcall_free_noswitch(ptr); } diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 94f866643532c..2f9488d1bc7a7 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -19,9 +19,6 @@ use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater}; use clone::Clone; use old_iter::BaseIter; use old_iter; -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] use iterator::Iterator; use kinds::Copy; use libc; @@ -1824,7 +1821,7 @@ pub trait CopyableVector { } /// Extension methods for vectors -impl<'self,T:Copy> CopyableVector for &'self const [T] { +impl<'self,T:Copy> CopyableVector for &'self [T] { /// Returns a copy of `v`. #[inline] fn to_owned(&self) -> ~[T] { @@ -2710,18 +2707,12 @@ impl Clone for ~[A] { } // could be implemented with &[T] with .slice(), but this avoids bounds checks -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] pub struct VecIterator<'self, T> { priv ptr: *T, priv end: *T, priv lifetime: &'self T // FIXME: #5922 } -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl<'self, T> Iterator<&'self T> for VecIterator<'self, T> { #[inline] fn next(&mut self) -> Option<&'self T> { diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 2e64c0c45bffe..e899b1abc2648 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -263,7 +263,7 @@ pub fn compile_rest(sess: Session, middle::check_loop::check_crate(ty_cx, crate)); let middle::moves::MoveMaps {moves_map, variable_moves_map, - capture_map} = + moved_variables_set, capture_map} = time(time_passes, ~"compute moves", || middle::moves::compute_moves(ty_cx, method_map, crate)); @@ -271,20 +271,19 @@ pub fn compile_rest(sess: Session, middle::check_match::check_crate(ty_cx, method_map, moves_map, crate)); - let last_use_map = - time(time_passes, ~"liveness checking", || - middle::liveness::check_crate(ty_cx, method_map, - variable_moves_map, - capture_map, crate)); + time(time_passes, ~"liveness checking", || + middle::liveness::check_crate(ty_cx, method_map, + variable_moves_map, + capture_map, crate)); - let (root_map, mutbl_map, write_guard_map) = + let (root_map, write_guard_map) = time(time_passes, ~"borrow checking", || middle::borrowck::check_crate(ty_cx, method_map, - moves_map, capture_map, - crate)); + moves_map, moved_variables_set, + capture_map, crate)); time(time_passes, ~"kind checking", || - kind::check_crate(ty_cx, method_map, last_use_map, crate)); + kind::check_crate(ty_cx, method_map, crate)); time(time_passes, ~"lint checking", || lint::check_crate(ty_cx, crate)); @@ -292,9 +291,7 @@ pub fn compile_rest(sess: Session, if upto == cu_no_trans { return (crate, Some(ty_cx)); } let maps = astencode::Maps { - mutbl_map: mutbl_map, root_map: root_map, - last_use_map: last_use_map, method_map: method_map, vtable_map: vtable_map, write_guard_map: write_guard_map, diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 55c81e6d17b20..fff97d2436af3 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -173,15 +173,19 @@ pub type Session = @Session_; pub impl Session_ { fn span_fatal(@self, sp: span, msg: ~str) -> ! { + debug!("span_fatal invoked: %s", msg); self.span_diagnostic.span_fatal(sp, msg) } fn fatal(@self, msg: ~str) -> ! { + debug!("fatal invoked: %s", msg); self.span_diagnostic.handler().fatal(msg) } fn span_err(@self, sp: span, msg: ~str) { + debug!("span_err invoked: %s", msg); self.span_diagnostic.span_err(sp, msg) } fn err(@self, msg: ~str) { + debug!("err invoked: %s", msg); self.span_diagnostic.handler().err(msg) } fn has_errors(@self) -> bool { @@ -191,15 +195,19 @@ pub impl Session_ { self.span_diagnostic.handler().abort_if_errors() } fn span_warn(@self, sp: span, msg: ~str) { + debug!("span_warn invoked: %s", msg); self.span_diagnostic.span_warn(sp, msg) } fn warn(@self, msg: ~str) { + debug!("warn invoked: %s", msg); self.span_diagnostic.handler().warn(msg) } fn span_note(@self, sp: span, msg: ~str) { + debug!("span_note invoked: %s", msg); self.span_diagnostic.span_note(sp, msg) } fn note(@self, msg: ~str) { + debug!("note invoked: %s", msg); self.span_diagnostic.handler().note(msg) } fn span_bug(@self, sp: span, msg: ~str) -> ! { diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs index 02e2a4c8734f8..22bce62336cab 100644 --- a/src/librustc/front/test.rs +++ b/src/librustc/front/test.rs @@ -69,7 +69,8 @@ fn generate_test_harness(sess: session::Session, testfns: ~[] }; - cx.ext_cx.bt_push(ExpandedFrom(CallInfo { + let ext_cx = cx.ext_cx; + ext_cx.bt_push(ExpandedFrom(CallInfo { call_site: dummy_sp(), callee: NameAndSpan { name: ~"test", @@ -84,7 +85,7 @@ fn generate_test_harness(sess: session::Session, let fold = fold::make_fold(precursor); let res = @fold.fold_crate(&*crate); - cx.ext_cx.bt_pop(); + ext_cx.bt_pop(); return res; } diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index cfe31360d321b..1a94b57279cc4 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -244,8 +244,8 @@ fn doc_transformed_self_ty(doc: ebml::Doc, } } -pub fn item_type(_: ast::def_id, item: ebml::Doc, tcx: ty::ctxt, cdata: cmd) - -> ty::t { +pub fn item_type(_item_id: ast::def_id, item: ebml::Doc, + tcx: ty::ctxt, cdata: cmd) -> ty::t { doc_type(item, tcx, cdata) } diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 011ee115e8c15..963afa08bfe33 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -245,6 +245,9 @@ fn parse_region(st: @mut PState) -> ty::Region { 't' => { ty::re_static } + 'e' => { + ty::re_static + } _ => fail!(~"parse_region: bad input") } } diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 763b1984b81c8..fdba3ac4f00ab 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -71,30 +71,29 @@ pub fn enc_ty(w: @io::Writer, cx: @ctxt, t: ty::t) { w.write_str(result_str); } ac_use_abbrevs(abbrevs) => { - match abbrevs.find(&t) { - Some(a) => { w.write_str(*a.s); return; } - None => { - let pos = w.tell(); - enc_sty(w, cx, /*bad*/copy ty::get(t).sty); - let end = w.tell(); - let len = end - pos; - fn estimate_sz(u: uint) -> uint { - let mut n = u; - let mut len = 0u; - while n != 0u { len += 1u; n = n >> 4u; } - return len; - } - let abbrev_len = 3u + estimate_sz(pos) + estimate_sz(len); - if abbrev_len < len { - // I.e. it's actually an abbreviation. - let s = ~"#" + uint::to_str_radix(pos, 16u) + ~":" + - uint::to_str_radix(len, 16u) + ~"#"; - let a = ty_abbrev { pos: pos, len: len, s: @s }; - abbrevs.insert(t, a); - } - return; + match abbrevs.find(&t) { + Some(a) => { w.write_str(*a.s); return; } + None => {} } - } + let pos = w.tell(); + enc_sty(w, cx, /*bad*/copy ty::get(t).sty); + let end = w.tell(); + let len = end - pos; + fn estimate_sz(u: uint) -> uint { + let mut n = u; + let mut len = 0u; + while n != 0u { len += 1u; n = n >> 4u; } + return len; + } + let abbrev_len = 3u + estimate_sz(pos) + estimate_sz(len); + if abbrev_len < len { + // I.e. it's actually an abbreviation. + let s = ~"#" + uint::to_str_radix(pos, 16u) + ~":" + + uint::to_str_radix(len, 16u) + ~"#"; + let a = ty_abbrev { pos: pos, len: len, s: @s }; + abbrevs.insert(t, a); + } + return; } } } @@ -152,6 +151,9 @@ fn enc_region(w: @io::Writer, cx: @ctxt, r: ty::Region) { ty::re_static => { w.write_char('t'); } + ty::re_empty => { + w.write_char('e'); + } ty::re_infer(_) => { // these should not crop up after typeck cx.diag.handler().bug(~"Cannot encode region variables"); diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index c65521228fa87..7a3bdce875da2 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -44,9 +44,7 @@ use writer = std::ebml::writer; // Auxiliary maps of things to be encoded pub struct Maps { - mutbl_map: middle::borrowck::mutbl_map, root_map: middle::borrowck::root_map, - last_use_map: middle::liveness::last_use_map, method_map: middle::typeck::method_map, vtable_map: middle::typeck::vtable_map, write_guard_map: middle::borrowck::write_guard_map, @@ -151,7 +149,7 @@ pub fn decode_inlined_item(cdata: @cstore::crate_metadata, fn reserve_id_range(sess: Session, from_id_range: ast_util::id_range) -> ast_util::id_range { // Handle the case of an empty range: - if ast_util::empty(from_id_range) { return from_id_range; } + if from_id_range.empty() { return from_id_range; } let cnt = from_id_range.max - from_id_range.min; let to_id_min = sess.parse_sess.next_id; let to_id_max = sess.parse_sess.next_id + cnt; @@ -162,7 +160,6 @@ fn reserve_id_range(sess: Session, pub impl ExtendedDecodeContext { fn tr_id(&self, id: ast::node_id) -> ast::node_id { /*! - * * Translates an internal id, meaning a node id that is known * to refer to some part of the item currently being inlined, * such as a local variable or argument. All naked node-ids @@ -173,12 +170,11 @@ pub impl ExtendedDecodeContext { */ // from_id_range should be non-empty - assert!(!ast_util::empty(self.from_id_range)); + assert!(!self.from_id_range.empty()); (id - self.from_id_range.min + self.to_id_range.min) } fn tr_def_id(&self, did: ast::def_id) -> ast::def_id { /*! - * * Translates an EXTERNAL def-id, converting the crate number * from the one used in the encoded data to the current crate * numbers.. By external, I mean that it be translated to a @@ -203,7 +199,6 @@ pub impl ExtendedDecodeContext { } fn tr_intern_def_id(&self, did: ast::def_id) -> ast::def_id { /*! - * * Translates an INTERNAL def-id, meaning a def-id that is * known to refer to some part of the item currently being * inlined. In that case, we want to convert the def-id to @@ -461,11 +456,7 @@ impl tr for ty::AutoAdjustment { impl tr for ty::AutoRef { fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::AutoRef { - ty::AutoRef { - kind: self.kind, - region: self.region.tr(xcx), - mutbl: self.mutbl, - } + self.map_region(|r| r.tr(xcx)) } } @@ -474,7 +465,7 @@ impl tr for ty::Region { match *self { ty::re_bound(br) => ty::re_bound(br.tr(xcx)), ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)), - ty::re_static | ty::re_infer(*) => *self, + ty::re_empty | ty::re_static | ty::re_infer(*) => *self, ty::re_free(ref fr) => { ty::re_free(ty::FreeRegion {scope_id: xcx.tr_id(fr.scope_id), bound_region: fr.bound_region.tr(xcx)}) @@ -914,23 +905,6 @@ fn encode_side_tables_for_id(ecx: @e::EncodeContext, } } - if maps.mutbl_map.contains(&id) { - do ebml_w.tag(c::tag_table_mutbl) { - ebml_w.id(id); - } - } - - for maps.last_use_map.find(&id).each |&m| { - do ebml_w.tag(c::tag_table_last_use) { - ebml_w.id(id); - do ebml_w.tag(c::tag_table_val) { - do ebml_w.emit_from_vec(/*bad*/ copy **m) |id| { - id.encode(ebml_w); - } - } - } - } - for maps.method_map.find(&id).each |&mme| { do ebml_w.tag(c::tag_table_method_map) { ebml_w.id(id); @@ -1108,9 +1082,7 @@ fn decode_side_tables(xcx: @ExtendedDecodeContext, found for id %d (orig %d)", tag, id, id0); - if tag == (c::tag_table_mutbl as uint) { - dcx.maps.mutbl_map.insert(id); - } else if tag == (c::tag_table_moves_map as uint) { + if tag == (c::tag_table_moves_map as uint) { dcx.maps.moves_map.insert(id); } else { let val_doc = entry_doc.get(c::tag_table_val as uint); @@ -1138,11 +1110,6 @@ fn decode_side_tables(xcx: @ExtendedDecodeContext, } else if tag == (c::tag_table_param_defs as uint) { let bounds = val_dsr.read_type_param_def(xcx); dcx.tcx.ty_param_defs.insert(id, bounds); - } else if tag == (c::tag_table_last_use as uint) { - let ids = val_dsr.read_to_vec(|| { - xcx.tr_id(val_dsr.read_int()) - }); - dcx.maps.last_use_map.insert(id, @mut ids); } else if tag == (c::tag_table_method_map as uint) { dcx.maps.method_map.insert( id, diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 07b6c80d4201c..56eb57009ca08 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -18,284 +18,143 @@ // 4. moves do not affect things loaned out in any way use middle::moves; -use middle::typeck::check::PurityState; -use middle::borrowck::{Loan, bckerr, BorrowckCtxt, inherent_mutability}; -use middle::borrowck::{ReqMaps, root_map_key, save_and_restore_managed}; -use middle::borrowck::{MoveError, MoveOk, MoveFromIllegalCmt}; -use middle::borrowck::{MoveWhileBorrowed}; -use middle::mem_categorization::{cat_arg, cat_comp, cat_deref}; -use middle::mem_categorization::{cat_local, cat_rvalue, cat_self}; -use middle::mem_categorization::{cat_special, cmt, gc_ptr, loan_path, lp_arg}; -use middle::mem_categorization::{lp_comp, lp_deref, lp_local}; +use middle::borrowck::*; +use mc = middle::mem_categorization; use middle::ty; -use util::ppaux::ty_to_str; - +use util::ppaux::Repr; use core::hashmap::HashSet; -use core::util::with; -use syntax::ast::m_mutbl; +use syntax::ast::{m_mutbl, m_imm, m_const}; use syntax::ast; use syntax::ast_util; -use syntax::codemap::span; -use syntax::print::pprust; use syntax::visit; +use syntax::codemap::span; -struct CheckLoanCtxt { +struct CheckLoanCtxt<'self> { bccx: @BorrowckCtxt, - req_maps: ReqMaps, - - reported: HashSet, - - declared_purity: @mut PurityState, - fn_args: @mut @~[ast::node_id] -} - -// if we are enforcing purity, why are we doing so? -#[deriving(Eq)] -enum purity_cause { - // enforcing purity because fn was declared pure: - pc_pure_fn, - - // enforce purity because we need to guarantee the - // validity of some alias; `bckerr` describes the - // reason we needed to enforce purity. - pc_cmt(bckerr) -} - -// if we're not pure, why? -#[deriving(Eq)] -enum impurity_cause { - // some surrounding block was marked as 'unsafe' - pc_unsafe, - - // nothing was unsafe, and nothing was pure - pc_default, + dfcx: &'self LoanDataFlow, + all_loans: &'self [Loan], + reported: @mut HashSet, } pub fn check_loans(bccx: @BorrowckCtxt, - req_maps: ReqMaps, - crate: @ast::crate) { + dfcx: &LoanDataFlow, + all_loans: &[Loan], + body: &ast::blk) { + debug!("check_loans(body id=%?)", body.node.id); + let clcx = @mut CheckLoanCtxt { bccx: bccx, - req_maps: req_maps, - reported: HashSet::new(), - declared_purity: @mut PurityState::function(ast::impure_fn, 0), - fn_args: @mut @~[] + dfcx: dfcx, + all_loans: all_loans, + reported: @mut HashSet::new(), }; + let vt = visit::mk_vt(@visit::Visitor {visit_expr: check_loans_in_expr, visit_local: check_loans_in_local, visit_block: check_loans_in_block, + visit_pat: check_loans_in_pat, visit_fn: check_loans_in_fn, .. *visit::default_visitor()}); - visit::visit_crate(crate, clcx, vt); + (vt.visit_block)(body, clcx, vt); } -#[deriving(Eq)] -enum assignment_type { - at_straight_up, - at_swap +enum MoveError { + MoveOk, + MoveFromIllegalCmt(mc::cmt), + MoveWhileBorrowed(/*loan*/@LoanPath, /*loan*/span) } -pub impl assignment_type { - fn checked_by_liveness(&self) -> bool { - // the liveness pass guarantees that immutable local variables - // are only assigned once; but it doesn't consider &mut - match *self { - at_straight_up => true, - at_swap => true - } - } - fn ing_form(&self, desc: ~str) -> ~str { - match *self { - at_straight_up => ~"assigning to " + desc, - at_swap => ~"swapping to and from " + desc - } - } -} - -pub impl CheckLoanCtxt { +pub impl<'self> CheckLoanCtxt<'self> { fn tcx(&self) -> ty::ctxt { self.bccx.tcx } - fn purity(&mut self, scope_id: ast::node_id) - -> Either + fn each_issued_loan(&self, + scope_id: ast::node_id, + op: &fn(&Loan) -> bool) { - let default_purity = match self.declared_purity.purity { - // an unsafe declaration overrides all - ast::unsafe_fn => return Right(pc_unsafe), - - // otherwise, remember what was declared as the - // default, but we must scan for requirements - // imposed by the borrow check - ast::pure_fn => Left(pc_pure_fn), - ast::extern_fn | ast::impure_fn => Right(pc_default) - }; - - // scan to see if this scope or any enclosing scope requires - // purity. if so, that overrides the declaration. - - let mut scope_id = scope_id; - loop { - match self.req_maps.pure_map.find(&scope_id) { - None => (), - Some(e) => return Left(pc_cmt(*e)) - } - - match self.tcx().region_maps.opt_encl_scope(scope_id) { - None => return default_purity, - Some(next_scope_id) => scope_id = next_scope_id + //! Iterates over each loan that that has been issued + //! on entrance to `scope_id`, regardless of whether it is + //! actually *in scope* at that point. Sometimes loans + //! are issued for future scopes and thus they may have been + //! *issued* but not yet be in effect. + + for self.dfcx.each_bit_on_entry(scope_id) |loan_index| { + let loan = &self.all_loans[loan_index]; + if !op(loan) { + return; } } } - fn walk_loans(&self, - mut scope_id: ast::node_id, - f: &fn(v: &Loan) -> bool) { - - loop { - for self.req_maps.req_loan_map.find(&scope_id).each |loans| { - for loans.each |loan| { - if !f(loan) { return; } - } - } - - match self.tcx().region_maps.opt_encl_scope(scope_id) { - None => return, - Some(next_scope_id) => scope_id = next_scope_id, - } - } - } - - fn walk_loans_of(&mut self, - scope_id: ast::node_id, - lp: @loan_path, - f: &fn(v: &Loan) -> bool) { - for self.walk_loans(scope_id) |loan| { - if loan.lp == lp { - if !f(loan) { return; } - } - } - } + fn each_in_scope_loan(&self, + scope_id: ast::node_id, + op: &fn(&Loan) -> bool) + { + //! Like `each_issued_loan()`, but only considers loans that are + //! currently in scope. - // when we are in a pure context, we check each call to ensure - // that the function which is invoked is itself pure. - // - // note: we take opt_expr and expr_id separately because for - // overloaded operators the callee has an id but no expr. - // annoying. - fn check_pure_callee_or_arg(&mut self, - pc: Either, - opt_expr: Option<@ast::expr>, - callee_id: ast::node_id, - callee_span: span) { - let tcx = self.tcx(); - - debug!("check_pure_callee_or_arg(pc=%?, expr=%?, \ - callee_id=%d, ty=%s)", - pc, - opt_expr.map(|e| pprust::expr_to_str(*e, tcx.sess.intr()) ), - callee_id, - ty_to_str(self.tcx(), ty::node_id_to_type(tcx, callee_id))); - - // Purity rules: an expr B is a legal callee or argument to a - // call within a pure function A if at least one of the - // following holds: - // - // (a) A was declared pure and B is one of its arguments; - // (b) B is a stack closure; - // (c) B is a pure fn; - // (d) B is not a fn. - - match opt_expr { - Some(expr) => { - match expr.node { - ast::expr_path(_) if pc == Left(pc_pure_fn) => { - let def = *self.tcx().def_map.get(&expr.id); - let did = ast_util::def_id_of_def(def); - let is_fn_arg = - did.crate == ast::local_crate && - (*self.fn_args).contains(&(did.node)); - if is_fn_arg { return; } // case (a) above - } - ast::expr_fn_block(*) | ast::expr_loop_body(*) | - ast::expr_do_body(*) => { - if self.is_stack_closure(expr.id) { - // case (b) above + let region_maps = self.tcx().region_maps; + for self.each_issued_loan(scope_id) |loan| { + if region_maps.is_subscope_of(scope_id, loan.kill_scope) { + if !op(loan) { return; } - } - _ => () } - } - None => () } + } - let callee_ty = ty::node_id_to_type(tcx, callee_id); - match ty::get(callee_ty).sty { - ty::ty_bare_fn(ty::BareFnTy {purity: purity, _}) | - ty::ty_closure(ty::ClosureTy {purity: purity, _}) => { - match purity { - ast::pure_fn => return, // case (c) above - ast::impure_fn | ast::unsafe_fn | ast::extern_fn => { - self.report_purity_error( - pc, callee_span, - fmt!("access to %s function", - purity.to_str())); + fn each_in_scope_restriction(&self, + scope_id: ast::node_id, + loan_path: @LoanPath, + op: &fn(&Loan, &Restriction) -> bool) + { + //! Iterates through all the in-scope restrictions for the + //! given `loan_path` + + for self.each_in_scope_loan(scope_id) |loan| { + for loan.restrictions.each |restr| { + if restr.loan_path == loan_path { + if !op(loan, restr) { + return; } } } - _ => return, // case (d) above } } - // True if the expression with the given `id` is a stack closure. - // The expression must be an expr_fn_block(*) - fn is_stack_closure(&mut self, id: ast::node_id) -> bool { - let fn_ty = ty::node_id_to_type(self.tcx(), id); - match ty::get(fn_ty).sty { - ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil, - _}) => true, - _ => false - } - } + fn loans_generated_by(&self, scope_id: ast::node_id) -> ~[uint] { + //! Returns a vector of the loans that are generated as + //! we encounter `scope_id`. - fn is_allowed_pure_arg(&mut self, expr: @ast::expr) -> bool { - return match expr.node { - ast::expr_path(_) => { - let def = *self.tcx().def_map.get(&expr.id); - let did = ast_util::def_id_of_def(def); - did.crate == ast::local_crate && - (*self.fn_args).contains(&(did.node)) - } - ast::expr_fn_block(*) => self.is_stack_closure(expr.id), - _ => false, - }; + let mut result = ~[]; + for self.dfcx.each_gen_bit(scope_id) |loan_index| { + result.push(loan_index); + } + return result; } fn check_for_conflicting_loans(&mut self, scope_id: ast::node_id) { - debug!("check_for_conflicting_loans(scope_id=%?)", scope_id); + //! Checks to see whether any of the loans that are issued + //! by `scope_id` conflict with loans that have already been + //! issued when we enter `scope_id` (for example, we do not + //! permit two `&mut` borrows of the same variable). - let new_loans = match self.req_maps.req_loan_map.find(&scope_id) { - None => return, - Some(&loans) => loans - }; - let new_loans: &mut ~[Loan] = new_loans; - - debug!("new_loans has length %?", new_loans.len()); + debug!("check_for_conflicting_loans(scope_id=%?)", scope_id); - let par_scope_id = self.tcx().region_maps.encl_scope(scope_id); - for self.walk_loans(par_scope_id) |old_loan| { - debug!("old_loan=%?", self.bccx.loan_to_repr(old_loan)); + let new_loan_indices = self.loans_generated_by(scope_id); + debug!("new_loan_indices = %?", new_loan_indices); - for new_loans.each |new_loan| { - self.report_error_if_loans_conflict(old_loan, new_loan); + for self.each_issued_loan(scope_id) |issued_loan| { + for new_loan_indices.each |&new_loan_index| { + let new_loan = &self.all_loans[new_loan_index]; + self.report_error_if_loans_conflict(issued_loan, new_loan); } } - let len = new_loans.len(); - for uint::range(0, len) |i| { - let loan_i = new_loans[i]; - for uint::range(i+1, len) |j| { - let loan_j = new_loans[j]; - self.report_error_if_loans_conflict(&loan_i, &loan_j); + for uint::range(0, new_loan_indices.len()) |i| { + let old_loan = &self.all_loans[new_loan_indices[i]]; + for uint::range(i+1, new_loan_indices.len()) |j| { + let new_loan = &self.all_loans[new_loan_indices[j]]; + self.report_error_if_loans_conflict(old_loan, new_loan); } } } @@ -303,219 +162,358 @@ pub impl CheckLoanCtxt { fn report_error_if_loans_conflict(&self, old_loan: &Loan, new_loan: &Loan) { - if old_loan.lp != new_loan.lp { - return; - } + //! Checks whether `old_loan` and `new_loan` can safely be issued + //! simultaneously. + + debug!("report_error_if_loans_conflict(old_loan=%s, new_loan=%s)", + old_loan.repr(self.tcx()), + new_loan.repr(self.tcx())); + + // Should only be called for loans that are in scope at the same time. + let region_maps = self.tcx().region_maps; + assert!(region_maps.scopes_intersect(old_loan.kill_scope, + new_loan.kill_scope)); + + self.report_error_if_loan_conflicts_with_restriction( + old_loan, new_loan, old_loan, new_loan) && + self.report_error_if_loan_conflicts_with_restriction( + new_loan, old_loan, old_loan, new_loan); + } - match (old_loan.kind, new_loan.kind) { - (PartialFreeze, PartialTake) | (PartialTake, PartialFreeze) | - (TotalFreeze, PartialFreeze) | (PartialFreeze, TotalFreeze) | - (Immobile, _) | (_, Immobile) | - (PartialFreeze, PartialFreeze) | - (PartialTake, PartialTake) | - (TotalFreeze, TotalFreeze) => { - /* ok */ - } + fn report_error_if_loan_conflicts_with_restriction(&self, + loan1: &Loan, + loan2: &Loan, + old_loan: &Loan, + new_loan: &Loan) -> bool { + //! Checks whether the restrictions introduced by `loan1` would + //! prohibit `loan2`. Returns false if an error is reported. + + debug!("report_error_if_loan_conflicts_with_restriction(\ + loan1=%s, loan2=%s)", + loan1.repr(self.tcx()), + loan2.repr(self.tcx())); + + // Restrictions that would cause the new loan to be immutable: + let illegal_if = match loan2.mutbl { + m_mutbl => RESTR_ALIAS | RESTR_FREEZE | RESTR_MUTATE, + m_imm => RESTR_ALIAS | RESTR_FREEZE, + m_const => RESTR_ALIAS, + }; + debug!("illegal_if=%?", illegal_if); + + for loan1.restrictions.each |restr| { + if !restr.set.intersects(illegal_if) { loop; } + if restr.loan_path != loan2.loan_path { loop; } - (PartialTake, TotalFreeze) | (TotalFreeze, PartialTake) | - (TotalTake, TotalFreeze) | (TotalFreeze, TotalTake) | - (TotalTake, PartialFreeze) | (PartialFreeze, TotalTake) | - (TotalTake, PartialTake) | (PartialTake, TotalTake) | - (TotalTake, TotalTake) => { - self.bccx.span_err( - new_loan.cmt.span, - fmt!("loan of %s as %s \ - conflicts with prior loan", - self.bccx.cmt_to_str(new_loan.cmt), - self.bccx.loan_kind_to_str(new_loan.kind))); - self.bccx.span_note( - old_loan.cmt.span, - fmt!("prior loan as %s granted here", - self.bccx.loan_kind_to_str(old_loan.kind))); + match (new_loan.mutbl, old_loan.mutbl) { + (m_mutbl, m_mutbl) => { + self.bccx.span_err( + new_loan.span, + fmt!("cannot borrow `%s` as mutable \ + more than once at at a time", + self.bccx.loan_path_to_str(new_loan.loan_path))); + self.bccx.span_note( + old_loan.span, + fmt!("second borrow of `%s` as mutable occurs here", + self.bccx.loan_path_to_str(new_loan.loan_path))); + return false; + } + + _ => { + self.bccx.span_err( + new_loan.span, + fmt!("cannot borrow `%s` as %s because \ + it is also borrowed as %s" + 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))); + self.bccx.span_note( + old_loan.span, + fmt!("second borrow of `%s` occurs here", + self.bccx.loan_path_to_str(new_loan.loan_path))); + return false; + } } } + + true } - fn is_local_variable(&self, cmt: cmt) -> bool { + fn is_local_variable(&self, cmt: mc::cmt) -> bool { match cmt.cat { - cat_local(_) => true, + mc::cat_local(_) => true, _ => false } } - fn check_assignment(&mut self, at: assignment_type, ex: @ast::expr) { + fn check_assignment(&self, expr: @ast::expr) { // We don't use cat_expr() here because we don't want to treat // auto-ref'd parameters in overloaded operators as rvalues. - let cmt = match self.bccx.tcx.adjustments.find(&ex.id) { - None => self.bccx.cat_expr_unadjusted(ex), - Some(&adj) => self.bccx.cat_expr_autoderefd(ex, adj) + let cmt = match self.bccx.tcx.adjustments.find(&expr.id) { + None => self.bccx.cat_expr_unadjusted(expr), + Some(&adj) => self.bccx.cat_expr_autoderefd(expr, adj) }; - debug!("check_assignment(cmt=%s)", - self.bccx.cmt_to_repr(cmt)); - - if self.is_local_variable(cmt) && at.checked_by_liveness() { - // liveness guarantees that immutable local variables - // are only assigned once - } else { - match cmt.mutbl { - McDeclared | McInherited => { - // Ok, but if this loan is a mutable loan, then mark the - // loan path (if it exists) as being used. This is similar - // to the check performed in loan.rs in issue_loan(). This - // type of use of mutable is different from issuing a loan, - // however. - for cmt.lp.each |lp| { - for lp.node_id().each |&id| { - self.tcx().used_mut_nodes.insert(id); - } - } - } - McReadOnly | McImmutable => { + debug!("check_assignment(cmt=%s)", cmt.repr(self.tcx())); + + // check that the value being assigned is declared as mutable + // and report an error otherwise. + match cmt.mutbl { + mc::McDeclared => { + // OK + } + mc::McInherited => { + // OK, but we may have to add an entry to `used_mut_nodes` + mark_writes_through_upvars_as_used_mut(self, cmt); + } + mc::McReadOnly | mc::McImmutable => { + // Subtle: liveness guarantees that immutable local + // variables are only assigned once, so no need to + // report an error for an assignment to a local + // variable (note also that it is not legal to borrow + // for a local variable before it has been assigned + // for the first time). + if !self.is_local_variable(cmt) { self.bccx.span_err( - ex.span, - at.ing_form(self.bccx.cmt_to_str(cmt))); - return; + expr.span, + fmt!("cannot assign to %s %s" + cmt.mutbl.to_user_str(), + self.bccx.cmt_to_str(cmt))); } + return; } } - // if this is a pure function, only loan-able state can be - // assigned, because it is uniquely tied to this function and - // is not visible from the outside - let purity = self.purity(ex.id); - match purity { - Right(_) => (), - Left(pc_cmt(_)) => { - // Subtle: Issue #3162. If we are enforcing purity - // because there is a reference to aliasable, mutable data - // that we require to be immutable, we can't allow writes - // even to data owned by the current stack frame. This is - // because that aliasable data might have been located on - // the current stack frame, we don't know. - self.report_purity_error( - purity, - ex.span, - at.ing_form(self.bccx.cmt_to_str(cmt))); - } - Left(pc_pure_fn) => { - if cmt.lp.is_none() { - self.report_purity_error( - purity, ex.span, - at.ing_form(self.bccx.cmt_to_str(cmt))); - } - } + if check_for_aliasable_mutable_writes(self, expr, cmt) { + check_for_assignment_to_restricted_or_frozen_location( + self, expr, cmt); } - // check for a conflicting loan as well, except in the case of - // taking a mutable ref. that will create a loan of its own - // which will be checked for compat separately in - // check_for_conflicting_loans() - for cmt.lp.each |lp| { - self.check_for_loan_conflicting_with_assignment( - at, ex, cmt, *lp); - } + fn mark_writes_through_upvars_as_used_mut(self: &CheckLoanCtxt, + cmt: mc::cmt) { + //! If the mutability of the `cmt` being written is inherited + //! from a local variable in another closure, liveness may + //! not have been able to detect that this variable's mutability + //! is important, so we must add the variable to the + //! `used_mut_nodes` table here. This is because liveness + //! does not consider closures. + + let mut passed_upvar = false; + let mut cmt = cmt; + loop { + debug!("mark_writes_through_upvars_as_used_mut(cmt=%s)", + cmt.repr(self.tcx())); + match cmt.cat { + mc::cat_local(id) | + mc::cat_arg(id, _) | + mc::cat_self(id) => { + if passed_upvar { + self.tcx().used_mut_nodes.insert(id); + } + return; + } - self.bccx.add_to_mutbl_map(cmt); + mc::cat_stack_upvar(b) => { + cmt = b; + passed_upvar = true; + } - // Check for and insert write guards as necessary. - self.add_write_guards_if_necessary(cmt); - } + mc::cat_rvalue | + mc::cat_static_item | + mc::cat_implicit_self | + mc::cat_copied_upvar(*) | + mc::cat_deref(_, _, mc::unsafe_ptr(*)) | + mc::cat_deref(_, _, mc::gc_ptr(*)) | + mc::cat_deref(_, _, mc::region_ptr(*)) => { + assert_eq!(cmt.mutbl, mc::McDeclared); + return; + } - fn add_write_guards_if_necessary(&mut self, cmt: cmt) { - match cmt.cat { - cat_deref(base, deref_count, ptr_kind) => { - self.add_write_guards_if_necessary(base); - - match ptr_kind { - gc_ptr(ast::m_mutbl) => { - let key = root_map_key { - id: base.id, - derefs: deref_count - }; - self.bccx.write_guard_map.insert(key); + mc::cat_discr(b, _) | + mc::cat_deref(b, _, mc::uniq_ptr(*)) => { + assert_eq!(cmt.mutbl, mc::McInherited); + cmt = b; + } + + mc::cat_interior(b, _) => { + if cmt.mutbl == mc::McInherited { + cmt = b; + } else { + return; // field declared as mutable or some such + } } - _ => {} } } - cat_comp(base, _) => { - self.add_write_guards_if_necessary(base); - } - _ => {} } - } - fn check_for_loan_conflicting_with_assignment(&mut self, - at: assignment_type, - ex: @ast::expr, - cmt: cmt, - lp: @loan_path) { - for self.walk_loans_of(ex.id, lp) |loan| { - match loan.kind { - Immobile => { /* ok */ } - TotalFreeze | PartialFreeze | - TotalTake | PartialTake => { - self.bccx.span_err( - ex.span, - fmt!("%s prohibited due to outstanding loan", - at.ing_form(self.bccx.cmt_to_str(cmt)))); - self.bccx.span_note( - loan.cmt.span, - fmt!("loan of %s granted here", - self.bccx.cmt_to_str(loan.cmt))); - return; + fn check_for_aliasable_mutable_writes(self: &CheckLoanCtxt, + expr: @ast::expr, + cmt: mc::cmt) -> bool { + //! Safety checks related to writes to aliasable, mutable locations + + let guarantor = cmt.guarantor(); + match guarantor.cat { + mc::cat_deref(b, _, mc::region_ptr(m_mutbl, _)) => { + // Statically prohibit writes to `&mut` when aliasable + + match b.freely_aliasable() { + None => {} + Some(cause) => { + self.bccx.report_aliasability_violation( + expr.span, + MutabilityViolation, + cause); + } + } } + + mc::cat_deref(base, deref_count, mc::gc_ptr(ast::m_mutbl)) => { + // Dynamically check writes to `@mut` + + let key = root_map_key { + id: base.id, + derefs: deref_count + }; + self.bccx.write_guard_map.insert(key); + } + + _ => {} } - } - // Subtle: if the mutability of the component being assigned - // is inherited from the thing that the component is embedded - // within, then we have to check whether that thing has been - // loaned out as immutable! An example: - // let mut x = {f: Some(3)}; - // let y = &x; // x loaned out as immutable - // x.f = none; // changes type of y.f, which appears to be imm - match *lp { - lp_comp(lp_base, ck) if inherent_mutability(ck) != m_mutbl => { - self.check_for_loan_conflicting_with_assignment( - at, ex, cmt, lp_base); - } - lp_comp(*) | lp_self | lp_local(*) | lp_arg(*) | lp_deref(*) => () + return true; // no errors reported } - } - fn report_purity_error(&mut self, pc: Either, - sp: span, msg: ~str) { - match pc { - Right(pc_default) => { fail!(~"pc_default should be filtered sooner") } - Right(pc_unsafe) => { - // this error was prevented by being marked as unsafe, so flag the - // definition as having contributed to the validity of the program - let def = self.declared_purity.def; - debug!("flagging %? as a used unsafe source", def); - self.tcx().used_unsafe.insert(def); - } - Left(pc_pure_fn) => { - self.tcx().sess.span_err( - sp, - fmt!("%s prohibited in pure context", msg)); - } - Left(pc_cmt(ref e)) => { - if self.reported.insert((*e).cmt.id) { - self.tcx().sess.span_err( - (*e).cmt.span, - fmt!("illegal borrow unless pure: %s", - self.bccx.bckerr_to_str((*e)))); - self.bccx.note_and_explain_bckerr((*e)); - self.tcx().sess.span_note( - sp, - fmt!("impure due to %s", msg)); + fn check_for_assignment_to_restricted_or_frozen_location( + self: &CheckLoanCtxt, + expr: @ast::expr, + cmt: mc::cmt) -> bool + { + //! Check for assignments that violate the terms of an + //! outstanding loan. + + let loan_path = match opt_loan_path(cmt) { + Some(lp) => lp, + None => { return true; /* no loan path, can't be any loans */ } + }; + + // Start by searching for an assignment to a *restricted* + // location. Here is one example of the kind of error caught + // by this check: + // + // let mut v = ~[1, 2, 3]; + // let p = &v; + // v = ~[4]; + // + // In this case, creating `p` triggers a RESTR_MUTATE + // restriction on the path `v`. + // + // Here is a second, more subtle example: + // + // let mut v = ~[1, 2, 3]; + // let p = &const v[0]; + // v[0] = 4; // OK + // v[1] = 5; // OK + // v = ~[4, 5, 3]; // Error + // + // In this case, `p` is pointing to `v[0]`, and it is a + // `const` pointer in any case. So the first two + // assignments are legal (and would be permitted by this + // check). However, the final assignment (which is + // logically equivalent) is forbidden, because it would + // cause the existing `v` array to be freed, thus + // invalidating `p`. In the code, this error results + // because `gather_loans::restrictions` adds a + // `RESTR_MUTATE` restriction whenever the contents of an + // owned pointer are borrowed, and hence while `v[*]` is not + // restricted from being written, `v` is. + for self.each_in_scope_restriction(expr.id, loan_path) + |loan, restr| + { + if restr.set.intersects(RESTR_MUTATE) { + self.report_illegal_mutation(expr, loan_path, loan); + return false; + } + } + + // The previous code handled assignments to paths that + // have been restricted. This covers paths that have been + // directly lent out and their base paths, but does not + // cover random extensions of those paths. For example, + // the following program is not declared illegal by the + // previous check: + // + // let mut v = ~[1, 2, 3]; + // let p = &v; + // v[0] = 4; // declared error by loop below, not code above + // + // The reason that this passes the previous check whereas + // an assignment like `v = ~[4]` fails is because the assignment + // here is to `v[*]`, and the existing restrictions were issued + // for `v`, not `v[*]`. + // + // So in this loop, we walk back up the loan path so long + // as the mutability of the path is dependent on a super + // path, and check that the super path was not lent out as + // mutable or immutable (a const loan is ok). + // + // Note that we are *not* checking for any and all + // restrictions. We are only interested in the pointers + // that the user created, whereas we add restrictions for + // all kinds of paths that are not directly aliased. If we checked + // for all restrictions, and not just loans, then the following + // valid program would be considered illegal: + // + // let mut v = ~[1, 2, 3]; + // let p = &const v[0]; + // v[1] = 5; // ok + // + // Here the restriction that `v` not be mutated would be misapplied + // to block the subpath `v[1]`. + let full_loan_path = loan_path; + let mut loan_path = loan_path; + loop { + match *loan_path { + // Peel back one layer if `loan_path` has + // inherited mutability + LpExtend(lp_base, mc::McInherited, _) => { + loan_path = lp_base; + } + + // Otherwise stop iterating + LpExtend(_, mc::McDeclared, _) | + LpExtend(_, mc::McImmutable, _) | + LpExtend(_, mc::McReadOnly, _) | + LpVar(_) => { + return true; + } + } + + // Check for a non-const loan of `loan_path` + for self.each_in_scope_loan(expr.id) |loan| { + if loan.loan_path == loan_path && loan.mutbl != m_const { + self.report_illegal_mutation(expr, full_loan_path, loan); + return false; + } + } } - } } } - fn check_move_out_from_expr(@mut self, ex: @ast::expr) { + fn report_illegal_mutation(&self, + expr: @ast::expr, + loan_path: &LoanPath, + loan: &Loan) { + self.bccx.span_err( + expr.span, + fmt!("cannot assign to `%s` because it is borrowed", + self.bccx.loan_path_to_str(loan_path))); + self.bccx.span_note( + loan.span, + fmt!("borrow of `%s` occurs here", + self.bccx.loan_path_to_str(loan_path))); + } + + fn check_move_out_from_expr(&self, ex: @ast::expr) { match ex.node { ast::expr_paren(*) => { /* In the case of an expr_paren(), the expression inside @@ -529,52 +527,57 @@ pub impl CheckLoanCtxt { MoveFromIllegalCmt(_) => { self.bccx.span_err( cmt.span, - fmt!("moving out of %s", + fmt!("cannot move out of %s", self.bccx.cmt_to_str(cmt))); } - MoveWhileBorrowed(_, loan_cmt) => { + MoveWhileBorrowed(loan_path, loan_span) => { self.bccx.span_err( cmt.span, - fmt!("moving out of %s prohibited \ - due to outstanding loan", - self.bccx.cmt_to_str(cmt))); + fmt!("cannot move out of `%s` \ + because it is borrowed", + self.bccx.loan_path_to_str(loan_path))); self.bccx.span_note( - loan_cmt.span, - fmt!("loan of %s granted here", - self.bccx.cmt_to_str(loan_cmt))); + loan_span, + fmt!("borrow of `%s` occurs here", + self.bccx.loan_path_to_str(loan_path))); } } } } } - fn analyze_move_out_from_cmt(&mut self, cmt: cmt) -> MoveError { - debug!("check_move_out_from_cmt(cmt=%s)", - self.bccx.cmt_to_repr(cmt)); + fn analyze_move_out_from_cmt(&self, cmt: mc::cmt) -> MoveError { + debug!("check_move_out_from_cmt(cmt=%s)", cmt.repr(self.tcx())); match cmt.cat { - // Rvalues, locals, and arguments can be moved: - cat_rvalue | cat_local(_) | cat_arg(_) | cat_self(_) => {} - - // We allow moving out of static items because the old code - // did. This seems consistent with permitting moves out of - // rvalues, I guess. - cat_special(sk_static_item) => {} - - cat_deref(_, _, unsafe_ptr) => {} - - // Nothing else. - _ => { - return MoveFromIllegalCmt(cmt); - } + // Rvalues, locals, and arguments can be moved: + mc::cat_rvalue | mc::cat_local(_) | + mc::cat_arg(_, ast::by_copy) | mc::cat_self(_) => {} + + // It seems strange to allow a move out of a static item, + // but what happens in practice is that you have a + // reference to a constant with a type that should be + // moved, like `None::<~int>`. The type of this constant + // is technically `Option<~int>`, which moves, but we know + // that the content of static items will never actually + // contain allocated pointers, so we can just memcpy it. + mc::cat_static_item => {} + + mc::cat_deref(_, _, mc::unsafe_ptr(*)) => {} + + // Nothing else. + _ => { + return MoveFromIllegalCmt(cmt); + } } - self.bccx.add_to_mutbl_map(cmt); + // NOTE inadequare if/when we permit `move a.b` // check for a conflicting loan: - for cmt.lp.each |lp| { - for self.walk_loans_of(cmt.id, *lp) |loan| { - return MoveWhileBorrowed(cmt, loan.cmt); + for opt_loan_path(cmt).each |&lp| { + for self.each_in_scope_restriction(cmt.id, lp) |loan, _| { + // Any restriction prevents moves. + return MoveWhileBorrowed(loan.loan_path, loan.span); } } @@ -582,105 +585,46 @@ pub impl CheckLoanCtxt { } fn check_call(&mut self, - expr: @ast::expr, - callee: Option<@ast::expr>, - callee_id: ast::node_id, - callee_span: span, - args: &[@ast::expr]) { - let pc = self.purity(expr.id); - match pc { - // no purity, no need to check for anything - Right(pc_default) => return, - - // some form of purity, definitely need to check - Left(_) => (), - - // Unsafe trumped. To see if the unsafe is necessary, see what the - // purity would have been without a trump, and if it's some form - // of purity then we need to go ahead with the check - Right(pc_unsafe) => { - match do with(&mut self.declared_purity.purity, - ast::impure_fn) { self.purity(expr.id) } { - Right(pc_unsafe) => fail!(~"unsafe can't trump twice"), - Right(pc_default) => return, - Left(_) => () - } - } - - } - self.check_pure_callee_or_arg( - pc, callee, callee_id, callee_span); - for args.each |arg| { - self.check_pure_callee_or_arg( - pc, Some(*arg), arg.id, arg.span); - } + _expr: @ast::expr, + _callee: Option<@ast::expr>, + _callee_id: ast::node_id, + _callee_span: span, + _args: &[@ast::expr]) + { + // NB: This call to check for conflicting loans is not truly + // necessary, because the callee_id never issues new loans. + // However, I added it for consistency and lest the system + // should change in the future. + // + // FIXME(#5074) nested method calls + // self.check_for_conflicting_loans(callee_id); } } -fn check_loans_in_fn(fk: &visit::fn_kind, - decl: &ast::fn_decl, - body: &ast::blk, - sp: span, - id: ast::node_id, - self: @mut CheckLoanCtxt, - visitor: visit::vt<@mut CheckLoanCtxt>) { - let is_stack_closure = self.is_stack_closure(id); - let fty = ty::node_id_to_type(self.tcx(), id); - - let declared_purity, src; +fn check_loans_in_fn<'a>(fk: &visit::fn_kind, + decl: &ast::fn_decl, + body: &ast::blk, + sp: span, + id: ast::node_id, + self: @mut CheckLoanCtxt<'a>, + visitor: visit::vt<@mut CheckLoanCtxt<'a>>) { match *fk { - visit::fk_item_fn(*) | visit::fk_method(*) | + visit::fk_item_fn(*) | + visit::fk_method(*) | visit::fk_dtor(*) => { - declared_purity = ty::ty_fn_purity(fty); - src = id; + // Don't process nested items. + return; } - visit::fk_anon(*) | visit::fk_fn_block(*) => { + visit::fk_anon(*) | + visit::fk_fn_block(*) => { + let fty = ty::node_id_to_type(self.tcx(), id); let fty_sigil = ty::ty_closure_sigil(fty); check_moves_from_captured_variables(self, id, fty_sigil); - let pair = ty::determine_inherited_purity( - (self.declared_purity.purity, self.declared_purity.def), - (ty::ty_fn_purity(fty), id), - fty_sigil); - declared_purity = pair.first(); - src = pair.second(); } } - debug!("purity on entry=%?", copy self.declared_purity); - do save_and_restore_managed(self.declared_purity) { - do save_and_restore_managed(self.fn_args) { - self.declared_purity = @mut PurityState::function(declared_purity, src); - - match *fk { - visit::fk_anon(*) | - visit::fk_fn_block(*) if is_stack_closure => { - // inherits the fn_args from enclosing ctxt - } - visit::fk_anon(*) | visit::fk_fn_block(*) | - visit::fk_method(*) | visit::fk_item_fn(*) | - visit::fk_dtor(*) => { - let mut fn_args = ~[]; - for decl.inputs.each |input| { - // For the purposes of purity, only consider function- - // typed bindings in trivial patterns to be function - // arguments. For example, do not allow `f` and `g` in - // (f, g): (&fn(), &fn()) to be called. - match input.pat.node { - ast::pat_ident(_, _, None) => { - fn_args.push(input.pat.id); - } - _ => {} // Ignore this argument. - } - } - *self.fn_args = @fn_args; - } - } - - visit::visit_fn(fk, decl, body, sp, id, self, visitor); - } - } - debug!("purity on exit=%?", copy self.declared_purity); + visit::visit_fn(fk, decl, body, sp, id, self, visitor); fn check_moves_from_captured_variables(self: @mut CheckLoanCtxt, id: ast::node_id, @@ -706,16 +650,16 @@ fn check_loans_in_fn(fk: &visit::fn_kind, fmt!("illegal by-move capture of %s", self.bccx.cmt_to_str(move_cmt))); } - MoveWhileBorrowed(move_cmt, loan_cmt) => { + MoveWhileBorrowed(loan_path, loan_span) => { self.bccx.span_err( cap_var.span, - fmt!("by-move capture of %s prohibited \ - due to outstanding loan", - self.bccx.cmt_to_str(move_cmt))); + fmt!("cannot move `%s` into closure \ + because it is borrowed", + self.bccx.loan_path_to_str(loan_path))); self.bccx.span_note( - loan_cmt.span, - fmt!("loan of %s granted here", - self.bccx.cmt_to_str(loan_cmt))); + loan_span, + fmt!("borrow of `%s` occurs here", + self.bccx.loan_path_to_str(loan_path))); } } } @@ -726,17 +670,19 @@ fn check_loans_in_fn(fk: &visit::fn_kind, } } -fn check_loans_in_local(local: @ast::local, - self: @mut CheckLoanCtxt, - vt: visit::vt<@mut CheckLoanCtxt>) { +fn check_loans_in_local<'a>(local: @ast::local, + self: @mut CheckLoanCtxt<'a>, + vt: visit::vt<@mut CheckLoanCtxt<'a>>) { visit::visit_local(local, self, vt); } -fn check_loans_in_expr(expr: @ast::expr, - self: @mut CheckLoanCtxt, - vt: visit::vt<@mut CheckLoanCtxt>) { - debug!("check_loans_in_expr(expr=%?/%s)", - expr.id, pprust::expr_to_str(expr, self.tcx().sess.intr())); +fn check_loans_in_expr<'a>(expr: @ast::expr, + self: @mut CheckLoanCtxt<'a>, + vt: visit::vt<@mut CheckLoanCtxt<'a>>) { + debug!("check_loans_in_expr(expr=%s)", + expr.repr(self.tcx())); + + visit::visit_expr(expr, self, vt); self.check_for_conflicting_loans(expr.id); @@ -746,12 +692,12 @@ fn check_loans_in_expr(expr: @ast::expr, match expr.node { ast::expr_swap(l, r) => { - self.check_assignment(at_swap, l); - self.check_assignment(at_swap, r); + self.check_assignment(l); + self.check_assignment(r); } ast::expr_assign(dest, _) | ast::expr_assign_op(_, dest, _) => { - self.check_assignment(at_straight_up, dest); + self.check_assignment(dest); } ast::expr_call(f, ref args, _) => { self.check_call(expr, Some(f), f.id, f.span, *args); @@ -776,32 +722,35 @@ fn check_loans_in_expr(expr: @ast::expr, expr.span, ~[]); } - ast::expr_match(*) => { - // Note: moves out of pattern bindings are not checked by - // the borrow checker, at least not directly. What happens - // is that if there are any moved bindings, the discriminant - // will be considered a move, and this will be checked as - // normal. Then, in `middle::check_match`, we will check - // that no move occurs in a binding that is underneath an - // `@` or `&`. Together these give the same guarantees as - // `check_move_out_from_expr()` without requiring us to - // rewalk the patterns and rebuild the pattern - // categorizations. - } _ => { } } - - visit::visit_expr(expr, self, vt); } -fn check_loans_in_block(blk: &ast::blk, - self: @mut CheckLoanCtxt, - vt: visit::vt<@mut CheckLoanCtxt>) { - do save_and_restore_managed(self.declared_purity) { - self.check_for_conflicting_loans(blk.node.id); +fn check_loans_in_pat<'a>(pat: @ast::pat, + self: @mut CheckLoanCtxt<'a>, + vt: visit::vt<@mut CheckLoanCtxt<'a>>) +{ + self.check_for_conflicting_loans(pat.id); + + // Note: moves out of pattern bindings are not checked by + // the borrow checker, at least not directly. What happens + // is that if there are any moved bindings, the discriminant + // will be considered a move, and this will be checked as + // normal. Then, in `middle::check_match`, we will check + // that no move occurs in a binding that is underneath an + // `@` or `&`. Together these give the same guarantees as + // `check_move_out_from_expr()` without requiring us to + // rewalk the patterns and rebuild the pattern + // categorizations. + + visit::visit_pat(pat, self, vt); +} - *self.declared_purity = self.declared_purity.recurse(blk); - visit::visit_block(blk, self, vt); - } +fn check_loans_in_block<'a>(blk: &ast::blk, + self: @mut CheckLoanCtxt<'a>, + vt: visit::vt<@mut CheckLoanCtxt<'a>>) +{ + visit::visit_block(blk, self, vt); + self.check_for_conflicting_loans(blk.node.id); } diff --git a/src/librustc/middle/borrowck/doc.rs b/src/librustc/middle/borrowck/doc.rs new file mode 100644 index 0000000000000..1e09fbe71843c --- /dev/null +++ b/src/librustc/middle/borrowck/doc.rs @@ -0,0 +1,750 @@ +// Copyright 2012 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. + +/*! + +# The Borrow Checker + +This pass has the job of enforcing memory safety. This is a subtle +topic. The only way I know how to explain it is terms of a formal +model, so that's what I'll do. + +# Formal model + +Let's consider a simple subset of Rust in which you can only borrow +from lvalues like so: + + LV = x | LV.f | *LV + +Here `x` represents some variable, `LV.f` is a field reference, +and `*LV` is a pointer dereference. There is no auto-deref or other +niceties. This means that if you have a type like: + + struct S { f: uint } + +and a variable `a: ~S`, then the rust expression `a.f` would correspond +to an `LV` of `(*a).f`. + +Here is the formal grammar for the types we'll consider: + + TY = () | S<'LT...> | ~TY | & 'LT MQ TY | @ MQ TY + MQ = mut | imm | const + +Most of these types should be pretty self explanatory. Here `S` is a +struct name and we assume structs are declared like so: + + SD = struct S<'LT...> { (f: TY)... } + +# An intuitive explanation + +## Issuing loans + +Now, imagine we had a program like this: + + struct Foo { f: uint, g: uint } + ... + 'a: { + let mut x: ~Foo = ...; + let y = &mut (*x).f; + x = ...; + } + +This is of course dangerous because mutating `x` will free the old +value and hence invalidate `y`. The borrow checker aims to prevent +this sort of thing. + +### Loans + +The way the borrow checker works is that it analyzes each borrow +expression (in our simple model, that's stuff like `&LV`, though in +real life there are a few other cases to consider). For each borrow +expression, it computes a vector of loans: + + LOAN = (LV, LT, PT, LK) + PT = Partial | Total + LK = MQ | RESERVE + +Each `LOAN` tuple indicates some sort of restriction on what can be +done to the lvalue `LV`; `LV` will always be a path owned by the +current stack frame. These restrictions are called "loans" because +they are always the result of a borrow expression. + +Every loan has a lifetime `LT` during which those restrictions are in +effect. The indicator `PT` distinguishes between *total* loans, in +which the LV itself was borrowed, and *partial* loans, which means +that some content ownwed by LV was borrowed. + +The final element in the loan tuple is the *loan kind* `LK`. There +are four kinds: mutable, immutable, const, and reserve: + +- A "mutable" loan means that LV may be written to through an alias, and + thus LV cannot be written to directly or immutably aliased (remember + that we preserve the invariant that any given value can only be + written to through one path at a time; hence if there is a mutable + alias to LV, then LV cannot be written directly until this alias is + out of scope). + +- An "immutable" loan means that LV must remain immutable. Hence it + cannot be written, but other immutable aliases are permitted. + +- A "const" loan means that an alias to LV exists. LV may still be + written or frozen. + +- A "reserve" loan is the strongest case. It prevents both mutation + and aliasing of any kind, including `&const` loans. Reserve loans + are a side-effect of borrowing an `&mut` loan. + +In addition to affecting mutability, a loan of any kind implies that +LV cannot be moved. + +### Example + +To give you a better feeling for what a loan is, let's look at three +loans that would be issued as a result of the borrow `&(*x).f` in the +example above: + + ((*x).f, Total, mut, 'a) + (*x, Partial, mut, 'a) + (x, Partial, mut, 'a) + +The first loan states that the expression `(*x).f` has been loaned +totally as mutable for the lifetime `'a`. This first loan would +prevent an assignment `(*x).f = ...` from occurring during the +lifetime `'a`. + +Now let's look at the second loan. You may have expected that each +borrow would result in only one loan. But this is not the case. +Instead, there will be loans for every path where mutation might +affect the validity of the borrowed pointer that is created (in some +cases, there can even be multiple loans per path, see the section on +"Borrowing in Calls" below for the gory details). The reason for this +is to prevent actions that would indirectly affect the borrowed path. +In this case, we wish to ensure that `(*x).f` is not mutated except +through the mutable alias `y`. Therefore, we must not only prevent an +assignment to `(*x).f` but also an assignment like `*x = Foo {...}`, +as this would also mutate the field `f`. To do so, we issue a +*partial* mutable loan for `*x` (the loan is partial because `*x` +itself was not borrowed). This partial loan will cause any attempt to +assign to `*x` to be flagged as an error. + +Because both partial and total loans prevent assignments, you may +wonder why we bother to distinguish between them. The reason for this +distinction has to do with preventing double borrows. In particular, +it is legal to borrow both `&mut x.f` and `&mut x.g` simultaneously, +but it is not legal to borrow `&mut x.f` twice. In the borrow checker, +the first case would result in two *partial* mutable loans of `x` +(along with one total mutable loan of `x.f` and one of `x.g) whereas +the second would result in two *total* mutable loans of `x.f` (along +with two partial mutable loans of `x`). Multiple *total mutable* loan +for the same path are not permitted, but multiple *partial* loans (of +any mutability) are permitted. + +Finally, we come to the third loan. This loan is a partial mutable +loan of `x`. This loan prevents us from reassigning `x`, which would +be bad for two reasons. First, it would change the value of `(*x).f` +but, even worse, it would cause the pointer `y` to become a dangling +pointer. Bad all around. + +## Checking for illegal assignments, moves, and reborrows + +Once we have computed the loans introduced by each borrow, the borrow +checker will determine the full set of loans in scope at each +expression and use that to decide whether that expression is legal. +Remember that the scope of loan is defined by its lifetime LT. We +sometimes say that a loan which is in-scope at a particular point is +an "outstanding loan". + +The kinds of expressions which in-scope loans can render illegal are +*assignments*, *moves*, and *borrows*. + +An assignments to an lvalue LV is illegal if there is in-scope mutable +or immutable loan for LV. Assignment with an outstanding mutable loan +is illegal because then the `&mut` pointer is supposed to be the only +way to mutate the value. Assignment with an outstanding immutable +loan is illegal because the value is supposed to be immutable at that +point. + +A move from an lvalue LV is illegal if there is any sort of +outstanding loan. + +A borrow expression may be illegal if any of the loans which it +produces conflict with other outstanding loans. Two loans are +considered compatible if one of the following conditions holds: + +- At least one loan is a const loan. +- Both loans are partial loans. +- Both loans are immutable. + +Any other combination of loans is illegal. + +# The set of loans that results from a borrow expression + +Here we'll define four functions---MUTATE, FREEZE, ALIAS, and +TAKE---which are all used to compute the set of LOANs that result +from a borrow expression. The first three functions each have +a similar type signature: + + MUTATE(LV, LT, PT) -> LOANS + FREEZE(LV, LT, PT) -> LOANS + ALIAS(LV, LT, PT) -> LOANS + +MUTATE, FREEZE, and ALIAS are used when computing the loans result +from mutable, immutable, and const loans respectively. For example, +the loans resulting from an expression like `&mut (*x).f` would be +computed by `MUTATE((*x).f, LT, Total)`, where `LT` is the lifetime of +the resulting pointer. Similarly the loans for `&(*x).f` and `&const +(*x).f` would be computed by `FREEZE((*x).f, LT, Total)` and +`ALIAS((*x).f, LT, Total)` respectively. (Actually this is a slight +simplification; see the section below on Borrows in Calls for the full +gory details) + +The names MUTATE, FREEZE, and ALIAS are intended to suggest the +semantics of `&mut`, `&`, and `&const` borrows respectively. `&mut`, +for example, creates a mutable alias of LV. `&` causes the borrowed +value to be frozen (immutable). `&const` does neither but does +introduce an alias to be the borrowed value. + +Each of these three functions is only defined for some inputs. That +is, it may occur that some particular borrow is not legal. For +example, it is illegal to make an `&mut` loan of immutable data. In +that case, the MUTATE() function is simply not defined (in the code, +it returns a Result<> condition to indicate when a loan would be +illegal). + +The final function, RESERVE, is used as part of borrowing an `&mut` +pointer. Due to the fact that it is used for one very particular +purpose, it has a rather simpler signature than the others: + + RESERVE(LV, LT) -> LOANS + +It is explained when we come to that case. + +## The function MUTATE() + +Here we use [inference rules][ir] to define the MUTATE() function. +We will go case by case for the various kinds of lvalues that +can be borrowed. + +[ir]: http://en.wikipedia.org/wiki/Rule_of_inference + +### Mutating local variables + +The rule for mutating local variables is as follows: + + Mutate-Variable: + LT <= Scope(x) + Mut(x) = Mut + -------------------------------------------------- + MUTATE(x, LT, PT) = (x, LT, PT, mut) + +Here `Scope(x)` is the lifetime of the block in which `x` was declared +and `Mut(x)` indicates the mutability with which `x` was declared. +This rule simply states that you can only create a mutable alias +to a variable if it is mutable, and that alias cannot outlive the +stack frame in which the variable is declared. + +### Mutating fields and owned pointers + +As it turns out, the rules for mutating fields and mutating owned +pointers turn out to be quite similar. The reason is that the +expressions `LV.f` and `*LV` are both owned by their base expression +`LV`. So basically the result of mutating `LV.f` or `*LV` is computed +by adding a loan for `LV.f` or `*LV` and then the loans for a partial +take of `LV`: + + Mutate-Field: + MUTATE(LV, LT, Partial) = LOANS + ------------------------------------------------------------ + MUTATE(LV.f, LT, PT) = LOANS, (LV.F, LT, PT, mut) + + Mutate-Owned-Ptr: + Type(LV) = ~Ty + MUTATE(LV, LT, Partial) = LOANS + ------------------------------------------------------------ + MUTATE(*LV, LT, PT) = LOANS, (*LV, LT, PT, mut) + +Note that while our micro-language only has fields, the slight +variations on the `Mutate-Field` rule are used for any interior content +that appears in the full Rust language, such as the contents of a +tuple, fields in a struct, or elements of a fixed-length vector. + +### Mutating dereferenced borrowed pointers + +The rule for borrowed pointers is by far the most complicated: + + Mutate-Mut-Borrowed-Ptr: + Type(LV) = <_P mut Ty // (1) + LT <= LT_P // (2) + RESERVE(LV, LT) = LOANS // (3) + ------------------------------------------------------------ + MUTATE(*LV, LT, PT) = LOANS, (*LV, LT, PT, Mut) + +Condition (1) states that only a mutable borrowed pointer can be +taken. Condition (2) states that the lifetime of the alias must be +less than the lifetime of the borrowed pointer being taken. + +Conditions (3) and (4) are where things get interesting. The intended +semantics of the borrow is that the new `&mut` pointer is the only one +which has the right to modify the data; the original `&mut` pointer +must not be used for mutation. Because borrowed pointers do not own +their content nor inherit mutability, we must be particularly cautious +of aliases, which could permit the original borrowed pointer to be +reached from another path and thus circumvent our loans. + +Here is one example of what could go wrong if we ignore clause (4): + + let x: &mut T; + ... + let y = &mut *x; // Only *y should be able to mutate... + let z = &const x; + **z = ...; // ...but here **z is still able to mutate! + +Another possible error could occur with moves: + + let x: &mut T; + ... + let y = &mut *x; // Issues loan: (*x, LT, Total, Mut) + let z = x; // moves from x + *z = ...; // Mutates *y indirectly! Bad. + +In both of these cases, the problem is that when creating the alias +`y` we would only issue a loan preventing assignment through `*x`. +But this loan can be easily circumvented by moving from `x` or +aliasing it. Note that, in the first example, the alias of `x` was +created using `&const`, which is a particularly weak form of alias. + +The danger of aliases can also occur when the `&mut` pointer itself +is already located in an alias location, as here: + + let x: @mut &mut T; // or &mut &mut T, &&mut T, + ... // &const &mut T, @&mut T, etc + let y = &mut **x; // Only *y should be able to mutate... + let z = x; + **z = ...; // ...but here **z is still able to mutate! + +When we cover the rules for RESERVE, we will see that it would +disallow this case, because MUTATE can only be applied to canonical +lvalues which are owned by the current stack frame. + +It might be the case that if `&const` and `@const` pointers were +removed, we could do away with RESERVE and simply use MUTATE instead. +But we have to be careful about the final example in particular, since +dynamic freezing would not be sufficient to prevent this example. +Perhaps a combination of MUTATE with a predicate OWNED(LV). + +One final detail: unlike every other case, when we calculate the loans +using RESERVE we do not use the original lifetime `LT` but rather +`GLB(Scope(LV), LT)`. What this says is: + +### Mutating dereferenced managed pointers + +Because the correctness of managed pointer loans is checked dynamically, +the rule is quite simple: + + Mutate-Mut-Managed-Ptr: + Type(LV) = @mut Ty + Add ROOT-FREEZE annotation for *LV with lifetime LT + ------------------------------------------------------------ + MUTATE(*LV, LT, Total) = [] + +No loans are issued. Instead, we add a side annotation that causes +`*LV` to be rooted and frozen on entry to LV. You could rephrase +these rules as having multiple returns values, or rephrase this as a +kind of loan, but whatever. + +One interesting point is that *partial takes* of `@mut` are forbidden. +This is not for any soundness reason but just because it is clearer +for users when `@mut` values are either lent completely or not at all. + +## The function FREEZE + +The rules for FREEZE are pretty similar to MUTATE. The first four +cases I'll just present without discussion, as the reasoning is +quite analogous to the MUTATE case: + + Freeze-Variable: + LT <= Scope(x) + -------------------------------------------------- + FREEZE(x, LT, PT) = (x, LT, PT, imm) + + Freeze-Field: + FREEZE(LV, LT, Partial) = LOANS + ------------------------------------------------------------ + FREEZE(LV.f, LT, PT) = LOANS, (LV.F, LT, PT, imm) + + Freeze-Owned-Ptr: + Type(LV) = ~Ty + FREEZE(LV, LT, Partial) = LOANS + ------------------------------------------------------------ + FREEZE(*LV, LT, PT) = LOANS, (*LV, LT, PT, imm) + + Freeze-Mut-Borrowed-Ptr: + Type(LV) = <_P mut Ty + LT <= LT_P + RESERVE(LV, LT) = LOANS + ------------------------------------------------------------ + FREEZE(*LV, LT, PT) = LOANS, (*LV, LT, PT, Imm) + + Freeze-Mut-Managed-Ptr: + Type(LV) = @mut Ty + Add ROOT-FREEZE annotation for *LV with lifetime LT + ------------------------------------------------------------ + Freeze(*LV, LT, Total) = [] + +The rule to "freeze" an immutable borrowed pointer is quite +simple, since the content is already immutable: + + Freeze-Imm-Borrowed-Ptr: + Type(LV) = <_P Ty // (1) + LT <= LT_P // (2) + ------------------------------------------------------------ + FREEZE(*LV, LT, PT) = LOANS, (*LV, LT, PT, Mut) + +The final two rules pertain to borrows of `@Ty`. There is a bit of +subtlety here. The main problem is that we must guarantee that the +managed box remains live for the entire borrow. We can either do this +dynamically, by rooting it, or (better) statically, and hence there +are two rules: + + Freeze-Imm-Managed-Ptr-1: + Type(LV) = @Ty + Add ROOT annotation for *LV + ------------------------------------------------------------ + FREEZE(*LV, LT, PT) = [] + + Freeze-Imm-Managed-Ptr-2: + Type(LV) = @Ty + LT <= Scope(LV) + Mut(LV) = imm + LV is not moved + ------------------------------------------------------------ + FREEZE(*LV, LT, PT) = [] + +The intention of the second rule is to avoid an extra root if LV +serves as a root. In that case, LV must (1) outlive the borrow; (2) +be immutable; and (3) not be moved. + +## The ALIAS function + +The function ALIAS is used for `&const` loans but also to handle one +corner case concerning function arguments (covered in the section +"Borrows in Calls" below). It computes the loans that result from +observing that there is a pointer to `LV` and thus that pointer must +remain valid. + +The first two rules are simple: + + Alias-Variable: + LT <= Scope(x) + -------------------------------------------------- + ALIAS(x, LT, PT) = (x, LT, PT, Const) + + Alias-Field: + ALIAS(LV, LT, Partial) = LOANS + ------------------------------------------------------------ + ALIAS(LV.f, LT, PT) = LOANS, (LV.F, LT, PT, Const) + +### Aliasing owned pointers + +The rule for owned pointers is somewhat interesting: + + Alias-Owned-Ptr: + Type(LV) = ~Ty + FREEZE(LV, LT, Partial) = LOANS + ------------------------------------------------------------ + ALIAS(*LV, LT, PT) = LOANS, (*LV, LT, PT, Const) + +Here we *freeze* the base `LV`. The reason is that if an owned +pointer is mutated it frees its content, which means that the alias to +`*LV` would become a dangling pointer. + +### Aliasing borrowed pointers + +The rule for borrowed pointers is quite simple, because borrowed +pointers do not own their content and thus do not play a role in +keeping it live: + + Alias-Borrowed-Ptr: + Type(LV) = <_P MQ Ty + LT <= LT_P + ------------------------------------------------------------ + ALIAS(*LV, LT, PT) = [] + +Basically, the existence of a borrowed pointer to some memory with +lifetime LT_P is proof that the memory can safely be aliased for any +lifetime LT <= LT_P. + +### Aliasing managed pointers + +The rules for aliasing managed pointers are similar to those +used with FREEZE, except that they apply to all manager pointers +regardles of mutability: + + Alias-Managed-Ptr-1: + Type(LV) = @MQ Ty + Add ROOT annotation for *LV + ------------------------------------------------------------ + ALIAS(*LV, LT, PT) = [] + + Alias-Managed-Ptr-2: + Type(LV) = @MQ Ty + LT <= Scope(LV) + Mut(LV) = imm + LV is not moved + ------------------------------------------------------------ + ALIAS(*LV, LT, PT) = [] + +## The RESERVE function + +The final function, RESERVE, is used for loans of `&mut` pointers. As +discussed in the section on the function MUTATE, we must be quite +careful when "re-borrowing" an `&mut` pointer to ensure that the original +`&mut` pointer can no longer be used to mutate. + +There are a couple of dangers to be aware of: + +- `&mut` pointers do not inherit mutability. Therefore, if you have + an lvalue LV with type `&mut T` and you freeze `LV`, you do *not* + freeze `*LV`. This is quite different from an `LV` with type `~T`. + +- Also, because they do not inherit mutability, if the `&mut` pointer + lives in an aliased location, then *any alias* can be used to write! + +As a consequence of these two rules, RESERVE can only be successfully +invoked on an lvalue LV that is *owned by the current stack frame*. +This ensures that there are no aliases that are not visible from the +outside. Moreover, Reserve loans are incompatible with all other +loans, even Const loans. This prevents any aliases from being created +within the current function. + +### Reserving local variables + +The rule for reserving a variable is generally straightforward but +with one interesting twist: + + Reserve-Variable: + -------------------------------------------------- + RESERVE(x, LT) = (x, LT, Total, Reserve) + +The twist here is that the incoming lifetime is not required to +be a subset of the incoming variable, unlike every other case. To +see the reason for this, imagine the following function: + + struct Foo { count: uint } + fn count_field(x: &'a mut Foo) -> &'a mut count { + &mut (*x).count + } + +This function consumes one `&mut` pointer and returns another with the +same lifetime pointing at a particular field. The borrow for the +`&mut` expression will result in a call to `RESERVE(x, 'a)`, which is +intended to guarantee that `*x` is not later aliased or used to +mutate. But the lifetime of `x` is limited to the current function, +which is a sublifetime of the parameter `'a`, so the rules used for +MUTATE, FREEZE, and ALIAS (which require that the lifetime of the loan +not exceed the lifetime of the variable) would result in an error. + +Nonetheless this function is perfectly legitimate. After all, the +caller has moved in an `&mut` pointer with lifetime `'a`, and thus has +given up their right to mutate the value for the remainder of `'a`. +So it is fine for us to return a pointer with the same lifetime. + +The reason that RESERVE differs from the other functions is that +RESERVE is not responsible for guaranteeing that the pointed-to data +will outlive the borrowed pointer being created. After all, `&mut` +values do not own the data they point at. + +### Reserving owned content + +The rules for fields and owned pointers are very straightforward: + + Reserve-Field: + RESERVE(LV, LT) = LOANS + ------------------------------------------------------------ + RESERVE(LV.f, LT) = LOANS, (LV.F, LT, Total, Reserve) + + Reserve-Owned-Ptr: + Type(LV) = ~Ty + RESERVE(LV, LT) = LOANS + ------------------------------------------------------------ + RESERVE(*LV, LT) = LOANS, (*LV, LT, Total, Reserve) + +### Reserving `&mut` borrowed pointers + +Unlike other borrowed pointers, `&mut` pointers are unaliasable, +so we can reserve them like everything else: + + Reserve-Mut-Borrowed-Ptr: + Type(LV) = <_P mut Ty + RESERVE(LV, LT) = LOANS + ------------------------------------------------------------ + RESERVE(*LV, LT) = LOANS, (*LV, LT, Total, Reserve) + +## Borrows in calls + +Earlier we said that the MUTATE, FREEZE, and ALIAS functions were used +to compute the loans resulting from a borrow expression. But this is +not strictly correct, there is a slight complication that occurs with +calls by which additional loans may be necessary. We will explain +that here and give the full details. + +Imagine a call expression `'a: E1(E2, E3)`, where `Ei` are some +expressions. If we break this down to something a bit lower-level, it +is kind of short for: + + 'a: { + 'a_arg1: let temp1: ... = E1; + 'a_arg2: let temp2: ... = E2; + 'a_arg3: let temp3: ... = E3; + 'a_call: temp1(temp2, temp3) + } + +Here the lifetime labels indicate the various lifetimes. As you can +see there are in fact four relevant lifetimes (only one of which was +named by the user): `'a` corresponds to the expression `E1(E2, E3)` as +a whole. `'a_arg1`, `'a_arg2`, and `'a_arg3` correspond to the +evaluations of `E1`, `E2`, and `E3` respectively. Finally, `'a_call` +corresponds to the *actual call*, which is the point where the values +of the parameters will be used. + +Now, let's look at a (contrived, but representative) example to see +why all this matters: + + struct Foo { f: uint, g: uint } + ... + fn add(p: &mut uint, v: uint) { + *p += v; + } + ... + fn inc(p: &mut uint) -> uint { + *p += 1; *p + } + fn weird() { + let mut x: ~Foo = ~Foo { ... }; + 'a: add(&mut (*x).f, + 'b: inc(&mut (*x).f)) // (*) + } + +The important part is the line marked `(*)` which contains a call to +`add()`. The first argument is a mutable borrow of the field `f`. +The second argument *always borrows* the field `f`. Now, if these two +borrows overlapped in time, this would be illegal, because there would +be two `&mut` pointers pointing at `f`. And, in a way, they *do* +overlap in time, since the first argument will be evaluated first, +meaning that the pointer will exist when the second argument executes. +But in another important way they do not overlap in time. Let's +expand out that final call to `add()` as we did before: + + 'a: { + 'a_arg1: let a_temp1: ... = add; + 'a_arg2: let a_temp2: &'a_call mut uint = &'a_call mut (*x).f; + 'a_arg3_: let a_temp3: uint = { + let b_temp1: ... = inc; + let b_temp2: &'b_call = &'b_call mut (*x).f; + 'b_call: b_temp1(b_temp2) + }; + 'a_call: a_temp1(a_temp2, a_temp3) + } + +When it's written this way, we can see that although there are two +borrows, the first has lifetime `'a_call` and the second has lifetime +`'b_call` and in fact these lifetimes do not overlap. So everything +is fine. + +But this does not mean that there isn't reason for caution! Imagine a +devious program like *this* one: + + struct Foo { f: uint, g: uint } + ... + fn add(p: &mut uint, v: uint) { + *p += v; + } + ... + fn consume(x: ~Foo) -> uint { + x.f + x.g + } + fn weird() { + let mut x: ~Foo = ~Foo { ... }; + 'a: add(&mut (*x).f, consume(x)) // (*) + } + +In this case, there is only one borrow, but the second argument is +`consume(x)` instead of a second borrow. Because `consume()` is +declared to take a `~Foo`, it will in fact free the pointer `x` when +it has finished executing. If it is not obvious why this is +troublesome, consider this expanded version of that call: + + 'a: { + 'a_arg1: let a_temp1: ... = add; + 'a_arg2: let a_temp2: &'a_call mut uint = &'a_call mut (*x).f; + 'a_arg3_: let a_temp3: uint = { + let b_temp1: ... = consume; + let b_temp2: ~Foo = x; + 'b_call: b_temp1(x) + }; + 'a_call: a_temp1(a_temp2, a_temp3) + } + +In this example, we will have borrowed the first argument before `x` +is freed and then free `x` during evaluation of the second +argument. This causes `a_temp2` to be invalidated. + +Of course the loans computed from the borrow expression are supposed +to prevent this situation. But if we just considered the loans from +`MUTATE((*x).f, 'a_call, Total)`, the resulting loans would be: + + ((*x).f, 'a_call, Total, Mut) + (*x, 'a_call, Partial, Mut) + (x, 'a_call, Partial, Mut) + +Because these loans are only in scope for `'a_call`, they do nothing +to prevent the move that occurs evaluating the second argument. + +The way that we solve this is to say that if you have a borrow +expression `&'LT_P mut LV` which itself occurs in the lifetime +`'LT_B`, then the resulting loans are: + + MUTATE(LV, LT_P, Total) + ALIAS(LV, LUB(LT_P, LT_B), Total) + +The call to MUTATE is what we've seen so far. The second part +expresses the idea that the expression LV will be evaluated starting +at LT_B until the end of LT_P. Now, in the normal case, LT_P >= LT_B, +and so the second set of loans that result from a ALIAS are basically +a no-op. However, in the case of an argument where the evaluation of +the borrow occurs before the interval where the resulting pointer will +be used, this ALIAS is important. + +In the case of our example, it would produce a set of loans like: + + ((*x).f, 'a, Total, Const) + (*x, 'a, Total, Const) + (x, 'a, Total, Imm) + +The scope of these loans is `'a = LUB('a_arg2, 'a_call)`, and so they +encompass all subsequent arguments. The first set of loans are Const +loans, which basically just prevent moves. However, when we cross +over the dereference of the owned pointer `x`, the rule for ALIAS +specifies that `x` must be frozen, and hence the final loan is an Imm +loan. In any case the troublesome second argument would be flagged +as an error. + +# Maps that are created + +Borrowck results in two maps. + +- `root_map`: identifies those expressions or patterns whose result + needs to be rooted. Conceptually the root_map maps from an + expression or pattern node to a `node_id` identifying the scope for + which the expression must be rooted (this `node_id` should identify + a block or call). The actual key to the map is not an expression id, + however, but a `root_map_key`, which combines an expression id with a + deref count and is used to cope with auto-deref. + +*/ diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs deleted file mode 100644 index e40d0e63eb38e..0000000000000 --- a/src/librustc/middle/borrowck/gather_loans.rs +++ /dev/null @@ -1,643 +0,0 @@ -// Copyright 2012 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. - -// ---------------------------------------------------------------------- -// Gathering loans -// -// The borrow check proceeds in two phases. In phase one, we gather the full -// set of loans that are required at any point. These are sorted according to -// their associated scopes. In phase two, checking loans, we will then make -// sure that all of these loans are honored. - -use middle::borrowck::preserve::{PreserveCondition, PcOk, PcIfPure}; -use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl}; -use middle::borrowck::{LoanKind, TotalFreeze, PartialFreeze, - TotalTake, PartialTake, Immobile}; -use middle::borrowck::ReqMaps; -use middle::borrowck::loan; -use middle::mem_categorization::{cmt, mem_categorization_ctxt}; -use middle::pat_util; -use middle::ty::{ty_region}; -use middle::ty; -use util::common::indenter; -use util::ppaux::{Repr, region_to_str}; - -use core::hashmap::{HashSet, HashMap}; -use syntax::ast::{m_const, m_imm, m_mutbl}; -use syntax::ast; -use syntax::codemap::span; -use syntax::print::pprust; -use syntax::visit; - -/// Context used while gathering loans: -/// -/// - `bccx`: the the borrow check context -/// - `req_maps`: the maps computed by `gather_loans()`, see def'n of the -/// struct `ReqMaps` for more info -/// - `item_ub`: the id of the block for the enclosing fn/method item -/// - `root_ub`: the id of the outermost block for which we can root -/// an `@T`. This is the id of the innermost enclosing -/// loop or function body. -/// -/// The role of `root_ub` is to prevent us from having to accumulate -/// vectors of rooted items at runtime. Consider this case: -/// -/// fn foo(...) -> int { -/// let mut ptr: ∫ -/// while some_cond { -/// let x: @int = ...; -/// ptr = &*x; -/// } -/// *ptr -/// } -/// -/// If we are not careful here, we would infer the scope of the borrow `&*x` -/// to be the body of the function `foo()` as a whole. We would then -/// have root each `@int` that is produced, which is an unbounded number. -/// No good. Instead what will happen is that `root_ub` will be set to the -/// body of the while loop and we will refuse to root the pointer `&*x` -/// because it would have to be rooted for a region greater than `root_ub`. -struct GatherLoanCtxt { - bccx: @BorrowckCtxt, - req_maps: ReqMaps, - item_ub: ast::node_id, - root_ub: ast::node_id, - ignore_adjustments: HashSet -} - -pub fn gather_loans(bccx: @BorrowckCtxt, crate: @ast::crate) -> ReqMaps { - let glcx = @mut GatherLoanCtxt { - bccx: bccx, - req_maps: ReqMaps { req_loan_map: HashMap::new(), - pure_map: HashMap::new() }, - item_ub: 0, - root_ub: 0, - ignore_adjustments: HashSet::new() - }; - let v = visit::mk_vt(@visit::Visitor {visit_expr: req_loans_in_expr, - visit_fn: req_loans_in_fn, - visit_stmt: add_stmt_to_map, - .. *visit::default_visitor()}); - visit::visit_crate(crate, glcx, v); - let @GatherLoanCtxt{req_maps, _} = glcx; - return req_maps; -} - -fn req_loans_in_fn(fk: &visit::fn_kind, - decl: &ast::fn_decl, - body: &ast::blk, - sp: span, - id: ast::node_id, - self: @mut GatherLoanCtxt, - v: visit::vt<@mut GatherLoanCtxt>) { - // see explanation attached to the `root_ub` field: - let old_item_id = self.item_ub; - let old_root_ub = self.root_ub; - self.root_ub = body.node.id; - - match *fk { - visit::fk_anon(*) | visit::fk_fn_block(*) => {} - visit::fk_item_fn(*) | visit::fk_method(*) | - visit::fk_dtor(*) => { - self.item_ub = body.node.id; - } - } - - visit::visit_fn(fk, decl, body, sp, id, self, v); - self.root_ub = old_root_ub; - self.item_ub = old_item_id; -} - -fn req_loans_in_expr(ex: @ast::expr, - self: @mut GatherLoanCtxt, - vt: visit::vt<@mut GatherLoanCtxt>) { - let bccx = self.bccx; - let tcx = bccx.tcx; - let old_root_ub = self.root_ub; - - debug!("req_loans_in_expr(expr=%?/%s)", - ex.id, pprust::expr_to_str(ex, tcx.sess.intr())); - - // If this expression is borrowed, have to ensure it remains valid: - { - let mut this = &mut *self; - if !this.ignore_adjustments.contains(&ex.id) { - for tcx.adjustments.find(&ex.id).each |&adjustments| { - this.guarantee_adjustments(ex, *adjustments); - } - } - } - - // Special checks for various kinds of expressions: - match ex.node { - ast::expr_addr_of(mutbl, base) => { - let base_cmt = self.bccx.cat_expr(base); - - // make sure that the thing we are pointing out stays valid - // for the lifetime `scope_r` of the resulting ptr: - let scope_r = ty_region(tcx, ex.span, tcx.ty(ex)); - self.guarantee_valid(base_cmt, mutbl, scope_r); - visit::visit_expr(ex, self, vt); - } - - ast::expr_match(ex_v, ref arms) => { - let cmt = self.bccx.cat_expr(ex_v); - for (*arms).each |arm| { - for arm.pats.each |pat| { - self.gather_pat(cmt, *pat, arm.body.node.id, ex.id); - } - } - visit::visit_expr(ex, self, vt); - } - - ast::expr_index(rcvr, _) | - ast::expr_binary(_, rcvr, _) | - ast::expr_unary(_, rcvr) | - ast::expr_assign_op(_, rcvr, _) - if self.bccx.method_map.contains_key(&ex.id) => { - // Receivers in method calls are always passed by ref. - // - // Here, in an overloaded operator, the call is this expression, - // and hence the scope of the borrow is this call. - // - // FIX? / NOT REALLY---technically we should check the other - // argument and consider the argument mode. But how annoying. - // And this problem when goes away when argument modes are - // phased out. So I elect to leave this undone. - let scope_r = ty::re_scope(ex.id); - let rcvr_cmt = self.bccx.cat_expr(rcvr); - self.guarantee_valid(rcvr_cmt, m_imm, scope_r); - - // FIXME (#3387): Total hack: Ignore adjustments for the left-hand - // side. Their regions will be inferred to be too large. - self.ignore_adjustments.insert(rcvr.id); - - visit::visit_expr(ex, self, vt); - } - - // FIXME--#3387 - // ast::expr_binary(_, lhs, rhs) => { - // // Universal comparison operators like ==, >=, etc - // // take their arguments by reference. - // let lhs_ty = ty::expr_ty(self.tcx(), lhs); - // if !ty::type_is_scalar(lhs_ty) { - // let scope_r = ty::re_scope(ex.id); - // let lhs_cmt = self.bccx.cat_expr(lhs); - // self.guarantee_valid(lhs_cmt, m_imm, scope_r); - // let rhs_cmt = self.bccx.cat_expr(rhs); - // self.guarantee_valid(rhs_cmt, m_imm, scope_r); - // } - // visit::visit_expr(ex, self, vt); - // } - - ast::expr_field(rcvr, _, _) - if self.bccx.method_map.contains_key(&ex.id) => { - // Receivers in method calls are always passed by ref. - // - // Here, the field a.b is in fact a closure. Eventually, this - // should be an &fn, but for now it's an @fn. In any case, - // the enclosing scope is either the call where it is a rcvr - // (if used like `a.b(...)`), the call where it's an argument - // (if used like `x(a.b)`), or the block (if used like `let x - // = a.b`). - let scope_r = self.tcx().region_maps.encl_region(ex.id); - let rcvr_cmt = self.bccx.cat_expr(rcvr); - self.guarantee_valid(rcvr_cmt, m_imm, scope_r); - visit::visit_expr(ex, self, vt); - } - - // see explanation attached to the `root_ub` field: - ast::expr_while(cond, ref body) => { - // during the condition, can only root for the condition - self.root_ub = cond.id; - (vt.visit_expr)(cond, self, vt); - - // during body, can only root for the body - self.root_ub = body.node.id; - (vt.visit_block)(body, self, vt); - } - - // see explanation attached to the `root_ub` field: - ast::expr_loop(ref body, _) => { - self.root_ub = body.node.id; - visit::visit_expr(ex, self, vt); - } - - _ => { - visit::visit_expr(ex, self, vt); - } - } - - // Check any contained expressions: - - self.root_ub = old_root_ub; -} - -pub impl GatherLoanCtxt { - fn tcx(&mut self) -> ty::ctxt { self.bccx.tcx } - - fn guarantee_adjustments(&mut self, - expr: @ast::expr, - adjustment: &ty::AutoAdjustment) { - debug!("guarantee_adjustments(expr=%s, adjustment=%?)", - expr.repr(self.tcx()), adjustment); - let _i = indenter(); - - match *adjustment { - ty::AutoAddEnv(*) => { - debug!("autoaddenv -- no autoref"); - return; - } - - ty::AutoDerefRef( - ty::AutoDerefRef { - autoref: None, _ }) => { - debug!("no autoref"); - return; - } - - ty::AutoDerefRef( - ty::AutoDerefRef { - autoref: Some(ref autoref), - autoderefs: autoderefs}) => { - let mcx = &mem_categorization_ctxt { - tcx: self.tcx(), - method_map: self.bccx.method_map}; - let cmt = mcx.cat_expr_autoderefd(expr, autoderefs); - debug!("after autoderef, cmt=%s", self.bccx.cmt_to_repr(cmt)); - - match autoref.kind { - ty::AutoPtr => { - self.guarantee_valid(cmt, - autoref.mutbl, - autoref.region) - } - ty::AutoBorrowVec | ty::AutoBorrowVecRef => { - let cmt_index = mcx.cat_index(expr, cmt); - self.guarantee_valid(cmt_index, - autoref.mutbl, - autoref.region) - } - ty::AutoBorrowFn => { - let cmt_deref = mcx.cat_deref_fn(expr, cmt, 0); - self.guarantee_valid(cmt_deref, - autoref.mutbl, - autoref.region) - } - } - } - } - } - - // 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_valid(&mut self, - cmt: cmt, - req_mutbl: ast::mutability, - scope_r: ty::Region) - { - - let loan_kind = match req_mutbl { - m_mutbl => TotalTake, - m_imm => TotalFreeze, - m_const => Immobile - }; - - self.bccx.stats.guaranteed_paths += 1; - - debug!("guarantee_valid(cmt=%s, req_mutbl=%?, \ - loan_kind=%?, scope_r=%s)", - self.bccx.cmt_to_repr(cmt), - req_mutbl, - loan_kind, - region_to_str(self.tcx(), scope_r)); - let _i = indenter(); - - match cmt.lp { - // If this expression is a loanable path, we MUST take out a - // loan. This is somewhat non-obvious. You might think, - // for example, that if we have an immutable local variable - // `x` whose value is being borrowed, we could rely on `x` - // not to change. This is not so, however, because even - // immutable locals can be moved. So we take out a loan on - // `x`, guaranteeing that it remains immutable for the - // duration of the reference: if there is an attempt to move - // it within that scope, the loan will be detected and an - // error will be reported. - Some(_) => { - match loan::loan(self.bccx, cmt, scope_r, loan_kind) { - Err(ref e) => { self.bccx.report((*e)); } - Ok(loans) => { - self.add_loans(cmt, loan_kind, scope_r, loans); - } - } - } - - // The path is not loanable: in that case, we must try and - // preserve it dynamically (or see that it is preserved by - // virtue of being rooted in some immutable path). We must - // also check that the mutability of the desired pointer - // matches with the actual mutability (but if an immutable - // pointer is desired, that is ok as long as we are pure) - None => { - let result: bckres = { - do self.check_mutbl(loan_kind, cmt).chain |pc1| { - do self.bccx.preserve(cmt, scope_r, - self.item_ub, - self.root_ub).chain |pc2| { - Ok(pc1.combine(pc2)) - } - } - }; - - match result { - Ok(PcOk) => { - debug!("result of preserve: PcOk"); - - // we were able guarantee the validity of the ptr, - // perhaps by rooting or because it is immutably - // rooted. good. - self.bccx.stats.stable_paths += 1; - } - Ok(PcIfPure(ref e)) => { - debug!("result of preserve: %?", PcIfPure((*e))); - - // we are only able to guarantee the validity if - // the scope is pure - match scope_r { - ty::re_scope(pure_id) => { - // if the scope is some block/expr in the - // fn, then just require that this scope - // be pure - self.req_maps.pure_map.insert(pure_id, *e); - self.bccx.stats.req_pure_paths += 1; - - debug!("requiring purity for scope %?", - scope_r); - - if self.tcx().sess.borrowck_note_pure() { - self.bccx.span_note( - cmt.span, - fmt!("purity required")); - } - } - _ => { - // otherwise, we can't enforce purity for - // that scope, so give up and report an - // error - self.bccx.report((*e)); - } - } - } - Err(ref e) => { - // we cannot guarantee the validity of this pointer - debug!("result of preserve: error"); - self.bccx.report((*e)); - } - } - } - } - } - - // Check that the pat `cmt` is compatible with the required - // mutability, presuming that it can be preserved to stay alive - // long enough. - // - // For example, if you have an expression like `&x.f` where `x` - // has type `@mut{f:int}`, this check might fail because `&x.f` - // reqires an immutable pointer, but `f` lives in (aliased) - // mutable memory. - fn check_mutbl(&mut self, - loan_kind: LoanKind, - cmt: cmt) - -> bckres { - debug!("check_mutbl(loan_kind=%?, cmt.mutbl=%?)", - loan_kind, cmt.mutbl); - - match loan_kind { - Immobile => Ok(PcOk), - - TotalTake | PartialTake => { - if cmt.mutbl.is_mutable() { - Ok(PcOk) - } else { - Err(bckerr { cmt: cmt, code: err_mutbl(loan_kind) }) - } - } - - TotalFreeze | PartialFreeze => { - if cmt.mutbl.is_immutable() { - Ok(PcOk) - } else if cmt.cat.is_mutable_box() { - Ok(PcOk) - } else { - // Eventually: - let e = bckerr {cmt: cmt, - code: err_mutbl(loan_kind)}; - Ok(PcIfPure(e)) - } - } - } - } - - fn add_loans(&mut self, - cmt: cmt, - loan_kind: LoanKind, - scope_r: ty::Region, - loans: ~[Loan]) { - if loans.len() == 0 { - return; - } - - // Normally we wouldn't allow `re_free` here. However, in this case - // it should be sound. Below is nmatsakis' reasoning: - // - // Perhaps [this permits] a function kind of like this one here, which - // consumes one mut pointer and returns a narrower one: - // - // struct Foo { f: int } - // fn foo(p: &'v mut Foo) -> &'v mut int { &mut p.f } - // - // I think this should work fine but there is more subtlety to it than - // I at first imagined. Unfortunately it's a very important use case, - // I think, so it really ought to work. The changes you [pcwalton] - // made to permit re_free() do permit this case, I think, but I'm not - // sure what else they permit. I have to think that over a bit. - // - // Ordinarily, a loan with scope re_free wouldn't make sense, because - // you couldn't enforce it. But in this case, your function signature - // informs the caller that you demand exclusive access to p and its - // contents for the lifetime v. Since borrowed pointers are - // non-copyable, they must have (a) made a borrow which will enforce - // those conditions and then (b) given you the resulting pointer. - // Therefore, they should be respecting the loan. So it actually seems - // that it's ok in this case to have a loan with re_free, so long as - // the scope of the loan is no greater than the region pointer on - // which it is based. Neat but not something I had previously - // considered all the way through. (Note that we already rely on - // similar reasoning to permit you to return borrowed pointers into - // immutable structures, this is just the converse I suppose) - - let scope_id = match scope_r { - ty::re_scope(scope_id) | - ty::re_free(ty::FreeRegion {scope_id, _}) => { - scope_id - } - _ => { - self.bccx.tcx.sess.span_bug( - cmt.span, - fmt!("loans required but scope is scope_region is %s \ - (%?)", - region_to_str(self.tcx(), scope_r), - scope_r)); - } - }; - - self.add_loans_to_scope_id(scope_id, loans); - - if loan_kind.is_freeze() && !cmt.mutbl.is_immutable() { - self.bccx.stats.loaned_paths_imm += 1; - - if self.tcx().sess.borrowck_note_loan() { - self.bccx.span_note( - cmt.span, - fmt!("immutable loan required")); - } - } else { - self.bccx.stats.loaned_paths_same += 1; - } - } - - fn add_loans_to_scope_id(&mut self, - scope_id: ast::node_id, - loans: ~[Loan]) { - debug!("adding %u loans to scope_id %?: %s", - loans.len(), scope_id, - str::connect(loans.map(|l| self.bccx.loan_to_repr(l)), ", ")); - match self.req_maps.req_loan_map.find(&scope_id) { - Some(req_loans) => { - req_loans.push_all(loans); - return; - } - None => {} - } - self.req_maps.req_loan_map.insert(scope_id, @mut loans); - } - - fn gather_pat(@mut self, - discr_cmt: cmt, - root_pat: @ast::pat, - arm_id: ast::node_id, - match_id: ast::node_id) { - do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| { - match pat.node { - ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => { - match bm { - ast::bind_by_ref(mutbl) => { - // ref x or ref x @ p --- creates a ptr which must - // remain valid for the scope of the match - - // find the region of the resulting pointer (note that - // the type of such a pattern will *always* be a - // region pointer) - let scope_r = ty_region(self.tcx(), pat.span, - self.tcx().ty(pat)); - - // if the scope of the region ptr turns out to be - // specific to this arm, wrap the categorization with - // a cat_discr() node. There is a detailed discussion - // of the function of this node in method preserve(): - let arm_scope = ty::re_scope(arm_id); - if self.bccx.is_subregion_of(scope_r, arm_scope) { - let cmt_discr = self.bccx.cat_discr(cmt, match_id); - self.guarantee_valid(cmt_discr, mutbl, scope_r); - } else { - self.guarantee_valid(cmt, mutbl, scope_r); - } - } - ast::bind_by_copy | ast::bind_infer => { - // Nothing to do here; neither copies nor moves induce - // borrows. - } - } - } - - ast::pat_vec(_, Some(slice_pat), _) => { - // The `slice_pat` here creates a slice into the - // original vector. This is effectively a borrow of - // the elements of the vector being matched. - - let slice_ty = self.tcx().ty(slice_pat); - 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); - self.guarantee_valid(cmt_index, slice_mutbl, slice_r); - } - - _ => {} - } - } - } - - fn vec_slice_info(@mut 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_evec(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, - fmt!("Type of slice pattern is not a slice")); - } - } - } - - fn pat_is_variant_or_struct(@mut self, pat: @ast::pat) -> bool { - pat_util::pat_is_variant_or_struct(self.bccx.tcx.def_map, pat) - } - - fn pat_is_binding(@mut self, pat: @ast::pat) -> bool { - pat_util::pat_is_binding(self.bccx.tcx.def_map, pat) - } -} - -// Setting up info that preserve needs. -// This is just the most convenient place to do it. -fn add_stmt_to_map(stmt: @ast::stmt, - self: @mut GatherLoanCtxt, - vt: visit::vt<@mut GatherLoanCtxt>) { - match stmt.node { - ast::stmt_expr(_, id) | ast::stmt_semi(_, id) => { - self.bccx.stmt_map.insert(id); - } - _ => () - } - visit::visit_stmt(stmt, self, vt); -} - diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs new file mode 100644 index 0000000000000..21d7e7041d959 --- /dev/null +++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs @@ -0,0 +1,322 @@ +// Copyright 2012 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. + +//! This module implements the check that the lifetime of a borrow +//! does not exceed the lifetime of the value being borrowed. + +use core::prelude::*; +use middle::borrowck::*; +use mc = middle::mem_categorization; +use middle::ty; +use syntax::ast::{m_const, m_imm, m_mutbl}; +use syntax::ast; +use syntax::codemap::span; + +pub fn guarantee_lifetime(bccx: @BorrowckCtxt, + item_scope_id: ast::node_id, + root_scope_id: ast::node_id, + span: span, + cmt: mc::cmt, + loan_region: ty::Region, + loan_mutbl: ast::mutability) { + debug!("guarantee_lifetime(cmt=%s, loan_region=%s)", + cmt.repr(bccx.tcx), loan_region.repr(bccx.tcx)); + let ctxt = GuaranteeLifetimeContext {bccx: bccx, + item_scope_id: item_scope_id, + span: span, + loan_region: loan_region, + loan_mutbl: loan_mutbl, + cmt_original: cmt, + root_scope_id: root_scope_id}; + ctxt.check(cmt, None); +} + +/////////////////////////////////////////////////////////////////////////// +// Private + +struct GuaranteeLifetimeContext { + bccx: @BorrowckCtxt, + + // the node id of the function body for the enclosing item + item_scope_id: ast::node_id, + + // the node id of the innermost loop / function body; this is the + // longest scope for which we can root managed boxes + root_scope_id: ast::node_id, + + span: span, + loan_region: ty::Region, + loan_mutbl: ast::mutability, + cmt_original: mc::cmt +} + +impl GuaranteeLifetimeContext { + fn tcx(&self) -> ty::ctxt { + self.bccx.tcx + } + + fn check(&self, cmt: mc::cmt, discr_scope: Option) { + //! Main routine. Walks down `cmt` until we find the "guarantor". + + match cmt.cat { + mc::cat_rvalue | + mc::cat_implicit_self | + mc::cat_copied_upvar(*) | + mc::cat_local(*) | + mc::cat_arg(*) | + mc::cat_self(*) | + mc::cat_deref(_, _, mc::region_ptr(*)) | + mc::cat_deref(_, _, mc::unsafe_ptr) => { + let scope = self.scope(cmt); + self.check_scope(scope) + } + + mc::cat_stack_upvar(cmt) => { + self.check(cmt, discr_scope) + } + + mc::cat_static_item => { + } + + mc::cat_deref(base, derefs, mc::gc_ptr(ptr_mutbl)) => { + let base_scope = self.scope(base); + + // See rule Freeze-Imm-Managed-Ptr-2 in doc.rs + let omit_root = ( + self.bccx.is_subregion_of(self.loan_region, base_scope) && + base.mutbl.is_immutable() && + !self.is_moved(base) + ); + + if !omit_root { + self.check_root(base, derefs, ptr_mutbl, discr_scope); + } else { + debug!("omitting root, base=%s, base_scope=%?", + base.repr(self.tcx()), base_scope); + } + } + + mc::cat_deref(base, _, mc::uniq_ptr(*)) | + mc::cat_interior(base, _) => { + self.check(base, discr_scope) + } + + mc::cat_discr(base, new_discr_scope) => { + // Subtle: in a match, we must ensure that each binding + // variable remains valid for the duration of the arm in + // which it appears, presuming that this arm is taken. + // But it is inconvenient in trans to root something just + // for one arm. Therefore, we insert a cat_discr(), + // basically a special kind of category that says "if this + // value must be dynamically rooted, root it for the scope + // `match_id`. + // + // As an example, consider this scenario: + // + // let mut x = @Some(3); + // match *x { Some(y) {...} None {...} } + // + // Technically, the value `x` need only be rooted + // in the `some` arm. However, we evaluate `x` in trans + // before we know what arm will be taken, so we just + // always root it for the duration of the match. + // + // As a second example, consider *this* scenario: + // + // let x = @mut @Some(3); + // match x { @@Some(y) {...} @@None {...} } + // + // Here again, `x` need only be rooted in the `some` arm. + // In this case, the value which needs to be rooted is + // found only when checking which pattern matches: but + // this check is done before entering the arm. Therefore, + // even in this case we just choose to keep the value + // rooted for the entire match. This means the value will be + // rooted even if the none arm is taken. Oh well. + // + // At first, I tried to optimize the second case to only + // root in one arm, but the result was suboptimal: first, + // it interfered with the construction of phi nodes in the + // arm, as we were adding code to root values before the + // phi nodes were added. This could have been addressed + // with a second basic block. However, the naive approach + // also yielded suboptimal results for patterns like: + // + // let x = @mut @...; + // match x { @@some_variant(y) | @@some_other_variant(y) => + // + // The reason is that we would root the value once for + // each pattern and not once per arm. This is also easily + // fixed, but it's yet more code for what is really quite + // the corner case. + // + // Nonetheless, if you decide to optimize this case in the + // future, you need only adjust where the cat_discr() + // node appears to draw the line between what will be rooted + // in the *arm* vs the *match*. + self.check(base, Some(new_discr_scope)) + } + } + } + + fn check_root(&self, + cmt_base: mc::cmt, + derefs: uint, + ptr_mutbl: ast::mutability, + discr_scope: Option) { + debug!("check_root(cmt_base=%s, derefs=%? ptr_mutbl=%?, \ + discr_scope=%?)", + cmt_base.repr(self.tcx()), + derefs, + ptr_mutbl, + discr_scope); + + // Make sure that the loan does not exceed the maximum time + // that we can root the value, dynamically. + let root_region = ty::re_scope(self.root_scope_id); + if !self.bccx.is_subregion_of(self.loan_region, root_region) { + self.report_error( + err_out_of_root_scope(root_region, self.loan_region)); + return; + } + + // Extract the scope id that indicates how long the rooting is required + let root_scope = match self.loan_region { + ty::re_scope(id) => id, + _ => { + // the check above should fail for anything is not re_scope + self.bccx.tcx.sess.span_bug( + cmt_base.span, + fmt!("Cannot issue root for scope region: %?", + self.loan_region)); + } + }; + + // If inside of a match arm, expand the rooting to the entire + // match. See the detailed discussion in `check()` above. + let mut root_scope = match discr_scope { + None => root_scope, + Some(id) => { + if self.bccx.is_subscope_of(root_scope, id) { + id + } else { + root_scope + } + } + }; + + // FIXME(#3511) grow to the nearest cleanup scope---this can + // cause observable errors if freezing! + if !self.bccx.tcx.region_maps.is_cleanup_scope(root_scope) { + debug!("%? is not a cleanup scope, adjusting", root_scope); + root_scope = self.bccx.tcx.region_maps.cleanup_scope(root_scope); + } + + // If we are borrowing the inside of an `@mut` box, + // we need to dynamically mark it to prevent incompatible + // borrows from happening later. + let opt_dyna = match ptr_mutbl { + m_imm | m_const => None, + m_mutbl => { + match self.loan_mutbl { + m_mutbl => Some(DynaMut), + m_imm | m_const => Some(DynaImm) + } + } + }; + + // Add a record of what is required + let rm_key = root_map_key {id: cmt_base.id, derefs: derefs}; + let root_info = RootInfo {scope: root_scope, freeze: opt_dyna}; + self.bccx.root_map.insert(rm_key, root_info); + + debug!("root_key: %? root_info: %?", rm_key, root_info); + } + + fn check_scope(&self, max_scope: ty::Region) { + //! Reports an error if `loan_region` is larger than `valid_scope` + + if !self.bccx.is_subregion_of(self.loan_region, max_scope) { + self.report_error(err_out_of_scope(max_scope, self.loan_region)); + } + } + + fn is_moved(&self, cmt: mc::cmt) -> bool { + //! True if `cmt` is something that is potentially moved + //! out of the current stack frame. + + match cmt.guarantor().cat { + mc::cat_local(id) | + mc::cat_self(id) | + mc::cat_arg(id, _) => { + self.bccx.moved_variables_set.contains(&id) + } + mc::cat_rvalue | + mc::cat_static_item | + mc::cat_implicit_self | + mc::cat_copied_upvar(*) | + mc::cat_deref(*) => { + false + } + r @ mc::cat_interior(*) | + r @ mc::cat_stack_upvar(*) | + r @ mc::cat_discr(*) => { + self.tcx().sess.span_bug( + cmt.span, + fmt!("illegal guarantor category: %?", r)); + } + } + } + + fn scope(&self, cmt: mc::cmt) -> ty::Region { + //! Returns the maximal region scope for the which the + //! lvalue `cmt` is guaranteed to be valid without any + //! rooting etc, and presuming `cmt` is not mutated. + + match cmt.cat { + mc::cat_rvalue => { + ty::re_scope(self.bccx.tcx.region_maps.cleanup_scope(cmt.id)) + } + mc::cat_implicit_self | + mc::cat_copied_upvar(_) => { + ty::re_scope(self.item_scope_id) + } + mc::cat_static_item => { + ty::re_static + } + mc::cat_local(local_id) | + mc::cat_arg(local_id, _) | + mc::cat_self(local_id) => { + self.bccx.tcx.region_maps.encl_region(local_id) + } + mc::cat_deref(_, _, mc::unsafe_ptr(*)) => { + ty::re_static + } + mc::cat_deref(_, _, mc::region_ptr(_, r)) => { + r + } + mc::cat_deref(cmt, _, mc::uniq_ptr(*)) | + mc::cat_deref(cmt, _, mc::gc_ptr(*)) | + mc::cat_interior(cmt, _) | + mc::cat_stack_upvar(cmt) | + mc::cat_discr(cmt, _) => { + self.scope(cmt) + } + } + } + + fn report_error(&self, code: bckerr_code) { + self.bccx.report(BckError { + cmt: self.cmt_original, + span: self.span, + code: code + }); + } +} diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs new file mode 100644 index 0000000000000..82638ceb4d4f1 --- /dev/null +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -0,0 +1,710 @@ +// Copyright 2012 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. + +// ---------------------------------------------------------------------- +// Gathering loans +// +// The borrow check proceeds in two phases. In phase one, we gather the full +// set of loans that are required at any point. These are sorted according to +// their associated scopes. In phase two, checking loans, we will then make +// sure that all of these loans are honored. + +use core::prelude::*; + +use middle::borrowck::*; +use mc = middle::mem_categorization; +use middle::pat_util; +use middle::ty::{ty_region}; +use middle::ty; +use util::common::indenter; +use util::ppaux::{Repr}; + +use core::hashmap::HashSet; +use core::vec; +use syntax::ast::{m_const, m_imm, m_mutbl}; +use syntax::ast; +use syntax::ast_util::id_range; +use syntax::codemap::span; +use syntax::print::pprust; +use syntax::visit; + +mod lifetime; +mod restrictions; + +/// Context used while gathering loans: +/// +/// - `bccx`: the the borrow check context +/// - `item_ub`: the id of the block for the enclosing fn/method item +/// - `root_ub`: the id of the outermost block for which we can root +/// an `@T`. This is the id of the innermost enclosing +/// loop or function body. +/// +/// The role of `root_ub` is to prevent us from having to accumulate +/// vectors of rooted items at runtime. Consider this case: +/// +/// fn foo(...) -> int { +/// let mut ptr: ∫ +/// while some_cond { +/// let x: @int = ...; +/// ptr = &*x; +/// } +/// *ptr +/// } +/// +/// If we are not careful here, we would infer the scope of the borrow `&*x` +/// to be the body of the function `foo()` as a whole. We would then +/// have root each `@int` that is produced, which is an unbounded number. +/// No good. Instead what will happen is that `root_ub` will be set to the +/// body of the while loop and we will refuse to root the pointer `&*x` +/// because it would have to be rooted for a region greater than `root_ub`. +struct GatherLoanCtxt { + bccx: @BorrowckCtxt, + id_range: id_range, + all_loans: @mut ~[Loan], + item_ub: ast::node_id, + repeating_ids: ~[ast::node_id], + ignore_adjustments: HashSet +} + +pub fn gather_loans(bccx: @BorrowckCtxt, + body: &ast::blk) -> (id_range, @mut ~[Loan]) { + let glcx = @mut GatherLoanCtxt { + bccx: bccx, + id_range: id_range::max(), + all_loans: @mut ~[], + item_ub: body.node.id, + repeating_ids: ~[body.node.id], + ignore_adjustments: HashSet::new() + }; + let v = visit::mk_vt(@visit::Visitor {visit_expr: gather_loans_in_expr, + visit_block: gather_loans_in_block, + visit_fn: gather_loans_in_fn, + visit_stmt: add_stmt_to_map, + visit_pat: add_pat_to_id_range, + .. *visit::default_visitor()}); + (v.visit_block)(body, glcx, v); + return (glcx.id_range, glcx.all_loans); +} + +fn add_pat_to_id_range(p: @ast::pat, + self: @mut GatherLoanCtxt, + v: visit::vt<@mut GatherLoanCtxt>) { + // NB: This visitor function just adds the pat ids into the id + // range. We gather loans that occur in patterns using the + // `gather_pat()` method below. Eventually these two should be + // brought together. + self.id_range.add(p.id); + visit::visit_pat(p, self, v); +} + +fn gather_loans_in_fn(fk: &visit::fn_kind, + decl: &ast::fn_decl, + body: &ast::blk, + sp: span, + id: ast::node_id, + self: @mut GatherLoanCtxt, + v: visit::vt<@mut GatherLoanCtxt>) { + match fk { + // Do not visit items here, the outer loop in borrowck/mod + // will visit them for us in turn. + &visit::fk_item_fn(*) | &visit::fk_method(*) | + &visit::fk_dtor(*) => { + return; + } + + // Visit closures as part of the containing item. + &visit::fk_anon(*) | &visit::fk_fn_block(*) => { + self.push_repeating_id(body.node.id); + visit::visit_fn(fk, decl, body, sp, id, self, v); + self.pop_repeating_id(body.node.id); + } + } +} + +fn gather_loans_in_block(blk: &ast::blk, + self: @mut GatherLoanCtxt, + vt: visit::vt<@mut GatherLoanCtxt>) { + self.id_range.add(blk.node.id); + visit::visit_block(blk, self, vt); +} + +fn gather_loans_in_expr(ex: @ast::expr, + self: @mut GatherLoanCtxt, + vt: visit::vt<@mut GatherLoanCtxt>) { + let bccx = self.bccx; + let tcx = bccx.tcx; + + debug!("gather_loans_in_expr(expr=%?/%s)", + ex.id, pprust::expr_to_str(ex, tcx.sess.intr())); + + self.id_range.add(ex.id); + self.id_range.add(ex.callee_id); + + // If this expression is borrowed, have to ensure it remains valid: + { + let mut this = &mut *self; // FIXME(#5074) + if !this.ignore_adjustments.contains(&ex.id) { + for tcx.adjustments.find(&ex.id).each |&adjustments| { + this.guarantee_adjustments(ex, *adjustments); + } + } + } + + // Special checks for various kinds of expressions: + match ex.node { + ast::expr_addr_of(mutbl, base) => { + let base_cmt = self.bccx.cat_expr(base); + + // make sure that the thing we are pointing out stays valid + // for the lifetime `scope_r` of the resulting ptr: + let scope_r = ty_region(tcx, ex.span, ty::expr_ty(tcx, ex)); + self.guarantee_valid(ex.id, ex.span, base_cmt, mutbl, scope_r); + visit::visit_expr(ex, self, vt); + } + + ast::expr_call(f, ref args, _) => { + let arg_tys = ty::ty_fn_args(ty::expr_ty(self.tcx(), f)); + self.guarantee_arguments(ex, *args, arg_tys); + visit::visit_expr(ex, self, vt); + } + + ast::expr_method_call(_, _, _, ref args, _) => { + let arg_tys = ty::ty_fn_args(ty::node_id_to_type(self.tcx(), + ex.callee_id)); + self.guarantee_arguments(ex, *args, arg_tys); + + visit::visit_expr(ex, self, vt); + } + + ast::expr_match(ex_v, ref arms) => { + let cmt = self.bccx.cat_expr(ex_v); + for arms.each |arm| { + for arm.pats.each |pat| { + self.gather_pat(cmt, *pat, arm.body.node.id, ex.id); + } + } + visit::visit_expr(ex, self, vt); + } + + ast::expr_index(rcvr, _) | + ast::expr_binary(_, rcvr, _) | + ast::expr_unary(_, rcvr) | + ast::expr_assign_op(_, rcvr, _) + if self.bccx.method_map.contains_key(&ex.id) => { + // Receivers in method calls are always passed by ref. + // + // Here, in an overloaded operator, the call is this expression, + // and hence the scope of the borrow is this call. + // + // FIX? / NOT REALLY---technically we should check the other + // argument and consider the argument mode. But how annoying. + // And this problem when goes away when argument modes are + // phased out. So I elect to leave this undone. + let scope_r = ty::re_scope(ex.id); + let rcvr_cmt = self.bccx.cat_expr(rcvr); + self.guarantee_valid(rcvr.id, rcvr.span, rcvr_cmt, m_imm, scope_r); + + // FIXME (#3387): Total hack: Ignore adjustments for the left-hand + // side. Their regions will be inferred to be too large. + self.ignore_adjustments.insert(rcvr.id); + + visit::visit_expr(ex, self, vt); + } + + // FIXME--#3387 + // ast::expr_binary(_, lhs, rhs) => { + // // Universal comparison operators like ==, >=, etc + // // take their arguments by reference. + // let lhs_ty = ty::expr_ty(self.tcx(), lhs); + // if !ty::type_is_scalar(lhs_ty) { + // let scope_r = ty::re_scope(ex.id); + // let lhs_cmt = self.bccx.cat_expr(lhs); + // self.guarantee_valid(lhs_cmt, m_imm, scope_r); + // let rhs_cmt = self.bccx.cat_expr(rhs); + // self.guarantee_valid(rhs_cmt, m_imm, scope_r); + // } + // visit::visit_expr(ex, self, vt); + // } + + // see explanation attached to the `root_ub` field: + ast::expr_while(cond, ref body) => { + // during the condition, can only root for the condition + self.push_repeating_id(cond.id); + (vt.visit_expr)(cond, self, vt); + self.pop_repeating_id(cond.id); + + // during body, can only root for the body + self.push_repeating_id(body.node.id); + (vt.visit_block)(body, self, vt); + self.pop_repeating_id(body.node.id); + } + + // see explanation attached to the `root_ub` field: + ast::expr_loop(ref body, _) => { + self.push_repeating_id(body.node.id); + visit::visit_expr(ex, self, vt); + self.pop_repeating_id(body.node.id); + } + + _ => { + visit::visit_expr(ex, self, vt); + } + } +} + +pub impl GatherLoanCtxt { + fn tcx(&self) -> ty::ctxt { self.bccx.tcx } + + fn push_repeating_id(&mut self, id: ast::node_id) { + self.repeating_ids.push(id); + } + + fn pop_repeating_id(&mut self, id: ast::node_id) { + let popped = self.repeating_ids.pop(); + assert!(id == popped); + } + + fn guarantee_arguments(&mut self, + call_expr: @ast::expr, + args: &[@ast::expr], + arg_tys: &[ty::arg]) { + for vec::each2(args, arg_tys) |arg, arg_ty| { + match ty::resolved_mode(self.tcx(), arg_ty.mode) { + ast::by_ref => { + self.guarantee_by_ref_argument(call_expr, *arg); + } + ast::by_copy => {} + } + } + } + + fn guarantee_by_ref_argument(&mut self, + call_expr: @ast::expr, + arg_expr: @ast::expr) { + // FIXME(#5074) nested method calls + let scope_r = ty::re_scope(call_expr.id); + let arg_cmt = self.bccx.cat_expr(arg_expr); + self.guarantee_valid(arg_expr.id, arg_expr.span, + arg_cmt, m_imm, scope_r); + } + + fn guarantee_adjustments(&mut self, + expr: @ast::expr, + adjustment: &ty::AutoAdjustment) { + debug!("guarantee_adjustments(expr=%s, adjustment=%?)", + expr.repr(self.tcx()), adjustment); + let _i = indenter(); + + match *adjustment { + ty::AutoAddEnv(*) => { + debug!("autoaddenv -- no autoref"); + return; + } + + ty::AutoDerefRef( + ty::AutoDerefRef { + autoref: None, _ }) => { + debug!("no autoref"); + return; + } + + ty::AutoDerefRef( + ty::AutoDerefRef { + autoref: Some(ref autoref), + autoderefs: autoderefs}) => { + let mcx = &mc::mem_categorization_ctxt { + tcx: self.tcx(), + method_map: self.bccx.method_map}; + let mut cmt = mcx.cat_expr_autoderefd(expr, autoderefs); + debug!("after autoderef, cmt=%s", cmt.repr(self.tcx())); + + match *autoref { + ty::AutoPtr(r, m) => { + self.guarantee_valid(expr.id, + expr.span, + cmt, + m, + r) + } + ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => { + let cmt_index = mcx.cat_index(expr, cmt); + self.guarantee_valid(expr.id, + expr.span, + cmt_index, + m, + r) + } + ty::AutoBorrowFn(r) => { + let cmt_deref = mcx.cat_deref_fn(expr, cmt, 0); + self.guarantee_valid(expr.id, + expr.span, + cmt_deref, + m_imm, + r) + } + ty::AutoUnsafe(_) => {} + } + } + } + } + + // 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_valid(&mut self, + borrow_id: ast::node_id, + borrow_span: span, + cmt: mc::cmt, + req_mutbl: ast::mutability, + loan_region: ty::Region) + { + debug!("guarantee_valid(borrow_id=%?, cmt=%s, \ + req_mutbl=%?, loan_region=%?)", + borrow_id, + cmt.repr(self.tcx()), + req_mutbl, + loan_region); + + // a loan for the empty region can never be dereferenced, so + // it is always safe + if loan_region == ty::re_empty { + return; + } + + let root_ub = { *self.repeating_ids.last() }; // FIXME(#5074) + + // Check that the lifetime of the borrow does not exceed + // the lifetime of the data being borrowed. + lifetime::guarantee_lifetime(self.bccx, self.item_ub, root_ub, + borrow_span, cmt, loan_region, req_mutbl); + + // Check that we don't allow mutable borrows of non-mutable data. + check_mutability(self.bccx, borrow_span, cmt, req_mutbl); + + // Compute the restrictions that are required to enforce the + // loan is safe. + let restr = restrictions::compute_restrictions( + self.bccx, borrow_span, + cmt, self.restriction_set(req_mutbl)); + + // Create the loan record (if needed). + let loan = match restr { + restrictions::Safe => { + // No restrictions---no loan record necessary + return; + } + + restrictions::SafeIf(loan_path, restrictions) => { + let loan_scope = match loan_region { + ty::re_scope(id) => id, + ty::re_free(ref fr) => fr.scope_id, + + ty::re_static => { + // If we get here, an error must have been + // reported in + // `lifetime::guarantee_lifetime()`, because + // the only legal ways to have a borrow with a + // static lifetime should not require + // restrictions. To avoid reporting derived + // errors, we just return here without adding + // any loans. + return; + } + + ty::re_empty | + ty::re_bound(*) | + ty::re_infer(*) => { + self.tcx().sess.span_bug( + cmt.span, + fmt!("Invalid borrow lifetime: %?", loan_region)); + } + }; + debug!("loan_scope = %?", loan_scope); + + let gen_scope = self.compute_gen_scope(borrow_id, loan_scope); + debug!("gen_scope = %?", gen_scope); + + let kill_scope = self.compute_kill_scope(loan_scope, loan_path); + debug!("kill_scope = %?", kill_scope); + + if req_mutbl == m_mutbl { + self.mark_loan_path_as_mutated(loan_path); + } + + let all_loans = &mut *self.all_loans; // FIXME(#5074) + Loan { + index: all_loans.len(), + loan_path: loan_path, + cmt: cmt, + mutbl: req_mutbl, + gen_scope: gen_scope, + kill_scope: kill_scope, + span: borrow_span, + restrictions: restrictions + } + } + }; + + debug!("guarantee_valid(borrow_id=%?), loan=%s", + borrow_id, loan.repr(self.tcx())); + + // let loan_path = loan.loan_path; + // let loan_gen_scope = loan.gen_scope; + // let loan_kill_scope = loan.kill_scope; + self.all_loans.push(loan); + + // if loan_gen_scope != borrow_id { + // NOTE handle case where gen_scope is not borrow_id + // + // Typically, the scope of the loan includes the point at + // which the loan is originated. This + // This is a subtle case. See the test case + // + // to see what we are guarding against. + + //let restr = restrictions::compute_restrictions( + // self.bccx, borrow_span, cmt, RESTR_EMPTY); + //let loan = { + // let all_loans = &mut *self.all_loans; // FIXME(#5074) + // Loan { + // index: all_loans.len(), + // loan_path: loan_path, + // cmt: cmt, + // mutbl: m_const, + // gen_scope: borrow_id, + // kill_scope: kill_scope, + // span: borrow_span, + // restrictions: restrictions + // } + // } + + fn check_mutability(bccx: @BorrowckCtxt, + borrow_span: span, + cmt: mc::cmt, + req_mutbl: ast::mutability) { + match req_mutbl { + m_const => { + // Data of any mutability can be lent as const. + } + + m_imm => { + match cmt.mutbl { + mc::McImmutable | mc::McDeclared | mc::McInherited => { + // both imm and mut data can be lent as imm; + // for mutable data, this is a freeze + } + mc::McReadOnly => { + bccx.report(BckError {span: borrow_span, + cmt: cmt, + code: err_mutbl(req_mutbl)}); + } + } + } + + m_mutbl => { + // Only mutable data can be lent as mutable. + if !cmt.mutbl.is_mutable() { + bccx.report(BckError {span: borrow_span, + cmt: cmt, + code: err_mutbl(req_mutbl)}); + } + } + } + } + } + + fn restriction_set(&self, req_mutbl: ast::mutability) -> RestrictionSet { + match req_mutbl { + m_const => RESTR_EMPTY, + m_imm => RESTR_EMPTY | RESTR_MUTATE, + m_mutbl => RESTR_EMPTY | RESTR_MUTATE | RESTR_FREEZE + } + } + + fn mark_loan_path_as_mutated(&self, loan_path: @LoanPath) { + //! For mutable loans of content whose mutability derives + //! from a local variable, mark the mutability decl as necessary. + + match *loan_path { + LpVar(local_id) => { + self.tcx().used_mut_nodes.insert(local_id); + } + LpExtend(base, mc::McInherited, _) => { + self.mark_loan_path_as_mutated(base); + } + LpExtend(_, mc::McDeclared, _) | + LpExtend(_, mc::McImmutable, _) | + LpExtend(_, mc::McReadOnly, _) => { + } + } + } + + fn compute_gen_scope(&self, + borrow_id: ast::node_id, + loan_scope: ast::node_id) -> ast::node_id { + //! Determine when to introduce the loan. Typically the loan + //! is introduced at the point of the borrow, but in some cases, + //! notably method arguments, the loan may be introduced only + //! later, once it comes into scope. + + let rm = self.bccx.tcx.region_maps; + if rm.is_subscope_of(borrow_id, loan_scope) { + borrow_id + } else { + loan_scope + } + } + + fn compute_kill_scope(&self, + loan_scope: ast::node_id, + lp: @LoanPath) -> ast::node_id { + //! Determine when the loan restrictions go out of scope. + //! This is either when the lifetime expires or when the + //! local variable which roots the loan-path goes out of scope, + //! whichever happens faster. + //! + //! It may seem surprising that we might have a loan region + //! larger than the variable which roots the loan-path; this can + //! come about when variables of `&mut` type are re-borrowed, + //! as in this example: + //! + //! fn counter<'a>(v: &'a mut Foo) -> &'a mut uint { + //! &mut v.counter + //! } + //! + //! In this case, the borrowed pointer (`'a`) outlives the + //! variable `v` that hosts it. Note that this doesn't come up + //! with immutable `&` pointers, because borrows of such pointers + //! do not require restrictions and hence do not cause a loan. + + let rm = self.bccx.tcx.region_maps; + let lexical_scope = rm.encl_scope(lp.node_id()); + if rm.is_subscope_of(lexical_scope, loan_scope) { + lexical_scope + } else { + assert!(rm.is_subscope_of(loan_scope, lexical_scope)); + loan_scope + } + } + + fn gather_pat(&mut self, + discr_cmt: mc::cmt, + root_pat: @ast::pat, + arm_body_id: ast::node_id, + match_id: ast::node_id) { + do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| { + match pat.node { + ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => { + match bm { + ast::bind_by_ref(mutbl) => { + // ref x or ref x @ p --- creates a ptr which must + // remain valid for the scope of the match + + // find the region of the resulting pointer (note that + // the type of such a pattern will *always* be a + // region pointer) + let scope_r = + ty_region(self.tcx(), pat.span, + ty::node_id_to_type(self.tcx(), pat.id)); + + // if the scope of the region ptr turns out to be + // specific to this arm, wrap the categorization + // with a cat_discr() node. There is a detailed + // discussion of the function of this node in + // `lifetime.rs`: + let arm_scope = ty::re_scope(arm_body_id); + if self.bccx.is_subregion_of(scope_r, arm_scope) { + let cmt_discr = self.bccx.cat_discr(cmt, match_id); + self.guarantee_valid(pat.id, pat.span, + cmt_discr, mutbl, scope_r); + } else { + self.guarantee_valid(pat.id, pat.span, + cmt, mutbl, scope_r); + } + } + ast::bind_by_copy | ast::bind_infer => { + // Nothing to do here; neither copies nor moves induce + // borrows. + } + } + } + + ast::pat_vec(_, Some(slice_pat), _) => { + // The `slice_pat` here creates a slice into the + // 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); + self.guarantee_valid(pat.id, pat.span, + cmt_index, slice_mutbl, slice_r); + } + + _ => {} + } + } + } + + 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_evec(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, + fmt!("Type of slice pattern is not a slice")); + } + } + } + + fn pat_is_variant_or_struct(&self, pat: @ast::pat) -> bool { + pat_util::pat_is_variant_or_struct(self.bccx.tcx.def_map, pat) + } + + fn pat_is_binding(&self, pat: @ast::pat) -> bool { + pat_util::pat_is_binding(self.bccx.tcx.def_map, pat) + } +} + +// Setting up info that preserve needs. +// This is just the most convenient place to do it. +fn add_stmt_to_map(stmt: @ast::stmt, + self: @mut GatherLoanCtxt, + vt: visit::vt<@mut GatherLoanCtxt>) { + match stmt.node { + ast::stmt_expr(_, id) | ast::stmt_semi(_, id) => { + self.bccx.stmt_map.insert(id); + } + _ => () + } + visit::visit_stmt(stmt, self, vt); +} diff --git a/src/librustc/middle/borrowck/gather_loans/restrictions.rs b/src/librustc/middle/borrowck/gather_loans/restrictions.rs new file mode 100644 index 0000000000000..950dbc58ec364 --- /dev/null +++ b/src/librustc/middle/borrowck/gather_loans/restrictions.rs @@ -0,0 +1,251 @@ +// Copyright 2012 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. + +//! Computes the restrictions that result from a borrow. + +use core::prelude::*; +use middle::borrowck::*; +use mc = middle::mem_categorization; +use middle::ty; +use syntax::ast::{m_const, m_imm, m_mutbl}; +use syntax::ast; +use syntax::codemap::span; + +pub enum RestrictionResult { + Safe, + SafeIf(@LoanPath, ~[Restriction]) +} + +pub fn compute_restrictions(bccx: @BorrowckCtxt, + span: span, + cmt: mc::cmt, + restr: RestrictionSet) -> RestrictionResult { + let ctxt = RestrictionsContext { + bccx: bccx, + span: span, + cmt_original: cmt + }; + + ctxt.compute(cmt, restr) +} + +/////////////////////////////////////////////////////////////////////////// +// Private + +struct RestrictionsContext { + bccx: @BorrowckCtxt, + span: span, + cmt_original: mc::cmt +} + +impl RestrictionsContext { + fn tcx(&self) -> ty::ctxt { + self.bccx.tcx + } + + fn compute(&self, + cmt: mc::cmt, + restrictions: RestrictionSet) -> RestrictionResult { + + // Check for those cases where we cannot control the aliasing + // and make sure that we are not being asked to. + match cmt.freely_aliasable() { + None => {} + Some(cause) => { + self.check_aliasing_permitted(cause, restrictions); + } + } + + match cmt.cat { + mc::cat_rvalue => { + // Effectively, rvalues are stored into a + // non-aliasable temporary on the stack. Since they + // are inherently non-aliasable, they can only be + // accessed later through the borrow itself and hence + // must inherently comply with its terms. + Safe + } + + mc::cat_local(local_id) | + mc::cat_arg(local_id, ast::by_copy) | + mc::cat_self(local_id) => { + let lp = @LpVar(local_id); + SafeIf(lp, ~[Restriction {loan_path: lp, + set: restrictions}]) + } + + mc::cat_interior(cmt_base, i @ mc::interior_variant(_)) => { + // When we borrow the interior of an enum, we have to + // ensure the enum itself is not mutated, because that + // could cause the type of the memory to change. + let result = self.compute(cmt_base, restrictions | RESTR_MUTATE); + self.extend(result, cmt.mutbl, LpInterior(i), restrictions) + } + + mc::cat_interior(cmt_base, i @ mc::interior_tuple) | + mc::cat_interior(cmt_base, i @ mc::interior_anon_field) | + mc::cat_interior(cmt_base, i @ mc::interior_field(*)) | + mc::cat_interior(cmt_base, i @ mc::interior_index(*)) => { + // For all of these cases, overwriting the base would + // not change the type of the memory, so no additional + // restrictions are needed. + // + // FIXME(#5397) --- Mut fields are not treated soundly + // (hopefully they will just get phased out) + let result = self.compute(cmt_base, restrictions); + self.extend(result, cmt.mutbl, LpInterior(i), restrictions) + } + + mc::cat_deref(cmt_base, _, mc::uniq_ptr(*)) => { + // When we borrow the interior of an owned pointer, we + // cannot permit the base to be mutated, because that + // would cause the unique pointer to be freed. + let result = self.compute(cmt_base, restrictions | RESTR_MUTATE); + self.extend(result, cmt.mutbl, LpDeref, restrictions) + } + + mc::cat_copied_upvar(*) | // FIXME(#2152) allow mutation of upvars + mc::cat_static_item(*) | + mc::cat_implicit_self(*) | + mc::cat_arg(_, ast::by_ref) | + mc::cat_deref(_, _, mc::region_ptr(m_imm, _)) | + mc::cat_deref(_, _, mc::gc_ptr(m_imm)) => { + Safe + } + + mc::cat_deref(_, _, mc::region_ptr(m_const, _)) | + mc::cat_deref(_, _, mc::gc_ptr(m_const)) => { + self.check_no_mutability_control(cmt, restrictions); + Safe + } + + mc::cat_deref(cmt_base, _, mc::gc_ptr(m_mutbl)) => { + // Technically, no restrictions are *necessary* here. + // The validity of the borrow is guaranteed + // dynamically. However, nonetheless we add a + // restriction to make a "best effort" to report + // static errors. For example, if there is code like + // + // let v = @mut ~[1, 2, 3]; + // for v.each |e| { + // v.push(e + 1); + // } + // + // Then the code below would add restrictions on `*v`, + // which means that an error would be reported + // here. This of course is not perfect. For example, + // a function like the following would not report an error + // at compile-time but would fail dynamically: + // + // let v = @mut ~[1, 2, 3]; + // let w = v; + // for v.each |e| { + // w.push(e + 1); + // } + // + // In addition, we only add a restriction for those cases + // where we can construct a sensible loan path, so an + // example like the following will fail dynamically: + // + // impl V { + // fn get_list(&self) -> @mut ~[int]; + // } + // ... + // let v: &V = ...; + // for v.get_list().each |e| { + // v.get_list().push(e + 1); + // } + match opt_loan_path(cmt_base) { + None => Safe, + Some(lp_base) => { + let lp = @LpExtend(lp_base, cmt.mutbl, LpDeref); + SafeIf(lp, ~[Restriction {loan_path: lp, + set: restrictions}]) + } + } + } + + mc::cat_deref(cmt_base, _, mc::region_ptr(m_mutbl, _)) => { + // Because an `&mut` pointer does not inherit its + // mutability, we can only prevent mutation or prevent + // freezing if it is not aliased. Therefore, in such + // cases we restrict aliasing on `cmt_base`. + if restrictions.intersects(RESTR_MUTATE | RESTR_FREEZE) { + let result = self.compute(cmt_base, restrictions | RESTR_ALIAS); + self.extend(result, cmt.mutbl, LpDeref, restrictions) + } else { + let result = self.compute(cmt_base, restrictions); + self.extend(result, cmt.mutbl, LpDeref, restrictions) + } + } + + mc::cat_deref(_, _, mc::unsafe_ptr) => { + // We are very trusting when working with unsafe pointers. + Safe + } + + mc::cat_stack_upvar(cmt_base) | + mc::cat_discr(cmt_base, _) => { + self.compute(cmt_base, restrictions) + } + } + } + + fn extend(&self, + result: RestrictionResult, + mc: mc::MutabilityCategory, + elem: LoanPathElem, + restrictions: RestrictionSet) -> RestrictionResult { + match result { + Safe => Safe, + SafeIf(base_lp, base_vec) => { + let lp = @LpExtend(base_lp, mc, elem); + SafeIf(lp, vec::append_one(base_vec, + Restriction {loan_path: lp, + set: restrictions})) + } + } + } + + fn check_aliasing_permitted(&self, + cause: mc::AliasableReason, + restrictions: RestrictionSet) { + //! This method is invoked when the current `cmt` is something + //! where aliasing cannot be controlled. It reports an error if + //! the restrictions required that it not be aliased; currently + //! this only occurs when re-borrowing an `&mut` pointer. + //! + //! NB: To be 100% consistent, we should report an error if + //! RESTR_FREEZE is found, because we cannot prevent freezing, + //! nor would we want to. However, we do not report such an + //! error, because this restriction only occurs when the user + //! is creating an `&mut` pointer to immutable or read-only + //! data, and there is already another piece of code that + //! checks for this condition. + + if restrictions.intersects(RESTR_ALIAS) { + self.bccx.report_aliasability_violation( + self.span, + BorrowViolation, + cause); + } + } + + fn check_no_mutability_control(&self, + cmt: mc::cmt, + restrictions: RestrictionSet) { + if restrictions.intersects(RESTR_MUTATE | RESTR_FREEZE) { + self.bccx.report(BckError {span: self.span, + cmt: cmt, + code: err_freeze_aliasable_const}); + } + } +} + diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs deleted file mode 100644 index 21de29b8f60ad..0000000000000 --- a/src/librustc/middle/borrowck/loan.rs +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2012 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. - -/*! - -The `Loan` module deals with borrows of *uniquely mutable* data. We -say that data is uniquely mutable if the current activation (stack -frame) controls the only mutable reference to the data. The most -common way that this can occur is if the current activation owns the -data being borrowed, but it can also occur with `&mut` pointers. The -primary characteristic of uniquely mutable data is that, at any given -time, there is at most one path that can be used to mutate it, and -that path is only accessible from the top stack frame. - -Given that some data found at a path P is being borrowed to a borrowed -pointer with mutability M and lifetime L, the job of the code in this -module is to compute the set of *loans* that are necessary to ensure -that (1) the data found at P outlives L and that (2) if M is mutable -then the path P will not be modified directly or indirectly except -through that pointer. A *loan* is the combination of a path P_L, a -mutability M_L, and a lifetime L_L where: - -- The path P_L indicates what data has been lent. -- The mutability M_L indicates the access rights on the data: - - const: the data cannot be moved - - immutable/mutable: the data cannot be moved or mutated -- The lifetime L_L indicates the *scope* of the loan. - -FIXME #4730 --- much more needed, don't have time to write this all up now - -*/ - -// ---------------------------------------------------------------------- -// Loan(Ex, M, S) = Ls holds if ToAddr(Ex) will remain valid for the entirety -// of the scope S, presuming that the returned set of loans `Ls` are honored. - -use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl}; -use middle::borrowck::{LoanKind, TotalFreeze, PartialFreeze, - TotalTake, PartialTake, Immobile}; -use middle::borrowck::{err_out_of_scope}; -use middle::mem_categorization::{cat_arg, cat_binding, cat_discr, cat_comp}; -use middle::mem_categorization::{cat_deref, cat_discr, cat_local, cat_self}; -use middle::mem_categorization::{cat_special, cat_stack_upvar, cmt}; -use middle::mem_categorization::{comp_field, comp_index, comp_variant}; -use middle::mem_categorization::{gc_ptr, region_ptr}; -use middle::ty; -use util::common::indenter; - -use syntax::ast::m_imm; -use syntax::ast; - -pub fn loan(bccx: @BorrowckCtxt, - cmt: cmt, - scope_region: ty::Region, - loan_kind: LoanKind) -> bckres<~[Loan]> -{ - let mut lc = LoanContext { - bccx: bccx, - scope_region: scope_region, - loans: ~[] - }; - match lc.loan(cmt, loan_kind, true) { - Err(ref e) => return Err((*e)), - Ok(()) => {} - } - // FIXME #4945: Workaround for borrow check bug. - Ok(copy lc.loans) -} - -struct LoanContext { - bccx: @BorrowckCtxt, - - // the region scope for which we must preserve the memory - scope_region: ty::Region, - - // accumulated list of loans that will be required - loans: ~[Loan] -} - -pub impl LoanContext { - fn tcx(&self) -> ty::ctxt { self.bccx.tcx } - - fn loan(&mut self, - cmt: cmt, - loan_kind: LoanKind, - owns_lent_data: bool) -> bckres<()> - { - /*! - * - * The main routine. - * - * # Parameters - * - * - `cmt`: the categorization of the data being borrowed - * - `req_mutbl`: the mutability of the borrowed pointer - * that was created - * - `owns_lent_data`: indicates whether `cmt` owns the - * data that is being lent. See - * discussion in `issue_loan()`. - */ - - debug!("loan(%s, %?)", - self.bccx.cmt_to_repr(cmt), - loan_kind); - let _i = indenter(); - - // see stable() above; should only be called when `cmt` is lendable - if cmt.lp.is_none() { - self.bccx.tcx.sess.span_bug( - cmt.span, - ~"loan() called with non-lendable value"); - } - - match cmt.cat { - cat_binding(_) | cat_rvalue | cat_special(_) => { - // should never be loanable - self.bccx.tcx.sess.span_bug( - cmt.span, - ~"rvalue with a non-none lp"); - } - cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => { - // FIXME(#4903) - let local_region = self.bccx.tcx.region_maps.encl_region(local_id); - self.issue_loan(cmt, local_region, loan_kind, - owns_lent_data) - } - cat_stack_upvar(cmt) => { - self.loan(cmt, loan_kind, owns_lent_data) - } - cat_discr(base, _) => { - self.loan(base, loan_kind, owns_lent_data) - } - cat_comp(cmt_base, comp_field(_, m)) | - cat_comp(cmt_base, comp_index(_, m)) => { - // For most components, the type of the embedded data is - // stable. Therefore, the base structure need only be - // const---unless the component must be immutable. In - // that case, it must also be embedded in an immutable - // location, or else the whole structure could be - // overwritten and the component along with it. - self.loan_stable_comp(cmt, cmt_base, loan_kind, m, - owns_lent_data) - } - cat_comp(cmt_base, comp_tuple) | - cat_comp(cmt_base, comp_anon_field) => { - // As above. - self.loan_stable_comp(cmt, cmt_base, loan_kind, m_imm, - owns_lent_data) - } - cat_comp(cmt_base, comp_variant(enum_did)) => { - // For enums, the memory is unstable if there are multiple - // variants, because if the enum value is overwritten then - // the memory changes type. - if ty::enum_is_univariant(self.bccx.tcx, enum_did) { - self.loan_stable_comp(cmt, cmt_base, loan_kind, m_imm, - owns_lent_data) - } else { - self.loan_unstable_deref(cmt, cmt_base, loan_kind, - owns_lent_data) - } - } - cat_deref(cmt_base, _, uniq_ptr) => { - // For unique pointers, the memory being pointed out is - // unstable because if the unique pointer is overwritten - // then the memory is freed. - self.loan_unstable_deref(cmt, cmt_base, loan_kind, - owns_lent_data) - } - cat_deref(cmt_base, _, region_ptr(ast::m_mutbl, region)) => { - // Mutable data can be loaned out as immutable or const. We must - // loan out the base as well as the main memory. For example, - // if someone borrows `*b`, we want to borrow `b` as immutable - // as well. - do self.loan(cmt_base, TotalFreeze, false).chain |_| { - self.issue_loan(cmt, region, loan_kind, owns_lent_data) - } - } - cat_deref(_, _, unsafe_ptr) | - cat_deref(_, _, gc_ptr(_)) | - cat_deref(_, _, region_ptr(_, _)) => { - // Aliased data is simply not lendable. - self.bccx.tcx.sess.span_bug( - cmt.span, - ~"aliased ptr with a non-none lp"); - } - } - } - - // A "stable component" is one where assigning the base of the - // component cannot cause the component itself to change types. - // Example: record fields. - fn loan_stable_comp(&mut self, - cmt: cmt, - cmt_base: cmt, - loan_kind: LoanKind, - comp_mutbl: ast::mutability, - owns_lent_data: bool) -> bckres<()> - { - let base_kind = match (comp_mutbl, loan_kind) { - // Declared as "immutable" means: inherited mutability and - // hence mutable iff parent is mutable. So propagate - // mutability on up. - (m_imm, TotalFreeze) | (m_imm, PartialFreeze) => PartialFreeze, - (m_imm, TotalTake) | (m_imm, PartialTake) => PartialTake, - - // Declared as "mutable" means: always mutable no matter - // what the mutability of the base is. So that means we - // can weaken the condition on the base to PartialFreeze. - // This implies that the user could freeze the base, but - // that is ok since the even with an &T base, the mut - // field will still be considered mutable. - (_, TotalTake) | (_, PartialTake) | - (_, TotalFreeze) | (_, PartialFreeze) => { - PartialFreeze - } - - // If we just need to guarantee the value won't be moved, - // it doesn't matter what mutability the component was - // declared with. - (_, Immobile) => Immobile, - }; - - do self.loan(cmt_base, base_kind, owns_lent_data).chain |_ok| { - // can use static for the scope because the base - // determines the lifetime, ultimately - self.issue_loan(cmt, ty::re_static, loan_kind, - owns_lent_data) - } - } - - // An "unstable deref" means a deref of a ptr/comp where, if the - // base of the deref is assigned to, pointers into the result of the - // deref would be invalidated. Examples: interior of variants, uniques. - fn loan_unstable_deref(&mut self, - cmt: cmt, - cmt_base: cmt, - loan_kind: LoanKind, - owns_lent_data: bool) -> bckres<()> { - // Variant components: the base must be immutable, because - // if it is overwritten, the types of the embedded data - // could change. - do self.loan(cmt_base, PartialFreeze, owns_lent_data).chain |_| { - // can use static, as in loan_stable_comp() - self.issue_loan(cmt, ty::re_static, loan_kind, - owns_lent_data) - } - } - - fn issue_loan(&mut self, - cmt: cmt, - scope_ub: ty::Region, - loan_kind: LoanKind, - owns_lent_data: bool) -> bckres<()> { - // Subtle: the `scope_ub` is the maximal lifetime of `cmt`. - // Therefore, if `cmt` owns the data being lent, then the - // scope of the loan must be less than `scope_ub`, or else the - // data would be freed while the loan is active. - // - // However, if `cmt` does *not* own the data being lent, then - // it is ok if `cmt` goes out of scope during the loan. This - // can occur when you have an `&mut` parameter that is being - // reborrowed. - - if !owns_lent_data || - self.bccx.is_subregion_of(self.scope_region, scope_ub) - { - if cmt.mutbl.is_mutable() { - // If this loan is a mutable loan, then mark the loan path (if - // it exists) as being used. This is similar to the check - // performed in check_loans.rs in check_assignment(), but this - // is for a different purpose of having the 'mut' qualifier. - for cmt.lp.each |lp| { - for lp.node_id().each |&id| { - self.tcx().used_mut_nodes.insert(id); - } - } - } else if loan_kind.is_take() { - // We do not allow non-mutable data to be "taken" - // under any circumstances. - return Err(bckerr { - cmt:cmt, - code:err_mutbl(loan_kind) - }); - } - - self.loans.push(Loan { - // Note: cmt.lp must be Some(_) because otherwise this - // loan process does not apply at all. - lp: cmt.lp.get(), - cmt: cmt, - kind: loan_kind - }); - - return Ok(()); - } else { - // The loan being requested lives longer than the data - // being loaned out! - return Err(bckerr { - cmt:cmt, - code:err_out_of_scope(scope_ub, self.scope_region) - }); - } - } -} - diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 3746f9c6e60b1..c108b020378eb 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -8,254 +8,64 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! -# Borrow check - -This pass is in job of enforcing *memory safety* and *purity*. As -memory safety is by far the more complex topic, I'll focus on that in -this description, but purity will be covered later on. In the context -of Rust, memory safety means three basic things: - -- no writes to immutable memory; -- all pointers point to non-freed memory; -- all pointers point to memory of the same type as the pointer. - -The last point might seem confusing: after all, for the most part, -this condition is guaranteed by the type check. However, there are -two cases where the type check effectively delegates to borrow check. - -The first case has to do with enums. If there is a pointer to the -interior of an enum, and the enum is in a mutable location (such as a -local variable or field declared to be mutable), it is possible that -the user will overwrite the enum with a new value of a different -variant, and thus effectively change the type of the memory that the -pointer is pointing at. +/*! See doc.rs for a thorough explanation of the borrow checker */ -The second case has to do with mutability. Basically, the type -checker has only a limited understanding of mutability. It will allow -(for example) the user to get an immutable pointer with the address of -a mutable local variable. It will also allow a `@mut T` or `~mut T` -pointer to be borrowed as a `&r.T` pointer. These seeming oversights -are in fact intentional; they allow the user to temporarily treat a -mutable value as immutable. It is up to the borrow check to guarantee -that the value in question is not in fact mutated during the lifetime -`r` of the reference. +use core; +use core::prelude::*; -# Definition of unstable memory - -The primary danger to safety arises due to *unstable memory*. -Unstable memory is memory whose validity or type may change as a -result of an assignment, move, or a variable going out of scope. -There are two cases in Rust where memory is unstable: the contents of -unique boxes and enums. - -Unique boxes are unstable because when the variable containing the -unique box is re-assigned, moves, or goes out of scope, the unique box -is freed or---in the case of a move---potentially given to another -task. In either case, if there is an extant and usable pointer into -the box, then safety guarantees would be compromised. - -Enum values are unstable because they are reassigned the types of -their contents may change if they are assigned with a different -variant than they had previously. - -# Safety criteria that must be enforced - -Whenever a piece of memory is borrowed for lifetime L, there are two -things which the borrow checker must guarantee. First, it must -guarantee that the memory address will remain allocated (and owned by -the current task) for the entirety of the lifetime L. Second, it must -guarantee that the type of the data will not change for the entirety -of the lifetime L. In exchange, the region-based type system will -guarantee that the pointer is not used outside the lifetime L. These -guarantees are to some extent independent but are also inter-related. - -In some cases, the type of a pointer cannot be invalidated but the -lifetime can. For example, imagine a pointer to the interior of -a shared box like: - - let mut x = @mut {f: 5, g: 6}; - let y = &mut x.f; - -Here, a pointer was created to the interior of a shared box which -contains a record. Even if `*x` were to be mutated like so: - - *x = {f: 6, g: 7}; - -This would cause `*y` to change from 5 to 6, but the pointer pointer -`y` remains valid. It still points at an integer even if that integer -has been overwritten. - -However, if we were to reassign `x` itself, like so: - - x = @{f: 6, g: 7}; - -This could potentially invalidate `y`, because if `x` were the final -reference to the shared box, then that memory would be released and -now `y` points at freed memory. (We will see that to prevent this -scenario we will *root* shared boxes that reside in mutable memory -whose contents are borrowed; rooting means that we create a temporary -to ensure that the box is not collected). - -In other cases, like an enum on the stack, the memory cannot be freed -but its type can change: - - let mut x = Some(5); - match x { - Some(ref y) => { ... } - None => { ... } - } - -Here as before, the pointer `y` would be invalidated if we were to -reassign `x` to `none`. (We will see that this case is prevented -because borrowck tracks data which resides on the stack and prevents -variables from reassigned if there may be pointers to their interior) - -Finally, in some cases, both dangers can arise. For example, something -like the following: - - let mut x = ~Some(5); - match x { - ~Some(ref y) => { ... } - ~None => { ... } - } - -In this case, if `x` to be reassigned or `*x` were to be mutated, then -the pointer `y` would be invalided. (This case is also prevented by -borrowck tracking data which is owned by the current stack frame) - -# Summary of the safety check - -In order to enforce mutability, the borrow check has a few tricks up -its sleeve: - -- When data is owned by the current stack frame, we can identify every - possible assignment to a local variable and simply prevent - potentially dangerous assignments directly. - -- If data is owned by a shared box, we can root the box to increase - its lifetime. - -- If data is found within a borrowed pointer, we can assume that the - data will remain live for the entirety of the borrowed pointer. - -- We can rely on the fact that pure actions (such as calling pure - functions) do not mutate data which is not owned by the current - stack frame. - -# Possible future directions - -There are numerous ways that the `borrowck` could be strengthened, but -these are the two most likely: - -- flow-sensitivity: we do not currently consider flow at all but only - block-scoping. This means that innocent code like the following is - rejected: - - let mut x: int; - ... - x = 5; - let y: &int = &x; // immutable ptr created - ... - - The reason is that the scope of the pointer `y` is the entire - enclosing block, and the assignment `x = 5` occurs within that - block. The analysis is not smart enough to see that `x = 5` always - happens before the immutable pointer is created. This is relatively - easy to fix and will surely be fixed at some point. - -- finer-grained purity checks: currently, our fallback for - guaranteeing random references into mutable, aliasable memory is to - require *total purity*. This is rather strong. We could use local - type-based alias analysis to distinguish writes that could not - possibly invalid the references which must be guaranteed. This - would only work within the function boundaries; function calls would - still require total purity. This seems less likely to be - implemented in the short term as it would make the code - significantly more complex; there is currently no code to analyze - the types and determine the possible impacts of a write. - -# How the code works - -The borrow check code is divided into several major modules, each of -which is documented in its own file. - -The `gather_loans` and `check_loans` are the two major passes of the -analysis. The `gather_loans` pass runs over the IR once to determine -what memory must remain valid and for how long. Its name is a bit of -a misnomer; it does in fact gather up the set of loans which are -granted, but it also determines when @T pointers must be rooted and -for which scopes purity must be required. - -The `check_loans` pass walks the IR and examines the loans and purity -requirements computed in `gather_loans`. It checks to ensure that (a) -the conditions of all loans are honored; (b) no contradictory loans -were granted (for example, loaning out the same memory as mutable and -immutable simultaneously); and (c) any purity requirements are -honored. - -The remaining modules are helper modules used by `gather_loans` and -`check_loans`: - -- `categorization` has the job of analyzing an expression to determine - what kind of memory is used in evaluating it (for example, where - dereferences occur and what kind of pointer is dereferenced; whether - the memory is mutable; etc) -- `loan` determines when data uniquely tied to the stack frame can be - loaned out. -- `preserve` determines what actions (if any) must be taken to preserve - aliasable data. This is the code which decides when to root - an @T pointer or to require purity. - -# Maps that are created - -Borrowck results in two maps. - -- `root_map`: identifies those expressions or patterns whose result - needs to be rooted. Conceptually the root_map maps from an - expression or pattern node to a `node_id` identifying the scope for - which the expression must be rooted (this `node_id` should identify - a block or call). The actual key to the map is not an expression id, - however, but a `root_map_key`, which combines an expression id with a - deref count and is used to cope with auto-deref. - -- `mutbl_map`: identifies those local variables which are modified or - moved. This is used by trans to guarantee that such variables are - given a memory location and not used as immediates. - */ - -use middle::mem_categorization::*; +use mc = middle::mem_categorization; use middle::ty; use middle::typeck; use middle::moves; +use middle::dataflow::DataFlowContext; +use middle::dataflow::DataFlowOperator; use util::common::stmt_set; -use util::ppaux::note_and_explain_region; +use util::ppaux::{note_and_explain_region, Repr}; use core::hashmap::{HashSet, HashMap}; -use core::to_bytes; -use syntax::ast::{mutability, m_imm}; +use core::io; +use core::result::{Result}; +use core::ops::{BitOr, BitAnd}; use syntax::ast; +use syntax::ast_map; +use syntax::visit; use syntax::codemap::span; +macro_rules! if_ok( + ($inp: expr) => ( + match $inp { + Ok(v) => { v } + Err(e) => { return Err(e); } + } + ) +) + +pub mod doc; + pub mod check_loans; + +#[path="gather_loans/mod.rs"] pub mod gather_loans; -pub mod loan; -pub mod preserve; + +pub struct LoanDataFlowOperator; +pub type LoanDataFlow = DataFlowContext; pub fn check_crate( tcx: ty::ctxt, method_map: typeck::method_map, moves_map: moves::MovesMap, + moved_variables_set: moves::MovedVariablesSet, capture_map: moves::CaptureMap, - crate: @ast::crate) -> (root_map, mutbl_map, write_guard_map) + crate: @ast::crate) -> (root_map, write_guard_map) { let bccx = @BorrowckCtxt { tcx: tcx, method_map: method_map, moves_map: moves_map, + moved_variables_set: moved_variables_set, capture_map: capture_map, root_map: root_map(), - mutbl_map: @mut HashSet::new(), + loan_map: @mut HashMap::new(), write_guard_map: @mut HashSet::new(), stmt_map: @mut HashSet::new(), stats: @mut BorrowStats { @@ -267,8 +77,9 @@ pub fn check_crate( } }; - let req_maps = gather_loans::gather_loans(bccx, crate); - check_loans::check_loans(bccx, req_maps, crate); + let v = visit::mk_vt(@visit::Visitor {visit_fn: borrowck_fn, + ..*visit::default_visitor()}); + visit::visit_crate(crate, bccx, v); if tcx.sess.borrowck_stats() { io::println(~"--- borrowck stats ---"); @@ -284,7 +95,7 @@ pub fn check_crate( make_stat(bccx, bccx.stats.req_pure_paths))); } - return (bccx.root_map, bccx.mutbl_map, bccx.write_guard_map); + return (bccx.root_map, bccx.write_guard_map); fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> ~str { let stat_f = stat as float; @@ -293,6 +104,46 @@ pub fn check_crate( } } +fn borrowck_fn(fk: &visit::fn_kind, + decl: &ast::fn_decl, + body: &ast::blk, + sp: span, + id: ast::node_id, + self: @BorrowckCtxt, + v: visit::vt<@BorrowckCtxt>) { + match fk { + &visit::fk_anon(*) | + &visit::fk_fn_block(*) => { + // Closures are checked as part of their containing fn item. + } + + &visit::fk_item_fn(*) | + &visit::fk_method(*) | + &visit::fk_dtor(*) => { + debug!("borrowck_fn(id=%?)", id); + + // Check the body of fn items. + let (id_range, all_loans) = + gather_loans::gather_loans(self, body); + let all_loans: &~[Loan] = &*all_loans; // FIXME(#5074) + let mut dfcx = + DataFlowContext::new(self.tcx, + self.method_map, + LoanDataFlowOperator, + id_range, + all_loans.len()); + for all_loans.eachi |loan_idx, loan| { + dfcx.add_gen(loan.gen_scope, loan_idx); + dfcx.add_kill(loan.kill_scope, loan_idx); + } + dfcx.propagate(body); + check_loans::check_loans(self, &dfcx, *all_loans, body); + } + } + + visit::visit_fn(fk, decl, body, sp, id, self, v); +} + // ---------------------------------------------------------------------- // Type definitions @@ -300,9 +151,10 @@ pub struct BorrowckCtxt { tcx: ty::ctxt, method_map: typeck::method_map, moves_map: moves::MovesMap, + moved_variables_set: moves::MovedVariablesSet, capture_map: moves::CaptureMap, root_map: root_map, - mutbl_map: mutbl_map, + loan_map: LoanMap, write_guard_map: write_guard_map, stmt_map: stmt_set, @@ -318,137 +170,228 @@ pub struct BorrowStats { guaranteed_paths: uint } -pub struct RootInfo { - scope: ast::node_id, - // This will be true if we need to freeze this box at runtime. This will - // result in a call to `borrow_as_imm()` and `return_to_mut()`. - freezes: bool // True if we need to freeze this box at runtime. -} - -// a map mapping id's of expressions of gc'd type (@T, @[], etc) where -// the box needs to be kept live to the id of the scope for which they -// must stay live. -pub type root_map = @mut HashMap; +pub type LoanMap = @mut HashMap; // the keys to the root map combine the `id` of the expression with -// the number of types that it is autodereferenced. So, for example, +// the number of types that it is autodereferenced. So, for example, // if you have an expression `x.f` and x has type ~@T, we could add an // entry {id:x, derefs:0} to refer to `x` itself, `{id:x, derefs:1}` // to refer to the deref of the unique pointer, and so on. -#[deriving(Eq)] +#[deriving(Eq, IterBytes)] pub struct root_map_key { id: ast::node_id, derefs: uint } -// set of ids of local vars / formal arguments that are modified / moved. -// this is used in trans for optimization purposes. -pub type mutbl_map = @mut HashSet; - // A set containing IDs of expressions of gc'd type that need to have a write // guard. pub type write_guard_map = @mut HashSet; -// Errors that can occur -#[deriving(Eq)] -pub enum bckerr_code { - err_mut_uniq, - err_mut_variant, - err_root_not_permitted, - err_mutbl(LoanKind), - err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope - err_out_of_scope(ty::Region, ty::Region) // superscope, subscope -} +pub type BckResult = Result; -// Combination of an error code and the categorization of the expression -// that caused it #[deriving(Eq)] -pub struct bckerr { - cmt: cmt, - code: bckerr_code +pub enum PartialTotal { + Partial, // Loan affects some portion + Total // Loan affects entire path } -pub enum MoveError { - MoveOk, - MoveFromIllegalCmt(cmt), - MoveWhileBorrowed(/*move*/ cmt, /*loan*/ cmt) +/////////////////////////////////////////////////////////////////////////// +// Loans and loan paths + +/// Record of a loan that was issued. +pub struct Loan { + index: uint, + loan_path: @LoanPath, + cmt: mc::cmt, + mutbl: ast::mutability, + restrictions: ~[Restriction], + gen_scope: ast::node_id, + kill_scope: ast::node_id, + span: span, } -// shorthand for something that fails with `bckerr` or succeeds with `T` -pub type bckres = Result; +#[deriving(Eq)] +pub enum LoanPath { + LpVar(ast::node_id), // `x` in doc.rs + LpExtend(@LoanPath, mc::MutabilityCategory, LoanPathElem) +} #[deriving(Eq)] -pub enum LoanKind { - TotalFreeze, // Entire path is frozen (borrowed as &T) - PartialFreeze, // Some subpath is frozen (borrowed as &T) - TotalTake, // Entire path is "taken" (borrowed as &mut T) - PartialTake, // Some subpath is "taken" (borrowed as &mut T) - Immobile // Path cannot be moved (borrowed as &const T) +pub enum LoanPathElem { + LpDeref, // `*LV` in doc.rs + LpInterior(mc::interior_kind) // `LV.f` in doc.rs } -/// a complete record of a loan that was granted -pub struct Loan { - lp: @loan_path, - cmt: cmt, - kind: LoanKind +pub impl LoanPath { + fn node_id(&self) -> ast::node_id { + match *self { + LpVar(local_id) => local_id, + LpExtend(base, _, _) => base.node_id() + } + } } -/// maps computed by `gather_loans` that are then used by `check_loans` -/// -/// - `req_loan_map`: map from each block/expr to the required loans needed -/// for the duration of that block/expr -/// - `pure_map`: map from block/expr that must be pure to the error message -/// that should be reported if they are not pure -pub struct ReqMaps { - req_loan_map: HashMap, - pure_map: HashMap +pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> { + //! Computes the `LoanPath` (if any) for a `cmt`. + //! Note that this logic is somewhat duplicated in + //! the method `compute()` found in `gather_loans::restrictions`, + //! which allows it to share common loan path pieces as it + //! traverses the CMT. + + match cmt.cat { + mc::cat_rvalue | + mc::cat_static_item | + mc::cat_copied_upvar(_) | + mc::cat_implicit_self | + mc::cat_arg(_, ast::by_ref) => { + None + } + + mc::cat_local(id) | + mc::cat_arg(id, ast::by_copy) | + mc::cat_self(id) => { + Some(@LpVar(id)) + } + + mc::cat_deref(cmt_base, _, _) => { + opt_loan_path(cmt_base).map( + |&lp| @LpExtend(lp, cmt.mutbl, LpDeref)) + } + + mc::cat_interior(cmt_base, ik) => { + opt_loan_path(cmt_base).map( + |&lp| @LpExtend(lp, cmt.mutbl, LpInterior(ik))) + } + + mc::cat_stack_upvar(cmt_base) | + mc::cat_discr(cmt_base, _) => { + opt_loan_path(cmt_base) + } + } } -pub fn save_and_restore(save_and_restore_t: &mut T, - f: &fn() -> U) -> U { - let old_save_and_restore_t = *save_and_restore_t; - let u = f(); - *save_and_restore_t = old_save_and_restore_t; - u +/////////////////////////////////////////////////////////////////////////// +// Restrictions +// +// 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 and mutable pointers to +// the value cannot be created. +// - `RESTR_FREEZE`: Immutable pointers to the value cannot be created. +// - `RESTR_ALIAS`: The lvalue may not be aliased in any way. +// +// In addition, no value which is restricted may be moved. Therefore, +// restrictions are meaningful even if the RestrictionSet is empty, +// because the restriction against moves is implied. + +pub struct Restriction { + loan_path: @LoanPath, + set: RestrictionSet } -pub fn save_and_restore_managed(save_and_restore_t: @mut T, - f: &fn() -> U) -> U { - let old_save_and_restore_t = *save_and_restore_t; - let u = f(); - *save_and_restore_t = old_save_and_restore_t; - u +pub struct RestrictionSet { + bits: u32 } -pub impl LoanKind { - fn is_freeze(&self) -> bool { - match *self { - TotalFreeze | PartialFreeze => true, - _ => false - } +pub static RESTR_EMPTY: RestrictionSet = RestrictionSet {bits: 0b000}; +pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b001}; +pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b010}; +pub static RESTR_ALIAS: RestrictionSet = RestrictionSet {bits: 0b100}; + +pub impl RestrictionSet { + fn intersects(&self, restr: RestrictionSet) -> bool { + (self.bits & restr.bits) != 0 } - fn is_take(&self) -> bool { - match *self { - TotalTake | PartialTake => true, - _ => false - } + fn contains_all(&self, restr: RestrictionSet) -> bool { + (self.bits & restr.bits) == restr.bits } } -/// Creates and returns a new root_map +impl BitOr for RestrictionSet { + fn bitor(&self, rhs: &RestrictionSet) -> RestrictionSet { + RestrictionSet {bits: self.bits | rhs.bits} + } +} -impl to_bytes::IterBytes for root_map_key { - fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) { - to_bytes::iter_bytes_2(&self.id, &self.derefs, lsb0, f); +impl BitAnd for RestrictionSet { + fn bitand(&self, rhs: &RestrictionSet) -> RestrictionSet { + RestrictionSet {bits: self.bits & rhs.bits} } } +/////////////////////////////////////////////////////////////////////////// +// Rooting of managed boxes +// +// When we borrow the interior of a managed box, it is sometimes +// necessary to *root* the box, meaning to stash a copy of the box +// somewhere that the garbage collector will find it. This ensures +// that the box is not collected for the lifetime of the borrow. +// +// As part of this rooting, we sometimes also freeze the box at +// runtime, meaning that we dynamically detect when the box is +// borrowed in incompatible ways. +// +// Both of these actions are driven through the `root_map`, which maps +// from a node to the dynamic rooting action that should be taken when +// that node executes. The node is identified through a +// `root_map_key`, which pairs a node-id and a deref count---the +// problem is that sometimes the box that needs to be rooted is only +// uncovered after a certain number of auto-derefs. + +pub struct RootInfo { + scope: ast::node_id, + freeze: Option // Some() if we should freeze box at runtime +} + +pub type root_map = @mut HashMap; + pub fn root_map() -> root_map { return @mut HashMap::new(); } -// ___________________________________________________________________________ +pub enum DynaFreezeKind { + DynaImm, + DynaMut +} + +impl ToStr for DynaFreezeKind { + fn to_str(&self) -> ~str { + match *self { + DynaMut => ~"mutable", + DynaImm => ~"immutable" + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Errors + +// Errors that can occur +#[deriving(Eq)] +pub enum bckerr_code { + err_mutbl(ast::mutability), + 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 +} + +// Combination of an error code and the categorization of the expression +// that caused it +#[deriving(Eq)] +pub struct BckError { + span: span, + cmt: mc::cmt, + code: bckerr_code +} + +pub enum AliasableViolationKind { + MutabilityViolation, + BorrowViolation +} + +/////////////////////////////////////////////////////////////////////////// // Misc pub impl BorrowckCtxt { @@ -456,27 +399,31 @@ pub impl BorrowckCtxt { self.tcx.region_maps.is_subregion_of(r_sub, r_sup) } - fn cat_expr(&self, expr: @ast::expr) -> cmt { - cat_expr(self.tcx, self.method_map, expr) + fn is_subscope_of(&self, r_sub: ast::node_id, r_sup: ast::node_id) -> bool { + self.tcx.region_maps.is_subscope_of(r_sub, r_sup) + } + + fn cat_expr(&self, expr: @ast::expr) -> mc::cmt { + mc::cat_expr(self.tcx, self.method_map, expr) } - fn cat_expr_unadjusted(&self, expr: @ast::expr) -> cmt { - cat_expr_unadjusted(self.tcx, self.method_map, expr) + fn cat_expr_unadjusted(&self, expr: @ast::expr) -> mc::cmt { + mc::cat_expr_unadjusted(self.tcx, self.method_map, expr) } fn cat_expr_autoderefd(&self, expr: @ast::expr, - adj: @ty::AutoAdjustment) -> cmt { + adj: @ty::AutoAdjustment) -> mc::cmt { match *adj { ty::AutoAddEnv(*) => { // no autoderefs - cat_expr_unadjusted(self.tcx, self.method_map, expr) + mc::cat_expr_unadjusted(self.tcx, self.method_map, expr) } ty::AutoDerefRef( ty::AutoDerefRef { autoderefs: autoderefs, _}) => { - cat_expr_autoderefd(self.tcx, self.method_map, expr, - autoderefs) + mc::cat_expr_autoderefd(self.tcx, self.method_map, expr, + autoderefs) } } } @@ -485,43 +432,33 @@ pub impl BorrowckCtxt { id: ast::node_id, span: span, ty: ty::t, - def: ast::def) -> cmt { - cat_def(self.tcx, self.method_map, id, span, ty, def) - } - - fn cat_variant(&self, - arg: N, - enum_did: ast::def_id, - cmt: cmt) -> cmt { - cat_variant(self.tcx, self.method_map, arg, enum_did, cmt) + def: ast::def) -> mc::cmt { + mc::cat_def(self.tcx, self.method_map, id, span, ty, def) } - fn cat_discr(&self, cmt: cmt, match_id: ast::node_id) -> cmt { - return @cmt_ {cat:cat_discr(cmt, match_id),.. *cmt}; + fn cat_discr(&self, cmt: mc::cmt, match_id: ast::node_id) -> mc::cmt { + @mc::cmt_ {cat:mc::cat_discr(cmt, match_id), + mutbl:cmt.mutbl.inherit(), + ..*cmt} } - fn mc_ctxt(&self) -> mem_categorization_ctxt { - mem_categorization_ctxt {tcx: self.tcx, + fn mc_ctxt(&self) -> mc::mem_categorization_ctxt { + mc::mem_categorization_ctxt {tcx: self.tcx, method_map: self.method_map} } - fn cat_pattern(&self, cmt: cmt, pat: @ast::pat, op: &fn(cmt, @ast::pat)) { + fn cat_pattern(&self, + cmt: mc::cmt, + pat: @ast::pat, + op: &fn(mc::cmt, @ast::pat)) { let mc = self.mc_ctxt(); mc.cat_pattern(cmt, pat, op); } - fn report_if_err(&self, bres: bckres<()>) { - match bres { - Ok(()) => (), - Err(ref e) => self.report((*e)) - } - } - - fn report(&self, err: bckerr) { + fn report(&self, err: BckError) { self.span_err( - err.cmt.span, - fmt!("illegal borrow: %s", - self.bckerr_to_str(err))); + err.span, + self.bckerr_to_str(err)); self.note_and_explain_bckerr(err); } @@ -533,51 +470,75 @@ pub impl BorrowckCtxt { self.tcx.sess.span_note(s, m); } - fn add_to_mutbl_map(&self, cmt: cmt) { - match cmt.cat { - cat_local(id) | cat_arg(id) => { - self.mutbl_map.insert(id); - } - cat_stack_upvar(cmt) => { - self.add_to_mutbl_map(cmt); - } - _ => () - } - } - - fn bckerr_to_str(&self, err: bckerr) -> ~str { + fn bckerr_to_str(&self, err: BckError) -> ~str { match err.code { err_mutbl(lk) => { - fmt!("creating %s alias to %s", - self.loan_kind_to_str(lk), - self.cmt_to_str(err.cmt)) + fmt!("cannot borrow %s %s as %s", + err.cmt.mutbl.to_user_str(), + self.cmt_to_str(err.cmt), + self.mut_to_str(lk)) } - err_mut_uniq => { - ~"unique value in aliasable, mutable location" + err_out_of_root_scope(*) => { + fmt!("cannot root managed value long enough") } - err_mut_variant => { - ~"enum variant in aliasable, mutable location" + err_out_of_scope(*) => { + fmt!("borrowed value does not live long enough") } - err_root_not_permitted => { - // note: I don't expect users to ever see this error - // message, reasons are discussed in attempt_root() in - // preserve.rs. - ~"rooting is not permitted" + 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. + fmt!("unsafe borrow of aliasable, const value") } - err_out_of_root_scope(*) => { - ~"cannot root managed value long enough" + } + } + + fn report_aliasability_violation(&self, + span: span, + kind: AliasableViolationKind, + cause: mc::AliasableReason) { + let prefix = match kind { + MutabilityViolation => "cannot assign to an `&mut`", + BorrowViolation => "cannot borrow an `&mut`" + }; + + match cause { + mc::AliasableOther => { + self.tcx.sess.span_err( + span, + fmt!("%s in an aliasable location", prefix)); } - err_out_of_scope(*) => { - ~"borrowed value does not live long enough" + mc::AliasableManaged(ast::m_mutbl) => { + // FIXME(#5074) we should prob do this borrow + self.tcx.sess.span_err( + span, + fmt!("%s in a `@mut` pointer; \ + try borrowing as `&mut` first", prefix)); + } + mc::AliasableManaged(m) => { + self.tcx.sess.span_err( + span, + fmt!("%s in a `@%s` pointer; \ + try an `@mut` instead", + prefix, + self.mut_to_keyword(m))); + } + mc::AliasableBorrowed(m) => { + self.tcx.sess.span_err( + span, + fmt!("%s in a `&%s` pointer; \ + try an `&mut` instead", + prefix, + self.mut_to_keyword(m))); } } } - fn note_and_explain_bckerr(&self, err: bckerr) { + fn note_and_explain_bckerr(&self, err: BckError) { let code = err.code; match code { - err_mutbl(*) | err_mut_uniq | err_mut_variant | - err_root_not_permitted => {} + err_mutbl(*) | err_freeze_aliasable_const(*) => {} err_out_of_root_scope(super_scope, sub_scope) => { note_and_explain_region( @@ -607,46 +568,140 @@ pub impl BorrowckCtxt { } } + fn append_loan_path_to_str_from_interior(&self, + loan_path: &LoanPath, + out: &mut ~str) { + match *loan_path { + LpExtend(_, _, LpDeref) => { + str::push_char(out, '('); + self.append_loan_path_to_str(loan_path, out); + str::push_char(out, ')'); + } + LpExtend(_, _, LpInterior(_)) | + LpVar(_) => { + self.append_loan_path_to_str(loan_path, out); + } + } + } + + 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::node_local(ident)) => { + str::push_str(out, *self.tcx.sess.intr().get(ident)); + } + r => { + self.tcx.sess.bug( + fmt!("Loan path LpVar(%?) maps to %?, not local", + id, r)); + } + } + } - fn cmt_to_str(&self, cmt: cmt) -> ~str { - let mc = &mem_categorization_ctxt {tcx: self.tcx, - method_map: self.method_map}; - mc.cmt_to_str(cmt) + LpExtend(lp_base, _, LpInterior(mc::interior_field(fld, _))) => { + self.append_loan_path_to_str_from_interior(lp_base, out); + str::push_char(out, '.'); + str::push_str(out, *self.tcx.sess.intr().get(fld)); + } + + LpExtend(lp_base, _, LpInterior(mc::interior_index(*))) => { + self.append_loan_path_to_str_from_interior(lp_base, out); + str::push_str(out, "[]"); + } + + LpExtend(lp_base, _, LpInterior(mc::interior_tuple)) | + LpExtend(lp_base, _, LpInterior(mc::interior_anon_field)) | + LpExtend(lp_base, _, LpInterior(mc::interior_variant(_))) => { + self.append_loan_path_to_str_from_interior(lp_base, out); + str::push_str(out, ".(tuple)"); + } + + LpExtend(lp_base, _, LpDeref) => { + str::push_char(out, '*'); + self.append_loan_path_to_str(lp_base, out); + } + } + } + + fn loan_path_to_str(&self, loan_path: &LoanPath) -> ~str { + let mut result = ~""; + self.append_loan_path_to_str(loan_path, &mut result); + result } - fn cmt_to_repr(&self, cmt: cmt) -> ~str { - let mc = &mem_categorization_ctxt {tcx: self.tcx, - method_map: self.method_map}; - mc.cmt_to_repr(cmt) + 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) } fn mut_to_str(&self, mutbl: ast::mutability) -> ~str { - let mc = &mem_categorization_ctxt {tcx: self.tcx, - method_map: self.method_map}; + let mc = &mc::mem_categorization_ctxt {tcx: self.tcx, + method_map: self.method_map}; mc.mut_to_str(mutbl) } - fn loan_kind_to_str(&self, lk: LoanKind) -> ~str { - match lk { - TotalFreeze | PartialFreeze => ~"immutable", - TotalTake | PartialTake => ~"mutable", - Immobile => ~"read-only" + fn mut_to_keyword(&self, mutbl: ast::mutability) -> &'static str { + match mutbl { + ast::m_imm => "", + ast::m_const => "const", + ast::m_mutbl => "mut" } } +} + +impl DataFlowOperator for LoanDataFlowOperator { + #[inline(always)] + fn initial_value(&self) -> bool { + false // no loans in scope by default + } + + #[inline(always)] + fn join(&self, succ: uint, pred: uint) -> uint { + succ | pred // loans from both preds are in scope + } + + #[inline(always)] + fn walk_closures(&self) -> bool { + true + } +} - fn loan_to_repr(&self, loan: &Loan) -> ~str { - fmt!("Loan(lp=%?, cmt=%s, kind=%?)", - loan.lp, self.cmt_to_repr(loan.cmt), loan.kind) +impl Repr for Loan { + fn repr(&self, tcx: ty::ctxt) -> ~str { + fmt!("Loan_%?(%s, %?, %?-%?, %s)", + self.index, + self.loan_path.repr(tcx), + self.mutbl, + self.gen_scope, + self.kill_scope, + self.restrictions.repr(tcx)) } } -// The inherent mutability of a component is its default mutability -// assuming it is embedded in an immutable context. In general, the -// mutability can be "overridden" if the component is embedded in a -// mutable structure. -pub fn inherent_mutability(ck: comp_kind) -> mutability { - match ck { - comp_tuple | comp_anon_field | comp_variant(_) => m_imm, - comp_field(_, m) | comp_index(_, m) => m +impl Repr for Restriction { + fn repr(&self, tcx: ty::ctxt) -> ~str { + fmt!("Restriction(%s, %x)", + self.loan_path.repr(tcx), + self.set.bits as uint) + } +} + +impl Repr for LoanPath { + fn repr(&self, tcx: ty::ctxt) -> ~str { + match self { + &LpVar(id) => { + fmt!("$(%?)", id) + } + + &LpExtend(lp, _, LpDeref) => { + fmt!("%s.*", lp.repr(tcx)) + } + + &LpExtend(lp, _, LpInterior(ref interior)) => { + fmt!("%s.%s", lp.repr(tcx), interior.repr(tcx)) + } + } } } diff --git a/src/librustc/middle/borrowck/preserve.rs b/src/librustc/middle/borrowck/preserve.rs deleted file mode 100644 index c44920fffa568..0000000000000 --- a/src/librustc/middle/borrowck/preserve.rs +++ /dev/null @@ -1,409 +0,0 @@ -// Copyright 2012 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. - -// ---------------------------------------------------------------------- -// Preserve(Ex, S) holds if ToAddr(Ex) will remain valid for the entirety of -// the scope S. -// - -use middle::borrowck::{RootInfo, bckerr, bckerr_code, bckres, BorrowckCtxt}; -use middle::borrowck::{err_mut_uniq, err_mut_variant}; -use middle::borrowck::{err_out_of_root_scope, err_out_of_scope}; -use middle::borrowck::{err_root_not_permitted, root_map_key}; -use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref}; -use middle::mem_categorization::{cat_discr, cat_local, cat_self, cat_special}; -use middle::mem_categorization::{cat_stack_upvar, cmt, comp_field}; -use middle::mem_categorization::{comp_index, comp_variant, gc_ptr}; -use middle::mem_categorization::{region_ptr}; -use middle::ty; -use util::common::indenter; - -use syntax::ast; - -pub enum PreserveCondition { - PcOk, - PcIfPure(bckerr) -} - -pub impl PreserveCondition { - // combines two preservation conditions such that if either of - // them requires purity, the result requires purity - fn combine(&self, pc: PreserveCondition) -> PreserveCondition { - match *self { - PcOk => {pc} - PcIfPure(_) => {*self} - } - } -} - -pub impl BorrowckCtxt { - fn preserve(&self, - cmt: cmt, - scope_region: ty::Region, - item_ub: ast::node_id, - root_ub: ast::node_id) -> bckres - { - let ctxt = PreserveCtxt { - bccx: self, - scope_region: scope_region, - item_ub: item_ub, - root_ub: root_ub, - root_managed_data: true - }; - ctxt.preserve(cmt) - } -} - -struct PreserveCtxt<'self> { - bccx: &'self BorrowckCtxt, - - // the region scope for which we must preserve the memory - scope_region: ty::Region, - - // the scope for the body of the enclosing fn/method item - item_ub: ast::node_id, - - // the upper bound on how long we can root an @T pointer - root_ub: ast::node_id, - - // if false, do not attempt to root managed data - root_managed_data: bool -} - -pub impl<'self> PreserveCtxt<'self> { - fn tcx(&self) -> ty::ctxt { self.bccx.tcx } - - fn preserve(&self, cmt: cmt) -> bckres { - debug!("preserve(cmt=%s, root_ub=%?, root_managed_data=%b)", - self.bccx.cmt_to_repr(cmt), self.root_ub, - self.root_managed_data); - let _i = indenter(); - - match cmt.cat { - cat_special(sk_implicit_self) | - cat_special(sk_heap_upvar) => { - self.compare_scope(cmt, ty::re_scope(self.item_ub)) - } - cat_special(sk_static_item) | cat_special(sk_method) => { - Ok(PcOk) - } - cat_rvalue => { - // when we borrow an rvalue, we can keep it rooted but only - // up to the root_ub point - - // When we're in a 'const &x = ...' context, self.root_ub is - // zero and the rvalue is static, not bound to a scope. - let scope_region = if self.root_ub == 0 { - ty::re_static - } else { - // Maybe if we pass in the parent instead here, - // we can prevent the "scope not found" error - debug!("scope_region thing: %? ", cmt.id); - self.tcx().region_maps.encl_region(cmt.id) - }; - - self.compare_scope(cmt, scope_region) - } - cat_stack_upvar(cmt) => { - self.preserve(cmt) - } - cat_local(local_id) => { - // Normally, local variables are lendable, and so this - // case should never trigger. However, if we are - // preserving an expression like a.b where the field `b` - // has @ type, then it will recurse to ensure that the `a` - // is stable to try and avoid rooting the value `a.b`. In - // this case, root_managed_data will be false. - if self.root_managed_data { - self.tcx().sess.span_bug( - cmt.span, - ~"preserve() called with local and !root_managed_data"); - } - let local_region = self.tcx().region_maps.encl_region(local_id); - self.compare_scope(cmt, local_region) - } - cat_binding(local_id) => { - // Bindings are these kind of weird implicit pointers (cc - // #2329). We require (in gather_loans) that they be - // rooted in an immutable location. - let local_region = self.tcx().region_maps.encl_region(local_id); - self.compare_scope(cmt, local_region) - } - cat_arg(local_id) => { - // This can happen as not all args are lendable (e.g., && - // modes). In that case, the caller guarantees stability - // for at least the scope of the fn. This is basically a - // deref of a region ptr. - let local_region = self.tcx().region_maps.encl_region(local_id); - self.compare_scope(cmt, local_region) - } - cat_self(local_id) => { - let local_region = self.tcx().region_maps.encl_region(local_id); - self.compare_scope(cmt, local_region) - } - cat_comp(cmt_base, comp_field(*)) | - cat_comp(cmt_base, comp_index(*)) | - cat_comp(cmt_base, comp_tuple) | - cat_comp(cmt_base, comp_anon_field) => { - // Most embedded components: if the base is stable, the - // type never changes. - self.preserve(cmt_base) - } - cat_comp(cmt_base, comp_variant(enum_did)) => { - if ty::enum_is_univariant(self.tcx(), enum_did) { - self.preserve(cmt_base) - } else { - // If there are multiple variants: overwriting the - // base could cause the type of this memory to change, - // so require imm. - self.require_imm(cmt, cmt_base, err_mut_variant) - } - } - cat_deref(cmt_base, _, uniq_ptr) => { - // Overwriting the base could cause this memory to be - // freed, so require imm. - self.require_imm(cmt, cmt_base, err_mut_uniq) - } - cat_deref(_, _, region_ptr(_, region)) => { - // References are always "stable" for lifetime `region` by - // induction (when the reference of type &MT was created, - // the memory must have been stable). - self.compare_scope(cmt, region) - } - cat_deref(_, _, unsafe_ptr) => { - // Unsafe pointers are the user's problem - Ok(PcOk) - } - cat_deref(base, derefs, gc_ptr(*)) => { - // GC'd pointers of type @MT: if this pointer lives in - // immutable, stable memory, then everything is fine. But - // otherwise we have no guarantee the pointer will stay - // live, so we must root the pointer (i.e., inc the ref - // count) for the duration of the loan. - debug!("base.mutbl = %?", base.mutbl); - if cmt.cat.derefs_through_mutable_box() { - self.attempt_root(cmt, base, derefs) - } else if base.mutbl.is_immutable() { - let non_rooting_ctxt = PreserveCtxt { - root_managed_data: false, - ..*self - }; - match non_rooting_ctxt.preserve(base) { - Ok(PcOk) => { - Ok(PcOk) - } - Ok(PcIfPure(_)) => { - debug!("must root @T, otherwise purity req'd"); - self.attempt_root(cmt, base, derefs) - } - Err(ref e) => { - debug!("must root @T, err: %s", - self.bccx.bckerr_to_str((*e))); - self.attempt_root(cmt, base, derefs) - } - } - } else { - self.attempt_root(cmt, base, derefs) - } - } - cat_discr(base, match_id) => { - // Subtle: in a match, we must ensure that each binding - // variable remains valid for the duration of the arm in - // which it appears, presuming that this arm is taken. - // But it is inconvenient in trans to root something just - // for one arm. Therefore, we insert a cat_discr(), - // basically a special kind of category that says "if this - // value must be dynamically rooted, root it for the scope - // `match_id`. - // - // As an example, consider this scenario: - // - // let mut x = @Some(3); - // match *x { Some(y) {...} None {...} } - // - // Technically, the value `x` need only be rooted - // in the `some` arm. However, we evaluate `x` in trans - // before we know what arm will be taken, so we just - // always root it for the duration of the match. - // - // As a second example, consider *this* scenario: - // - // let x = @mut @Some(3); - // match x { @@Some(y) {...} @@None {...} } - // - // Here again, `x` need only be rooted in the `some` arm. - // In this case, the value which needs to be rooted is - // found only when checking which pattern matches: but - // this check is done before entering the arm. Therefore, - // even in this case we just choose to keep the value - // rooted for the entire match. This means the value will be - // rooted even if the none arm is taken. Oh well. - // - // At first, I tried to optimize the second case to only - // root in one arm, but the result was suboptimal: first, - // it interfered with the construction of phi nodes in the - // arm, as we were adding code to root values before the - // phi nodes were added. This could have been addressed - // with a second basic block. However, the naive approach - // also yielded suboptimal results for patterns like: - // - // let x = @mut @...; - // match x { @@some_variant(y) | @@some_other_variant(y) => - // - // The reason is that we would root the value once for - // each pattern and not once per arm. This is also easily - // fixed, but it's yet more code for what is really quite - // the corner case. - // - // Nonetheless, if you decide to optimize this case in the - // future, you need only adjust where the cat_discr() - // node appears to draw the line between what will be rooted - // in the *arm* vs the *match*. - - let match_rooting_ctxt = PreserveCtxt { - scope_region: ty::re_scope(match_id), - ..*self - }; - match_rooting_ctxt.preserve(base) - } - } - } - - /// Reqiures that `cmt` (which is a deref or subcomponent of - /// `base`) be found in an immutable location (that is, `base` - /// must be immutable). Also requires that `base` itself is - /// preserved. - fn require_imm(&self, - cmt: cmt, - cmt_base: cmt, - code: bckerr_code) -> bckres { - // Variant contents and unique pointers: must be immutably - // rooted to a preserved address. - match self.preserve(cmt_base) { - // the base is preserved, but if we are not mutable then - // purity is required - Ok(PcOk) => { - if !cmt_base.mutbl.is_immutable() { - Ok(PcIfPure(bckerr {cmt:cmt, code:code})) - } else { - Ok(PcOk) - } - } - - // the base requires purity too, that's fine - Ok(PcIfPure(ref e)) => { - Ok(PcIfPure((*e))) - } - - // base is not stable, doesn't matter - Err(ref e) => { - Err((*e)) - } - } - } - - /// Checks that the scope for which the value must be preserved - /// is a subscope of `scope_ub`; if so, success. - fn compare_scope(&self, - cmt: cmt, - scope_ub: ty::Region) -> bckres { - if self.bccx.is_subregion_of(self.scope_region, scope_ub) { - Ok(PcOk) - } else { - Err(bckerr { - cmt:cmt, - code:err_out_of_scope(scope_ub, self.scope_region) - }) - } - } - - /// Here, `cmt=*base` is always a deref of managed data (if - /// `derefs` != 0, then an auto-deref). This routine determines - /// whether it is safe to MAKE cmt stable by rooting the pointer - /// `base`. We can only do the dynamic root if the desired - /// lifetime `self.scope_region` is a subset of `self.root_ub` - /// scope; otherwise, it would either require that we hold the - /// value live for longer than the current fn or else potentially - /// require that an statically unbounded number of values be - /// rooted (if a loop exists). - fn attempt_root(&self, cmt: cmt, base: cmt, - derefs: uint) -> bckres { - if !self.root_managed_data { - // normally, there is a root_ub; the only time that this - // is none is when a boxed value is stored in an immutable - // location. In that case, we will test to see if that - // immutable location itself can be preserved long enough - // in which case no rooting is necessary. But there it - // would be sort of pointless to avoid rooting the inner - // box by rooting an outer box, as it would just keep more - // memory live than necessary, so we set root_ub to none. - return Err(bckerr { cmt: cmt, code: err_root_not_permitted }); - } - - let root_region = ty::re_scope(self.root_ub); - match self.scope_region { - // we can only root values if the desired region is some concrete - // scope within the fn body - ty::re_scope(scope_id) => { - debug!("Considering root map entry for %s: \ - node %d:%u -> scope_id %?, root_ub %?", - self.bccx.cmt_to_repr(cmt), base.id, - derefs, scope_id, self.root_ub); - if self.bccx.is_subregion_of(self.scope_region, root_region) { - debug!("Elected to root"); - let rk = root_map_key { id: base.id, derefs: derefs }; - // This code could potentially lead cause boxes to be frozen - // for longer than necessarily at runtime. It prevents an - // ICE in trans; the fundamental problem is that it's hard - // to make sure trans and borrowck have the same notion of - // scope. The real fix is to clean up how trans handles - // cleanups, but that's hard. If this becomes an issue, it's - // an option to just change this to `let scope_to_use = - // scope_id;`. Though that would potentially re-introduce - // the ICE. See #3511 for more details. - let scope_to_use = if - self.bccx.stmt_map.contains(&scope_id) { - // Root it in its parent scope, b/c - // trans won't introduce a new scope for the - // stmt - self.root_ub - } - else { - // Use the more precise scope - scope_id - }; - // We freeze if and only if this is a *mutable* @ box that - // we're borrowing into a pointer. - self.bccx.root_map.insert(rk, RootInfo { - scope: scope_to_use, - freezes: cmt.cat.derefs_through_mutable_box() - }); - return Ok(PcOk); - } else { - debug!("Unable to root"); - return Err(bckerr { - cmt: cmt, - code: err_out_of_root_scope(root_region, - self.scope_region) - }); - } - } - - // we won't be able to root long enough - _ => { - return Err(bckerr { - cmt:cmt, - code:err_out_of_root_scope(root_region, self.scope_region) - }); - } - - } - } -} diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index bba4d35b56046..dea08eedb61cf 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -185,9 +185,7 @@ pub fn lookup_const_by_id(tcx: ty::ctxt, } } else { let maps = astencode::Maps { - mutbl_map: @mut HashSet::new(), root_map: @mut HashMap::new(), - last_use_map: @mut HashMap::new(), method_map: @mut HashMap::new(), vtable_map: @mut HashMap::new(), write_guard_map: @mut HashSet::new(), diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs new file mode 100644 index 0000000000000..cfdd7f95030aa --- /dev/null +++ b/src/librustc/middle/dataflow.rs @@ -0,0 +1,1009 @@ +// Copyright 2012 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. + + +/*! + * A module for propagating forward dataflow information. The analysis + * assumes that the items to be propagated can be represented as bits + * and thus uses bitvectors. Your job is simply to specify the so-called + * GEN and KILL bits for each expression. + */ + +use core::prelude::*; +use core::cast; +use core::uint; +use syntax::ast; +use syntax::ast_util; +use syntax::ast_util::id_range; +use syntax::print::{pp, pprust}; +use middle::ty; +use middle::typeck; +use util::ppaux::Repr; + +pub struct DataFlowContext { + priv tcx: ty::ctxt, + priv method_map: typeck::method_map, + + /// the data flow operator + priv oper: O, + + /// range of ids that appear within the item in question + priv id_range: id_range, + + /// number of bits to propagate per id + priv bits_per_id: uint, + + /// number of words we will use to store bits_per_id. + /// equal to bits_per_id/uint::bits rounded up. + priv words_per_id: uint, + + // Bit sets per id. The following three fields (`gens`, `kills`, + // and `on_entry`) all have the same structure. For each id in + // `id_range`, there is a range of words equal to `words_per_id`. + // So, to access the bits for any given id, you take a slice of + // the full vector (see the method `compute_id_range()`). + + /// bits generated as we exit the scope `id`. Updated by `add_gen()`. + priv gens: ~[uint], + + /// bits killed as we exit the scope `id`. Updated by `add_kill()`. + priv kills: ~[uint], + + /// bits that are valid on entry to the scope `id`. Updated by + /// `propagate()`. + priv on_entry: ~[uint] +} + +/// Parameterization for the precise form of data flow that is used. +pub trait DataFlowOperator { + /// Specifies the initial value for each bit in the `on_entry` set + fn initial_value(&self) -> bool; + + /// 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<'self, O> { + dfcx: &'self mut DataFlowContext, + changed: bool +} + +#[deriving(Eq)] +enum LoopKind { + /// A `while` or `loop` loop + TrueLoop, + + /// A `for` "loop" (i.e., really a func call where `break`, `return`, + /// and `loop` all essentially perform an early return from the closure) + ForLoop +} + +struct LoopScope<'self> { + loop_id: ast::node_id, + loop_kind: LoopKind, + break_bits: ~[uint] +} + +impl DataFlowContext { + pub fn new(tcx: ty::ctxt, + method_map: typeck::method_map, + oper: O, + id_range: id_range, + bits_per_id: uint) -> DataFlowContext { + let words_per_id = (bits_per_id + uint::bits - 1) / uint::bits; + + debug!("DataFlowContext::new(id_range=%?, bits_per_id=%?, words_per_id=%?)", + id_range, bits_per_id, words_per_id); + + let len = (id_range.max - id_range.min) as uint * words_per_id; + let gens = vec::from_elem(len, 0); + let kills = vec::from_elem(len, 0); + let elem = if oper.initial_value() {uint::max_value} else {0}; + let on_entry = vec::from_elem(len, elem); + + DataFlowContext { + tcx: tcx, + method_map: method_map, + words_per_id: words_per_id, + bits_per_id: bits_per_id, + oper: oper, + id_range: id_range, + gens: gens, + kills: kills, + on_entry: on_entry + } + } + + pub fn add_gen(&mut self, id: ast::node_id, bit: uint) { + //! Indicates that `id` generates `bit` + + debug!("add_gen(id=%?, bit=%?)", id, bit); + let (start, end) = self.compute_id_range(id); + { + let gens = vec::mut_slice(self.gens, start, end); + set_bit(gens, bit); + } + } + + pub fn add_kill(&mut self, id: ast::node_id, bit: uint) { + //! Indicates that `id` kills `bit` + + debug!("add_kill(id=%?, bit=%?)", id, bit); + let (start, end) = self.compute_id_range(id); + { + let kills = vec::mut_slice(self.kills, start, end); + set_bit(kills, bit); + } + } + + fn apply_gen_kill(&self, id: ast::node_id, bits: &mut [uint]) { + //! Applies the gen and kill sets for `id` to `bits` + + debug!("apply_gen_kill(id=%?, bits=%s) [before]", + id, mut_bits_to_str(bits)); + let (start, end) = self.compute_id_range(id); + let gens = self.gens.slice(start, end); + bitwise(bits, gens, |a, b| a | b); + let kills = self.kills.slice(start, end); + bitwise(bits, kills, |a, b| a & !b); + + debug!("apply_gen_kill(id=%?, bits=%s) [after]", + id, mut_bits_to_str(bits)); + } + + fn apply_kill(&self, id: ast::node_id, bits: &mut [uint]) { + debug!("apply_kill(id=%?, bits=%s) [before]", + id, mut_bits_to_str(bits)); + let (start, end) = self.compute_id_range(id); + let kills = self.kills.slice(start, end); + bitwise(bits, kills, |a, b| a & !b); + debug!("apply_kill(id=%?, bits=%s) [after]", + id, mut_bits_to_str(bits)); + } + + fn compute_id_range(&self, absolute_id: ast::node_id) -> (uint, uint) { + assert!(absolute_id >= self.id_range.min); + assert!(absolute_id < self.id_range.max); + + let relative_id = absolute_id - self.id_range.min; + let start = (relative_id as uint) * self.words_per_id; + let end = start + self.words_per_id; + (start, end) + } + + + pub fn each_bit_on_entry(&self, + id: ast::node_id, + f: &fn(uint) -> bool) { + //! Iterates through each bit that is set on entry to `id`. + //! Only useful after `propagate()` has been called. + + let (start, end) = self.compute_id_range(id); + let on_entry = vec::slice(self.on_entry, start, end); + debug!("each_bit_on_entry(id=%?, on_entry=%s)", + id, bits_to_str(on_entry)); + self.each_bit(on_entry, f); + } + + pub fn each_gen_bit(&self, + id: ast::node_id, + f: &fn(uint) -> bool) { + //! Iterates through each bit in the gen set for `id`. + + let (start, end) = self.compute_id_range(id); + let gens = vec::slice(self.gens, start, end); + debug!("each_gen_bit(id=%?, gens=%s)", + id, bits_to_str(gens)); + self.each_bit(gens, f) + } + + fn each_bit(&self, + words: &[uint], + f: &fn(uint) -> bool) { + //! Helper for iterating over the bits in a bit set. + + for words.eachi |word_index, &word| { + if word != 0 { + let base_index = word_index * uint::bits; + for uint::range(0, uint::bits) |offset| { + let bit = 1 << offset; + if (word & bit) != 0 { + // NB: we round up the total number of bits + // that we store in any given bit set so that + // it is an even multiple of uint::bits. This + // means that there may be some stray bits at + // the end that do not correspond to any + // actual value. So before we callback, check + // whether the bit_index is greater than the + // actual value the user specified and stop + // iterating if so. + let bit_index = base_index + offset; + if bit_index >= self.bits_per_id || !f(bit_index) { + return; + } + } + } + } + } + } +} + +impl DataFlowContext { +// ^^^^^^^^^^^^ only needed for pretty printing + pub fn propagate(&mut self, blk: &ast::blk) { + //! Performs the data flow analysis. + + if self.bits_per_id == 0 { + // Optimize the surprisingly common degenerate case. + return; + } + + let mut propcx = PropagationContext { + dfcx: self, + changed: true + }; + + let mut temp = vec::from_elem(self.words_per_id, 0); + let mut loop_scopes = ~[]; + + while propcx.changed { + propcx.changed = false; + propcx.reset(temp); + propcx.walk_block(blk, temp, &mut loop_scopes); + } + + debug!("Dataflow result:"); + debug!("%s", { + let this = @copy *self; + this.pretty_print_to(io::stderr(), blk); + "" + }); + } + + fn pretty_print_to(@self, wr: @io::Writer, blk: &ast::blk) { + let pre: @fn(pprust::ann_node) = |node| { + let (ps, id) = match node { + pprust::node_expr(ps, expr) => (ps, expr.id), + pprust::node_block(ps, blk) => (ps, blk.node.id), + pprust::node_item(ps, _) => (ps, 0), + pprust::node_pat(ps, pat) => (ps, pat.id) + }; + + if id >= self.id_range.min || id < self.id_range.max { + let (start, end) = self.compute_id_range(id); + let on_entry = vec::slice(self.on_entry, start, end); + let entry_str = bits_to_str(on_entry); + + let gens = vec::slice(self.gens, start, end); + let gens_str = if gens.any(|&u| u != 0) { + fmt!(" gen: %s", bits_to_str(gens)) + } else { + ~"" + }; + + let kills = vec::slice(self.kills, start, end); + let kills_str = if kills.any(|&u| u != 0) { + fmt!(" kill: %s", bits_to_str(kills)) + } else { + ~"" + }; + + let comment_str = fmt!("id %d: %s%s%s", + id, entry_str, gens_str, kills_str); + pprust::synth_comment(ps, comment_str); + pp::space(ps.s); + } + }; + + let post: @fn(pprust::ann_node) = |_| { + }; + + let ps = pprust::rust_printer_annotated( + wr, self.tcx.sess.intr(), + pprust::pp_ann {pre:pre, post:post}); + pprust::cbox(ps, pprust::indent_unit); + pprust::ibox(ps, 0u); + pprust::print_block(ps, blk); + pp::eof(ps.s); + } +} + +impl<'self, O:DataFlowOperator> PropagationContext<'self, O> { + fn tcx(&self) -> ty::ctxt { + self.dfcx.tcx + } + + fn walk_block(&mut self, + blk: &ast::blk, + in_out: &mut [uint], + loop_scopes: &mut ~[LoopScope]) { + debug!("DataFlowContext::walk_block(blk.node.id=%?, in_out=%s)", + blk.node.id, bits_to_str(reslice(in_out))); + + self.merge_with_entry_set(blk.node.id, in_out); + + for blk.node.stmts.each |&stmt| { + self.walk_stmt(stmt, in_out, loop_scopes); + } + + self.walk_opt_expr(blk.node.expr, in_out, loop_scopes); + + self.dfcx.apply_gen_kill(blk.node.id, in_out); + } + + fn walk_stmt(&mut self, + stmt: @ast::stmt, + in_out: &mut [uint], + loop_scopes: &mut ~[LoopScope]) { + match stmt.node { + ast::stmt_decl(decl, _) => { + self.walk_decl(decl, in_out, loop_scopes); + } + + ast::stmt_expr(expr, _) | ast::stmt_semi(expr, _) => { + self.walk_expr(expr, in_out, loop_scopes); + } + + ast::stmt_mac(*) => { + self.tcx().sess.span_bug(stmt.span, ~"unexpanded macro"); + } + } + } + + fn walk_decl(&mut self, + decl: @ast::decl, + in_out: &mut [uint], + loop_scopes: &mut ~[LoopScope]) { + match decl.node { + ast::decl_local(ref locals) => { + for locals.each |local| { + self.walk_pat(local.node.pat, in_out, loop_scopes); + self.walk_opt_expr(local.node.init, in_out, loop_scopes); + } + } + + ast::decl_item(_) => {} + } + } + + fn walk_expr(&mut self, + expr: @ast::expr, + in_out: &mut [uint], + loop_scopes: &mut ~[LoopScope]) { + debug!("DataFlowContext::walk_expr(expr=%s, in_out=%s)", + expr.repr(self.dfcx.tcx), bits_to_str(reslice(in_out))); + + self.merge_with_entry_set(expr.id, in_out); + + match expr.node { + ast::expr_fn_block(ref decl, ref 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_vec(); + + loop_scopes.push(LoopScope { + loop_id: expr.id, + loop_kind: ForLoop, + break_bits: reslice(in_out).to_vec() + }); + for decl.inputs.each |input| { + 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(); + 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::expr_if(cond, ref then, els) => { + // + // (cond) + // | + // v + // ( ) + // / \ + // | | + // v v + // (then)(els) + // | | + // v v + // ( succ ) + // + self.walk_expr(cond, in_out, loop_scopes); + + let mut then_bits = reslice(in_out).to_vec(); + self.walk_block(then, then_bits, loop_scopes); + + self.walk_opt_expr(els, in_out, loop_scopes); + join_bits(&self.dfcx.oper, then_bits, in_out); + } + + ast::expr_while(cond, ref blk) => { + // + // (expr) <--+ + // | | + // v | + // +--(cond) | + // | | | + // | v | + // v (blk) ----+ + // | + // <--+ (break) + // + + self.walk_expr(cond, in_out, loop_scopes); + + let mut body_bits = reslice(in_out).to_vec(); + loop_scopes.push(LoopScope { + loop_id: expr.id, + loop_kind: TrueLoop, + break_bits: reslice(in_out).to_vec() + }); + self.walk_block(blk, body_bits, loop_scopes); + self.add_to_entry_set(expr.id, body_bits); + let new_loop_scope = loop_scopes.pop(); + copy_bits(new_loop_scope.break_bits, in_out); + } + + ast::expr_loop(ref blk, _) => { + // + // (expr) <--+ + // | | + // v | + // (blk) ----+ + // | + // <--+ (break) + // + + let mut body_bits = reslice(in_out).to_vec(); + self.reset(in_out); + loop_scopes.push(LoopScope { + loop_id: expr.id, + loop_kind: TrueLoop, + break_bits: reslice(in_out).to_vec() + }); + self.walk_block(blk, body_bits, loop_scopes); + self.add_to_entry_set(expr.id, body_bits); + + let new_loop_scope = loop_scopes.pop(); + assert_eq!(new_loop_scope.loop_id, expr.id); + copy_bits(new_loop_scope.break_bits, in_out); + } + + ast::expr_match(discr, ref arms) => { + // + // (discr) + // / | \ + // | | | + // v v v + // (..arms..) + // | | | + // v v v + // ( succ ) + // + // + self.walk_expr(discr, in_out, loop_scopes); + + let mut guards = reslice(in_out).to_vec(); + + // We know that exactly one arm will be taken, so we + // can start out with a blank slate and just union + // together the bits from each arm: + self.reset(in_out); + + for arms.each |arm| { + // in_out reflects the discr and all guards to date + self.walk_opt_expr(arm.guard, guards, loop_scopes); + + // 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_vec(); + 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); + } + } + + ast::expr_ret(o_e) => { + self.walk_opt_expr(o_e, in_out, loop_scopes); + + // is this a return from a `for`-loop closure? + match loop_scopes.position(|s| s.loop_kind == ForLoop) { + Some(i) => { + // if so, add the in_out bits to the state + // upon exit. Remember that we cannot count + // upon the `for` loop function not to invoke + // the closure again etc. + self.break_from_to(expr, &mut loop_scopes[i], in_out); + } + + None => {} + } + + self.reset(in_out); + } + + ast::expr_break(label) => { + let scope = self.find_scope(expr, label, loop_scopes); + self.break_from_to(expr, scope, in_out); + self.reset(in_out); + } + + ast::expr_again(label) => { + let scope = self.find_scope(expr, label, loop_scopes); + + match scope.loop_kind { + TrueLoop => { + self.pop_scopes(expr, scope, in_out); + self.add_to_entry_set(scope.loop_id, reslice(in_out)); + } + + ForLoop => { + // If this `loop` construct is looping back to a `for` + // loop, then `loop` is really just a return from the + // closure. Therefore, we treat it the same as `break`. + // See case for `expr_fn_block` for more details. + self.break_from_to(expr, scope, in_out); + } + } + + self.reset(in_out); + } + + ast::expr_assign(l, r) | + ast::expr_assign_op(_, l, r) => { + self.walk_expr(r, in_out, loop_scopes); + self.walk_expr(l, in_out, loop_scopes); + } + + ast::expr_swap(l, r) => { + self.walk_expr(l, in_out, loop_scopes); + self.walk_expr(r, in_out, loop_scopes); + } + + ast::expr_vec(ref exprs, _) => { + self.walk_exprs(*exprs, in_out, loop_scopes) + } + + ast::expr_repeat(l, r, _) => { + self.walk_expr(l, in_out, loop_scopes); + self.walk_expr(r, in_out, loop_scopes); + } + + ast::expr_struct(_, ref fields, with_expr) => { + self.walk_opt_expr(with_expr, in_out, loop_scopes); + for fields.each |field| { + self.walk_expr(field.node.expr, in_out, loop_scopes); + } + } + + ast::expr_call(f, ref args, _) => { + self.walk_call(expr.callee_id, expr.id, + f, *args, in_out, loop_scopes); + } + + ast::expr_method_call(rcvr, _, _, ref args, _) => { + self.walk_call(expr.callee_id, expr.id, + rcvr, *args, in_out, loop_scopes); + } + + ast::expr_index(l, r) | + ast::expr_binary(_, l, r) if self.is_method_call(expr) => { + self.walk_call(expr.callee_id, expr.id, + l, [r], in_out, loop_scopes); + } + + ast::expr_unary(_, e) if self.is_method_call(expr) => { + self.walk_call(expr.callee_id, expr.id, + e, [], in_out, loop_scopes); + } + + ast::expr_tup(ref exprs) => { + self.walk_exprs(*exprs, in_out, loop_scopes); + } + + ast::expr_binary(op, l, r) if ast_util::lazy_binop(op) => { + self.walk_expr(l, in_out, loop_scopes); + let temp = reslice(in_out).to_vec(); + self.walk_expr(r, in_out, loop_scopes); + join_bits(&self.dfcx.oper, temp, in_out); + } + + ast::expr_log(l, r) | + ast::expr_index(l, r) | + ast::expr_binary(_, l, r) => { + self.walk_exprs([l, r], in_out, loop_scopes); + } + + ast::expr_lit(*) | + ast::expr_path(*) => { + } + + ast::expr_addr_of(_, e) | + ast::expr_copy(e) | + ast::expr_loop_body(e) | + ast::expr_do_body(e) | + ast::expr_cast(e, _) | + ast::expr_unary(_, e) | + ast::expr_paren(e) | + ast::expr_vstore(e, _) | + ast::expr_field(e, _, _) => { + self.walk_expr(e, in_out, loop_scopes); + } + + ast::expr_inline_asm(ref inline_asm) => { + for inline_asm.inputs.each |&(_, expr)| { + self.walk_expr(expr, in_out, loop_scopes); + } + for inline_asm.outputs.each |&(_, expr)| { + self.walk_expr(expr, in_out, loop_scopes); + } + } + + ast::expr_block(ref blk) => { + self.walk_block(blk, in_out, loop_scopes); + } + + ast::expr_mac(*) => { + self.tcx().sess.span_bug(expr.span, ~"unexpanded macro"); + } + } + + self.dfcx.apply_gen_kill(expr.id, in_out); + } + + fn pop_scopes(&mut self, + from_expr: @ast::expr, + to_scope: &mut LoopScope, + in_out: &mut [uint]) { + //! Whenever you have a `break` or a `loop` statement, flow + //! exits through any number of enclosing scopes on its + //! way to the new destination. This function applies the kill + //! sets of those enclosing scopes to `in_out` (those kill sets + //! concern items that are going out of scope). + + let tcx = self.tcx(); + let region_maps = tcx.region_maps; + + debug!("pop_scopes(from_expr=%s, to_scope=%?, in_out=%s)", + from_expr.repr(tcx), to_scope.loop_id, + bits_to_str(reslice(in_out))); + + let mut id = from_expr.id; + while id != to_scope.loop_id { + self.dfcx.apply_kill(id, in_out); + + match region_maps.opt_encl_scope(id) { + Some(i) => { id = i; } + None => { + tcx.sess.span_bug( + from_expr.span, + fmt!("pop_scopes(from_expr=%s, to_scope=%?) \ + to_scope does not enclose from_expr", + from_expr.repr(tcx), to_scope.loop_id)); + } + } + } + } + + fn break_from_to(&mut self, + from_expr: @ast::expr, + to_scope: &mut LoopScope, + 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=%s, to_scope=%?) final break_bits=%s", + from_expr.repr(self.tcx()), + to_scope.loop_id, + bits_to_str(reslice(in_out))); + } + + fn walk_exprs(&mut self, + exprs: &[@ast::expr], + in_out: &mut [uint], + loop_scopes: &mut ~[LoopScope]) { + for exprs.each |&expr| { + self.walk_expr(expr, in_out, loop_scopes); + } + } + + fn walk_opt_expr(&mut self, + opt_expr: Option<@ast::expr>, + in_out: &mut [uint], + loop_scopes: &mut ~[LoopScope]) { + for opt_expr.each |&expr| { + self.walk_expr(expr, in_out, loop_scopes); + } + } + + fn walk_call(&mut self, + _callee_id: ast::node_id, + call_id: ast::node_id, + arg0: @ast::expr, + args: &[@ast::expr], + in_out: &mut [uint], + loop_scopes: &mut ~[LoopScope]) { + self.walk_expr(arg0, in_out, loop_scopes); + self.walk_exprs(args, in_out, loop_scopes); + + // FIXME(#5074) nested method calls + // self.merge_with_entry_set(callee_id, in_out); + // self.dfcx.apply_gen_kill(callee_id, in_out); + + let return_ty = ty::node_id_to_type(self.tcx(), call_id); + let fails = ty::type_is_bot(return_ty); + if fails { + self.reset(in_out); + } + } + + fn walk_pat(&mut self, + pat: @ast::pat, + in_out: &mut [uint], + _loop_scopes: &mut ~[LoopScope]) { + debug!("DataFlowContext::walk_pat(pat=%s, in_out=%s)", + pat.repr(self.dfcx.tcx), bits_to_str(reslice(in_out))); + + do ast_util::walk_pat(pat) |p| { + debug!(" p.id=%? in_out=%s", p.id, bits_to_str(reslice(in_out))); + self.merge_with_entry_set(p.id, in_out); + self.dfcx.apply_gen_kill(p.id, in_out); + } + } + + fn walk_pat_alternatives(&mut self, + pats: &[@ast::pat], + in_out: &mut [uint], + loop_scopes: &mut ~[LoopScope]) { + if pats.len() == 1 { + // Common special case: + return self.walk_pat(pats[0], in_out, loop_scopes); + } + + // 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_vec(); + self.reset(in_out); + for pats.each |&pat| { + let mut temp = copy initial_state; + self.walk_pat(pat, in_out, loop_scopes); + join_bits(&self.dfcx.oper, temp, in_out); + } + } + + fn find_scope<'a>(&self, + expr: @ast::expr, + label: Option, + loop_scopes: &'a mut ~[LoopScope]) -> &'a mut LoopScope { + let index = match label { + None => { + let len = loop_scopes.len(); + len - 1 + } + + Some(_) => { + match self.tcx().def_map.find(&expr.id) { + Some(&ast::def_label(loop_id)) => { + match loop_scopes.position(|l| l.loop_id == loop_id) { + Some(i) => i, + None => { + self.tcx().sess.span_bug( + expr.span, + fmt!("No loop scope for id %?", loop_id)); + } + } + } + + r => { + self.tcx().sess.span_bug( + expr.span, + fmt!("Bad entry `%?` in def_map for label", r)); + } + } + } + }; + + &mut loop_scopes[index] + } + + fn is_method_call(&self, expr: @ast::expr) -> bool { + self.dfcx.method_map.contains_key(&expr.id) + } + + fn reset(&mut self, bits: &mut [uint]) { + let e = if self.dfcx.oper.initial_value() {uint::max_value} else {0}; + for vec::each_mut(bits) |b| { *b = e; } + } + + fn add_to_entry_set(&mut self, id: ast::node_id, pred_bits: &[uint]) { + debug!("add_to_entry_set(id=%?, pred_bits=%s)", + id, bits_to_str(pred_bits)); + let (start, end) = self.dfcx.compute_id_range(id); + let changed = { // FIXME(#5074) awkward construction + let on_entry = vec::mut_slice(self.dfcx.on_entry, start, end); + join_bits(&self.dfcx.oper, pred_bits, on_entry) + }; + if changed { + debug!("changed entry set for %? to %s", + id, bits_to_str(self.dfcx.on_entry.slice(start, end))); + self.changed = true; + } + } + + fn merge_with_entry_set(&mut self, + id: ast::node_id, + pred_bits: &mut [uint]) { + debug!("merge_with_entry_set(id=%?, pred_bits=%s)", + id, mut_bits_to_str(pred_bits)); + let (start, end) = self.dfcx.compute_id_range(id); + let changed = { // FIXME(#5074) awkward construction + let on_entry = vec::mut_slice(self.dfcx.on_entry, start, end); + let changed = join_bits(&self.dfcx.oper, reslice(pred_bits), on_entry); + copy_bits(reslice(on_entry), pred_bits); + changed + }; + if changed { + debug!("changed entry set for %? to %s", + id, bits_to_str(self.dfcx.on_entry.slice(start, end))); + self.changed = true; + } + } +} + +fn mut_bits_to_str(words: &mut [uint]) -> ~str { + bits_to_str(reslice(words)) +} + +fn bits_to_str(words: &[uint]) -> ~str { + let mut result = ~""; + let mut sep = '['; + + // Note: this is a little endian printout of bytes. + + for words.each |&word| { + let mut v = word; + for uint::range(0, uint::bytes) |_| { + str::push_char(&mut result, sep); + str::push_str(&mut result, fmt!("%02x", v & 0xFF)); + v >>= 8; + sep = '-'; + } + } + str::push_char(&mut result, ']'); + return result; +} + +fn copy_bits(in_vec: &[uint], out_vec: &mut [uint]) -> bool { + bitwise(out_vec, in_vec, |_, b| b) +} + +fn join_bits(oper: &O, + in_vec: &[uint], + out_vec: &mut [uint]) -> bool { + bitwise(out_vec, in_vec, |a, b| oper.join(a, b)) +} + +#[inline(always)] +fn bitwise(out_vec: &mut [uint], + in_vec: &[uint], + op: &fn(uint, uint) -> uint) -> bool { + assert_eq!(out_vec.len(), in_vec.len()); + let mut changed = false; + for uint::range(0, out_vec.len()) |i| { + let old_val = out_vec[i]; + let new_val = op(old_val, in_vec[i]); + out_vec[i] = new_val; + changed |= (old_val != new_val); + } + return changed; +} + +fn set_bit(words: &mut [uint], bit: uint) -> bool { + debug!("set_bit: words=%s bit=%s", + mut_bits_to_str(words), bit_str(bit)); + let word = bit / uint::bits; + let bit_in_word = bit % uint::bits; + let bit_mask = 1 << bit_in_word; + debug!("word=%u bit_in_word=%u bit_mask=%u", word, bit_in_word, word); + let oldv = words[word]; + let newv = oldv | bit_mask; + words[word] = newv; + oldv != newv +} + +fn bit_str(bit: uint) -> ~str { + let byte = bit >> 8; + let lobits = 1 << (bit & 0xFF); + fmt!("[%u:%u-%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) + } +} + diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index cf488b0ac8939..199eb274ab9e1 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -10,7 +10,6 @@ use middle::freevars::freevar_entry; use middle::freevars; -use middle::liveness; use middle::pat_util; use middle::ty; use middle::typeck; @@ -56,19 +55,16 @@ pub static try_adding: &'static str = "Try adding a move"; pub struct Context { tcx: ty::ctxt, method_map: typeck::method_map, - last_use_map: liveness::last_use_map, - current_item: node_id, + current_item: node_id } pub fn check_crate(tcx: ty::ctxt, method_map: typeck::method_map, - last_use_map: liveness::last_use_map, crate: @crate) { let ctx = Context { tcx: tcx, method_map: method_map, - last_use_map: last_use_map, - current_item: -1, + current_item: -1 }; let visit = visit::mk_vt(@visit::Visitor { visit_arm: check_arm, diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 2de12b9eb9746..784db49a0fd62 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -252,10 +252,9 @@ pub impl LanguageItems { } } -fn LanguageItemCollector<'r>(crate: @crate, - session: Session, - items: &'r mut LanguageItems) - -> LanguageItemCollector<'r> { +fn LanguageItemCollector(crate: @crate, + session: Session) + -> LanguageItemCollector { let mut item_refs = HashMap::new(); item_refs.insert(@~"const", ConstTraitLangItem as uint); @@ -303,13 +302,13 @@ fn LanguageItemCollector<'r>(crate: @crate, LanguageItemCollector { crate: crate, session: session, - items: items, + items: LanguageItems::new(), item_refs: item_refs } } -struct LanguageItemCollector<'self> { - items: &'self mut LanguageItems, +struct LanguageItemCollector { + items: LanguageItems, crate: @crate, session: Session, @@ -317,8 +316,8 @@ struct LanguageItemCollector<'self> { item_refs: HashMap<@~str, uint>, } -pub impl<'self> LanguageItemCollector<'self> { - fn match_and_collect_meta_item(&self, item_def_id: def_id, +pub impl LanguageItemCollector { + fn match_and_collect_meta_item(&mut self, item_def_id: def_id, meta_item: @meta_item) { match meta_item.node { meta_name_value(key, literal) => { @@ -333,7 +332,7 @@ pub impl<'self> LanguageItemCollector<'self> { } } - fn collect_item(&self, item_index: uint, item_def_id: def_id) { + fn collect_item(&mut self, item_index: uint, item_def_id: def_id) { // Check for duplicates. match self.items.items[item_index] { Some(original_def_id) if original_def_id != item_def_id => { @@ -349,34 +348,37 @@ pub impl<'self> LanguageItemCollector<'self> { self.items.items[item_index] = Some(item_def_id); } - fn match_and_collect_item(&self, + fn match_and_collect_item(&mut self, item_def_id: def_id, key: @~str, value: @~str) { if *key != ~"lang" { return; // Didn't match. } - match self.item_refs.find(&value) { + let item_index = self.item_refs.find(&value).map(|x| **x); + // prevent borrow checker from considering ^~~~~~~~~~~ + // self to be borrowed (annoying) + + match item_index { + Some(item_index) => { + self.collect_item(item_index, item_def_id); + } None => { // Didn't match. - } - Some(&item_index) => { - self.collect_item(item_index, item_def_id) + return; } } } - fn collect_local_language_items(&self) { - unsafe { - let this: *LanguageItemCollector<'self> = transmute(self); - visit_crate(self.crate, (), mk_simple_visitor(@SimpleVisitor { - visit_item: |item| { - for item.attrs.each |attribute| { - unsafe { - (*this).match_and_collect_meta_item( - local_def(item.id), - attribute.node.value - ); - } + fn collect_local_language_items(&mut self) { + let this = ptr::addr_of(&self); + visit_crate(self.crate, (), mk_simple_visitor(@SimpleVisitor { + visit_item: |item| { + for item.attrs.each |attribute| { + unsafe { + (*this).match_and_collect_meta_item( + local_def(item.id), + attribute.node.value + ); } }, .. *default_simple_visitor() @@ -384,7 +386,7 @@ pub impl<'self> LanguageItemCollector<'self> { } } - fn collect_external_language_items(&self) { + fn collect_external_language_items(&mut self) { let crate_store = self.session.cstore; do iter_crate_data(crate_store) |crate_number, _crate_metadata| { for each_lang_item(crate_store, crate_number) @@ -408,7 +410,7 @@ pub impl<'self> LanguageItemCollector<'self> { } } - fn collect(&self) { + fn collect(&mut self) { self.collect_local_language_items(); self.collect_external_language_items(); self.check_completeness(); @@ -418,9 +420,9 @@ pub impl<'self> LanguageItemCollector<'self> { pub fn collect_language_items(crate: @crate, session: Session) -> LanguageItems { - let mut items = LanguageItems::new(); - let collector = LanguageItemCollector(crate, session, &mut items); + let mut collector = LanguageItemCollector(crate, session); collector.collect(); - copy items + let LanguageItemCollector { items, _ } = collector; + items } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 94d82d0acb8e4..2e2c92abcdc78 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -121,16 +121,6 @@ use syntax::visit::{fk_anon, fk_dtor, fk_fn_block, fk_item_fn, fk_method}; use syntax::visit::{vt}; use syntax::{visit, ast_util}; -// Maps from an expr id to a list of variable ids for which this expr -// is the last use. Typically, the expr is a path and the node id is -// the local/argument/etc that the path refers to. However, it also -// possible for the expr to be a closure, in which case the list is a -// list of closed over variables that can be moved into the closure. -// -// Very subtle (#2633): borrowck will remove entries from this table -// if it detects an outstanding loan (that is, the addr is taken). -pub type last_use_map = @mut HashMap; - #[deriving(Eq)] struct Variable(uint); #[deriving(Eq)] @@ -158,7 +148,7 @@ pub fn check_crate(tcx: ty::ctxt, method_map: typeck::method_map, variable_moves_map: moves::VariableMovesMap, capture_map: moves::CaptureMap, - crate: @crate) -> last_use_map { + crate: @crate) { let visitor = visit::mk_vt(@visit::Visitor { visit_fn: visit_fn, visit_local: visit_local, @@ -168,16 +158,13 @@ pub fn check_crate(tcx: ty::ctxt, .. *visit::default_visitor() }); - let last_use_map = @mut HashMap::new(); let initial_maps = @mut IrMaps(tcx, method_map, variable_moves_map, capture_map, - last_use_map, 0); visit::visit_crate(crate, initial_maps, visitor); tcx.sess.abort_if_errors(); - return last_use_map; } impl to_str::ToStr for LiveNode { @@ -241,23 +228,11 @@ enum VarKind { ImplicitRet } -fn relevant_def(def: def) -> Option { - match def { - def_binding(nid, _) | - def_arg(nid, _) | - def_local(nid, _) | - def_self(nid, _) => Some(nid), - - _ => None - } -} - struct IrMaps { tcx: ty::ctxt, method_map: typeck::method_map, variable_moves_map: moves::VariableMovesMap, capture_map: moves::CaptureMap, - last_use_map: last_use_map, num_live_nodes: uint, num_vars: uint, @@ -274,7 +249,6 @@ fn IrMaps(tcx: ty::ctxt, method_map: typeck::method_map, variable_moves_map: moves::VariableMovesMap, capture_map: moves::CaptureMap, - last_use_map: last_use_map, cur_item: node_id) -> IrMaps { IrMaps { @@ -282,7 +256,6 @@ fn IrMaps(tcx: ty::ctxt, method_map: method_map, variable_moves_map: variable_moves_map, capture_map: capture_map, - last_use_map: last_use_map, num_live_nodes: 0, num_vars: 0, live_node_map: HashMap::new(), @@ -367,29 +340,6 @@ pub impl IrMaps { fn lnk(&mut self, ln: LiveNode) -> LiveNodeKind { self.lnks[*ln] } - - fn add_last_use(&mut self, expr_id: node_id, var: Variable) { - let vk = self.var_kinds[*var]; - debug!("Node %d is a last use of variable %?", expr_id, vk); - match vk { - Arg(id, _) | - Local(LocalInfo { id: id, kind: FromLetNoInitializer, _ }) | - Local(LocalInfo { id: id, kind: FromLetWithInitializer, _ }) | - Local(LocalInfo { id: id, kind: FromMatch(_), _ }) => { - let v = match self.last_use_map.find(&expr_id) { - Some(&v) => v, - None => { - let v = @mut ~[]; - self.last_use_map.insert(expr_id, v); - v - } - }; - - v.push(id); - } - ImplicitRet => debug!("--but it is not owned"), - } - } } fn visit_item(item: @item, self: @mut IrMaps, v: vt<@mut IrMaps>) { @@ -413,7 +363,6 @@ fn visit_fn(fk: &visit::fn_kind, self.method_map, self.variable_moves_map, self.capture_map, - self.last_use_map, self.cur_item); unsafe { @@ -522,7 +471,7 @@ fn visit_expr(expr: @expr, self: @mut IrMaps, vt: vt<@mut IrMaps>) { expr_path(_) => { let def = *self.tcx.def_map.get(&expr.id); debug!("expr %d: path that leads to %?", expr.id, def); - if relevant_def(def).is_some() { + if moves::moved_variable_node_id_from_def(def).is_some() { self.add_live_node_for_node(expr.id, ExprNode(expr.span)); } visit::visit_expr(expr, self, vt); @@ -539,7 +488,7 @@ fn visit_expr(expr: @expr, self: @mut IrMaps, vt: vt<@mut IrMaps>) { let cvs = self.capture_map.get(&expr.id); let mut call_caps = ~[]; for cvs.each |cv| { - match relevant_def(cv.def) { + match moves::moved_variable_node_id_from_def(cv.def) { Some(rv) => { let cv_ln = self.add_live_node(FreeVarNode(cv.span)); let is_move = match cv.mode { @@ -668,7 +617,7 @@ pub impl Liveness { match expr.node { expr_path(_) => { let def = *self.tcx.def_map.get(&expr.id); - relevant_def(def).map( + moves::moved_variable_node_id_from_def(def).map( |rdef| self.variable(*rdef, expr.span) ) } @@ -684,7 +633,7 @@ pub impl Liveness { span: span) -> Option { match self.tcx.def_map.find(&node_id) { Some(&def) => { - relevant_def(def).map( + moves::moved_variable_node_id_from_def(def).map( |rdef| self.variable(*rdef, span) ) } @@ -1388,7 +1337,7 @@ pub impl Liveness { fn access_path(&self, expr: @expr, succ: LiveNode, acc: uint) -> LiveNode { let def = *self.tcx.def_map.get(&expr.id); - match relevant_def(def) { + match moves::moved_variable_node_id_from_def(def) { Some(nid) => { let ln = self.live_node(expr.id, expr.span); if acc != 0u { @@ -1521,7 +1470,6 @@ fn check_expr(expr: @expr, self: @Liveness, vt: vt<@Liveness>) { expr_path(_) => { for self.variable_from_def_map(expr.id, expr.span).each |var| { let ln = self.live_node(expr.id, expr.span); - self.consider_last_use(expr, ln, *var); match self.ir.variable_moves_map.find(&expr.id) { None => {} @@ -1540,7 +1488,6 @@ fn check_expr(expr: @expr, self: @Liveness, vt: vt<@Liveness>) { let caps = self.ir.captures(expr); for caps.each |cap| { let var = self.variable(cap.var_nid, expr.span); - self.consider_last_use(expr, cap.ln, var); if cap.is_move { self.check_move_from_var(cap.ln, var, expr); } @@ -1609,7 +1556,7 @@ enum ReadKind { } pub impl Liveness { - fn check_ret(@self, id: node_id, sp: span, _fk: &visit::fn_kind, + fn check_ret(&self, id: node_id, sp: span, _fk: &visit::fn_kind, entry_ln: LiveNode) { if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() { // if no_ret_var is live, then we fall off the end of the @@ -1629,11 +1576,11 @@ pub impl Liveness { } } - fn check_move_from_var(@self, ln: LiveNode, + fn check_move_from_var(&self, + ln: LiveNode, var: Variable, move_expr: @expr) { /*! - * * Checks whether `var` is live on entry to any of the * successors of `ln`. If it is, report an error. * `move_expr` is the expression which caused the variable @@ -1653,16 +1600,6 @@ pub impl Liveness { } } - fn consider_last_use(@self, expr: @expr, ln: LiveNode, var: Variable) { - debug!("consider_last_use(expr.id=%?, ln=%s, var=%s)", - expr.id, ln.to_str(), var.to_str()); - - match self.live_on_exit(ln, var) { - Some(_) => {} - None => self.ir.add_last_use(expr.id, var) - } - } - fn check_lvalue(@self, expr: @expr, vt: vt<@Liveness>) { match expr.node { expr_path(_) => { @@ -1679,7 +1616,7 @@ pub impl Liveness { self.warn_about_dead_assign(expr.span, expr.id, ln, var); } def => { - match relevant_def(def) { + match moves::moved_variable_node_id_from_def(def) { Some(nid) => { let ln = self.live_node(expr.id, expr.span); let var = self.variable(nid, expr.span); @@ -1699,14 +1636,14 @@ pub impl Liveness { } } - fn check_for_reassignments_in_pat(@self, pat: @pat, mutbl: bool) { + fn check_for_reassignments_in_pat(&self, pat: @pat, mutbl: bool) { do self.pat_bindings(pat) |ln, var, sp, id| { self.check_for_reassignment(ln, var, sp, if mutbl {Some(id)} else {None}); } } - fn check_for_reassignment(@self, ln: LiveNode, var: Variable, + fn check_for_reassignment(&self, ln: LiveNode, var: Variable, orig_span: span, mutbl: Option) { match self.assigned_on_exit(ln, var) { Some(ExprNode(span)) => { @@ -1731,7 +1668,7 @@ pub impl Liveness { } } - fn report_illegal_move(@self, lnk: LiveNodeKind, + fn report_illegal_move(&self, lnk: LiveNodeKind, var: Variable, move_expr: @expr) { // the only time that it is possible to have a moved variable @@ -1796,7 +1733,8 @@ pub impl Liveness { }; } - fn report_move_location(@self, move_expr: @expr, + fn report_move_location(&self, + move_expr: @expr, var: Variable, expr_descr: &str, pronoun: &str) { @@ -1810,7 +1748,8 @@ pub impl Liveness { ty_to_str(self.tcx, move_expr_ty))); } - fn report_illegal_read(@self, chk_span: span, + fn report_illegal_read(&self, + chk_span: span, lnk: LiveNodeKind, var: Variable, rk: ReadKind) { @@ -1841,12 +1780,12 @@ pub impl Liveness { } } - fn should_warn(@self, var: Variable) -> Option<@~str> { + fn should_warn(&self, var: Variable) -> Option<@~str> { let name = self.ir.variable_name(var); if name[0] == ('_' as u8) { None } else { Some(name) } } - fn warn_about_unused_args(@self, decl: &fn_decl, entry_ln: LiveNode) { + fn warn_about_unused_args(&self, decl: &fn_decl, entry_ln: LiveNode) { for decl.inputs.each |arg| { do pat_util::pat_bindings(self.tcx.def_map, arg.pat) |_bm, p_id, sp, _n| { @@ -1856,7 +1795,7 @@ pub impl Liveness { } } - fn warn_about_unused_or_dead_vars_in_pat(@self, pat: @pat) { + fn warn_about_unused_or_dead_vars_in_pat(&self, pat: @pat) { do self.pat_bindings(pat) |ln, var, sp, id| { if !self.warn_about_unused(sp, id, ln, var) { self.warn_about_dead_assign(sp, id, ln, var); @@ -1864,7 +1803,7 @@ pub impl Liveness { } } - fn warn_about_unused(@self, sp: span, id: node_id, + fn warn_about_unused(&self, sp: span, id: node_id, ln: LiveNode, var: Variable) -> bool { if !self.used_on_entry(ln, var) { for self.should_warn(var).each |name| { @@ -1894,7 +1833,7 @@ pub impl Liveness { return false; } - fn warn_about_dead_assign(@self, sp: span, id: node_id, + fn warn_about_dead_assign(&self, sp: span, id: node_id, ln: LiveNode, var: Variable) { if self.live_on_exit(ln, var).is_none() { for self.should_warn(var).each |name| { diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 7fa198be1d47f..f525230664a21 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -48,7 +48,7 @@ use middle::ty; use middle::typeck; -use util::ppaux::{ty_to_str, region_to_str}; +use util::ppaux::{ty_to_str, region_to_str, Repr}; use util::common::indenter; use syntax::ast::{m_imm, m_const, m_mutbl}; @@ -58,50 +58,48 @@ use syntax::print::pprust; #[deriving(Eq)] pub enum categorization { - cat_rvalue, // result of eval'ing some misc expr - cat_special(special_kind), // - cat_local(ast::node_id), // local variable - cat_binding(ast::node_id), // pattern binding - cat_arg(ast::node_id), // formal argument - cat_stack_upvar(cmt), // upvar in stack closure - cat_deref(cmt, uint, ptr_kind), // deref of a ptr - cat_comp(cmt, comp_kind), // adjust to locate an internal component - cat_discr(cmt, ast::node_id), // match discriminant (see preserve()) - cat_self(ast::node_id), // explicit `self` + cat_rvalue, // result of eval'ing some misc expr + cat_static_item, + cat_implicit_self, + cat_copied_upvar(CopiedUpvar), // upvar copied into @fn or ~fn env + cat_stack_upvar(cmt), // by ref upvar from &fn + cat_local(ast::node_id), // local variable + cat_arg(ast::node_id, ast::rmode), // formal argument + cat_deref(cmt, uint, ptr_kind), // deref of a ptr + cat_interior(cmt, interior_kind), // something interior + cat_discr(cmt, ast::node_id), // match discriminant (see preserve()) + cat_self(ast::node_id), // explicit `self` +} + +#[deriving(Eq)] +struct CopiedUpvar { + upvar_id: ast::node_id, + onceness: ast::Onceness, } // different kinds of pointers: #[deriving(Eq)] pub enum ptr_kind { - uniq_ptr, + uniq_ptr(ast::mutability), gc_ptr(ast::mutability), region_ptr(ast::mutability, ty::Region), unsafe_ptr } -// I am coining the term "components" to mean "pieces of a data -// structure accessible without a dereference": +// We use the term "interior" to mean "something reachable from the +// base without a pointer dereference", e.g. a field #[deriving(Eq)] -pub enum comp_kind { - comp_tuple, // elt in a tuple - comp_anon_field, // anonymous field (in e.g. +pub enum interior_kind { + interior_tuple, // elt in a tuple + interior_anon_field, // anonymous field (in e.g. // struct Foo(int, int); - comp_variant(ast::def_id), // internals to a variant of given enum - comp_field(ast::ident, // name of field + interior_variant(ast::def_id), // internals to a variant of given enum + interior_field(ast::ident, // name of field ast::mutability), // declared mutability of field - comp_index(ty::t, // type of vec/str/etc being deref'd + interior_index(ty::t, // type of vec/str/etc being deref'd ast::mutability) // mutability of vec content } -// different kinds of expressions we might evaluate -#[deriving(Eq)] -pub enum special_kind { - sk_method, - sk_static_item, - sk_implicit_self, // old by-reference `self` - sk_heap_upvar -} - #[deriving(Eq)] pub enum MutabilityCategory { McImmutable, // Immutable. @@ -120,39 +118,29 @@ pub struct cmt_ { id: ast::node_id, // id of expr/pat producing this value span: span, // span of same expr/pat cat: categorization, // categorization of expr - lp: Option<@loan_path>, // loan path for expr, if any mutbl: MutabilityCategory, // mutability of expr as lvalue ty: ty::t // type of the expr } pub type cmt = @cmt_; -// a loan path is like a category, but it exists only when the data is -// interior to the stack frame. loan paths are used as the key to a -// map indicating what is borrowed at any point in time. -#[deriving(Eq)] -pub enum loan_path { - lp_local(ast::node_id), - lp_arg(ast::node_id), - lp_self, - lp_deref(@loan_path, ptr_kind), - lp_comp(@loan_path, comp_kind) -} - // We pun on *T to mean both actual deref of a ptr as well // as accessing of components: -pub enum deref_kind {deref_ptr(ptr_kind), deref_comp(comp_kind)} +pub enum deref_kind {deref_ptr(ptr_kind), deref_interior(interior_kind)} // Categorizes a derefable type. Note that we include vectors and strings as // derefable (we model an index as the combination of a deref and then a // pointer adjustment). pub fn opt_deref_kind(t: ty::t) -> Option { match ty::get(t).sty { - ty::ty_uniq(*) | + ty::ty_uniq(mt) => { + Some(deref_ptr(uniq_ptr(mt.mutbl))) + } + ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) | ty::ty_closure(ty::ClosureTy {sigil: ast::OwnedSigil, _}) => { - Some(deref_ptr(uniq_ptr)) + Some(deref_ptr(uniq_ptr(m_imm))) } ty::ty_rptr(r, mt) | @@ -181,19 +169,19 @@ pub fn opt_deref_kind(t: ty::t) -> Option { } ty::ty_enum(did, _) => { - Some(deref_comp(comp_variant(did))) + Some(deref_interior(interior_variant(did))) } ty::ty_struct(_, _) => { - Some(deref_comp(comp_anon_field)) + Some(deref_interior(interior_anon_field)) } ty::ty_evec(mt, ty::vstore_fixed(_)) => { - Some(deref_comp(comp_index(t, mt.mutbl))) + Some(deref_interior(interior_index(t, mt.mutbl))) } ty::ty_estr(ty::vstore_fixed(_)) => { - Some(deref_comp(comp_index(t, m_imm))) + Some(deref_interior(interior_index(t, m_imm))) } _ => None @@ -338,21 +326,11 @@ pub impl MutabilityCategory { } } - fn to_user_str(&self) -> ~str { + fn to_user_str(&self) -> &'static str { match *self { - McDeclared | McInherited => ~"mutable", - McImmutable => ~"immutable", - McReadOnly => ~"const" - } - } -} - -pub impl loan_path { - fn node_id(&self) -> Option { - match *self { - lp_local(id) | lp_arg(id) => Some(id), - lp_deref(lp, _) | lp_comp(lp, _) => lp.node_id(), - lp_self => None + McDeclared | McInherited => "mutable", + McImmutable => "immutable", + McReadOnly => "const" } } } @@ -419,9 +397,9 @@ pub impl mem_categorization_ctxt { } ast::expr_field(base, f_name, _) => { - if self.method_map.contains_key(&expr.id) { - return self.cat_method_ref(expr, expr_ty); - } + // Method calls are now a special syntactic form, + // so `a.b` should always be a field. + assert!(!self.method_map.contains_key(&expr.id)); let base_cmt = self.cat_expr(base); self.cat_field(expr, base_cmt, f_name, expr.id) @@ -475,8 +453,7 @@ pub impl mem_categorization_ctxt { @cmt_ { id:id, span:span, - cat:cat_special(sk_static_item), - lp:None, + cat:cat_static_item, mutbl: McImmutable, ty:expr_ty } @@ -487,66 +464,71 @@ pub impl mem_categorization_ctxt { // stuff as `&const` and `&mut`? // m: mutability of the argument - // lp: loan path, must be none for aliasable things let m = if mutbl {McDeclared} else {McImmutable}; - let lp = Some(@lp_arg(vid)); + let mode = ty::resolved_mode(self.tcx, mode); @cmt_ { - id:id, - span:span, - cat:cat_arg(vid), - lp:lp, + id: id, + span: span, + cat: cat_arg(vid, mode), mutbl: m, ty:expr_ty } } ast::def_self(self_id, is_implicit) => { - let cat, loan_path; - if is_implicit { - cat = cat_special(sk_implicit_self); - loan_path = None; + let cat = if is_implicit { + cat_implicit_self } else { - cat = cat_self(self_id); - loan_path = Some(@lp_self); + cat_self(self_id) }; @cmt_ { id:id, span:span, cat:cat, - lp:loan_path, mutbl: McImmutable, ty:expr_ty } } - ast::def_upvar(_, inner, fn_node_id, _) => { - let ty = ty::node_id_to_type(self.tcx, fn_node_id); - let sigil = ty::ty_closure_sigil(ty); - match sigil { - ast::BorrowedSigil => { - let upcmt = self.cat_def(id, span, expr_ty, *inner); - @cmt_ { - id:id, - span:span, - cat:cat_stack_upvar(upcmt), - lp:upcmt.lp, - mutbl:upcmt.mutbl, - ty:upcmt.ty - } - } - ast::OwnedSigil | ast::ManagedSigil => { - // FIXME #2152 allow mutation of moved upvars - @cmt_ { - id:id, - span:span, - cat:cat_special(sk_heap_upvar), - lp:None, - mutbl:McImmutable, - ty:expr_ty - } - } - } + ast::def_upvar(upvar_id, inner, fn_node_id, _) => { + let ty = ty::node_id_to_type(self.tcx, fn_node_id); + match ty::get(ty).sty { + ty::ty_closure(ref closure_ty) => { + let sigil = closure_ty.sigil; + match sigil { + ast::BorrowedSigil => { + 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 + } + } + ast::OwnedSigil | ast::ManagedSigil => { + // FIXME #2152 allow mutation of moved upvars + @cmt_ { + id:id, + span:span, + cat:cat_copied_upvar(CopiedUpvar { + upvar_id: upvar_id, + onceness: closure_ty.onceness}), + mutbl:McImmutable, + ty:expr_ty + } + } + } + } + _ => { + self.tcx.sess.span_bug( + span, + fmt!("Upvar of non-closure %? - %s", + fn_node_id, ty.repr(self.tcx))); + } + } } ast::def_local(vid, mutbl) => { @@ -555,7 +537,6 @@ pub impl mem_categorization_ctxt { id:id, span:span, cat:cat_local(vid), - lp:Some(@lp_local(vid)), mutbl:m, ty:expr_ty } @@ -567,7 +548,6 @@ pub impl mem_categorization_ctxt { id:id, span:span, cat:cat_local(vid), - lp:Some(@lp_local(vid)), mutbl:McImmutable, ty:expr_ty } @@ -582,8 +562,7 @@ pub impl mem_categorization_ctxt { @cmt_ { id: arg.id(), span: arg.span(), - cat: cat_comp(cmt, comp_variant(enum_did)), - lp: cmt.lp.map(|l| @lp_comp(*l, comp_variant(enum_did)) ), + cat: cat_interior(cmt, interior_variant(enum_did)), mutbl: cmt.mutbl.inherit(), ty: self.tcx.ty(arg) } @@ -594,7 +573,6 @@ pub impl mem_categorization_ctxt { id:elt.id(), span:elt.span(), cat:cat_rvalue, - lp:None, mutbl:McImmutable, ty:expr_ty } @@ -606,9 +584,9 @@ pub impl mem_categorization_ctxt { /// or if the container is mutable. fn inherited_mutability(&self, base_m: MutabilityCategory, - comp_m: ast::mutability) -> MutabilityCategory + interior_m: ast::mutability) -> MutabilityCategory { - match comp_m { + match interior_m { m_imm => base_m.inherit(), m_const => McReadOnly, m_mutbl => McDeclared @@ -634,13 +612,11 @@ pub impl mem_categorization_ctxt { } }; let m = self.inherited_mutability(base_cmt.mutbl, f_mutbl); - let f_comp = comp_field(f_name, f_mutbl); - let lp = base_cmt.lp.map(|lp| @lp_comp(*lp, f_comp) ); + let f_interior = interior_field(f_name, f_mutbl); @cmt_ { id: node.id(), span: node.span(), - cat: cat_comp(base_cmt, f_comp), - lp:lp, + cat: cat_interior(base_cmt, f_interior), mutbl: m, ty: self.tcx.ty(node) } @@ -688,25 +664,10 @@ pub impl mem_categorization_ctxt { { match deref_kind(self.tcx, base_cmt.ty) { deref_ptr(ptr) => { - let lp = do base_cmt.lp.chain_ref |l| { - // Given that the ptr itself is loanable, we can - // loan out deref'd uniq ptrs or mut ptrs as the data - // they are the only way to mutably reach the data they - // point at. Other ptr types admit mutable aliases and - // are therefore not loanable. - match ptr { - uniq_ptr => Some(@lp_deref(*l, ptr)), - region_ptr(ast::m_mutbl, _) => { - Some(@lp_deref(*l, ptr)) - } - gc_ptr(*) | region_ptr(_, _) | unsafe_ptr => None - } - }; - // for unique ptrs, we inherit mutability from the // owning reference. let m = match ptr { - uniq_ptr => { + uniq_ptr(*) => { self.inherited_mutability(base_cmt.mutbl, mt.mutbl) } gc_ptr(*) | region_ptr(_, _) | unsafe_ptr => { @@ -718,20 +679,17 @@ pub impl mem_categorization_ctxt { id:node.id(), span:node.span(), cat:cat_deref(base_cmt, deref_cnt, ptr), - lp:lp, mutbl:m, ty:mt.ty } } - deref_comp(comp) => { - let lp = base_cmt.lp.map(|l| @lp_comp(*l, comp) ); + deref_interior(interior) => { let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl); @cmt_ { id:node.id(), span:node.span(), - cat:cat_comp(base_cmt, comp), - lp:lp, + cat:cat_interior(base_cmt, interior), mutbl:m, ty:mt.ty } @@ -754,17 +712,10 @@ pub impl mem_categorization_ctxt { return match deref_kind(self.tcx, base_cmt.ty) { deref_ptr(ptr) => { - // (a) the contents are loanable if the base is loanable - // and this is a *unique* vector - let deref_lp = match ptr { - uniq_ptr => {base_cmt.lp.map(|lp| @lp_deref(*lp, uniq_ptr))} - _ => {None} - }; - - // (b) for unique ptrs, we inherit mutability from the + // for unique ptrs, we inherit mutability from the // owning reference. let m = match ptr { - uniq_ptr => { + uniq_ptr(*) => { self.inherited_mutability(base_cmt.mutbl, mt.mutbl) } gc_ptr(_) | region_ptr(_, _) | unsafe_ptr => { @@ -772,37 +723,34 @@ pub impl mem_categorization_ctxt { } }; - // (c) the deref is explicit in the resulting cmt + // the deref is explicit in the resulting cmt let deref_cmt = @cmt_ { id:elt.id(), span:elt.span(), cat:cat_deref(base_cmt, 0u, ptr), - lp:deref_lp, mutbl:m, ty:mt.ty }; - comp(elt, deref_cmt, base_cmt.ty, m, mt) + interior(elt, deref_cmt, base_cmt.ty, m, mt) } - deref_comp(_) => { + deref_interior(_) => { // fixed-length vectors have no deref let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl); - comp(elt, base_cmt, base_cmt.ty, m, mt) + interior(elt, base_cmt, base_cmt.ty, m, mt) } }; - fn comp(elt: N, of_cmt: cmt, - vect: ty::t, mutbl: MutabilityCategory, - mt: ty::mt) -> cmt + fn interior(elt: N, of_cmt: cmt, + vect: ty::t, mutbl: MutabilityCategory, + mt: ty::mt) -> cmt { - let comp = comp_index(vect, mt.mutbl); - let index_lp = of_cmt.lp.map(|lp| @lp_comp(*lp, comp) ); + let interior = interior_index(vect, mt.mutbl); @cmt_ { id:elt.id(), span:elt.span(), - cat:cat_comp(of_cmt, comp), - lp:index_lp, + cat:cat_interior(of_cmt, interior), mutbl:mutbl, ty:mt.ty } @@ -815,8 +763,7 @@ pub impl mem_categorization_ctxt { @cmt_ { id: elt.id(), span: elt.span(), - cat: cat_comp(cmt, comp_tuple), - lp: cmt.lp.map(|l| @lp_comp(*l, comp_tuple) ), + cat: cat_interior(cmt, interior_tuple), mutbl: cmt.mutbl.inherit(), ty: self.tcx.ty(elt) } @@ -828,26 +775,12 @@ pub impl mem_categorization_ctxt { @cmt_ { id: elt.id(), span: elt.span(), - cat: cat_comp(cmt, comp_anon_field), - lp: cmt.lp.map(|l| @lp_comp(*l, comp_anon_field)), + cat: cat_interior(cmt, interior_anon_field), mutbl: cmt.mutbl.inherit(), ty: self.tcx.ty(elt) } } - fn cat_method_ref(&self, - expr: @ast::expr, - expr_ty: ty::t) -> cmt { - @cmt_ { - id:expr.id, - span:expr.span, - cat:cat_special(sk_method), - lp:None, - mutbl:McImmutable, - ty:expr_ty - } - } - fn cat_pattern(&self, cmt: cmt, pat: @ast::pat, @@ -890,7 +823,7 @@ pub impl mem_categorization_ctxt { let tcx = self.tcx; debug!("cat_pattern: id=%d pat=%s cmt=%s", pat.id, pprust::pat_to_str(pat, tcx.sess.intr()), - self.cmt_to_repr(cmt)); + cmt.repr(tcx)); let _i = indenter(); op(cmt, pat); @@ -986,29 +919,6 @@ pub impl mem_categorization_ctxt { } } - fn cat_to_repr(&self, cat: categorization) -> ~str { - match cat { - cat_special(sk_method) => ~"method", - cat_special(sk_static_item) => ~"static_item", - cat_special(sk_implicit_self) => ~"implicit-self", - cat_special(sk_heap_upvar) => ~"heap-upvar", - cat_stack_upvar(_) => ~"stack-upvar", - cat_rvalue => ~"rvalue", - cat_local(node_id) => fmt!("local(%d)", node_id), - cat_binding(node_id) => fmt!("binding(%d)", node_id), - cat_arg(node_id) => fmt!("arg(%d)", node_id), - cat_self(node_id) => fmt!("self(%d)", node_id), - cat_deref(cmt, derefs, ptr) => { - fmt!("%s->(%s, %u)", self.cat_to_repr(cmt.cat), - self.ptr_sigil(ptr), derefs) - } - cat_comp(cmt, comp) => { - fmt!("%s.%s", self.cat_to_repr(cmt.cat), *self.comp_to_repr(comp)) - } - cat_discr(cmt, _) => self.cat_to_repr(cmt.cat) - } - } - fn mut_to_str(&self, mutbl: ast::mutability) -> ~str { match mutbl { m_mutbl => ~"mutable", @@ -1017,84 +927,33 @@ pub impl mem_categorization_ctxt { } } - fn ptr_sigil(&self, ptr: ptr_kind) -> ~str { - match ptr { - uniq_ptr => ~"~", - gc_ptr(_) => ~"@", - region_ptr(_, _) => ~"&", - unsafe_ptr => ~"*" - } - } - - fn comp_to_repr(&self, comp: comp_kind) -> @~str { - match comp { - comp_field(fld, _) => self.tcx.sess.str_of(fld), - comp_index(*) => @~"[]", - comp_tuple => @~"()", - comp_anon_field => @~"", - comp_variant(_) => @~"" - } - } - - fn lp_to_str(&self, lp: @loan_path) -> ~str { - match *lp { - lp_local(node_id) => { - fmt!("local(%d)", node_id) - } - lp_arg(node_id) => { - fmt!("arg(%d)", node_id) - } - lp_self => ~"self", - lp_deref(lp, ptr) => { - fmt!("%s->(%s)", self.lp_to_str(lp), - self.ptr_sigil(ptr)) - } - lp_comp(lp, comp) => { - fmt!("%s.%s", self.lp_to_str(lp), - *self.comp_to_repr(comp)) - } - } - } - - fn cmt_to_repr(&self, cmt: cmt) -> ~str { - fmt!("{%s id:%d m:%? lp:%s ty:%s}", - self.cat_to_repr(cmt.cat), - cmt.id, - cmt.mutbl, - cmt.lp.map_default(~"none", |p| self.lp_to_str(*p) ), - ty_to_str(self.tcx, cmt.ty)) - } - fn cmt_to_str(&self, cmt: cmt) -> ~str { - let mut_str = cmt.mutbl.to_user_str(); match cmt.cat { - cat_special(sk_method) => ~"method", - cat_special(sk_static_item) => ~"static item", - cat_special(sk_implicit_self) => ~"self reference", - cat_special(sk_heap_upvar) => { + cat_static_item => ~"static item", + cat_implicit_self => ~"self reference", + cat_copied_upvar(_) => { ~"captured outer variable in a heap closure" } cat_rvalue => ~"non-lvalue", - cat_local(_) => mut_str + ~" local variable", - cat_binding(_) => ~"pattern binding", + cat_local(_) => ~"local variable", cat_self(_) => ~"self value", - cat_arg(_) => ~"argument", - cat_deref(_, _, pk) => fmt!("dereference of %s %s pointer", - mut_str, self.ptr_sigil(pk)), - cat_stack_upvar(_) => { - ~"captured outer " + mut_str + ~" variable in a stack closure" - } - cat_comp(_, comp_field(*)) => mut_str + ~" field", - cat_comp(_, comp_tuple) => ~"tuple content", - cat_comp(_, comp_anon_field) => ~"anonymous field", - cat_comp(_, comp_variant(_)) => ~"enum content", - cat_comp(_, comp_index(t, _)) => { + cat_arg(*) => ~"argument", + cat_deref(_, _, pk) => fmt!("dereference of %s pointer", + ptr_sigil(pk)), + cat_interior(_, interior_field(*)) => ~"field", + cat_interior(_, interior_tuple) => ~"tuple content", + cat_interior(_, interior_anon_field) => ~"anonymous field", + cat_interior(_, interior_variant(_)) => ~"enum content", + cat_interior(_, interior_index(t, _)) => { match ty::get(t).sty { - ty::ty_evec(*) => mut_str + ~" vec content", - ty::ty_estr(*) => mut_str + ~" str content", - _ => mut_str + ~" indexed content" + ty::ty_evec(*) => ~"vec content", + ty::ty_estr(*) => ~"str content", + _ => ~"indexed content" } } + cat_stack_upvar(_) => { + ~"captured outer variable" + } cat_discr(cmt, _) => { self.cmt_to_str(cmt) } @@ -1149,33 +1008,142 @@ pub fn field_mutbl(tcx: ty::ctxt, return None; } -pub impl categorization { - fn derefs_through_mutable_box(&const self) -> bool { - match *self { - cat_deref(_, _, gc_ptr(ast::m_mutbl)) => { - true +pub enum AliasableReason { + AliasableManaged(ast::mutability), + AliasableBorrowed(ast::mutability), + AliasableOther +} + +pub impl cmt_ { + fn guarantor(@self) -> cmt { + //! Returns `self` after stripping away any owned pointer derefs or + //! interior content. The return value is basically the `cmt` which + //! determines how long the value in `self` remains live. + + match self.cat { + cat_rvalue | + cat_static_item | + cat_implicit_self | + cat_copied_upvar(*) | + cat_local(*) | + cat_self(*) | + cat_arg(*) | + cat_deref(_, _, unsafe_ptr(*)) | + cat_deref(_, _, gc_ptr(*)) | + cat_deref(_, _, region_ptr(*)) => { + self } - cat_deref(subcmt, _, _) | - cat_comp(subcmt, _) | - cat_discr(subcmt, _) | - cat_stack_upvar(subcmt) => { - subcmt.cat.derefs_through_mutable_box() + cat_stack_upvar(b) | + cat_discr(b, _) | + cat_interior(b, _) | + cat_deref(b, _, uniq_ptr(*)) => { + b.guarantor() } + } + } + + fn is_freely_aliasable(&self) -> bool { + self.freely_aliasable().is_some() + } + + fn freely_aliasable(&self) -> Option { + //! True if this lvalue resides in an area that is + //! freely aliasable, meaning that rustc cannot track + //! the alias//es with precision. + + // Maybe non-obvious: copied upvars can only be considered + // non-aliasable in once closures, since any other kind can be + // aliased and eventually recused. + + match self.cat { + cat_copied_upvar(CopiedUpvar {onceness: ast::Once, _}) | + cat_rvalue(*) | + cat_local(*) | + cat_arg(_, ast::by_copy) | + cat_self(*) | + cat_deref(_, _, unsafe_ptr(*)) | // of course it is aliasable, but... + cat_deref(_, _, region_ptr(m_mutbl, _)) => { + None + } + + cat_copied_upvar(CopiedUpvar {onceness: ast::Many, _}) | + cat_static_item(*) | + cat_implicit_self(*) | + cat_arg(_, ast::by_ref) => { + Some(AliasableOther) + } + + cat_deref(_, _, gc_ptr(m)) => { + Some(AliasableManaged(m)) + } + + cat_deref(_, _, region_ptr(m @ m_const, _)) | + cat_deref(_, _, region_ptr(m @ m_imm, _)) => { + Some(AliasableBorrowed(m)) + } + + cat_stack_upvar(b) | + cat_deref(b, _, uniq_ptr(*)) | + cat_interior(b, _) | + cat_discr(b, _) => { + b.freely_aliasable() + } + } + } +} + +impl Repr for cmt { + fn repr(&self, tcx: ty::ctxt) -> ~str { + fmt!("{%s id:%d m:%? ty:%s}", + self.cat.repr(tcx), + self.id, + self.mutbl, + self.ty.repr(tcx)) + } +} + +impl Repr for categorization { + fn repr(&self, tcx: ty::ctxt) -> ~str { + match *self { + cat_static_item | + cat_implicit_self | cat_rvalue | - cat_special(*) | + cat_copied_upvar(*) | cat_local(*) | - cat_binding(*) | - cat_arg(*) | - cat_self(*) => { - false + cat_self(*) | + cat_arg(*) => fmt!("%?", *self), + cat_deref(cmt, derefs, ptr) => { + fmt!("%s->(%s, %u)", cmt.cat.repr(tcx), + ptr_sigil(ptr), derefs) + } + cat_interior(cmt, interior) => { + fmt!("%s.%s", + cmt.cat.repr(tcx), + interior.repr(tcx)) } + cat_stack_upvar(cmt) | + cat_discr(cmt, _) => cmt.cat.repr(tcx) } } +} + +pub fn ptr_sigil(ptr: ptr_kind) -> ~str { + match ptr { + uniq_ptr(_) => ~"~", + gc_ptr(_) => ~"@", + region_ptr(_, _) => ~"&", + unsafe_ptr => ~"*" + } +} - fn is_mutable_box(&const self) -> bool { +impl Repr for interior_kind { + fn repr(&self, tcx: ty::ctxt) -> ~str { match *self { - cat_deref(_, _, gc_ptr(ast::m_mutbl)) => true, - _ => false + interior_field(fld, _) => copy *tcx.sess.str_of(fld), + interior_index(*) => ~"[]", + interior_tuple => ~"()", + interior_anon_field => ~"", + interior_variant(_) => ~"" } } } diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs index fe1466bf808a3..d8a0e6bacf489 100644 --- a/src/librustc/middle/moves.rs +++ b/src/librustc/middle/moves.rs @@ -246,10 +246,19 @@ pub type MovesMap = @mut HashSet; * expression */ pub type VariableMovesMap = @mut HashMap; +/** + * Set of variable node-ids that are moved. + * + * Note: The `VariableMovesMap` stores expression ids that + * are moves, whereas this set stores the ids of the variables + * that are moved at some point */ +pub type MovedVariablesSet = @mut HashSet; + /** See the section Output on the module comment for explanation. */ pub struct MoveMaps { moves_map: MovesMap, variable_moves_map: VariableMovesMap, + moved_variables_set: MovedVariablesSet, capture_map: CaptureMap } @@ -279,13 +288,25 @@ pub fn compute_moves(tcx: ty::ctxt, move_maps: MoveMaps { moves_map: @mut HashSet::new(), variable_moves_map: @mut HashMap::new(), - capture_map: @mut HashMap::new() + capture_map: @mut HashMap::new(), + moved_variables_set: @mut HashSet::new() } }; visit::visit_crate(crate, visit_cx, visitor); return visit_cx.move_maps; } +pub fn moved_variable_node_id_from_def(def: def) -> Option { + match def { + def_binding(nid, _) | + def_arg(nid, _, _) | + def_local(nid, _) | + def_self(nid, _) => Some(nid), + + _ => None + } +} + // ______________________________________________________________________ // Expressions @@ -419,6 +440,11 @@ pub impl VisitContext { MoveInPart(entire_expr) => { self.move_maps.variable_moves_map.insert( expr.id, entire_expr); + + let def = *self.tcx.def_map.get(&expr.id); + for moved_variable_node_id_from_def(def).each |&id| { + self.move_maps.moved_variables_set.insert(id); + } } Read => {} MoveInWhole => { diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index f32998281711f..ea21ab0527b4d 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -47,59 +47,27 @@ The region maps encode information about region relationships. - the free region map is populated during type check as we check each function. See the function `relate_free_regions` for more information. +- `cleanup_scopes` includes scopes where trans cleanups occur + - this is intended to reflect the current state of trans, not + necessarily how I think things ought to work */ pub struct RegionMaps { priv scope_map: HashMap, priv free_region_map: HashMap, + priv cleanup_scopes: HashSet } -pub struct ctxt { +pub struct Context { sess: Session, def_map: resolve::DefMap, // Generated maps: region_maps: @mut RegionMaps, - // Generally speaking, expressions are parented to their innermost - // enclosing block. But some kinds of expressions serve as - // parents: calls, methods, etc. In addition, some expressions - // serve as parents by virtue of where they appear. For example, - // the condition in a while loop is always a parent. In those - // cases, we add the node id of such an expression to this set so - // that when we visit it we can view it as a parent. - root_exprs: @mut HashSet, - - // The parent scope is the innermost block, statement, call, or match - // expression during the execution of which the current expression - // will be evaluated. Generally speaking, the innermost parent - // scope is also the closest suitable ancestor in the AST tree. - // - // There is a subtle point concerning call arguments. Imagine - // you have a call: - // - // { // block a - // foo( // call b - // x, - // y); - // } - // - // In what lifetime are the expressions `x` and `y` evaluated? At - // first, I imagine the answer was the block `a`, as the arguments - // are evaluated before the call takes place. But this turns out - // to be wrong. The lifetime of the call must encompass the - // argument evaluation as well. - // - // The reason is that evaluation of an earlier argument could - // create a borrow which exists during the evaluation of later - // arguments. Consider this torture test, for example, - // - // fn test1(x: @mut ~int) { - // foo(&**x, *x = ~5); - // } - // - // Here, the first argument `&**x` will be a borrow of the `~int`, - // but the second argument overwrites that very value! Bad. - // (This test is borrowck-pure-scope-in-call.rs, btw) + // Scope where variables should be parented to + var_parent: parent, + + // Innermost enclosing expression parent: parent, } @@ -128,10 +96,22 @@ pub impl RegionMaps { sup: ast::node_id) { debug!("record_parent(sub=%?, sup=%?)", sub, sup); + assert!(sub != sup); self.scope_map.insert(sub, sup); } + pub fn record_cleanup_scope(&mut self, + scope_id: ast::node_id) + { + //! Records that a scope is a CLEANUP SCOPE. This is invoked + //! from within regionck. We wait until regionck because we do + //! not know which operators are overloaded until that point, + //! and only overloaded operators result in cleanup scopes. + + self.cleanup_scopes.insert(scope_id); + } + fn opt_encl_scope(&self, id: ast::node_id) -> Option { @@ -151,6 +131,22 @@ pub impl RegionMaps { } } + fn is_cleanup_scope(&self, scope_id: ast::node_id) -> bool { + self.cleanup_scopes.contains(&scope_id) + } + + fn cleanup_scope(&self, + expr_id: ast::node_id) -> ast::node_id + { + //! Returns the scope when temps in expr will be cleaned up + + let mut id = self.encl_scope(expr_id); + while !self.cleanup_scopes.contains(&id) { + id = self.encl_scope(id); + } + return id; + } + fn encl_region(&self, id: ast::node_id) -> ty::Region { @@ -159,22 +155,38 @@ pub impl RegionMaps { ty::re_scope(self.encl_scope(id)) } - fn is_sub_scope(&self, - sub_scope: ast::node_id, - superscope: ast::node_id) -> bool + pub fn scopes_intersect(&self, + scope1: ast::node_id, + scope2: ast::node_id) -> bool + { + self.is_subscope_of(scope1, scope2) || self.is_subscope_of(scope2, scope1) + } + + fn is_subscope_of(&self, + subscope: ast::node_id, + superscope: ast::node_id) -> bool { /*! - * Returns true if `sub_scope` is equal to or is lexically + * Returns true if `subscope` is equal to or is lexically * nested inside `superscope` and false otherwise. */ - let mut sub_scope = sub_scope; - while superscope != sub_scope { - match self.scope_map.find(&sub_scope) { - None => return false, - Some(&scope) => sub_scope = scope + let mut s = subscope; + while superscope != s { + match self.scope_map.find(&s) { + None => { + debug!("is_subscope_of(%?, %?, s=%?)=false", + subscope, superscope, s); + + return false; + } + Some(&scope) => s = scope } } + + debug!("is_subscope_of(%?, %?)=true", + subscope, superscope); + return true; } @@ -239,11 +251,11 @@ pub impl RegionMaps { } (ty::re_scope(sub_scope), ty::re_scope(super_scope)) => { - self.is_sub_scope(sub_scope, super_scope) + self.is_subscope_of(sub_scope, super_scope) } (ty::re_scope(sub_scope), ty::re_free(ref fr)) => { - self.is_sub_scope(sub_scope, fr.scope_id) + self.is_subscope_of(sub_scope, fr.scope_id) } (ty::re_free(sub_fr), ty::re_free(super_fr)) => { @@ -301,6 +313,7 @@ pub impl RegionMaps { fn ancestors_of(self: &RegionMaps, scope: ast::node_id) -> ~[ast::node_id] { + // debug!("ancestors_of(scope=%d)", scope); let mut result = ~[scope]; let mut scope = scope; loop { @@ -311,13 +324,14 @@ pub impl RegionMaps { scope = superscope; } } + // debug!("ancestors_of_loop(scope=%d)", scope); } } } } /// Extracts that current parent from cx, failing if there is none. -pub fn parent_id(cx: ctxt, span: span) -> ast::node_id { +pub fn parent_id(cx: Context, span: span) -> ast::node_id { match cx.parent { None => { cx.sess.span_bug(span, ~"crate should not be parent here"); @@ -329,144 +343,137 @@ pub fn parent_id(cx: ctxt, span: span) -> ast::node_id { } /// Records the current parent (if any) as the parent of `child_id`. -pub fn record_parent(cx: ctxt, child_id: ast::node_id) { +pub fn parent_to_expr(cx: Context, child_id: ast::node_id) { for cx.parent.each |parent_id| { cx.region_maps.record_parent(child_id, *parent_id); } } -pub fn resolve_block(blk: &ast::blk, cx: ctxt, visitor: visit::vt) { +pub fn resolve_block(blk: &ast::blk, cx: Context, visitor: visit::vt) { // Record the parent of this block. - record_parent(cx, blk.node.id); + parent_to_expr(cx, blk.node.id); // Descend. - let new_cx: ctxt = ctxt {parent: Some(blk.node.id),.. cx}; + let new_cx = Context {var_parent: Some(blk.node.id), + parent: Some(blk.node.id), + ..cx}; visit::visit_block(blk, new_cx, visitor); } -pub fn resolve_arm(arm: &ast::arm, cx: ctxt, visitor: visit::vt) { +pub fn resolve_arm(arm: &ast::arm, cx: Context, visitor: visit::vt) { visit::visit_arm(arm, cx, visitor); } -pub fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt) { - match pat.node { - ast::pat_ident(*) => { - let defn_opt = cx.def_map.find(&pat.id); - match defn_opt { - Some(&ast::def_variant(_,_)) => { - /* Nothing to do; this names a variant. */ - } - _ => { - /* This names a local. Bind it to the containing scope. */ - record_parent(cx, pat.id); - } - } - } - _ => { /* no-op */ } - } - +pub fn resolve_pat(pat: @ast::pat, cx: Context, visitor: visit::vt) { + assert!(cx.var_parent == cx.parent); + parent_to_expr(cx, pat.id); visit::visit_pat(pat, cx, visitor); } -pub fn resolve_stmt(stmt: @ast::stmt, cx: ctxt, visitor: visit::vt) { +pub fn resolve_stmt(stmt: @ast::stmt, cx: Context, visitor: visit::vt) { match stmt.node { - ast::stmt_decl(*) => { - visit::visit_stmt(stmt, cx, visitor); - } - // This code has to be kept consistent with trans::base::trans_stmt - ast::stmt_expr(_, stmt_id) | - ast::stmt_semi(_, stmt_id) => { - record_parent(cx, stmt_id); - let mut expr_cx = cx; - expr_cx.parent = Some(stmt_id); - visit::visit_stmt(stmt, expr_cx, visitor); - } - ast::stmt_mac(*) => cx.sess.bug(~"unexpanded macro") + ast::stmt_decl(*) => { + visit::visit_stmt(stmt, cx, visitor); + } + ast::stmt_expr(_, stmt_id) | + ast::stmt_semi(_, stmt_id) => { + parent_to_expr(cx, stmt_id); + let expr_cx = Context {parent: Some(stmt_id), ..cx}; + visit::visit_stmt(stmt, expr_cx, visitor); + } + ast::stmt_mac(*) => cx.sess.bug(~"unexpanded macro") } } -pub fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt) { - record_parent(cx, expr.id); +pub fn resolve_expr(expr: @ast::expr, cx: Context, visitor: visit::vt) { + parent_to_expr(cx, expr.id); let mut new_cx = cx; + new_cx.parent = Some(expr.id); match expr.node { - // Calls or overloadable operators - // FIXME #3387 - // ast::expr_index(*) | ast::expr_binary(*) | - // ast::expr_unary(*) | - ast::expr_call(*) | ast::expr_method_call(*) => { - debug!("node %d: %s", expr.id, pprust::expr_to_str(expr, - cx.sess.intr())); - new_cx.parent = Some(expr.id); - } - ast::expr_match(*) => { - debug!("node %d: %s", expr.id, pprust::expr_to_str(expr, - cx.sess.intr())); - new_cx.parent = Some(expr.id); - } - ast::expr_while(cond, _) => { - new_cx.root_exprs.insert(cond.id); - } - _ => {} + ast::expr_assign_op(*) | ast::expr_index(*) | ast::expr_binary(*) | + ast::expr_unary(*) | ast::expr_call(*) | ast::expr_method_call(*) => { + // FIXME(#5074) Nested method calls + // + // The lifetimes for a call or method call look as follows: + // + // call.id + // - arg0.id + // - ... + // - argN.id + // - call.callee_id + // + // The idea is that call.callee_id represents *the time when + // the invoked function is actually running* and call.id + // represents *the time to prepare the arguments and make the + // call*. See the section "Borrows in Calls" borrowck/doc.rs + // for an extended explanantion of why this distinction is + // important. + // + // parent_to_expr(new_cx, expr.callee_id); + } + + ast::expr_match(*) => { + new_cx.var_parent = Some(expr.id); + } + + _ => {} }; - if new_cx.root_exprs.contains(&expr.id) { - new_cx.parent = Some(expr.id); - } visit::visit_expr(expr, new_cx, visitor); } pub fn resolve_local(local: @ast::local, - cx: ctxt, - visitor: visit::vt) { - record_parent(cx, local.node.id); + cx: Context, + visitor: visit::vt) { + assert!(cx.var_parent == cx.parent); + parent_to_expr(cx, local.node.id); visit::visit_local(local, cx, visitor); } -pub fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt) { +pub fn resolve_item(item: @ast::item, cx: Context, visitor: visit::vt) { // Items create a new outer block scope as far as we're concerned. - let new_cx: ctxt = ctxt {parent: None,.. cx}; + let new_cx = Context {var_parent: None, parent: None, ..cx}; visit::visit_item(item, new_cx, visitor); } pub fn resolve_fn(fk: &visit::fn_kind, decl: &ast::fn_decl, body: &ast::blk, - sp: span, + _sp: span, id: ast::node_id, - cx: ctxt, - visitor: visit::vt) { - let fn_cx = match *fk { - visit::fk_item_fn(*) | visit::fk_method(*) | - visit::fk_dtor(*) => { - // Top-level functions are a root scope. - ctxt {parent: Some(id),.. cx} - } - - visit::fk_anon(*) | visit::fk_fn_block(*) => { - // Closures continue with the inherited scope. - cx - } - }; - - // Record the ID of `self`. + cx: Context, + visitor: visit::vt) { + debug!("region::resolve_fn(id=%?, body.node.id=%?, cx.parent=%?)", + id, body.node.id, cx.parent); + + // The arguments and `self` are parented to the body of the fn. + let decl_cx = Context {parent: Some(body.node.id), + var_parent: Some(body.node.id), + ..cx}; match *fk { visit::fk_method(_, _, method) => { cx.region_maps.record_parent(method.self_id, body.node.id); } _ => {} } + visit::visit_fn_decl(decl, decl_cx, visitor); - debug!("visiting fn with body %d. cx.parent: %? \ - fn_cx.parent: %?", - body.node.id, cx.parent, fn_cx.parent); - - for decl.inputs.each |input| { - cx.region_maps.record_parent(input.id, body.node.id); - } - - visit::visit_fn(fk, decl, body, sp, id, fn_cx, visitor); + // The body of the fn itself is either a root scope (top-level fn) + // or it continues with the inherited scope (closures). + let body_cx = match *fk { + visit::fk_item_fn(*) | + visit::fk_method(*) | + visit::fk_dtor(*) => { + Context {parent: None, var_parent: None, ..cx} + } + visit::fk_anon(*) | + visit::fk_fn_block(*) => { + cx + } + }; + (visitor.visit_block)(body, body_cx, visitor); } pub fn resolve_crate(sess: Session, @@ -475,13 +482,14 @@ pub fn resolve_crate(sess: Session, { let region_maps = @mut RegionMaps { scope_map: HashMap::new(), - free_region_map: HashMap::new() + free_region_map: HashMap::new(), + cleanup_scopes: HashSet::new(), }; - let cx: ctxt = ctxt {sess: sess, - def_map: def_map, - region_maps: region_maps, - root_exprs: @mut HashSet::new(), - parent: None}; + let cx = Context {sess: sess, + def_map: def_map, + region_maps: region_maps, + parent: None, + var_parent: None}; let visitor = visit::mk_vt(@visit::Visitor { visit_block: resolve_block, visit_item: resolve_item, @@ -772,7 +780,8 @@ pub fn determine_rp_in_ty(ty: @ast::Ty, pprust::ty_to_str(ty, sess.intr())); if cx.region_is_relevant(r) { - cx.add_rp(cx.item_id, cx.add_variance(rv_contravariant)) + let rv = cx.add_variance(rv_contravariant); + cx.add_rp(cx.item_id, rv) } } @@ -782,14 +791,14 @@ pub fn determine_rp_in_ty(ty: @ast::Ty, match f.region { Some(_) => { if cx.region_is_relevant(f.region) { - cx.add_rp(cx.item_id, - cx.add_variance(rv_contravariant)) + let rv = cx.add_variance(rv_contravariant); + cx.add_rp(cx.item_id, rv) } } None => { if f.sigil == ast::BorrowedSigil && cx.anon_implies_rp { - cx.add_rp(cx.item_id, - cx.add_variance(rv_contravariant)); + let rv = cx.add_variance(rv_contravariant); + cx.add_rp(cx.item_id, rv) } } } @@ -820,7 +829,8 @@ pub fn determine_rp_in_ty(ty: @ast::Ty, debug!("reference to external, rp'd type %s", pprust::ty_to_str(ty, sess.intr())); if cx.region_is_relevant(path.rp) { - cx.add_rp(cx.item_id, cx.add_variance(variance)) + let rv = cx.add_variance(variance); + cx.add_rp(cx.item_id, rv) } } } diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 294a21fac2c23..ffc9d1488cf13 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -971,7 +971,7 @@ pub impl Resolver { module_.children.insert(name, child); return (child, new_parent); } - Some(child) => { + Some(&child) => { // Enforce the duplicate checking mode: // // * If we're requesting duplicate module checking, check that @@ -1033,7 +1033,7 @@ pub impl Resolver { *self.session.str_of(name))); } } - return (*child, new_parent); + return (child, new_parent); } } } @@ -1864,7 +1864,7 @@ pub impl Resolver { *self.session.str_of(target)); match module_.import_resolutions.find(&target) { - Some(resolution) => { + Some(&resolution) => { debug!("(building import directive) bumping \ reference"); resolution.outstanding_references += 1; @@ -2395,7 +2395,7 @@ pub impl Resolver { (*ident, new_import_resolution); } None => { /* continue ... */ } - Some(dest_import_resolution) => { + Some(&dest_import_resolution) => { // Merge the two import resolutions at a finer-grained // level. @@ -2433,8 +2433,8 @@ pub impl Resolver { module_.import_resolutions.insert (*ident, dest_import_resolution); } - Some(existing_import_resolution) => { - dest_import_resolution = *existing_import_resolution; + Some(&existing_import_resolution) => { + dest_import_resolution = existing_import_resolution; } } diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 3755cca8c35e9..785bb3edc07cd 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -1114,7 +1114,8 @@ pub fn compare_values(cx: block, pub fn store_non_ref_bindings(bcx: block, data: &ArmData, opt_temp_cleanups: Option<&mut ~[ValueRef]>) - -> block { + -> block +{ /*! * * For each copy/move binding, copy the value from the value @@ -1125,6 +1126,7 @@ pub fn store_non_ref_bindings(bcx: block, */ let mut bcx = bcx; + let mut opt_temp_cleanups = opt_temp_cleanups; for data.bindings_map.each_value |&binding_info| { match binding_info.trmode { TrByValue(is_move, lldest) => { @@ -1139,9 +1141,10 @@ pub fn store_non_ref_bindings(bcx: block, } }; - for opt_temp_cleanups.each |temp_cleanups| { + do opt_temp_cleanups.mutate |temp_cleanups| { add_clean_temp_mem(bcx, lldest, binding_info.ty); temp_cleanups.push(lldest); + temp_cleanups } } TrByRef | TrByImplicitRef => {} diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index efa10dfc2aa34..7be6bdb654e1f 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -391,14 +391,16 @@ pub fn get_tydesc_simple(ccx: @CrateContext, t: ty::t) -> ValueRef { pub fn get_tydesc(ccx: @CrateContext, t: ty::t) -> @mut tydesc_info { match ccx.tydescs.find(&t) { - Some(&inf) => inf, - _ => { - ccx.stats.n_static_tydescs += 1u; - let inf = glue::declare_tydesc(ccx, t); - ccx.tydescs.insert(t, inf); - inf - } + Some(&inf) => { + return inf; + } + _ => { } } + + ccx.stats.n_static_tydescs += 1u; + let inf = glue::declare_tydesc(ccx, t); + ccx.tydescs.insert(t, inf); + return inf; } pub fn set_optimize_for_size(f: ValueRef) { @@ -888,18 +890,18 @@ pub fn need_invoke(bcx: block) -> bool { let current = &mut *cur; let kind = &mut *current.kind; match *kind { - block_scope(ref mut inf) => { - for vec::each((*inf).cleanups) |cleanup| { - match *cleanup { - clean(_, cleanup_type) | clean_temp(_, _, cleanup_type) => { - if cleanup_type == normal_exit_and_unwind { - return true; + block_scope(ref mut inf) => { + for vec::each((*inf).cleanups) |cleanup| { + match *cleanup { + clean(_, cleanup_type) | clean_temp(_, _, cleanup_type) => { + if cleanup_type == normal_exit_and_unwind { + return true; + } + } } - } } } - } - _ => () + _ => () } cur = match current.parent { Some(next) => next, @@ -1011,12 +1013,12 @@ pub fn add_root_cleanup(bcx: block, ty=%s)", bcx.to_str(), root_info.scope, - root_info.freezes, + root_info.freeze, val_str(bcx.ccx().tn, root_loc), ppaux::ty_to_str(bcx.ccx().tcx, ty)); let bcx_scope = find_bcx_for_scope(bcx, root_info.scope); - if root_info.freezes { + if root_info.freeze.is_some() { add_clean_frozen_root(bcx_scope, root_loc, ty); } else { add_clean_temp_mem(bcx_scope, root_loc, ty); @@ -1029,6 +1031,12 @@ pub fn add_root_cleanup(bcx: block, Some(NodeInfo { id, _ }) if id == scope_id => { return bcx_sid } + + // NOTE This is messier than it ought to be and not really right + Some(NodeInfo { callee_id: Some(id), _ }) if id == scope_id => { + return bcx_sid + } + _ => { match bcx_sid.parent { None => bcx.tcx().sess.bug( @@ -2484,37 +2492,40 @@ pub fn get_dtor_symbol(ccx: @CrateContext, id: ast::node_id, substs: Option<@param_substs>) -> ~str { - let t = ty::node_id_to_type(ccx.tcx, id); - match ccx.item_symbols.find(&id) { - Some(s) => (/*bad*/copy *s), - None if substs.is_none() => { - let s = mangle_exported_name( - ccx, - vec::append(path, ~[path_name((ccx.names)(~"dtor"))]), - t); - // XXX: Bad copy, use `@str`? - ccx.item_symbols.insert(id, copy s); - s - } - None => { - // Monomorphizing, so just make a symbol, don't add - // this to item_symbols - match substs { - Some(ss) => { - let mono_ty = ty::subst_tps(ccx.tcx, ss.tys, ss.self_ty, t); - mangle_exported_name( - ccx, - vec::append(path, - ~[path_name((ccx.names)(~"dtor"))]), - mono_ty) - } - None => { - ccx.sess.bug(fmt!("get_dtor_symbol: not monomorphizing and \ - couldn't find a symbol for dtor %?", path)); - } - } - } - } + let t = ty::node_id_to_type(ccx.tcx, id); + match ccx.item_symbols.find(&id) { + Some(s) => { + return /*bad*/copy *s; + } + None => { } + } + + return if substs.is_none() { + let s = mangle_exported_name( + ccx, + vec::append(path, ~[path_name((ccx.names)(~"dtor"))]), + t); + // XXX: Bad copy, use `@str`? + ccx.item_symbols.insert(id, copy s); + s + } else { + // Monomorphizing, so just make a symbol, don't add + // this to item_symbols + match substs { + Some(ss) => { + let mono_ty = ty::subst_tps(ccx.tcx, ss.tys, ss.self_ty, t); + mangle_exported_name( + ccx, + vec::append(path, + ~[path_name((ccx.names)(~"dtor"))]), + mono_ty) + } + None => { + ccx.sess.bug(fmt!("get_dtor_symbol: not monomorphizing and \ + couldn't find a symbol for dtor %?", path)); + } + } + }; } pub fn get_item_val(ccx: @CrateContext, id: ast::node_id) -> ValueRef { diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index ad0fea3b4b4af..af00257fe1603 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -39,7 +39,6 @@ use middle::trans::monomorphize; use middle::trans::type_of; use middle::ty; use middle::typeck; -use util::common::indenter; use util::ppaux::Repr; use syntax::ast; @@ -689,7 +688,6 @@ pub fn trans_arg_expr(bcx: block, self_mode, arg_expr.repr(bcx.tcx()), ret_flag.map(|v| bcx.val_str(*v))); - let _indenter = indenter(); // translate the arg expr to a datum let arg_datumblock = match ret_flag { diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index f8fb0f4b7cf31..2ebd696dbfdef 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -574,13 +574,17 @@ pub trait get_node_info { impl get_node_info for @ast::expr { fn info(&self) -> Option { - Some(NodeInfo { id: self.id, span: self.span }) + Some(NodeInfo {id: self.id, + callee_id: Some(self.callee_id), + span: self.span}) } } impl get_node_info for ast::blk { fn info(&self) -> Option { - Some(NodeInfo { id: self.node.id, span: self.span }) + Some(NodeInfo {id: self.node.id, + callee_id: None, + span: self.span}) } } @@ -592,6 +596,7 @@ impl get_node_info for Option<@ast::expr> { pub struct NodeInfo { id: ast::node_id, + callee_id: Option, span: span } diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 25f34b8eaa9d1..3a331e8791ba2 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -195,18 +195,19 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef { match adj.autoref { None => { } Some(ref autoref) => { - assert!(autoref.region == ty::re_static); - assert!(autoref.mutbl != ast::m_mutbl); // Don't copy data to do a deref+ref. let llptr = match maybe_ptr { Some(ptr) => ptr, None => const_addr_of(cx, llconst) }; - match autoref.kind { - ty::AutoPtr => { + match *autoref { + ty::AutoUnsafe(m) | + ty::AutoPtr(ty::re_static, m) => { + assert!(m != ast::m_mutbl); llconst = llptr; } - ty::AutoBorrowVec => { + ty::AutoBorrowVec(ty::re_static, m) => { + assert!(m != ast::m_mutbl); let size = machine::llsize_of(cx, val_ty(llconst)); assert!(abi::slice_elt_base == 0); diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index fa27f652ac880..705d443b1155d 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -524,8 +524,8 @@ pub impl Datum { * case, we will call this function, which will stash a copy * away until we exit the scope `scope_id`. */ - debug!("root(scope_id=%?, freezes=%?, self=%?)", - root_info.scope, root_info.freezes, self.to_str(bcx.ccx())); + debug!("root(root_info=%?, self=%?)", + root_info, self.to_str(bcx.ccx())); if bcx.sess().trace() { trans_trace( @@ -539,7 +539,8 @@ pub impl Datum { add_root_cleanup(bcx, root_info, scratch.val, scratch.ty); // If we need to freeze the box, do that now. - if root_info.freezes { + if root_info.freeze.is_some() { + // NOTE distinguish the two kinds of freezing here callee::trans_lang_call( bcx, bcx.tcx().lang_items.borrow_as_imm_fn(), diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index f83562add3169..ac6fa7a5a1c4f 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -146,9 +146,9 @@ use middle::trans::type_of; use middle::ty; use middle::ty::struct_mutable_fields; use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn, - AutoDerefRef, AutoAddEnv}; + AutoDerefRef, AutoAddEnv, AutoUnsafe}; use util::common::indenter; -use util::ppaux::ty_to_str; +use util::ppaux::Repr; use core::cast::transmute; use core::hashmap::HashMap; @@ -201,6 +201,8 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { trans_to_datum_unadjusted(bcx, expr) }); + debug!("unadjusted datum: %s", datum.to_str(bcx.ccx())); + if adj.autoderefs > 0 { let DatumBlock { bcx: new_bcx, datum: new_datum } = datum.autoderef(bcx, expr.id, adj.autoderefs); @@ -209,25 +211,24 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { } datum = match adj.autoref { - None => datum, - Some(ref autoref) => { - match autoref.kind { - AutoPtr => { - unpack_datum!(bcx, auto_ref(bcx, datum)) - } - AutoBorrowVec => { - unpack_datum!(bcx, auto_slice(bcx, datum)) - } - AutoBorrowVecRef => { - unpack_datum!(bcx, auto_slice_and_ref(bcx, datum)) - } - AutoBorrowFn => { - // currently, all closure types are - // represented precisely the same, so no - // runtime adjustment is required: - datum - } - } + None => { + datum + } + Some(AutoUnsafe(*)) | // region + unsafe ptrs have same repr + Some(AutoPtr(*)) => { + unpack_datum!(bcx, auto_ref(bcx, datum)) + } + Some(AutoBorrowVec(*)) => { + unpack_datum!(bcx, auto_slice(bcx, datum)) + } + Some(AutoBorrowVecRef(*)) => { + unpack_datum!(bcx, auto_slice_and_ref(bcx, datum)) + } + Some(AutoBorrowFn(*)) => { + // currently, all closure types are + // represented precisely the same, so no + // runtime adjustment is required: + datum } }; @@ -273,7 +274,7 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { let tcx = bcx.tcx(); let closure_ty = expr_ty_adjusted(bcx, expr); - debug!("add_env(closure_ty=%s)", ty_to_str(tcx, closure_ty)); + debug!("add_env(closure_ty=%s)", closure_ty.repr(tcx)); let scratch = scratch_datum(bcx, closure_ty, false); let llfn = GEPi(bcx, scratch.val, [0u, abi::fn_field_code]); assert!(datum.appropriate_mode() == ByValue); @@ -612,7 +613,7 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr, let sigil = ty::ty_closure_sigil(expr_ty); debug!("translating fn_block %s with type %s", expr_to_str(expr, tcx.sess.intr()), - ty_to_str(tcx, expr_ty)); + expr_ty.repr(tcx)); return closure::trans_expr_fn(bcx, sigil, decl, body, expr.id, expr.id, None, dest); @@ -1088,6 +1089,9 @@ pub fn trans_local_var(bcx: block, def: ast::def) -> Datum { } }; + debug!("def_self() reference, self_info.t=%s", + self_info.t.repr(bcx.tcx())); + // This cast should not be necessary. We should cast self *once*, // but right now this conflicts with default methods. let real_self_ty = monomorphize_type(bcx, self_info.t); @@ -1151,7 +1155,7 @@ pub fn with_field_tys(tcx: ty::ctxt, tcx.sess.bug(fmt!( "cannot get field types from the enum type %s \ without a node ID", - ty_to_str(tcx, ty))); + ty.repr(tcx))); } Some(node_id) => { match *tcx.def_map.get(&node_id) { @@ -1173,7 +1177,7 @@ pub fn with_field_tys(tcx: ty::ctxt, _ => { tcx.sess.bug(fmt!( "cannot get field types from the type %s", - ty_to_str(tcx, ty))); + ty.repr(tcx))); } } } diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs index 7a7f03c2273e1..40b7a444a3e97 100644 --- a/src/librustc/middle/trans/inline.rs +++ b/src/librustc/middle/trans/inline.rs @@ -29,26 +29,33 @@ pub fn maybe_instantiate_inline(ccx: @CrateContext, fn_id: ast::def_id, -> ast::def_id { let _icx = ccx.insn_ctxt("maybe_instantiate_inline"); match ccx.external.find(&fn_id) { - Some(&Some(node_id)) => { - // Already inline - debug!("maybe_instantiate_inline(%s): already inline as node id %d", - ty::item_path_str(ccx.tcx, fn_id), node_id); - local_def(node_id) - } - Some(&None) => fn_id, // Not inlinable - None => { // Not seen yet - match csearch::maybe_get_item_ast( + Some(&Some(node_id)) => { + // Already inline + debug!("maybe_instantiate_inline(%s): already inline as node id %d", + ty::item_path_str(ccx.tcx, fn_id), node_id); + return local_def(node_id); + } + Some(&None) => { + return fn_id; // Not inlinable + } + None => { + // Not seen yet + } + } + + let csearch_result = + csearch::maybe_get_item_ast( ccx.tcx, fn_id, |a,b,c,d| { astencode::decode_inlined_item(a, b, ccx.maps, /*bad*/ copy c, d) - }) { - - csearch::not_found => { + }); + return match csearch_result { + csearch::not_found => { ccx.external.insert(fn_id, None); fn_id - } - csearch::found(ast::ii_item(item)) => { + } + csearch::found(ast::ii_item(item)) => { ccx.external.insert(fn_id, Some(item.id)); ccx.stats.n_inlines += 1; if translate { trans_item(ccx, item); } @@ -122,8 +129,6 @@ pub fn maybe_instantiate_inline(ccx: @CrateContext, fn_id: ast::def_id, ccx.external.insert(fn_id, Some((*dtor).node.id)); local_def((*dtor).node.id) } - } - } - } + }; } diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 90f9f93be2b48..86b087b937f25 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -44,6 +44,11 @@ pub fn trans_impl(ccx: @CrateContext, path: path, name: ast::ident, methods: &[@ast::method], generics: &ast::Generics, self_ty: Option, id: ast::node_id) { let _icx = ccx.insn_ctxt("impl::trans_impl"); + let tcx = ccx.tcx; + + debug!("trans_impl(path=%s, name=%s, self_ty=%s, id=%?)", + path.repr(tcx), name.repr(tcx), self_ty.repr(tcx), id); + if !generics.ty_params.is_empty() { return; } let sub_path = vec::append_one(path, path_name(name)); for vec::each(methods) |method| { diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index 72ad6dde4f17d..98db829370c0c 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -137,6 +137,9 @@ pub fn monomorphic_fn(ccx: @CrateContext, ast_map::node_local(*) => { ccx.tcx.sess.bug(~"Can't monomorphize a local") } + ast_map::node_callee_scope(*) => { + ccx.tcx.sess.bug(~"Can't monomorphize a callee-scope") + } ast_map::node_struct_ctor(_, i, pt) => (pt, i.ident, i.span) }; @@ -279,6 +282,7 @@ pub fn monomorphic_fn(ccx: @CrateContext, ast_map::node_trait_method(*) | ast_map::node_arg(*) | ast_map::node_block(*) | + ast_map::node_callee_scope(*) | ast_map::node_local(*) => { ccx.tcx.sess.bug(fmt!("Can't monomorphize a %?", map_node)) } diff --git a/src/librustc/middle/trans/reachable.rs b/src/librustc/middle/trans/reachable.rs index 3ccef0dbc4aca..a446408d00a10 100644 --- a/src/librustc/middle/trans/reachable.rs +++ b/src/librustc/middle/trans/reachable.rs @@ -42,19 +42,19 @@ pub fn find_reachable(crate_mod: &_mod, exp_map2: resolve::ExportMap2, tcx: ty::ctxt, method_map: typeck::method_map) -> map { let mut rmap = HashSet::new(); { - let cx = ctx { + let mut cx = @mut ctx { exp_map2: exp_map2, tcx: tcx, method_map: method_map, rmap: &mut rmap }; - traverse_public_mod(&cx, ast::crate_node_id, crate_mod); - traverse_all_resources_and_impls(&cx, crate_mod); + traverse_public_mod(cx, ast::crate_node_id, crate_mod); + traverse_all_resources_and_impls(cx, crate_mod); } return @rmap; } -fn traverse_exports(cx: &ctx, mod_id: node_id) -> bool { +fn traverse_exports(cx: @mut ctx, mod_id: node_id) -> bool { let mut found_export = false; match cx.exp_map2.find(&mod_id) { Some(ref exp2s) => { @@ -68,23 +68,25 @@ fn traverse_exports(cx: &ctx, mod_id: node_id) -> bool { return found_export; } -fn traverse_def_id(cx: &ctx, did: def_id) { +fn traverse_def_id(cx: @mut ctx, did: def_id) { if did.crate != local_crate { return; } match cx.tcx.items.find(&did.node) { None => (), // This can happen for self, for example Some(&ast_map::node_item(item, _)) => traverse_public_item(cx, item), Some(&ast_map::node_method(_, impl_id, _)) => traverse_def_id(cx, impl_id), Some(&ast_map::node_foreign_item(item, _, _, _)) => { + let cx = &mut *cx; // NOTE reborrow @mut cx.rmap.insert(item.id); } Some(&ast_map::node_variant(ref v, _, _)) => { + let cx = &mut *cx; // NOTE reborrow @mut cx.rmap.insert(v.node.id); } _ => () } } -fn traverse_public_mod(cx: &ctx, mod_id: node_id, m: &_mod) { +fn traverse_public_mod(cx: @mut ctx, mod_id: node_id, m: &_mod) { if !traverse_exports(cx, mod_id) { // No exports, so every local item is exported for m.items.each |item| { @@ -93,17 +95,21 @@ fn traverse_public_mod(cx: &ctx, mod_id: node_id, m: &_mod) { } } -fn traverse_public_item(cx: &ctx, item: @item) { - // FIXME #6021: naming rmap shouldn't be necessary - let rmap: &mut HashSet = cx.rmap; - if rmap.contains(&item.id) { return; } - rmap.insert(item.id); +fn traverse_public_item(cx: @mut ctx, item: @item) { + { + // FIXME #6021: naming rmap shouldn't be necessary + let cx = &mut *cx; + let rmap: &mut HashSet = cx.rmap; + if rmap.contains(&item.id) { return; } + rmap.insert(item.id); + } + match item.node { item_mod(ref m) => traverse_public_mod(cx, item.id, m), item_foreign_mod(ref nm) => { if !traverse_exports(cx, item.id) { for nm.items.each |item| { - cx.rmap.insert(item.id); + (&mut *cx).rmap.insert(item.id); // NOTE reborrow @mut } } } @@ -119,17 +125,17 @@ fn traverse_public_item(cx: &ctx, item: @item) { m.generics.ty_params.len() > 0u || attr::find_inline_attr(m.attrs) != attr::ia_none { - cx.rmap.insert(m.id); + (&mut *cx).rmap.insert(m.id); // NOTE reborrow @mut traverse_inline_body(cx, &m.body); } } } item_struct(ref struct_def, ref generics) => { for struct_def.ctor_id.each |&ctor_id| { - cx.rmap.insert(ctor_id); + (&mut *cx).rmap.insert(ctor_id); // NOTE reborrow @mut } for struct_def.dtor.each |dtor| { - cx.rmap.insert(dtor.node.id); + (&mut *cx).rmap.insert(dtor.node.id); if generics.ty_params.len() > 0u || attr::find_inline_attr(dtor.node.attrs) != attr::ia_none { @@ -148,11 +154,13 @@ fn traverse_public_item(cx: &ctx, item: @item) { } } -fn traverse_ty<'a, 'b>(ty: @Ty, cx: &'b ctx<'a>, v: visit::vt<&'b ctx<'a>>) { - // FIXME #6021: naming rmap shouldn't be necessary - let rmap: &mut HashSet = cx.rmap; - if rmap.contains(&ty.id) { return; } - rmap.insert(ty.id); +fn traverse_ty<'a>(ty: @Ty, cx: @mut ctx<'a>, v: visit::vt<@mut ctx<'a>>) { + { + // FIXME #6021: naming rmap shouldn't be necessary + let cx = &mut *cx; + if cx.rmap.contains(&ty.id) { return; } + cx.rmap.insert(ty.id); + } match ty.node { ty_path(p, p_id) => { @@ -171,9 +179,9 @@ fn traverse_ty<'a, 'b>(ty: @Ty, cx: &'b ctx<'a>, v: visit::vt<&'b ctx<'a>>) { } } -fn traverse_inline_body(cx: &ctx, body: &blk) { - fn traverse_expr<'a, 'b>(e: @expr, cx: &'b ctx<'a>, - v: visit::vt<&'b ctx<'a>>) { +fn traverse_inline_body(cx: @mut ctx, body: &blk) { + fn traverse_expr<'a>(e: @expr, cx: @mut ctx<'a>, + v: visit::vt<@mut ctx<'a>>) { match e.node { expr_path(_) => { match cx.tcx.def_map.find(&e.id) { @@ -218,7 +226,7 @@ fn traverse_inline_body(cx: &ctx, body: &blk) { // Don't ignore nested items: for example if a generic fn contains a // generic impl (as in deque::create), we need to monomorphize the // impl as well - fn traverse_item(i: @item, cx: &ctx, _v: visit::vt<&ctx>) { + fn traverse_item(i: @item, cx: @mut ctx, _v: visit::vt<@mut ctx>) { traverse_public_item(cx, i); } visit::visit_block(body, cx, visit::mk_vt(@visit::Visitor { @@ -228,7 +236,7 @@ fn traverse_inline_body(cx: &ctx, body: &blk) { })); } -fn traverse_all_resources_and_impls(cx: &ctx, crate_mod: &_mod) { +fn traverse_all_resources_and_impls(cx: @mut ctx, crate_mod: &_mod) { visit::visit_mod( crate_mod, codemap::dummy_sp(), diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index c7fb1e94adf4c..28705ac49320a 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -183,26 +183,21 @@ pub struct AutoDerefRef { #[auto_encode] #[auto_decode] -pub struct AutoRef { - kind: AutoRefKind, - region: Region, - mutbl: ast::mutability -} - -#[auto_encode] -#[auto_decode] -pub enum AutoRefKind { +pub enum AutoRef { /// Convert from T to &T - AutoPtr, + AutoPtr(Region, ast::mutability), /// Convert from @[]/~[]/&[] to &[] (or str) - AutoBorrowVec, + AutoBorrowVec(Region, ast::mutability), /// Convert from @[]/~[]/&[] to &&[] (or str) - AutoBorrowVecRef, + AutoBorrowVecRef(Region, ast::mutability), /// Convert from @fn()/~fn()/&fn() to &fn() - AutoBorrowFn + AutoBorrowFn(Region), + + /// Convert from T to *T + AutoUnsafe(ast::mutability) } // Stores information about provided methods (a.k.a. default methods) in @@ -432,11 +427,20 @@ pub enum Region { /// A concrete region naming some expression within the current function. re_scope(node_id), - /// Static data that has an "infinite" lifetime. + /// Static data that has an "infinite" lifetime. Top in the region lattice. re_static, /// A region variable. Should not exist after typeck. - re_infer(InferRegion) + re_infer(InferRegion), + + /// Empty lifetime is for data that is never accessed. + /// Bottom in the region lattice. We treat re_empty somewhat + /// specially; at least right now, we do not generate instances of + /// it during the GLB computations, but rather + /// generate an error instead. This is to improve error messages. + /// The only way to get an instance of re_empty is to have a region + /// variable with no constraints. + re_empty, } pub impl Region { @@ -2874,6 +2878,17 @@ pub fn ty_region(tcx: ctxt, } } +pub fn replace_fn_sig(cx: ctxt, fsty: &sty, new_sig: FnSig) -> t { + match *fsty { + ty_bare_fn(ref f) => mk_bare_fn(cx, BareFnTy {sig: new_sig, ..*f}), + ty_closure(ref f) => mk_closure(cx, ClosureTy {sig: new_sig, ..*f}), + ref s => { + cx.sess.bug( + fmt!("ty_fn_sig() called on non-fn type: %?", s)); + } + } +} + pub fn replace_closure_return_type(tcx: ctxt, fn_type: t, ret_type: t) -> t { /*! * @@ -2993,26 +3008,26 @@ pub fn adjust_ty(cx: ctxt, match adj.autoref { None => adjusted_ty, Some(ref autoref) => { - match autoref.kind { - AutoPtr => { - mk_rptr(cx, autoref.region, - mt {ty: adjusted_ty, - mutbl: autoref.mutbl}) + match *autoref { + AutoPtr(r, m) => { + mk_rptr(cx, r, mt {ty: adjusted_ty, mutbl: m}) } - AutoBorrowVec => { - borrow_vec(cx, span, autoref, adjusted_ty) + AutoBorrowVec(r, m) => { + borrow_vec(cx, span, r, m, adjusted_ty) } - AutoBorrowVecRef => { - adjusted_ty = borrow_vec(cx, span, autoref, - adjusted_ty); - mk_rptr(cx, autoref.region, - mt {ty: adjusted_ty, mutbl: ast::m_imm}) + AutoBorrowVecRef(r, m) => { + adjusted_ty = borrow_vec(cx, span, r, m, adjusted_ty); + mk_rptr(cx, r, mt {ty: adjusted_ty, mutbl: ast::m_imm}) } - AutoBorrowFn => { - borrow_fn(cx, span, autoref, adjusted_ty) + AutoBorrowFn(r) => { + borrow_fn(cx, span, r, adjusted_ty) + } + + AutoUnsafe(m) => { + mk_ptr(cx, mt {ty: adjusted_ty, mutbl: m}) } } } @@ -3021,15 +3036,15 @@ pub fn adjust_ty(cx: ctxt, }; fn borrow_vec(cx: ctxt, span: span, - autoref: &AutoRef, ty: ty::t) -> ty::t { + r: Region, m: ast::mutability, + ty: ty::t) -> ty::t { match get(ty).sty { ty_evec(mt, _) => { - ty::mk_evec(cx, mt {ty: mt.ty, mutbl: autoref.mutbl}, - vstore_slice(autoref.region)) + ty::mk_evec(cx, mt {ty: mt.ty, mutbl: m}, vstore_slice(r)) } ty_estr(_) => { - ty::mk_estr(cx, vstore_slice(autoref.region)) + ty::mk_estr(cx, vstore_slice(r)) } ref s => { @@ -3041,13 +3056,12 @@ pub fn adjust_ty(cx: ctxt, } } - fn borrow_fn(cx: ctxt, span: span, - autoref: &AutoRef, ty: ty::t) -> ty::t { + fn borrow_fn(cx: ctxt, span: span, r: Region, ty: ty::t) -> ty::t { match get(ty).sty { ty_closure(ref fty) => { ty::mk_closure(cx, ClosureTy { sigil: BorrowedSigil, - region: autoref.region, + region: r, ..copy *fty }) } @@ -3062,6 +3076,18 @@ pub fn adjust_ty(cx: ctxt, } } +pub impl AutoRef { + fn map_region(&self, f: &fn(Region) -> Region) -> AutoRef { + match *self { + ty::AutoPtr(r, m) => ty::AutoPtr(f(r), m), + ty::AutoBorrowVec(r, m) => ty::AutoBorrowVec(f(r), m), + ty::AutoBorrowVecRef(r, m) => ty::AutoBorrowVecRef(f(r), m), + ty::AutoBorrowFn(r) => ty::AutoBorrowFn(f(r)), + ty::AutoUnsafe(m) => ty::AutoUnsafe(m), + } + } +} + pub struct ParamsTy { params: ~[t], ty: t @@ -3986,7 +4012,7 @@ pub fn lookup_field_type(tcx: ctxt, } else { match tcx.tcache.find(&id) { - Some(tpt) => tpt.ty, + Some(&ty_param_bounds_and_ty {ty, _}) => ty, None => { let tpt = csearch::get_field_type(tcx, struct_id, id); tcx.tcache.insert(id, tpt); diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index 7f0066a1aa272..0c9b61164d231 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -118,8 +118,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: @ast::Path, // Assign the pattern the type of the *enum*, not the variant. let enum_tpt = ty::lookup_item_type(tcx, enm); - instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id, - pcx.block_region); + instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id); // check that the type of the value being matched is a subtype // of the type of the pattern: @@ -159,8 +158,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: @ast::Path, } else { ctor_tpt }; - instantiate_path(pcx.fcx, path, struct_tpt, pat.span, pat.id, - pcx.block_region); + instantiate_path(pcx.fcx, path, struct_tpt, pat.span, pat.id); // Check that the type of the value being matched is a subtype of // the type of the pattern. diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index fb5b53d9400fb..0cc2ddd32b46a 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -119,7 +119,8 @@ pub fn lookup( // In a call `a.b::(...)`: expr: @ast::expr, // The expression `a.b(...)`. self_expr: @ast::expr, // The expression `a`. - callee_id: node_id, // Where to store `a.b`'s type + callee_id: node_id, /* Where to store `a.b`'s type, + * also the scope of the call */ m_name: ast::ident, // The ident `b`. self_ty: ty::t, // The type of `a`. supplied_tps: &[ty::t], // The list of types X, Y, ... . @@ -127,7 +128,7 @@ pub fn lookup( check_traits: CheckTraitsFlag, // Whether we check traits only. autoderef_receiver: AutoderefReceiverFlag) -> Option { - let mut impl_dups = HashSet::new(); + let mut impl_dups = @mut HashSet::new(); let lcx = LookupContext { fcx: fcx, expr: expr, @@ -135,7 +136,7 @@ pub fn lookup( callee_id: callee_id, m_name: m_name, supplied_tps: supplied_tps, - impl_dups: &mut impl_dups, + impl_dups: impl_dups, inherent_candidates: @mut ~[], extension_candidates: @mut ~[], deref_args: deref_args, @@ -154,7 +155,7 @@ pub struct LookupContext<'self> { callee_id: node_id, m_name: ast::ident, supplied_tps: &'self [ty::t], - impl_dups: &'self mut HashSet, + impl_dups: @mut HashSet, inherent_candidates: @mut ~[Candidate], extension_candidates: @mut ~[Candidate], deref_args: check::DerefArgs, @@ -640,7 +641,7 @@ pub impl<'self> LookupContext<'self> { /*! * * In the event that we are invoking a method with a receiver - * of a linear borrowed type like `&mut T` or `&mut [T]`, + * of a borrowed type like `&T`, `&mut T`, or `&mut [T]`, * we will "reborrow" the receiver implicitly. For example, if * you have a call `r.inc()` and where `r` has type `&mut T`, * then we treat that like `(&mut *r).inc()`. This avoids @@ -657,26 +658,19 @@ pub impl<'self> LookupContext<'self> { let tcx = self.tcx(); return match ty::get(self_ty).sty { - ty::ty_rptr(_, self_mt) if self_mt.mutbl == m_mutbl => { - let region = self.infcx().next_region_var(self.expr.span, - self.expr.id); + ty::ty_rptr(_, self_mt) => { + let region = self.infcx().next_region_var_nb(self.expr.span); (ty::mk_rptr(tcx, region, self_mt), ty::AutoDerefRef(ty::AutoDerefRef { autoderefs: autoderefs+1, - autoref: Some(ty::AutoRef {kind: AutoPtr, - region: region, - mutbl: self_mt.mutbl})})) + autoref: Some(ty::AutoPtr(region, self_mt.mutbl))})) } - ty::ty_evec(self_mt, vstore_slice(_)) - if self_mt.mutbl == m_mutbl => { - let region = self.infcx().next_region_var(self.expr.span, - self.expr.id); + ty::ty_evec(self_mt, vstore_slice(_)) => { + let region = self.infcx().next_region_var_nb(self.expr.span); (ty::mk_evec(tcx, self_mt, vstore_slice(region)), ty::AutoDerefRef(ty::AutoDerefRef { - autoderefs: autoderefs, - autoref: Some(ty::AutoRef {kind: AutoBorrowVec, - region: region, - mutbl: self_mt.mutbl})})) + autoderefs: autoderefs, + autoref: Some(ty::AutoBorrowVec(region, self_mt.mutbl))})) } _ => { (self_ty, @@ -793,7 +787,7 @@ pub impl<'self> LookupContext<'self> { fn search_for_some_kind_of_autorefd_method( &self, - kind: AutoRefKind, + kind: &fn(Region, ast::mutability) -> ty::AutoRef, autoderefs: uint, mutbls: &[ast::mutability], mk_autoref_ty: &fn(ast::mutability, ty::Region) -> ty::t) @@ -801,8 +795,7 @@ pub impl<'self> LookupContext<'self> { { // This is hokey. We should have mutability inference as a // variable. But for now, try &const, then &, then &mut: - let region = self.infcx().next_region_var(self.expr.span, - self.expr.id); + let region = self.infcx().next_region_var_nb(self.expr.span); for mutbls.each |mutbl| { let autoref_ty = mk_autoref_ty(*mutbl, region); match self.search_for_method(autoref_ty) { @@ -812,12 +805,7 @@ pub impl<'self> LookupContext<'self> { self.self_expr.id, @ty::AutoDerefRef(ty::AutoDerefRef { autoderefs: autoderefs, - autoref: Some(ty::AutoRef { - kind: kind, - region: region, - mutbl: *mutbl, - }), - })); + autoref: Some(kind(region, *mutbl))})); return Some(mme); } } @@ -1024,8 +1012,7 @@ pub impl<'self> LookupContext<'self> { let (_, opt_transformed_self_ty, fn_sig) = replace_bound_regions_in_fn_sig( tcx, @Nil, Some(transformed_self_ty), &bare_fn_ty.sig, - |_br| self.fcx.infcx().next_region_var( - self.expr.span, self.expr.id)); + |_br| self.fcx.infcx().next_region_var_nb(self.expr.span)); let transformed_self_ty = opt_transformed_self_ty.get(); let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {sig: fn_sig, ..bare_fn_ty}); debug!("after replacing bound regions, fty=%s", self.ty_to_str(fty)); diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index b9f3de873cf07..84fc40f6954a8 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -923,11 +923,9 @@ pub impl FnCtxt { fn region_var_if_parameterized(&self, rp: Option, - span: span, - lower_bound: ty::Region) + span: span) -> Option { - rp.map( - |_rp| self.infcx().next_region_var_with_lb(span, lower_bound)) + rp.map(|_rp| self.infcx().next_region_var_nb(span)) } fn type_error_message(&self, @@ -1108,8 +1106,7 @@ pub fn impl_self_ty(vcx: &VtableContext, }; let self_r = if region_param.is_some() { - Some(vcx.infcx.next_region_var(location_info.span, - location_info.id)) + Some(vcx.infcx.next_region_var_nb(location_info.span)) } else { None }; @@ -1317,9 +1314,18 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, // that they appear in call position. check_expr(fcx, f); + // Store the type of `f` as the type of the callee + let fn_ty = fcx.expr_ty(f); + + // NOTE here we write the callee type before regions have been + // substituted; in the method case, we write the type after + // regions have been substituted. Methods are correct, but it + // is awkward to deal with this now. Best thing would I think + // be to just have a separate "callee table" that contains the + // FnSig and not a general purpose ty::t + fcx.write_ty(call_expr.callee_id, fn_ty); // Extract the function signature from `in_fty`. - let fn_ty = fcx.expr_ty(f); let fn_sty = structure_of(fcx, f.span, fn_ty); // FIXME(#3678) For now, do not permit calls to C abi functions. @@ -1356,7 +1362,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, let (_, _, fn_sig) = replace_bound_regions_in_fn_sig( fcx.tcx(), @Nil, None, &fn_sig, - |_br| fcx.infcx().next_region_var(call_expr.span, call_expr.id)); + |_br| fcx.infcx().next_region_var_nb(call_expr.span)); // Call the generic checker. check_argument_types(fcx, call_expr.span, fn_sig.inputs, f, @@ -1936,9 +1942,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, // Generate the struct type. let self_region = - fcx.region_var_if_parameterized(region_parameterized, - span, - ty::re_scope(id)); + fcx.region_var_if_parameterized(region_parameterized, span); let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count); let substitutions = substs { self_r: self_region, @@ -2024,9 +2028,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, // Generate the enum type. let self_region = - fcx.region_var_if_parameterized(region_parameterized, - span, - ty::re_scope(id)); + fcx.region_var_if_parameterized(region_parameterized, span); let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count); let substitutions = substs { self_r: self_region, @@ -2366,13 +2368,12 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, // (and how long it is valid), which we don't know yet until type // inference is complete. // - // Therefore, here we simply generate a region variable with - // the current expression as a lower bound. The region - // inferencer will then select the ultimate value. Finally, - // borrowck is charged with guaranteeing that the value whose - // address was taken can actually be made to live as long as - // it needs to live. - let region = fcx.infcx().next_region_var(expr.span, expr.id); + // Therefore, here we simply generate a region variable. The + // region inferencer will then select the ultimate value. + // Finally, borrowck is charged with guaranteeing that the + // value whose address was taken can actually be made to live + // as long as it needs to live. + let region = fcx.infcx().next_region_var_nb(expr.span); let tm = ty::mt { ty: fcx.expr_ty(oprnd), mutbl: mutbl }; let oprnd_t = if ty::type_is_error(tm.ty) { @@ -2389,8 +2390,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, let defn = lookup_def(fcx, pth.span, id); let tpt = ty_param_bounds_and_ty_for_def(fcx, expr.span, defn); - let region_lb = ty::re_scope(expr.id); - instantiate_path(fcx, pth, tpt, expr.span, expr.id, region_lb); + instantiate_path(fcx, pth, tpt, expr.span, expr.id); } ast::expr_inline_asm(ref ia) => { fcx.require_unsafe(expr.span, ~"use of inline assembly"); @@ -3258,8 +3258,7 @@ pub fn instantiate_path(fcx: @mut FnCtxt, pth: @ast::Path, tpt: ty_param_bounds_and_ty, span: span, - node_id: ast::node_id, - region_lb: ty::Region) { + node_id: ast::node_id) { debug!(">>> instantiate_path"); let ty_param_count = tpt.generics.type_param_defs.len(); @@ -3285,8 +3284,7 @@ pub fn instantiate_path(fcx: @mut FnCtxt, } } None => { // no lifetime parameter supplied, insert default - fcx.region_var_if_parameterized( - tpt.generics.region_param, span, region_lb) + fcx.region_var_if_parameterized(tpt.generics.region_param, span) } }; @@ -3370,7 +3368,7 @@ pub fn ast_expr_vstore_to_vstore(fcx: @mut FnCtxt, ast::expr_vstore_uniq => ty::vstore_uniq, ast::expr_vstore_box | ast::expr_vstore_mut_box => ty::vstore_box, ast::expr_vstore_slice | ast::expr_vstore_mut_slice => { - let r = fcx.infcx().next_region_var(e.span, e.id); + let r = fcx.infcx().next_region_var_nb(e.span); ty::vstore_slice(r) } } diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index cb2b854276d6f..1c35c911b14cd 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -28,16 +28,15 @@ this point a bit better. */ use middle::freevars::get_freevars; -use middle::pat_util::pat_bindings; use middle::ty::{re_scope}; use middle::ty; use middle::typeck::check::FnCtxt; -use middle::typeck::check::lookup_def; use middle::typeck::check::regionmanip::relate_nested_regions; use middle::typeck::infer::resolve_and_force_all_but_regions; use middle::typeck::infer::resolve_type; use util::ppaux::{note_and_explain_region, ty_to_str, region_to_str}; +use middle::pat_util; use syntax::ast::{ManagedSigil, OwnedSigil, BorrowedSigil}; use syntax::ast::{def_arg, def_binding, def_local, def_self, def_upvar}; @@ -73,7 +72,11 @@ fn encl_region_of_def(fcx: @mut FnCtxt, def: ast::def) -> ty::Region { } pub impl Rcx { - fn resolve_type(@mut self, unresolved_ty: ty::t) -> ty::t { + fn tcx(&self) -> ty::ctxt { + self.fcx.ccx.tcx + } + + fn resolve_type(&mut self, unresolved_ty: ty::t) -> ty::t { /*! * Try to resolve the type for the given node, returning * t_err if an error results. Note that we never care @@ -149,10 +152,17 @@ pub fn regionck_fn(fcx: @mut FnCtxt, blk: &ast::blk) { fn regionck_visitor() -> rvt { visit::mk_vt(@visit::Visitor {visit_item: visit_item, - visit_stmt: visit_stmt, visit_expr: visit_expr, - visit_block: visit_block, + + // NOTE this should be visit_pat + // but causes errors in formal + // arguments in closures due to + // #XYZ! + //visit_pat: visit_pat, + visit_arm: visit_arm, visit_local: visit_local, + + visit_block: visit_block, .. *visit::default_visitor()}) } @@ -160,44 +170,103 @@ fn visit_item(_item: @ast::item, _rcx: @mut Rcx, _v: rvt) { // Ignore items } -fn visit_local(l: @ast::local, rcx: @mut Rcx, v: rvt) { - // Check to make sure that the regions in all local variables are - // within scope. - // - // Note: we do this here rather than in visit_pat because we do - // not wish to constrain the regions in *patterns* in quite the - // same way. `visit_node()` guarantees that the region encloses - // the node in question, which ultimately constrains the regions - // in patterns to enclose the match expression as a whole. But we - // want them to enclose the *arm*. However, regions in patterns - // must either derive from the discriminant or a ref pattern: in - // the case of the discriminant, the regions will be constrained - // when the type of the discriminant is checked. In the case of a - // ref pattern, the variable is created with a suitable lower - // bound. - let e = rcx.errors_reported; - (v.visit_pat)(l.node.pat, rcx, v); - let def_map = rcx.fcx.ccx.tcx.def_map; - do pat_bindings(def_map, l.node.pat) |_bm, id, sp, _path| { - visit_node(id, sp, rcx); - } - if e != rcx.errors_reported { - return; // if decl has errors, skip initializer expr - } +fn visit_block(b: &ast::blk, rcx: @mut Rcx, v: rvt) { + rcx.fcx.tcx().region_maps.record_cleanup_scope(b.node.id); + visit::visit_block(b, rcx, v); +} - (v.visit_ty)(l.node.ty, rcx, v); - for l.node.init.each |i| { - (v.visit_expr)(*i, rcx, v); +fn visit_arm(arm: &ast::arm, rcx: @mut Rcx, v: rvt) { + // see above + for arm.pats.each |&p| { + constrain_bindings_in_pat(p, rcx); } + + visit::visit_arm(arm, rcx, v); } -fn visit_block(b: &ast::blk, rcx: @mut Rcx, v: rvt) { - visit::visit_block(b, rcx, v); +fn visit_local(l: @ast::local, rcx: @mut Rcx, v: rvt) { + // see above + constrain_bindings_in_pat(l.node.pat, rcx); + visit::visit_local(l, rcx, v); +} + +fn constrain_bindings_in_pat(pat: @ast::pat, rcx: @mut Rcx) { + let tcx = rcx.fcx.tcx(); + debug!("regionck::visit_pat(pat=%s)", pat.repr(tcx)); + do pat_util::pat_bindings(tcx.def_map, pat) |_, id, span, _| { + // If we have a variable that contains region'd data, that + // data will be accessible from anywhere that the variable is + // accessed. We must be wary of loops like this: + // + // // from src/test/compile-fail/borrowck-lend-flow.rs + // let mut v = ~3, w = ~4; + // let mut x = &mut w; + // loop { + // **x += 1; // (2) + // borrow(v); //~ ERROR cannot borrow + // x = &mut v; // (1) + // } + // + // Typically, we try to determine the region of a borrow from + // those points where it is dereferenced. In this case, one + // might imagine that the lifetime of `x` need only be the + // body of the loop. But of course this is incorrect because + // the pointer that is created at point (1) is consumed at + // point (2), meaning that it must be live across the loop + // iteration. The easiest way to guarantee this is to require + // that the lifetime of any regions that appear in a + // variable's type enclose at least the variable's scope. + + let encl_region = tcx.region_maps.encl_region(id); + constrain_regions_in_type_of_node(rcx, id, encl_region, span); + } } fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) { debug!("regionck::visit_expr(e=%s)", rcx.fcx.expr_to_str(expr)); + let has_method_map = rcx.fcx.inh.method_map.contains_key(&expr.id); + + // Record cleanup scopes, which are used by borrowck to decide the + // maximum lifetime of a temporary rvalue. These were derived by + // examining where trans creates block scopes, not because this + // reflects some principled decision around temporary lifetimes. + // Ordinarily this would seem like something that should be setup + // in region, but we need to know which uses of operators are + // overloaded. See #3511. + let tcx = rcx.fcx.tcx(); + match expr.node { + ast::expr_index(*) | + ast::expr_binary(*) | + ast::expr_assign_op(*) | + ast::expr_unary(*) if has_method_map => { + tcx.region_maps.record_cleanup_scope(expr.id); + } + ast::expr_binary(ast::and, lhs, rhs) | + ast::expr_binary(ast::or, lhs, rhs) => { + tcx.region_maps.record_cleanup_scope(lhs.id); + tcx.region_maps.record_cleanup_scope(rhs.id); + } + ast::expr_call(*) | + ast::expr_method_call(*) => { + tcx.region_maps.record_cleanup_scope(expr.id); + } + ast::expr_match(_, ref arms) => { + tcx.region_maps.record_cleanup_scope(expr.id); + for arms.each |arm| { + for arm.guard.each |guard| { + tcx.region_maps.record_cleanup_scope(guard.id); + } + } + } + ast::expr_while(cond, ref body) => { + tcx.region_maps.record_cleanup_scope(cond.id); + tcx.region_maps.record_cleanup_scope(body.node.id); + } + _ => {} + } + + // Check any autoderefs or autorefs that appear. for rcx.fcx.inh.adjustments.find(&expr.id).each |&adjustment| { debug!("adjustment=%?", adjustment); match *adjustment { @@ -208,6 +277,13 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) { constrain_derefs(rcx, expr, autoderefs, expr_ty); for opt_autoref.each |autoref| { guarantor::for_autoref(rcx, expr, autoderefs, autoref); + + // Require that the resulting region encompasses + // the current node. + // + // FIXME(#5074) remove to support nested method calls + constrain_regions_in_type_of_node( + rcx, expr.id, ty::re_scope(expr.id), expr.span); } } _ => {} @@ -215,58 +291,40 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) { } match expr.node { - ast::expr_path(*) => { - // Avoid checking the use of local variables, as we - // already check their definitions. The def'n always - // encloses the use. So if the def'n is enclosed by the - // region, then the uses will also be enclosed (and - // otherwise, an error will have been reported at the - // def'n site). - match lookup_def(rcx.fcx, expr.span, expr.id) { - ast::def_local(*) | ast::def_arg(*) | - ast::def_upvar(*) => return, - _ => () - } + ast::expr_call(callee, ref args, _) => { + constrain_callee(rcx, expr, callee); + constrain_call(rcx, expr, None, *args, false); } - ast::expr_call(callee, ref args, _) => { - // Check for a.b() where b is a method. Ensure that - // any types in the callee are valid for the entire - // method call. - - // FIXME(#3387)--we should really invoke - // `constrain_auto_ref()` on all exprs. But that causes a - // lot of spurious errors because of how the region - // hierarchy is setup. - if rcx.fcx.inh.method_map.contains_key(&callee.id) { - match callee.node { - ast::expr_field(base, _, _) => { - constrain_auto_ref(rcx, base); - } - _ => { - // This can happen if you have code like - // (x[0])() where `x[0]` is overloaded. Just - // ignore it. - } - } - } else { - constrain_auto_ref(rcx, callee); - } + ast::expr_method_call(arg0, _, _, ref args, _) => { + constrain_call(rcx, expr, Some(arg0), *args, false); + } - for args.each |arg| { - constrain_auto_ref(rcx, *arg); - } + ast::expr_index(lhs, rhs) | + ast::expr_assign_op(_, lhs, rhs) | + ast::expr_binary(_, 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 + // implicit "by ref" sort of passing style here. This + // should be converted to an adjustment! + constrain_call(rcx, expr, Some(lhs), [rhs], true); } - ast::expr_method_call(rcvr, _, _, ref args, _) => { - // Check for a.b() where b is a method. Ensure that - // any types in the callee are valid for the entire - // method call. + ast::expr_unary(_, lhs) if has_method_map => { + // As above. + constrain_call(rcx, expr, Some(lhs), [], true); + } - constrain_auto_ref(rcx, rcvr); - for args.each |arg| { - constrain_auto_ref(rcx, *arg); - } + ast::expr_unary(ast::deref, base) => { + // For *a, the lifetime of a must enclose the deref + let base_ty = rcx.resolve_node_type(base.id); + constrain_derefs(rcx, expr, 1, base_ty); + } + + ast::expr_index(vec_expr, _) => { + // For a[b], the lifetime of a must enclose the deref + let vec_type = rcx.resolve_expr_type_adjusted(vec_expr); + constrain_index(rcx, expr, vec_type); } ast::expr_cast(source, _) => { @@ -294,18 +352,18 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) { } } - ast::expr_index(vec_expr, _) => { - let vec_type = rcx.resolve_expr_type_adjusted(vec_expr); - constrain_index(rcx, expr, vec_type); - } - - ast::expr_unary(ast::deref, base) => { - let base_ty = rcx.resolve_node_type(base.id); - constrain_derefs(rcx, expr, 1, base_ty); - } - ast::expr_addr_of(_, base) => { guarantor::for_addr_of(rcx, expr, base); + + // Require that when you write a `&expr` expression, the + // resulting pointer has a lifetime that encompasses the + // `&expr` expression itself. Note that we constraining + // the type of the node expr.id here *before applying + // adjustments*. + // + // FIXME(#5074) nested method calls requires that this rule change + let ty0 = rcx.resolve_node_type(expr.id); + constrain_regions_in_type(rcx, ty::re_scope(expr.id), expr.span, ty0); } ast::expr_match(discr, ref arms) => { @@ -313,6 +371,8 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) { } ast::expr_fn_block(*) => { + // The lifetime of a block fn must not outlive the variables + // it closes over let function_type = rcx.resolve_node_type(expr.id); match ty::get(function_type).sty { ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil, @@ -326,46 +386,107 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) { _ => () } - if !visit_node(expr.id, expr.span, rcx) { return; } visit::visit_expr(expr, rcx, v); } -fn visit_stmt(s: @ast::stmt, rcx: @mut Rcx, v: rvt) { - visit::visit_stmt(s, rcx, v); -} +fn constrain_callee(rcx: @mut Rcx, + call_expr: @ast::expr, + callee_expr: @ast::expr) +{ + let tcx = rcx.fcx.tcx(); -fn visit_node(id: ast::node_id, span: span, rcx: @mut Rcx) -> bool { - /*! - * - * checks the type of the node `id` and reports an error if it - * references a region that is not in scope for that node. - * Returns false if an error is reported; this is used to cause us - * to cut off region checking for that subtree to avoid reporting - * tons of errors. */ - - let fcx = rcx.fcx; - - // find the region where this expr evaluation is taking place - let tcx = fcx.ccx.tcx; - let encl_region = match tcx.region_maps.opt_encl_scope(id) { - None => ty::re_static, - Some(r) => ty::re_scope(r) - }; - - // Otherwise, look at the type and see if it is a region pointer. - constrain_regions_in_type_of_node(rcx, id, encl_region, span) + let call_region = ty::re_scope(call_expr.id); + + let callee_ty = rcx.resolve_node_type(call_expr.callee_id); + if ty::type_is_error(callee_ty) { + return; + } + + match ty::get(callee_ty).sty { + ty::ty_bare_fn(*) => { } + ty::ty_closure(ref closure_ty) => { + match rcx.fcx.mk_subr(true, callee_expr.span, + call_region, closure_ty.region) { + result::Err(_) => { + tcx.sess.span_err( + callee_expr.span, + fmt!("cannot invoke closure outside of its lifetime")); + note_and_explain_region( + tcx, + "the closure is only valid for ", + closure_ty.region, + ""); + } + result::Ok(_) => {} + } + } + _ => { + tcx.sess.span_bug( + callee_expr.span, + fmt!("Calling non-function: %s", callee_ty.repr(tcx))); + } + } } -fn encl_region_or_static(rcx: @mut Rcx, expr: @ast::expr) -> ty::Region { - // FIXME(#3850) --- interactions with modes compel overly large granularity - // that is, we would probably prefer to just return re_scope(expr.id) - // here but we cannot just yet. +fn constrain_call(rcx: @mut Rcx, + // might be expr_call, expr_method_call, or an overloaded + // operator + call_expr: @ast::expr, + receiver: Option<@ast::expr>, + arg_exprs: &[@ast::expr], + implicitly_ref_args: bool) +{ + //! Invoked on every call site (i.e., normal calls, method calls, + //! and overloaded operators). Constrains the regions which appear + //! in the type of the function. Also constrains the regions that + //! appear in the arguments appropriately. let tcx = rcx.fcx.tcx(); - match tcx.region_maps.opt_encl_scope(expr.id) { - Some(s) => ty::re_scope(s), - None => ty::re_static // occurs in constants + debug!("constrain_call(call_expr=%s, implicitly_ref_args=%?)", + call_expr.repr(tcx), implicitly_ref_args); + let callee_ty = rcx.resolve_node_type(call_expr.callee_id); + if ty::type_is_error(callee_ty) { + return; } + let fn_sig = ty::ty_fn_sig(callee_ty); + + // `callee_region` is the scope representing the time in which the + // call occurs. + // + // FIXME(#5074) to support nested method calls, should be callee_id + let callee_scope = call_expr.id; + let callee_region = ty::re_scope(callee_scope); + + for fn_sig.inputs.eachi |i, input| { + // ensure that any regions appearing in the argument type are + // valid for at least the lifetime of the function: + constrain_regions_in_type_of_node( + rcx, arg_exprs[i].id, callee_region, arg_exprs[i].span); + + // unfortunately, there are two means of taking implicit + // references, and we need to propagate constraints as a + // result. modes are going away and the "DerefArgs" code + // should be ported to use adjustments + ty::set_default_mode(tcx, input.mode, ast::by_copy); + let is_by_ref = ty::resolved_mode(tcx, input.mode) == ast::by_ref; + if implicitly_ref_args || is_by_ref { + guarantor::for_by_ref(rcx, arg_exprs[i], callee_scope); + } + } + + // as loop above, but for receiver + for receiver.each |&r| { + constrain_regions_in_type_of_node( + rcx, r.id, callee_region, r.span); + if implicitly_ref_args { + guarantor::for_by_ref(rcx, r, callee_scope); + } + } + + // constrain regions that may appear in the return type to be + // valid for the function call: + constrain_regions_in_type( + rcx, callee_region, call_expr.span, fn_sig.output); } fn constrain_derefs(rcx: @mut Rcx, @@ -379,9 +500,8 @@ fn constrain_derefs(rcx: @mut Rcx, * pointer being derefenced, the lifetime of the pointer includes * the deref expr. */ - let tcx = rcx.fcx.tcx(); - let r_deref_expr = encl_region_or_static(rcx, deref_expr); + let r_deref_expr = ty::re_scope(deref_expr.id); for uint::range(0, derefs) |i| { debug!("constrain_derefs(deref_expr=%s, derefd_ty=%s, derefs=%?/%?", rcx.fcx.expr_to_str(deref_expr), @@ -390,19 +510,8 @@ fn constrain_derefs(rcx: @mut Rcx, match ty::get(derefd_ty).sty { ty::ty_rptr(r_ptr, _) => { - match rcx.fcx.mk_subr(true, deref_expr.span, r_deref_expr, r_ptr) { - result::Ok(*) => {} - result::Err(*) => { - tcx.sess.span_err( - deref_expr.span, - fmt!("dereference of reference outside its lifetime")); - note_and_explain_region( - tcx, - "the reference is only valid for ", - r_ptr, - ""); - } - } + mk_subregion_due_to_derefence(rcx, deref_expr.span, + r_deref_expr, r_ptr); } _ => {} @@ -417,6 +526,27 @@ fn constrain_derefs(rcx: @mut Rcx, } } +pub fn mk_subregion_due_to_derefence(rcx: @mut Rcx, + deref_span: span, + minimum_lifetime: ty::Region, + maximum_lifetime: ty::Region) { + match rcx.fcx.mk_subr(true, deref_span, + minimum_lifetime, maximum_lifetime) { + result::Ok(*) => {} + result::Err(*) => { + rcx.tcx().sess.span_err( + deref_span, + fmt!("dereference of reference outside its lifetime")); + note_and_explain_region( + rcx.tcx(), + "the reference is only valid for ", + maximum_lifetime, + ""); + } + } +} + + fn constrain_index(rcx: @mut Rcx, index_expr: @ast::expr, indexed_ty: ty::t) @@ -433,7 +563,7 @@ fn constrain_index(rcx: @mut Rcx, rcx.fcx.expr_to_str(index_expr), rcx.fcx.infcx().ty_to_str(indexed_ty)); - let r_index_expr = encl_region_or_static(rcx, index_expr); + let r_index_expr = ty::re_scope(index_expr.id); match ty::get(indexed_ty).sty { ty::ty_estr(ty::vstore_slice(r_ptr)) | ty::ty_evec(_, ty::vstore_slice(r_ptr)) => { @@ -456,66 +586,22 @@ fn constrain_index(rcx: @mut Rcx, } } -fn constrain_auto_ref(rcx: @mut Rcx, expr: @ast::expr) { - /*! - * - * If `expr` is auto-ref'd (e.g., as part of a borrow), then this - * function ensures that the lifetime of the resulting borrowed - * ptr includes at least the expression `expr`. */ - - debug!("constrain_auto_ref(expr=%s)", rcx.fcx.expr_to_str(expr)); - - let adjustment = rcx.fcx.inh.adjustments.find(&expr.id); - let region = match adjustment { - Some(&@ty::AutoDerefRef( - ty::AutoDerefRef { - autoref: Some(ref auto_ref), _})) => { - auto_ref.region - } - _ => { return; } - }; - - let tcx = rcx.fcx.tcx(); - let encl_region = tcx.region_maps.encl_region(expr.id); - match rcx.fcx.mk_subr(true, expr.span, encl_region, region) { - result::Ok(()) => {} - result::Err(_) => { - // In practice, this cannot happen: `region` is always a - // region variable, and constraints on region variables - // are collected and then resolved later. However, I - // included the span_err() here (rather than, say, - // span_bug()) because it seemed more future-proof: if, - // for some reason, the code were to change so that in - // some cases `region` is not a region variable, then - // reporting an error would be the correct path. - tcx.sess.span_err( - expr.span, - ~"lifetime of borrowed pointer does not include \ - the expression being borrowed"); - note_and_explain_region( - tcx, - ~"lifetime of the borrowed pointer is", - region, - ~""); - rcx.errors_reported += 1; - } - } -} - -fn constrain_free_variables( - rcx: @mut Rcx, - region: ty::Region, - expr: @ast::expr) { +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. */ + * outlive the closure itself. + */ let tcx = rcx.fcx.ccx.tcx; + debug!("constrain_free_variables(%s, %s)", + region.repr(tcx), expr.repr(tcx)); for get_freevars(tcx, expr.id).each |freevar| { debug!("freevar def is %?", freevar.def); let def = freevar.def; let en_region = encl_region_of_def(rcx.fcx, def); + debug!("en_region = %s", en_region.repr(tcx)); match rcx.fcx.mk_subr(true, freevar.span, region, en_region) { result::Ok(()) => {} @@ -541,9 +627,13 @@ fn constrain_free_variables( fn constrain_regions_in_type_of_node( rcx: @mut Rcx, id: ast::node_id, - encl_region: ty::Region, + minimum_lifetime: ty::Region, span: span) -> bool { + //! Guarantees that any lifetimes which appear in the type of + //! the node `id` (after applying adjustments) are valid for at + //! least `minimum_lifetime` + let tcx = rcx.fcx.tcx(); // Try to resolve the type. If we encounter an error, then typeck @@ -553,22 +643,21 @@ fn constrain_regions_in_type_of_node( let adjustment = rcx.fcx.inh.adjustments.find(&id); let ty = ty::adjust_ty(tcx, span, ty0, adjustment); debug!("constrain_regions_in_type_of_node(\ - ty=%s, ty0=%s, id=%d, encl_region=%?, adjustment=%?)", + ty=%s, ty0=%s, id=%d, minimum_lifetime=%?, adjustment=%?)", ty_to_str(tcx, ty), ty_to_str(tcx, ty0), - id, encl_region, adjustment); - constrain_regions_in_type(rcx, encl_region, span, ty) + id, minimum_lifetime, adjustment); + constrain_regions_in_type(rcx, minimum_lifetime, span, ty) } fn constrain_regions_in_type( rcx: @mut Rcx, - encl_region: ty::Region, + minimum_lifetime: ty::Region, span: span, ty: ty::t) -> bool { /*! - * * Requires that any regions which appear in `ty` must be - * superregions of `encl_region`. Also enforces the constraint + * superregions of `minimum_lifetime`. Also enforces the constraint * that given a pointer type `&'r T`, T must not contain regions * that outlive 'r, as well as analogous constraints for other * lifetime'd types. @@ -583,11 +672,11 @@ fn constrain_regions_in_type( let e = rcx.errors_reported; let tcx = rcx.fcx.ccx.tcx; - debug!("constrain_regions_in_type(encl_region=%s, ty=%s)", - region_to_str(tcx, encl_region), + debug!("constrain_regions_in_type(minimum_lifetime=%s, ty=%s)", + region_to_str(tcx, minimum_lifetime), ty_to_str(tcx, ty)); - do relate_nested_regions(tcx, Some(encl_region), ty) |r_sub, r_sup| { + do relate_nested_regions(tcx, Some(minimum_lifetime), ty) |r_sub, r_sup| { debug!("relate(r_sub=%s, r_sup=%s)", region_to_str(tcx, r_sub), region_to_str(tcx, r_sup)); @@ -595,12 +684,12 @@ fn constrain_regions_in_type( if r_sup.is_bound() || r_sub.is_bound() { // a bound region is one which appears inside an fn type. // (e.g., the `&` in `fn(&T)`). Such regions need not be - // constrained by `encl_region` as they are placeholders + // constrained by `minimum_lifetime` as they are placeholders // for regions that are as-yet-unknown. } else { match rcx.fcx.mk_subr(true, span, r_sub, r_sup) { result::Err(_) => { - if r_sub == encl_region { + if r_sub == minimum_lifetime { tcx.sess.span_err( span, fmt!("reference is not valid outside of its lifetime")); @@ -639,7 +728,6 @@ fn constrain_regions_in_type( pub mod guarantor { /*! - * * The routines in this module are aiming to deal with the case * where a the contents of a borrowed pointer are re-borrowed. * Imagine you have a borrowed pointer `b` with lifetime L1 and @@ -686,6 +774,7 @@ pub mod guarantor { */ use middle::typeck::check::regionck::{Rcx, infallibly_mk_subr}; + use middle::typeck::check::regionck::mk_subregion_due_to_derefence; use middle::ty; use syntax::ast; use syntax::codemap::span; @@ -693,14 +782,12 @@ pub mod guarantor { 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). */ debug!("guarantor::for_addr_of(base=%s)", rcx.fcx.expr_to_str(base)); - let _i = ::util::common::indenter(); let guarantor = guarantor(rcx, base); link(rcx, expr.span, expr.id, guarantor); @@ -708,13 +795,14 @@ pub mod guarantor { 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). */ + debug!("regionck::for_match()"); let discr_guarantor = guarantor(rcx, discr); + debug!("discr_guarantor=%s", discr_guarantor.repr(rcx.tcx())); for arms.each |arm| { for arm.pats.each |pat| { link_ref_bindings_in_pat(rcx, *pat, discr_guarantor); @@ -727,7 +815,6 @@ pub mod guarantor { 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 @@ -736,30 +823,30 @@ pub mod guarantor { debug!("guarantor::for_autoref(expr=%s, autoref=%?)", rcx.fcx.expr_to_str(expr), autoref); - let _i = ::util::common::indenter(); 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.kind { - ty::AutoPtr => { + match *autoref { + ty::AutoPtr(r, _) => { // In this case, we are implicitly adding an `&`. - maybe_make_subregion(rcx, expr, autoref.region, - expr_ct.cat.guarantor); + maybe_make_subregion(rcx, expr, r, expr_ct.cat.guarantor); } - ty::AutoBorrowVec | - ty::AutoBorrowVecRef | - ty::AutoBorrowFn => { + ty::AutoBorrowVec(r, _) | + ty::AutoBorrowVecRef(r, _) | + ty::AutoBorrowFn(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, autoref.region, + maybe_make_subregion(rcx, expr, r, guarantor_of_deref(&expr_ct.cat)); } + + ty::AutoUnsafe(_) => {} } fn maybe_make_subregion( @@ -774,6 +861,28 @@ pub mod guarantor { } } + pub fn for_by_ref(rcx: @mut Rcx, + expr: @ast::expr, + callee_scope: ast::node_id) { + /*! + * 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=%s, callee_scope=%?)", + expr.repr(tcx), callee_scope); + let mut expr_cat = categorize(rcx, expr); + debug!("guarantor::for_by_ref(expr=%?, callee_scope=%?) category=%?", + expr.id, callee_scope, expr_cat); + let minimum_lifetime = ty::re_scope(callee_scope); + for expr_cat.guarantor.each |guarantor| { + mk_subregion_due_to_derefence(rcx, expr.span, + minimum_lifetime, *guarantor); + } + } + fn link( rcx: @mut Rcx, span: span, @@ -907,7 +1016,6 @@ pub mod guarantor { fn categorize(rcx: @mut Rcx, expr: @ast::expr) -> ExprCategorization { debug!("categorize(expr=%s)", rcx.fcx.expr_to_str(expr)); - let _i = ::util::common::indenter(); let mut expr_ct = categorize_unadjusted(rcx, expr); debug!("before adjustments, cat=%?", expr_ct.cat); @@ -928,12 +1036,24 @@ pub mod guarantor { expr_ct = apply_autoderefs( rcx, expr, adjustment.autoderefs, expr_ct); - for adjustment.autoref.each |autoref| { - // If there is an autoref, then the result of this - // expression will be some sort of borrowed pointer. - expr_ct.cat.guarantor = None; - expr_ct.cat.pointer = BorrowedPointer(autoref.region); - debug!("autoref, cat=%?", expr_ct.cat); + match adjustment.autoref { + 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)) => { + // If there is an autoref, then the result of this + // expression will be some sort of borrowed pointer. + expr_ct.cat.guarantor = None; + expr_ct.cat.pointer = BorrowedPointer(r); + debug!("autoref, cat=%?", expr_ct.cat); + } } } @@ -948,7 +1068,6 @@ pub mod guarantor { expr: @ast::expr) -> ExprCategorizationType { debug!("categorize_unadjusted(expr=%s)", rcx.fcx.expr_to_str(expr)); - let _i = ::util::common::indenter(); let guarantor = { if rcx.fcx.inh.method_map.contains_key(&expr.id) { @@ -1053,7 +1172,6 @@ pub mod guarantor { debug!("link_ref_bindings_in_pat(pat=%s, guarantor=%?)", rcx.fcx.pat_to_str(pat), guarantor); - let _i = ::util::common::indenter(); match pat.node { ast::pat_wild => {} @@ -1069,7 +1187,10 @@ pub mod guarantor { link_ref_bindings_in_pat(rcx, *p, guarantor); } } - ast::pat_enum(*) => {} + ast::pat_enum(_, None) => {} + ast::pat_enum(_, Some(ref pats)) => { + link_ref_bindings_in_pats(rcx, pats, guarantor); + } ast::pat_struct(_, ref fpats, _) => { for fpats.each |fpat| { link_ref_bindings_in_pat(rcx, fpat.pat, guarantor); diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs index f293893bc131f..cfbd012b7b7cd 100644 --- a/src/librustc/middle/typeck/check/regionmanip.rs +++ b/src/librustc/middle/typeck/check/regionmanip.rs @@ -87,7 +87,7 @@ pub fn replace_bound_regions_in_fn_sig( to_r: &fn(ty::bound_region) -> ty::Region, r: ty::Region) -> isr_alist { match r { - ty::re_free(*) | ty::re_static | ty::re_scope(_) | + ty::re_empty | ty::re_free(*) | ty::re_static | ty::re_scope(_) | ty::re_infer(_) => { isr } @@ -153,6 +153,7 @@ pub fn replace_bound_regions_in_fn_sig( } // Free regions like these just stay the same: + ty::re_empty | ty::re_static | ty::re_scope(_) | ty::re_free(*) | diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index d6b09d1e7f453..b7713eaa2fd6e 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -134,23 +134,22 @@ fn resolve_type_vars_for_node(wbcx: @mut WbCtxt, sp: span, id: ast::node_id) } Some(&@ty::AutoDerefRef(adj)) => { - let resolved_autoref = match adj.autoref { - Some(ref autoref) => { - match resolve_region(fcx.infcx(), autoref.region, - resolve_all | force_all) { - Err(e) => { - // This should not, I think, happen. - fcx.ccx.tcx.sess.span_err( - sp, fmt!("cannot resolve scope of borrow: %s", - infer::fixup_err_to_str(e))); - Some(*autoref) - } - Ok(r) => { - Some(ty::AutoRef {region: r, ..*autoref}) - } + let fixup_region = |r| { + match resolve_region(fcx.infcx(), r, resolve_all | force_all) { + Ok(r1) => r1, + Err(e) => { + // This should not, I think, happen. + fcx.ccx.tcx.sess.span_err( + sp, fmt!("cannot resolve scope of borrow: %s", + infer::fixup_err_to_str(e))); + r } } - None => None + }; + + let resolved_autoref = match adj.autoref { + None => None, + Some(ref r) => Some(r.map_region(fixup_region)) }; let resolved_adj = @ty::AutoDerefRef(ty::AutoDerefRef { diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index 05b2f6f577b82..573e4bd579011 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -393,7 +393,7 @@ pub impl CoherenceChecker { let pmm = self.crate_context.tcx.provided_methods; match pmm.find(&local_def(impl_id)) { - Some(mis) => { + Some(&mis) => { // If the trait already has an entry in the // provided_methods_map, we just need to add this // method to that entry. @@ -426,8 +426,8 @@ pub impl CoherenceChecker { self.crate_context.coherence_info.inherent_methods .insert(base_def_id, implementation_list); } - Some(existing_implementation_list) => { - implementation_list = *existing_implementation_list; + Some(&existing_implementation_list) => { + implementation_list = existing_implementation_list; } } @@ -443,8 +443,8 @@ pub impl CoherenceChecker { self.crate_context.coherence_info.extension_methods .insert(trait_id, implementation_list); } - Some(existing_implementation_list) => { - implementation_list = *existing_implementation_list; + Some(&existing_implementation_list) => { + implementation_list = existing_implementation_list; } } @@ -507,7 +507,7 @@ pub impl CoherenceChecker { m.insert(self_t, the_impl); self.crate_context.tcx.trait_impls.insert(trait_t, m); } - Some(m) => { + Some(&m) => { m.insert(self_t, the_impl); } } diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs index dcd1c861540f4..3620b609edf3b 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -65,7 +65,7 @@ we may want to adjust precisely when coercions occur. */ use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowFn}; -use middle::ty::{AutoDerefRef, AutoRef}; +use middle::ty::{AutoDerefRef}; use middle::ty::{vstore_slice, vstore_box, vstore_uniq}; use middle::ty::{mt}; use middle::ty; @@ -120,9 +120,9 @@ pub impl Coerce { }; } - ty::ty_ptr(_) => { + ty::ty_ptr(mt_b) => { return do self.unpack_actual_value(a) |sty_a| { - self.coerce_unsafe_ptr(a, sty_a, b) + self.coerce_unsafe_ptr(a, sty_a, b, mt_b) }; } @@ -205,11 +205,7 @@ pub impl Coerce { if_ok!(sub.tys(a_borrowed, b)); Ok(Some(@AutoDerefRef(AutoDerefRef { autoderefs: 1, - autoref: Some(AutoRef { - kind: AutoPtr, - region: r_borrow, - mutbl: mt_b.mutbl - }) + autoref: Some(AutoPtr(r_borrow, mt_b.mutbl)) }))) } @@ -235,11 +231,7 @@ pub impl Coerce { if_ok!(self.subtype(a_borrowed, b)); Ok(Some(@AutoDerefRef(AutoDerefRef { autoderefs: 0, - autoref: Some(AutoRef { - kind: AutoBorrowVec, - region: r_a, - mutbl: m_imm - }) + autoref: Some(AutoBorrowVec(r_a, m_imm)) }))) } @@ -268,11 +260,7 @@ pub impl Coerce { if_ok!(sub.tys(a_borrowed, b)); Ok(Some(@AutoDerefRef(AutoDerefRef { autoderefs: 0, - autoref: Some(AutoRef { - kind: AutoBorrowVec, - region: r_borrow, - mutbl: mt_b.mutbl - }) + autoref: Some(AutoBorrowVec(r_borrow, mt_b.mutbl)) }))) } @@ -308,11 +296,7 @@ pub impl Coerce { if_ok!(self.subtype(a_borrowed, b)); Ok(Some(@AutoDerefRef(AutoDerefRef { autoderefs: 0, - autoref: Some(AutoRef { - kind: AutoBorrowFn, - region: r_borrow, - mutbl: m_imm - }) + autoref: Some(AutoBorrowFn(r_borrow)) }))) } @@ -363,7 +347,8 @@ pub impl Coerce { fn coerce_unsafe_ptr(&self, a: ty::t, sty_a: &ty::sty, - b: ty::t) -> CoerceResult + b: ty::t, + mt_b: ty::mt) -> CoerceResult { debug!("coerce_unsafe_ptr(a=%s, sty_a=%?, b=%s)", a.inf_str(self.infcx), sty_a, @@ -376,10 +361,17 @@ pub impl Coerce { } }; - // borrowed pointers and unsafe pointers have the same - // representation, so just check that the types which they - // point at are compatible: + // check that the types which they point at are compatible let a_unsafe = ty::mk_ptr(self.infcx.tcx, mt_a); - self.subtype(a_unsafe, b) + if_ok!(self.subtype(a_unsafe, b)); + + // although borrowed ptrs and unsafe ptrs have the same + // representation, we still register an AutoDerefRef so that + // regionck knows that that the region for `a` must be valid + // here + Ok(Some(@AutoDerefRef(AutoDerefRef { + autoderefs: 1, + autoref: Some(ty::AutoUnsafe(mt_b.mutbl)) + }))) } } diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 7b5a93d4cad88..4491b04b382ec 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -339,7 +339,7 @@ pub fn fixup_err_to_str(f: fixup_err) -> ~str { fn new_ValsAndBindings() -> ValsAndBindings { ValsAndBindings { - vals: @mut SmallIntMap::new(), + vals: SmallIntMap::new(), bindings: ~[] } } @@ -469,28 +469,6 @@ pub fn resolve_region(cx: @mut InferCtxt, r: ty::Region, modes: uint) resolver.resolve_region_chk(r) } -/* -fn resolve_borrowings(cx: @mut InferCtxt) { - for cx.borrowings.each |item| { - match resolve_region(cx, item.scope, resolve_all|force_all) { - Ok(region) => { - debug!("borrowing for expr %d resolved to region %?, mutbl %?", - item.expr_id, region, item.mutbl); - cx.tcx.borrowings.insert( - item.expr_id, {region: region, mutbl: item.mutbl}); - } - - Err(e) => { - let str = fixup_err_to_str(e); - cx.tcx.sess.span_err( - item.span, - fmt!("could not resolve lifetime for borrow: %s", str)); - } - } - } -} -*/ - trait then { fn then(&self, f: &fn() -> Result) -> Result; @@ -554,7 +532,8 @@ struct Snapshot { } pub impl InferCtxt { - fn combine_fields(@mut self, a_is_expected: bool, + fn combine_fields(@mut self, + a_is_expected: bool, span: span) -> CombineFields { CombineFields {infcx: self, a_is_expected: a_is_expected, @@ -565,25 +544,24 @@ pub impl InferCtxt { Sub(self.combine_fields(a_is_expected, span)) } - fn in_snapshot(@mut self) -> bool { + fn in_snapshot(&self) -> bool { self.region_vars.in_snapshot() } - fn start_snapshot(@mut self) -> Snapshot { - let this = &mut *self; + fn start_snapshot(&mut self) -> Snapshot { Snapshot { ty_var_bindings_len: - this.ty_var_bindings.bindings.len(), + self.ty_var_bindings.bindings.len(), int_var_bindings_len: - this.int_var_bindings.bindings.len(), + self.int_var_bindings.bindings.len(), float_var_bindings_len: - this.float_var_bindings.bindings.len(), + self.float_var_bindings.bindings.len(), region_vars_snapshot: - this.region_vars.start_snapshot(), + self.region_vars.start_snapshot(), } } - fn rollback_to(@mut self, snapshot: &Snapshot) { + fn rollback_to(&mut self, snapshot: &Snapshot) { debug!("rollback!"); rollback_to(&mut self.ty_var_bindings, snapshot.ty_var_bindings_len); @@ -596,7 +574,7 @@ pub impl InferCtxt { } /// Execute `f` and commit the bindings if successful - fn commit(@mut self, f: &fn() -> Result) -> Result { + fn commit(&mut self, f: &fn() -> Result) -> Result { assert!(!self.in_snapshot()); debug!("commit()"); @@ -611,7 +589,7 @@ pub impl InferCtxt { } /// Execute `f`, unroll bindings on failure - fn try(@mut self, f: &fn() -> Result) -> Result { + fn try(&mut self, f: &fn() -> Result) -> Result { debug!("try()"); do indent { let snapshot = self.start_snapshot(); @@ -625,7 +603,7 @@ pub impl InferCtxt { } /// Execute `f` then unroll any bindings it creates - fn probe(@mut self, f: &fn() -> Result) -> Result { + fn probe(&mut self, f: &fn() -> Result) -> Result { debug!("probe()"); do indent { let snapshot = self.start_snapshot(); @@ -647,45 +625,47 @@ fn next_simple_var( } pub impl InferCtxt { - fn next_ty_var_id(@mut self) -> TyVid { + fn next_ty_var_id(&mut self) -> TyVid { let id = self.ty_var_counter; self.ty_var_counter += 1; - let vals = self.ty_var_bindings.vals; - vals.insert(id, Root(Bounds { lb: None, ub: None }, 0u)); + { + let vals = &mut self.ty_var_bindings.vals; + vals.insert(id, Root(Bounds { lb: None, ub: None }, 0u)); + } return TyVid(id); } - fn next_ty_var(@mut self) -> ty::t { + fn next_ty_var(&mut self) -> ty::t { ty::mk_var(self.tcx, self.next_ty_var_id()) } - fn next_ty_vars(@mut self, n: uint) -> ~[ty::t] { + fn next_ty_vars(&mut self, n: uint) -> ~[ty::t] { vec::from_fn(n, |_i| self.next_ty_var()) } - fn next_int_var_id(@mut self) -> IntVid { + fn next_int_var_id(&mut self) -> IntVid { IntVid(next_simple_var(&mut self.int_var_counter, &mut self.int_var_bindings)) } - fn next_int_var(@mut self) -> ty::t { + fn next_int_var(&mut self) -> ty::t { ty::mk_int_var(self.tcx, self.next_int_var_id()) } - fn next_float_var_id(@mut self) -> FloatVid { + fn next_float_var_id(&mut self) -> FloatVid { FloatVid(next_simple_var(&mut self.float_var_counter, &mut self.float_var_bindings)) } - fn next_float_var(@mut self) -> ty::t { + fn next_float_var(&mut self) -> ty::t { ty::mk_float_var(self.tcx, self.next_float_var_id()) } - fn next_region_var_nb(@mut self, span: span) -> ty::Region { + fn next_region_var_nb(&mut self, span: span) -> ty::Region { ty::re_infer(ty::ReVar(self.region_vars.new_region_var(span))) } - fn next_region_var_with_lb(@mut self, span: span, + fn next_region_var_with_lb(&mut self, span: span, lb_region: ty::Region) -> ty::Region { let region_var = self.next_region_var_nb(span); @@ -697,12 +677,12 @@ pub impl InferCtxt { return region_var; } - fn next_region_var(@mut self, span: span, scope_id: ast::node_id) + fn next_region_var(&mut self, span: span, scope_id: ast::node_id) -> ty::Region { self.next_region_var_with_lb(span, ty::re_scope(scope_id)) } - fn resolve_regions(@mut self) { + fn resolve_regions(&mut self) { self.region_vars.resolve_regions(); } @@ -722,7 +702,6 @@ pub impl InferCtxt { result::Err(_) => typ } } - fn resolve_type_vars_in_trait_ref_if_possible(@mut self, trait_ref: &ty::TraitRef) -> ty::TraitRef @@ -786,7 +765,7 @@ pub impl InferCtxt { self.type_error_message(sp, mk_msg, a, Some(err)); } - fn replace_bound_regions_with_fresh_regions(@mut self, + fn replace_bound_regions_with_fresh_regions(&mut self, span: span, fsig: &ty::FnSig) -> (ty::FnSig, isr_alist) { @@ -806,7 +785,7 @@ pub impl InferCtxt { } fn fold_regions_in_sig( - @mut self, + &mut self, fn_sig: &ty::FnSig, fldr: &fn(r: ty::Region, in_fn: bool) -> ty::Region) -> ty::FnSig { diff --git a/src/librustc/middle/typeck/infer/region_inference.rs b/src/librustc/middle/typeck/infer/region_inference.rs index e12a3f2e97522..0761ad5c7b819 100644 --- a/src/librustc/middle/typeck/infer/region_inference.rs +++ b/src/librustc/middle/typeck/infer/region_inference.rs @@ -24,7 +24,7 @@ it's worth spending more time on a more involved analysis. Moreover, regions are a simpler case than types: they don't have aggregate structure, for example. -Unlike normal type inference, which is similar in spirit H-M and thus +Unlike normal type inference, which is similar in spirit to H-M and thus works progressively, the region type inference works by accumulating constraints over the course of a function. Finally, at the end of processing a function, we process and solve the constraints all at @@ -130,7 +130,7 @@ of these variables can effectively be unified into a single variable. Once SCCs are removed, we are left with a DAG. At this point, we can walk the DAG in toplogical order once to compute the expanding nodes, and again in reverse topological order to compute the contracting -nodes.The main reason I did not write it this way is that I did not +nodes. The main reason I did not write it this way is that I did not feel like implementing the SCC and toplogical sort algorithms at the moment. @@ -538,7 +538,7 @@ more convincing in the future. use middle::ty; use middle::ty::{FreeRegion, Region, RegionVid}; -use middle::ty::{re_static, re_infer, re_free, re_bound}; +use middle::ty::{re_empty, re_static, re_infer, re_free, re_bound}; use middle::ty::{re_scope, ReVar, ReSkolemized, br_fresh}; use middle::typeck::infer::cres; use util::common::indenter; @@ -547,6 +547,9 @@ use util::ppaux::note_and_explain_region; use core::cell::{Cell, empty_cell}; use core::hashmap::{HashMap, HashSet}; use core::to_bytes; +use core::uint; +use core::vec; +use core; use syntax::codemap::span; use syntax::ast; @@ -572,18 +575,12 @@ impl to_bytes::IterBytes for Constraint { } } -#[deriving(Eq)] +#[deriving(Eq, IterBytes)] struct TwoRegions { a: Region, b: Region, } -impl to_bytes::IterBytes for TwoRegions { - fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) { - to_bytes::iter_bytes_2(&self.a, &self.b, lsb0, f) - } -} - enum UndoLogEntry { Snapshot, AddVar(RegionVid), @@ -637,7 +634,7 @@ pub fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings { } pub impl RegionVarBindings { - fn in_snapshot(&mut self) -> bool { + fn in_snapshot(&self) -> bool { self.undo_log.len() > 0 } @@ -832,7 +829,6 @@ pub impl RegionVarBindings { } fn resolve_var(&mut self, rid: RegionVid) -> ty::Region { - debug!("RegionVarBindings: resolve_var(%?=%u)", rid, rid.to_uint()); if self.values.is_empty() { self.tcx.sess.span_bug( self.var_spans[rid.to_uint()], @@ -841,29 +837,14 @@ pub impl RegionVarBindings { } let v = self.values.with_ref(|values| values[rid.to_uint()]); + debug!("RegionVarBindings: resolve_var(%?=%u)=%?", + rid, rid.to_uint(), v); match v { Value(r) => r, NoValue => { - // No constraints, report an error. It is plausible - // that we could select an arbitrary region here - // instead. At the moment I am not doing this because - // this generally masks bugs in the inference - // algorithm, and given our syntax one cannot create - // generally create a lifetime variable that isn't - // used in some type, and hence all lifetime variables - // should ultimately have some bounds. - - self.tcx.sess.span_err( - self.var_spans[rid.to_uint()], - fmt!("Unconstrained region variable #%u", rid.to_uint())); - - // Touch of a hack: to suppress duplicate messages, - // replace the NoValue entry with ErrorValue. - let mut values = self.values.take(); - values[rid.to_uint()] = ErrorValue; - self.values.put_back(values); - re_static + // No constraints, return ty::re_empty + re_empty } ErrorValue => { @@ -1031,6 +1012,10 @@ priv impl RegionVarBindings { re_static // nothing lives longer than static } + (re_empty, r) | (r, re_empty) => { + r // everything lives longer than empty + } + (re_infer(ReVar(v_id)), _) | (_, re_infer(ReVar(v_id))) => { self.tcx.sess.span_bug( self.var_spans[v_id.to_uint()], @@ -1127,6 +1112,11 @@ priv impl RegionVarBindings { Ok(r) } + (re_empty, _) | (_, re_empty) => { + // nothing lives shorter than everything else + Ok(re_empty) + } + (re_infer(ReVar(v_id)), _) | (_, re_infer(ReVar(v_id))) => { self.tcx.sess.span_bug( @@ -1266,8 +1256,6 @@ struct SpannedRegion { span: span, } -type TwoRegionsMap = HashSet; - pub impl RegionVarBindings { fn infer_variable_values(&mut self) -> ~[GraphNodeValue] { let mut graph = self.construct_graph(); @@ -1329,11 +1317,15 @@ pub impl RegionVarBindings { node_id: RegionVid, edge_dir: Direction, edge_idx: uint) { + //! Insert edge `edge_idx` on the link list of edges in direction + //! `edge_dir` for the node `node_id` let edge_dir = edge_dir as uint; - graph.edges[edge_idx].next_edge[edge_dir] = - graph.nodes[node_id.to_uint()].head_edge[edge_dir]; - graph.nodes[node_id.to_uint()].head_edge[edge_dir] = - edge_idx; + assert_eq!(graph.edges[edge_idx].next_edge[edge_dir], + uint::max_value); + let n = node_id.to_uint(); + let prev_head = graph.nodes[n].head_edge[edge_dir]; + graph.edges[edge_idx].next_edge[edge_dir] = prev_head; + graph.nodes[n].head_edge[edge_dir] = edge_idx; } } @@ -1484,6 +1476,8 @@ pub impl RegionVarBindings { } } Err(_) => { + debug!("Setting %? to ErrorValue: no glb of %?, %?", + a_vid, a_region, b_region); a_node.value = ErrorValue; false } @@ -1495,7 +1489,21 @@ pub impl RegionVarBindings { &mut self, graph: &Graph) -> ~[GraphNodeValue] { - let mut dup_map = HashSet::new(); + debug!("extract_values_and_report_conflicts()"); + + // This is the best way that I have found to suppress + // duplicate and related errors. Basically we keep a set of + // flags for every node. Whenever an error occurs, we will + // walk some portion of the graph looking to find pairs of + // conflicting regions to report to the user. As we walk, we + // trip the flags from false to true, and if we find that + // we've already reported an error involving any particular + // node we just stop and don't report the current error. The + // idea is to report errors that derive from independent + // regions of the graph, but not those that derive from + // overlapping locations. + let mut dup_vec = graph.nodes.map(|_| uint::max_value); + graph.nodes.mapi(|idx, node| { match node.value { Value(_) => { @@ -1530,15 +1538,16 @@ pub impl RegionVarBindings { that is not used is not a problem, so if this rule starts to create problems we'll have to revisit this portion of the code and think hard about it. =) */ + let node_vid = RegionVid { id: idx }; match node.classification { Expanding => { self.report_error_for_expanding_node( - graph, &mut dup_map, node_vid); + graph, dup_vec, node_vid); } Contracting => { self.report_error_for_contracting_node( - graph, &mut dup_map, node_vid); + graph, dup_vec, node_vid); } } } @@ -1548,38 +1557,26 @@ pub impl RegionVarBindings { }) } - // Used to suppress reporting the same basic error over and over - fn is_reported(&mut self, - dup_map: &mut TwoRegionsMap, - r_a: Region, - r_b: Region) - -> bool { - let key = TwoRegions { a: r_a, b: r_b }; - !dup_map.insert(key) - } - fn report_error_for_expanding_node(&mut self, graph: &Graph, - dup_map: &mut TwoRegionsMap, + dup_vec: &mut [uint], node_idx: RegionVid) { // Errors in expanding nodes result from a lower-bound that is // not contained by an upper-bound. - let lower_bounds = - self.collect_concrete_regions(graph, node_idx, Incoming); - let upper_bounds = - self.collect_concrete_regions(graph, node_idx, Outgoing); + let (lower_bounds, lower_dup) = + self.collect_concrete_regions(graph, node_idx, Incoming, dup_vec); + let (upper_bounds, upper_dup) = + self.collect_concrete_regions(graph, node_idx, Outgoing, dup_vec); + + if lower_dup || upper_dup { + return; + } for vec::each(lower_bounds) |lower_bound| { for vec::each(upper_bounds) |upper_bound| { if !self.is_subregion_of(lower_bound.region, upper_bound.region) { - if self.is_reported(dup_map, - lower_bound.region, - upper_bound.region) { - return; - } - self.tcx.sess.span_err( self.var_spans[node_idx.to_uint()], fmt!("cannot infer an appropriate lifetime \ @@ -1609,16 +1606,28 @@ pub impl RegionVarBindings { } } } + + self.tcx.sess.span_bug( + self.var_spans[node_idx.to_uint()], + fmt!("report_error_for_expanding_node() could not find error \ + for var %?, lower_bounds=%s, upper_bounds=%s", + node_idx, + lower_bounds.map(|x| x.region).repr(self.tcx), + upper_bounds.map(|x| x.region).repr(self.tcx))); } fn report_error_for_contracting_node(&mut self, graph: &Graph, - dup_map: &mut TwoRegionsMap, + dup_vec: &mut [uint], node_idx: RegionVid) { // Errors in contracting nodes result from two upper-bounds // that have no intersection. - let upper_bounds = self.collect_concrete_regions(graph, node_idx, - Outgoing); + let (upper_bounds, dup_found) = + self.collect_concrete_regions(graph, node_idx, Outgoing, dup_vec); + + if dup_found { + return; + } for vec::each(upper_bounds) |upper_bound_1| { for vec::each(upper_bounds) |upper_bound_2| { @@ -1627,12 +1636,6 @@ pub impl RegionVarBindings { Ok(_) => {} Err(_) => { - if self.is_reported(dup_map, - upper_bound_1.region, - upper_bound_2.region) { - return; - } - self.tcx.sess.span_err( self.var_spans[node_idx.to_uint()], fmt!("cannot infer an appropriate lifetime \ @@ -1663,50 +1666,94 @@ pub impl RegionVarBindings { } } } + + self.tcx.sess.span_bug( + self.var_spans[node_idx.to_uint()], + fmt!("report_error_for_contracting_node() could not find error \ + for var %?, upper_bounds=%s", + node_idx, + upper_bounds.map(|x| x.region).repr(self.tcx))); } fn collect_concrete_regions(&mut self, graph: &Graph, orig_node_idx: RegionVid, - dir: Direction) - -> ~[SpannedRegion] { - let mut set = HashSet::new(); - let mut stack = ~[orig_node_idx]; - set.insert(orig_node_idx.to_uint()); - let mut result = ~[]; - while !vec::is_empty(stack) { - let node_idx = stack.pop(); - for self.each_edge(graph, node_idx, dir) |edge| { + dir: Direction, + dup_vec: &mut [uint]) + -> (~[SpannedRegion], bool) { + struct WalkState { + set: HashSet, + stack: ~[RegionVid], + result: ~[SpannedRegion], + dup_found: bool + } + let mut state = WalkState { + set: HashSet::new(), + stack: ~[orig_node_idx], + result: ~[], + dup_found: false + }; + state.set.insert(orig_node_idx); + + // to start off the process, walk the source node in the + // direction specified + process_edges(self, &mut state, graph, orig_node_idx, dir); + + while !state.stack.is_empty() { + let node_idx = state.stack.pop(); + let classification = graph.nodes[node_idx.to_uint()].classification; + + // check whether we've visited this node on some previous walk + if dup_vec[node_idx.to_uint()] == uint::max_value { + dup_vec[node_idx.to_uint()] = orig_node_idx.to_uint(); + } else if dup_vec[node_idx.to_uint()] != orig_node_idx.to_uint() { + state.dup_found = true; + } + + debug!("collect_concrete_regions(orig_node_idx=%?, node_idx=%?, \ + classification=%?)", + orig_node_idx, node_idx, classification); + + // figure out the direction from which this node takes its + // values, and search for concrete regions etc in that direction + let dir = match classification { + Expanding => Incoming, + Contracting => Outgoing + }; + + process_edges(self, &mut state, graph, node_idx, dir); + } + + let WalkState {result, dup_found, _} = state; + return (result, dup_found); + + fn process_edges(self: &mut RegionVarBindings, + state: &mut WalkState, + graph: &Graph, + source_vid: RegionVid, + dir: Direction) { + debug!("process_edges(source_vid=%?, dir=%?)", source_vid, dir); + + for self.each_edge(graph, source_vid, dir) |edge| { match edge.constraint { - ConstrainVarSubVar(from_vid, to_vid) => { - let vid = match dir { - Incoming => from_vid, - Outgoing => to_vid - }; - if set.insert(vid.to_uint()) { - stack.push(vid); + ConstrainVarSubVar(from_vid, to_vid) => { + let opp_vid = + if from_vid == source_vid {to_vid} else {from_vid}; + if state.set.insert(opp_vid) { + state.stack.push(opp_vid); + } } - } - - ConstrainRegSubVar(region, _) => { - assert!(dir == Incoming); - result.push(SpannedRegion { - region: region, - span: edge.span - }); - } - ConstrainVarSubReg(_, region) => { - assert!(dir == Outgoing); - result.push(SpannedRegion { - region: region, - span: edge.span - }); - } + ConstrainRegSubVar(region, _) | + ConstrainVarSubReg(_, region) => { + state.result.push(SpannedRegion { + region: region, + span: edge.span + }); + } } } } - return result; } fn each_edge(&mut self, diff --git a/src/librustc/middle/typeck/infer/unify.rs b/src/librustc/middle/typeck/infer/unify.rs index bc13074422450..8db4774322a05 100644 --- a/src/librustc/middle/typeck/infer/unify.rs +++ b/src/librustc/middle/typeck/infer/unify.rs @@ -23,7 +23,7 @@ pub enum VarValue { } pub struct ValsAndBindings { - vals: @mut SmallIntMap>, + vals: SmallIntMap>, bindings: ~[(V, VarValue)], } @@ -60,26 +60,25 @@ pub impl InferCtxt { vid: V) -> Node { let vid_u = vid.to_uint(); - match vb.vals.find(&vid_u) { + let var_val = match vb.vals.find(&vid_u) { + Some(&var_val) => var_val, None => { tcx.sess.bug(fmt!( "failed lookup of vid `%u`", vid_u)); } - Some(var_val) => { - match *var_val { - Redirect(vid) => { - let node: Node = helper(tcx, vb, vid); - if node.root != vid { - // Path compression - vb.vals.insert(vid.to_uint(), - Redirect(node.root)); - } - node - } - Root(ref pt, rk) => { - Node {root: vid, possible_types: *pt, rank: rk} - } + }; + match var_val { + Redirect(vid) => { + let node: Node = helper(tcx, vb, vid); + if node.root != vid { + // Path compression + vb.vals.insert(vid.to_uint(), + Redirect(node.root)); } + node + } + Root(pt, rk) => { + Node {root: vid, possible_types: pt, rank: rk} } } } @@ -99,8 +98,8 @@ pub impl InferCtxt { { // FIXME(#4903)---borrow checker is not flow sensitive let vb = UnifyVid::appropriate_vals_and_bindings(self); - let old_v = vb.vals.get(&vid.to_uint()); - vb.bindings.push((vid, *old_v)); + let old_v = { *vb.vals.get(&vid.to_uint()) }; // FIXME(#4903) + vb.bindings.push((vid, old_v)); vb.vals.insert(vid.to_uint(), new_v); } } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index aa8c3f8fd1b7e..d99d87231bece 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -65,6 +65,9 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region) Some(&ast_map::node_block(ref blk)) => { explain_span(cx, "block", blk.span) } + Some(&ast_map::node_callee_scope(expr)) => { + explain_span(cx, "callee", expr.span) + } Some(&ast_map::node_expr(expr)) => { match expr.node { ast::expr_call(*) => explain_span(cx, "call", expr.span), @@ -113,6 +116,8 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region) re_static => { (~"the static lifetime", None) } + re_empty => { (~"the empty lifetime", None) } + // I believe these cases should not occur (except when debugging, // perhaps) re_infer(_) | re_bound(_) => { @@ -212,7 +217,8 @@ pub fn region_to_str_space(cx: ctxt, prefix: &str, region: Region) -> ~str { bound_region_to_str_space(cx, prefix, br) } re_infer(ReVar(_)) => prefix.to_str(), - re_static => fmt!("%s'static ", prefix) + re_static => fmt!("%s'static ", prefix), + re_empty => fmt!("%s' ", prefix) } } @@ -740,6 +746,15 @@ impl Repr for ty::vstore { } } +impl Repr for ast_map::path_elt { + fn repr(&self, tcx: ctxt) -> ~str { + match *self { + ast_map::path_mod(id) => id.repr(tcx), + ast_map::path_name(id) => id.repr(tcx) + } + } +} + // Local Variables: // mode: rust // fill-column: 78; diff --git a/src/libstd/arc.rs b/src/libstd/arc.rs index f45fb4e765833..027bf93b4814f 100644 --- a/src/libstd/arc.rs +++ b/src/libstd/arc.rs @@ -419,26 +419,26 @@ pub struct RWReadMode<'self, T> { pub impl<'self, T:Const + Owned> RWWriteMode<'self, T> { /// Access the pre-downgrade RWARC in write mode. - fn write(&self, blk: &fn(x: &mut T) -> U) -> U { + fn write(&mut self, blk: &fn(x: &mut T) -> U) -> U { match *self { RWWriteMode { - data: ref data, + data: &ref mut data, token: ref token, poison: _ } => { do token.write { - blk(&mut **data) + blk(data) } } } } /// Access the pre-downgrade RWARC in write mode with a condvar. - fn write_cond<'x, 'c, U>(&self, + fn write_cond<'x, 'c, U>(&mut self, blk: &fn(x: &'x mut T, c: &'c Condvar) -> U) -> U { match *self { RWWriteMode { - data: ref data, + data: &ref mut data, token: ref token, poison: ref poison } => { @@ -449,7 +449,7 @@ pub impl<'self, T:Const + Owned> RWWriteMode<'self, T> { failed: &mut *poison.failed, cond: cond }; - blk(&mut **data, &cvar) + blk(data, &cvar) } } } diff --git a/src/libstd/bitv.rs b/src/libstd/bitv.rs index 5314c35419cc5..d48d7af354b41 100644 --- a/src/libstd/bitv.rs +++ b/src/libstd/bitv.rs @@ -215,16 +215,16 @@ pub struct Bitv { nbits: uint } -priv impl Bitv { +fn die() -> ! { + fail!(~"Tried to do operation on bit vectors with different sizes"); +} - fn die(&self) -> ! { - fail!(~"Tried to do operation on bit vectors with different sizes"); - } +priv impl Bitv { #[inline(always)] fn do_op(&mut self, op: Op, other: &Bitv) -> bool { if self.nbits != other.nbits { - self.die(); + die(); } match self.rep { Small(ref mut s) => match other.rep { @@ -234,10 +234,10 @@ priv impl Bitv { Assign => s.become(*s1, self.nbits), Difference => s.difference(*s1, self.nbits) }, - Big(_) => self.die() + Big(_) => die() }, Big(ref mut s) => match other.rep { - Small(_) => self.die(), + Small(_) => die(), Big(ref s1) => match op { Union => s.union(*s1, self.nbits), Intersect => s.intersect(*s1, self.nbits), diff --git a/src/libstd/net_tcp.rs b/src/libstd/net_tcp.rs index 764152d6812c5..ec4c025180c7f 100644 --- a/src/libstd/net_tcp.rs +++ b/src/libstd/net_tcp.rs @@ -885,8 +885,8 @@ impl io::Reader for TcpSocketBuf { let ncopy = uint::min(nbuffered, needed); let dst = ptr::mut_offset( vec::raw::to_mut_ptr(buf), count); - let src = ptr::const_offset( - vec::raw::to_const_ptr(self.data.buf), + let src = ptr::offset( + vec::raw::to_ptr(self.data.buf), self.data.buf_off); ptr::copy_memory(dst, src, ncopy); self.data.buf_off += ncopy; @@ -969,7 +969,7 @@ impl io::Reader for TcpSocketBuf { /// Implementation of `io::Reader` trait for a buffered `net::tcp::TcpSocket` impl io::Writer for TcpSocketBuf { - pub fn write(&self, data: &const [u8]) { + pub fn write(&self, data: &[u8]) { unsafe { let socket_data_ptr: *TcpSocketData = &(*((*(self.data)).sock).socket_data); diff --git a/src/libstd/serialize.rs b/src/libstd/serialize.rs index 032df4c819cdd..29d108e3ac2b1 100644 --- a/src/libstd/serialize.rs +++ b/src/libstd/serialize.rs @@ -20,9 +20,6 @@ use core::hashmap::{HashMap, HashSet}; use core::trie::{TrieMap, TrieSet}; use deque::Deque; use dlist::DList; -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] use treemap::{TreeMap, TreeSet}; pub trait Encoder { @@ -730,9 +727,6 @@ impl Decodable for TrieSet { } } -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl< E: Encoder, K: Encodable + Eq + TotalOrd, @@ -750,9 +744,6 @@ impl< } } -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl< D: Decoder, K: Decodable + Eq + TotalOrd, @@ -771,9 +762,6 @@ impl< } } -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl< S: Encoder, T: Encodable + Eq + TotalOrd @@ -789,9 +777,6 @@ impl< } } -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] impl< D: Decoder, T: Decodable + Eq + TotalOrd diff --git a/src/libstd/sort.rs b/src/libstd/sort.rs index 3e6011e80df81..c153d7f22c034 100644 --- a/src/libstd/sort.rs +++ b/src/libstd/sort.rs @@ -61,6 +61,7 @@ pub fn merge_sort(v: &const [T], le: Le) -> ~[T] { } } +#[cfg(stage0)] fn part(arr: &mut [T], left: uint, right: uint, pivot: uint, compare_func: Le) -> uint { arr[pivot] <-> arr[right]; @@ -79,6 +80,23 @@ fn part(arr: &mut [T], left: uint, return storage_index; } +#[cfg(not(stage0))] +fn part(arr: &mut [T], left: uint, + right: uint, pivot: uint, compare_func: Le) -> uint { + arr[pivot] <-> arr[right]; + let mut storage_index: uint = left; + let mut i: uint = left; + while i < right { + if compare_func(&arr[i], &arr[right]) { + arr[i] <-> arr[storage_index]; + storage_index += 1; + } + i += 1; + } + arr[storage_index] <-> arr[right]; + return storage_index; +} + fn qsort(arr: &mut [T], left: uint, right: uint, compare_func: Le) { if right > left { @@ -162,7 +180,8 @@ fn qsort3(arr: &mut [T], left: int, right: int) { */ pub fn quick_sort3(arr: &mut [T]) { if arr.len() <= 1 { return; } - qsort3(arr, 0, (arr.len() - 1) as int); + let len = arr.len() - 1; // FIXME(#5074) nested calls + qsort3(arr, 0, (len - 1) as int); } pub trait Sort { @@ -195,15 +214,20 @@ pub fn tim_sort(array: &mut [T]) { let mut idx = 0; let mut remaining = size; loop { - let arr = vec::mut_slice(array, idx, size); - let mut run_len: uint = count_run_ascending(arr); - - if run_len < min_run { - let force = if remaining <= min_run {remaining} else {min_run}; - let slice = vec::mut_slice(arr, 0, force); - binarysort(slice, run_len); - run_len = force; - } + let run_len: uint = { + // This scope contains the slice `arr` here: + let arr = vec::mut_slice(array, idx, size); + let mut run_len: uint = count_run_ascending(arr); + + if run_len < min_run { + let force = if remaining <= min_run {remaining} else {min_run}; + let slice = vec::mut_slice(arr, 0, force); + binarysort(slice, run_len); + run_len = force; + } + + run_len + }; ms.push_run(idx, run_len); ms.merge_collapse(array); @@ -250,7 +274,7 @@ fn binarysort(array: &mut [T], start: uint) { fn reverse_slice(v: &mut [T], start: uint, end:uint) { let mut i = start; while i < end / 2 { - util::swap(&mut v[i], &mut v[end - i - 1]); + v[i] <-> v[end - i - 1]; i += 1; } } @@ -433,14 +457,17 @@ impl MergeState { self.runs[n+1].len = self.runs[n+2].len; } - let slice = vec::mut_slice(array, b1, b1+l1); - let k = gallop_right(&const array[b2], slice, 0); + let k = { // constrain lifetime of slice below + let slice = vec::mut_slice(array, b1, b1+l1); + gallop_right(&const array[b2], slice, 0) + }; b1 += k; l1 -= k; if l1 != 0 { - let slice = vec::mut_slice(array, b2, b2+l2); - let l2 = gallop_left( - &const array[b1+l1-1],slice,l2-1); + let l2 = { // constrain lifetime of slice below + let slice = vec::mut_slice(array, b2, b2+l2); + gallop_left(&const array[b1+l1-1],slice,l2-1) + }; if l2 > 0 { if l1 <= l2 { self.merge_lo(array, b1, l1, b2, l2); @@ -621,9 +648,11 @@ impl MergeState { loop { assert!(len2 > 1 && len1 != 0); - let tmp_view = vec::mut_slice(array, base1, base1+len1); - count1 = len1 - gallop_right( - &const tmp[c2], tmp_view, len1-1); + { // constrain scope of tmp_view: + let tmp_view = vec::mut_slice (array, base1, base1+len1); + count1 = len1 - gallop_right( + &const tmp[c2], tmp_view, len1-1); + } if count1 != 0 { dest -= count1; c1 -= count1; len1 -= count1; @@ -636,12 +665,11 @@ impl MergeState { if len2 == 1 { break_outer = true; break; } let count2; - { + { // constrain scope of tmp_view let tmp_view = vec::mut_slice(tmp, 0, len2); count2 = len2 - gallop_left(&const array[c1], tmp_view, len2-1); - // Make tmp_view go out of scope to appease borrowck. } if count2 != 0 { diff --git a/src/libstd/std.rc b/src/libstd/std.rc index 40db9f89d0fd7..d9af8b111bba7 100644 --- a/src/libstd/std.rc +++ b/src/libstd/std.rc @@ -71,7 +71,6 @@ pub mod rope; pub mod smallintmap; pub mod sort; pub mod dlist; -#[cfg(not(stage0))] pub mod treemap; // And ... other stuff diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index f9828ad2b9e4e..eb131b17c2f39 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -19,6 +19,7 @@ use diagnostic::span_handler; use parse::token::ident_interner; use print::pprust; use visit; +use syntax::parse::token::special_idents; use core::hashmap::HashMap; @@ -89,14 +90,13 @@ pub enum ast_node { node_variant(variant, @item, @path), node_expr(@expr), node_stmt(@stmt), - // Locals are numbered, because the alias analysis needs to know in which - // order they are introduced. - node_arg(arg, uint), - node_local(uint), + node_arg, + node_local(ident), // Destructor for a struct node_dtor(Generics, @struct_dtor, def_id, @path), node_block(blk), node_struct_ctor(@struct_def, @item, @path), + node_callee_scope(@expr) } pub type map = @mut HashMap; @@ -104,7 +104,6 @@ pub type map = @mut HashMap; pub struct Ctx { map: map, path: path, - local_id: uint, diag: @span_handler, } @@ -120,9 +119,8 @@ pub fn mk_ast_map_visitor() -> vt { visit_expr: map_expr, visit_stmt: map_stmt, visit_fn: map_fn, - visit_local: map_local, - visit_arm: map_arm, visit_block: map_block, + visit_pat: map_pat, .. *visit::default_visitor() }); } @@ -131,7 +129,6 @@ pub fn map_crate(diag: @span_handler, c: @crate) -> map { let cx = @mut Ctx { map: @mut HashMap::new(), path: ~[], - local_id: 0u, diag: diag, }; visit::visit_crate(c, cx, mk_ast_map_visitor()); @@ -154,7 +151,6 @@ pub fn map_decoded_item(diag: @span_handler, let cx = @mut Ctx { map: map, path: copy path, - local_id: 0, diag: diag, }; let v = mk_ast_map_visitor(); @@ -189,9 +185,7 @@ pub fn map_fn( v: visit::vt<@mut Ctx> ) { for decl.inputs.each |a| { - cx.map.insert(a.id, - node_arg(/* FIXME (#2543) */ copy *a, cx.local_id)); - cx.local_id += 1u; + cx.map.insert(a.id, node_arg); } match *fk { visit::fk_dtor(generics, ref attrs, self_id, parent_id) => { @@ -222,33 +216,22 @@ pub fn map_block(b: &blk, cx: @mut Ctx, v: visit::vt<@mut Ctx>) { visit::visit_block(b, cx, v); } -pub fn number_pat(cx: @mut Ctx, pat: @pat) { - do ast_util::walk_pat(pat) |p| { - match p.node { - pat_ident(*) => { - cx.map.insert(p.id, node_local(cx.local_id)); - cx.local_id += 1u; - } - _ => () +pub fn map_pat(pat: @pat, cx: @mut Ctx, v: visit::vt<@mut Ctx>) { + match pat.node { + pat_ident(_, path, _) => { + // Note: this is at least *potentially* a pattern... + cx.map.insert(pat.id, node_local(ast_util::path_to_ident(path))); } - }; -} - -pub fn map_local(loc: @local, cx: @mut Ctx, v: visit::vt<@mut Ctx>) { - number_pat(cx, loc.node.pat); - visit::visit_local(loc, cx, v); -} + _ => () + } -pub fn map_arm(arm: &arm, cx: @mut Ctx, v: visit::vt<@mut Ctx>) { - number_pat(cx, arm.pats[0]); - visit::visit_arm(arm, cx, v); + visit::visit_pat(pat, cx, v); } pub fn map_method(impl_did: def_id, impl_path: @path, m: @method, cx: @mut Ctx) { cx.map.insert(m.id, node_method(m, impl_did, impl_path)); - cx.map.insert(m.self_id, node_local(cx.local_id)); - cx.local_id += 1u; + cx.map.insert(m.self_id, node_local(special_idents::self_)); } pub fn map_item(i: @item, cx: @mut Ctx, v: visit::vt<@mut Ctx>) { @@ -317,6 +300,7 @@ pub fn map_item(i: @item, cx: @mut Ctx, v: visit::vt<@mut Ctx>) { } _ => () } + match i.node { item_mod(_) | item_foreign_mod(_) => { cx.path.push(path_mod(i.ident)); @@ -352,6 +336,18 @@ pub fn map_struct_def( pub fn map_expr(ex: @expr, cx: @mut Ctx, v: visit::vt<@mut Ctx>) { cx.map.insert(ex.id, node_expr(ex)); + match ex.node { + // Expressions which are or might be calls: + ast::expr_call(*) | + ast::expr_method_call(*) | + ast::expr_index(*) | + ast::expr_binary(*) | + ast::expr_assign_op(*) | + ast::expr_unary(*) => { + cx.map.insert(ex.callee_id, node_callee_scope(ex)); + } + _ => {} + } visit::visit_expr(ex, cx, v); } @@ -401,15 +397,18 @@ pub fn node_id_to_str(map: map, id: node_id, itr: @ident_interner) -> ~str { Some(&node_expr(expr)) => { fmt!("expr %s (id=%?)", pprust::expr_to_str(expr, itr), id) } + Some(&node_callee_scope(expr)) => { + fmt!("callee_scope %s (id=%?)", pprust::expr_to_str(expr, itr), id) + } Some(&node_stmt(stmt)) => { fmt!("stmt %s (id=%?)", pprust::stmt_to_str(stmt, itr), id) } - Some(&node_arg(_, _)) => { // add more info here + Some(&node_arg) => { fmt!("arg (id=%?)", id) } - Some(&node_local(_)) => { // add more info here - fmt!("local (id=%?)", id) + Some(&node_local(ident)) => { + fmt!("local (id=%?, name=%s)", id, *itr.get(ident)) } Some(&node_dtor(*)) => { // add more info here fmt!("node_dtor (id=%?)", id) diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 148b713a4f58f..7e24adabdb048 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -388,8 +388,20 @@ pub struct id_range { max: node_id, } -pub fn empty(range: id_range) -> bool { - range.min >= range.max +pub impl id_range { + fn max() -> id_range { + id_range {min: int::max_value, + max: int::min_value} + } + + fn empty(&self) -> bool { + self.min >= self.max + } + + fn add(&mut self, id: node_id) { + self.min = int::min(self.min, id); + self.max = int::max(self.max, id + 1); + } } pub fn id_visitor(vfn: @fn(node_id)) -> visit::vt<()> { @@ -493,13 +505,11 @@ pub fn visit_ids_for_inlined_item(item: &inlined_item, vfn: @fn(node_id)) { } pub fn compute_id_range(visit_ids_fn: &fn(@fn(node_id))) -> id_range { - let min = @mut int::max_value; - let max = @mut int::min_value; + let result = @mut id_range::max(); do visit_ids_fn |id| { - *min = int::min(*min, id); - *max = int::max(*max, id + 1); + result.add(id); } - id_range { min: *min, max: *max } + *result } pub fn compute_id_range_for_inlined_item(item: &inlined_item) -> id_range { diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 1194506a8876f..7facc181effec 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -246,7 +246,7 @@ pub impl FileMap { // the new charpos must be > the last one (or it's the first one). let lines = &mut *self.lines; assert!((lines.len() == 0) || (lines[lines.len() - 1] < pos)); - self.lines.push(pos); + lines.push(pos); } // get a line from the list of pre-computed line-beginnings @@ -308,7 +308,7 @@ pub impl CodeMap { multibyte_chars: @mut ~[], }; - self.files.push(filemap); + files.push(filemap); return filemap; } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 5bad9ecae3ed7..7d058f22e4c45 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -210,29 +210,29 @@ pub fn syntax_expander_table() -> SyntaxEnv { // when a macro expansion occurs, the resulting nodes have the backtrace() // -> expn_info of their expansion context stored into their span. pub trait ext_ctxt { - fn codemap(@mut self) -> @CodeMap; - fn parse_sess(@mut self) -> @mut parse::ParseSess; - fn cfg(@mut self) -> ast::crate_cfg; - fn call_site(@mut self) -> span; - fn print_backtrace(@mut self); - fn backtrace(@mut self) -> Option<@ExpnInfo>; - fn mod_push(@mut self, mod_name: ast::ident); - fn mod_pop(@mut self); - fn mod_path(@mut self) -> ~[ast::ident]; - fn bt_push(@mut self, ei: codemap::ExpnInfo); - fn bt_pop(@mut self); - fn span_fatal(@mut self, sp: span, msg: &str) -> !; - fn span_err(@mut self, sp: span, msg: &str); - fn span_warn(@mut self, sp: span, msg: &str); - fn span_unimpl(@mut self, sp: span, msg: &str) -> !; - fn span_bug(@mut self, sp: span, msg: &str) -> !; - fn bug(@mut self, msg: &str) -> !; - fn next_id(@mut self) -> ast::node_id; - fn trace_macros(@mut self) -> bool; - fn set_trace_macros(@mut self, x: bool); + fn codemap(&self) -> @CodeMap; + fn parse_sess(&self) -> @mut parse::ParseSess; + fn cfg(&self) -> ast::crate_cfg; + fn call_site(&self) -> span; + fn print_backtrace(&self); + fn backtrace(&self) -> Option<@ExpnInfo>; + fn mod_push(&self, mod_name: ast::ident); + fn mod_pop(&self); + fn mod_path(&self) -> ~[ast::ident]; + fn bt_push(&self, ei: codemap::ExpnInfo); + fn bt_pop(&self); + fn span_fatal(&self, sp: span, msg: &str) -> !; + fn span_err(&self, sp: span, msg: &str); + fn span_warn(&self, sp: span, msg: &str); + fn span_unimpl(&self, sp: span, msg: &str) -> !; + fn span_bug(&self, sp: span, msg: &str) -> !; + fn bug(&self, msg: &str) -> !; + fn next_id(&self) -> ast::node_id; + fn trace_macros(&self) -> bool; + fn set_trace_macros(&self, x: bool); /* for unhygienic identifier transformation */ - fn str_of(@mut self, id: ast::ident) -> ~str; - fn ident_of(@mut self, st: ~str) -> ast::ident; + fn str_of(&self, id: ast::ident) -> ~str; + fn ident_of(&self, st: ~str) -> ast::ident; } pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg) @@ -241,25 +241,31 @@ pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg) parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg, backtrace: @mut Option<@ExpnInfo>, - mod_path: ~[ast::ident], - trace_mac: bool + + // These two @mut's should really not be here, + // but the self types for CtxtRepr are all wrong + // and there are bugs in the code for object + // types that make this hard to get right at the + // moment. - nmatsakis + mod_path: @mut ~[ast::ident], + trace_mac: @mut bool } impl ext_ctxt for CtxtRepr { - fn codemap(@mut self) -> @CodeMap { self.parse_sess.cm } - fn parse_sess(@mut self) -> @mut parse::ParseSess { self.parse_sess } - fn cfg(@mut self) -> ast::crate_cfg { copy self.cfg } - fn call_site(@mut self) -> span { + fn codemap(&self) -> @CodeMap { self.parse_sess.cm } + fn parse_sess(&self) -> @mut parse::ParseSess { self.parse_sess } + fn cfg(&self) -> ast::crate_cfg { copy self.cfg } + fn call_site(&self) -> span { match *self.backtrace { Some(@ExpandedFrom(CallInfo {call_site: cs, _})) => cs, None => self.bug(~"missing top span") } } - fn print_backtrace(@mut self) { } - fn backtrace(@mut self) -> Option<@ExpnInfo> { *self.backtrace } - fn mod_push(@mut self, i: ast::ident) { self.mod_path.push(i); } - fn mod_pop(@mut self) { self.mod_path.pop(); } - fn mod_path(@mut self) -> ~[ast::ident] { copy self.mod_path } - fn bt_push(@mut self, ei: codemap::ExpnInfo) { + fn print_backtrace(&self) { } + fn backtrace(&self) -> Option<@ExpnInfo> { *self.backtrace } + fn mod_push(&self, i: ast::ident) { self.mod_path.push(i); } + fn mod_pop(&self) { self.mod_path.pop(); } + fn mod_path(&self) -> ~[ast::ident] { copy *self.mod_path } + fn bt_push(&self, ei: codemap::ExpnInfo) { match ei { ExpandedFrom(CallInfo {call_site: cs, callee: ref callee}) => { *self.backtrace = @@ -270,7 +276,7 @@ pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg) } } } - fn bt_pop(@mut self) { + fn bt_pop(&self) { match *self.backtrace { Some(@ExpandedFrom(CallInfo { call_site: span {expn_info: prev, _}, _ @@ -280,52 +286,52 @@ pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg) _ => self.bug(~"tried to pop without a push") } } - fn span_fatal(@mut self, sp: span, msg: &str) -> ! { + fn span_fatal(&self, sp: span, msg: &str) -> ! { self.print_backtrace(); self.parse_sess.span_diagnostic.span_fatal(sp, msg); } - fn span_err(@mut self, sp: span, msg: &str) { + fn span_err(&self, sp: span, msg: &str) { self.print_backtrace(); self.parse_sess.span_diagnostic.span_err(sp, msg); } - fn span_warn(@mut self, sp: span, msg: &str) { + fn span_warn(&self, sp: span, msg: &str) { self.print_backtrace(); self.parse_sess.span_diagnostic.span_warn(sp, msg); } - fn span_unimpl(@mut self, sp: span, msg: &str) -> ! { + fn span_unimpl(&self, sp: span, msg: &str) -> ! { self.print_backtrace(); self.parse_sess.span_diagnostic.span_unimpl(sp, msg); } - fn span_bug(@mut self, sp: span, msg: &str) -> ! { + fn span_bug(&self, sp: span, msg: &str) -> ! { self.print_backtrace(); self.parse_sess.span_diagnostic.span_bug(sp, msg); } - fn bug(@mut self, msg: &str) -> ! { + fn bug(&self, msg: &str) -> ! { self.print_backtrace(); self.parse_sess.span_diagnostic.handler().bug(msg); } - fn next_id(@mut self) -> ast::node_id { + fn next_id(&self) -> ast::node_id { return parse::next_node_id(self.parse_sess); } - fn trace_macros(@mut self) -> bool { - self.trace_mac + fn trace_macros(&self) -> bool { + *self.trace_mac } - fn set_trace_macros(@mut self, x: bool) { - self.trace_mac = x + fn set_trace_macros(&self, x: bool) { + *self.trace_mac = x } - fn str_of(@mut self, id: ast::ident) -> ~str { + fn str_of(&self, id: ast::ident) -> ~str { copy *self.parse_sess.interner.get(id) } - fn ident_of(@mut self, st: ~str) -> ast::ident { + fn ident_of(&self, st: ~str) -> ast::ident { self.parse_sess.interner.intern(@/*bad*/ copy st) } } - let imp: @mut CtxtRepr = @mut CtxtRepr { + let imp: @CtxtRepr = @CtxtRepr { parse_sess: parse_sess, cfg: cfg, backtrace: @mut None, - mod_path: ~[], - trace_mac: false + mod_path: @mut ~[], + trace_mac: @mut false }; ((imp) as @ext_ctxt) } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index fde5a2594226e..841f64e0b0502 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -27,6 +27,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv, fld: @ast_fold, orig: @fn(&expr_, span, @ast_fold) -> (expr_, span)) -> (expr_, span) { + let mut cx = cx; match *e { // expr_mac should really be expr_ext or something; it's the // entry-point for all syntax extensions. @@ -112,6 +113,8 @@ pub fn expand_mod_items(extsbox: @mut SyntaxEnv, fld: @ast_fold, orig: @fn(&ast::_mod, @ast_fold) -> ast::_mod) -> ast::_mod { + let mut cx = cx; + // Fold the contents first: let module_ = orig(module_, fld); diff --git a/src/libsyntax/ext/pipes/liveness.rs b/src/libsyntax/ext/pipes/liveness.rs index 4597dab89cbfe..7843db5578929 100644 --- a/src/libsyntax/ext/pipes/liveness.rs +++ b/src/libsyntax/ext/pipes/liveness.rs @@ -38,11 +38,11 @@ updating the states using rule (2) until there are no changes. */ use ext::base::ext_ctxt; -use ext::pipes::proto::protocol; +use ext::pipes::proto::{protocol_}; use std::bitv::Bitv; -pub fn analyze(proto: protocol, _cx: @ext_ctxt) { +pub fn analyze(proto: &mut protocol_, _cx: @ext_ctxt) { debug!("initializing colive analysis"); let num_states = proto.num_states(); let mut colive = do (copy proto.states).map_to_vec |state| { diff --git a/src/libsyntax/ext/pipes/proto.rs b/src/libsyntax/ext/pipes/proto.rs index 79072a2f577ff..ffb55ee50d992 100644 --- a/src/libsyntax/ext/pipes/proto.rs +++ b/src/libsyntax/ext/pipes/proto.rs @@ -138,26 +138,26 @@ pub struct protocol_ { pub impl protocol_ { /// Get a state. - fn get_state(&mut self, name: ~str) -> state { + fn get_state(&self, name: ~str) -> state { self.states.find(|i| i.name == name).get() } - fn get_state_by_id(&mut self, id: uint) -> state { self.states[id] } + fn get_state_by_id(&self, id: uint) -> state { self.states[id] } - fn has_state(&mut self, name: ~str) -> bool { + fn has_state(&self, name: ~str) -> bool { self.states.find(|i| i.name == name).is_some() } - fn filename(&mut self) -> ~str { + fn filename(&self) -> ~str { ~"proto://" + self.name } - fn num_states(&mut self) -> uint { + fn num_states(&self) -> uint { let states = &mut *self.states; states.len() } - fn has_ty_params(&mut self) -> bool { + fn has_ty_params(&self) -> bool { for self.states.each |s| { if s.generics.ty_params.len() > 0 { return true; @@ -165,7 +165,7 @@ pub impl protocol_ { } false } - fn is_bounded(&mut self) -> bool { + fn is_bounded(&self) -> bool { let bounded = self.bounded.get(); bounded } @@ -179,7 +179,7 @@ pub impl protocol_ { generics: ast::Generics) -> state { let messages = @mut ~[]; - let states = &*self.states; + let states = &mut *self.states; let state = @state_ { id: states.len(), @@ -192,7 +192,7 @@ pub impl protocol_ { proto: self }; - self.states.push(state); + states.push(state); state } } diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs index e2ad5becb123b..c1acee8e2cd96 100644 --- a/src/libsyntax/print/pp.rs +++ b/src/libsyntax/print/pp.rs @@ -491,9 +491,9 @@ pub impl Printer { } END => { debug!("print END -> pop END"); - let print_stack = &*self.print_stack; + let print_stack = &mut *self.print_stack; assert!((print_stack.len() != 0u)); - self.print_stack.pop(); + print_stack.pop(); } BREAK(b) => { let top = self.get_top(); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index d5645ada9294a..ff8259e899699 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -72,6 +72,12 @@ pub fn end(s: @ps) { } pub fn rust_printer(writer: @io::Writer, intr: @ident_interner) -> @ps { + return rust_printer_annotated(writer, intr, no_ann()); +} + +pub fn rust_printer_annotated(writer: @io::Writer, + intr: @ident_interner, + ann: pp_ann) -> @ps { return @ps { s: pp::mk_printer(writer, default_columns), cm: None::<@CodeMap>, @@ -83,7 +89,7 @@ pub fn rust_printer(writer: @io::Writer, intr: @ident_interner) -> @ps { cur_lit: 0 }, boxes: @mut ~[], - ann: no_ann() + ann: ann }; } diff --git a/src/libsyntax/util/interner.rs b/src/libsyntax/util/interner.rs index 9ab7d4bc443ef..e3a8727762218 100644 --- a/src/libsyntax/util/interner.rs +++ b/src/libsyntax/util/interner.rs @@ -44,10 +44,10 @@ pub impl Interner { None => (), } - let vect = &*self.vect; + let vect = &mut *self.vect; let new_idx = vect.len(); self.map.insert(val, new_idx); - self.vect.push(val); + vect.push(val); new_idx } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 80df8fb91a515..a42f640a175a7 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -22,6 +22,12 @@ use opt_vec::OptVec; // children (potentially passing in different contexts to each), call // visit::visit_* to apply the default traversal algorithm (again, it can // override the context), or prevent deeper traversal by doing nothing. +// +// Note: it is an important invariant that the default visitor walks the body +// of a function in "execution order" (more concretely, reverse post-order +// with respect to the CFG implied by the AST), meaning that if AST node A may +// execute before AST node B, then A is visited first. The borrow checker in +// particular relies on this property. // Our typesystem doesn't do circular types, so the visitor record can not // hold functions that take visitors. A vt enum is used to break the cycle. diff --git a/src/test/compile-fail/access-mode-in-closures.rs b/src/test/compile-fail/access-mode-in-closures.rs index f6b9a82ec676c..61fb754f7619f 100644 --- a/src/test/compile-fail/access-mode-in-closures.rs +++ b/src/test/compile-fail/access-mode-in-closures.rs @@ -16,6 +16,6 @@ fn unpack(_unpack: &fn(v: &sty) -> ~[int]) {} fn main() { let _foo = unpack(|s| { // Test that `s` is moved here. - match *s { sty(v) => v } //~ ERROR moving out of dereference of immutable & pointer + match *s { sty(v) => v } //~ ERROR cannot move out }); } 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 e2dd13a4405d1..85f60f34bdb80 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 @@ -17,6 +17,7 @@ fn main() { y = Some(x.downgrade(write_mode)); //~^ ERROR cannot infer an appropriate lifetime } + y.get(); // Adding this line causes a method unification failure instead // do (&option::unwrap(y)).read |state| { assert!(*state == 1); } } diff --git a/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs b/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs index 78a50a4f21242..c7ae6a0dc6c52 100644 --- a/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs +++ b/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs @@ -17,6 +17,7 @@ fn main() { do x.write_downgrade |write_mode| { y = Some(write_mode); } + y.get(); // Adding this line causes a method unification failure instead // do (&option::unwrap(y)).write |state| { assert!(*state == 1); } } diff --git a/src/test/compile-fail/attempted-access-non-fatal.rs b/src/test/compile-fail/attempted-access-non-fatal.rs index ba15abc3f8965..1d9249bc17b1f 100644 --- a/src/test/compile-fail/attempted-access-non-fatal.rs +++ b/src/test/compile-fail/attempted-access-non-fatal.rs @@ -11,6 +11,6 @@ // Check that bogus field access is non-fatal fn main() { let x = 0; - debug!(x.foo); //~ ERROR attempted access of field - debug!(x.bar); //~ ERROR attempted access of field + let _ = x.foo; //~ ERROR attempted access of field + let _ = x.bar; //~ ERROR attempted access of field } diff --git a/src/test/compile-fail/borrowck-addr-of-upvar.rs b/src/test/compile-fail/borrowck-addr-of-upvar.rs index 640bc887731f9..83baedc789277 100644 --- a/src/test/compile-fail/borrowck-addr-of-upvar.rs +++ b/src/test/compile-fail/borrowck-addr-of-upvar.rs @@ -9,12 +9,12 @@ // except according to those terms. fn foo(x: @int) -> @fn() -> &'static int { - let result: @fn() -> &'static int = || &*x; //~ ERROR illegal borrow + let result: @fn() -> &'static int = || &*x; //~ ERROR cannot root result } fn bar(x: @int) -> @fn() -> &int { - let result: @fn() -> &int = || &*x; //~ ERROR illegal borrow + let result: @fn() -> &int = || &*x; //~ ERROR cannot root result } diff --git a/src/test/compile-fail/borrowck-assign-comp-idx.rs b/src/test/compile-fail/borrowck-assign-comp-idx.rs index 25b56abb5ba00..d447d9e4a2288 100644 --- a/src/test/compile-fail/borrowck-assign-comp-idx.rs +++ b/src/test/compile-fail/borrowck-assign-comp-idx.rs @@ -17,9 +17,11 @@ fn a() { let mut p = ~[1]; // Create an immutable pointer into p's contents: - let _q: &int = &p[0]; //~ NOTE loan of mutable vec content granted here + let q: &int = &p[0]; - p[0] = 5; //~ ERROR assigning to mutable vec content prohibited due to outstanding loan + p[0] = 5; //~ ERROR cannot assign + + debug!("%d", *q); } fn borrow(_x: &[int], _f: &fn()) {} @@ -30,8 +32,8 @@ fn b() { let mut p = ~[1]; - do borrow(p) { //~ NOTE loan of mutable vec content granted here - p[0] = 5; //~ ERROR assigning to mutable vec content prohibited due to outstanding loan + do borrow(p) { + p[0] = 5; //~ ERROR cannot assign to } } diff --git a/src/test/compile-fail/borrowck-assign-comp.rs b/src/test/compile-fail/borrowck-assign-comp.rs index 283f04a283f4e..b8c0cbe97433e 100644 --- a/src/test/compile-fail/borrowck-assign-comp.rs +++ b/src/test/compile-fail/borrowck-assign-comp.rs @@ -12,12 +12,13 @@ struct point { x: int, y: int } fn a() { let mut p = point {x: 3, y: 4}; - let _q = &p; //~ NOTE loan of mutable local variable granted here + let q = &p; // This assignment is illegal because the field x is not // inherently mutable; since `p` was made immutable, `p.x` is now // immutable. Otherwise the type of &_q.x (&int) would be wrong. - p.x = 5; //~ ERROR assigning to mutable field prohibited due to outstanding loan + p.x = 5; //~ ERROR cannot assign to `p.x` + q.x; } fn c() { @@ -25,9 +26,10 @@ fn c() { // and then try to overwrite `p` as a whole. let mut p = point {x: 3, y: 4}; - let _q = &p.y; //~ NOTE loan of mutable local variable granted here - p = point {x: 5, y: 7};//~ ERROR assigning to mutable local variable prohibited due to outstanding loan - copy p; + let q = &p.y; + p = point {x: 5, y: 7};//~ ERROR cannot assign to `p` + p.x; // silence warning + *q; // stretch loan } fn d() { @@ -35,9 +37,9 @@ fn d() { // address of a subcomponent and then modify that subcomponent: let mut p = point {x: 3, y: 4}; - let _q = &p.y; //~ NOTE loan of mutable field granted here - p.y = 5; //~ ERROR assigning to mutable field prohibited due to outstanding loan - copy p; + let q = &p.y; + p.y = 5; //~ ERROR cannot assign to `p.y` + *q; } fn main() { diff --git a/src/test/compile-fail/borrowck-assign-to-constants.rs b/src/test/compile-fail/borrowck-assign-to-constants.rs index 0d65aacb65b7a..f0dc28b736d16 100644 --- a/src/test/compile-fail/borrowck-assign-to-constants.rs +++ b/src/test/compile-fail/borrowck-assign-to-constants.rs @@ -12,6 +12,6 @@ static foo: int = 5; fn main() { // assigning to various global constants - None = Some(3); //~ ERROR assigning to static item - foo = 6; //~ ERROR assigning to static item + None = Some(3); //~ ERROR cannot assign to immutable static item + foo = 6; //~ ERROR cannot assign to immutable static item } diff --git a/src/test/compile-fail/borrowck-assign-to-enum.rs b/src/test/compile-fail/borrowck-assign-to-enum.rs index a35d88a76f393..fcaba0adc46eb 100644 --- a/src/test/compile-fail/borrowck-assign-to-enum.rs +++ b/src/test/compile-fail/borrowck-assign-to-enum.rs @@ -12,5 +12,5 @@ struct foo(int); fn main() { let x = foo(3); - *x = 4; //~ ERROR assigning to anonymous field + *x = 4; //~ ERROR cannot assign to immutable anonymous field } diff --git a/src/test/compile-fail/borrowck-assign-to-subfield.rs b/src/test/compile-fail/borrowck-assign-to-subfield.rs index 610802ca68b31..2ee5ecfcb9ce0 100644 --- a/src/test/compile-fail/borrowck-assign-to-subfield.rs +++ b/src/test/compile-fail/borrowck-assign-to-subfield.rs @@ -34,6 +34,6 @@ fn main() { // in these cases we pass through a box, so the mut // of the box is dominant - p.x.a = 2; //~ ERROR assigning to immutable field + p.x.a = 2; //~ ERROR cannot assign to immutable field p.z.a = 2; } diff --git a/src/test/compile-fail/auto-ref-borrowck-failure.rs b/src/test/compile-fail/borrowck-auto-mut-ref-to-immut-var.rs similarity index 81% rename from src/test/compile-fail/auto-ref-borrowck-failure.rs rename to src/test/compile-fail/borrowck-auto-mut-ref-to-immut-var.rs index 90b9b44cfbea3..ce95ee94f4252 100644 --- a/src/test/compile-fail/auto-ref-borrowck-failure.rs +++ b/src/test/compile-fail/borrowck-auto-mut-ref-to-immut-var.rs @@ -14,18 +14,14 @@ struct Foo { x: int } -trait Stuff { - fn printme(self); -} - -impl<'self> Stuff for &'self mut Foo { - fn printme(self) { +pub impl Foo { + fn printme(&mut self) { io::println(fmt!("%d", self.x)); } } fn main() { let x = Foo { x: 3 }; - x.printme(); //~ ERROR illegal borrow + x.printme(); //~ ERROR cannot borrow } diff --git a/src/test/compile-fail/borrowck-autoref-3261.rs b/src/test/compile-fail/borrowck-autoref-3261.rs index c95b93445adca..192fe669f57ae 100644 --- a/src/test/compile-fail/borrowck-autoref-3261.rs +++ b/src/test/compile-fail/borrowck-autoref-3261.rs @@ -17,10 +17,10 @@ pub impl X { } fn main() { let mut x = X(Right(main)); - do (&mut x).with |opt| { //~ ERROR illegal borrow + do (&mut x).with |opt| { match opt { &Right(ref f) => { - x = X(Left((0,0))); //~ ERROR assigning to captured outer mutable variable + x = X(Left((0,0))); //~ ERROR cannot assign to `x` (*f)() }, _ => fail!() diff --git a/src/test/compile-fail/borrowck-bad-nested-calls-free.rs b/src/test/compile-fail/borrowck-bad-nested-calls-free.rs new file mode 100644 index 0000000000000..ff1ec38ad6406 --- /dev/null +++ b/src/test/compile-fail/borrowck-bad-nested-calls-free.rs @@ -0,0 +1,43 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we detect nested calls that could free pointers evaluated +// for earlier arguments. + +fn rewrite(v: &mut ~uint) -> uint { + *v = ~22; + **v +} + +fn add(v: &uint, w: uint) -> uint { + *v + w +} + +fn implicit() { + let mut a = ~1; + + // Note the danger here: + // + // the pointer for the first argument has already been + // evaluated, but it gets freed when evaluating the second + // argument! + add( + a, + rewrite(&mut a)); //~ ERROR cannot borrow +} + +fn explicit() { + let mut a = ~1; + add( + &*a, + rewrite(&mut a)); //~ ERROR cannot borrow +} + +fn main() {} \ No newline at end of file diff --git a/src/test/compile-fail/borrowck-bad-nested-calls-move.rs b/src/test/compile-fail/borrowck-bad-nested-calls-move.rs new file mode 100644 index 0000000000000..0adf486b8b3ab --- /dev/null +++ b/src/test/compile-fail/borrowck-bad-nested-calls-move.rs @@ -0,0 +1,43 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we detect nested calls that could free pointers evaluated +// for earlier arguments. + +fn rewrite(v: &mut ~uint) -> uint { + *v = ~22; + **v +} + +fn add(v: &uint, w: ~uint) -> uint { + *v + *w +} + +fn implicit() { + let mut a = ~1; + + // Note the danger here: + // + // the pointer for the first argument has already been + // evaluated, but it gets moved when evaluating the second + // argument! + add( + a, + a); //~ ERROR cannot move +} + +fn explicit() { + let mut a = ~1; + add( + &*a, + a); //~ ERROR cannot move +} + +fn main() {} \ No newline at end of file diff --git a/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs b/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs index 005908f86d87d..1051c5829ec38 100644 --- a/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs +++ b/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs @@ -22,32 +22,37 @@ fn make_foo() -> ~Foo { fail!() } fn borrow_same_field_twice_mut_mut() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1; - let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let bar1 = &mut foo.bar1; + let _bar2 = &mut foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_same_field_twice_mut_imm() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1; - let _bar2 = &foo.bar1; //~ ERROR conflicts with prior loan + let bar1 = &mut foo.bar1; + let _bar2 = &foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_same_field_twice_imm_mut() { let mut foo = make_foo(); - let _bar1 = &foo.bar1; - let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let bar1 = &foo.bar1; + let _bar2 = &mut foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_same_field_twice_imm_imm() { let mut foo = make_foo(); - let _bar1 = &foo.bar1; + let bar1 = &foo.bar1; let _bar2 = &foo.bar1; + *bar1; } -fn borrow_both_mut() { +fn borrow_both_fields_mut() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1; + let bar1 = &mut foo.bar1; let _bar2 = &mut foo.bar2; + *bar1; } fn borrow_both_mut_pattern() { @@ -59,66 +64,77 @@ fn borrow_both_mut_pattern() { fn borrow_var_and_pattern() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1; + let bar1 = &mut foo.bar1; match *foo { Foo { bar1: ref mut _bar1, bar2: _ } => {} - //~^ ERROR conflicts with prior loan + //~^ ERROR cannot borrow } + *bar1; } fn borrow_mut_and_base_imm() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1.int1; - let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan - let _foo2 = &*foo; //~ ERROR conflicts with prior loan + let bar1 = &mut foo.bar1.int1; + let _foo1 = &foo.bar1; //~ ERROR cannot borrow + let _foo2 = &*foo; //~ ERROR cannot borrow + *bar1; } fn borrow_mut_and_base_mut() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1.int1; - let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let bar1 = &mut foo.bar1.int1; + let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_mut_and_base_mut2() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1.int1; - let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan + let bar1 = &mut foo.bar1.int1; + let _foo2 = &mut *foo; //~ ERROR cannot borrow + *bar1; } fn borrow_imm_and_base_mut() { let mut foo = make_foo(); - let _bar1 = &foo.bar1.int1; - let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let bar1 = &foo.bar1.int1; + let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_imm_and_base_mut2() { let mut foo = make_foo(); - let _bar1 = &foo.bar1.int1; - let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan + let bar1 = &foo.bar1.int1; + let _foo2 = &mut *foo; //~ ERROR cannot borrow + *bar1; } fn borrow_imm_and_base_imm() { let mut foo = make_foo(); - let _bar1 = &foo.bar1.int1; + let bar1 = &foo.bar1.int1; let _foo1 = &foo.bar1; let _foo2 = &*foo; + *bar1; } fn borrow_mut_and_imm() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1; + let bar1 = &mut foo.bar1; let _foo1 = &foo.bar2; + *bar1; } fn borrow_mut_from_imm() { let foo = make_foo(); - let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow + let bar1 = &mut foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_long_path_both_mut() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1.int1; - let _foo1 = &mut foo.bar2.int2; + let bar1 = &mut foo.bar1.int1; + let foo1 = &mut foo.bar2.int2; + *bar1; + *foo1; } fn main() {} diff --git a/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs b/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs index 035e293bc36b6..cdcf50c906e36 100644 --- a/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs +++ b/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs @@ -22,32 +22,37 @@ fn make_foo() -> Foo { fail!() } fn borrow_same_field_twice_mut_mut() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1; - let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let bar1 = &mut foo.bar1; + let _bar2 = &mut foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_same_field_twice_mut_imm() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1; - let _bar2 = &foo.bar1; //~ ERROR conflicts with prior loan + let bar1 = &mut foo.bar1; + let _bar2 = &foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_same_field_twice_imm_mut() { let mut foo = make_foo(); - let _bar1 = &foo.bar1; - let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let bar1 = &foo.bar1; + let _bar2 = &mut foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_same_field_twice_imm_imm() { let mut foo = make_foo(); - let _bar1 = &foo.bar1; + let bar1 = &foo.bar1; let _bar2 = &foo.bar1; + *bar1; } fn borrow_both_mut() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1; + let bar1 = &mut foo.bar1; let _bar2 = &mut foo.bar2; + *bar1; } fn borrow_both_mut_pattern() { @@ -59,66 +64,76 @@ fn borrow_both_mut_pattern() { fn borrow_var_and_pattern() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1; + let bar1 = &mut foo.bar1; match foo { - Foo { bar1: ref mut _bar1, bar2: _ } => {} - //~^ ERROR conflicts with prior loan + Foo { bar1: ref mut _bar1, bar2: _ } => {} // + //~^ ERROR cannot borrow } + *bar1; } fn borrow_mut_and_base_imm() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1.int1; - let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan - let _foo2 = &foo; //~ ERROR conflicts with prior loan + let bar1 = &mut foo.bar1.int1; + let _foo1 = &foo.bar1; //~ ERROR cannot borrow + let _foo2 = &foo; //~ ERROR cannot borrow + *bar1; } fn borrow_mut_and_base_mut() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1.int1; - let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let bar1 = &mut foo.bar1.int1; + let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_mut_and_base_mut2() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1.int1; - let _foo2 = &mut foo; //~ ERROR conflicts with prior loan + let bar1 = &mut foo.bar1.int1; + let _foo2 = &mut foo; //~ ERROR cannot borrow + *bar1; } fn borrow_imm_and_base_mut() { let mut foo = make_foo(); - let _bar1 = &foo.bar1.int1; - let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let bar1 = &foo.bar1.int1; + let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_imm_and_base_mut2() { let mut foo = make_foo(); - let _bar1 = &foo.bar1.int1; - let _foo2 = &mut foo; //~ ERROR conflicts with prior loan + let bar1 = &foo.bar1.int1; + let _foo2 = &mut foo; //~ ERROR cannot borrow + *bar1; } fn borrow_imm_and_base_imm() { let mut foo = make_foo(); - let _bar1 = &foo.bar1.int1; + let bar1 = &foo.bar1.int1; let _foo1 = &foo.bar1; let _foo2 = &foo; + *bar1; } fn borrow_mut_and_imm() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1; + let bar1 = &mut foo.bar1; let _foo1 = &foo.bar2; + *bar1; } fn borrow_mut_from_imm() { let foo = make_foo(); - let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow + let bar1 = &mut foo.bar1; //~ ERROR cannot borrow + *bar1; } fn borrow_long_path_both_mut() { let mut foo = make_foo(); - let _bar1 = &mut foo.bar1.int1; + let bar1 = &mut foo.bar1.int1; let _foo1 = &mut foo.bar2.int2; + *bar1; } fn main() {} diff --git a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs index 4a6a90ae5167f..1e5c4c5cc410c 100644 --- a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs +++ b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs @@ -28,5 +28,6 @@ fn defer<'r>(x: &'r [&'r str]) -> defer<'r> { } fn main() { - let _x = defer(~["Goodbye", "world!"]); //~ ERROR illegal borrow + let x = defer(~["Goodbye", "world!"]); //~ ERROR borrowed value does not live long enough + x.x[0]; } diff --git a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs index bda659aa7b97e..887cb59930ebc 100644 --- a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs +++ b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs @@ -15,7 +15,7 @@ use core::hashmap::HashMap; fn main() { let mut buggy_map :HashMap = HashMap::new::(); - buggy_map.insert(42, &*~1); //~ ERROR illegal borrow + buggy_map.insert(42, &*~1); //~ ERROR borrowed value does not live long enough // but it is ok if we use a temporary let tmp = ~2; diff --git a/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs b/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs index 2c68429baec92..be51091c1fd1e 100644 --- a/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs +++ b/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs @@ -27,13 +27,15 @@ fn a(x: &mut Foo) { fn b(x: &Foo) { x.f(); x.g(); - x.h(); //~ ERROR illegal borrow + x.h(); //~ ERROR cannot borrow } fn c(x: &const Foo) { - x.f(); //~ ERROR illegal borrow unless pure + x.f(); //~ ERROR cannot borrow + //~^ ERROR unsafe borrow x.g(); - x.h(); //~ ERROR illegal borrow + x.h(); //~ ERROR cannot borrow + //~^ ERROR unsafe borrow } fn main() { diff --git a/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs b/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs index 88db5f5434116..8af10231921aa 100644 --- a/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs +++ b/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs @@ -10,9 +10,9 @@ fn main() { let mut _a = 3; - let _b = &mut _a; //~ NOTE loan of mutable local variable granted here + let _b = &mut _a; { let _c = &*_b; - _a = 4; //~ ERROR assigning to mutable local variable prohibited + _a = 4; //~ ERROR cannot assign to `_a` } } diff --git a/src/test/compile-fail/borrowck-insert-during-each.rs b/src/test/compile-fail/borrowck-insert-during-each.rs index 17c0efe225e4d..109753b38e70b 100644 --- a/src/test/compile-fail/borrowck-insert-during-each.rs +++ b/src/test/compile-fail/borrowck-insert-during-each.rs @@ -23,8 +23,8 @@ pub impl Foo { } fn bar(f: &mut Foo) { - do f.foo |a| { //~ NOTE prior loan as mutable granted here - f.n.insert(*a); //~ ERROR conflicts with prior loan + do f.foo |a| { + f.n.insert(*a); //~ ERROR cannot borrow } } diff --git a/src/test/compile-fail/borrowck-issue-2657-1.rs b/src/test/compile-fail/borrowck-issue-2657-1.rs index ce183c1888f13..8bcd5f9a72e70 100644 --- a/src/test/compile-fail/borrowck-issue-2657-1.rs +++ b/src/test/compile-fail/borrowck-issue-2657-1.rs @@ -10,9 +10,9 @@ fn main() { let x = Some(~1); -match x { //~ NOTE loan of immutable local variable granted here +match x { Some(ref _y) => { - let _a = x; //~ ERROR moving out of immutable local variable prohibited due to outstanding loan + let _a = x; //~ ERROR cannot move } _ => {} } diff --git a/src/test/compile-fail/borrowck-issue-2657-2.rs b/src/test/compile-fail/borrowck-issue-2657-2.rs index d2217778d4148..fac805c57ca09 100644 --- a/src/test/compile-fail/borrowck-issue-2657-2.rs +++ b/src/test/compile-fail/borrowck-issue-2657-2.rs @@ -12,7 +12,7 @@ fn main() { let x = Some(~1); match x { Some(ref y) => { - let _b = *y; //~ ERROR moving out of dereference of immutable & pointer + let _b = *y; //~ ERROR cannot move out } _ => {} } diff --git a/src/test/compile-fail/borrowck-lend-flow-if.rs b/src/test/compile-fail/borrowck-lend-flow-if.rs new file mode 100644 index 0000000000000..563f63b98be05 --- /dev/null +++ b/src/test/compile-fail/borrowck-lend-flow-if.rs @@ -0,0 +1,52 @@ +// Copyright 2012 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. + +// Note: the borrowck analysis is currently flow-insensitive. +// Therefore, some of these errors are marked as spurious and could be +// corrected by a simple change to the analysis. The others are +// either genuine or would require more advanced changes. The latter +// cases are noted. + +fn borrow(_v: &int) {} +fn borrow_mut(_v: &mut int) {} +fn cond() -> bool { fail!() } +fn for_func(_f: &fn() -> bool) { fail!() } +fn produce() -> T { fail!(); } + +fn inc(v: &mut ~int) { + *v = ~(**v + 1); +} + +fn pre_freeze_cond() { + // In this instance, the freeze is conditional and starts before + // the mut borrow. + + let mut v = ~3; + let _w; + if cond() { + _w = &v; + } + borrow_mut(v); //~ ERROR cannot borrow +} + +fn pre_freeze_else() { + // In this instance, the freeze and mut borrow are on separate sides + // of the if. + + let mut v = ~3; + let _w; + if cond() { + _w = &v; + } else { + borrow_mut(v); + } +} + +fn main() {} diff --git a/src/test/compile-fail/borrowck-lend-flow-loop.rs b/src/test/compile-fail/borrowck-lend-flow-loop.rs new file mode 100644 index 0000000000000..b6384ad9590ab --- /dev/null +++ b/src/test/compile-fail/borrowck-lend-flow-loop.rs @@ -0,0 +1,164 @@ +// Copyright 2012 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. + +// Note: the borrowck analysis is currently flow-insensitive. +// Therefore, some of these errors are marked as spurious and could be +// corrected by a simple change to the analysis. The others are +// either genuine or would require more advanced changes. The latter +// cases are noted. + +fn borrow(_v: &int) {} +fn borrow_mut(_v: &mut int) {} +fn cond() -> bool { fail!() } +fn for_func(_f: &fn() -> bool) { fail!() } +fn produce() -> T { fail!(); } + +fn inc(v: &mut ~int) { + *v = ~(**v + 1); +} + +fn loop_overarching_alias_mut() { + // In this instance, the borrow encompasses the entire loop. + + let mut v = ~3; + let mut x = &mut v; + **x += 1; + loop { + borrow(v); //~ ERROR cannot borrow + } +} + +fn block_overarching_alias_mut() { + // In this instance, the borrow encompasses the entire closure call. + + let mut v = ~3; + let mut x = &mut v; + for 3.times { + borrow(v); //~ ERROR cannot borrow + } + *x = ~5; +} + +fn loop_aliased_mut() { + // In this instance, the borrow is carried through the loop. + + let mut v = ~3, w = ~4; + let mut _x = &w; + loop { + borrow_mut(v); //~ ERROR cannot borrow + _x = &v; + } +} + +fn while_aliased_mut() { + // In this instance, the borrow is carried through the loop. + + let mut v = ~3, w = ~4; + let mut _x = &w; + while cond() { + borrow_mut(v); //~ ERROR cannot borrow + _x = &v; + } +} + +fn for_loop_aliased_mut() { + // In this instance, the borrow is carried through the loop. + + let mut v = ~3, w = ~4; + let mut _x = &w; + for for_func { + borrow_mut(v); //~ ERROR cannot borrow + _x = &v; + } +} + +fn loop_aliased_mut_break() { + // In this instance, the borrow is carried through the loop. + + let mut v = ~3, w = ~4; + let mut _x = &w; + loop { + borrow_mut(v); + _x = &v; + break; + } + borrow_mut(v); //~ ERROR cannot borrow +} + +fn while_aliased_mut_break() { + // In this instance, the borrow is carried through the loop. + + let mut v = ~3, w = ~4; + let mut _x = &w; + while cond() { + borrow_mut(v); + _x = &v; + break; + } + borrow_mut(v); //~ ERROR cannot borrow +} + +fn for_aliased_mut_break() { + // In this instance, the borrow is carried through the loop. + + let mut v = ~3, w = ~4; + let mut _x = &w; + for for_func { + // here we cannot be sure that `for_func` respects the break below + borrow_mut(v); //~ ERROR cannot borrow + _x = &v; + break; + } + borrow_mut(v); //~ ERROR cannot borrow +} + +fn while_aliased_mut_cond(cond: bool, cond2: bool) { + let mut v = ~3, w = ~4; + let mut x = &mut w; + while cond { + **x += 1; + borrow(v); //~ ERROR cannot borrow + if cond2 { + x = &mut v; //~ ERROR cannot borrow + } + } +} + +fn loop_break_pops_scopes<'r>(_v: &'r mut [uint], f: &fn(&'r mut uint) -> bool) { + // Here we check that when you break out of an inner loop, the + // borrows that go out of scope as you exit the inner loop are + // removed from the bitset. + + while cond() { + while cond() { + // this borrow is limited to the scope of `r`... + let r: &'r mut uint = produce(); + if !f(&mut *r) { + break; // ...so it is not live as exit the `while` loop here + } + } + } +} + +fn loop_loop_pops_scopes<'r>(_v: &'r mut [uint], f: &fn(&'r mut uint) -> bool) { + // Similar to `loop_break_pops_scopes` but for the `loop` keyword + + while cond() { + while cond() { + // this borrow is limited to the scope of `r`... + let r: &'r mut uint = produce(); + if !f(&mut *r) { + loop; // ...so it is not live as exit (and re-enter) the `while` loop here + } + } + } +} + +fn main() {} diff --git a/src/test/compile-fail/borrowck-lend-flow-match.rs b/src/test/compile-fail/borrowck-lend-flow-match.rs new file mode 100644 index 0000000000000..7603fdc82a824 --- /dev/null +++ b/src/test/compile-fail/borrowck-lend-flow-match.rs @@ -0,0 +1,60 @@ +// Copyright 2012 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. + +// xfail-pretty -- comments are infaithfully preserved + +#[allow(unused_variable)]; +#[allow(dead_assignment)]; + +fn cond() -> bool { fail!() } +fn link<'a>(v: &'a uint, w: &mut &'a uint) -> bool { *w = v; true } + +fn separate_arms() { + // Here both arms perform assignments, but only is illegal. + + let mut x = None; + match x { + None => { + // It is ok to reassign x here, because there is in + // fact no outstanding loan of x! + x = Some(0); + } + Some(ref _i) => { + x = Some(1); //~ ERROR cannot assign + } + } + copy x; // just to prevent liveness warnings +} + +fn guard() { + // Here the guard performs a borrow. This borrow "infects" all + // subsequent arms (but not the prior ones). + + let mut a = ~3; + let mut b = ~4; + let mut w = &*a; + match 22 { + _ if cond() => { + b = ~5; + } + + _ if link(&*b, &mut w) => { + b = ~6; //~ ERROR cannot assign + } + + _ => { + b = ~7; //~ ERROR cannot assign + } + } + + b = ~8; //~ ERROR cannot assign +} + +fn main() {} diff --git a/src/test/compile-fail/borrowck-lend-flow.rs b/src/test/compile-fail/borrowck-lend-flow.rs index ed6446a6311b8..59cac0c5d953a 100644 --- a/src/test/compile-fail/borrowck-lend-flow.rs +++ b/src/test/compile-fail/borrowck-lend-flow.rs @@ -15,96 +15,37 @@ // cases are noted. fn borrow(_v: &int) {} +fn borrow_mut(_v: &mut int) {} +fn cond() -> bool { fail!() } +fn for_func(_f: &fn() -> bool) { fail!() } +fn produce() -> T { fail!(); } fn inc(v: &mut ~int) { *v = ~(**v + 1); } -fn post_aliased_const() { - let mut v = ~3; - borrow(v); - let _w = &const v; -} - -fn post_aliased_mut() { - // SPURIOUS--flow - let mut v = ~3; - borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan - let _w = &mut v; //~ NOTE prior loan as mutable granted here -} +fn pre_freeze() { + // In this instance, the freeze starts before the mut borrow. -fn post_aliased_scope(cond: bool) { let mut v = ~3; - borrow(v); - if cond { inc(&mut v); } + let _w = &v; + borrow_mut(v); //~ ERROR cannot borrow } -fn loop_overarching_alias_mut() { - let mut v = ~3; - let mut _x = &mut v; //~ NOTE prior loan as mutable granted here - loop { - borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan - } -} +fn pre_const() { + // In this instance, the freeze starts before the mut borrow. -fn block_overarching_alias_mut() { let mut v = ~3; - let mut _x = &mut v; //~ NOTE prior loan as mutable granted here - for 3.times { - borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan - } -} - -fn loop_aliased_mut() { - let mut v = ~3, w = ~4; - let mut _x = &mut w; - loop { - borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan - _x = &mut v; //~ NOTE prior loan as mutable granted here - } -} - -fn while_aliased_mut(cond: bool) { - let mut v = ~3, w = ~4; - let mut _x = &mut w; - while cond { - borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan - _x = &mut v; //~ NOTE prior loan as mutable granted here - } -} - -fn while_aliased_mut_cond(cond: bool, cond2: bool) { - let mut v = ~3, w = ~4; - let mut _x = &mut w; - while cond { - borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan - if cond2 { - _x = &mut v; //~ NOTE prior loan as mutable granted here - } - } -} - -fn loop_in_block() { - let mut v = ~3, w = ~4; - let mut _x = &mut w; - for uint::range(0u, 10u) |_i| { - borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan - _x = &mut v; //~ NOTE prior loan as mutable granted here - } + let _w = &const v; + borrow_mut(v); } -fn at_most_once_block() { - fn at_most_once(f: &fn()) { f() } +fn post_freeze() { + // In this instance, the const alias starts after the borrow. - // Here, the borrow check has no way of knowing that the block is - // executed at most once. - - let mut v = ~3, w = ~4; - let mut _x = &mut w; - do at_most_once { - borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan - _x = &mut v; //~ NOTE prior loan as mutable granted here - } + let mut v = ~3; + borrow_mut(v); + let _w = &v; } 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 784fce1300f76..50dd815d49302 100644 --- a/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs +++ b/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs @@ -14,17 +14,17 @@ fn borrow(v: &int, f: &fn(x: &int)) { fn box_imm() { let v = ~3; - let _w = &v; //~ NOTE loan of immutable local variable granted here + let _w = &v; do task::spawn { debug!("v=%d", *v); - //~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan + //~^ ERROR cannot move `v` into closure } let v = ~3; - let _w = &v; //~ NOTE loan of immutable local variable granted here + let _w = &v; task::spawn(|| { debug!("v=%d", *v); - //~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan + //~^ ERROR cannot move }); } diff --git a/src/test/compile-fail/borrowck-loan-blocks-move.rs b/src/test/compile-fail/borrowck-loan-blocks-move.rs index 3af77d2df7f01..b9a79f4f3b1b1 100644 --- a/src/test/compile-fail/borrowck-loan-blocks-move.rs +++ b/src/test/compile-fail/borrowck-loan-blocks-move.rs @@ -13,8 +13,8 @@ fn take(_v: ~int) { fn box_imm() { let v = ~3; - let _w = &v; //~ NOTE loan of immutable local variable granted here - take(v); //~ ERROR moving out of immutable local variable prohibited due to outstanding loan + let _w = &v; + take(v); //~ ERROR cannot move out of `v` because it is borrowed } fn main() { 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 14cb37d775c43..f8415a38573c4 100644 --- a/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs +++ b/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs @@ -14,8 +14,8 @@ fn borrow(v: &int, f: &fn(x: &int)) { fn box_imm() { let mut v = ~3; - do borrow(v) |w| { //~ NOTE loan of mutable local variable granted here - v = ~4; //~ ERROR assigning to captured outer mutable variable in a stack closure prohibited due to outstanding loan + do borrow(v) |w| { + v = ~4; //~ ERROR cannot assign to `v` because it is borrowed assert!(*v == 3); assert!(*w == 4); } diff --git a/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs b/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs index a2ba5ad489167..f369bf208c2ce 100644 --- a/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs +++ b/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs @@ -22,13 +22,13 @@ use core::either::{Either, Left, Right}; fn g() { let mut x: Either = Left(3); - io::println(f(&mut x, &x).to_str()); //~ ERROR conflicts with prior loan + io::println(f(&mut x, &x).to_str()); //~ ERROR cannot borrow } fn h() { let mut x: Either = Left(3); let y: &Either = &x; - let z: &mut Either = &mut x; //~ ERROR conflicts with prior loan + let z: &mut Either = &mut x; //~ ERROR cannot borrow *z = *y; } diff --git a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs index a4ad7e69b3336..8c9d3009e5e67 100644 --- a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs +++ b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct Point { +struct Point { x: int, y: int, } @@ -38,10 +38,10 @@ fn b() { // Here I create an outstanding loan and check that we get conflicts: - let q = &mut p; //~ NOTE prior loan as mutable granted here + let q = &mut p; p + 3; // ok for pure fns - p.times(3); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan + p.times(3); //~ ERROR cannot borrow `p` q.x += 1; } diff --git a/src/test/compile-fail/borrowck-loan-rcvr.rs b/src/test/compile-fail/borrowck-loan-rcvr.rs index 4473574926a34..decf6228fc658 100644 --- a/src/test/compile-fail/borrowck-loan-rcvr.rs +++ b/src/test/compile-fail/borrowck-loan-rcvr.rs @@ -13,7 +13,6 @@ struct point { x: int, y: int } trait methods { fn impurem(&self); fn blockm(&self, f: &fn()); - fn purem(&self); } impl methods for point { @@ -21,9 +20,6 @@ impl methods for point { } fn blockm(&self, f: &fn()) { f() } - - fn purem(&self) { - } } fn a() { @@ -31,12 +27,11 @@ fn a() { // Here: it's ok to call even though receiver is mutable, because we // can loan it out. - p.purem(); p.impurem(); // But in this case we do not honor the loan: - do p.blockm { //~ NOTE loan of mutable local variable granted here - p.x = 10; //~ ERROR assigning to mutable field prohibited due to outstanding loan + do p.blockm { + p.x = 10; //~ ERROR cannot assign } } @@ -45,20 +40,21 @@ fn b() { // Here I create an outstanding loan and check that we get conflicts: - let l = &mut p; //~ NOTE prior loan as mutable granted here - //~^ NOTE prior loan as mutable granted here - - p.purem(); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan - p.impurem(); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan + let l = &mut p; + p.impurem(); //~ ERROR cannot borrow l.x += 1; } fn c() { - // Loaning @mut as & is considered legal due to dynamic checks: + // Loaning @mut as & is considered legal due to dynamic checks... let q = @mut point {x: 3, y: 4}; - q.purem(); q.impurem(); + + // ...but we still detect errors statically when we can. + do q.blockm { + q.x = 10; //~ ERROR cannot assign + } } fn main() { diff --git a/src/test/compile-fail/borrowck-loan-vec-content.rs b/src/test/compile-fail/borrowck-loan-vec-content.rs index d27d690437aff..6a8e64377aab2 100644 --- a/src/test/compile-fail/borrowck-loan-vec-content.rs +++ b/src/test/compile-fail/borrowck-loan-vec-content.rs @@ -24,8 +24,8 @@ 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]; - do takes_imm_elt(&v[0]) { //~ NOTE loan of mutable vec content granted here - v[1] = 4; //~ ERROR assigning to mutable vec content prohibited due to outstanding loan + do takes_imm_elt(&v[0]) { + v[1] = 4; //~ ERROR cannot assign } } diff --git a/src/test/compile-fail/borrowck-move-by-capture.rs b/src/test/compile-fail/borrowck-move-by-capture.rs index 18b4ce0640c41..c199c8795756d 100644 --- a/src/test/compile-fail/borrowck-move-by-capture.rs +++ b/src/test/compile-fail/borrowck-move-by-capture.rs @@ -4,7 +4,7 @@ fn main() { let foo = ~3; let _pfoo = &foo; let _f: @fn() -> int = || *foo + 5; - //~^ ERROR by-move capture + //~^ ERROR cannot move `foo` let bar = ~3; let _g = || { diff --git a/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs b/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs index d0b0f51d0cf77..e4e449822768b 100644 --- a/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs +++ b/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs @@ -10,7 +10,7 @@ fn main() { let x: int = 3; - let y: &mut int = &mut x; //~ ERROR illegal borrow + let y: &mut int = &mut x; //~ ERROR cannot borrow *y = 5; debug!(*y); } diff --git a/src/test/compile-fail/borrowck-mut-boxed-vec.rs b/src/test/compile-fail/borrowck-mut-boxed-vec.rs index d4c0b5a1e9bf9..a69edebce51fb 100644 --- a/src/test/compile-fail/borrowck-mut-boxed-vec.rs +++ b/src/test/compile-fail/borrowck-mut-boxed-vec.rs @@ -10,8 +10,8 @@ fn main() { let v = @mut [ 1, 2, 3 ]; - for v.each |_x| { //~ ERROR illegal borrow - v[1] = 4; + for v.each |_x| { + v[1] = 4; //~ ERROR cannot assign } } diff --git a/src/test/compile-fail/borrowck-mut-deref-comp.rs b/src/test/compile-fail/borrowck-mut-deref-comp.rs index 540793d4135f2..d1dc296197892 100644 --- a/src/test/compile-fail/borrowck-mut-deref-comp.rs +++ b/src/test/compile-fail/borrowck-mut-deref-comp.rs @@ -11,8 +11,8 @@ struct foo(~int); fn borrow(x: @mut foo) { - let _y = &***x; //~ ERROR illegal borrow unless pure - *x = foo(~4); //~ NOTE impure due to assigning to dereference of mutable @ pointer + let _y = &***x; + *x = foo(~4); //~ ERROR cannot assign } fn main() { diff --git a/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs b/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs index bc0340983ae34..ec17976c5065c 100644 --- a/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs +++ b/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs @@ -14,5 +14,5 @@ fn write(v: &mut [int]) { fn main() { let v = ~[1, 2, 3]; - write(v); //~ ERROR illegal borrow + write(v); //~ ERROR cannot borrow } diff --git a/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs b/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs index 4af3bc17240ce..ed270de51e2ed 100644 --- a/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs +++ b/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs @@ -19,9 +19,9 @@ enum cycle { fn main() { let mut x = ~node(node_ {a: ~empty}); // Create a cycle! - match *x { //~ NOTE loan of mutable local variable granted here + match *x { node(ref mut y) => { - y.a = x; //~ ERROR moving out of mutable local variable prohibited due to outstanding loan + y.a = x; //~ ERROR cannot move out of } empty => {} }; diff --git a/src/test/compile-fail/borrowck-pat-by-value-binding.rs b/src/test/compile-fail/borrowck-pat-by-value-binding.rs index d8c8841d391a2..d60ed3d0e372b 100644 --- a/src/test/compile-fail/borrowck-pat-by-value-binding.rs +++ b/src/test/compile-fail/borrowck-pat-by-value-binding.rs @@ -12,23 +12,24 @@ fn process(_t: T) {} fn match_const_opt_by_mut_ref(v: &const Option) { match *v { - Some(ref mut i) => process(i), //~ ERROR illegal borrow + Some(ref mut i) => process(i), //~ ERROR cannot borrow + //~^ ERROR unsafe borrow of aliasable, const value None => () } } fn match_const_opt_by_const_ref(v: &const Option) { match *v { - Some(ref const i) => process(i), //~ ERROR illegal borrow unless pure - //~^ NOTE impure due to + Some(ref const i) => process(i), + //~^ ERROR unsafe borrow of aliasable, const value None => () } } fn match_const_opt_by_imm_ref(v: &const Option) { match *v { - Some(ref i) => process(i), //~ ERROR illegal borrow unless pure - //~^ NOTE impure due to + Some(ref i) => process(i), //~ ERROR cannot borrow + //~^ ERROR unsafe borrow of aliasable, const value None => () } } diff --git a/src/test/compile-fail/borrowck-pat-enum.rs b/src/test/compile-fail/borrowck-pat-enum.rs index 4aa1ecc0ce3fe..c50357e8b9c62 100644 --- a/src/test/compile-fail/borrowck-pat-enum.rs +++ b/src/test/compile-fail/borrowck-pat-enum.rs @@ -26,7 +26,8 @@ fn match_ref_unused(&&v: Option) { fn match_const_reg(v: &const Option) -> int { match *v { - Some(ref i) => {*i} // OK because this is pure + Some(ref i) => {*i} //~ ERROR cannot borrow + //~^ ERROR unsafe borrow None => {0} } } @@ -43,8 +44,8 @@ fn match_const_reg_unused(v: &const Option) { fn match_const_reg_impure(v: &const Option) { match *v { - Some(ref i) => {impure(*i)} //~ ERROR illegal borrow unless pure - //~^ NOTE impure due to access to impure function + Some(ref i) => {impure(*i)} //~ ERROR cannot borrow + //~^ ERROR unsafe borrow None => {} } } @@ -56,5 +57,12 @@ fn match_imm_reg(v: &Option) { } } +fn match_mut_reg(v: &mut Option) { + match *v { + Some(ref i) => {impure(*i)} // OK, frozen + None => {} + } +} + fn main() { } diff --git a/src/test/compile-fail/borrowck-pat-reassign-binding.rs b/src/test/compile-fail/borrowck-pat-reassign-binding.rs index ca1fdc97c22f8..d05160132c6c2 100644 --- a/src/test/compile-fail/borrowck-pat-reassign-binding.rs +++ b/src/test/compile-fail/borrowck-pat-reassign-binding.rs @@ -12,11 +12,14 @@ fn main() { let mut x: Option = None; - match x { //~ NOTE loan of mutable local variable granted here - None => {} + match x { + None => { + // Note: on this branch, no borrow has occurred. + x = Some(0); + } Some(ref i) => { - // Not ok: i is an outstanding ptr into x. - x = Some(*i+1); //~ ERROR assigning to mutable local variable prohibited due to outstanding loan + // But on this branch, `i` is an outstanding borrow + x = Some(*i+1); //~ ERROR cannot assign to `x` } } copy x; // just to prevent liveness warnings diff --git a/src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs b/src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs deleted file mode 100644 index dd6eca951b8f3..0000000000000 --- a/src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2012 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. - -// xfail-pretty -- comments are infaithfully preserved - -fn main() { - let mut x = None; - match x { //~ NOTE loan of mutable local variable granted here - None => { - // It is ok to reassign x here, because there is in - // fact no outstanding loan of x! - x = Some(0); - } - Some(ref _i) => { - x = Some(1); //~ ERROR assigning to mutable local variable prohibited due to outstanding loan - } - } - copy x; // just to prevent liveness warnings -} diff --git a/src/test/compile-fail/borrowck-reborrow-from-mut.rs b/src/test/compile-fail/borrowck-reborrow-from-mut.rs index 60f817dee0c54..b4bd64f213586 100644 --- a/src/test/compile-fail/borrowck-reborrow-from-mut.rs +++ b/src/test/compile-fail/borrowck-reborrow-from-mut.rs @@ -20,17 +20,17 @@ struct Bar { fn borrow_same_field_twice_mut_mut(foo: &mut Foo) { let _bar1 = &mut foo.bar1; - let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let _bar2 = &mut foo.bar1; //~ ERROR cannot borrow } fn borrow_same_field_twice_mut_imm(foo: &mut Foo) { let _bar1 = &mut foo.bar1; - let _bar2 = &foo.bar1; //~ ERROR conflicts with prior loan + let _bar2 = &foo.bar1; //~ ERROR cannot borrow } fn borrow_same_field_twice_imm_mut(foo: &mut Foo) { let _bar1 = &foo.bar1; - let _bar2 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let _bar2 = &mut foo.bar1; //~ ERROR cannot borrow } fn borrow_same_field_twice_imm_imm(foo: &mut Foo) { @@ -53,34 +53,34 @@ fn borrow_var_and_pattern(foo: &mut Foo) { let _bar1 = &mut foo.bar1; match *foo { Foo { bar1: ref mut _bar1, bar2: _ } => {} - //~^ ERROR conflicts with prior loan + //~^ ERROR cannot borrow } } fn borrow_mut_and_base_imm(foo: &mut Foo) { let _bar1 = &mut foo.bar1.int1; - let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan - let _foo2 = &*foo; //~ ERROR conflicts with prior loan + let _foo1 = &foo.bar1; //~ ERROR cannot borrow + let _foo2 = &*foo; //~ ERROR cannot borrow } fn borrow_mut_and_base_mut(foo: &mut Foo) { let _bar1 = &mut foo.bar1.int1; - let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow } fn borrow_mut_and_base_mut2(foo: &mut Foo) { let _bar1 = &mut foo.bar1.int1; - let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan + let _foo2 = &mut *foo; //~ ERROR cannot borrow } fn borrow_imm_and_base_mut(foo: &mut Foo) { let _bar1 = &foo.bar1.int1; - let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan + let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow } fn borrow_imm_and_base_mut2(foo: &mut Foo) { let _bar1 = &foo.bar1.int1; - let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan + let _foo2 = &mut *foo; //~ ERROR cannot borrow } fn borrow_imm_and_base_imm(foo: &mut Foo) { @@ -95,7 +95,7 @@ fn borrow_mut_and_imm(foo: &mut Foo) { } fn borrow_mut_from_imm(foo: &Foo) { - let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow + let _bar1 = &mut foo.bar1; //~ ERROR cannot borrow } fn borrow_long_path_both_mut(foo: &mut Foo) { diff --git a/src/test/compile-fail/borrowck-ref-into-rvalue.rs b/src/test/compile-fail/borrowck-ref-into-rvalue.rs index 37ee747069ccf..18865ad7d54d8 100644 --- a/src/test/compile-fail/borrowck-ref-into-rvalue.rs +++ b/src/test/compile-fail/borrowck-ref-into-rvalue.rs @@ -10,12 +10,12 @@ fn main() { let msg; - match Some(~"Hello") { //~ ERROR illegal borrow - Some(ref m) => { + match Some(~"Hello") { + Some(ref m) => { //~ ERROR borrowed value does not live long enough msg = m; - }, + }, None => { fail!() } - } + } io::println(*msg); } diff --git a/src/test/compile-fail/borrowck-ref-mut-of-imm.rs b/src/test/compile-fail/borrowck-ref-mut-of-imm.rs index aad86241e9a43..3a37116a1664d 100644 --- a/src/test/compile-fail/borrowck-ref-mut-of-imm.rs +++ b/src/test/compile-fail/borrowck-ref-mut-of-imm.rs @@ -11,7 +11,7 @@ fn destructure(x: Option) -> int { match x { None => 0, - Some(ref mut v) => *v //~ ERROR illegal borrow + Some(ref mut v) => *v //~ ERROR cannot borrow } } diff --git a/src/test/compile-fail/borrowck-unary-move-2.rs b/src/test/compile-fail/borrowck-unary-move-2.rs index 520772f1ceea9..898830bbe55ba 100644 --- a/src/test/compile-fail/borrowck-unary-move-2.rs +++ b/src/test/compile-fail/borrowck-unary-move-2.rs @@ -28,5 +28,5 @@ struct wrapper(noncopyable); fn main() { let x1 = wrapper(noncopyable()); - let _x2 = *x1; //~ ERROR moving out of anonymous field + let _x2 = *x1; //~ ERROR cannot move out } diff --git a/src/test/compile-fail/borrowck-unary-move.rs b/src/test/compile-fail/borrowck-unary-move.rs index f95b365ee2ef9..107e478004abb 100644 --- a/src/test/compile-fail/borrowck-unary-move.rs +++ b/src/test/compile-fail/borrowck-unary-move.rs @@ -9,8 +9,8 @@ // except according to those terms. fn foo(+x: ~int) -> int { - let y = &*x; //~ NOTE loan of argument granted here - free(x); //~ ERROR moving out of argument prohibited due to outstanding loan + let y = &*x; + free(x); //~ ERROR cannot move out of `*x` because it is borrowed *y } diff --git a/src/test/compile-fail/borrowck-uniq-via-box.rs b/src/test/compile-fail/borrowck-uniq-via-box.rs deleted file mode 100644 index e1c0e67ff8dcc..0000000000000 --- a/src/test/compile-fail/borrowck-uniq-via-box.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -struct Rec { - f: ~int, -} - -struct Outer { - f: Inner -} - -struct Inner { - g: Innermost -} - -struct Innermost { - h: ~int, -} - -fn borrow(_v: &int) {} - -fn box_mut(v: @mut ~int) { - borrow(*v); //~ ERROR illegal borrow unless pure -} - -fn box_mut_rec(v: @mut Rec) { - borrow(v.f); //~ ERROR illegal borrow unless pure -} - -fn box_mut_recs(v: @mut Outer) { - borrow(v.f.g.h); //~ ERROR illegal borrow unless pure -} - -fn box_imm(v: @~int) { - borrow(*v); // OK -} - -fn box_imm_rec(v: @Rec) { - borrow(v.f); // OK -} - -fn box_imm_recs(v: @Outer) { - borrow(v.f.g.h); // OK -} - -fn main() { -} - diff --git a/src/test/compile-fail/borrowck-uniq-via-lend.rs b/src/test/compile-fail/borrowck-uniq-via-lend.rs index ee96237a26c82..80ba1968bc751 100644 --- a/src/test/compile-fail/borrowck-uniq-via-lend.rs +++ b/src/test/compile-fail/borrowck-uniq-via-lend.rs @@ -43,8 +43,8 @@ fn aliased_const() { fn aliased_mut() { let mut v = ~3; - let _w = &mut v; //~ NOTE prior loan as mutable granted here - borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan + let _w = &mut v; + borrow(v); //~ ERROR cannot borrow `*v` } fn aliased_other() { @@ -56,8 +56,8 @@ fn aliased_other() { fn aliased_other_reassign() { let mut v = ~3, w = ~4; let mut _x = &mut w; - _x = &mut v; //~ NOTE prior loan as mutable granted here - borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan + _x = &mut v; + borrow(v); //~ ERROR cannot borrow `*v` } fn main() { diff --git a/src/test/compile-fail/borrowck-uniq-via-ref.rs b/src/test/compile-fail/borrowck-uniq-via-ref.rs index 2cf363e13ee09..8bf627d991911 100644 --- a/src/test/compile-fail/borrowck-uniq-via-ref.rs +++ b/src/test/compile-fail/borrowck-uniq-via-ref.rs @@ -25,6 +25,7 @@ struct Innermost { } fn borrow(_v: &int) {} +fn borrow_const(_v: &const int) {} fn box_mut(v: &mut ~int) { borrow(*v); // OK: &mut -> &imm @@ -51,15 +52,15 @@ fn box_imm_recs(v: &Outer) { } fn box_const(v: &const ~int) { - borrow(*v); //~ ERROR illegal borrow unless pure + borrow_const(*v); //~ ERROR unsafe borrow } fn box_const_rec(v: &const Rec) { - borrow(v.f); //~ ERROR illegal borrow unless pure + borrow_const(v.f); //~ ERROR unsafe borrow } fn box_const_recs(v: &const Outer) { - borrow(v.f.g.h); //~ ERROR illegal borrow unless pure + borrow_const(v.f.g.h); //~ ERROR unsafe borrow } fn main() { diff --git a/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs b/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs index c8a0dbedd5d95..0c21b64bb0fb0 100644 --- a/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs @@ -1,7 +1,7 @@ fn a() -> &[int] { let vec = [1, 2, 3, 4]; - let tail = match vec { //~ ERROR illegal borrow - [_, ..tail] => tail, + let tail = match vec { + [_, ..tail] => tail, //~ ERROR does not live long enough _ => fail!(~"a") }; tail @@ -9,8 +9,8 @@ fn a() -> &[int] { fn b() -> &[int] { let vec = [1, 2, 3, 4]; - let init = match vec { //~ ERROR illegal borrow - [..init, _] => init, + let init = match vec { + [..init, _] => init, //~ ERROR does not live long enough _ => fail!(~"b") }; init @@ -18,8 +18,8 @@ fn b() -> &[int] { fn c() -> &[int] { let vec = [1, 2, 3, 4]; - let slice = match vec { //~ ERROR illegal borrow - [_, ..slice, _] => slice, + let slice = match vec { + [_, ..slice, _] => slice, //~ ERROR does not live long enough _ => fail!(~"c") }; slice diff --git a/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs b/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs index 27902100373a9..6b51fc8f5b3d7 100644 --- a/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs @@ -2,7 +2,7 @@ fn a() { let mut v = ~[1, 2, 3]; match v { [_a, ..tail] => { - v.push(tail[0] + tail[1]); //~ ERROR conflicts with prior loan + v.push(tail[0] + tail[1]); //~ ERROR cannot borrow } _ => {} }; 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 16b48aedb0c7f..2898e312930fe 100644 --- a/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs @@ -1,8 +1,9 @@ fn main() { let mut a = [1, 2, 3, 4]; - let _ = match a { + let t = match a { [1, 2, ..tail] => tail, _ => core::util::unreachable() }; - a[0] = 0; //~ ERROR: assigning to mutable vec content prohibited due to outstanding loan + 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 05ff85d612c82..16711ea61ffac 100644 --- a/src/test/compile-fail/borrowck-vec-pattern-nesting.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs @@ -2,7 +2,7 @@ fn a() { let mut vec = [~1, ~2, ~3]; match vec { [~ref _a] => { - vec[0] = ~4; //~ ERROR prohibited due to outstanding loan + vec[0] = ~4; //~ ERROR cannot assign to `vec[]` because it is borrowed } _ => fail!(~"foo") } @@ -12,7 +12,7 @@ fn b() { let mut vec = [~1, ~2, ~3]; match vec { [.._b] => { - vec[0] = ~4; //~ ERROR prohibited due to outstanding loan + vec[0] = ~4; //~ ERROR cannot assign to `vec[]` because it is borrowed } } } 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 714a80def9358..dbdd8f0809a6e 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 @@ -1,7 +1,7 @@ fn a() -> &int { let vec = [1, 2, 3, 4]; - let tail = match vec { //~ ERROR illegal borrow - [_a, ..tail] => &tail[0], + let tail = match vec { + [_a, ..tail] => &tail[0], //~ ERROR borrowed value does not live long enough _ => fail!(~"foo") }; tail diff --git a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs index e47ad721b0d7b..5ef23897ec0d3 100644 --- a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs +++ b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs @@ -1,6 +1,6 @@ fn main() { let mut b = ~3; - let _x = &mut *b; //~ NOTE prior loan as mutable granted here - let _y = &mut *b; //~ ERROR loan of dereference of mutable ~ pointer as mutable conflicts with prior loan + let _x = &mut *b; + let _y = &mut *b; //~ ERROR cannot borrow } diff --git a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs index 015f368ecb068..a744127a600db 100644 --- a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs +++ b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs @@ -1,8 +1,8 @@ fn main() { let mut a = ~3; - let mut b = &mut a; //~ NOTE loan of mutable local variable granted here + let mut b = &mut a; let _c = &mut *b; - let mut d = /*move*/ a; //~ ERROR moving out of mutable local variable prohibited due to outstanding loan + let mut d = /*move*/ a; //~ ERROR cannot move out *d += 1; } diff --git a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs index 36d32fddda150..44da00d8d1707 100644 --- a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs +++ b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs @@ -1,7 +1,7 @@ fn main() { let mut b = ~3; - let _x = &mut *b; //~ NOTE loan of mutable local variable granted here - let mut y = /*move*/ b; //~ ERROR moving out of mutable local variable prohibited + let _x = &mut *b; + let mut y = /*move*/ b; //~ ERROR cannot move out *y += 1; } diff --git a/src/test/compile-fail/borrowck-wg-move-base-2.rs b/src/test/compile-fail/borrowck-wg-move-base-2.rs index ba85616e63f28..8a2187714534a 100644 --- a/src/test/compile-fail/borrowck-wg-move-base-2.rs +++ b/src/test/compile-fail/borrowck-wg-move-base-2.rs @@ -2,7 +2,7 @@ fn foo(x: &mut int) { let mut a = 3; let mut _y = &mut *x; let _z = &mut *_y; - _y = &mut a; //~ ERROR assigning to mutable local variable prohibited + _y = &mut a; //~ ERROR cannot assign } fn main() { diff --git a/src/test/compile-fail/fn-variance-3.rs b/src/test/compile-fail/fn-variance-3.rs index 5df2007721def..4d145d3f9ea3a 100644 --- a/src/test/compile-fail/fn-variance-3.rs +++ b/src/test/compile-fail/fn-variance-3.rs @@ -31,5 +31,5 @@ fn main() { // mutability check will fail, because the // type of r has been inferred to be // fn(@const int) -> @const int - *r(@mut 3) = 4; //~ ERROR assigning to dereference of const @ pointer + *r(@mut 3) = 4; //~ ERROR cannot assign to const dereference of @ pointer } diff --git a/src/test/compile-fail/immut-function-arguments.rs b/src/test/compile-fail/immut-function-arguments.rs index 2084729372d1d..66b5bd172cace 100644 --- a/src/test/compile-fail/immut-function-arguments.rs +++ b/src/test/compile-fail/immut-function-arguments.rs @@ -9,11 +9,11 @@ // except according to those terms. fn f(y: ~int) { - *y = 5; //~ ERROR assigning to dereference of immutable ~ pointer + *y = 5; //~ ERROR cannot assign } fn g() { - let _frob: &fn(~int) = |q| { *q = 2; }; //~ ERROR assigning to dereference of immutable ~ pointer + let _frob: &fn(~int) = |q| { *q = 2; }; //~ ERROR cannot assign } diff --git a/src/test/compile-fail/index_message.rs b/src/test/compile-fail/index_message.rs index 3611dbb6866cb..26dd98757a8c2 100644 --- a/src/test/compile-fail/index_message.rs +++ b/src/test/compile-fail/index_message.rs @@ -10,5 +10,5 @@ fn main() { let z = (); - debug!(z[0]); //~ ERROR cannot index a value of type `()` + let _ = z[0]; //~ ERROR cannot index a value of type `()` } diff --git a/src/test/compile-fail/issue-1896-1.rs b/src/test/compile-fail/issue-1896-1.rs index fc5132d65104f..13adcd42da2b8 100644 --- a/src/test/compile-fail/issue-1896-1.rs +++ b/src/test/compile-fail/issue-1896-1.rs @@ -8,11 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test that we require managed closures to be rooted when borrowed. + struct boxedFn<'self> { theFn: &'self fn() -> uint } fn createClosure (closedUint: uint) -> boxedFn { let theFn: @fn() -> uint = || closedUint; - boxedFn {theFn: theFn} //~ ERROR illegal borrow + boxedFn {theFn: theFn} //~ ERROR cannot root } fn main () { diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs index 2842d884c9918..cdc8d546dd848 100644 --- a/src/test/compile-fail/issue-2149.rs +++ b/src/test/compile-fail/issue-2149.rs @@ -23,5 +23,4 @@ impl vec_monad for ~[A] { fn main() { ["hi"].bind(|x| [x] ); //~^ ERROR type `[&'static str, .. 1]` does not implement any method in scope named `bind` - //~^^ ERROR Unconstrained region variable } diff --git a/src/test/compile-fail/issue-2151.rs b/src/test/compile-fail/issue-2151.rs index e2bbda7d65a99..bb6d47a47622b 100644 --- a/src/test/compile-fail/issue-2151.rs +++ b/src/test/compile-fail/issue-2151.rs @@ -10,7 +10,6 @@ fn main() { for vec::each(fail!()) |i| { - debug!(i * 2); - //~^ ERROR the type of this value must be known + let _ = i * 2; //~ ERROR the type of this value must be known }; } diff --git a/src/test/compile-fail/issue-2590.rs b/src/test/compile-fail/issue-2590.rs index 7a99ab8a94f16..a0b967d59593a 100644 --- a/src/test/compile-fail/issue-2590.rs +++ b/src/test/compile-fail/issue-2590.rs @@ -18,7 +18,7 @@ trait parse { impl parse for parser { fn parse(&self) -> ~[int] { - self.tokens //~ ERROR moving out of immutable field + self.tokens //~ ERROR cannot move out of field } } diff --git a/src/test/compile-fail/issue-3044.rs b/src/test/compile-fail/issue-3044.rs index fcd5b1deee552..23b11bbe409e1 100644 --- a/src/test/compile-fail/issue-3044.rs +++ b/src/test/compile-fail/issue-3044.rs @@ -11,7 +11,6 @@ fn main() { let needlesArr: ~[char] = ~['a', 'f']; do vec::foldr(needlesArr) |x, y| { - //~^ ERROR Unconstrained region variable #2 } //~^ ERROR 2 parameters were supplied (including the closure passed by the `do` keyword) // diff --git a/src/test/compile-fail/issue-511.rs b/src/test/compile-fail/issue-511.rs index 90c46e5d602c9..c872f89d88450 100644 --- a/src/test/compile-fail/issue-511.rs +++ b/src/test/compile-fail/issue-511.rs @@ -17,5 +17,5 @@ fn f(o: &mut Option) { fn main() { f::(&mut option::None); - //~^ ERROR illegal borrow: creating mutable alias to static item + //~^ ERROR cannot borrow } diff --git a/src/test/compile-fail/kindck-owned-trait-contains.rs b/src/test/compile-fail/kindck-owned-trait-contains.rs index 54ee8bcc70e37..6bb90bff228d4 100644 --- a/src/test/compile-fail/kindck-owned-trait-contains.rs +++ b/src/test/compile-fail/kindck-owned-trait-contains.rs @@ -29,4 +29,7 @@ fn main() { }; assert!(3 == *(y.get())); //~ ERROR dereference of reference outside its lifetime //~^ ERROR reference is not valid outside of its lifetime + //~^^ ERROR reference is not valid outside of its lifetime + //~^^^ ERROR reference is not valid outside of its lifetime + //~^^^^ ERROR cannot infer an appropriate lifetime } diff --git a/src/test/compile-fail/lambda-mutate-nested.rs b/src/test/compile-fail/lambda-mutate-nested.rs index 8b009b91af96c..bfd1e12f3a6e0 100644 --- a/src/test/compile-fail/lambda-mutate-nested.rs +++ b/src/test/compile-fail/lambda-mutate-nested.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:assigning to captured outer immutable variable in a stack closure // Make sure that nesting a block within a @fn doesn't let us // mutate upvars from a @fn. fn f2(x: &fn()) { x(); } @@ -16,6 +15,7 @@ fn f2(x: &fn()) { x(); } fn main() { let i = 0; let ctr: @fn() -> int = || { f2(|| i = i + 1 ); i }; + //~^ ERROR cannot assign error!(ctr()); error!(ctr()); error!(ctr()); diff --git a/src/test/compile-fail/lambda-mutate.rs b/src/test/compile-fail/lambda-mutate.rs index ee5b3d8968418..a848d8698a3d6 100644 --- a/src/test/compile-fail/lambda-mutate.rs +++ b/src/test/compile-fail/lambda-mutate.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:assigning to captured outer variable in a heap closure // Make sure we can't write to upvars from @fns fn main() { let i = 0; let ctr: @fn() -> int = || { i = i + 1; i }; + //~^ ERROR cannot assign error!(ctr()); error!(ctr()); error!(ctr()); diff --git a/src/test/compile-fail/moves-based-on-type-block-bad.rs b/src/test/compile-fail/moves-based-on-type-block-bad.rs index 020dadfc96cd2..feeaadeea8263 100644 --- a/src/test/compile-fail/moves-based-on-type-block-bad.rs +++ b/src/test/compile-fail/moves-based-on-type-block-bad.rs @@ -16,7 +16,7 @@ fn main() { let s = S { x: ~Bar(~42) }; loop { do f(&s) |hellothere| { - match hellothere.x { //~ ERROR moving out of immutable field + match hellothere.x { //~ ERROR cannot move out ~Foo(_) => {} ~Bar(x) => io::println(x.to_str()), ~Baz => {} diff --git a/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs index 3c15047a29697..ecd58d485a89d 100644 --- a/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs +++ b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs @@ -13,6 +13,6 @@ fn test(_x: ~uint) {} fn main() { let i = ~3; for uint::range(0, 10) |_x| { - test(i); //~ ERROR moving out of captured outer immutable variable in a stack closure + test(i); //~ ERROR cannot move out } } diff --git a/src/test/compile-fail/mutable-class-fields-2.rs b/src/test/compile-fail/mutable-class-fields-2.rs index 56c715c9847a5..f5d24b316414e 100644 --- a/src/test/compile-fail/mutable-class-fields-2.rs +++ b/src/test/compile-fail/mutable-class-fields-2.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:assigning to immutable field struct cat { priv mut meows : uint, @@ -17,7 +16,7 @@ struct cat { pub impl cat { fn eat(&self) { - self.how_hungry -= 5; + self.how_hungry -= 5; //~ ERROR cannot assign } } diff --git a/src/test/compile-fail/mutable-class-fields.rs b/src/test/compile-fail/mutable-class-fields.rs index 6d11a98c0cb2f..8bebec7134cc3 100644 --- a/src/test/compile-fail/mutable-class-fields.rs +++ b/src/test/compile-fail/mutable-class-fields.rs @@ -8,12 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:assigning to immutable field struct cat { priv mut meows : uint, - how_hungry : int, - } fn cat(in_x : uint, in_y : int) -> cat { @@ -25,5 +22,5 @@ fn cat(in_x : uint, in_y : int) -> cat { fn main() { let nyan : cat = cat(52u, 99); - nyan.how_hungry = 0; + nyan.how_hungry = 0; //~ ERROR cannot assign } diff --git a/src/test/compile-fail/mutable-huh-ptr-assign.rs b/src/test/compile-fail/mutable-huh-ptr-assign.rs index ed356f4001dd6..6b3fd4f715384 100644 --- a/src/test/compile-fail/mutable-huh-ptr-assign.rs +++ b/src/test/compile-fail/mutable-huh-ptr-assign.rs @@ -12,7 +12,7 @@ extern mod std; fn main() { unsafe fn f(&&v: *const int) { - *v = 1 //~ ERROR assigning to dereference of const * pointer + *v = 1 //~ ERROR cannot assign } unsafe { diff --git a/src/test/compile-fail/regions-addr-of-arg.rs b/src/test/compile-fail/regions-addr-of-arg.rs index 7f2140d96e16c..4fff5a6f87c78 100644 --- a/src/test/compile-fail/regions-addr-of-arg.rs +++ b/src/test/compile-fail/regions-addr-of-arg.rs @@ -9,7 +9,7 @@ // except according to those terms. fn foo(a: int) { - let _p: &'static int = &a; //~ ERROR illegal borrow + let _p: &'static int = &a; //~ ERROR borrowed value does not live long enough } fn bar(a: int) { diff --git a/src/test/compile-fail/regions-creating-enums.rs b/src/test/compile-fail/regions-creating-enums.rs index 120428e02f4cb..2ab0c14b49b65 100644 --- a/src/test/compile-fail/regions-creating-enums.rs +++ b/src/test/compile-fail/regions-creating-enums.rs @@ -30,12 +30,12 @@ fn compute(x: &ast) -> uint { fn map_nums(x: &ast, f: &fn(uint) -> uint) -> &ast { match *x { num(x) => { - return &num(f(x)); //~ ERROR illegal borrow + return &num(f(x)); //~ ERROR borrowed value does not live long enough } add(x, y) => { let m_x = map_nums(x, f); let m_y = map_nums(y, f); - return &add(m_x, m_y); //~ ERROR illegal borrow + return &add(m_x, m_y); //~ ERROR borrowed value does not live long enough } } } diff --git a/src/test/compile-fail/regions-creating-enums4.rs b/src/test/compile-fail/regions-creating-enums4.rs index 1cb378cf406f8..8f764745697c7 100644 --- a/src/test/compile-fail/regions-creating-enums4.rs +++ b/src/test/compile-fail/regions-creating-enums4.rs @@ -14,8 +14,7 @@ enum ast<'self> { } 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 an appropriate lifetime } fn main() { diff --git a/src/test/compile-fail/regions-escape-bound-fn.rs b/src/test/compile-fail/regions-escape-bound-fn.rs index c81ef77f497db..5ac5e334be23d 100644 --- a/src/test/compile-fail/regions-escape-bound-fn.rs +++ b/src/test/compile-fail/regions-escape-bound-fn.rs @@ -14,6 +14,6 @@ fn with_int(f: &fn(x: &int)) { } fn main() { - let mut x: Option<&int> = None; //~ ERROR cannot infer + let mut x: Option<&int> = None; //~ ERROR cannot infer with_int(|y| x = Some(y)); } 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 ac10b5c454a85..19bd0bf9747bb 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 illegal borrow + p = &x; //~ ERROR borrowed value 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 da5e3c2660ef7..92e2cd73dfbd8 100644 --- a/src/test/compile-fail/regions-escape-loop-via-vec.rs +++ b/src/test/compile-fail/regions-escape-loop-via-vec.rs @@ -14,8 +14,8 @@ fn broken() { let mut _y = ~[&mut x]; while x < 10 { let mut z = x; - _y.push(&mut z); //~ ERROR illegal borrow - x += 1; //~ ERROR assigning to mutable local variable prohibited due to outstanding loan + _y.push(&mut z); //~ ERROR borrowed value does not live long enough + x += 1; //~ ERROR cannot assign } } diff --git a/src/test/compile-fail/regions-escape-via-trait-or-not.rs b/src/test/compile-fail/regions-escape-via-trait-or-not.rs index f7165784c7975..aa431d6b81c6e 100644 --- a/src/test/compile-fail/regions-escape-via-trait-or-not.rs +++ b/src/test/compile-fail/regions-escape-via-trait-or-not.rs @@ -23,13 +23,8 @@ fn with(f: &fn(x: &int) -> R) -> int { } fn return_it() -> int { - with(|o| o) - //~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements - //~^^ ERROR reference is not valid outside of its lifetime - //~^^^ ERROR reference is not valid outside of its lifetime + with(|o| o) //~ ERROR reference is not valid outside of its lifetime } fn main() { - let x = return_it(); - debug!("foo=%d", x); } diff --git a/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs b/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs index a8b7ae1b9c8e4..6e9d6c1ef0fde 100644 --- a/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs +++ b/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs @@ -18,7 +18,7 @@ fn x_coord<'r>(p: &'r point) -> &'r int { } fn foo(p: @point) -> &int { - let xc = x_coord(p); //~ ERROR illegal borrow + let xc = x_coord(p); //~ ERROR cannot root assert!(*xc == 3); return xc; } diff --git a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs index bf8f227b5730e..d23e20130f782 100644 --- a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs +++ b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs @@ -17,7 +17,7 @@ fn foo(cond: &fn() -> bool, box: &fn() -> @int) { // Here we complain because the resulting region // of this borrow is the fn body as a whole. - y = borrow(x); //~ ERROR illegal borrow: cannot root managed value long enough + y = borrow(x); //~ ERROR cannot root assert!(*x == *y); if cond() { break; } diff --git a/src/test/compile-fail/regions-nested-fns-2.rs b/src/test/compile-fail/regions-nested-fns-2.rs index 2e9a4eb141037..fe995052c52e4 100644 --- a/src/test/compile-fail/regions-nested-fns-2.rs +++ b/src/test/compile-fail/regions-nested-fns-2.rs @@ -13,7 +13,7 @@ fn ignore(_f: &fn<'z>(&'z int) -> &'z int) {} fn nested() { let y = 3; ignore(|z| { - if false { &y } else { z } //~ ERROR illegal borrow + if false { &y } else { z } //~ ERROR borrowed value does not live long enough }); } diff --git a/src/test/compile-fail/regions-nested-fns.rs b/src/test/compile-fail/regions-nested-fns.rs index 3089c362a5044..74399967446ea 100644 --- a/src/test/compile-fail/regions-nested-fns.rs +++ b/src/test/compile-fail/regions-nested-fns.rs @@ -16,7 +16,7 @@ fn nested<'x>(x: &'x int) { ignore::<&fn<'z>(&'z int)>(|z| { ay = x; - ay = &y; //~ ERROR cannot infer an appropriate lifetime + ay = &y; ay = z; }); diff --git a/src/test/compile-fail/regions-ret-borrowed-1.rs b/src/test/compile-fail/regions-ret-borrowed-1.rs index f916b0d95c2ee..f600f6f6edd92 100644 --- a/src/test/compile-fail/regions-ret-borrowed-1.rs +++ b/src/test/compile-fail/regions-ret-borrowed-1.rs @@ -19,6 +19,7 @@ fn with<'a, R>(f: &fn(x: &'a int) -> R) -> R { fn return_it<'a>() -> &'a int { with(|o| o) //~ ERROR mismatched types //~^ ERROR reference is not valid outside of its lifetime + //~^^ ERROR reference is not valid outside of its lifetime } fn main() { diff --git a/src/test/compile-fail/regions-ret-borrowed.rs b/src/test/compile-fail/regions-ret-borrowed.rs index 157b99de9e806..a3540f8f931f9 100644 --- a/src/test/compile-fail/regions-ret-borrowed.rs +++ b/src/test/compile-fail/regions-ret-borrowed.rs @@ -22,6 +22,7 @@ fn with(f: &fn(x: &int) -> R) -> R { fn return_it() -> &int { with(|o| o) //~ ERROR mismatched types //~^ ERROR reference is not valid outside of its lifetime + //~^^ ERROR reference is not valid outside of its lifetime } fn main() { diff --git a/src/test/compile-fail/regions-ret.rs b/src/test/compile-fail/regions-ret.rs index be7b28f6ef4b5..f1a7bdf228166 100644 --- a/src/test/compile-fail/regions-ret.rs +++ b/src/test/compile-fail/regions-ret.rs @@ -9,7 +9,7 @@ // except according to those terms. fn f<'a>(_x : &'a int) -> &'a int { - return &3; //~ ERROR illegal borrow + return &3; //~ ERROR borrowed value does not live long enough } fn main() { diff --git a/src/test/compile-fail/regions-var-type-out-of-scope.rs b/src/test/compile-fail/regions-var-type-out-of-scope.rs index 7d75ac7434931..addf20fd70249 100644 --- a/src/test/compile-fail/regions-var-type-out-of-scope.rs +++ b/src/test/compile-fail/regions-var-type-out-of-scope.rs @@ -14,7 +14,7 @@ fn foo(cond: bool) { let mut x; if cond { - x = &3; //~ ERROR illegal borrow: borrowed value does not live long enough + x = &3; //~ ERROR borrowed value does not live long enough assert!((*x == 3)); } } diff --git a/src/test/compile-fail/swap-no-lval.rs b/src/test/compile-fail/swap-no-lval.rs index 4fe30792e4b31..eca5fb0d315d8 100644 --- a/src/test/compile-fail/swap-no-lval.rs +++ b/src/test/compile-fail/swap-no-lval.rs @@ -10,6 +10,6 @@ fn main() { 5 <-> 3; - //~^ ERROR swapping to and from non-lvalue - //~^^ ERROR swapping to and from non-lvalue + //~^ ERROR cannot assign + //~^^ ERROR cannot assign } diff --git a/src/test/compile-fail/writing-to-immutable-vec.rs b/src/test/compile-fail/writing-to-immutable-vec.rs index 3f4c8ccef8175..faa3d6cfe47e7 100644 --- a/src/test/compile-fail/writing-to-immutable-vec.rs +++ b/src/test/compile-fail/writing-to-immutable-vec.rs @@ -8,5 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:assigning to immutable vec content -fn main() { let v: ~[int] = ~[1, 2, 3]; v[1] = 4; } +fn main() { + let v: ~[int] = ~[1, 2, 3]; + v[1] = 4; //~ ERROR cannot assign +} diff --git a/src/test/compile-fail/issue-4500.rs b/src/test/run-pass/borrowck-nested-calls.rs similarity index 51% rename from src/test/compile-fail/issue-4500.rs rename to src/test/run-pass/borrowck-nested-calls.rs index 356a64498219a..4494f5f2fa337 100644 --- a/src/test/compile-fail/issue-4500.rs +++ b/src/test/run-pass/borrowck-nested-calls.rs @@ -8,7 +8,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn main () { - let mut _p: & int = & 4; - _p = &*~3; //~ ERROR illegal borrow +// xfail-test #5074 nested method calls + +// Test that (safe) nested calls with `&mut` receivers are permitted. + +struct Foo {a: uint, b: uint} + +pub impl Foo { + fn inc_a(&mut self, v: uint) { self.a += v; } + + fn next_b(&mut self) -> uint { + let b = self.b; + self.b += 1; + b + } +} + +fn main() { + let mut f = Foo {a: 22, b: 23}; + f.inc_a(f.next_b()); + assert_eq!(f.a, 22+23); + assert_eq!(f.b, 24); } diff --git a/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs b/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs index 0e67532d7a1fc..b0d06dae10dc0 100644 --- a/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs +++ b/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs @@ -1,10 +1,10 @@ trait Reverser { - fn reverse(&self); + fn reverse(self); } impl<'self> Reverser for &'self mut [uint] { - fn reverse(&self) { - vec::reverse(*self); + fn reverse(self) { + vec::reverse(self); } } diff --git a/src/test/run-pass/issue-2735-2.rs b/src/test/run-pass/issue-2735-2.rs index 96f76b0fd6ba6..ca584e1a6e3b8 100644 --- a/src/test/run-pass/issue-2735-2.rs +++ b/src/test/run-pass/issue-2735-2.rs @@ -9,27 +9,25 @@ // except according to those terms. // This test should behave exactly like issue-2735-3 -struct defer<'self> { - b: &'self mut bool, +struct defer { + b: @mut bool, } #[unsafe_destructor] -impl<'self> Drop for defer<'self> { +impl Drop for defer { fn finalize(&self) { - unsafe { - *(self.b) = true; - } + *self.b = true; } } -fn defer<'r>(b: &'r mut bool) -> defer<'r> { +fn defer(b: @mut bool) -> defer { defer { b: b } } pub fn main() { - let mut dtor_ran = false; - let _ = defer(&mut dtor_ran); - assert!((dtor_ran)); + let dtor_ran = @mut false; + let _ = defer(dtor_ran); + assert!(*dtor_ran); } diff --git a/src/test/run-pass/issue-2735-3.rs b/src/test/run-pass/issue-2735-3.rs index 50e3c946f50ef..44ca5d6929bd6 100644 --- a/src/test/run-pass/issue-2735-3.rs +++ b/src/test/run-pass/issue-2735-3.rs @@ -9,27 +9,25 @@ // except according to those terms. // This test should behave exactly like issue-2735-2 -struct defer<'self> { - b: &'self mut bool, +struct defer { + b: @mut bool, } #[unsafe_destructor] -impl<'self> Drop for defer<'self> { +impl Drop for defer { fn finalize(&self) { - unsafe { - *(self.b) = true; - } + *self.b = true; } } -fn defer<'r>(b: &'r mut bool) -> defer<'r> { +fn defer(b: @mut bool) -> defer { defer { b: b } } pub fn main() { - let mut dtor_ran = false; - defer(&mut dtor_ran); - assert!((dtor_ran)); + let dtor_ran = @mut false; + defer(dtor_ran); + assert!(*dtor_ran); } From aa48a170d55e3003b70626b16ccd698b5aca8269 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Apr 2013 14:07:26 -0400 Subject: [PATCH 04/46] dataflow: fix flow of information through pattern variants --- src/librustc/middle/dataflow.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index cfdd7f95030aa..9d032a1839e23 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -846,10 +846,9 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> { // alternatives, so we must treat this like an N-way select // statement. let initial_state = reslice(in_out).to_vec(); - self.reset(in_out); for pats.each |&pat| { let mut temp = copy initial_state; - self.walk_pat(pat, in_out, loop_scopes); + self.walk_pat(pat, temp, loop_scopes); join_bits(&self.dfcx.oper, temp, in_out); } } From 7a0c1ea560126a6ccefbe7981fd1e18d80d3ed20 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Apr 2013 14:07:52 -0400 Subject: [PATCH 05/46] correct used_mut annotations for args, inherited case --- src/librustc/middle/borrowck/check_loans.rs | 33 +++++++++------- .../middle/borrowck/gather_loans/lifetime.rs | 4 +- .../middle/borrowck/gather_loans/mod.rs | 39 ------------------- .../borrowck/gather_loans/restrictions.rs | 4 +- src/librustc/middle/borrowck/mod.rs | 5 +-- 5 files changed, 24 insertions(+), 61 deletions(-) diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 56eb57009ca08..9330395c061a7 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -260,11 +260,21 @@ pub impl<'self> CheckLoanCtxt<'self> { // and report an error otherwise. match cmt.mutbl { mc::McDeclared => { - // OK + // OK, but we have to mark arguments as requiring mut + // if they are assigned (other cases are handled by liveness, + // since we need to distinguish local variables assigned + // once vs those assigned multiple times) + match cmt.cat { + mc::cat_self(*) | + mc::cat_arg(*) => { + mark_variable_as_used_mut(self, cmt); + } + _ => {} + } } mc::McInherited => { // OK, but we may have to add an entry to `used_mut_nodes` - mark_writes_through_upvars_as_used_mut(self, cmt); + mark_variable_as_used_mut(self, cmt); } mc::McReadOnly | mc::McImmutable => { // Subtle: liveness guarantees that immutable local @@ -289,33 +299,28 @@ pub impl<'self> CheckLoanCtxt<'self> { self, expr, cmt); } - fn mark_writes_through_upvars_as_used_mut(self: &CheckLoanCtxt, - cmt: mc::cmt) { + fn mark_variable_as_used_mut(self: &CheckLoanCtxt, + cmt: mc::cmt) { //! If the mutability of the `cmt` being written is inherited - //! from a local variable in another closure, liveness may + //! from a local variable, liveness will //! not have been able to detect that this variable's mutability //! is important, so we must add the variable to the - //! `used_mut_nodes` table here. This is because liveness - //! does not consider closures. + //! `used_mut_nodes` table here. - let mut passed_upvar = false; let mut cmt = cmt; loop { debug!("mark_writes_through_upvars_as_used_mut(cmt=%s)", cmt.repr(self.tcx())); match cmt.cat { mc::cat_local(id) | - mc::cat_arg(id, _) | + mc::cat_arg(id) | mc::cat_self(id) => { - if passed_upvar { - self.tcx().used_mut_nodes.insert(id); - } + self.tcx().used_mut_nodes.insert(id); return; } mc::cat_stack_upvar(b) => { cmt = b; - passed_upvar = true; } mc::cat_rvalue | @@ -552,7 +557,7 @@ pub impl<'self> CheckLoanCtxt<'self> { match cmt.cat { // Rvalues, locals, and arguments can be moved: mc::cat_rvalue | mc::cat_local(_) | - mc::cat_arg(_, ast::by_copy) | mc::cat_self(_) => {} + mc::cat_arg(_) | mc::cat_self(_) => {} // It seems strange to allow a move out of a static item, // but what happens in practice is that you have a diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs index 21d7e7041d959..4d267b7dc471c 100644 --- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs +++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs @@ -255,7 +255,7 @@ impl GuaranteeLifetimeContext { match cmt.guarantor().cat { mc::cat_local(id) | mc::cat_self(id) | - mc::cat_arg(id, _) => { + mc::cat_arg(id) => { self.bccx.moved_variables_set.contains(&id) } mc::cat_rvalue | @@ -292,7 +292,7 @@ impl GuaranteeLifetimeContext { ty::re_static } mc::cat_local(local_id) | - mc::cat_arg(local_id, _) | + mc::cat_arg(local_id) | mc::cat_self(local_id) => { self.bccx.tcx.region_maps.encl_region(local_id) } diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index 82638ceb4d4f1..1bc3b70ac3842 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -27,7 +27,6 @@ use util::common::indenter; use util::ppaux::{Repr}; use core::hashmap::HashSet; -use core::vec; use syntax::ast::{m_const, m_imm, m_mutbl}; use syntax::ast; use syntax::ast_util::id_range; @@ -169,20 +168,6 @@ fn gather_loans_in_expr(ex: @ast::expr, visit::visit_expr(ex, self, vt); } - ast::expr_call(f, ref args, _) => { - let arg_tys = ty::ty_fn_args(ty::expr_ty(self.tcx(), f)); - self.guarantee_arguments(ex, *args, arg_tys); - visit::visit_expr(ex, self, vt); - } - - ast::expr_method_call(_, _, _, ref args, _) => { - let arg_tys = ty::ty_fn_args(ty::node_id_to_type(self.tcx(), - ex.callee_id)); - self.guarantee_arguments(ex, *args, arg_tys); - - visit::visit_expr(ex, self, vt); - } - ast::expr_match(ex_v, ref arms) => { let cmt = self.bccx.cat_expr(ex_v); for arms.each |arm| { @@ -271,30 +256,6 @@ pub impl GatherLoanCtxt { assert!(id == popped); } - fn guarantee_arguments(&mut self, - call_expr: @ast::expr, - args: &[@ast::expr], - arg_tys: &[ty::arg]) { - for vec::each2(args, arg_tys) |arg, arg_ty| { - match ty::resolved_mode(self.tcx(), arg_ty.mode) { - ast::by_ref => { - self.guarantee_by_ref_argument(call_expr, *arg); - } - ast::by_copy => {} - } - } - } - - fn guarantee_by_ref_argument(&mut self, - call_expr: @ast::expr, - arg_expr: @ast::expr) { - // FIXME(#5074) nested method calls - let scope_r = ty::re_scope(call_expr.id); - let arg_cmt = self.bccx.cat_expr(arg_expr); - self.guarantee_valid(arg_expr.id, arg_expr.span, - arg_cmt, m_imm, scope_r); - } - fn guarantee_adjustments(&mut self, expr: @ast::expr, adjustment: &ty::AutoAdjustment) { diff --git a/src/librustc/middle/borrowck/gather_loans/restrictions.rs b/src/librustc/middle/borrowck/gather_loans/restrictions.rs index 950dbc58ec364..0be4c67a9bc91 100644 --- a/src/librustc/middle/borrowck/gather_loans/restrictions.rs +++ b/src/librustc/middle/borrowck/gather_loans/restrictions.rs @@ -15,7 +15,6 @@ use middle::borrowck::*; use mc = middle::mem_categorization; use middle::ty; use syntax::ast::{m_const, m_imm, m_mutbl}; -use syntax::ast; use syntax::codemap::span; pub enum RestrictionResult { @@ -74,7 +73,7 @@ impl RestrictionsContext { } mc::cat_local(local_id) | - mc::cat_arg(local_id, ast::by_copy) | + mc::cat_arg(local_id) | mc::cat_self(local_id) => { let lp = @LpVar(local_id); SafeIf(lp, ~[Restriction {loan_path: lp, @@ -114,7 +113,6 @@ impl RestrictionsContext { mc::cat_copied_upvar(*) | // FIXME(#2152) allow mutation of upvars mc::cat_static_item(*) | mc::cat_implicit_self(*) | - mc::cat_arg(_, ast::by_ref) | mc::cat_deref(_, _, mc::region_ptr(m_imm, _)) | mc::cat_deref(_, _, mc::gc_ptr(m_imm)) => { Safe diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index c108b020378eb..3f10223723727 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -242,13 +242,12 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> { mc::cat_rvalue | mc::cat_static_item | mc::cat_copied_upvar(_) | - mc::cat_implicit_self | - mc::cat_arg(_, ast::by_ref) => { + mc::cat_implicit_self => { None } mc::cat_local(id) | - mc::cat_arg(id, ast::by_copy) | + mc::cat_arg(id) | mc::cat_self(id) => { Some(@LpVar(id)) } From 545d51c1608198aed4e3bdca0dac23bcc6af19b9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Apr 2013 14:08:18 -0400 Subject: [PATCH 06/46] rustc: remove modes --- src/librustc/middle/mem_categorization.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index f525230664a21..5921e4b0e4ca0 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -64,7 +64,7 @@ pub enum categorization { cat_copied_upvar(CopiedUpvar), // upvar copied into @fn or ~fn env cat_stack_upvar(cmt), // by ref upvar from &fn cat_local(ast::node_id), // local variable - cat_arg(ast::node_id, ast::rmode), // formal argument + cat_arg(ast::node_id), // formal argument cat_deref(cmt, uint, ptr_kind), // deref of a ptr cat_interior(cmt, interior_kind), // something interior cat_discr(cmt, ast::node_id), // match discriminant (see preserve()) @@ -465,11 +465,10 @@ pub impl mem_categorization_ctxt { // m: mutability of the argument let m = if mutbl {McDeclared} else {McImmutable}; - let mode = ty::resolved_mode(self.tcx, mode); @cmt_ { id: id, span: span, - cat: cat_arg(vid, mode), + cat: cat_arg(vid), mutbl: m, ty:expr_ty } @@ -1059,7 +1058,7 @@ pub impl cmt_ { cat_copied_upvar(CopiedUpvar {onceness: ast::Once, _}) | cat_rvalue(*) | cat_local(*) | - cat_arg(_, ast::by_copy) | + cat_arg(_) | cat_self(*) | cat_deref(_, _, unsafe_ptr(*)) | // of course it is aliasable, but... cat_deref(_, _, region_ptr(m_mutbl, _)) => { @@ -1068,8 +1067,7 @@ pub impl cmt_ { cat_copied_upvar(CopiedUpvar {onceness: ast::Many, _}) | cat_static_item(*) | - cat_implicit_self(*) | - cat_arg(_, ast::by_ref) => { + cat_implicit_self(*) => { Some(AliasableOther) } From 70b9ad1748748d93ccef95b59435a7357b350d11 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Apr 2013 14:09:14 -0400 Subject: [PATCH 07/46] rustc: work around issue with default-method-simple, fix some rebase errors --- src/librustc/middle/ty.rs | 7 +++++++ src/librustc/middle/typeck/check/method.rs | 16 ++++++++++++++++ src/librustc/middle/typeck/check/mod.rs | 3 ++- src/librustc/middle/typeck/check/regionck.rs | 10 ++++------ 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 28705ac49320a..b17dac82048bf 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1553,6 +1553,13 @@ pub fn type_is_ty_var(ty: t) -> bool { pub fn type_is_bool(ty: t) -> bool { get(ty).sty == ty_bool } +pub fn type_is_self(ty: t) -> bool { + match get(ty).sty { + ty_self(*) => true, + _ => false + } +} + pub fn type_is_structural(ty: t) -> bool { match get(ty).sty { ty_struct(*) | ty_tup(_) | ty_enum(*) | ty_closure(_) | ty_trait(*) | diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 0cc2ddd32b46a..de6530fb464c5 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -658,6 +658,12 @@ pub impl<'self> LookupContext<'self> { let tcx = self.tcx(); return match ty::get(self_ty).sty { + ty::ty_rptr(_, self_mt) if default_method_hack(self_mt) => { + (self_ty, + ty::AutoDerefRef(ty::AutoDerefRef { + autoderefs: autoderefs, + autoref: None})) + } ty::ty_rptr(_, self_mt) => { let region = self.infcx().next_region_var_nb(self.expr.span); (ty::mk_rptr(tcx, region, self_mt), @@ -679,6 +685,16 @@ pub impl<'self> LookupContext<'self> { autoref: None})) } }; + + fn default_method_hack(self_mt: ty::mt) -> bool { + // FIXME(#6129). Default methods can't deal with autoref. + // + // I am a horrible monster and I pray for death. Currently + // the default method code fails when you try to reborrow + // because it is not handling types correctly. In lieu of + // fixing that, I am introducing this horrible hack. - ndm + self_mt.mutbl == m_imm && ty::type_is_self(self_mt.ty) + } } fn search_for_autosliced_method( diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 84fc40f6954a8..fb58df3d55c68 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -2966,7 +2966,8 @@ pub fn check_block(fcx0: @mut FnCtxt, blk: &ast::blk) { pub fn check_block_with_expected(fcx: @mut FnCtxt, blk: &ast::blk, expected: Option) { - let prev = replace(&mut fcx.ps, fcx.ps.recurse(blk)); + let purity_state = fcx.ps.recurse(blk); + let prev = replace(&mut fcx.ps, purity_state); do fcx.with_region_lb(blk.node.id) { let mut warned = false; diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 1c35c911b14cd..be513cbb0f307 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -457,20 +457,18 @@ fn constrain_call(rcx: @mut Rcx, let callee_scope = call_expr.id; let callee_region = ty::re_scope(callee_scope); - for fn_sig.inputs.eachi |i, input| { + for arg_exprs.each |&arg_expr| { // ensure that any regions appearing in the argument type are // valid for at least the lifetime of the function: constrain_regions_in_type_of_node( - rcx, arg_exprs[i].id, callee_region, arg_exprs[i].span); + rcx, arg_expr.id, callee_region, arg_expr.span); // unfortunately, there are two means of taking implicit // references, and we need to propagate constraints as a // result. modes are going away and the "DerefArgs" code // should be ported to use adjustments - ty::set_default_mode(tcx, input.mode, ast::by_copy); - let is_by_ref = ty::resolved_mode(tcx, input.mode) == ast::by_ref; - if implicitly_ref_args || is_by_ref { - guarantor::for_by_ref(rcx, arg_exprs[i], callee_scope); + if implicitly_ref_args { + guarantor::for_by_ref(rcx, arg_expr, callee_scope); } } From 418f99111852d13e9446c70cd616e6e6780bb632 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Apr 2013 14:10:21 -0400 Subject: [PATCH 08/46] allover: numerous unused muts etc --- src/compiletest/header.rs | 3 +-- src/compiletest/runtest.rs | 2 +- src/libcore/cell.rs | 4 ++-- src/libcore/comm.rs | 4 ++-- src/libcore/flate.rs | 13 ++++++------- src/libcore/libc.rs | 3 +-- src/libcore/os.rs | 17 ++++++++--------- src/libcore/rt/sched/mod.rs | 2 -- src/libcore/unstable/extfmt.rs | 6 +++--- src/libcore/vec.rs | 9 +++------ src/librustc/middle/lang_items.rs | 11 +++++------ src/librustc/middle/moves.rs | 2 +- src/librustc/util/ppaux.rs | 3 ++- src/libstd/ebml.rs | 2 +- src/libstd/future.rs | 2 +- src/libstd/sort.rs | 1 - src/libstd/workcache.rs | 2 +- src/libsyntax/parse/mod.rs | 2 +- src/libsyntax/parse/parser.rs | 3 +-- src/test/compile-fail/die-not-static.rs | 3 +-- 20 files changed, 41 insertions(+), 53 deletions(-) diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index b0d04c6739b4a..28bbbda966340 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -82,14 +82,13 @@ pub fn load_props(testfile: &Path) -> TestProps { } pub fn is_test_ignored(config: config, testfile: &Path) -> bool { - let mut found = false; for iter_header(testfile) |ln| { if parse_name_directive(ln, ~"xfail-test") { return true; } if parse_name_directive(ln, xfail_target()) { return true; } if config.mode == common::mode_pretty && parse_name_directive(ln, ~"xfail-pretty") { return true; } }; - return found; + return false; fn xfail_target() -> ~str { ~"xfail-" + str::from_slice(os::SYSNAME) diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index fef4cabf7fd6d..5805c1730296c 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -106,7 +106,7 @@ fn run_rpass_test(config: config, props: TestProps, testfile: &Path) { fatal_ProcRes(~"test run failed!", ProcRes); } } else { - let mut ProcRes = jit_test(config, props, testfile); + let ProcRes = jit_test(config, props, testfile); if ProcRes.status != 0 { fatal_ProcRes(~"jit failed!", ProcRes); } } diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 27e03d2bf3103..959defeec0413 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -42,7 +42,7 @@ pub fn empty_cell() -> Cell { pub impl Cell { /// Yields the value, failing if the cell is empty. fn take(&self) -> T { - let mut self = unsafe { transmute_mut(self) }; + let self = unsafe { transmute_mut(self) }; if self.is_empty() { fail!(~"attempt to take an empty cell"); } @@ -54,7 +54,7 @@ pub impl Cell { /// Returns the value, failing if the cell is full. fn put_back(&self, value: T) { - let mut self = unsafe { transmute_mut(self) }; + let self = unsafe { transmute_mut(self) }; if !self.is_empty() { fail!(~"attempt to put a value back into a full cell"); } diff --git a/src/libcore/comm.rs b/src/libcore/comm.rs index 50a3bba049bbb..d075ff08bb7eb 100644 --- a/src/libcore/comm.rs +++ b/src/libcore/comm.rs @@ -205,8 +205,8 @@ impl Selectable for Port { fn header(&self) -> *PacketHeader { unsafe { match self.endp { - Some(ref endp) => endp.header(), - None => fail!(~"peeking empty stream") + Some(ref endp) => endp.header(), + None => fail!(~"peeking empty stream") } } } diff --git a/src/libcore/flate.rs b/src/libcore/flate.rs index c3518cc8b6ee2..ba10f97e626c4 100644 --- a/src/libcore/flate.rs +++ b/src/libcore/flate.rs @@ -16,7 +16,6 @@ Simple compression use libc; use libc::{c_void, size_t, c_int}; -use ptr; use vec; #[cfg(test)] use rand; @@ -29,13 +28,13 @@ pub mod rustrt { pub extern { unsafe fn tdefl_compress_mem_to_heap(psrc_buf: *const c_void, src_buf_len: size_t, - pout_len: *size_t, + pout_len: *mut size_t, flags: c_int) -> *c_void; unsafe fn tinfl_decompress_mem_to_heap(psrc_buf: *const c_void, src_buf_len: size_t, - pout_len: *size_t, + pout_len: *mut size_t, flags: c_int) -> *c_void; } @@ -53,11 +52,11 @@ pub fn deflate_bytes(bytes: &const [u8]) -> ~[u8] { let res = rustrt::tdefl_compress_mem_to_heap(b as *c_void, len as size_t, - &outsz, + &mut outsz, lz_norm); assert!(res as int != 0); let out = vec::raw::from_buf_raw(res as *u8, - outsz as uint); + outsz as uint); libc::free(res); out } @@ -67,11 +66,11 @@ pub fn deflate_bytes(bytes: &const [u8]) -> ~[u8] { pub fn inflate_bytes(bytes: &const [u8]) -> ~[u8] { do vec::as_const_buf(bytes) |b, len| { unsafe { - let outsz : size_t = 0; + let mut outsz : size_t = 0; let res = rustrt::tinfl_decompress_mem_to_heap(b as *c_void, len as size_t, - &outsz, + &mut outsz, 0); assert!(res as int != 0); let out = vec::raw::from_buf_raw(res as *u8, diff --git a/src/libcore/libc.rs b/src/libcore/libc.rs index 44864630f9873..d7a9ab4d63b5f 100644 --- a/src/libcore/libc.rs +++ b/src/libcore/libc.rs @@ -253,8 +253,7 @@ pub mod types { pub type ssize_t = i32; } pub mod posix01 { - use libc::types::os::arch::c95::{c_int, c_short, c_long, - time_t}; + use libc::types::os::arch::c95::{c_short, c_long, time_t}; use libc::types::os::arch::posix88::{dev_t, gid_t, ino_t}; use libc::types::os::arch::posix88::{mode_t, off_t}; use libc::types::os::arch::posix88::{uid_t}; diff --git a/src/libcore/os.rs b/src/libcore/os.rs index f1962eeaa23d0..d5271ec228ba5 100644 --- a/src/libcore/os.rs +++ b/src/libcore/os.rs @@ -351,13 +351,13 @@ pub fn fsync_fd(fd: c_int, _l: io::fsync::Level) -> c_int { } } -pub struct Pipe { mut in: c_int, mut out: c_int } +pub struct Pipe { in: c_int, out: c_int } #[cfg(unix)] pub fn pipe() -> Pipe { unsafe { let mut fds = Pipe {in: 0 as c_int, - out: 0 as c_int }; + out: 0 as c_int }; assert!((libc::pipe(&mut fds.in) == (0 as c_int))); return Pipe {in: fds.in, out: fds.out}; } @@ -373,8 +373,7 @@ pub fn pipe() -> Pipe { // fully understand. Here we explicitly make the pipe non-inheritable, // which means to pass it to a subprocess they need to be duplicated // first, as in rust_run_program. - let mut fds = Pipe {in: 0 as c_int, - out: 0 as c_int }; + let mut fds = Pipe {in: 0 as c_int, out: 0 as c_int}; let res = libc::pipe(&mut fds.in, 1024 as ::libc::c_uint, (libc::O_BINARY | libc::O_NOINHERIT) as c_int); assert!((res == 0 as c_int)); @@ -959,10 +958,10 @@ pub fn last_os_error() -> ~str { #[cfg(target_os = "macos")] #[cfg(target_os = "android")] #[cfg(target_os = "freebsd")] - fn strerror_r(errnum: c_int, buf: *c_char, buflen: size_t) -> c_int { + fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int { #[nolink] extern { - unsafe fn strerror_r(errnum: c_int, buf: *c_char, + unsafe fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int; } unsafe { @@ -974,10 +973,10 @@ pub fn last_os_error() -> ~str { // and requires macros to instead use the POSIX compliant variant. // So we just use __xpg_strerror_r which is always POSIX compliant #[cfg(target_os = "linux")] - fn strerror_r(errnum: c_int, buf: *c_char, buflen: size_t) -> c_int { + fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int { #[nolink] extern { - unsafe fn __xpg_strerror_r(errnum: c_int, buf: *c_char, + unsafe fn __xpg_strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int; } unsafe { @@ -987,7 +986,7 @@ pub fn last_os_error() -> ~str { let mut buf = [0 as c_char, ..TMPBUF_SZ]; unsafe { - let err = strerror_r(errno() as c_int, &buf[0], + let err = strerror_r(errno() as c_int, &mut buf[0], TMPBUF_SZ as size_t); if err < 0 { fail!(~"strerror_r failure"); diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs index a2132676c1a03..333146394ee1f 100644 --- a/src/libcore/rt/sched/mod.rs +++ b/src/libcore/rt/sched/mod.rs @@ -136,7 +136,6 @@ pub impl Scheduler { /// Called by a running task to end execution, after which it will /// be recycled by the scheduler for reuse in a new task. fn terminate_current_task(~self) { - let mut self = self; assert!(self.in_task_context()); rtdebug!("ending running task"); @@ -152,7 +151,6 @@ pub impl Scheduler { } fn schedule_new_task(~self, task: ~Task) { - let mut self = self; assert!(self.in_task_context()); do self.switch_running_tasks_and_then(task) |last_task| { diff --git a/src/libcore/unstable/extfmt.rs b/src/libcore/unstable/extfmt.rs index b812be5575a4a..e5d32c4bb3259 100644 --- a/src/libcore/unstable/extfmt.rs +++ b/src/libcore/unstable/extfmt.rs @@ -501,7 +501,7 @@ pub mod rt { pub fn conv_int(cv: Conv, i: int, buf: &mut ~str) { let radix = 10; let prec = get_int_precision(cv); - let mut s : ~str = uint_to_str_prec(int::abs(i) as uint, radix, prec); + let s : ~str = uint_to_str_prec(int::abs(i) as uint, radix, prec); let head = if i >= 0 { if have_flag(cv.flags, flag_sign_always) { @@ -516,7 +516,7 @@ pub mod rt { } pub fn conv_uint(cv: Conv, u: uint, buf: &mut ~str) { let prec = get_int_precision(cv); - let mut rs = + let rs = match cv.ty { TyDefault => uint_to_str_prec(u, 10, prec), TyHexLower => uint_to_str_prec(u, 16, prec), @@ -559,7 +559,7 @@ pub mod rt { CountIs(c) => (float::to_str_exact, c as uint), CountImplied => (float::to_str_digits, 6u) }; - let mut s = to_str(f, digits); + let s = to_str(f, digits); let head = if 0.0 <= f { if have_flag(cv.flags, flag_sign_always) { Some('+') diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 2f9488d1bc7a7..4c817da081982 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -1826,12 +1826,9 @@ impl<'self,T:Copy> CopyableVector for &'self [T] { #[inline] fn to_owned(&self) -> ~[T] { let mut result = ~[]; - // FIXME: #4568 - unsafe { - reserve(&mut result, self.len()); - for self.each |e| { - result.push(copy *e); - } + reserve(&mut result, self.len()); + for self.each |e| { + result.push(copy *e); } result diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 784db49a0fd62..e7261d53952a2 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -28,7 +28,6 @@ use syntax::ast_util::local_def; use syntax::visit::{default_simple_visitor, mk_simple_visitor, SimpleVisitor}; use syntax::visit::visit_crate; -use core::cast::transmute; use core::hashmap::HashMap; pub enum LangItem { @@ -370,7 +369,7 @@ pub impl LanguageItemCollector { } fn collect_local_language_items(&mut self) { - let this = ptr::addr_of(&self); + let this: *mut LanguageItemCollector = &mut *self; visit_crate(self.crate, (), mk_simple_visitor(@SimpleVisitor { visit_item: |item| { for item.attrs.each |attribute| { @@ -380,10 +379,10 @@ pub impl LanguageItemCollector { attribute.node.value ); } - }, - .. *default_simple_visitor() - })); - } + } + }, + .. *default_simple_visitor() + })); } fn collect_external_language_items(&mut self) { diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs index d8a0e6bacf489..0daad80af5db7 100644 --- a/src/librustc/middle/moves.rs +++ b/src/librustc/middle/moves.rs @@ -299,7 +299,7 @@ pub fn compute_moves(tcx: ty::ctxt, pub fn moved_variable_node_id_from_def(def: def) -> Option { match def { def_binding(nid, _) | - def_arg(nid, _, _) | + def_arg(nid, _) | def_local(nid, _) | def_self(nid, _) => Some(nid), diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index d99d87231bece..751fe19f2a236 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -13,7 +13,8 @@ use middle::ty::{ReSkolemized, ReVar}; use middle::ty::{bound_region, br_anon, br_named, br_self, br_cap_avoid}; use middle::ty::{br_fresh, ctxt, field, method}; use middle::ty::{mt, t, param_bound, param_ty}; -use middle::ty::{re_bound, re_free, re_scope, re_infer, re_static, Region}; +use middle::ty::{re_bound, re_free, re_scope, re_infer, re_static, Region, + re_empty}; use middle::ty::{ty_bool, ty_bot, ty_box, ty_struct, ty_enum}; use middle::ty::{ty_err, ty_estr, ty_evec, ty_float, ty_bare_fn, ty_closure}; use middle::ty::{ty_nil, ty_opaque_box, ty_opaque_closure_ptr, ty_param}; diff --git a/src/libstd/ebml.rs b/src/libstd/ebml.rs index 9b89036eee51b..65ce9d8989f05 100644 --- a/src/libstd/ebml.rs +++ b/src/libstd/ebml.rs @@ -617,7 +617,7 @@ pub mod writer { priv impl Encoder { // used internally to emit things like the vector length and so on fn _emit_tagged_uint(&self, t: EbmlEncoderTag, v: uint) { - assert!(v <= 0xFFFF_FFFF_u); + assert!(v <= 0xFFFF_FFFF_u); // FIXME(#6130) assert warns on 32-bit self.wr_tagged_u32(t as uint, v as u32); } diff --git a/src/libstd/future.rs b/src/libstd/future.rs index c3fc16bdf70ba..5e3e64b2f1cfa 100644 --- a/src/libstd/future.rs +++ b/src/libstd/future.rs @@ -23,7 +23,7 @@ use core::cast; use core::cell::Cell; -use core::comm::{ChanOne, PortOne, oneshot, send_one}; +use core::comm::{PortOne, oneshot, send_one}; use core::pipes::recv; use core::task; diff --git a/src/libstd/sort.rs b/src/libstd/sort.rs index c153d7f22c034..f3d30ecd5cdf1 100644 --- a/src/libstd/sort.rs +++ b/src/libstd/sort.rs @@ -11,7 +11,6 @@ //! Sorting methods use core::cmp::{Eq, Ord}; -use core::util; use core::vec::len; use core::vec; diff --git a/src/libstd/workcache.rs b/src/libstd/workcache.rs index bb4a9e97ea1f4..c01d1f5a2d7b0 100644 --- a/src/libstd/workcache.rs +++ b/src/libstd/workcache.rs @@ -17,7 +17,7 @@ use sort; use core::cell::Cell; use core::cmp; -use core::comm::{ChanOne, PortOne, oneshot, send_one}; +use core::comm::{PortOne, oneshot, send_one}; use core::either::{Either, Left, Right}; use core::hashmap::HashMap; use core::io; diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 7e7931bbb606b..5d51a54d770b1 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -24,7 +24,7 @@ use parse::token::{ident_interner, mk_ident_interner}; use core::io; use core::option::{None, Option, Some}; use core::path::Path; -use core::result::{Err, Ok, Result}; +use core::result::{Err, Ok}; pub mod lexer; pub mod parser; diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 50bdfb2f55726..1129e7b708ea2 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -938,7 +938,7 @@ pub impl Parser { match *self.token { token::MOD_SEP => { match self.look_ahead(1u) { - token::IDENT(id,_) => { + token::IDENT(*) => { self.bump(); ids.push(self.parse_ident()); } @@ -3728,7 +3728,6 @@ pub impl Parser { items: _, foreign_items: foreign_items } = self.parse_foreign_items(first_item_attrs, true); - let mut initial_attrs = attrs_remaining; assert!(*self.token == token::RBRACE); ast::foreign_mod { sort: sort, diff --git a/src/test/compile-fail/die-not-static.rs b/src/test/compile-fail/die-not-static.rs index b30e3942e6330..d33c591d8c87f 100644 --- a/src/test/compile-fail/die-not-static.rs +++ b/src/test/compile-fail/die-not-static.rs @@ -1,7 +1,6 @@ -// error-pattern:illegal borrow: borrowed value does not live long enough - fn main() { let v = ~"test"; let sslice = str::slice(v, 0, v.len()); + //~^ ERROR borrowed value does not live long enough fail!(sslice); } From dc21daeeb88c20b6431e70fe4ef5cc416af8410d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Apr 2013 16:34:22 -0400 Subject: [PATCH 09/46] borrowck: fix critical bug prevent us from ever using write guards :) --- src/librustc/middle/borrowck/check_loans.rs | 3 +++ .../middle/borrowck/gather_loans/lifetime.rs | 1 + .../middle/borrowck/gather_loans/mod.rs | 4 ++-- src/test/run-pass/too-much-recursion.rs | 22 ------------------- 4 files changed, 6 insertions(+), 24 deletions(-) delete mode 100644 src/test/run-pass/too-much-recursion.rs diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 9330395c061a7..70da9c9380559 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -357,6 +357,8 @@ pub impl<'self> CheckLoanCtxt<'self> { //! Safety checks related to writes to aliasable, mutable locations let guarantor = cmt.guarantor(); + debug!("check_for_aliasable_mutable_writes(cmt=%s, guarantor=%s)", + cmt.repr(self.tcx()), guarantor.repr(self.tcx())); match guarantor.cat { mc::cat_deref(b, _, mc::region_ptr(m_mutbl, _)) => { // Statically prohibit writes to `&mut` when aliasable @@ -379,6 +381,7 @@ pub impl<'self> CheckLoanCtxt<'self> { id: base.id, derefs: deref_count }; + debug!("Inserting write guard at %?", key); self.bccx.write_guard_map.insert(key); } diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs index 4d267b7dc471c..fdfb26c0d0835 100644 --- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs +++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs @@ -90,6 +90,7 @@ impl GuaranteeLifetimeContext { // See rule Freeze-Imm-Managed-Ptr-2 in doc.rs let omit_root = ( + ptr_mutbl == m_imm && self.bccx.is_subregion_of(self.loan_region, base_scope) && base.mutbl.is_immutable() && !self.is_moved(base) diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index 1bc3b70ac3842..8a986a22d4c48 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -148,7 +148,7 @@ fn gather_loans_in_expr(ex: @ast::expr, // If this expression is borrowed, have to ensure it remains valid: { - let mut this = &mut *self; // FIXME(#5074) + let this = &mut *self; // FIXME(#5074) if !this.ignore_adjustments.contains(&ex.id) { for tcx.adjustments.find(&ex.id).each |&adjustments| { this.guarantee_adjustments(ex, *adjustments); @@ -283,7 +283,7 @@ pub impl GatherLoanCtxt { let mcx = &mc::mem_categorization_ctxt { tcx: self.tcx(), method_map: self.bccx.method_map}; - let mut cmt = mcx.cat_expr_autoderefd(expr, autoderefs); + let cmt = mcx.cat_expr_autoderefd(expr, autoderefs); debug!("after autoderef, cmt=%s", cmt.repr(self.tcx())); match *autoref { diff --git a/src/test/run-pass/too-much-recursion.rs b/src/test/run-pass/too-much-recursion.rs deleted file mode 100644 index adccc786926dc..0000000000000 --- a/src/test/run-pass/too-much-recursion.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2012 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. - -// xfail-win32 -// error-pattern:ran out of stack - -// Test that the task fails after hitting the recursion limit, but -// that it doesn't bring down the whole proc - -pub fn main() { - do task::spawn_unlinked { - fn f() { f() }; - f(); - }; -} From f236b850c0dc4c1b925e33d1173f359605801307 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Apr 2013 16:35:01 -0400 Subject: [PATCH 10/46] remove some unused mut decls --- src/libcore/num/num.rs | 2 +- src/librustc/middle/resolve.rs | 4 ++-- src/librustc/middle/trans/base.rs | 2 +- src/librustc/middle/trans/callee.rs | 2 +- src/librustc/middle/trans/expr.rs | 2 -- src/librustc/middle/typeck/check/method.rs | 4 ++-- src/librustc/middle/typeck/check/mod.rs | 4 ++-- src/librustc/middle/typeck/check/regionck.rs | 2 +- src/test/run-fail/borrowck-wg-fail-2.rs | 3 ++- src/test/run-fail/borrowck-wg-fail-3.rs | 3 ++- src/test/run-fail/borrowck-wg-fail.rs | 6 ++---- 11 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index de7597fa821d6..96fed51309ee3 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -385,7 +385,7 @@ pub fn pow_with_uint+Mul>( } #[cfg(test)] -fn test_num(ten: T, two: T) { +pub fn test_num(ten: T, two: T) { assert_eq!(ten.add(&two), cast(12)); assert_eq!(ten.sub(&two), cast(8)); assert_eq!(ten.mul(&two), cast(20)); diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index ffc9d1488cf13..ff46abaf7128c 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -5267,7 +5267,7 @@ pub impl Resolver { debug!("Import resolutions:"); for module_.import_resolutions.each |name, import_resolution| { - let mut value_repr; + let value_repr; match import_resolution.target_for_namespace(ValueNS) { None => { value_repr = ~""; } Some(_) => { @@ -5276,7 +5276,7 @@ pub impl Resolver { } } - let mut type_repr; + let type_repr; match import_resolution.target_for_namespace(TypeNS) { None => { type_repr = ~""; } Some(_) => { diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 7be6bdb654e1f..1785feda7790c 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2366,7 +2366,7 @@ pub fn create_entry_wrapper(ccx: @CrateContext, // Call main. let lloutputarg = C_null(T_ptr(T_i8())); let llenvarg = unsafe { llvm::LLVMGetParam(llfdecl, 1 as c_uint) }; - let mut args = ~[lloutputarg, llenvarg]; + let args = ~[lloutputarg, llenvarg]; let llresult = Call(bcx, main_llfn, args); Store(bcx, llresult, fcx.llretptr.get()); diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index af00257fe1603..c4c6133b405c0 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -722,7 +722,7 @@ pub fn trans_arg_expr(bcx: block, } } }; - let mut arg_datum = arg_datumblock.datum; + let arg_datum = arg_datumblock.datum; let bcx = arg_datumblock.bcx; debug!(" arg datum: %s", arg_datum.to_str(bcx.ccx())); diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index ac6fa7a5a1c4f..fa6f7802e7d50 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -563,7 +563,6 @@ fn trans_rvalue_stmt_unadjusted(bcx: block, expr: @ast::expr) -> block { fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr, dest: Dest) -> block { - let mut bcx = bcx; let _icx = bcx.insn_ctxt("trans_rvalue_dps_unadjusted"); let tcx = bcx.tcx(); @@ -1408,7 +1407,6 @@ fn trans_eager_binop(bcx: block, lhs_datum: &Datum, rhs_datum: &Datum) -> DatumBlock { - let mut bcx = bcx; let _icx = bcx.insn_ctxt("trans_eager_binop"); let lhs = lhs_datum.to_appropriate_llval(bcx); diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index de6530fb464c5..9f9132fa94665 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -120,7 +120,7 @@ pub fn lookup( expr: @ast::expr, // The expression `a.b(...)`. self_expr: @ast::expr, // The expression `a`. callee_id: node_id, /* Where to store `a.b`'s type, - * also the scope of the call */ + * also the scope of the call */ m_name: ast::ident, // The ident `b`. self_ty: ty::t, // The type of `a`. supplied_tps: &[ty::t], // The list of types X, Y, ... . @@ -128,7 +128,7 @@ pub fn lookup( check_traits: CheckTraitsFlag, // Whether we check traits only. autoderef_receiver: AutoderefReceiverFlag) -> Option { - let mut impl_dups = @mut HashSet::new(); + let impl_dups = @mut HashSet::new(); let lcx = LookupContext { fcx: fcx, expr: expr, diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index fb58df3d55c68..70282fdc57cfe 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -1684,7 +1684,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, }; // construct the function type - let mut fn_ty = astconv::ty_of_closure(fcx, + let fn_ty = astconv::ty_of_closure(fcx, fcx, sigil, purity, @@ -1695,7 +1695,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, &opt_vec::Empty, expr.span); - let mut fty_sig; + let fty_sig; let fty = if error_happened { fty_sig = FnSig { bound_lifetime_names: opt_vec::Empty, diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index be513cbb0f307..fd19738b32110 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -871,7 +871,7 @@ pub mod guarantor { let tcx = rcx.tcx(); debug!("guarantor::for_by_ref(expr=%s, callee_scope=%?)", expr.repr(tcx), callee_scope); - let mut expr_cat = categorize(rcx, expr); + 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::re_scope(callee_scope); diff --git a/src/test/run-fail/borrowck-wg-fail-2.rs b/src/test/run-fail/borrowck-wg-fail-2.rs index 126135772ade0..121ec9c79216f 100644 --- a/src/test/run-fail/borrowck-wg-fail-2.rs +++ b/src/test/run-fail/borrowck-wg-fail-2.rs @@ -7,5 +7,6 @@ struct S { fn main() { let x = @mut S { x: 3 }; let y: &S = x; - x.x = 5; + let z = x; + z.x = 5; } diff --git a/src/test/run-fail/borrowck-wg-fail-3.rs b/src/test/run-fail/borrowck-wg-fail-3.rs index ad4c794212121..2b95cf3fe5fa9 100644 --- a/src/test/run-fail/borrowck-wg-fail-3.rs +++ b/src/test/run-fail/borrowck-wg-fail-3.rs @@ -3,6 +3,7 @@ fn main() { let x = @mut 3; let y: &mut int = x; - *x = 5; + let z = x; + *z = 5; } diff --git a/src/test/run-fail/borrowck-wg-fail.rs b/src/test/run-fail/borrowck-wg-fail.rs index d393832c6e862..fd2d36b895ada 100644 --- a/src/test/run-fail/borrowck-wg-fail.rs +++ b/src/test/run-fail/borrowck-wg-fail.rs @@ -1,9 +1,7 @@ // error-pattern:borrowed -fn f(x: &int, y: @mut int) { - unsafe { - *y = 2; - } +fn f(_x: &int, y: @mut int) { + *y = 2; } fn main() { From 5ab33a297521c2d5885422bc1744f6d9dab8f3f7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 08:49:48 -0400 Subject: [PATCH 11/46] correct incorrect handling of overloaded operators, exposing various other bits of rot --- src/libcore/ptr.rs | 16 +-- .../middle/borrowck/gather_loans/mod.rs | 63 +++--------- src/librustc/middle/typeck/check/regionck.rs | 9 +- src/libstd/arc.rs | 8 +- src/libstd/rope.rs | 28 +++--- src/libstd/sort.rs | 97 ++++++++++--------- src/libstd/std.rc | 7 ++ .../borrowck-loan-in-overloaded-op.rs | 10 +- src/test/run-pass/auto-ref-slice-plus-ref.rs | 8 +- 9 files changed, 117 insertions(+), 129 deletions(-) diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 86b36834bbd6e..85e46a0feff6b 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -296,34 +296,34 @@ impl Ord for *const T { // Equality for region pointers #[cfg(notest)] -impl<'self,T:Eq> Eq for &'self const T { +impl<'self,T:Eq> Eq for &'self T { #[inline(always)] - fn eq(&self, other: & &'self const T) -> bool { + fn eq(&self, other: & &'self T) -> bool { return *(*self) == *(*other); } #[inline(always)] - fn ne(&self, other: & &'self const T) -> bool { + fn ne(&self, other: & &'self T) -> bool { return *(*self) != *(*other); } } // Comparison for region pointers #[cfg(notest)] -impl<'self,T:Ord> Ord for &'self const T { +impl<'self,T:Ord> Ord for &'self T { #[inline(always)] - fn lt(&self, other: & &'self const T) -> bool { + fn lt(&self, other: & &'self T) -> bool { *(*self) < *(*other) } #[inline(always)] - fn le(&self, other: & &'self const T) -> bool { + fn le(&self, other: & &'self T) -> bool { *(*self) <= *(*other) } #[inline(always)] - fn ge(&self, other: & &'self const T) -> bool { + fn ge(&self, other: & &'self T) -> bool { *(*self) >= *(*other) } #[inline(always)] - fn gt(&self, other: & &'self const T) -> bool { + fn gt(&self, other: & &'self T) -> bool { *(*self) > *(*other) } } diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index 8a986a22d4c48..ecdf260bffffa 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -68,8 +68,7 @@ struct GatherLoanCtxt { id_range: id_range, all_loans: @mut ~[Loan], item_ub: ast::node_id, - repeating_ids: ~[ast::node_id], - ignore_adjustments: HashSet + repeating_ids: ~[ast::node_id] } pub fn gather_loans(bccx: @BorrowckCtxt, @@ -79,8 +78,7 @@ pub fn gather_loans(bccx: @BorrowckCtxt, id_range: id_range::max(), all_loans: @mut ~[], item_ub: body.node.id, - repeating_ids: ~[body.node.id], - ignore_adjustments: HashSet::new() + repeating_ids: ~[body.node.id] }; let v = visit::mk_vt(@visit::Visitor {visit_expr: gather_loans_in_expr, visit_block: gather_loans_in_block, @@ -147,13 +145,8 @@ fn gather_loans_in_expr(ex: @ast::expr, self.id_range.add(ex.callee_id); // If this expression is borrowed, have to ensure it remains valid: - { - let this = &mut *self; // FIXME(#5074) - if !this.ignore_adjustments.contains(&ex.id) { - for tcx.adjustments.find(&ex.id).each |&adjustments| { - this.guarantee_adjustments(ex, *adjustments); - } - } + for tcx.adjustments.find(&ex.id).each |&adjustments| { + self.guarantee_adjustments(ex, *adjustments); } // Special checks for various kinds of expressions: @@ -178,46 +171,20 @@ fn gather_loans_in_expr(ex: @ast::expr, visit::visit_expr(ex, self, vt); } - ast::expr_index(rcvr, _) | - ast::expr_binary(_, rcvr, _) | - ast::expr_unary(_, rcvr) | - ast::expr_assign_op(_, rcvr, _) + ast::expr_index(_, arg) | + ast::expr_binary(_, _, arg) if self.bccx.method_map.contains_key(&ex.id) => { - // Receivers in method calls are always passed by ref. - // - // Here, in an overloaded operator, the call is this expression, - // and hence the scope of the borrow is this call. - // - // FIX? / NOT REALLY---technically we should check the other - // argument and consider the argument mode. But how annoying. - // And this problem when goes away when argument modes are - // phased out. So I elect to leave this undone. - let scope_r = ty::re_scope(ex.id); - let rcvr_cmt = self.bccx.cat_expr(rcvr); - self.guarantee_valid(rcvr.id, rcvr.span, rcvr_cmt, m_imm, scope_r); - - // FIXME (#3387): Total hack: Ignore adjustments for the left-hand - // side. Their regions will be inferred to be too large. - self.ignore_adjustments.insert(rcvr.id); - - visit::visit_expr(ex, self, vt); + // Arguments in method calls are always passed by ref. + // + // Currently these do not use adjustments, so we have to + // hardcode this check here (note that the receiver DOES use + // adjustments). + let scope_r = ty::re_scope(ex.id); + let arg_cmt = self.bccx.cat_expr(arg); + self.guarantee_valid(arg.id, arg.span, arg_cmt, m_imm, scope_r); + visit::visit_expr(ex, self, vt); } - // FIXME--#3387 - // ast::expr_binary(_, lhs, rhs) => { - // // Universal comparison operators like ==, >=, etc - // // take their arguments by reference. - // let lhs_ty = ty::expr_ty(self.tcx(), lhs); - // if !ty::type_is_scalar(lhs_ty) { - // let scope_r = ty::re_scope(ex.id); - // let lhs_cmt = self.bccx.cat_expr(lhs); - // self.guarantee_valid(lhs_cmt, m_imm, scope_r); - // let rhs_cmt = self.bccx.cat_expr(rhs); - // self.guarantee_valid(rhs_cmt, m_imm, scope_r); - // } - // visit::visit_expr(ex, self, vt); - // } - // see explanation attached to the `root_ub` field: ast::expr_while(cond, ref body) => { // during the condition, can only root for the condition diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index fd19738b32110..1b0a22752a592 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -236,9 +236,16 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) { // overloaded. See #3511. let tcx = rcx.fcx.tcx(); match expr.node { + // You'd think that x += y where `+=` is overloaded would be a + // cleanup scope. You'd be... kind of right. In fact the + // handling of `+=` and friends in trans for overloaded + // operators is a hopeless mess and I can't figure out how to + // represent it. - ndm + // + // ast::expr_assign_op(*) | + ast::expr_index(*) | ast::expr_binary(*) | - ast::expr_assign_op(*) | ast::expr_unary(*) if has_method_map => { tcx.region_maps.record_cleanup_scope(expr.id); } diff --git a/src/libstd/arc.rs b/src/libstd/arc.rs index 027bf93b4814f..98d7a01b928b0 100644 --- a/src/libstd/arc.rs +++ b/src/libstd/arc.rs @@ -598,8 +598,8 @@ mod tests { let arc = ~RWARC(1); let arc2 = (*arc).clone(); do task::try || { - do arc2.write_downgrade |write_mode| { - do (&write_mode).write |one| { + do arc2.write_downgrade |mut write_mode| { + do write_mode.write |one| { assert!(*one == 2); } } @@ -733,8 +733,8 @@ mod tests { } // Downgrader (us) - do arc.write_downgrade |write_mode| { - do (&write_mode).write_cond |state, cond| { + do arc.write_downgrade |mut write_mode| { + do write_mode.write_cond |state, cond| { wc1.send(()); // send to another writer who will wake us up while *state == 0 { cond.wait(); diff --git a/src/libstd/rope.rs b/src/libstd/rope.rs index 1292d2a858559..93364f8a319ee 100644 --- a/src/libstd/rope.rs +++ b/src/libstd/rope.rs @@ -1256,22 +1256,24 @@ mod tests { match (r) { node::Empty => return ~"", node::Content(x) => { - let str = @mut ~""; - fn aux(str: @mut ~str, node: @node::Node) { + let mut str = ~""; + fn aux(str: &mut ~str, node: @node::Node) { match (*node) { - node::Leaf(x) => { - *str += str::slice( - *x.content, x.byte_offset, - x.byte_offset + x.byte_len).to_owned(); - } - node::Concat(ref x) => { - aux(str, x.left); - aux(str, x.right); - } + node::Leaf(x) => { + str::push_str( + str, + str::slice( + *x.content, x.byte_offset, + x.byte_offset + x.byte_len)); + } + node::Concat(ref x) => { + aux(str, x.left); + aux(str, x.right); + } } } - aux(str, x); - return *str + aux(&mut str, x); + return str } } } diff --git a/src/libstd/sort.rs b/src/libstd/sort.rs index f3d30ecd5cdf1..119b47c904e8e 100644 --- a/src/libstd/sort.rs +++ b/src/libstd/sort.rs @@ -22,12 +22,12 @@ type Le<'self, T> = &'self fn(v1: &T, v2: &T) -> bool; * Has worst case O(n log n) performance, best case O(n), but * is not space efficient. This is a stable sort. */ -pub fn merge_sort(v: &const [T], le: Le) -> ~[T] { +pub fn merge_sort(v: &[T], le: Le) -> ~[T] { type Slice = (uint, uint); return merge_sort_(v, (0u, len(v)), le); - fn merge_sort_(v: &const [T], slice: Slice, le: Le) + fn merge_sort_(v: &[T], slice: Slice, le: Le) -> ~[T] { let begin = slice.first(); let end = slice.second(); @@ -179,7 +179,7 @@ fn qsort3(arr: &mut [T], left: int, right: int) { */ pub fn quick_sort3(arr: &mut [T]) { if arr.len() <= 1 { return; } - let len = arr.len() - 1; // FIXME(#5074) nested calls + let len = arr.len(); // FIXME(#5074) nested calls qsort3(arr, 0, (len - 1) as int); } @@ -263,7 +263,7 @@ fn binarysort(array: &mut [T], start: uint) { assert!(left == right); let n = start-left; - copy_vec(array, left+1, array, left, n); + shift_vec(array, left+1, left, n); array[left] = pivot; start += 1; } @@ -309,8 +309,8 @@ fn count_run_ascending(array: &mut [T]) -> uint { return run; } -fn gallop_left(key: &const T, - array: &const [T], +fn gallop_left(key: &T, + array: &[T], hint: uint) -> uint { let size = array.len(); @@ -360,8 +360,8 @@ fn gallop_left(key: &const T, return ofs; } -fn gallop_right(key: &const T, - array: &const [T], +fn gallop_right(key: &T, + array: &[T], hint: uint) -> uint { let size = array.len(); @@ -457,15 +457,15 @@ impl MergeState { } let k = { // constrain lifetime of slice below - let slice = vec::mut_slice(array, b1, b1+l1); - gallop_right(&const array[b2], slice, 0) + let slice = vec::slice(array, b1, b1+l1); + gallop_right(&array[b2], slice, 0) }; b1 += k; l1 -= k; if l1 != 0 { let l2 = { // constrain lifetime of slice below - let slice = vec::mut_slice(array, b2, b2+l2); - gallop_left(&const array[b1+l1-1],slice,l2-1) + let slice = vec::slice(array, b2, b2+l2); + gallop_left(&array[b1+l1-1],slice,l2-1) }; if l2 > 0 { if l1 <= l2 { @@ -497,11 +497,11 @@ impl MergeState { dest += 1; c2 += 1; len2 -= 1; if len2 == 0 { - copy_vec(array, dest, tmp, 0, len1); + copy_vec(array, dest, tmp.slice(0, len1)); return; } if len1 == 1 { - copy_vec(array, dest, array, c2, len2); + shift_vec(array, dest, c2, len2); array[dest+len2] <-> tmp[c1]; return; } @@ -539,10 +539,12 @@ impl MergeState { loop { assert!(len1 > 1 && len2 != 0); - let tmp_view = vec::const_slice(tmp, c1, c1+len1); - count1 = gallop_right(&const array[c2], tmp_view, 0); + count1 = { + let tmp_view = vec::slice(tmp, c1, c1+len1); + gallop_right(&array[c2], tmp_view, 0) + }; if count1 != 0 { - copy_vec(array, dest, tmp, c1, count1); + copy_vec(array, dest, tmp.slice(c1, c1+count1)); dest += count1; c1 += count1; len1 -= count1; if len1 <= 1 { break_outer = true; break; } } @@ -550,10 +552,12 @@ impl MergeState { dest += 1; c2 += 1; len2 -= 1; if len2 == 0 { break_outer = true; break; } - let tmp_view = vec::const_slice(array, c2, c2+len2); - count2 = gallop_left(&const tmp[c1], tmp_view, 0); + count2 = { + let tmp_view = vec::slice(array, c2, c2+len2); + gallop_left(&tmp[c1], tmp_view, 0) + }; if count2 != 0 { - copy_vec(array, dest, array, c2, count2); + shift_vec(array, dest, c2, count2); dest += count2; c2 += count2; len2 -= count2; if len2 == 0 { break_outer = true; break; } } @@ -573,14 +577,14 @@ impl MergeState { if len1 == 1 { assert!(len2 > 0); - copy_vec(array, dest, array, c2, len2); + shift_vec(array, dest, c2, len2); array[dest+len2] <-> tmp[c1]; } else if len1 == 0 { fail!(~"Comparison violates its contract!"); } else { assert!(len2 == 0); assert!(len1 > 1); - copy_vec(array, dest, tmp, c1, len1); + copy_vec(array, dest, tmp.slice(c1, c1+len1)); } } @@ -603,13 +607,13 @@ impl MergeState { dest -= 1; c1 -= 1; len1 -= 1; if len1 == 0 { - copy_vec(array, dest-(len2-1), tmp, 0, len2); + copy_vec(array, dest-(len2-1), tmp.slice(0, len2)); return; } if len2 == 1 { dest -= len1; c1 -= len1; - copy_vec(array, dest+1, array, c1+1, len1); + shift_vec(array, dest+1, c1+1, len1); array[dest] <-> tmp[c2]; return; } @@ -650,12 +654,12 @@ impl MergeState { { // constrain scope of tmp_view: let tmp_view = vec::mut_slice (array, base1, base1+len1); count1 = len1 - gallop_right( - &const tmp[c2], tmp_view, len1-1); + &tmp[c2], tmp_view, len1-1); } if count1 != 0 { dest -= count1; c1 -= count1; len1 -= count1; - copy_vec(array, dest+1, array, c1+1, count1); + shift_vec(array, dest+1, c1+1, count1); if len1 == 0 { break_outer = true; break; } } @@ -666,14 +670,14 @@ impl MergeState { let count2; { // constrain scope of tmp_view let tmp_view = vec::mut_slice(tmp, 0, len2); - count2 = len2 - gallop_left(&const array[c1], + count2 = len2 - gallop_left(&array[c1], tmp_view, len2-1); } if count2 != 0 { dest -= count2; c2 -= count2; len2 -= count2; - copy_vec(array, dest+1, tmp, c2+1, count2); + copy_vec(array, dest+1, tmp.slice(c2+1, c2+1+count2)); if len2 <= 1 { break_outer = true; break; } } array[dest] <-> array[c1]; @@ -695,14 +699,14 @@ impl MergeState { assert!(len1 > 0); dest -= len1; c1 -= len1; - copy_vec(array, dest+1, array, c1+1, len1); + shift_vec(array, dest+1, c1+1, len1); array[dest] <-> tmp[c2]; } else if len2 == 0 { fail!(~"Comparison violates its contract!"); } else { assert!(len1 == 0); assert!(len2 != 0); - copy_vec(array, dest-(len2-1), tmp, 0, len2); + copy_vec(array, dest-(len2-1), tmp.slice(0, len2)); } } @@ -738,21 +742,25 @@ impl MergeState { #[inline(always)] fn copy_vec(dest: &mut [T], s1: uint, - from: &const [T], - s2: uint, - len: uint) { - assert!(s1+len <= dest.len() && s2+len <= from.len()); - - let mut slice = ~[]; - for uint::range(s2, s2+len) |i| { - slice.push(from[i]); - } + from: &[T]) { + assert!(s1+from.len() <= dest.len()); - for slice.eachi |i, v| { + for from.eachi |i, v| { dest[s1+i] = *v; } } +#[inline(always)] +fn shift_vec(dest: &mut [T], + s1: uint, + s2: uint, + len: uint) { + assert!(s1+len <= dest.len()); + + let tmp = dest.slice(s2, s2+len).to_vec(); + copy_vec(dest, s1, tmp); +} + #[cfg(test)] mod test_qsort3 { use sort::*; @@ -764,8 +772,7 @@ mod test_qsort3 { quick_sort3::(v1); let mut i = 0; while i < len { - // debug!(v2[i]); - assert!((v2[i] == v1[i])); + assert_eq!(v2[i], v1[i]); i += 1; } } @@ -1036,7 +1043,7 @@ mod big_tests { tabulate_managed(low, high); } - fn multiplyVec(arr: &const [T], num: uint) -> ~[T] { + fn multiplyVec(arr: &[T], num: uint) -> ~[T] { let size = arr.len(); let res = do vec::from_fn(num) |i| { arr[i % size] @@ -1052,7 +1059,7 @@ mod big_tests { } fn tabulate_unique(lo: uint, hi: uint) { - fn isSorted(arr: &const [T]) { + fn isSorted(arr: &[T]) { for uint::range(0, arr.len()-1) |i| { if arr[i] > arr[i+1] { fail!(~"Array not sorted"); @@ -1123,7 +1130,7 @@ mod big_tests { } fn tabulate_managed(lo: uint, hi: uint) { - fn isSorted(arr: &const [@T]) { + fn isSorted(arr: &[@T]) { for uint::range(0, arr.len()-1) |i| { if arr[i] > arr[i+1] { fail!(~"Array not sorted"); diff --git a/src/libstd/std.rc b/src/libstd/std.rc index d9af8b111bba7..9d1ddb8ec544b 100644 --- a/src/libstd/std.rc +++ b/src/libstd/std.rc @@ -69,7 +69,14 @@ pub mod list; pub mod priority_queue; pub mod rope; pub mod smallintmap; + +#[cfg(stage0)] +#[path="sort_stage0.rs"] +pub mod sort; + +#[cfg(not(stage0))] pub mod sort; + pub mod dlist; pub mod treemap; diff --git a/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs b/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs index 482d1b6b8b617..0361213af2226 100644 --- a/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs +++ b/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs @@ -8,18 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// xfail-test #3387 - struct foo(~uint); impl Add for foo { - fn add(f: &foo) -> foo { - foo(~(**self + **(*f))) + fn add(&self, f: &foo) -> foo { + foo(~(***self + **(*f))) } } fn main() { let x = foo(~3); - let _y = x + x; - //~^ ERROR moving out of immutable local variable prohibited due to outstanding loan + let _y = x + {x}; // the `{x}` forces a move to occur + //~^ ERROR cannot move out of `x` } diff --git a/src/test/run-pass/auto-ref-slice-plus-ref.rs b/src/test/run-pass/auto-ref-slice-plus-ref.rs index 1dc56132875d4..07629651e5605 100644 --- a/src/test/run-pass/auto-ref-slice-plus-ref.rs +++ b/src/test/run-pass/auto-ref-slice-plus-ref.rs @@ -17,13 +17,13 @@ trait MyIter { } impl<'self> MyIter for &'self [int] { - fn test_imm(&self) { assert!(self[0] == 1) } - fn test_const(&const self) { assert!(self[0] == 1) } + fn test_imm(&self) { assert_eq!(self[0], 1) } + fn test_const(&const self) { assert_eq!(self[0], 1) } } impl<'self> MyIter for &'self str { - fn test_imm(&self) { assert!(*self == "test") } - fn test_const(&const self) { assert!(*self == "test") } + fn test_imm(&self) { assert_eq!(*self, "test") } + fn test_const(&const self) { assert_eq!(self[0], 't') } } pub fn main() { From d96c65afc88b159b9ae76136f8d2695574e273f0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 08:50:04 -0400 Subject: [PATCH 12/46] keep old sort for stage0 --- src/libstd/sort_stage0.rs | 1239 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1239 insertions(+) create mode 100644 src/libstd/sort_stage0.rs diff --git a/src/libstd/sort_stage0.rs b/src/libstd/sort_stage0.rs new file mode 100644 index 0000000000000..f3d30ecd5cdf1 --- /dev/null +++ b/src/libstd/sort_stage0.rs @@ -0,0 +1,1239 @@ +// Copyright 2012 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. + +//! Sorting methods + +use core::cmp::{Eq, Ord}; +use core::vec::len; +use core::vec; + +type Le<'self, T> = &'self fn(v1: &T, v2: &T) -> bool; + +/** + * Merge sort. Returns a new vector containing the sorted list. + * + * Has worst case O(n log n) performance, best case O(n), but + * is not space efficient. This is a stable sort. + */ +pub fn merge_sort(v: &const [T], le: Le) -> ~[T] { + type Slice = (uint, uint); + + return merge_sort_(v, (0u, len(v)), le); + + fn merge_sort_(v: &const [T], slice: Slice, le: Le) + -> ~[T] { + let begin = slice.first(); + let end = slice.second(); + + let v_len = end - begin; + if v_len == 0 { return ~[]; } + if v_len == 1 { return ~[v[begin]]; } + + let mid = v_len / 2 + begin; + let a = (begin, mid); + let b = (mid, end); + return merge(le, merge_sort_(v, a, le), merge_sort_(v, b, le)); + } + + fn merge(le: Le, a: &[T], b: &[T]) -> ~[T] { + let mut rs = vec::with_capacity(len(a) + len(b)); + let a_len = len(a); + let mut a_ix = 0; + let b_len = len(b); + let mut b_ix = 0; + while a_ix < a_len && b_ix < b_len { + if le(&a[a_ix], &b[b_ix]) { + rs.push(a[a_ix]); + a_ix += 1; + } else { rs.push(b[b_ix]); b_ix += 1; } + } + rs.push_all(vec::slice(a, a_ix, a_len)); + rs.push_all(vec::slice(b, b_ix, b_len)); + rs + } +} + +#[cfg(stage0)] +fn part(arr: &mut [T], left: uint, + right: uint, pivot: uint, compare_func: Le) -> uint { + arr[pivot] <-> arr[right]; + let mut storage_index: uint = left; + let mut i: uint = left; + while i < right { + let a: &mut T = &mut arr[i]; + let b: &mut T = &mut arr[right]; + if compare_func(a, b) { + arr[i] <-> arr[storage_index]; + storage_index += 1; + } + i += 1; + } + arr[storage_index] <-> arr[right]; + return storage_index; +} + +#[cfg(not(stage0))] +fn part(arr: &mut [T], left: uint, + right: uint, pivot: uint, compare_func: Le) -> uint { + arr[pivot] <-> arr[right]; + let mut storage_index: uint = left; + let mut i: uint = left; + while i < right { + if compare_func(&arr[i], &arr[right]) { + arr[i] <-> arr[storage_index]; + storage_index += 1; + } + i += 1; + } + arr[storage_index] <-> arr[right]; + return storage_index; +} + +fn qsort(arr: &mut [T], left: uint, + right: uint, compare_func: Le) { + if right > left { + let pivot = (left + right) / 2u; + let new_pivot = part::(arr, left, right, pivot, compare_func); + if new_pivot != 0u { + // Need to do this check before recursing due to overflow + qsort::(arr, left, new_pivot - 1u, compare_func); + } + qsort::(arr, new_pivot + 1u, right, compare_func); + } +} + +/** + * Quicksort. Sorts a mut vector in place. + * + * Has worst case O(n^2) performance, average case O(n log n). + * This is an unstable sort. + */ +pub fn quick_sort(arr: &mut [T], compare_func: Le) { + if len::(arr) == 0u { return; } + qsort::(arr, 0u, len::(arr) - 1u, compare_func); +} + +fn qsort3(arr: &mut [T], left: int, right: int) { + if right <= left { return; } + let v: T = arr[right]; + let mut i: int = left - 1; + let mut j: int = right; + let mut p: int = i; + let mut q: int = j; + loop { + i += 1; + while arr[i] < v { i += 1; } + j -= 1; + while v < arr[j] { + if j == left { break; } + j -= 1; + } + if i >= j { break; } + arr[i] <-> arr[j]; + if arr[i] == v { + p += 1; + arr[p] <-> arr[i]; + } + if v == arr[j] { + q -= 1; + arr[j] <-> arr[q]; + } + } + arr[i] <-> arr[right]; + j = i - 1; + i += 1; + let mut k: int = left; + while k < p { + arr[k] <-> arr[j]; + k += 1; + j -= 1; + if k == len::(arr) as int { break; } + } + k = right - 1; + while k > q { + arr[i] <-> arr[k]; + k -= 1; + i += 1; + if k == 0 { break; } + } + qsort3::(arr, left, j); + qsort3::(arr, i, right); +} + +/** + * Fancy quicksort. Sorts a mut vector in place. + * + * Based on algorithm presented by ~[Sedgewick and Bentley] + * (http://www.cs.princeton.edu/~rs/talks/QuicksortIsOptimal.pdf). + * According to these slides this is the algorithm of choice for + * 'randomly ordered keys, abstract compare' & 'small number of key values'. + * + * This is an unstable sort. + */ +pub fn quick_sort3(arr: &mut [T]) { + if arr.len() <= 1 { return; } + let len = arr.len() - 1; // FIXME(#5074) nested calls + qsort3(arr, 0, (len - 1) as int); +} + +pub trait Sort { + fn qsort(self); +} + +impl<'self, T:Copy + Ord + Eq> Sort for &'self mut [T] { + fn qsort(self) { quick_sort3(self); } +} + +static MIN_MERGE: uint = 64; +static MIN_GALLOP: uint = 7; +static INITIAL_TMP_STORAGE: uint = 128; + +pub fn tim_sort(array: &mut [T]) { + let size = array.len(); + if size < 2 { + return; + } + + if size < MIN_MERGE { + let init_run_len = count_run_ascending(array); + binarysort(array, init_run_len); + return; + } + + let mut ms = MergeState(); + let min_run = min_run_length(size); + + let mut idx = 0; + let mut remaining = size; + loop { + let run_len: uint = { + // This scope contains the slice `arr` here: + let arr = vec::mut_slice(array, idx, size); + let mut run_len: uint = count_run_ascending(arr); + + if run_len < min_run { + let force = if remaining <= min_run {remaining} else {min_run}; + let slice = vec::mut_slice(arr, 0, force); + binarysort(slice, run_len); + run_len = force; + } + + run_len + }; + + ms.push_run(idx, run_len); + ms.merge_collapse(array); + + idx += run_len; + remaining -= run_len; + if remaining == 0 { break; } + } + + ms.merge_force_collapse(array); +} + +fn binarysort(array: &mut [T], start: uint) { + let size = array.len(); + let mut start = start; + assert!(start <= size); + + if start == 0 { start += 1; } + + while start < size { + let pivot = array[start]; + let mut left = 0; + let mut right = start; + assert!(left <= right); + + while left < right { + let mid = (left + right) >> 1; + if pivot < array[mid] { + right = mid; + } else { + left = mid+1; + } + } + assert!(left == right); + let n = start-left; + + copy_vec(array, left+1, array, left, n); + array[left] = pivot; + start += 1; + } +} + +// Reverse the order of elements in a slice, in place +fn reverse_slice(v: &mut [T], start: uint, end:uint) { + let mut i = start; + while i < end / 2 { + v[i] <-> v[end - i - 1]; + i += 1; + } +} + +fn min_run_length(n: uint) -> uint { + let mut n = n; + let mut r = 0; // becomes 1 if any 1 bits are shifted off + + while n >= MIN_MERGE { + r |= n & 1; + n >>= 1; + } + return n + r; +} + +fn count_run_ascending(array: &mut [T]) -> uint { + let size = array.len(); + assert!(size > 0); + if size == 1 { return 1; } + + let mut run = 2; + if array[1] < array[0] { + while run < size && array[run] < array[run-1] { + run += 1; + } + reverse_slice(array, 0, run); + } else { + while run < size && array[run] >= array[run-1] { + run += 1; + } + } + + return run; +} + +fn gallop_left(key: &const T, + array: &const [T], + hint: uint) + -> uint { + let size = array.len(); + assert!(size != 0 && hint < size); + + let mut last_ofs = 0; + let mut ofs = 1; + + if *key > array[hint] { + // Gallop right until array[hint+last_ofs] < key <= array[hint+ofs] + let max_ofs = size - hint; + while ofs < max_ofs && *key > array[hint+ofs] { + last_ofs = ofs; + ofs = (ofs << 1) + 1; + if ofs < last_ofs { ofs = max_ofs; } // uint overflow guard + } + if ofs > max_ofs { ofs = max_ofs; } + + last_ofs += hint; + ofs += hint; + } else { + let max_ofs = hint + 1; + while ofs < max_ofs && *key <= array[hint-ofs] { + last_ofs = ofs; + ofs = (ofs << 1) + 1; + if ofs < last_ofs { ofs = max_ofs; } // uint overflow guard + } + + if ofs > max_ofs { ofs = max_ofs; } + + let tmp = last_ofs; + last_ofs = hint - ofs; + ofs = hint - tmp; + } + assert!((last_ofs < ofs || last_ofs+1 < ofs+1) && ofs <= size); + + last_ofs += 1; + while last_ofs < ofs { + let m = last_ofs + ((ofs - last_ofs) >> 1); + if *key > array[m] { + last_ofs = m+1; + } else { + ofs = m; + } + } + assert!(last_ofs == ofs); + return ofs; +} + +fn gallop_right(key: &const T, + array: &const [T], + hint: uint) + -> uint { + let size = array.len(); + assert!(size != 0 && hint < size); + + let mut last_ofs = 0; + let mut ofs = 1; + + if *key >= array[hint] { + // Gallop right until array[hint+last_ofs] <= key < array[hint+ofs] + let max_ofs = size - hint; + while ofs < max_ofs && *key >= array[hint+ofs] { + last_ofs = ofs; + ofs = (ofs << 1) + 1; + if ofs < last_ofs { ofs = max_ofs; } + } + if ofs > max_ofs { ofs = max_ofs; } + + last_ofs += hint; + ofs += hint; + } else { + // Gallop left until array[hint-ofs] <= key < array[hint-last_ofs] + let max_ofs = hint + 1; + while ofs < max_ofs && *key < array[hint-ofs] { + last_ofs = ofs; + ofs = (ofs << 1) + 1; + if ofs < last_ofs { ofs = max_ofs; } + } + if ofs > max_ofs { ofs = max_ofs; } + + let tmp = last_ofs; + last_ofs = hint - ofs; + ofs = hint - tmp; + } + + assert!((last_ofs < ofs || last_ofs+1 < ofs+1) && ofs <= size); + + last_ofs += 1; + while last_ofs < ofs { + let m = last_ofs + ((ofs - last_ofs) >> 1); + + if *key >= array[m] { + last_ofs = m + 1; + } else { + ofs = m; + } + } + assert!(last_ofs == ofs); + return ofs; +} + +struct RunState { + base: uint, + len: uint, +} + +struct MergeState { + min_gallop: uint, + runs: ~[RunState], +} + +// Fixme (#3853) Move into MergeState +fn MergeState() -> MergeState { + MergeState { + min_gallop: MIN_GALLOP, + runs: ~[], + } +} + +impl MergeState { + fn push_run(&mut self, run_base: uint, run_len: uint) { + let tmp = RunState{base: run_base, len: run_len}; + self.runs.push(tmp); + } + + fn merge_at(&mut self, n: uint, array: &mut [T]) { + let size = self.runs.len(); + assert!(size >= 2); + assert!(n == size-2 || n == size-3); + + let mut b1 = self.runs[n].base; + let mut l1 = self.runs[n].len; + let b2 = self.runs[n+1].base; + let l2 = self.runs[n+1].len; + + assert!(l1 > 0 && l2 > 0); + assert!(b1 + l1 == b2); + + self.runs[n].len = l1 + l2; + if n == size-3 { + self.runs[n+1].base = self.runs[n+2].base; + self.runs[n+1].len = self.runs[n+2].len; + } + + let k = { // constrain lifetime of slice below + let slice = vec::mut_slice(array, b1, b1+l1); + gallop_right(&const array[b2], slice, 0) + }; + b1 += k; + l1 -= k; + if l1 != 0 { + let l2 = { // constrain lifetime of slice below + let slice = vec::mut_slice(array, b2, b2+l2); + gallop_left(&const array[b1+l1-1],slice,l2-1) + }; + if l2 > 0 { + if l1 <= l2 { + self.merge_lo(array, b1, l1, b2, l2); + } else { + self.merge_hi(array, b1, l1, b2, l2); + } + } + } + self.runs.pop(); + } + + fn merge_lo(&mut self, array: &mut [T], base1: uint, len1: uint, + base2: uint, len2: uint) { + assert!(len1 != 0 && len2 != 0 && base1+len1 == base2); + + let mut tmp = ~[]; + for uint::range(base1, base1+len1) |i| { + tmp.push(array[i]); + } + + let mut c1 = 0; + let mut c2 = base2; + let mut dest = base1; + let mut len1 = len1; + let mut len2 = len2; + + array[dest] <-> array[c2]; + dest += 1; c2 += 1; len2 -= 1; + + if len2 == 0 { + copy_vec(array, dest, tmp, 0, len1); + return; + } + if len1 == 1 { + copy_vec(array, dest, array, c2, len2); + array[dest+len2] <-> tmp[c1]; + return; + } + + let mut min_gallop = self.min_gallop; + loop { + let mut count1 = 0; + let mut count2 = 0; + let mut break_outer = false; + + loop { + assert!(len1 > 1 && len2 != 0); + if array[c2] < tmp[c1] { + array[dest] <-> array[c2]; + dest += 1; c2 += 1; len2 -= 1; + count2 += 1; count1 = 0; + if len2 == 0 { + break_outer = true; + } + } else { + array[dest] <-> tmp[c1]; + dest += 1; c1 += 1; len1 -= 1; + count1 += 1; count2 = 0; + if len1 == 1 { + break_outer = true; + } + } + if break_outer || ((count1 | count2) >= min_gallop) { + break; + } + } + if break_outer { break; } + + // Start to gallop + loop { + assert!(len1 > 1 && len2 != 0); + + let tmp_view = vec::const_slice(tmp, c1, c1+len1); + count1 = gallop_right(&const array[c2], tmp_view, 0); + if count1 != 0 { + copy_vec(array, dest, tmp, c1, count1); + dest += count1; c1 += count1; len1 -= count1; + if len1 <= 1 { break_outer = true; break; } + } + array[dest] <-> array[c2]; + dest += 1; c2 += 1; len2 -= 1; + if len2 == 0 { break_outer = true; break; } + + let tmp_view = vec::const_slice(array, c2, c2+len2); + count2 = gallop_left(&const tmp[c1], tmp_view, 0); + if count2 != 0 { + copy_vec(array, dest, array, c2, count2); + dest += count2; c2 += count2; len2 -= count2; + if len2 == 0 { break_outer = true; break; } + } + array[dest] <-> tmp[c1]; + dest += 1; c1 += 1; len1 -= 1; + if len1 == 1 { break_outer = true; break; } + min_gallop -= 1; + if !(count1 >= MIN_GALLOP || count2 >= MIN_GALLOP) { + break; + } + } + if break_outer { break; } + if min_gallop < 0 { min_gallop = 0; } + min_gallop += 2; // Penalize for leaving gallop + } + self.min_gallop = if min_gallop < 1 { 1 } else { min_gallop }; + + if len1 == 1 { + assert!(len2 > 0); + copy_vec(array, dest, array, c2, len2); + array[dest+len2] <-> tmp[c1]; + } else if len1 == 0 { + fail!(~"Comparison violates its contract!"); + } else { + assert!(len2 == 0); + assert!(len1 > 1); + copy_vec(array, dest, tmp, c1, len1); + } + } + + fn merge_hi(&mut self, array: &mut [T], base1: uint, len1: uint, + base2: uint, len2: uint) { + assert!(len1 != 1 && len2 != 0 && base1 + len1 == base2); + + let mut tmp = ~[]; + for uint::range(base2, base2+len2) |i| { + tmp.push(array[i]); + } + + let mut c1 = base1 + len1 - 1; + let mut c2 = len2 - 1; + let mut dest = base2 + len2 - 1; + let mut len1 = len1; + let mut len2 = len2; + + array[dest] <-> array[c1]; + dest -= 1; c1 -= 1; len1 -= 1; + + if len1 == 0 { + copy_vec(array, dest-(len2-1), tmp, 0, len2); + return; + } + if len2 == 1 { + dest -= len1; + c1 -= len1; + copy_vec(array, dest+1, array, c1+1, len1); + array[dest] <-> tmp[c2]; + return; + } + + let mut min_gallop = self.min_gallop; + loop { + let mut count1 = 0; + let mut count2 = 0; + let mut break_outer = false; + + loop { + assert!(len1 != 0 && len2 > 1); + if tmp[c2] < array[c1] { + array[dest] <-> array[c1]; + dest -= 1; c1 -= 1; len1 -= 1; + count1 += 1; count2 = 0; + if len1 == 0 { + break_outer = true; + } + } else { + array[dest] <-> tmp[c2]; + dest -= 1; c2 -= 1; len2 -= 1; + count2 += 1; count1 = 0; + if len2 == 1 { + break_outer = true; + } + } + if break_outer || ((count1 | count2) >= min_gallop) { + break; + } + } + if break_outer { break; } + + // Start to gallop + loop { + assert!(len2 > 1 && len1 != 0); + + { // constrain scope of tmp_view: + let tmp_view = vec::mut_slice (array, base1, base1+len1); + count1 = len1 - gallop_right( + &const tmp[c2], tmp_view, len1-1); + } + + if count1 != 0 { + dest -= count1; c1 -= count1; len1 -= count1; + copy_vec(array, dest+1, array, c1+1, count1); + if len1 == 0 { break_outer = true; break; } + } + + array[dest] <-> tmp[c2]; + dest -= 1; c2 -= 1; len2 -= 1; + if len2 == 1 { break_outer = true; break; } + + let count2; + { // constrain scope of tmp_view + let tmp_view = vec::mut_slice(tmp, 0, len2); + count2 = len2 - gallop_left(&const array[c1], + tmp_view, + len2-1); + } + + if count2 != 0 { + dest -= count2; c2 -= count2; len2 -= count2; + copy_vec(array, dest+1, tmp, c2+1, count2); + if len2 <= 1 { break_outer = true; break; } + } + array[dest] <-> array[c1]; + dest -= 1; c1 -= 1; len1 -= 1; + if len1 == 0 { break_outer = true; break; } + min_gallop -= 1; + if !(count1 >= MIN_GALLOP || count2 >= MIN_GALLOP) { + break; + } + } + + if break_outer { break; } + if min_gallop < 0 { min_gallop = 0; } + min_gallop += 2; // Penalize for leaving gallop + } + self.min_gallop = if min_gallop < 1 { 1 } else { min_gallop }; + + if len2 == 1 { + assert!(len1 > 0); + dest -= len1; + c1 -= len1; + copy_vec(array, dest+1, array, c1+1, len1); + array[dest] <-> tmp[c2]; + } else if len2 == 0 { + fail!(~"Comparison violates its contract!"); + } else { + assert!(len1 == 0); + assert!(len2 != 0); + copy_vec(array, dest-(len2-1), tmp, 0, len2); + } + } + + fn merge_collapse(&mut self, array: &mut [T]) { + while self.runs.len() > 1 { + let mut n = self.runs.len()-2; + if n > 0 && + self.runs[n-1].len <= self.runs[n].len + self.runs[n+1].len + { + if self.runs[n-1].len < self.runs[n+1].len { n -= 1; } + } else if self.runs[n].len <= self.runs[n+1].len { + /* keep going */ + } else { + break; + } + self.merge_at(n, array); + } + } + + fn merge_force_collapse(&mut self, array: &mut [T]) { + while self.runs.len() > 1 { + let mut n = self.runs.len()-2; + if n > 0 { + if self.runs[n-1].len < self.runs[n+1].len { + n -= 1; + } + } + self.merge_at(n, array); + } + } +} + +#[inline(always)] +fn copy_vec(dest: &mut [T], + s1: uint, + from: &const [T], + s2: uint, + len: uint) { + assert!(s1+len <= dest.len() && s2+len <= from.len()); + + let mut slice = ~[]; + for uint::range(s2, s2+len) |i| { + slice.push(from[i]); + } + + for slice.eachi |i, v| { + dest[s1+i] = *v; + } +} + +#[cfg(test)] +mod test_qsort3 { + use sort::*; + + use core::vec; + + fn check_sort(v1: &mut [int], v2: &mut [int]) { + let len = vec::len::(v1); + quick_sort3::(v1); + let mut i = 0; + while i < len { + // debug!(v2[i]); + assert!((v2[i] == v1[i])); + i += 1; + } + } + + #[test] + fn test() { + { + let mut v1 = ~[3, 7, 4, 5, 2, 9, 5, 8]; + let mut v2 = ~[2, 3, 4, 5, 5, 7, 8, 9]; + check_sort(v1, v2); + } + { + let mut v1 = ~[1, 1, 1]; + let mut v2 = ~[1, 1, 1]; + check_sort(v1, v2); + } + { + let mut v1: ~[int] = ~[]; + let mut v2: ~[int] = ~[]; + check_sort(v1, v2); + } + { let mut v1 = ~[9]; let mut v2 = ~[9]; check_sort(v1, v2); } + { + let mut v1 = ~[9, 3, 3, 3, 9]; + let mut v2 = ~[3, 3, 3, 9, 9]; + check_sort(v1, v2); + } + } +} + +#[cfg(test)] +mod test_qsort { + use sort::*; + + use core::int; + use core::vec; + + fn check_sort(v1: &mut [int], v2: &mut [int]) { + let len = vec::len::(v1); + fn leual(a: &int, b: &int) -> bool { *a <= *b } + quick_sort::(v1, leual); + let mut i = 0u; + while i < len { + // debug!(v2[i]); + assert!((v2[i] == v1[i])); + i += 1; + } + } + + #[test] + fn test() { + { + let mut v1 = ~[3, 7, 4, 5, 2, 9, 5, 8]; + let mut v2 = ~[2, 3, 4, 5, 5, 7, 8, 9]; + check_sort(v1, v2); + } + { + let mut v1 = ~[1, 1, 1]; + let mut v2 = ~[1, 1, 1]; + check_sort(v1, v2); + } + { + let mut v1: ~[int] = ~[]; + let mut v2: ~[int] = ~[]; + check_sort(v1, v2); + } + { let mut v1 = ~[9]; let mut v2 = ~[9]; check_sort(v1, v2); } + { + let mut v1 = ~[9, 3, 3, 3, 9]; + let mut v2 = ~[3, 3, 3, 9, 9]; + check_sort(v1, v2); + } + } + + // Regression test for #750 + #[test] + fn test_simple() { + let mut names = ~[2, 1, 3]; + + let expected = ~[1, 2, 3]; + + do quick_sort(names) |x, y| { int::le(*x, *y) }; + + let immut_names = names; + + let pairs = vec::zip_slice(expected, immut_names); + for vec::each(pairs) |p| { + let (a, b) = *p; + debug!("%d %d", a, b); + assert!((a == b)); + } + } +} + +#[cfg(test)] +mod tests { + + use sort::*; + + use core::vec; + + fn check_sort(v1: &[int], v2: &[int]) { + let len = vec::len::(v1); + pub fn le(a: &int, b: &int) -> bool { *a <= *b } + let f = le; + let v3 = merge_sort::(v1, f); + let mut i = 0u; + while i < len { + debug!(v3[i]); + assert!((v3[i] == v2[i])); + i += 1; + } + } + + #[test] + fn test() { + { + let v1 = ~[3, 7, 4, 5, 2, 9, 5, 8]; + let v2 = ~[2, 3, 4, 5, 5, 7, 8, 9]; + check_sort(v1, v2); + } + { let v1 = ~[1, 1, 1]; let v2 = ~[1, 1, 1]; check_sort(v1, v2); } + { let v1:~[int] = ~[]; let v2:~[int] = ~[]; check_sort(v1, v2); } + { let v1 = ~[9]; let v2 = ~[9]; check_sort(v1, v2); } + { + let v1 = ~[9, 3, 3, 3, 9]; + let v2 = ~[3, 3, 3, 9, 9]; + check_sort(v1, v2); + } + } + + #[test] + fn test_merge_sort_mutable() { + pub fn le(a: &int, b: &int) -> bool { *a <= *b } + let mut v1 = ~[3, 2, 1]; + let v2 = merge_sort(v1, le); + assert!(v2 == ~[1, 2, 3]); + } + + #[test] + fn test_merge_sort_stability() { + // tjc: funny that we have to use parens + fn ile(x: &(&'static str), y: &(&'static str)) -> bool + { + // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use + // to_ascii_consume and to_str_consume to not do a unnecessary copy. + // (Actually, could just remove the to_str_* call, but needs an deriving(Ord) on + // Ascii) + let x = x.to_ascii().to_lower().to_str_ascii(); + let y = y.to_ascii().to_lower().to_str_ascii(); + x <= y + } + + let names1 = ~["joe bob", "Joe Bob", "Jack Brown", "JOE Bob", + "Sally Mae", "JOE BOB", "Alex Andy"]; + let names2 = ~["Alex Andy", "Jack Brown", "joe bob", "Joe Bob", + "JOE Bob", "JOE BOB", "Sally Mae"]; + let names3 = merge_sort(names1, ile); + assert!(names3 == names2); + } +} + +#[cfg(test)] +mod test_tim_sort { + use sort::tim_sort; + use core::rand::RngUtil; + + struct CVal { + val: float, + } + + impl Ord for CVal { + fn lt(&self, other: &CVal) -> bool { + let rng = rand::rng(); + if rng.gen::() > 0.995 { fail!(~"It's happening!!!"); } + (*self).val < other.val + } + fn le(&self, other: &CVal) -> bool { (*self).val <= other.val } + fn gt(&self, other: &CVal) -> bool { (*self).val > other.val } + fn ge(&self, other: &CVal) -> bool { (*self).val >= other.val } + } + + fn check_sort(v1: &mut [int], v2: &mut [int]) { + let len = vec::len::(v1); + tim_sort::(v1); + let mut i = 0u; + while i < len { + // debug!(v2[i]); + assert!((v2[i] == v1[i])); + i += 1u; + } + } + + #[test] + fn test() { + { + let mut v1 = ~[3, 7, 4, 5, 2, 9, 5, 8]; + let mut v2 = ~[2, 3, 4, 5, 5, 7, 8, 9]; + check_sort(v1, v2); + } + { + let mut v1 = ~[1, 1, 1]; + let mut v2 = ~[1, 1, 1]; + check_sort(v1, v2); + } + { + let mut v1: ~[int] = ~[]; + let mut v2: ~[int] = ~[]; + check_sort(v1, v2); + } + { let mut v1 = ~[9]; let mut v2 = ~[9]; check_sort(v1, v2); } + { + let mut v1 = ~[9, 3, 3, 3, 9]; + let mut v2 = ~[3, 3, 3, 9, 9]; + check_sort(v1, v2); + } + } + + #[test] + #[should_fail] + #[cfg(unix)] + fn crash_test() { + let rng = rand::rng(); + let mut arr = do vec::from_fn(1000) |_i| { + CVal { val: rng.gen() } + }; + + tim_sort(arr); + fail!(~"Guarantee the fail"); + } + + struct DVal { val: uint } + + impl Ord for DVal { + fn lt(&self, _x: &DVal) -> bool { true } + fn le(&self, _x: &DVal) -> bool { true } + fn gt(&self, _x: &DVal) -> bool { true } + fn ge(&self, _x: &DVal) -> bool { true } + } + + #[test] + fn test_bad_Ord_impl() { + let rng = rand::rng(); + let mut arr = do vec::from_fn(500) |_i| { + DVal { val: rng.gen() } + }; + + tim_sort(arr); + } +} + +#[cfg(test)] +mod big_tests { + use sort::*; + use core::rand::RngUtil; + + #[test] + fn test_unique() { + let low = 5; + let high = 10; + tabulate_unique(low, high); + } + + #[test] + fn test_managed() { + let low = 5; + let high = 10; + tabulate_managed(low, high); + } + + fn multiplyVec(arr: &const [T], num: uint) -> ~[T] { + let size = arr.len(); + let res = do vec::from_fn(num) |i| { + arr[i % size] + }; + res + } + + fn makeRange(n: uint) -> ~[uint] { + let one = do vec::from_fn(n) |i| { i }; + let mut two = copy one; + vec::reverse(two); + vec::append(two, one) + } + + fn tabulate_unique(lo: uint, hi: uint) { + fn isSorted(arr: &const [T]) { + for uint::range(0, arr.len()-1) |i| { + if arr[i] > arr[i+1] { + fail!(~"Array not sorted"); + } + } + } + + let rng = rand::rng(); + + for uint::range(lo, hi) |i| { + let n = 1 << i; + let mut arr: ~[float] = do vec::from_fn(n) |_i| { + rng.gen() + }; + + tim_sort(arr); // *sort + isSorted(arr); + + vec::reverse(arr); + tim_sort(arr); // \sort + isSorted(arr); + + tim_sort(arr); // /sort + isSorted(arr); + + for 3.times { + let i1 = rng.gen_uint_range(0, n); + let i2 = rng.gen_uint_range(0, n); + arr[i1] <-> arr[i2]; + } + tim_sort(arr); // 3sort + isSorted(arr); + + if n >= 10 { + let size = arr.len(); + let mut idx = 1; + while idx <= 10 { + arr[size-idx] = rng.gen(); + idx += 1; + } + } + tim_sort(arr); // +sort + isSorted(arr); + + for (n/100).times { + let idx = rng.gen_uint_range(0, n); + arr[idx] = rng.gen(); + } + tim_sort(arr); + isSorted(arr); + + let mut arr = if n > 4 { + let part = vec::slice(arr, 0, 4); + multiplyVec(part, n) + } else { arr }; + tim_sort(arr); // ~sort + isSorted(arr); + + let mut arr = vec::from_elem(n, -0.5); + tim_sort(arr); // =sort + isSorted(arr); + + let half = n / 2; + let mut arr = makeRange(half).map(|i| *i as float); + tim_sort(arr); // !sort + isSorted(arr); + } + } + + fn tabulate_managed(lo: uint, hi: uint) { + fn isSorted(arr: &const [@T]) { + for uint::range(0, arr.len()-1) |i| { + if arr[i] > arr[i+1] { + fail!(~"Array not sorted"); + } + } + } + + let rng = rand::rng(); + + for uint::range(lo, hi) |i| { + let n = 1 << i; + let arr: ~[@float] = do vec::from_fn(n) |_i| { + @rng.gen() + }; + let mut arr = arr; + + tim_sort(arr); // *sort + isSorted(arr); + + vec::reverse(arr); + tim_sort(arr); // \sort + isSorted(arr); + + tim_sort(arr); // /sort + isSorted(arr); + + for 3.times { + let i1 = rng.gen_uint_range(0, n); + let i2 = rng.gen_uint_range(0, n); + arr[i1] <-> arr[i2]; + } + tim_sort(arr); // 3sort + isSorted(arr); + + if n >= 10 { + let size = arr.len(); + let mut idx = 1; + while idx <= 10 { + arr[size-idx] = @rng.gen(); + idx += 1; + } + } + tim_sort(arr); // +sort + isSorted(arr); + + for (n/100).times { + let idx = rng.gen_uint_range(0, n); + arr[idx] = @rng.gen(); + } + tim_sort(arr); + isSorted(arr); + + let mut arr = if n > 4 { + let part = vec::slice(arr, 0, 4); + multiplyVec(part, n) + } else { arr }; + tim_sort(arr); // ~sort + isSorted(arr); + + let mut arr = vec::from_elem(n, @(-0.5)); + tim_sort(arr); // =sort + isSorted(arr); + + let half = n / 2; + let mut arr = makeRange(half).map(|i| @(*i as float)); + tim_sort(arr); // !sort + isSorted(arr); + } + } + + struct LVal<'self> { + val: uint, + key: &'self fn(@uint), + } + + #[unsafe_destructor] + impl<'self> Drop for LVal<'self> { + fn finalize(&self) { + let x = unsafe { task::local_data::local_data_get(self.key) }; + match x { + Some(@y) => { + unsafe { + task::local_data::local_data_set(self.key, @(y+1)); + } + } + _ => fail!(~"Expected key to work"), + } + } + } + + impl<'self> Ord for LVal<'self> { + fn lt<'a>(&self, other: &'a LVal<'self>) -> bool { + (*self).val < other.val + } + fn le<'a>(&self, other: &'a LVal<'self>) -> bool { + (*self).val <= other.val + } + fn gt<'a>(&self, other: &'a LVal<'self>) -> bool { + (*self).val > other.val + } + fn ge<'a>(&self, other: &'a LVal<'self>) -> bool { + (*self).val >= other.val + } + } +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: From 84861101eca12942b42f36f8adb18cfc74515431 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 09:14:47 -0400 Subject: [PATCH 13/46] rustc: print out filename/line-number when a borrow fails --- src/libcore/unstable/lang.rs | 25 ++++++++++++++----- .../middle/borrowck/gather_loans/mod.rs | 1 - src/librustc/middle/trans/datum.rs | 20 +++++++++++---- src/librustc/middle/trans/expr.rs | 2 +- src/libsyntax/parse/parser.rs | 2 +- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index cf71b01aeaeeb..c5062f25ea540 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -53,7 +53,7 @@ pub fn fail_(expr: *c_char, file: *c_char, line: size_t) -> ! { #[lang="fail_bounds_check"] pub fn fail_bounds_check(file: *c_char, line: size_t, - index: size_t, len: size_t) { + index: size_t, len: size_t) { let msg = fmt!("index out of bounds: the len is %d but the index is %d", len as int, index as int); do str::as_buf(msg) |p, _len| { @@ -61,12 +61,10 @@ pub fn fail_bounds_check(file: *c_char, line: size_t, } } -pub fn fail_borrowed() { +pub fn fail_borrowed(file: *c_char, line: size_t) { let msg = "borrowed"; do str::as_buf(msg) |msg_p, _| { - do str::as_buf("???") |file_p, _| { - fail_(msg_p as *c_char, file_p as *c_char, 0); - } + fail_(msg_p as *c_char, file, line); } } @@ -160,12 +158,27 @@ pub unsafe fn return_to_mut(a: *u8) { } } +#[cfg(stage0)] #[lang="check_not_borrowed"] #[inline(always)] pub unsafe fn check_not_borrowed(a: *u8) { let a: *mut BoxRepr = transmute(a); if ((*a).header.ref_count & FROZEN_BIT) != 0 { - fail_borrowed(); + do str::as_buf("XXX") |file_p, _| { + fail_borrowed(file_p as *c_char, 0); + } + } +} + +#[cfg(not(stage0))] +#[lang="check_not_borrowed"] +#[inline(always)] +pub unsafe fn check_not_borrowed(a: *u8, + file: *c_char, + line: size_t) { + let a: *mut BoxRepr = transmute(a); + if ((*a).header.ref_count & FROZEN_BIT) != 0 { + fail_borrowed(file, line); } } diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index ecdf260bffffa..7ac6dfd3ec385 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -26,7 +26,6 @@ use middle::ty; use util::common::indenter; use util::ppaux::{Repr}; -use core::hashmap::HashSet; use syntax::ast::{m_const, m_imm, m_mutbl}; use syntax::ast; use syntax::ast_util::id_range; diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index 705d443b1155d..03c81cad50e11 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -105,6 +105,7 @@ use util::ppaux::ty_to_str; use core::container::Set; // XXX: this should not be necessary use core::to_bytes; use syntax::ast; +use syntax::codemap::span; use syntax::parse::token::special_idents; #[deriving(Eq)] @@ -556,17 +557,24 @@ pub impl Datum { } } - fn perform_write_guard(&self, bcx: block) -> block { + fn perform_write_guard(&self, bcx: block, span: span) -> block { // Create scratch space, but do not root it. let llval = match self.mode { ByValue => self.val, ByRef => Load(bcx, self.val), }; + let loc = bcx.sess().parse_sess.cm.lookup_char_pos(span.lo); + let line = C_int(bcx.ccx(), loc.line as int); + let filename_cstr = C_cstr(bcx.ccx(), @/*bad*/copy loc.file.name); + let filename = PointerCast(bcx, filename_cstr, T_ptr(T_i8())); + callee::trans_lang_call( bcx, bcx.tcx().lang_items.check_not_borrowed_fn(), - ~[ PointerCast(bcx, llval, T_ptr(T_i8())) ], + ~[PointerCast(bcx, llval, T_ptr(T_i8())), + filename, + line], expr::Ignore) } @@ -621,6 +629,7 @@ pub impl Datum { fn try_deref(&self, bcx: block, // block wherein to generate insn's + span: span, // location where deref occurs expr_id: ast::node_id, // id of expr being deref'd derefs: uint, // number of times deref'd already is_auto: bool) // if true, only deref if auto-derefable @@ -645,7 +654,7 @@ pub impl Datum { // // (Note: write-guarded values are always boxes) let bcx = if ccx.maps.write_guard_map.contains(&key) { - self.perform_write_guard(bcx) + self.perform_write_guard(bcx, span) } else { bcx }; match ty::get(self.ty).sty { @@ -759,7 +768,7 @@ pub impl Datum { expr: @ast::expr, // the expression whose value is being deref'd derefs: uint) -> DatumBlock { - match self.try_deref(bcx, expr.id, derefs, false) { + match self.try_deref(bcx, expr.span, expr.id, derefs, false) { (Some(lvres), bcx) => DatumBlock { bcx: bcx, datum: lvres }, (None, _) => { bcx.ccx().sess.span_bug( @@ -769,6 +778,7 @@ pub impl Datum { } fn autoderef(&self, bcx: block, + span: span, expr_id: ast::node_id, max: uint) -> DatumBlock { @@ -783,7 +793,7 @@ pub impl Datum { let mut bcx = bcx; while derefs < max { derefs += 1u; - match datum.try_deref(bcx, expr_id, derefs, true) { + match datum.try_deref(bcx, span, expr_id, derefs, true) { (None, new_bcx) => { bcx = new_bcx; break } (Some(datum_deref), new_bcx) => { datum = datum_deref; diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index fa6f7802e7d50..bc44d7de98342 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -205,7 +205,7 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { if adj.autoderefs > 0 { let DatumBlock { bcx: new_bcx, datum: new_datum } = - datum.autoderef(bcx, expr.id, adj.autoderefs); + datum.autoderef(bcx, expr.span, expr.id, adj.autoderefs); datum = new_datum; bcx = new_bcx; } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 1129e7b708ea2..1ee651ede7510 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3723,7 +3723,7 @@ pub impl Parser { first_item_attrs: ~[attribute]) -> foreign_mod { let ParsedItemsAndViewItems { - attrs_remaining: attrs_remaining, + attrs_remaining: _, view_items: view_items, items: _, foreign_items: foreign_items From 4af2d90af59bb5e28e5d114d8a6004d68fad3bd5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 10:29:47 -0400 Subject: [PATCH 14/46] add an option to debug borrows (RUST_DEBUG_BORROW) so you can find out where the offending borrow occurred. This ... still needs some work. --- src/libcore/rt/env.rs | 6 ++- src/libcore/unstable/lang.rs | 78 ++++++++++++++++++++++++++--- src/librustc/middle/trans/_match.rs | 2 +- src/librustc/middle/trans/datum.rs | 14 ++++-- src/librustc/middle/trans/expr.rs | 2 +- src/rt/rust_builtin.cpp | 14 ++++++ src/rt/rust_env.cpp | 2 + src/rt/rust_env.h | 1 + src/rt/rust_task.cpp | 11 ++++ src/rt/rust_task.h | 5 ++ 10 files changed, 121 insertions(+), 14 deletions(-) diff --git a/src/libcore/rt/env.rs b/src/libcore/rt/env.rs index 92e2ec51306e2..1f52cf77868a2 100644 --- a/src/libcore/rt/env.rs +++ b/src/libcore/rt/env.rs @@ -31,8 +31,10 @@ pub struct Environment { argc: c_int, /// The argv value passed to main argv: **c_char, - /// Print GC debugging info - debug_mem: bool + /// Print GC debugging info (true if env var RUST_DEBUG_MEM is set) + debug_mem: bool, + /// Track origin of `@mut` borrows (true if env var RUST_DEBUG_BORROWS is set) + debug_borrows: bool } /// Get the global environment settings diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index c5062f25ea540..0705be7cdefe5 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -17,6 +17,7 @@ use str; use sys; use unstable::exchange_alloc; use cast::transmute; +use task::rt::rust_get_task; #[allow(non_camel_case_types)] pub type rust_task = c_void; @@ -27,7 +28,8 @@ pub static FROZEN_BIT: uint = 0x80000000; pub static FROZEN_BIT: uint = 0x8000000000000000; pub mod rustrt { - use libc::{c_char, uintptr_t}; + use unstable::lang::rust_task; + use libc::{c_void, c_char, uintptr_t}; pub extern { #[rust_stack] @@ -43,6 +45,12 @@ pub mod rustrt { #[fast_ffi] unsafe fn rust_upcall_free_noswitch(ptr: *c_char); + + #[rust_stack] + fn rust_take_task_borrow_list(task: *rust_task) -> *c_void; + + #[rust_stack] + fn rust_set_task_borrow_list(task: *rust_task, map: *c_void); } } @@ -61,10 +69,50 @@ pub fn fail_bounds_check(file: *c_char, line: size_t, } } -pub fn fail_borrowed(file: *c_char, line: size_t) { - let msg = "borrowed"; - do str::as_buf(msg) |msg_p, _| { - fail_(msg_p as *c_char, file, line); +struct BorrowRecord { + box: *mut BoxRepr, + file: *c_char, + line: size_t +} + +fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) { + unsafe { + let cur_task = rust_get_task(); + let mut borrow_list: ~[BorrowRecord] = { + let ptr = rustrt::rust_take_task_borrow_list(cur_task); + if ptr.is_null() { ~[] } else { transmute(ptr) } + }; + borrow_list = f(borrow_list); + rustrt::rust_set_task_borrow_list(cur_task, transmute(borrow_list)); + } +} + +pub fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { + if !::rt::env::get().debug_borrows { + let msg = "borrowed"; + do str::as_buf(msg) |msg_p, _| { + fail_(msg_p as *c_char, file, line); + } + } else { + do swap_task_borrow_list |borrow_list| { + let mut msg = ~"borrowed"; + let mut sep = " at "; + for borrow_list.each_reverse |entry| { + if entry.box == box { + str::push_str(&mut msg, sep); + let filename = unsafe { + str::raw::from_c_str(entry.file) + }; + str::push_str(&mut msg, filename); + str::push_str(&mut msg, fmt!(":%u", line as uint)); + sep = " and at "; + } + } + do str::as_buf(msg) |msg_p, _| { + fail_(msg_p as *c_char, file, line) + } + borrow_list + } } } @@ -140,6 +188,7 @@ pub unsafe fn local_free(ptr: *c_char) { rustrt::rust_upcall_free_noswitch(ptr); } +#[cfg(stage0)] #[lang="borrow_as_imm"] #[inline(always)] pub unsafe fn borrow_as_imm(a: *u8) { @@ -147,6 +196,21 @@ pub unsafe fn borrow_as_imm(a: *u8) { (*a).header.ref_count |= FROZEN_BIT; } +#[cfg(not(stage0))] +#[lang="borrow_as_imm"] +#[inline(always)] +pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) { + let a: *mut BoxRepr = transmute(a); + (*a).header.ref_count |= FROZEN_BIT; + if ::rt::env::get().debug_borrows { + do swap_task_borrow_list |borrow_list| { + let mut borrow_list = borrow_list; + borrow_list.push(BorrowRecord {box: a, file: file, line: line}); + borrow_list + } + } +} + #[lang="return_to_mut"] #[inline(always)] pub unsafe fn return_to_mut(a: *u8) { @@ -165,7 +229,7 @@ pub unsafe fn check_not_borrowed(a: *u8) { let a: *mut BoxRepr = transmute(a); if ((*a).header.ref_count & FROZEN_BIT) != 0 { do str::as_buf("XXX") |file_p, _| { - fail_borrowed(file_p as *c_char, 0); + fail_borrowed(a, file_p as *c_char, 0); } } } @@ -178,7 +242,7 @@ pub unsafe fn check_not_borrowed(a: *u8, line: size_t) { let a: *mut BoxRepr = transmute(a); if ((*a).header.ref_count & FROZEN_BIT) != 0 { - fail_borrowed(file, line); + fail_borrowed(a, file, line); } } diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 785bb3edc07cd..d7f9567c333d0 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -966,7 +966,7 @@ pub fn root_pats_as_necessary(bcx: block, let datum = Datum {val: val, ty: node_id_type(bcx, pat_id), mode: ByRef, source: ZeroMem}; - bcx = datum.root(bcx, root_info); + bcx = datum.root(bcx, br.pats[col].span, root_info); // If we kept going, we'd only re-root the same value, so // return now. return bcx; diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index 03c81cad50e11..ae71a3e6c22c0 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -517,7 +517,7 @@ pub impl Datum { } } - fn root(&self, bcx: block, root_info: RootInfo) -> block { + fn root(&self, bcx: block, span: span, root_info: RootInfo) -> block { /*! * * In some cases, borrowck will decide that an @T/@[]/@str @@ -542,6 +542,12 @@ pub impl Datum { // If we need to freeze the box, do that now. if root_info.freeze.is_some() { // NOTE distinguish the two kinds of freezing here + + let loc = bcx.sess().parse_sess.cm.lookup_char_pos(span.lo); + let line = C_int(bcx.ccx(), loc.line as int); + let filename_cstr = C_cstr(bcx.ccx(), @/*bad*/copy loc.file.name); + let filename = PointerCast(bcx, filename_cstr, T_ptr(T_i8())); + callee::trans_lang_call( bcx, bcx.tcx().lang_items.borrow_as_imm_fn(), @@ -549,7 +555,9 @@ pub impl Datum { Load(bcx, PointerCast(bcx, scratch.val, - T_ptr(T_ptr(T_i8())))) + T_ptr(T_ptr(T_i8())))), + filename, + line ], expr::Ignore) } else { @@ -647,7 +655,7 @@ pub impl Datum { let key = root_map_key { id: expr_id, derefs: derefs }; let bcx = match ccx.maps.root_map.find(&key) { None => bcx, - Some(&root_info) => self.root(bcx, root_info) + Some(&root_info) => self.root(bcx, span, root_info) }; // Perform the write guard, if necessary. diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index bc44d7de98342..166b8bc01f856 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -828,7 +828,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock { // at the end of the scope with id `scope_id`: let root_key = root_map_key { id: expr.id, derefs: 0u }; for bcx.ccx().maps.root_map.find(&root_key).each |&root_info| { - bcx = unrooted_datum.root(bcx, *root_info); + bcx = unrooted_datum.root(bcx, expr.span, *root_info); } return DatumBlock {bcx: bcx, datum: unrooted_datum}; diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index ee025a39ff472..197b8b36d2d4f 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -683,6 +683,20 @@ rust_task_local_data_atexit(rust_task *task, void (*cleanup_fn)(void *data)) { task->task_local_data_cleanup = cleanup_fn; } +// set/get/atexit task_borrow_list can run on the rust stack for speed. +extern "C" void * +rust_take_task_borrow_list(rust_task *task) { + void *r = task->borrow_list; + task->borrow_list = NULL; + return r; +} +extern "C" void +rust_set_task_borrow_list(rust_task *task, void *data) { + assert(task->borrow_list == NULL); + assert(data != NULL); + task->borrow_list = data; +} + extern "C" void task_clear_event_reject(rust_task *task) { task->clear_event_reject(); diff --git a/src/rt/rust_env.cpp b/src/rt/rust_env.cpp index 041b4efac52a2..e6fe35609ec93 100644 --- a/src/rt/rust_env.cpp +++ b/src/rt/rust_env.cpp @@ -24,6 +24,7 @@ #define RUST_SEED "RUST_SEED" #define RUST_POISON_ON_FREE "RUST_POISON_ON_FREE" #define RUST_DEBUG_MEM "RUST_DEBUG_MEM" +#define RUST_DEBUG_BORROWS "RUST_DEBUG_BORROWS" #if defined(__WIN32__) static int @@ -130,6 +131,7 @@ load_env(int argc, char **argv) { env->argc = argc; env->argv = argv; env->debug_mem = getenv(RUST_DEBUG_MEM) != NULL; + env->debug_borrows = getenv(RUST_DEBUG_BORROWS) != NULL; return env; } diff --git a/src/rt/rust_env.h b/src/rt/rust_env.h index df27f7674f265..322198bb031ff 100644 --- a/src/rt/rust_env.h +++ b/src/rt/rust_env.h @@ -28,6 +28,7 @@ struct rust_env { int argc; char **argv; rust_bool debug_mem; + rust_bool debug_borrows; }; rust_env* load_env(int argc, char **argv); diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index e6293aa5c1de0..ea42936f2e51d 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -42,6 +42,7 @@ rust_task::rust_task(rust_sched_loop *sched_loop, rust_task_state state, total_stack_sz(0), task_local_data(NULL), task_local_data_cleanup(NULL), + borrow_list(NULL), state(state), cond(NULL), cond_name("none"), @@ -75,6 +76,16 @@ rust_task::delete_this() assert(ref_count == 0); // || // (ref_count == 1 && this == sched->root_task)); + if (borrow_list) { + // NOTE should free borrow_list from within rust code! + // If there is a pointer in there, it is a ~[BorrowRecord] pointer, + // which are currently allocated with LIBC malloc/free. But this is + // not really the right way to do this, we should be freeing this + // pointer from Rust code. + free(borrow_list); + borrow_list = NULL; + } + sched_loop->release_task(this); } diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index 4aa1199cabc3f..dc45c0439ea12 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -241,6 +241,11 @@ rust_task : public kernel_owned void *task_local_data; void (*task_local_data_cleanup)(void *data); + // Contains a ~[BorrowRecord] pointer, or NULL. + // + // Used by borrow management code in libcore/unstable/lang.rs. + void *borrow_list; + private: // Protects state, cond, cond_name From 3159335ac308d9e3a2d3ada3b9354bf160debbdc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 10:31:10 -0400 Subject: [PATCH 15/46] avoid broken += operator, bogus use of const --- src/test/run-pass/auto-ref-slice-plus-ref.rs | 2 +- src/test/run-pass/lambda-infer-unresolved.rs | 2 +- src/test/run-pass/reflect-visit-data.rs | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/run-pass/auto-ref-slice-plus-ref.rs b/src/test/run-pass/auto-ref-slice-plus-ref.rs index 07629651e5605..58a477900c324 100644 --- a/src/test/run-pass/auto-ref-slice-plus-ref.rs +++ b/src/test/run-pass/auto-ref-slice-plus-ref.rs @@ -23,7 +23,7 @@ impl<'self> MyIter for &'self [int] { impl<'self> MyIter for &'self str { fn test_imm(&self) { assert_eq!(*self, "test") } - fn test_const(&const self) { assert_eq!(self[0], 't') } + fn test_const(&const self) { assert_eq!(self[0], 't' as u8) } } pub fn main() { diff --git a/src/test/run-pass/lambda-infer-unresolved.rs b/src/test/run-pass/lambda-infer-unresolved.rs index 2e70e90038971..4aeeda8312cac 100644 --- a/src/test/run-pass/lambda-infer-unresolved.rs +++ b/src/test/run-pass/lambda-infer-unresolved.rs @@ -17,5 +17,5 @@ struct Refs { refs: ~[int], n: int } pub fn main() { let e = @mut Refs{refs: ~[], n: 0}; let f: @fn() = || error!(copy e.n); - e.refs += ~[1]; + e.refs.push(1); } diff --git a/src/test/run-pass/reflect-visit-data.rs b/src/test/run-pass/reflect-visit-data.rs index e520d221c9935..5b01d24aa8be1 100644 --- a/src/test/run-pass/reflect-visit-data.rs +++ b/src/test/run-pass/reflect-visit-data.rs @@ -513,16 +513,16 @@ impl TyVisitor for my_visitor { fn visit_bot(&self) -> bool { true } fn visit_nil(&self) -> bool { true } fn visit_bool(&self) -> bool { - do self.get::() |b| { - self.vals += ~[bool::to_str(b)]; - }; - true + do self.get::() |b| { + self.vals.push(bool::to_str(b)); + }; + true } fn visit_int(&self) -> bool { - do self.get::() |i| { - self.vals += ~[int::to_str(i)]; - }; - true + do self.get::() |i| { + self.vals.push(int::to_str(i)); + }; + true } fn visit_i8(&self) -> bool { true } fn visit_i16(&self) -> bool { true } From 38f93f2121e111dd9dd149f5bdeaa9a34a4e42f1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 13:48:00 -0400 Subject: [PATCH 16/46] wip---work on making rooting work properly --- src/libcore/num/int-template.rs | 4 + src/libcore/num/uint-template.rs | 4 + src/libcore/unstable/lang.rs | 67 ++++++++++++++--- src/librustc/middle/lang_items.rs | 22 ++++-- src/librustc/middle/trans/base.rs | 65 ++++------------- src/librustc/middle/trans/common.rs | 52 ++++++++----- src/librustc/middle/trans/datum.rs | 73 ++++++++++++------- .../borrowck-loan-rcvr-overloaded-op.rs | 5 +- 8 files changed, 175 insertions(+), 117 deletions(-) diff --git a/src/libcore/num/int-template.rs b/src/libcore/num/int-template.rs index 85489755fc7a9..77b4eab133865 100644 --- a/src/libcore/num/int-template.rs +++ b/src/libcore/num/int-template.rs @@ -200,6 +200,8 @@ impl Mul for T { #[inline(always)] fn mul(&self, other: &T) -> T { *self * *other } } + +#[cfg(notest)] impl Quot for T { /// /// Returns the integer quotient, truncated towards 0. As this behaviour reflects @@ -222,6 +224,8 @@ impl Quot for T { #[inline(always)] fn quot(&self, other: &T) -> T { *self / *other } } + +#[cfg(notest)] impl Rem for T { /// /// Returns the integer remainder after division, satisfying: diff --git a/src/libcore/num/uint-template.rs b/src/libcore/num/uint-template.rs index f975226cde63b..2d20444946865 100644 --- a/src/libcore/num/uint-template.rs +++ b/src/libcore/num/uint-template.rs @@ -165,10 +165,14 @@ impl Mul for T { #[inline(always)] fn mul(&self, other: &T) -> T { *self * *other } } + +#[cfg(notest)] impl Quot for T { #[inline(always)] fn quot(&self, other: &T) -> T { *self / *other } } + +#[cfg(notest)] impl Rem for T { #[inline(always)] fn rem(&self, other: &T) -> T { *self % *other } diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index 0705be7cdefe5..d0e3c2b06787f 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -10,6 +10,7 @@ //! Runtime calls emitted by the compiler. +use uint; use cast::transmute; use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int, STDERR_FILENO}; use managed::raw::BoxRepr; @@ -22,10 +23,9 @@ use task::rt::rust_get_task; #[allow(non_camel_case_types)] pub type rust_task = c_void; -#[cfg(target_word_size = "32")] -pub static FROZEN_BIT: uint = 0x80000000; -#[cfg(target_word_size = "64")] -pub static FROZEN_BIT: uint = 0x8000000000000000; +pub static FROZEN_BIT: uint = 1 << (uint::bits - 1); +pub static MUT_BIT: uint = 1 << (uint::bits - 2); +static ALL_BITS: uint = FROZEN_BIT | MUT_BIT; pub mod rustrt { use unstable::lang::rust_task; @@ -196,21 +196,51 @@ pub unsafe fn borrow_as_imm(a: *u8) { (*a).header.ref_count |= FROZEN_BIT; } +fn add_borrow_to_task_list(a: *mut BoxRepr, file: *c_char, line: size_t) { + do swap_task_borrow_list |borrow_list| { + let mut borrow_list = borrow_list; + borrow_list.push(BorrowRecord {box: a, file: file, line: line}); + borrow_list + } +} + #[cfg(not(stage0))] #[lang="borrow_as_imm"] #[inline(always)] -pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) { +pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint { let a: *mut BoxRepr = transmute(a); - (*a).header.ref_count |= FROZEN_BIT; - if ::rt::env::get().debug_borrows { - do swap_task_borrow_list |borrow_list| { - let mut borrow_list = borrow_list; - borrow_list.push(BorrowRecord {box: a, file: file, line: line}); - borrow_list + + let ref_count = (*a).header.ref_count; + if (ref_count & MUT_BIT) != 0 { + fail_borrowed(a, file, line); + } else { + (*a).header.ref_count |= FROZEN_BIT; + if ::rt::env::get().debug_borrows { + add_borrow_to_list(a, file, line); } } + ref_count } +#[cfg(not(stage0))] +#[lang="borrow_as_mut"] +#[inline(always)] +pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint { + let a: *mut BoxRepr = transmute(a); + + let ref_count = (*a).header.ref_count; + if (ref_count & (MUT_BIT|FROZEN_BIT)) != 0 { + fail_borrowed(a, file, line); + } else { + (*a).header.ref_count |= (MUT_BIT|FROZEN_BIT); + if ::rt::env::get().debug_borrows { + add_borrow_to_list(a, file, line); + } + } + ref_count +} + +#[cfg(stage0)] #[lang="return_to_mut"] #[inline(always)] pub unsafe fn return_to_mut(a: *u8) { @@ -222,6 +252,21 @@ pub unsafe fn return_to_mut(a: *u8) { } } +#[cfg(not(stage0))] +#[lang="return_to_mut"] +#[inline(always)] +pub unsafe fn return_to_mut(a: *u8, old_ref_count: uint) { + // Sometimes the box is null, if it is conditionally frozen. + // See e.g. #4904. + if !a.is_null() { + let a: *mut BoxRepr = transmute(a); + + let ref_count = (*a).header.ref_count & !ALL_BITS; + let old_bits = old_ref_count & ALL_BITS; + (*a).header.ref_count = ref_count | old_bits; + } +} + #[cfg(stage0)] #[lang="check_not_borrowed"] #[inline(always)] diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index e7261d53952a2..0266b77997e7d 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -66,11 +66,12 @@ pub enum LangItem { MallocFnLangItem, // 28 FreeFnLangItem, // 29 BorrowAsImmFnLangItem, // 30 - ReturnToMutFnLangItem, // 31 - CheckNotBorrowedFnLangItem, // 32 - StrDupUniqFnLangItem, // 33 + BorrowAsMutFnLangItem, // 31 + ReturnToMutFnLangItem, // 32 + CheckNotBorrowedFnLangItem, // 33 + StrDupUniqFnLangItem, // 34 - StartFnLangItem, // 34 + StartFnLangItem, // 35 } pub struct LanguageItems { @@ -128,11 +129,12 @@ pub impl LanguageItems { 28 => "malloc", 29 => "free", 30 => "borrow_as_imm", - 31 => "return_to_mut", - 32 => "check_not_borrowed", - 33 => "strdup_uniq", + 31 => "borrow_as_mut", + 32 => "return_to_mut", + 33 => "check_not_borrowed", + 34 => "strdup_uniq", - 34 => "start", + 35 => "start", _ => "???" } @@ -237,6 +239,9 @@ pub impl LanguageItems { pub fn borrow_as_imm_fn(&const self) -> def_id { self.items[BorrowAsImmFnLangItem as uint].get() } + pub fn borrow_as_mut_fn(&const self) -> def_id { + self.items[BorrowAsMutFnLangItem as uint].get() + } pub fn return_to_mut_fn(&const self) -> def_id { self.items[ReturnToMutFnLangItem as uint].get() } @@ -292,6 +297,7 @@ fn LanguageItemCollector(crate: @crate, item_refs.insert(@~"malloc", MallocFnLangItem as uint); item_refs.insert(@~"free", FreeFnLangItem as uint); item_refs.insert(@~"borrow_as_imm", BorrowAsImmFnLangItem as uint); + item_refs.insert(@~"borrow_as_mut", BorrowAsMutFnLangItem as uint); item_refs.insert(@~"return_to_mut", ReturnToMutFnLangItem as uint); item_refs.insert(@~"check_not_borrowed", CheckNotBorrowedFnLangItem as uint); diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 1785feda7790c..7738e97779971 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -991,63 +991,30 @@ pub fn get_landing_pad(bcx: block) -> BasicBlockRef { return pad_bcx.llbb; } -// Arranges for the value found in `*root_loc` to be dropped once the scope -// associated with `scope_id` exits. This is used to keep boxes live when -// there are extant region pointers pointing at the interior. -// -// Note that `root_loc` is not the value itself but rather a pointer to the -// value. Generally it in alloca'd value. The reason for this is that the -// value is initialized in an inner block but may be freed in some outer -// block, so an SSA value that is valid in the inner block may not be valid in -// the outer block. In fact, the inner block may not even execute. Rather -// than generate the full SSA form, we just use an alloca'd value. -pub fn add_root_cleanup(bcx: block, - root_info: RootInfo, - root_loc: ValueRef, - ty: ty::t) { - - debug!("add_root_cleanup(bcx=%s, \ - scope=%d, \ - freezes=%?, \ - root_loc=%s, \ - ty=%s)", - bcx.to_str(), - root_info.scope, - root_info.freeze, - val_str(bcx.ccx().tn, root_loc), - ppaux::ty_to_str(bcx.ccx().tcx, ty)); - - let bcx_scope = find_bcx_for_scope(bcx, root_info.scope); - if root_info.freeze.is_some() { - add_clean_frozen_root(bcx_scope, root_loc, ty); - } else { - add_clean_temp_mem(bcx_scope, root_loc, ty); - } - - fn find_bcx_for_scope(bcx: block, scope_id: ast::node_id) -> block { - let mut bcx_sid = bcx; - loop { - bcx_sid = match bcx_sid.node_info { - Some(NodeInfo { id, _ }) if id == scope_id => { +pub fn find_bcx_for_scope(bcx: block, scope_id: ast::node_id) -> block { + let mut bcx_sid = bcx; + loop { + bcx_sid = match bcx_sid.node_info { + Some(NodeInfo { id, _ }) if id == scope_id => { return bcx_sid } - // NOTE This is messier than it ought to be and not really right - Some(NodeInfo { callee_id: Some(id), _ }) if id == scope_id => { - return bcx_sid - } + // NOTE This is messier than it ought to be and not really right + Some(NodeInfo { callee_id: Some(id), _ }) if id == scope_id => { + return bcx_sid + } - _ => { - match bcx_sid.parent { - None => bcx.tcx().sess.bug( - fmt!("no enclosing scope with id %d", scope_id)), - Some(bcx_par) => bcx_par + _ => { + match bcx_sid.parent { + None => bcx.tcx().sess.bug( + fmt!("no enclosing scope with id %d", scope_id)), + Some(bcx_par) => bcx_par + } } - } } } } -} + pub fn do_spill(bcx: block, v: ValueRef, t: ty::t) -> ValueRef { if ty::type_is_bot(t) { diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 2ebd696dbfdef..d0da8a2e1ee74 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -467,28 +467,40 @@ pub fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) { scope_clean_changed(scope_info); } } -pub fn add_clean_frozen_root(bcx: block, val: ValueRef, t: ty::t) { - debug!("add_clean_frozen_root(%s, %s, %s)", - bcx.to_str(), val_str(bcx.ccx().tn, val), - t.repr(bcx.tcx())); - let (root, rooted) = root_for_cleanup(bcx, val, t); - let cleanup_type = cleanup_type(bcx.tcx(), t); +pub fn add_clean_return_to_mut(bcx: block, + frozen_val_ref: ValueRef, + bits_val_ref: ValueRef) { + //! When an `@mut` has been frozen, we have to + //! call the lang-item `return_to_mut` when the + //! freeze goes out of scope. We need to pass + //! in both the value which was frozen (`frozen_val`) and + //! the value (`bits_val_ref`) which was returned when the + //! box was frozen initially. Here, both `frozen_val_ref` and + //! `bits_val_ref` are in fact pointers to stack slots. + + debug!("add_clean_return_to_mut(%s, %s, %s)", + bcx.to_str(), + val_str(bcx.ccx().tn, frozen_val_ref), + val_str(bcx.ccx().tn, bits_val_ref)); do in_scope_cx(bcx) |scope_info| { scope_info.cleanups.push( - clean_temp(val, |bcx| { - let bcx = callee::trans_lang_call( - bcx, - bcx.tcx().lang_items.return_to_mut_fn(), - ~[ - build::Load(bcx, - build::PointerCast(bcx, - root, - T_ptr(T_ptr(T_i8())))) - ], - expr::Ignore - ); - glue::drop_ty_root(bcx, root, rooted, t) - }, cleanup_type)); + clean_temp( + frozen_val_ref, + |bcx| { + callee::trans_lang_call( + bcx, + bcx.tcx().lang_items.return_to_mut_fn(), + ~[ + build::Load(bcx, + build::PointerCast(bcx, + frozen_val_ref, + T_ptr(T_ptr(T_i8())))), + build::Load(bcx, bits_val_ref) + ], + expr::Ignore + ) + }, + normal_exit_only)); scope_clean_changed(scope_info); } } diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index ae71a3e6c22c0..94bff65843b24 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -87,7 +87,7 @@ use lib; use lib::llvm::ValueRef; -use middle::borrowck::{RootInfo, root_map_key}; +use middle::borrowck::{RootInfo, root_map_key, DynaImm, DynaMut}; use middle::trans::adt; use middle::trans::base::*; use middle::trans::build::*; @@ -517,7 +517,7 @@ pub impl Datum { } } - fn root(&self, bcx: block, span: span, root_info: RootInfo) -> block { + fn root(&self, mut bcx: block, span: span, root_info: RootInfo) -> block { /*! * * In some cases, borrowck will decide that an @T/@[]/@str @@ -535,34 +535,53 @@ pub impl Datum { root_info.scope)); } + // First, root the datum. Note that we must zero this value, + // because sometimes we root on one path but not another. + // See e.g. #4904. let scratch = scratch_datum(bcx, self.ty, true); self.copy_to_datum(bcx, INIT, scratch); - add_root_cleanup(bcx, root_info, scratch.val, scratch.ty); - - // If we need to freeze the box, do that now. - if root_info.freeze.is_some() { - // NOTE distinguish the two kinds of freezing here - - let loc = bcx.sess().parse_sess.cm.lookup_char_pos(span.lo); - let line = C_int(bcx.ccx(), loc.line as int); - let filename_cstr = C_cstr(bcx.ccx(), @/*bad*/copy loc.file.name); - let filename = PointerCast(bcx, filename_cstr, T_ptr(T_i8())); - - callee::trans_lang_call( - bcx, - bcx.tcx().lang_items.borrow_as_imm_fn(), - ~[ - Load(bcx, - PointerCast(bcx, - scratch.val, - T_ptr(T_ptr(T_i8())))), - filename, - line - ], - expr::Ignore) - } else { - bcx + let cleanup_bcx = find_bcx_for_scope(bcx, root_info.scope); + add_clean_temp_mem(cleanup_bcx, scratch.val, scratch.ty); + + // Now, consider also freezing it. + match root_info.freeze { + None => {} + Some(freeze_kind) => { + let loc = bcx.sess().parse_sess.cm.lookup_char_pos(span.lo); + let line = C_int(bcx.ccx(), loc.line as int); + let filename_cstr = C_cstr(bcx.ccx(), @/*bad*/copy loc.file.name); + let filename = PointerCast(bcx, filename_cstr, T_ptr(T_i8())); + + // in this case, we don't have to zero, because + // scratch.val will be NULL should the cleanup get + // called without the freezing actually occurring, and + // return_to_mut checks for this condition. + let scratch_bits = scratch_datum(bcx, ty::mk_uint(), false); + + let freeze_did = match freeze_kind { + DynaImm => bcx.tcx().lang_items.borrow_as_imm_fn(), + DynaMut => bcx.tcx().lang_items.borrow_as_mut_fn(), + }; + + bcx = callee::trans_lang_call( + bcx, + freeze_did, + ~[ + Load(bcx, + PointerCast(bcx, + scratch.val, + T_ptr(T_ptr(T_i8())))), + filename, + line + ], + expr::SaveIn(scratch_bits.val)); + + add_clean_return_to_mut( + cleanup_bcx, scratch.val, scratch_bits.val); + } } + + bcx } fn perform_write_guard(&self, bcx: block, span: span) -> block { diff --git a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs index 8c9d3009e5e67..2c4ae242fb483 100644 --- a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs +++ b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs @@ -40,10 +40,11 @@ fn b() { let q = &mut p; - p + 3; // ok for pure fns + p + 3; //~ ERROR cannot borrow `p` p.times(3); //~ ERROR cannot borrow `p` - q.x += 1; + *q + 3; // OK to use the new alias `q` + q.x += 1; // and OK to mutate it } fn c() { From 14bf5c4fe7eb110fc124a710b40bc7c5a7801e25 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 20:19:28 -0400 Subject: [PATCH 17/46] rustc: adjust total number of lang items --- src/librustc/middle/lang_items.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 0266b77997e7d..314b58d13e147 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -75,13 +75,13 @@ pub enum LangItem { } pub struct LanguageItems { - items: [Option, ..35] + items: [Option, ..36] } pub impl LanguageItems { pub fn new() -> LanguageItems { LanguageItems { - items: [ None, ..35 ] + items: [ None, ..36 ] } } From ef6b24d1350ad658faee68f7eddd2c05a56900ce Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 20:22:08 -0400 Subject: [PATCH 18/46] rustc: fix the fact that trans_lvalue rooted twice --- src/librustc/middle/trans/_match.rs | 2 +- src/librustc/middle/trans/datum.rs | 9 ++-- src/librustc/middle/trans/expr.rs | 73 +++++++++-------------------- 3 files changed, 29 insertions(+), 55 deletions(-) diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index d7f9567c333d0..be39edd2d9b78 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -966,7 +966,7 @@ pub fn root_pats_as_necessary(bcx: block, let datum = Datum {val: val, ty: node_id_type(bcx, pat_id), mode: ByRef, source: ZeroMem}; - bcx = datum.root(bcx, br.pats[col].span, root_info); + bcx = datum.root(bcx, br.pats[col].span, key, root_info); // If we kept going, we'd only re-root the same value, so // return now. return bcx; diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index 94bff65843b24..f4fb3b6c6f5d5 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -517,7 +517,8 @@ pub impl Datum { } } - fn root(&self, mut bcx: block, span: span, root_info: RootInfo) -> block { + fn root(&self, mut bcx: block, span: span, + root_key: root_map_key, root_info: RootInfo) -> block { /*! * * In some cases, borrowck will decide that an @T/@[]/@str @@ -525,8 +526,8 @@ pub impl Datum { * case, we will call this function, which will stash a copy * away until we exit the scope `scope_id`. */ - debug!("root(root_info=%?, self=%?)", - root_info, self.to_str(bcx.ccx())); + debug!("root(root_map_key=%?, root_info=%?, self=%?)", + root_key, root_info, self.to_str(bcx.ccx())); if bcx.sess().trace() { trans_trace( @@ -674,7 +675,7 @@ pub impl Datum { let key = root_map_key { id: expr_id, derefs: derefs }; let bcx = match ccx.maps.root_map.find(&key) { None => bcx, - Some(&root_info) => self.root(bcx, span, root_info) + Some(&root_info) => self.root(bcx, span, key, root_info) }; // Perform the write guard, if necessary. diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 166b8bc01f856..a993f781cdb18 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -821,57 +821,30 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock { trace_span!(bcx, expr.span, @shorten(bcx.expr_to_str(expr))); - let unrooted_datum = unpack_datum!(bcx, unrooted(bcx, expr)); - - // If the lvalue must remain rooted, create a scratch datum, copy - // the lvalue in there, and then arrange for it to be cleaned up - // at the end of the scope with id `scope_id`: - let root_key = root_map_key { id: expr.id, derefs: 0u }; - for bcx.ccx().maps.root_map.find(&root_key).each |&root_info| { - bcx = unrooted_datum.root(bcx, expr.span, *root_info); - } - - return DatumBlock {bcx: bcx, datum: unrooted_datum}; - - fn unrooted(bcx: block, expr: @ast::expr) -> DatumBlock { - /*! - * - * Translates `expr`. Note that this version generally - * yields an unrooted, unmoved version. Rooting and possible - * moves are dealt with above in trans_lvalue_unadjusted(). - * - * One exception is if `expr` refers to a local variable, - * in which case the source may already be FromMovedLvalue - * if appropriate. - */ - - let mut bcx = bcx; - - match expr.node { - ast::expr_paren(e) => { - return unrooted(bcx, e); - } - ast::expr_path(_) => { - return trans_def_lvalue(bcx, expr, bcx.def(expr.id)); - } - ast::expr_field(base, ident, _) => { - return trans_rec_field(bcx, base, ident); - } - ast::expr_index(base, idx) => { - return trans_index(bcx, expr, base, idx); - } - ast::expr_unary(ast::deref, base) => { - let basedatum = unpack_datum!(bcx, trans_to_datum(bcx, base)); - return basedatum.deref(bcx, base, 0); - } - _ => { - bcx.tcx().sess.span_bug( - expr.span, - fmt!("trans_lvalue reached fall-through case: %?", - expr.node)); - } + return match expr.node { + ast::expr_paren(e) => { + unrooted(bcx, e) } - } + ast::expr_path(_) => { + trans_def_lvalue(bcx, expr, bcx.def(expr.id)) + } + ast::expr_field(base, ident, _) => { + trans_rec_field(bcx, base, ident) + } + ast::expr_index(base, idx) => { + trans_index(bcx, expr, base, idx) + } + ast::expr_unary(ast::deref, base) => { + let basedatum = unpack_datum!(bcx, trans_to_datum(bcx, base)); + basedatum.deref(bcx, base, 0) + } + _ => { + bcx.tcx().sess.span_bug( + expr.span, + fmt!("trans_lvalue reached fall-through case: %?", + expr.node)); + } + }; fn trans_rec_field(bcx: block, base: @ast::expr, From d231c427e655b7164571a1a712563ba5fd2e4a3c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 20:23:07 -0400 Subject: [PATCH 19/46] core: add more debugging printouts to borrowing --- src/libcore/unstable/lang.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index d0e3c2b06787f..283f232964963 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -51,6 +51,8 @@ pub mod rustrt { #[rust_stack] fn rust_set_task_borrow_list(task: *rust_task, map: *c_void); + + fn rust_dbg_breakpoint(); } } @@ -88,6 +90,8 @@ fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) { } pub fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { + debug_ptr("fail_borrowed: ", box); + if !::rt::env::get().debug_borrows { let msg = "borrowed"; do str::as_buf(msg) |msg_p, _| { @@ -130,7 +134,7 @@ pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char { static ENABLE_DEBUG_PTR: bool = false; #[inline] -pub fn debug_ptr(tag: &'static str, p: *T) { +pub fn debug_ptr(tag: &'static str, p: *const T) { //! A useful debugging function that prints a pointer + tag + newline //! without allocating memory. @@ -138,7 +142,7 @@ pub fn debug_ptr(tag: &'static str, p: *T) { debug_ptr_slow(tag, p); } - fn debug_ptr_slow(tag: &'static str, p: *T) { + fn debug_ptr_slow(tag: &'static str, p: *const T) { use io; let dbg = STDERR_FILENO as io::fd_t; let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', @@ -209,14 +213,17 @@ fn add_borrow_to_task_list(a: *mut BoxRepr, file: *c_char, line: size_t) { #[inline(always)] pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint { let a: *mut BoxRepr = transmute(a); - let ref_count = (*a).header.ref_count; + + debug_ptr("borrow_as_imm (ptr): ", a); + debug_ptr("borrow_as_imm (ref): ", ref_count as *()); + if (ref_count & MUT_BIT) != 0 { fail_borrowed(a, file, line); } else { (*a).header.ref_count |= FROZEN_BIT; if ::rt::env::get().debug_borrows { - add_borrow_to_list(a, file, line); + add_borrow_to_task_list(a, file, line); } } ref_count @@ -228,13 +235,16 @@ pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint { pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint { let a: *mut BoxRepr = transmute(a); + debug_ptr("borrow_as_mut (ptr): ", a); + debug_ptr("borrow_as_mut (line): ", line as *()); + let ref_count = (*a).header.ref_count; if (ref_count & (MUT_BIT|FROZEN_BIT)) != 0 { fail_borrowed(a, file, line); } else { (*a).header.ref_count |= (MUT_BIT|FROZEN_BIT); if ::rt::env::get().debug_borrows { - add_borrow_to_list(a, file, line); + add_borrow_to_task_list(a, file, line); } } ref_count @@ -261,6 +271,9 @@ pub unsafe fn return_to_mut(a: *u8, old_ref_count: uint) { if !a.is_null() { let a: *mut BoxRepr = transmute(a); + debug_ptr("return_to_mut (ptr): ", a); + debug_ptr("return_to_mut (ref): ", old_ref_count as *()); + let ref_count = (*a).header.ref_count & !ALL_BITS; let old_bits = old_ref_count & ALL_BITS; (*a).header.ref_count = ref_count | old_bits; From 6210de9529f37bb4ff51050bca19dfd6f28026c9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 21:46:34 -0400 Subject: [PATCH 20/46] lang: fix code for maintaining borrow list --- src/libcore/unstable/lang.rs | 50 +++++++++++++++++++++++------ src/librustc/middle/trans/common.rs | 8 +++-- src/librustc/middle/trans/datum.rs | 3 +- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index 283f232964963..017fc4b7b63a0 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -19,6 +19,7 @@ use sys; use unstable::exchange_alloc; use cast::transmute; use task::rt::rust_get_task; +use option::{Some, None}; #[allow(non_camel_case_types)] pub type rust_task = c_void; @@ -71,6 +72,7 @@ pub fn fail_bounds_check(file: *c_char, line: size_t, } } +#[deriving(Eq)] struct BorrowRecord { box: *mut BoxRepr, file: *c_char, @@ -108,7 +110,7 @@ pub fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { str::raw::from_c_str(entry.file) }; str::push_str(&mut msg, filename); - str::push_str(&mut msg, fmt!(":%u", line as uint)); + str::push_str(&mut msg, fmt!(":%u", entry.line as uint)); sep = " and at "; } } @@ -208,6 +210,26 @@ fn add_borrow_to_task_list(a: *mut BoxRepr, file: *c_char, line: size_t) { } } +fn remove_borrow_from_task_list(a: *mut BoxRepr, file: *c_char, line: size_t) { + do swap_task_borrow_list |borrow_list| { + let mut borrow_list = borrow_list; + let br = BorrowRecord {box: a, file: file, line: line}; + match borrow_list.rposition_elem(&br) { + Some(idx) => { + borrow_list.remove(idx); + borrow_list + } + None => { + let err = fmt!("no borrow found, br=%?, borrow_list=%?", + br, borrow_list); + do str::as_buf(err) |msg_p, _| { + fail_(msg_p as *c_char, file, line) + } + } + } + } +} + #[cfg(not(stage0))] #[lang="borrow_as_imm"] #[inline(always)] @@ -215,8 +237,9 @@ pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint { let a: *mut BoxRepr = transmute(a); let ref_count = (*a).header.ref_count; - debug_ptr("borrow_as_imm (ptr): ", a); - debug_ptr("borrow_as_imm (ref): ", ref_count as *()); + debug_ptr("borrow_as_imm (ptr) :", a); + debug_ptr(" (ref) :", ref_count as *()); + debug_ptr(" (line): ", line as *()); if (ref_count & MUT_BIT) != 0 { fail_borrowed(a, file, line); @@ -236,7 +259,7 @@ pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint { let a: *mut BoxRepr = transmute(a); debug_ptr("borrow_as_mut (ptr): ", a); - debug_ptr("borrow_as_mut (line): ", line as *()); + debug_ptr(" (line): ", line as *()); let ref_count = (*a).header.ref_count; if (ref_count & (MUT_BIT|FROZEN_BIT)) != 0 { @@ -265,18 +288,25 @@ pub unsafe fn return_to_mut(a: *u8) { #[cfg(not(stage0))] #[lang="return_to_mut"] #[inline(always)] -pub unsafe fn return_to_mut(a: *u8, old_ref_count: uint) { +pub unsafe fn return_to_mut(a: *u8, old_ref_count: uint, + file: *c_char, line: size_t) { // Sometimes the box is null, if it is conditionally frozen. // See e.g. #4904. if !a.is_null() { let a: *mut BoxRepr = transmute(a); + let ref_count = (*a).header.ref_count; + let combined = (ref_count & !ALL_BITS) | (old_ref_count & ALL_BITS); + (*a).header.ref_count = combined; - debug_ptr("return_to_mut (ptr): ", a); - debug_ptr("return_to_mut (ref): ", old_ref_count as *()); + debug_ptr("return_to_mut (ptr) : ", a); + debug_ptr(" (line): ", line as *()); + debug_ptr(" (old) : ", old_ref_count as *()); + debug_ptr(" (new) : ", ref_count as *()); + debug_ptr(" (comb): ", combined as *()); - let ref_count = (*a).header.ref_count & !ALL_BITS; - let old_bits = old_ref_count & ALL_BITS; - (*a).header.ref_count = ref_count | old_bits; + if ::rt::env::get().debug_borrows { + remove_borrow_from_task_list(a, file, line); + } } } diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index d0da8a2e1ee74..dba9ddd2b1d1a 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -469,7 +469,9 @@ pub fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) { } pub fn add_clean_return_to_mut(bcx: block, frozen_val_ref: ValueRef, - bits_val_ref: ValueRef) { + bits_val_ref: ValueRef, + filename_val: ValueRef, + line_val: ValueRef) { //! When an `@mut` has been frozen, we have to //! call the lang-item `return_to_mut` when the //! freeze goes out of scope. We need to pass @@ -495,7 +497,9 @@ pub fn add_clean_return_to_mut(bcx: block, build::PointerCast(bcx, frozen_val_ref, T_ptr(T_ptr(T_i8())))), - build::Load(bcx, bits_val_ref) + build::Load(bcx, bits_val_ref), + filename_val, + line_val ], expr::Ignore ) diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index f4fb3b6c6f5d5..d6df6c87dec43 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -578,7 +578,8 @@ pub impl Datum { expr::SaveIn(scratch_bits.val)); add_clean_return_to_mut( - cleanup_bcx, scratch.val, scratch_bits.val); + cleanup_bcx, scratch.val, scratch_bits.val, + filename, line); } } From fbaf8399c8d4670b16684a7d94c8ed70839ffc8e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 21:50:09 -0400 Subject: [PATCH 21/46] rustc: more fix for trans_lvalue rooted twice --- src/librustc/middle/trans/expr.rs | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index a993f781cdb18..30c541c0c055b 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -823,7 +823,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock { return match expr.node { ast::expr_paren(e) => { - unrooted(bcx, e) + trans_lvalue_unadjusted(bcx, e) } ast::expr_path(_) => { trans_def_lvalue(bcx, expr, bcx.def(expr.id)) @@ -849,12 +849,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock { fn trans_rec_field(bcx: block, base: @ast::expr, field: ast::ident) -> DatumBlock { - /*! - * - * Translates `base.field`. Note that this version always - * yields an unrooted, unmoved version. Rooting and possible - * moves are dealt with above in trans_lvalue_unadjusted(). - */ + //! Translates `base.field`. let mut bcx = bcx; let _icx = bcx.insn_ctxt("trans_rec_field"); @@ -878,12 +873,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock { index_expr: @ast::expr, base: @ast::expr, idx: @ast::expr) -> DatumBlock { - /*! - * - * Translates `base[idx]`. Note that this version always - * yields an unrooted, unmoved version. Rooting and possible - * moves are dealt with above in trans_lvalue_unadjusted(). - */ + //! Translates `base[idx]`. let _icx = bcx.insn_ctxt("trans_index"); let ccx = bcx.ccx(); @@ -946,14 +936,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock { def: ast::def) -> DatumBlock { - /*! - * - * Translates a reference to a path. Note that this version - * generally yields an unrooted, unmoved version. Rooting and - * possible moves are dealt with above in - * trans_lvalue_unadjusted(), with the caveat that local variables - * may already be in move mode. - */ + //! Translates a reference to a path. let _icx = bcx.insn_ctxt("trans_def_lvalue"); let ccx = bcx.ccx(); From 5f886342be75ef465a8e0ffe6f4e7c237fb8fd67 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 May 2013 21:50:32 -0400 Subject: [PATCH 22/46] syntax: fix up dynamic borrow errors in libsyntax --- src/libsyntax/parse/lexer.rs | 4 ++-- src/libsyntax/parse/parser.rs | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs index 60d6ce504fd9a..163c7852132a5 100644 --- a/src/libsyntax/parse/lexer.rs +++ b/src/libsyntax/parse/lexer.rs @@ -163,7 +163,7 @@ fn string_advance_token(r: @mut StringReader) { } } -fn byte_offset(rdr: @mut StringReader) -> BytePos { +fn byte_offset(rdr: &StringReader) -> BytePos { (rdr.pos - rdr.filemap.start_pos) } @@ -176,7 +176,7 @@ pub fn get_str_from(rdr: @mut StringReader, start: BytePos) -> ~str { // EFFECT: advance the StringReader by one character. If a newline is // discovered, add it to the FileMap's list of line start offsets. -pub fn bump(rdr: @mut StringReader) { +pub fn bump(rdr: &mut StringReader) { rdr.last_pos = rdr.pos; let current_byte_offset = byte_offset(rdr).to_uint();; if current_byte_offset < (*rdr.src).len() { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 1ee651ede7510..457c6df8db254 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1607,9 +1607,9 @@ pub impl Parser { token::LBRACE | token::LPAREN | token::LBRACKET => { self.parse_matcher_subseq( name_idx, - &*self.token, + *self.token, // tjc: not sure why we need a copy - &token::flip_delimiter(&*self.token) + token::flip_delimiter(&*self.token) ) } _ => self.fatal(~"expected open delimiter") @@ -1623,15 +1623,15 @@ pub impl Parser { fn parse_matcher_subseq( &self, name_idx: @mut uint, - bra: &token::Token, - ket: &token::Token + bra: token::Token, + ket: token::Token ) -> ~[matcher] { let mut ret_val = ~[]; let mut lparens = 0u; - self.expect(bra); + self.expect(&bra); - while *self.token != *ket || lparens > 0u { + while *self.token != ket || lparens > 0u { if *self.token == token::LPAREN { lparens += 1u; } if *self.token == token::RPAREN { lparens -= 1u; } ret_val.push(self.parse_matcher(name_idx)); @@ -1651,8 +1651,8 @@ pub impl Parser { let name_idx_lo = *name_idx; let ms = self.parse_matcher_subseq( name_idx, - &token::LPAREN, - &token::RPAREN + token::LPAREN, + token::RPAREN ); if ms.len() == 0u { self.fatal(~"repetition body must be nonempty"); From 88ec89d3fe42029dd6005822191dc97de07d930c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 2 May 2013 14:32:37 -0400 Subject: [PATCH 23/46] fix numerous dynamic borrow failures --- src/libcore/unstable/lang.rs | 2 +- src/libcore/util.rs | 19 +++++++------ src/librustc/middle/liveness.rs | 18 ++++++------ src/librustc/middle/region.rs | 3 +- src/librustc/middle/resolve.rs | 32 ++++++++++------------ src/librustc/middle/typeck/check/vtable.rs | 7 +++-- src/librustc/middle/typeck/coherence.rs | 23 ++++++++-------- src/librustc/middle/typeck/infer/glb.rs | 4 ++- src/librustc/middle/typeck/infer/lub.rs | 4 ++- src/librustc/middle/typeck/infer/mod.rs | 25 ++++++++--------- src/librustc/rustc.rc | 3 ++ src/libsyntax/codemap.rs | 2 +- 12 files changed, 77 insertions(+), 65 deletions(-) diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index 017fc4b7b63a0..d25147fcde118 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -133,7 +133,7 @@ pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char { /// Because this code is so perf. sensitive, use a static constant so that /// debug printouts are compiled out most of the time. -static ENABLE_DEBUG_PTR: bool = false; +static ENABLE_DEBUG_PTR: bool = true; #[inline] pub fn debug_ptr(tag: &'static str, p: *const T) { diff --git a/src/libcore/util.rs b/src/libcore/util.rs index a08e38c021fad..43616ebfd3032 100644 --- a/src/libcore/util.rs +++ b/src/libcore/util.rs @@ -26,19 +26,20 @@ pub fn ignore(_x: T) { } /// Sets `*ptr` to `new_value`, invokes `op()`, and then restores the /// original value of `*ptr`. +/// +/// NB: This function accepts `@mut T` and not `&mut T` to avoid +/// an obvious borrowck hazard. Typically passing in `&mut T` will +/// cause borrow check errors because it freezes whatever location +/// that `&mut T` is stored in (either statically or dynamically). #[inline(always)] -pub fn with( - ptr: &mut T, - new_value: T, +pub fn with( + ptr: @mut T, + mut value: T, op: &fn() -> R) -> R { - // NDM: if swap operator were defined somewhat differently, - // we wouldn't need to copy... - - let old_value = *ptr; - *ptr = new_value; + value <-> *ptr; let result = op(); - *ptr = old_value; + *ptr = value; return result; } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 2e2c92abcdc78..59a6e6469e2bb 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -112,7 +112,6 @@ use util::ppaux::ty_to_str; use core::cast::transmute; use core::hashmap::HashMap; -use core::util::with; use syntax::ast::*; use syntax::codemap::span; use syntax::parse::token::special_idents; @@ -343,9 +342,10 @@ pub impl IrMaps { } fn visit_item(item: @item, self: @mut IrMaps, v: vt<@mut IrMaps>) { - do with(&mut self.cur_item, item.id) { - visit::visit_item(item, self, v) - } + let old_cur_item = self.cur_item; + self.cur_item = item.id; + visit::visit_item(item, self, v); + self.cur_item = old_cur_item; } fn visit_fn(fk: &visit::fn_kind, @@ -762,11 +762,13 @@ pub impl Liveness { None => { // Vanilla 'break' or 'loop', so use the enclosing // loop scope - let loop_scope = &mut *self.loop_scope; - if loop_scope.len() == 0 { + let len = { // FIXME(#5074) stage0 + let loop_scope = &mut *self.loop_scope; + loop_scope.len() + }; + if len == 0 { self.tcx.sess.span_bug(sp, ~"break outside loop"); - } - else { + } else { // FIXME(#5275): this shouldn't have to be a method... self.last_loop_scope() } diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index ea21ab0527b4d..06eb2542235e4 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -949,7 +949,8 @@ pub fn determine_rp_in_crate(sess: Session, let cx = &mut *cx; while cx.worklist.len() != 0 { let c_id = cx.worklist.pop(); - let c_variance = *cx.region_paramd_items.get(&c_id); + let c_variance = { *cx.region_paramd_items.get(&c_id) }; + // NOTE cleanup scopes cause an exaggerated lock here debug!("popped %d from worklist", c_id); match cx.dep_map.find(&c_id) { None => {} diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index ff46abaf7128c..8e83ea7e32e3b 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -779,9 +779,9 @@ pub fn Resolver(session: Session, unresolved_imports: 0, current_module: current_module, - value_ribs: ~[], - type_ribs: ~[], - label_ribs: ~[], + value_ribs: @mut ~[], + type_ribs: @mut ~[], + label_ribs: @mut ~[], xray_context: NoXray, current_trait_refs: None, @@ -830,13 +830,13 @@ pub struct Resolver { // The current set of local scopes, for values. // FIXME #4948: Reuse ribs to avoid allocation. - value_ribs: ~[@Rib], + value_ribs: @mut ~[@Rib], // The current set of local scopes, for types. - type_ribs: ~[@Rib], + type_ribs: @mut ~[@Rib], // The current set of local scopes, for labels. - label_ribs: ~[@Rib], + label_ribs: @mut ~[@Rib], // Whether the current context is an X-ray context. An X-ray context is // allowed to access private names of any module. @@ -4313,19 +4313,18 @@ pub impl Resolver { } pat_struct(path, _, _) => { - let structs: &mut HashSet = &mut self.structs; match self.resolve_path(path, TypeNS, false, visitor) { Some(def_ty(class_id)) - if structs.contains(&class_id) => { + if self.structs.contains(&class_id) => { let class_def = def_struct(class_id); self.record_def(pattern.id, class_def); } - Some(definition @ def_struct(class_id)) - if structs.contains(&class_id) => { + Some(definition @ def_struct(class_id)) => { + assert!(self.structs.contains(&class_id)); self.record_def(pattern.id, definition); } Some(definition @ def_variant(_, variant_id)) - if structs.contains(&variant_id) => { + if self.structs.contains(&variant_id) => { self.record_def(pattern.id, definition); } result => { @@ -4627,12 +4626,12 @@ pub impl Resolver { let search_result; match namespace { ValueNS => { - search_result = self.search_ribs(&mut self.value_ribs, ident, + search_result = self.search_ribs(self.value_ribs, ident, span, DontAllowCapturingSelf); } TypeNS => { - search_result = self.search_ribs(&mut self.type_ribs, ident, + search_result = self.search_ribs(self.type_ribs, ident, span, AllowCapturingSelf); } } @@ -4822,15 +4821,14 @@ pub impl Resolver { expr_struct(path, _, _) => { // Resolve the path to the structure it goes to. - let structs: &mut HashSet = &mut self.structs; match self.resolve_path(path, TypeNS, false, visitor) { Some(def_ty(class_id)) | Some(def_struct(class_id)) - if structs.contains(&class_id) => { + if self.structs.contains(&class_id) => { let class_def = def_struct(class_id); self.record_def(expr.id, class_def); } Some(definition @ def_variant(_, class_id)) - if structs.contains(&class_id) => { + if self.structs.contains(&class_id) => { self.record_def(expr.id, definition); } _ => { @@ -4856,7 +4854,7 @@ pub impl Resolver { } expr_break(Some(label)) | expr_again(Some(label)) => { - match self.search_ribs(&mut self.label_ribs, label, expr.span, + match self.search_ribs(self.label_ribs, label, expr.span, DontAllowCapturingSelf) { None => self.session.span_err(expr.span, diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 44b6212261246..c177d5ab0eb3a 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -244,11 +244,14 @@ fn lookup_vtable(vcx: &VtableContext, // Nothing found. Continue. } Some(implementations) => { - let implementations: &mut ~[@Impl] = *implementations; + let len = { // FIXME(#5074): stage0 requires it + let implementations: &mut ~[@Impl] = *implementations; + implementations.len() + }; // implementations is the list of all impls in scope for // trait_ref. (Usually, there's just one.) - for uint::range(0, implementations.len()) |i| { + for uint::range(0, len) |i| { let im = implementations[i]; // im is one specific impl of trait_ref. diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index 573e4bd579011..d779c20b3e81c 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -240,8 +240,8 @@ pub impl CoherenceChecker { fn check_implementation(&self, item: @item, associated_traits: ~[@trait_ref]) { - let self_type = self.crate_context.tcx.tcache.get( - &local_def(item.id)); + let tcx = self.crate_context.tcx; + let self_type = ty::lookup_item_type(tcx, local_def(item.id)); // If there are no traits, then this implementation must have a // base type. @@ -452,10 +452,8 @@ pub impl CoherenceChecker { } fn check_implementation_coherence(&self) { - let coherence_info = &mut self.crate_context.coherence_info; - let extension_methods = &coherence_info.extension_methods; - - for extension_methods.each_key |&trait_id| { + let coherence_info = self.crate_context.coherence_info; + for coherence_info.extension_methods.each_key |&trait_id| { self.check_implementation_coherence_of(trait_id); } } @@ -514,13 +512,16 @@ pub impl CoherenceChecker { } fn iter_impls_of_trait(&self, trait_def_id: def_id, f: &fn(@Impl)) { - let coherence_info = &mut self.crate_context.coherence_info; - let extension_methods = &coherence_info.extension_methods; + let coherence_info = self.crate_context.coherence_info; + let extension_methods = &*coherence_info.extension_methods; match extension_methods.find(&trait_def_id) { Some(impls) => { - let impls: &mut ~[@Impl] = *impls; - for uint::range(0, impls.len()) |i| { + let len = { // FIXME(#5074) stage0 requires this + let impls: &mut ~[@Impl] = *impls; + impls.len() + }; + for uint::range(0, len) |i| { f(impls[i]); } } @@ -1014,7 +1015,7 @@ pub impl CoherenceChecker { // fn populate_destructor_table(&self) { - let coherence_info = &mut self.crate_context.coherence_info; + let coherence_info = self.crate_context.coherence_info; let tcx = self.crate_context.tcx; let drop_trait = tcx.lang_items.drop_trait(); let impls_opt = coherence_info.extension_methods.find(&drop_trait); diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs index 2bbcd24595cba..b818f666444f6 100644 --- a/src/librustc/middle/typeck/infer/glb.rs +++ b/src/librustc/middle/typeck/infer/glb.rs @@ -16,6 +16,7 @@ use middle::typeck::infer::lub::Lub; use middle::typeck::infer::sub::Sub; use middle::typeck::infer::to_str::InferStr; use middle::typeck::infer::{cres, InferCtxt}; +use middle::typeck::infer::fold_regions_in_sig; use middle::typeck::isr_alist; use syntax::ast; use syntax::ast::{Many, Once, extern_fn, impure_fn, m_const, m_imm, m_mutbl}; @@ -188,7 +189,8 @@ impl Combine for Glb { let new_vars = self.infcx.region_vars.vars_created_since_snapshot(snapshot); let sig1 = - self.infcx.fold_regions_in_sig( + fold_regions_in_sig( + self.infcx.tcx, &sig0, |r, _in_fn| generalize_region(self, snapshot, new_vars, a_isr, a_vars, b_vars, diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs index 8591433801796..34e006c9615a7 100644 --- a/src/librustc/middle/typeck/infer/lub.rs +++ b/src/librustc/middle/typeck/infer/lub.rs @@ -16,6 +16,7 @@ use middle::typeck::infer::lattice::*; use middle::typeck::infer::sub::Sub; use middle::typeck::infer::to_str::InferStr; use middle::typeck::infer::{cres, InferCtxt}; +use middle::typeck::infer::fold_regions_in_sig; use middle::typeck::isr_alist; use util::common::indent; use util::ppaux::mt_to_str; @@ -141,7 +142,8 @@ impl Combine for Lub { let new_vars = self.infcx.region_vars.vars_created_since_snapshot(snapshot); let sig1 = - self.infcx.fold_regions_in_sig( + fold_regions_in_sig( + self.infcx.tcx, &sig0, |r, _in_fn| generalize_region(self, snapshot, new_vars, a_isr, r)); diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 4491b04b382ec..899b8cfd7edeb 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -574,7 +574,7 @@ pub impl InferCtxt { } /// Execute `f` and commit the bindings if successful - fn commit(&mut self, f: &fn() -> Result) -> Result { + fn commit(@mut self, f: &fn() -> Result) -> Result { assert!(!self.in_snapshot()); debug!("commit()"); @@ -589,7 +589,7 @@ pub impl InferCtxt { } /// Execute `f`, unroll bindings on failure - fn try(&mut self, f: &fn() -> Result) -> Result { + fn try(@mut self, f: &fn() -> Result) -> Result { debug!("try()"); do indent { let snapshot = self.start_snapshot(); @@ -603,7 +603,7 @@ pub impl InferCtxt { } /// Execute `f` then unroll any bindings it creates - fn probe(&mut self, f: &fn() -> Result) -> Result { + fn probe(@mut self, f: &fn() -> Result) -> Result { debug!("probe()"); do indent { let snapshot = self.start_snapshot(); @@ -783,15 +783,14 @@ pub impl InferCtxt { }); (fn_sig, isr) } +} - fn fold_regions_in_sig( - &mut self, - fn_sig: &ty::FnSig, - fldr: &fn(r: ty::Region, in_fn: bool) -> ty::Region) -> ty::FnSig - { - do ty::fold_sig(fn_sig) |t| { - ty::fold_regions(self.tcx, t, fldr) - } +pub fn fold_regions_in_sig( + tcx: ty::ctxt, + fn_sig: &ty::FnSig, + fldr: &fn(r: ty::Region, in_fn: bool) -> ty::Region) -> ty::FnSig +{ + do ty::fold_sig(fn_sig) |t| { + ty::fold_regions(tcx, t, fldr) } - -} +} \ No newline at end of file diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc index 9e34d5c6177e6..1ecb38854c815 100644 --- a/src/librustc/rustc.rc +++ b/src/librustc/rustc.rc @@ -76,6 +76,9 @@ pub mod middle { } pub mod ty; pub mod subst; + #[cfg(stage0)] #[path = "resolve_stage0.rs"] + pub mod resolve; + #[cfg(not(stage0))] pub mod resolve; #[path = "typeck/mod.rs"] pub mod typeck; diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 7facc181effec..1c822b520f6af 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -355,7 +355,7 @@ pub impl CodeMap { } pub fn span_to_str(&self, sp: span) -> ~str { - let files = &mut *self.files; + let files = &*self.files; if files.len() == 0 && sp == dummy_sp() { return ~"no-location"; } From 4999d44d5b244671492fbdc05121416e05f729eb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 2 May 2013 16:37:28 -0400 Subject: [PATCH 24/46] trans: fix borrow violation --- src/librustc/middle/trans/base.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 7738e97779971..80cedf2372786 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1258,7 +1258,7 @@ pub fn trans_block_cleanups(bcx: block, cleanups: ~[cleanup]) -> block { } pub fn trans_block_cleanups_(bcx: block, - cleanups: ~[cleanup], + cleanups: &[cleanup], /* cleanup_cx: block, */ is_lpad: bool) -> block { let _icx = bcx.insn_ctxt("trans_block_cleanups"); @@ -1317,7 +1317,7 @@ pub fn cleanup_and_leave(bcx: block, dest: sub_cx.llbb }); bcx = trans_block_cleanups_(sub_cx, - block_cleanups(cur), + inf.cleanups, is_lpad); } _ => () From cc62680cc95d2ea733dbdc03c30e5c4c48fdad04 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 2 May 2013 17:08:04 -0400 Subject: [PATCH 25/46] free the borrow list propertly instead of crashing --- src/libcore/cleanup.rs | 5 +++++ src/libcore/unstable/lang.rs | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/libcore/cleanup.rs b/src/libcore/cleanup.rs index 1dfe1b22dc4fb..5e2f4af184dc4 100644 --- a/src/libcore/cleanup.rs +++ b/src/libcore/cleanup.rs @@ -15,6 +15,7 @@ use ptr::mut_null; use repr::BoxRepr; use sys::TypeDesc; use cast::transmute; +use unstable::lang::clear_task_borrow_list; #[cfg(notest)] use ptr::to_unsafe_ptr; @@ -179,6 +180,10 @@ pub unsafe fn annihilate() { n_bytes_freed: 0 }; + // Quick hack: we need to free this list upon task exit, and this + // is a convenient place to do it. + clear_task_borrow_list(); + // Pass 1: Make all boxes immortal. // // In this pass, nothing gets freed, so it does not matter whether diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index d25147fcde118..a3e44b5feea4a 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -91,7 +91,16 @@ fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) { } } -pub fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { +pub unsafe fn clear_task_borrow_list() { + // pub because it is used by the box annihilator. + let cur_task = rust_get_task(); + let ptr = rustrt::rust_take_task_borrow_list(cur_task); + if !ptr.is_null() { + let _: ~[BorrowRecord] = transmute(ptr); + } +} + +fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { debug_ptr("fail_borrowed: ", box); if !::rt::env::get().debug_borrows { From 9bded762601e47b16bd97ef4439536fb9d7af0cd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 2 May 2013 21:15:36 -0400 Subject: [PATCH 26/46] move @mut into scope_info --- src/librustc/middle/trans/base.rs | 67 ++++++++++++------------ src/librustc/middle/trans/common.rs | 37 +++++++------ src/librustc/middle/trans/controlflow.rs | 4 +- src/librustc/middle/trans/expr.rs | 1 - 4 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 80cedf2372786..8a21d9116f5e6 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -34,7 +34,6 @@ use lib; use metadata::common::LinkMeta; use metadata::{csearch, cstore, encoder}; use middle::astencode; -use middle::borrowck::RootInfo; use middle::resolve; use middle::trans::_match; use middle::trans::adt; @@ -62,7 +61,6 @@ use middle::trans::type_of::*; use middle::ty; use util::common::indenter; use util::ppaux::{Repr, ty_to_str}; -use util::ppaux; use core::hash; use core::hashmap::{HashMap, HashSet}; @@ -887,11 +885,10 @@ pub fn need_invoke(bcx: block) -> bool { // Walk the scopes to look for cleanups let mut cur = bcx; loop { - let current = &mut *cur; - let kind = &mut *current.kind; - match *kind { - block_scope(ref mut inf) => { - for vec::each((*inf).cleanups) |cleanup| { + match cur.kind { + block_scope(inf) => { + let inf = &mut *inf; // FIXME(#5074) workaround old borrowck + for vec::each(inf.cleanups) |cleanup| { match *cleanup { clean(_, cleanup_type) | clean_temp(_, _, cleanup_type) => { if cleanup_type == normal_exit_and_unwind { @@ -903,7 +900,7 @@ pub fn need_invoke(bcx: block) -> bool { } _ => () } - cur = match current.parent { + cur = match cur.parent { Some(next) => next, None => return false } @@ -925,11 +922,13 @@ pub fn in_lpad_scope_cx(bcx: block, f: &fn(si: &mut scope_info)) { let mut bcx = bcx; loop { { - // FIXME #4280: Borrow check bug workaround. - let kind: &mut block_kind = &mut *bcx.kind; - match *kind { - block_scope(ref mut inf) => { - if inf.cleanups.len() > 0u || bcx.parent.is_none() { + match bcx.kind { + block_scope(inf) => { + let len = { // FIXME(#5074) workaround old borrowck + let inf = &mut *inf; + inf.cleanups.len() + }; + if len > 0u || bcx.parent.is_none() { f(inf); return; } @@ -1194,7 +1193,7 @@ pub fn new_block(cx: fn_ctxt, parent: Option, kind: block_kind, } pub fn simple_block_scope() -> block_kind { - block_scope(scope_info { + block_scope(@mut scope_info { loop_break: None, loop_label: None, cleanups: ~[], @@ -1222,7 +1221,7 @@ pub fn loop_scope_block(bcx: block, loop_label: Option, n: ~str, opt_node_info: Option) -> block { - return new_block(bcx.fcx, Some(bcx), block_scope(scope_info { + return new_block(bcx.fcx, Some(bcx), block_scope(@mut scope_info { loop_break: Some(loop_break), loop_label: loop_label, cleanups: ~[], @@ -1300,28 +1299,28 @@ pub fn cleanup_and_leave(bcx: block, @fmt!("cleanup_and_leave(%s)", cur.to_str())); } - { - // FIXME #4280: Borrow check bug workaround. - let kind: &mut block_kind = &mut *cur.kind; - match *kind { - block_scope(ref mut inf) if !inf.cleanups.is_empty() => { - for vec::find((*inf).cleanup_paths, - |cp| cp.target == leave).each |cp| { - Br(bcx, cp.dest); - return; - } - let sub_cx = sub_block(bcx, ~"cleanup"); - Br(bcx, sub_cx.llbb); - inf.cleanup_paths.push(cleanup_path { - target: leave, - dest: sub_cx.llbb - }); + match cur.kind { + block_scope(inf) if !inf.empty_cleanups() => { + let (sub_cx, inf_cleanups) = { + let inf = &mut *inf; // FIXME(#5074) workaround stage0 + for vec::find((*inf).cleanup_paths, + |cp| cp.target == leave).each |cp| { + Br(bcx, cp.dest); + return; + } + let sub_cx = sub_block(bcx, ~"cleanup"); + Br(bcx, sub_cx.llbb); + inf.cleanup_paths.push(cleanup_path { + target: leave, + dest: sub_cx.llbb + }); + (sub_cx, copy inf.cleanups) + }; bcx = trans_block_cleanups_(sub_cx, - inf.cleanups, + inf_cleanups, is_lpad); - } - _ => () } + _ => () } match upto { diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index dba9ddd2b1d1a..705a89381dfa5 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -532,6 +532,7 @@ pub fn add_clean_free(cx: block, ptr: ValueRef, heap: heap) { // drop glue checks whether it is zero. pub fn revoke_clean(cx: block, val: ValueRef) { do in_scope_cx(cx) |scope_info| { + let scope_info = &mut *scope_info; // FIXME(#5074) workaround borrowck let cleanup_pos = vec::position( scope_info.cleanups, |cu| match *cu { @@ -550,9 +551,9 @@ pub fn revoke_clean(cx: block, val: ValueRef) { } pub fn block_cleanups(bcx: block) -> ~[cleanup] { - match *bcx.kind { + match bcx.kind { block_non_scope => ~[], - block_scope(ref mut inf) => /*bad*/copy inf.cleanups + block_scope(inf) => /*bad*/copy inf.cleanups } } @@ -561,7 +562,7 @@ pub enum block_kind { // cleaned up. May correspond to an actual block in the language, but also // to an implicit scope, for example, calls introduce an implicit scope in // which the arguments are evaluated and cleaned up. - block_scope(scope_info), + block_scope(@mut scope_info), // A non-scope block is a basic block created as a translation artifact // from translating code that expresses conditional logic rather than by @@ -584,6 +585,12 @@ pub struct scope_info { landing_pad: Option, } +pub impl scope_info { + fn empty_cleanups(&mut self) -> bool { + self.cleanups.is_empty() + } +} + pub trait get_node_info { fn info(&self) -> Option; } @@ -632,7 +639,7 @@ pub struct block_ { unreachable: bool, parent: Option, // The 'kind' of basic block this is. - kind: @mut block_kind, + kind: block_kind, // Is this block part of a landing pad? is_lpad: bool, // info about the AST node this block originated from, if any @@ -651,7 +658,7 @@ pub fn block_(llbb: BasicBlockRef, parent: Option, kind: block_kind, terminated: false, unreachable: false, parent: parent, - kind: @mut kind, + kind: kind, is_lpad: is_lpad, node_info: node_info, fcx: fcx @@ -699,21 +706,17 @@ pub fn val_str(tn: @TypeNames, v: ValueRef) -> @str { return ty_str(tn, val_ty(v)); } -pub fn in_scope_cx(cx: block, f: &fn(si: &mut scope_info)) { +pub fn in_scope_cx(cx: block, f: &fn(si: @mut scope_info)) { let mut cur = cx; loop { - { - // XXX: Borrow check bug workaround. - let kind: &mut block_kind = &mut *cur.kind; - match *kind { - block_scope(ref mut inf) => { - debug!("in_scope_cx: selected cur=%s (cx=%s)", - cur.to_str(), cx.to_str()); - f(inf); - return; - } - _ => () + match cur.kind { + block_scope(inf) => { + debug!("in_scope_cx: selected cur=%s (cx=%s)", + cur.to_str(), cx.to_str()); + f(inf); + return; } + _ => () } cur = block_parent(cur); } diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 113136fa58d13..60b6cf9e23f79 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -243,8 +243,8 @@ pub fn trans_break_cont(bcx: block, let mut unwind = bcx; let mut target; loop { - match *unwind.kind { - block_scope(scope_info { + match unwind.kind { + block_scope(@scope_info { loop_break: Some(brk), loop_label: l, _ diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 30c541c0c055b..ff493c46a176a 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -123,7 +123,6 @@ use back::abi; use lib; use lib::llvm::{ValueRef, TypeRef, llvm}; use metadata::csearch; -use middle::borrowck::root_map_key; use middle::trans::_match; use middle::trans::adt; use middle::trans::asm; From 34024353e86e22e459a1981f563e3d4f43906432 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 May 2013 05:42:00 -0400 Subject: [PATCH 27/46] Change borrow debugging so it is disabled by -O --- src/libcore/rt/env.rs | 2 - src/libcore/unstable/lang.rs | 121 +++++++++++++++------------- src/librustc/middle/lang_items.rs | 20 ++++- src/librustc/middle/trans/common.rs | 32 ++++++-- src/librustc/middle/trans/datum.rs | 24 +++++- src/rt/rust_env.cpp | 2 - src/rt/rust_env.h | 1 - 7 files changed, 129 insertions(+), 73 deletions(-) diff --git a/src/libcore/rt/env.rs b/src/libcore/rt/env.rs index 1f52cf77868a2..e479375401a3b 100644 --- a/src/libcore/rt/env.rs +++ b/src/libcore/rt/env.rs @@ -33,8 +33,6 @@ pub struct Environment { argv: **c_char, /// Print GC debugging info (true if env var RUST_DEBUG_MEM is set) debug_mem: bool, - /// Track origin of `@mut` borrows (true if env var RUST_DEBUG_BORROWS is set) - debug_borrows: bool } /// Get the global environment settings diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index a3e44b5feea4a..27fc3287bdb39 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -19,7 +19,7 @@ use sys; use unstable::exchange_alloc; use cast::transmute; use task::rt::rust_get_task; -use option::{Some, None}; +use option::{Option, Some, None}; #[allow(non_camel_case_types)] pub type rust_task = c_void; @@ -79,6 +79,19 @@ struct BorrowRecord { line: size_t } +fn try_take_task_borrow_list() -> Option<~[BorrowRecord]> { + unsafe { + let cur_task = rust_get_task(); + let ptr = rustrt::rust_take_task_borrow_list(cur_task); + if ptr.is_null() { + None + } else { + let v: ~[BorrowRecord] = transmute(ptr); + Some(v) + } + } +} + fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) { unsafe { let cur_task = rust_get_task(); @@ -93,23 +106,20 @@ fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) { pub unsafe fn clear_task_borrow_list() { // pub because it is used by the box annihilator. - let cur_task = rust_get_task(); - let ptr = rustrt::rust_take_task_borrow_list(cur_task); - if !ptr.is_null() { - let _: ~[BorrowRecord] = transmute(ptr); - } + let _ = try_take_task_borrow_list(); } fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { debug_ptr("fail_borrowed: ", box); - if !::rt::env::get().debug_borrows { - let msg = "borrowed"; - do str::as_buf(msg) |msg_p, _| { - fail_(msg_p as *c_char, file, line); + match try_take_task_borrow_list() { + None => { // not recording borrows + let msg = "borrowed"; + do str::as_buf(msg) |msg_p, _| { + fail_(msg_p as *c_char, file, line); + } } - } else { - do swap_task_borrow_list |borrow_list| { + Some(borrow_list) => { // recording borrows let mut msg = ~"borrowed"; let mut sep = " at "; for borrow_list.each_reverse |entry| { @@ -126,7 +136,6 @@ fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { do str::as_buf(msg) |msg_p, _| { fail_(msg_p as *c_char, file, line) } - borrow_list } } } @@ -211,34 +220,6 @@ pub unsafe fn borrow_as_imm(a: *u8) { (*a).header.ref_count |= FROZEN_BIT; } -fn add_borrow_to_task_list(a: *mut BoxRepr, file: *c_char, line: size_t) { - do swap_task_borrow_list |borrow_list| { - let mut borrow_list = borrow_list; - borrow_list.push(BorrowRecord {box: a, file: file, line: line}); - borrow_list - } -} - -fn remove_borrow_from_task_list(a: *mut BoxRepr, file: *c_char, line: size_t) { - do swap_task_borrow_list |borrow_list| { - let mut borrow_list = borrow_list; - let br = BorrowRecord {box: a, file: file, line: line}; - match borrow_list.rposition_elem(&br) { - Some(idx) => { - borrow_list.remove(idx); - borrow_list - } - None => { - let err = fmt!("no borrow found, br=%?, borrow_list=%?", - br, borrow_list); - do str::as_buf(err) |msg_p, _| { - fail_(msg_p as *c_char, file, line) - } - } - } - } -} - #[cfg(not(stage0))] #[lang="borrow_as_imm"] #[inline(always)] @@ -252,12 +233,8 @@ pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint { if (ref_count & MUT_BIT) != 0 { fail_borrowed(a, file, line); - } else { - (*a).header.ref_count |= FROZEN_BIT; - if ::rt::env::get().debug_borrows { - add_borrow_to_task_list(a, file, line); - } } + ref_count } @@ -273,15 +250,53 @@ pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint { let ref_count = (*a).header.ref_count; if (ref_count & (MUT_BIT|FROZEN_BIT)) != 0 { fail_borrowed(a, file, line); - } else { - (*a).header.ref_count |= (MUT_BIT|FROZEN_BIT); - if ::rt::env::get().debug_borrows { - add_borrow_to_task_list(a, file, line); - } } ref_count } + +#[cfg(not(stage0))] +#[lang="record_borrow"] +pub unsafe fn record_borrow(a: *u8, old_ref_count: uint, + file: *c_char, line: size_t) { + if (old_ref_count & ALL_BITS) == 0 { + // was not borrowed before + let a: *mut BoxRepr = transmute(a); + do swap_task_borrow_list |borrow_list| { + let mut borrow_list = borrow_list; + borrow_list.push(BorrowRecord {box: a, file: file, line: line}); + borrow_list + } + } +} + +#[cfg(not(stage0))] +#[lang="unrecord_borrow"] +pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint, + file: *c_char, line: size_t) { + if (old_ref_count & ALL_BITS) == 0 { + // was not borrowed before + let a: *mut BoxRepr = transmute(a); + do swap_task_borrow_list |borrow_list| { + let mut borrow_list = borrow_list; + let br = BorrowRecord {box: a, file: file, line: line}; + match borrow_list.rposition_elem(&br) { + Some(idx) => { + borrow_list.remove(idx); + borrow_list + } + None => { + let err = fmt!("no borrow found, br=%?, borrow_list=%?", + br, borrow_list); + do str::as_buf(err) |msg_p, _| { + fail_(msg_p as *c_char, file, line) + } + } + } + } + } +} + #[cfg(stage0)] #[lang="return_to_mut"] #[inline(always)] @@ -312,10 +327,6 @@ pub unsafe fn return_to_mut(a: *u8, old_ref_count: uint, debug_ptr(" (old) : ", old_ref_count as *()); debug_ptr(" (new) : ", ref_count as *()); debug_ptr(" (comb): ", combined as *()); - - if ::rt::env::get().debug_borrows { - remove_borrow_from_task_list(a, file, line); - } } } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 314b58d13e147..448f33796bcfd 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -70,18 +70,20 @@ pub enum LangItem { ReturnToMutFnLangItem, // 32 CheckNotBorrowedFnLangItem, // 33 StrDupUniqFnLangItem, // 34 + RecordBorrowFnLangItem, // 35 + UnrecordBorrowFnLangItem, // 36 - StartFnLangItem, // 35 + StartFnLangItem, // 37 } pub struct LanguageItems { - items: [Option, ..36] + items: [Option, ..38] } pub impl LanguageItems { pub fn new() -> LanguageItems { LanguageItems { - items: [ None, ..36 ] + items: [ None, ..38 ] } } @@ -133,8 +135,10 @@ pub impl LanguageItems { 32 => "return_to_mut", 33 => "check_not_borrowed", 34 => "strdup_uniq", + 35 => "record_borrow", + 36 => "unrecord_borrow", - 35 => "start", + 37 => "start", _ => "???" } @@ -251,6 +255,12 @@ pub impl LanguageItems { pub fn strdup_uniq_fn(&const self) -> def_id { self.items[StrDupUniqFnLangItem as uint].get() } + pub fn record_borrow_fn(&const self) -> def_id { + self.items[RecordBorrowFnLangItem as uint].get() + } + pub fn unrecord_borrow_fn(&const self) -> def_id { + self.items[UnrecordBorrowFnLangItem as uint].get() + } pub fn start_fn(&const self) -> def_id { self.items[StartFnLangItem as uint].get() } @@ -302,6 +312,8 @@ fn LanguageItemCollector(crate: @crate, item_refs.insert(@~"check_not_borrowed", CheckNotBorrowedFnLangItem as uint); item_refs.insert(@~"strdup_uniq", StrDupUniqFnLangItem as uint); + item_refs.insert(@~"record_borrow", RecordBorrowFnLangItem as uint); + item_refs.insert(@~"unrecord_borrow", UnrecordBorrowFnLangItem as uint); item_refs.insert(@~"start", StartFnLangItem as uint); LanguageItemCollector { diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 705a89381dfa5..2a13cf73f8bba 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -489,15 +489,37 @@ pub fn add_clean_return_to_mut(bcx: block, clean_temp( frozen_val_ref, |bcx| { + let mut bcx = bcx; + + let box_ptr = + build::Load(bcx, + build::PointerCast(bcx, + frozen_val_ref, + T_ptr(T_ptr(T_i8())))); + + let bits_val = + build::Load(bcx, + bits_val_ref); + + if bcx.tcx().sess.opts.optimize == session::No { + bcx = callee::trans_lang_call( + bcx, + bcx.tcx().lang_items.unrecord_borrow_fn(), + ~[ + box_ptr, + bits_val, + filename_val, + line_val + ], + expr::Ignore); + } + callee::trans_lang_call( bcx, bcx.tcx().lang_items.return_to_mut_fn(), ~[ - build::Load(bcx, - build::PointerCast(bcx, - frozen_val_ref, - T_ptr(T_ptr(T_i8())))), - build::Load(bcx, bits_val_ref), + box_ptr, + bits_val, filename_val, line_val ], diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index d6df6c87dec43..af7165c53a7c2 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -101,6 +101,7 @@ use middle::trans::type_of; use middle::ty; use util::common::indenter; use util::ppaux::ty_to_str; +use driver::session; use core::container::Set; // XXX: this should not be necessary use core::to_bytes; @@ -564,19 +565,34 @@ pub impl Datum { DynaMut => bcx.tcx().lang_items.borrow_as_mut_fn(), }; + let box_ptr = Load(bcx, + PointerCast(bcx, + scratch.val, + T_ptr(T_ptr(T_i8())))); + bcx = callee::trans_lang_call( bcx, freeze_did, ~[ - Load(bcx, - PointerCast(bcx, - scratch.val, - T_ptr(T_ptr(T_i8())))), + box_ptr, filename, line ], expr::SaveIn(scratch_bits.val)); + if bcx.tcx().sess.opts.optimize == session::No { + bcx = callee::trans_lang_call( + bcx, + bcx.tcx().lang_items.record_borrow_fn(), + ~[ + box_ptr, + Load(bcx, scratch_bits.val), + filename, + line + ], + expr::Ignore); + } + add_clean_return_to_mut( cleanup_bcx, scratch.val, scratch_bits.val, filename, line); diff --git a/src/rt/rust_env.cpp b/src/rt/rust_env.cpp index e6fe35609ec93..041b4efac52a2 100644 --- a/src/rt/rust_env.cpp +++ b/src/rt/rust_env.cpp @@ -24,7 +24,6 @@ #define RUST_SEED "RUST_SEED" #define RUST_POISON_ON_FREE "RUST_POISON_ON_FREE" #define RUST_DEBUG_MEM "RUST_DEBUG_MEM" -#define RUST_DEBUG_BORROWS "RUST_DEBUG_BORROWS" #if defined(__WIN32__) static int @@ -131,7 +130,6 @@ load_env(int argc, char **argv) { env->argc = argc; env->argv = argv; env->debug_mem = getenv(RUST_DEBUG_MEM) != NULL; - env->debug_borrows = getenv(RUST_DEBUG_BORROWS) != NULL; return env; } diff --git a/src/rt/rust_env.h b/src/rt/rust_env.h index 322198bb031ff..df27f7674f265 100644 --- a/src/rt/rust_env.h +++ b/src/rt/rust_env.h @@ -28,7 +28,6 @@ struct rust_env { int argc; char **argv; rust_bool debug_mem; - rust_bool debug_borrows; }; rust_env* load_env(int argc, char **argv); From e7d96934c16c915d18be391836fbf0ebca6c558b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 May 2013 12:11:15 -0400 Subject: [PATCH 28/46] Correct mismatch between the way that pattern ids and expression ids map to types (pattern ids map to the input type, expression ids map to the output type) --- src/librustc/middle/borrowck/check_loans.rs | 2 +- .../middle/borrowck/gather_loans/lifetime.rs | 8 +- src/librustc/middle/borrowck/mod.rs | 18 +- src/librustc/middle/mem_categorization.rs | 195 +++++++++--------- src/librustc/middle/trans/_match.rs | 17 +- src/librustc/middle/trans/datum.rs | 4 +- src/librustc/middle/trans/expr.rs | 2 +- 7 files changed, 131 insertions(+), 115 deletions(-) diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 70da9c9380559..c2dc2fb22ab5b 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -378,7 +378,7 @@ pub impl<'self> CheckLoanCtxt<'self> { // Dynamically check writes to `@mut` let key = root_map_key { - id: base.id, + id: guarantor.id, derefs: deref_count }; debug!("Inserting write guard at %?", key); diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs index fdfb26c0d0835..43fff110a7a7e 100644 --- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs +++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs @@ -97,7 +97,7 @@ impl GuaranteeLifetimeContext { ); if !omit_root { - self.check_root(base, derefs, ptr_mutbl, discr_scope); + self.check_root(cmt, base, derefs, ptr_mutbl, discr_scope); } else { debug!("omitting root, base=%s, base_scope=%?", base.repr(self.tcx()), base_scope); @@ -168,12 +168,14 @@ impl GuaranteeLifetimeContext { } fn check_root(&self, + cmt_deref: mc::cmt, cmt_base: mc::cmt, derefs: uint, ptr_mutbl: ast::mutability, discr_scope: Option) { - debug!("check_root(cmt_base=%s, derefs=%? ptr_mutbl=%?, \ + debug!("check_root(cmt_deref=%s, cmt_base=%s, derefs=%?, ptr_mutbl=%?, \ discr_scope=%?)", + cmt_deref.repr(self.tcx()), cmt_base.repr(self.tcx()), derefs, ptr_mutbl, @@ -234,7 +236,7 @@ impl GuaranteeLifetimeContext { }; // Add a record of what is required - let rm_key = root_map_key {id: cmt_base.id, derefs: derefs}; + let rm_key = root_map_key {id: cmt_deref.id, derefs: derefs}; let root_info = RootInfo {scope: root_scope, freeze: opt_dyna}; self.bccx.root_map.insert(rm_key, root_info); diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 3f10223723727..a44f743c9ead0 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -172,11 +172,19 @@ pub struct BorrowStats { pub type LoanMap = @mut HashMap; -// the keys to the root map combine the `id` of the expression with -// the number of types that it is autodereferenced. So, for example, -// if you have an expression `x.f` and x has type ~@T, we could add an -// entry {id:x, derefs:0} to refer to `x` itself, `{id:x, derefs:1}` -// to refer to the deref of the unique pointer, and so on. +// The keys to the root map combine the `id` of the deref expression +// with the number of types that it is *autodereferenced*. So, for +// example, imagine I have a variable `x: @@@T` and an expression +// `(*x).f`. This will have 3 derefs, one explicit and then two +// autoderefs. These are the relevant `root_map_key` values that could +// appear: +// +// {id:*x, derefs:0} --> roots `x` (type: @@@T, due to explicit deref) +// {id:*x, derefs:1} --> roots `*x` (type: @@T, due to autoderef #1) +// {id:*x, derefs:2} --> roots `**x` (type: @T, due to autoderef #2) +// +// Note that there is no entry with derefs:3---the type of that expression +// is T, which is not a box. #[deriving(Eq, IterBytes)] pub struct root_map_key { id: ast::node_id, diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 5921e4b0e4ca0..f1c337125d704 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -92,12 +92,12 @@ pub enum ptr_kind { pub enum interior_kind { interior_tuple, // elt in a tuple interior_anon_field, // anonymous field (in e.g. - // struct Foo(int, int); + // struct Foo(int, int); interior_variant(ast::def_id), // internals to a variant of given enum interior_field(ast::ident, // name of field - ast::mutability), // declared mutability of field + ast::mutability), // declared mutability of field interior_index(ty::t, // type of vec/str/etc being deref'd - ast::mutability) // mutability of vec content + ast::mutability) // mutability of vec content } #[deriving(Eq)] @@ -108,18 +108,27 @@ pub enum MutabilityCategory { McInherited // Inherited from the fact that owner is mutable. } +// `cmt`: "Category, Mutability, and Type". +// // a complete categorization of a value indicating where it originated // and how it is located, as well as the mutability of the memory in // which the value is stored. // -// note: cmt stands for "categorized mutable type". +// *WARNING* The field `cmt.type` is NOT necessarily the same as the +// result of `node_id_to_type(cmt.id)`. This is because the `id` is +// always the `id` of the node producing the type; in an expression +// like `*x`, the type of this deref node is the deref'd type (`T`), +// but in a pattern like `@x`, the `@x` pattern is again a +// dereference, but its type is the type *before* the dereference +// (`@T`). So use `cmt.type` to find the type of the value in a consistent +// fashion. For more details, see the method `cat_pattern` #[deriving(Eq)] pub struct cmt_ { id: ast::node_id, // id of expr/pat producing this value span: span, // span of same expr/pat cat: categorization, // categorization of expr mutbl: MutabilityCategory, // mutability of expr as lvalue - ty: ty::t // type of the expr + ty: ty::t // type of the expr (*see WARNING above*) } pub type cmt = @cmt_; @@ -245,19 +254,6 @@ pub fn cat_def( return mcx.cat_def(expr_id, expr_span, expr_ty, def); } -pub fn cat_variant( - tcx: ty::ctxt, - method_map: typeck::method_map, - arg: N, - enum_did: ast::def_id, - cmt: cmt) -> cmt { - - let mcx = &mem_categorization_ctxt { - tcx: tcx, method_map: method_map - }; - return mcx.cat_variant(arg, enum_did, cmt); -} - pub trait ast_node { fn id(&self) -> ast::node_id; fn span(&self) -> span; @@ -273,16 +269,6 @@ impl ast_node for @ast::pat { fn span(&self) -> span { self.span } } -pub trait get_type_for_node { - fn ty(&self, node: N) -> ty::t; -} - -impl get_type_for_node for ty::ctxt { - fn ty(&self, node: N) -> ty::t { - ty::node_id_to_type(*self, node.id()) - } -} - pub struct mem_categorization_ctxt { tcx: ty::ctxt, method_map: typeck::method_map, @@ -336,6 +322,14 @@ pub impl MutabilityCategory { } pub impl mem_categorization_ctxt { + fn expr_ty(&self, expr: @ast::expr) -> ty::t { + ty::expr_ty(self.tcx, expr) + } + + fn pat_ty(&self, pat: @ast::pat) -> ty::t { + ty::node_id_to_type(self.tcx, pat.id) + } + fn cat_expr(&self, expr: @ast::expr) -> cmt { match self.tcx.adjustments.find(&expr.id) { None => { @@ -385,7 +379,7 @@ pub impl mem_categorization_ctxt { expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr())); let tcx = self.tcx; - let expr_ty = tcx.ty(expr); + let expr_ty = self.expr_ty(expr); match expr.node { ast::expr_unary(ast::deref, e_base) => { if self.method_map.contains_key(&expr.id) { @@ -402,7 +396,8 @@ pub impl mem_categorization_ctxt { assert!(!self.method_map.contains_key(&expr.id)); let base_cmt = self.cat_expr(base); - self.cat_field(expr, base_cmt, f_name, expr.id) + self.cat_field(expr, base_cmt, f_name, + self.expr_ty(expr), expr.id) } ast::expr_index(base, _) => { @@ -554,19 +549,6 @@ pub impl mem_categorization_ctxt { } } - fn cat_variant(&self, - arg: N, - enum_did: ast::def_id, - cmt: cmt) -> cmt { - @cmt_ { - id: arg.id(), - span: arg.span(), - cat: cat_interior(cmt, interior_variant(enum_did)), - mutbl: cmt.mutbl.inherit(), - ty: self.tcx.ty(arg) - } - } - fn cat_rvalue(&self, elt: N, expr_ty: ty::t) -> cmt { @cmt_ { id:elt.id(), @@ -598,6 +580,7 @@ pub impl mem_categorization_ctxt { node: N, base_cmt: cmt, f_name: ast::ident, + f_ty: ty::t, field_id: ast::node_id) -> cmt { let f_mutbl = match field_mutbl(self.tcx, base_cmt.ty, f_name, field_id) { @@ -617,7 +600,7 @@ pub impl mem_categorization_ctxt { span: node.span(), cat: cat_interior(base_cmt, f_interior), mutbl: m, - ty: self.tcx.ty(node) + ty: f_ty } } @@ -697,8 +680,8 @@ pub impl mem_categorization_ctxt { } fn cat_index(&self, - elt: N, - base_cmt: cmt) -> cmt { + elt: N, + base_cmt: cmt) -> cmt { let mt = match ty::index(base_cmt.ty) { Some(mt) => mt, None => { @@ -756,27 +739,17 @@ pub impl mem_categorization_ctxt { } } - fn cat_tuple_elt(&self, - elt: N, - cmt: cmt) -> cmt { - @cmt_ { - id: elt.id(), - span: elt.span(), - cat: cat_interior(cmt, interior_tuple), - mutbl: cmt.mutbl.inherit(), - ty: self.tcx.ty(elt) - } - } - - fn cat_anon_struct_field(&self, - elt: N, - cmt: cmt) -> cmt { + fn cat_imm_interior(&self, + node: N, + base_cmt: cmt, + interior_ty: ty::t, + interior: interior_kind) -> cmt { @cmt_ { - id: elt.id(), - span: elt.span(), - cat: cat_interior(cmt, interior_anon_field), - mutbl: cmt.mutbl.inherit(), - ty: self.tcx.ty(elt) + id: node.id(), + span: node.span(), + cat: cat_interior(base_cmt, interior), + mutbl: base_cmt.mutbl.inherit(), + ty: interior_ty } } @@ -797,27 +770,37 @@ pub impl mem_categorization_ctxt { // we can be sure that the binding will remain valid for the // duration of the arm. // - // The correspondence between the id in the cmt and which - // pattern is being referred to is somewhat...subtle. In - // general, the id of the cmt is the id of the node that - // produces the value. For patterns, that's actually the - // *subpattern*, generally speaking. + // (*) 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 + // come back and I'll dive into a bit more detail here. :) OK, + // back? // - // To see what I mean about ids etc, consider: + // 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: // // let x = @@3; // match x { // @@y { ... } // } // - // Here the cmt for `y` would be something like + // In this case, the cmt and the relevant ids would be: + // + // CMT Id Type of Id Type of cmt // // local(x)->@->@ + // ^~~~~~~^ `x` from discr @@int @@int + // ^~~~~~~~~~^ `@@y` pattern node @@int @int + // ^~~~~~~~~~~~~^ `@y` pattern node @int int // - // where the id of `local(x)` is the id of the `x` that appears - // in the match, the id of `local(x)->@` is the `@y` pattern, - // and the id of `local(x)->@->@` is the id of the `y` pattern. - + // You can see that the types of the id and the cmt are in + // sync in the first line, because that id is actually the id + // of an expression. But once we get to pattern ids, the types + // 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; debug!("cat_pattern: id=%d pat=%s cmt=%s", @@ -839,22 +822,27 @@ pub impl mem_categorization_ctxt { match self.tcx.def_map.find(&pat.id) { Some(&ast::def_variant(enum_did, _)) => { // variant(x, y, z) - for subpats.each |subpat| { - let subcmt = self.cat_variant(*subpat, enum_did, cmt); - self.cat_pattern(subcmt, *subpat, op); + for subpats.each |&subpat| { + let subpat_ty = self.pat_ty(subpat); // see (*) + let subcmt = + self.cat_imm_interior(pat, cmt, subpat_ty, + interior_variant(enum_did)); + self.cat_pattern(subcmt, subpat, op); } } Some(&ast::def_fn(*)) | Some(&ast::def_struct(*)) => { - for subpats.each |subpat| { - let cmt_field = self.cat_anon_struct_field(*subpat, - cmt); - self.cat_pattern(cmt_field, *subpat, op); + for subpats.each |&subpat| { + let subpat_ty = self.pat_ty(subpat); // see (*) + let cmt_field = + self.cat_imm_interior(pat, cmt, subpat_ty, + interior_anon_field); + self.cat_pattern(cmt_field, subpat, op); } } Some(&ast::def_const(*)) => { - for subpats.each |subpat| { - self.cat_pattern(cmt, *subpat, op); + for subpats.each |&subpat| { + self.cat_pattern(cmt, subpat, op); } } _ => { @@ -876,39 +864,43 @@ pub impl mem_categorization_ctxt { ast::pat_struct(_, ref field_pats, _) => { // {f1: p1, ..., fN: pN} for field_pats.each |fp| { - let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id); + let field_ty = self.pat_ty(fp.pat); // see (*) + let cmt_field = self.cat_field(pat, cmt, fp.ident, + field_ty, pat.id); self.cat_pattern(cmt_field, fp.pat, op); } } ast::pat_tup(ref subpats) => { // (p1, ..., pN) - for subpats.each |subpat| { - let subcmt = self.cat_tuple_elt(*subpat, cmt); - self.cat_pattern(subcmt, *subpat, op); + for subpats.each |&subpat| { + let subpat_ty = self.pat_ty(subpat); // see (*) + let subcmt = self.cat_imm_interior(pat, cmt, subpat_ty, + interior_tuple); + self.cat_pattern(subcmt, subpat, op); } } ast::pat_box(subpat) | ast::pat_uniq(subpat) | ast::pat_region(subpat) => { // @p1, ~p1 - let subcmt = self.cat_deref(subpat, cmt, 0); + let subcmt = self.cat_deref(pat, cmt, 0); self.cat_pattern(subcmt, subpat, op); } ast::pat_vec(ref before, slice, ref after) => { - for before.each |pat| { - let elt_cmt = self.cat_index(*pat, cmt); - self.cat_pattern(elt_cmt, *pat, op); + for before.each |&before_pat| { + let elt_cmt = self.cat_index(pat, cmt); + self.cat_pattern(elt_cmt, before_pat, op); } - for slice.each |slice_pat| { - let slice_ty = self.tcx.ty(*slice_pat); - let slice_cmt = self.cat_rvalue(*slice_pat, slice_ty); - self.cat_pattern(slice_cmt, *slice_pat, op); + for slice.each |&slice_pat| { + let slice_ty = self.pat_ty(slice_pat); + let slice_cmt = self.cat_rvalue(pat, slice_ty); + self.cat_pattern(slice_cmt, slice_pat, op); } - for after.each |pat| { - let elt_cmt = self.cat_index(*pat, cmt); - self.cat_pattern(elt_cmt, *pat, op); + for after.each |&after_pat| { + let elt_cmt = self.cat_index(pat, cmt); + self.cat_pattern(elt_cmt, after_pat, op); } } @@ -1145,4 +1137,3 @@ impl Repr for interior_kind { } } } - diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index be39edd2d9b78..80e34ca481426 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -947,6 +947,17 @@ pub fn collect_record_or_struct_fields(bcx: block, } } +pub fn pats_require_rooting(bcx: block, + m: &[@Match], + col: uint) + -> bool { + vec::any(m, |br| { + let pat_id = br.pats[col].id; + let key = root_map_key {id: pat_id, derefs: 0u }; + bcx.ccx().maps.root_map.contains_key(&key) + }) +} + pub fn root_pats_as_necessary(bcx: block, m: &[@Match], col: uint, @@ -1303,7 +1314,10 @@ pub fn compile_submatch(bcx: block, if pat_id == 0 { pat_id = br.pats[col].id; } } - bcx = root_pats_as_necessary(bcx, m, col, val); + // If we are not matching against an `@T`, we should not be + // required to root any values. + assert!(any_box_pat(m, col) || !pats_require_rooting(bcx, m, col)); + let rec_fields = collect_record_or_struct_fields(bcx, m, col); if rec_fields.len() > 0 { let pat_ty = node_id_type(bcx, pat_id); @@ -1364,6 +1378,7 @@ pub fn compile_submatch(bcx: block, // Unbox in case of a box field if any_box_pat(m, col) { + bcx = root_pats_as_necessary(bcx, m, col, val); let llbox = Load(bcx, val); let box_no_addrspace = non_gc_box_cast(bcx, llbox); let unboxed = diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index af7165c53a7c2..095798ae21272 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -675,7 +675,7 @@ pub impl Datum { fn try_deref(&self, bcx: block, // block wherein to generate insn's span: span, // location where deref occurs - expr_id: ast::node_id, // id of expr being deref'd + expr_id: ast::node_id, // id of deref expr derefs: uint, // number of times deref'd already is_auto: bool) // if true, only deref if auto-derefable -> (Option, block) @@ -810,7 +810,7 @@ pub impl Datum { } fn deref(&self, bcx: block, - expr: @ast::expr, // the expression whose value is being deref'd + expr: @ast::expr, // the deref expression derefs: uint) -> DatumBlock { match self.try_deref(bcx, expr.span, expr.id, derefs, false) { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index ff493c46a176a..1a9824dcfe8a1 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -835,7 +835,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock { } ast::expr_unary(ast::deref, base) => { let basedatum = unpack_datum!(bcx, trans_to_datum(bcx, base)); - basedatum.deref(bcx, base, 0) + basedatum.deref(bcx, expr, 0) } _ => { bcx.tcx().sess.span_bug( From f3a6ea26437e240b02d749331b3a2d60aab0588b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 May 2013 15:12:04 -0400 Subject: [PATCH 29/46] lang: um, actually set locking bits! this code got lost. --- src/libcore/unstable/lang.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index 27fc3287bdb39..01ab2345918b1 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -235,6 +235,8 @@ pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint { fail_borrowed(a, file, line); } + (*a).header.ref_count = ref_count | FROZEN_BIT; + ref_count } @@ -251,6 +253,9 @@ pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint { if (ref_count & (MUT_BIT|FROZEN_BIT)) != 0 { fail_borrowed(a, file, line); } + + (*a).header.ref_count = ref_count | MUT_BIT | FROZEN_BIT; + ref_count } @@ -349,7 +354,12 @@ pub unsafe fn check_not_borrowed(a: *u8, file: *c_char, line: size_t) { let a: *mut BoxRepr = transmute(a); - if ((*a).header.ref_count & FROZEN_BIT) != 0 { + let ref_count = (*a).header.ref_count; + debug_ptr("check_not_borrowed (ptr) : ", a); + debug_ptr(" (line): ", line as *()); + debug_ptr(" (rc) : ", ref_count as *()); + + if (ref_count & FROZEN_BIT) != 0 { fail_borrowed(a, file, line); } } From be08c3e5146953619ff777aaa422152dfee4ad28 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 May 2013 16:26:43 -0400 Subject: [PATCH 30/46] rustc: add rooting, write-guards to slices etc --- src/librustc/middle/trans/_match.rs | 25 +++++-- src/librustc/middle/trans/controlflow.rs | 2 +- src/librustc/middle/trans/datum.rs | 70 ++++++++++++++----- src/librustc/middle/trans/expr.rs | 19 +++-- src/test/run-fail/borrowck-wg-fail-2.rs | 3 + src/test/run-fail/borrowck-wg-fail-3.rs | 3 + src/test/run-fail/borrowck-wg-fail.rs | 3 + ...orrowck-wg-one-mut-one-imm-slice-method.rs | 37 ++++++++++ .../borrowck-wg-one-mut-one-imm-slices.rs | 16 +++++ .../run-fail/borrowck-wg-one-mut-one-imm.rs | 17 +++++ .../run-fail/borrowck-wg-two-array-indices.rs | 17 +++++ .../run-pass/borrowck-wg-two-imm-borrows.rs | 14 ++++ 12 files changed, 194 insertions(+), 32 deletions(-) create mode 100644 src/test/run-fail/borrowck-wg-one-mut-one-imm-slice-method.rs create mode 100644 src/test/run-fail/borrowck-wg-one-mut-one-imm-slices.rs create mode 100644 src/test/run-fail/borrowck-wg-one-mut-one-imm.rs create mode 100644 src/test/run-fail/borrowck-wg-two-array-indices.rs create mode 100644 src/test/run-pass/borrowck-wg-two-imm-borrows.rs diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 80e34ca481426..3b1cdf0ba47f7 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -866,7 +866,18 @@ pub fn extract_variant_args(bcx: block, ExtractedBlock { vals: args, bcx: bcx } } +fn match_datum(bcx: block, val: ValueRef, pat_id: ast::node_id) -> Datum { + //! Helper for converting from the ValueRef that we pass around in + //! the match code, which is always by ref, into a Datum. Eventually + //! we should just pass around a Datum and be done with it. + + let ty = node_id_type(bcx, pat_id); + Datum {val: val, ty: ty, mode: datum::ByRef, source: RevokeClean} +} + + pub fn extract_vec_elems(bcx: block, + pat_span: span, pat_id: ast::node_id, elem_count: uint, slice: Option, @@ -874,9 +885,9 @@ pub fn extract_vec_elems(bcx: block, count: ValueRef) -> ExtractedBlock { let _icx = bcx.insn_ctxt("match::extract_vec_elems"); + let vec_datum = match_datum(bcx, val, pat_id); + let (bcx, base, len) = vec_datum.get_vec_base_and_len(bcx, pat_span, pat_id); let vt = tvec::vec_types(bcx, node_id_type(bcx, pat_id)); - let unboxed = load_if_immediate(bcx, val, vt.vec_ty); - let (base, len) = tvec::get_base_and_len(bcx, unboxed, vt.vec_ty); let mut elems = do vec::from_fn(elem_count) |i| { match slice { @@ -1308,10 +1319,14 @@ pub fn compile_submatch(bcx: block, vec::slice(vals, col + 1u, vals.len())); let ccx = *bcx.fcx.ccx; let mut pat_id = 0; + let mut pat_span = dummy_sp(); for vec::each(m) |br| { // Find a real id (we're adding placeholder wildcard patterns, but // each column is guaranteed to have at least one real pattern) - if pat_id == 0 { pat_id = br.pats[col].id; } + if pat_id == 0 { + pat_id = br.pats[col].id; + pat_span = br.pats[col].span; + } } // If we are not matching against an `@T`, we should not be @@ -1579,8 +1594,8 @@ pub fn compile_submatch(bcx: block, vec_len_ge(_, i) => Some(i), _ => None }; - let args = extract_vec_elems(opt_cx, pat_id, n, slice, - val, test_val); + let args = extract_vec_elems(opt_cx, pat_span, pat_id, n, slice, + val, test_val); size = args.vals.len(); unpacked = /*bad*/copy args.vals; opt_cx = args.bcx; diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 60b6cf9e23f79..c8699cc6371bc 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -333,7 +333,7 @@ pub fn trans_fail_expr(bcx: block, bcx, expr::trans_to_datum(bcx, arg_expr)); if ty::type_is_str(arg_datum.ty) { - let (lldata, _lllen) = arg_datum.get_base_and_len(bcx); + let (lldata, _) = arg_datum.get_vec_base_and_len_no_root(bcx); return trans_fail_value(bcx, sp_opt, lldata); } else if bcx.unreachable || ty::type_is_bot(arg_datum.ty) { return bcx; diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index 095798ae21272..6ffe504b804fb 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -603,6 +603,8 @@ pub impl Datum { } fn perform_write_guard(&self, bcx: block, span: span) -> block { + debug!("perform_write_guard"); + // Create scratch space, but do not root it. let llval = match self.mode { ByValue => self.val, @@ -682,25 +684,10 @@ pub impl Datum { { let ccx = bcx.ccx(); - debug!("try_deref(expr_id=%d, derefs=%?, is_auto=%b, self=%?)", + debug!("try_deref(expr_id=%?, derefs=%?, is_auto=%b, self=%?)", expr_id, derefs, is_auto, self.to_str(bcx.ccx())); - let _indenter = indenter(); - - // root the autoderef'd value, if necessary: - // - // (Note: root'd values are always boxes) - let key = root_map_key { id: expr_id, derefs: derefs }; - let bcx = match ccx.maps.root_map.find(&key) { - None => bcx, - Some(&root_info) => self.root(bcx, span, key, root_info) - }; - // Perform the write guard, if necessary. - // - // (Note: write-guarded values are always boxes) - let bcx = if ccx.maps.write_guard_map.contains(&key) { - self.perform_write_guard(bcx, span) - } else { bcx }; + let bcx = self.root_and_write_guard(bcx, span, expr_id, derefs); match ty::get(self.ty).sty { ty::ty_box(_) | ty::ty_uniq(_) => { @@ -854,8 +841,53 @@ pub impl Datum { DatumBlock { bcx: bcx, datum: datum } } - fn get_base_and_len(&self, bcx: block) -> (ValueRef, ValueRef) { - tvec::get_base_and_len(bcx, self.to_appropriate_llval(bcx), self.ty) + fn root_and_write_guard(&self, + mut bcx: block, + span: span, + expr_id: ast::node_id, + derefs: uint) -> block { + let key = root_map_key { id: expr_id, derefs: derefs }; + debug!("root_and_write_guard(key=%?)", key); + + // root the autoderef'd value, if necessary: + // + // (Note: root'd values are always boxes) + let ccx = bcx.ccx(); + bcx = match ccx.maps.root_map.find(&key) { + None => bcx, + Some(&root_info) => self.root(bcx, span, key, root_info) + }; + + // Perform the write guard, if necessary. + // + // (Note: write-guarded values are always boxes) + if ccx.maps.write_guard_map.contains(&key) { + self.perform_write_guard(bcx, span) + } else { + bcx + } + } + + fn get_vec_base_and_len(&self, + mut bcx: block, + span: span, + expr_id: ast::node_id) + -> (block, ValueRef, ValueRef) { + //! Converts a vector into the slice pair. Performs rooting + //! and write guards checks. + + // only imp't for @[] and @str, but harmless + bcx = self.root_and_write_guard(bcx, span, expr_id, 0); + let (base, len) = self.get_vec_base_and_len_no_root(bcx); + (bcx, base, len) + } + + fn get_vec_base_and_len_no_root(&self, bcx: block) -> (ValueRef, ValueRef) { + //! Converts a vector into the slice pair. Des not root + //! nor perform write guard checks. + + let llval = self.to_appropriate_llval(bcx); + tvec::get_base_and_len(bcx, llval, self.ty) } fn to_result(&self, bcx: block) -> common::Result { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 1a9824dcfe8a1..b8cdfeb796db0 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -218,10 +218,10 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { unpack_datum!(bcx, auto_ref(bcx, datum)) } Some(AutoBorrowVec(*)) => { - unpack_datum!(bcx, auto_slice(bcx, datum)) + unpack_datum!(bcx, auto_slice(bcx, expr, datum)) } Some(AutoBorrowVecRef(*)) => { - unpack_datum!(bcx, auto_slice_and_ref(bcx, datum)) + unpack_datum!(bcx, auto_slice_and_ref(bcx, expr, datum)) } Some(AutoBorrowFn(*)) => { // currently, all closure types are @@ -241,7 +241,7 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { DatumBlock {bcx: bcx, datum: datum.to_rptr(bcx)} } - fn auto_slice(bcx: block, datum: Datum) -> DatumBlock { + fn auto_slice(bcx: block, expr: @ast::expr, datum: Datum) -> DatumBlock { // This is not the most efficient thing possible; since slices // are two words it'd be better if this were compiled in // 'dest' mode, but I can't find a nice way to structure the @@ -250,7 +250,9 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { let tcx = bcx.tcx(); let unit_ty = ty::sequence_element_type(tcx, datum.ty); - let (base, len) = datum.get_base_and_len(bcx); + // NOTE prob need to distinguish "auto-slice" from explicit index? + let (bcx, base, len) = + datum.get_vec_base_and_len(bcx, expr.span, expr.id); // this type may have a different region/mutability than the // real one, but it will have the same runtime representation @@ -283,8 +285,10 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { DatumBlock {bcx: bcx, datum: scratch} } - fn auto_slice_and_ref(bcx: block, datum: Datum) -> DatumBlock { - let DatumBlock { bcx, datum } = auto_slice(bcx, datum); + fn auto_slice_and_ref(bcx: block, + expr: @ast::expr, + datum: Datum) -> DatumBlock { + let DatumBlock { bcx, datum } = auto_slice(bcx, expr, datum); auto_ref(bcx, datum) } } @@ -903,7 +907,8 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock { let scaled_ix = Mul(bcx, ix_val, vt.llunit_size); base::maybe_name_value(bcx.ccx(), scaled_ix, ~"scaled_ix"); - let mut (base, len) = base_datum.get_base_and_len(bcx); + let mut (bcx, base, len) = + base_datum.get_vec_base_and_len(bcx, index_expr.span, index_expr.id); if ty::type_is_str(base_ty) { // acccount for null terminator in the case of string diff --git a/src/test/run-fail/borrowck-wg-fail-2.rs b/src/test/run-fail/borrowck-wg-fail-2.rs index 121ec9c79216f..59a5fecd34003 100644 --- a/src/test/run-fail/borrowck-wg-fail-2.rs +++ b/src/test/run-fail/borrowck-wg-fail-2.rs @@ -1,5 +1,8 @@ // error-pattern:borrowed +// Test that write guards trigger when there is a write to a field +// of a frozen structure. + struct S { x: int } diff --git a/src/test/run-fail/borrowck-wg-fail-3.rs b/src/test/run-fail/borrowck-wg-fail-3.rs index 2b95cf3fe5fa9..ebff553aafbad 100644 --- a/src/test/run-fail/borrowck-wg-fail-3.rs +++ b/src/test/run-fail/borrowck-wg-fail-3.rs @@ -1,5 +1,8 @@ // error-pattern:borrowed +// Test that write guards trigger when there is a write to a directly +// frozen @mut box. + fn main() { let x = @mut 3; let y: &mut int = x; diff --git a/src/test/run-fail/borrowck-wg-fail.rs b/src/test/run-fail/borrowck-wg-fail.rs index fd2d36b895ada..939d802c21ca1 100644 --- a/src/test/run-fail/borrowck-wg-fail.rs +++ b/src/test/run-fail/borrowck-wg-fail.rs @@ -1,5 +1,8 @@ // error-pattern:borrowed +// Test that write guards trigger when mut box is frozen +// as part of argument coercion. + fn f(_x: &int, y: @mut int) { *y = 2; } diff --git a/src/test/run-fail/borrowck-wg-one-mut-one-imm-slice-method.rs b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slice-method.rs new file mode 100644 index 0000000000000..91df90f8b3ac9 --- /dev/null +++ b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slice-method.rs @@ -0,0 +1,37 @@ +// error-pattern:borrowed + +// Test that write guards trigger when there is a coercion to +// a slice on the receiver of a method. + +trait MyMutSlice { + fn my_mut_slice(self) -> Self; +} + +impl<'self, T> MyMutSlice for &'self mut [T] { + fn my_mut_slice(self) -> &'self mut [T] { + self + } +} + +trait MySlice { + fn my_slice(self) -> Self; +} + +impl<'self, T> MySlice for &'self [T] { + fn my_slice(self) -> &'self [T] { + self + } +} + +fn add(x:&mut [int], y:&[int]) +{ + x[0] = x[0] + y[0]; +} + +pub fn main() +{ + let z = @mut [1,2,3]; + let z2 = z; + add(z.my_mut_slice(), z2.my_slice()); + print(fmt!("%d\n", z[0])); +} diff --git a/src/test/run-fail/borrowck-wg-one-mut-one-imm-slices.rs b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slices.rs new file mode 100644 index 0000000000000..bae693ce4eae2 --- /dev/null +++ b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slices.rs @@ -0,0 +1,16 @@ +// error-pattern:borrowed + +// Test that write guards trigger when arguments are coerced to slices. + +fn add(x:&mut [int], y:&[int]) +{ + x[0] = x[0] + y[0]; +} + +pub fn main() +{ + let z = @mut [1,2,3]; + let z2 = z; + add(z, z2); + print(fmt!("%d\n", z[0])); +} diff --git a/src/test/run-fail/borrowck-wg-one-mut-one-imm.rs b/src/test/run-fail/borrowck-wg-one-mut-one-imm.rs new file mode 100644 index 0000000000000..9e2a02b32dfed --- /dev/null +++ b/src/test/run-fail/borrowck-wg-one-mut-one-imm.rs @@ -0,0 +1,17 @@ +// error-pattern:borrowed + +// Test that write guards trigger when we are indexing into +// an @mut vector. + +fn add(x:&mut int, y:&int) +{ + *x = *x + *y; +} + +pub fn main() +{ + let z = @mut [1,2,3]; + let z2 = z; + add(&mut z[0], &z2[0]); + print(fmt!("%d\n", z[0])); +} diff --git a/src/test/run-fail/borrowck-wg-two-array-indices.rs b/src/test/run-fail/borrowck-wg-two-array-indices.rs new file mode 100644 index 0000000000000..ad68448876028 --- /dev/null +++ b/src/test/run-fail/borrowck-wg-two-array-indices.rs @@ -0,0 +1,17 @@ +// error-pattern:borrowed + +// Test that arguments trigger when there are *two mutable* borrows +// of indices. + +fn add(x:&mut int, y:&mut int) +{ + *x = *x + *y; +} + +pub fn main() +{ + let z = @mut [1,2,3]; + let z2 = z; + add(&mut z[0], &mut z2[0]); + print(fmt!("%d\n", z[0])); +} diff --git a/src/test/run-pass/borrowck-wg-two-imm-borrows.rs b/src/test/run-pass/borrowck-wg-two-imm-borrows.rs new file mode 100644 index 0000000000000..20f824e969a48 --- /dev/null +++ b/src/test/run-pass/borrowck-wg-two-imm-borrows.rs @@ -0,0 +1,14 @@ +// Test that we can borrow the same @mut box twice, so long as both are imm. + +fn add(x:&int, y:&int) +{ + *x + *y; +} + +pub fn main() +{ + let z = @mut [1,2,3]; + let z2 = z; + add(&z[0], &z2[0]); + print(fmt!("%d\n", z[0])); +} From 0ff8200671c38e0068ba40267d02f99737e77cab Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 May 2013 22:07:33 -0400 Subject: [PATCH 31/46] factor code for write guards into its own module; add neglected resolve_stage0 --- src/librustc/middle/resolve_stage0.rs | 5313 ++++++++++++++++++++++ src/librustc/middle/trans/write_guard.rs | 201 + 2 files changed, 5514 insertions(+) create mode 100644 src/librustc/middle/resolve_stage0.rs create mode 100644 src/librustc/middle/trans/write_guard.rs diff --git a/src/librustc/middle/resolve_stage0.rs b/src/librustc/middle/resolve_stage0.rs new file mode 100644 index 0000000000000..ff46abaf7128c --- /dev/null +++ b/src/librustc/middle/resolve_stage0.rs @@ -0,0 +1,5313 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use driver::session; +use driver::session::Session; +use metadata::csearch::{each_path, get_trait_method_def_ids}; +use metadata::csearch::get_method_name_and_self_ty; +use metadata::csearch::get_static_methods_if_impl; +use metadata::csearch::get_type_name_if_impl; +use metadata::cstore::find_extern_mod_stmt_cnum; +use metadata::decoder::{def_like, dl_def, dl_field, dl_impl}; +use middle::lang_items::LanguageItems; +use middle::lint::{allow, level, unused_imports}; +use middle::lint::{get_lint_level, get_lint_settings_level}; +use middle::pat_util::pat_bindings; + +use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm}; +use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk}; +use syntax::ast::{bind_infer, bind_by_ref, bind_by_copy}; +use syntax::ast::{crate, decl_item, def, def_arg, def_binding}; +use syntax::ast::{def_const, def_foreign_mod, def_fn, def_id, def_label}; +use syntax::ast::{def_local, def_mod, def_prim_ty, def_region, def_self}; +use syntax::ast::{def_self_ty, def_static_method, def_struct, def_ty}; +use syntax::ast::{def_ty_param, def_typaram_binder, def_trait}; +use syntax::ast::{def_upvar, def_use, def_variant, expr, expr_assign_op}; +use syntax::ast::{expr_binary, expr_break, expr_field}; +use syntax::ast::{expr_fn_block, expr_index, expr_method_call, expr_path}; +use syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param}; +use syntax::ast::{def_upvar, def_use, def_variant, quot, eq}; +use syntax::ast::{expr, expr_again, expr_assign_op}; +use syntax::ast::{expr_index, expr_loop}; +use syntax::ast::{expr_path, expr_struct, expr_unary, fn_decl}; +use syntax::ast::{foreign_item, foreign_item_const, foreign_item_fn, ge}; +use syntax::ast::Generics; +use syntax::ast::{gt, ident, inherited, item, item_struct}; +use syntax::ast::{item_const, item_enum, item_fn, item_foreign_mod}; +use syntax::ast::{item_impl, item_mac, item_mod, item_trait, item_ty, le}; +use syntax::ast::{local, local_crate, lt, method, mul}; +use syntax::ast::{named_field, ne, neg, node_id, pat, pat_enum, pat_ident}; +use syntax::ast::{Path, pat_lit, pat_range, pat_struct}; +use syntax::ast::{prim_ty, private, provided}; +use syntax::ast::{public, required, rem, self_ty_, shl, shr, stmt_decl}; +use syntax::ast::{struct_dtor, struct_field, struct_variant_kind}; +use syntax::ast::{sty_static, subtract, trait_ref, tuple_variant_kind, Ty}; +use syntax::ast::{ty_bool, ty_char, ty_f, ty_f32, ty_f64, ty_float, ty_i}; +use syntax::ast::{ty_i16, ty_i32, ty_i64, ty_i8, ty_int, TyParam, ty_path}; +use syntax::ast::{ty_str, ty_u, ty_u16, ty_u32, ty_u64, ty_u8, ty_uint}; +use syntax::ast::unnamed_field; +use syntax::ast::{variant, view_item, view_item_extern_mod}; +use syntax::ast::{view_item_use, view_path_glob, view_path_list}; +use syntax::ast::{view_path_simple, anonymous, named, not}; +use syntax::ast::{unsafe_fn}; +use syntax::ast_util::{def_id_of_def, local_def}; +use syntax::ast_util::{path_to_ident, walk_pat, trait_method_to_ty_method}; +use syntax::ast_util::{Privacy, Public, Private}; +use syntax::ast_util::{variant_visibility_to_privacy, visibility_to_privacy}; +use syntax::attr::{attr_metas, contains_name, attrs_contains_name}; +use syntax::parse::token::ident_interner; +use syntax::parse::token::special_idents; +use syntax::print::pprust::path_to_str; +use syntax::codemap::{span, dummy_sp}; +use syntax::visit::{default_visitor, mk_vt, Visitor, visit_block}; +use syntax::visit::{visit_crate, visit_expr, visit_expr_opt}; +use syntax::visit::{visit_foreign_item, visit_item}; +use syntax::visit::{visit_mod, visit_ty, vt}; +use syntax::opt_vec::OptVec; + +use core::option::Some; +use core::str::each_split_str; +use core::hashmap::{HashMap, HashSet}; +use core::util; + +// Definition mapping +pub type DefMap = @mut HashMap; + +pub struct binding_info { + span: span, + binding_mode: binding_mode, +} + +// Map from the name in a pattern to its binding mode. +pub type BindingMap = HashMap; + +// Implementation resolution +// +// FIXME #4946: This kind of duplicates information kept in +// ty::method. Maybe it should go away. + +pub struct MethodInfo { + did: def_id, + n_tps: uint, + ident: ident, + self_type: self_ty_ +} + +pub struct Impl { + did: def_id, + ident: ident, + methods: ~[@MethodInfo] +} + +// Trait method resolution +pub type TraitMap = HashMap; + +// This is the replacement export map. It maps a module to all of the exports +// within. +pub type ExportMap2 = @mut HashMap; + +pub struct Export2 { + name: @~str, // The name of the target. + def_id: def_id, // The definition of the target. + reexport: bool, // Whether this is a reexport. +} + +#[deriving(Eq)] +pub enum PatternBindingMode { + RefutableMode, + LocalIrrefutableMode, + ArgumentIrrefutableMode, +} + +#[deriving(Eq)] +pub enum Namespace { + TypeNS, + ValueNS +} + +/// A NamespaceResult represents the result of resolving an import in +/// a particular namespace. The result is either definitely-resolved, +/// definitely- unresolved, or unknown. +pub enum NamespaceResult { + /// Means that resolve hasn't gathered enough information yet to determine + /// whether the name is bound in this namespace. (That is, it hasn't + /// resolved all `use` directives yet.) + UnknownResult, + /// Means that resolve has determined that the name is definitely + /// not bound in the namespace. + UnboundResult, + /// Means that resolve has determined that the name is bound in the Module + /// argument, and specified by the NameBindings argument. + BoundResult(@mut Module, @mut NameBindings) +} + +pub impl NamespaceResult { + fn is_unknown(&self) -> bool { + match *self { + UnknownResult => true, + _ => false + } + } +} + +pub enum NameDefinition { + NoNameDefinition, //< The name was unbound. + ChildNameDefinition(def), //< The name identifies an immediate child. + ImportNameDefinition(def) //< The name identifies an import. +} + +#[deriving(Eq)] +pub enum Mutability { + Mutable, + Immutable +} + +pub enum SelfBinding { + NoSelfBinding, + HasSelfBinding(node_id, bool /* is implicit */) +} + +pub type ResolveVisitor = vt<()>; + +/// Contains data for specific types of import directives. +pub enum ImportDirectiveSubclass { + SingleImport(ident /* target */, ident /* source */), + GlobImport +} + +/// The context that we thread through while building the reduced graph. +pub enum ReducedGraphParent { + ModuleReducedGraphParent(@mut Module) +} + +pub enum ResolveResult { + Failed, // Failed to resolve the name. + Indeterminate, // Couldn't determine due to unresolved globs. + Success(T) // Successfully resolved the import. +} + +pub impl ResolveResult { + fn failed(&self) -> bool { + match *self { Failed => true, _ => false } + } + fn indeterminate(&self) -> bool { + match *self { Indeterminate => true, _ => false } + } +} + +pub enum TypeParameters<'self> { + NoTypeParameters, //< No type parameters. + HasTypeParameters(&'self Generics, //< Type parameters. + node_id, //< ID of the enclosing item + + // The index to start numbering the type parameters at. + // This is zero if this is the outermost set of type + // parameters, or equal to the number of outer type + // parameters. For example, if we have: + // + // impl I { + // fn method() { ... } + // } + // + // The index at the method site will be 1, because the + // outer T had index 0. + uint, + + // The kind of the rib used for type parameters. + RibKind) +} + +// The rib kind controls the translation of argument or local definitions +// (`def_arg` or `def_local`) to upvars (`def_upvar`). + +pub enum RibKind { + // No translation needs to be applied. + NormalRibKind, + + // We passed through a function scope at the given node ID. Translate + // upvars as appropriate. + FunctionRibKind(node_id /* func id */, node_id /* body id */), + + // We passed through an impl or trait and are now in one of its + // methods. Allow references to ty params that that impl or trait + // binds. Disallow any other upvars (including other ty params that are + // upvars). + // parent; method itself + MethodRibKind(node_id, MethodSort), + + // We passed through a function *item* scope. Disallow upvars. + OpaqueFunctionRibKind, + + // We're in a constant item. Can't refer to dynamic stuff. + ConstantItemRibKind +} + +// Methods can be required or provided. Required methods only occur in traits. +pub enum MethodSort { + Required, + Provided(node_id) +} + +// The X-ray flag indicates that a context has the X-ray privilege, which +// allows it to reference private names. Currently, this is used for the test +// runner. +// +// FIXME #4947: The X-ray flag is kind of questionable in the first +// place. It might be better to introduce an expr_xray_path instead. + +#[deriving(Eq)] +pub enum XrayFlag { + NoXray, //< Private items cannot be accessed. + Xray //< Private items can be accessed. +} + +pub enum UseLexicalScopeFlag { + DontUseLexicalScope, + UseLexicalScope +} + +pub enum SearchThroughModulesFlag { + DontSearchThroughModules, + SearchThroughModules +} + +pub enum ModulePrefixResult { + NoPrefixFound, + PrefixFound(@mut Module, uint) +} + +#[deriving(Eq)] +pub enum AllowCapturingSelfFlag { + AllowCapturingSelf, //< The "self" definition can be captured. + DontAllowCapturingSelf, //< The "self" definition cannot be captured. +} + +#[deriving(Eq)] +enum NameSearchType { + SearchItemsAndPublicImports, //< Search items and public imports. + SearchItemsAndAllImports, //< Search items and all imports. +} + +pub enum BareIdentifierPatternResolution { + FoundStructOrEnumVariant(def), + FoundConst(def), + BareIdentifierPatternUnresolved +} + +// Specifies how duplicates should be handled when adding a child item if +// another item exists with the same name in some namespace. +#[deriving(Eq)] +pub enum DuplicateCheckingMode { + ForbidDuplicateModules, + ForbidDuplicateTypes, + ForbidDuplicateValues, + ForbidDuplicateTypesAndValues, + OverwriteDuplicates +} + +// Returns the namespace associated with the given duplicate checking mode, +// or fails for OverwriteDuplicates. This is used for error messages. +pub fn namespace_for_duplicate_checking_mode(mode: DuplicateCheckingMode) + -> Namespace { + match mode { + ForbidDuplicateModules | ForbidDuplicateTypes | + ForbidDuplicateTypesAndValues => TypeNS, + ForbidDuplicateValues => ValueNS, + OverwriteDuplicates => fail!(~"OverwriteDuplicates has no namespace") + } +} + +/// One local scope. +pub struct Rib { + bindings: @mut HashMap, + kind: RibKind, +} + +pub fn Rib(kind: RibKind) -> Rib { + Rib { + bindings: @mut HashMap::new(), + kind: kind + } +} + + +/// One import directive. +pub struct ImportDirective { + privacy: Privacy, + module_path: ~[ident], + subclass: @ImportDirectiveSubclass, + span: span, +} + +pub fn ImportDirective(privacy: Privacy, + module_path: ~[ident], + subclass: @ImportDirectiveSubclass, + span: span) + -> ImportDirective { + ImportDirective { + privacy: privacy, + module_path: module_path, + subclass: subclass, + span: span + } +} + +/// The item that an import resolves to. +pub struct Target { + target_module: @mut Module, + bindings: @mut NameBindings, +} + +pub fn Target(target_module: @mut Module, + bindings: @mut NameBindings) + -> Target { + Target { + target_module: target_module, + bindings: bindings + } +} + +/// An ImportResolution represents a particular `use` directive. +pub struct ImportResolution { + /// The privacy of this `use` directive (whether it's `use` or + /// `pub use`. + privacy: Privacy, + span: span, + + // The number of outstanding references to this name. When this reaches + // zero, outside modules can count on the targets being correct. Before + // then, all bets are off; future imports could override this name. + + outstanding_references: uint, + + /// The value that this `use` directive names, if there is one. + value_target: Option, + /// The type that this `use` directive names, if there is one. + type_target: Option, + + /// There exists one state per import statement + state: @mut ImportState, +} + +pub fn ImportResolution(privacy: Privacy, + span: span, + state: @mut ImportState) -> ImportResolution { + ImportResolution { + privacy: privacy, + span: span, + outstanding_references: 0, + value_target: None, + type_target: None, + state: state, + } +} + +pub impl ImportResolution { + fn target_for_namespace(&self, namespace: Namespace) -> Option { + match namespace { + TypeNS => return copy self.type_target, + ValueNS => return copy self.value_target + } + } +} + +pub struct ImportState { + used: bool, + warned: bool +} + +pub fn ImportState() -> ImportState { + ImportState{ used: false, warned: false } +} + +/// The link from a module up to its nearest parent node. +pub enum ParentLink { + NoParentLink, + ModuleParentLink(@mut Module, ident), + BlockParentLink(@mut Module, node_id) +} + +/// The type of module this is. +pub enum ModuleKind { + NormalModuleKind, + ExternModuleKind, + TraitModuleKind, + AnonymousModuleKind, +} + +/// One node in the tree of modules. +pub struct Module { + parent_link: ParentLink, + def_id: Option, + kind: ModuleKind, + + children: @mut HashMap, + imports: @mut ~[@ImportDirective], + + // The external module children of this node that were declared with + // `extern mod`. + external_module_children: @mut HashMap, + + // The anonymous children of this node. Anonymous children are pseudo- + // modules that are implicitly created around items contained within + // blocks. + // + // For example, if we have this: + // + // fn f() { + // fn g() { + // ... + // } + // } + // + // There will be an anonymous module created around `g` with the ID of the + // entry block for `f`. + + anonymous_children: @mut HashMap, + + // The status of resolving each import in this module. + import_resolutions: @mut HashMap, + + // The number of unresolved globs that this module exports. + glob_count: uint, + + // The index of the import we're resolving. + resolved_import_count: uint, +} + +pub fn Module(parent_link: ParentLink, + def_id: Option, + kind: ModuleKind) + -> Module { + Module { + parent_link: parent_link, + def_id: def_id, + kind: kind, + children: @mut HashMap::new(), + imports: @mut ~[], + external_module_children: @mut HashMap::new(), + anonymous_children: @mut HashMap::new(), + import_resolutions: @mut HashMap::new(), + glob_count: 0, + resolved_import_count: 0 + } +} + +pub impl Module { + fn all_imports_resolved(&self) -> bool { + let imports = &mut *self.imports; + return imports.len() == self.resolved_import_count; + } +} + +// Records a possibly-private type definition. +pub struct TypeNsDef { + privacy: Privacy, + module_def: Option<@mut Module>, + type_def: Option +} + +// Records a possibly-private value definition. +pub struct ValueNsDef { + privacy: Privacy, + def: def, +} + +// Records the definitions (at most one for each namespace) that a name is +// bound to. +pub struct NameBindings { + type_def: Option, //< Meaning in type namespace. + value_def: Option, //< Meaning in value namespace. + + // For error reporting + // FIXME (#3783): Merge me into TypeNsDef and ValueNsDef. + type_span: Option, + value_span: Option, +} + +pub impl NameBindings { + /// Creates a new module in this set of name bindings. + fn define_module(@mut self, + privacy: Privacy, + parent_link: ParentLink, + def_id: Option, + kind: ModuleKind, + sp: span) { + // Merges the module with the existing type def or creates a new one. + let module_ = @mut Module(parent_link, def_id, kind); + match self.type_def { + None => { + self.type_def = Some(TypeNsDef { + privacy: privacy, + module_def: Some(module_), + type_def: None + }); + } + Some(copy type_def) => { + self.type_def = Some(TypeNsDef { + privacy: privacy, + module_def: Some(module_), + .. type_def + }); + } + } + self.type_span = Some(sp); + } + + /// Records a type definition. + fn define_type(@mut self, privacy: Privacy, def: def, sp: span) { + // Merges the type with the existing type def or creates a new one. + match self.type_def { + None => { + self.type_def = Some(TypeNsDef { + privacy: privacy, + module_def: None, + type_def: Some(def) + }); + } + Some(copy type_def) => { + self.type_def = Some(TypeNsDef { + privacy: privacy, + type_def: Some(def), + .. type_def + }); + } + } + self.type_span = Some(sp); + } + + /// Records a value definition. + fn define_value(@mut self, privacy: Privacy, def: def, sp: span) { + self.value_def = Some(ValueNsDef { privacy: privacy, def: def }); + self.value_span = Some(sp); + } + + /// Returns the module node if applicable. + fn get_module_if_available(&self) -> Option<@mut Module> { + match self.type_def { + Some(ref type_def) => (*type_def).module_def, + None => None + } + } + + /** + * Returns the module node. Fails if this node does not have a module + * definition. + */ + fn get_module(@mut self) -> @mut Module { + match self.get_module_if_available() { + None => { + fail!(~"get_module called on a node with no module \ + definition!") + } + Some(module_def) => module_def + } + } + + fn defined_in_namespace(&self, namespace: Namespace) -> bool { + match namespace { + TypeNS => return self.type_def.is_some(), + ValueNS => return self.value_def.is_some() + } + } + + fn defined_in_public_namespace(&self, namespace: Namespace) -> bool { + match namespace { + TypeNS => match self.type_def { + Some(def) => def.privacy != Private, + None => false + }, + ValueNS => match self.value_def { + Some(def) => def.privacy != Private, + None => false + } + } + } + + fn def_for_namespace(&self, namespace: Namespace) -> Option { + match namespace { + TypeNS => { + match self.type_def { + None => None, + Some(ref type_def) => { + // FIXME (#3784): This is reallllly questionable. + // Perhaps the right thing to do is to merge def_mod + // and def_ty. + match (*type_def).type_def { + Some(type_def) => Some(type_def), + None => { + match (*type_def).module_def { + Some(module_def) => { + let module_def = &mut *module_def; + module_def.def_id.map(|def_id| + def_mod(*def_id)) + } + None => None + } + } + } + } + } + } + ValueNS => { + match self.value_def { + None => None, + Some(value_def) => Some(value_def.def) + } + } + } + } + + fn privacy_for_namespace(&self, namespace: Namespace) -> Option { + match namespace { + TypeNS => { + match self.type_def { + None => None, + Some(ref type_def) => Some((*type_def).privacy) + } + } + ValueNS => { + match self.value_def { + None => None, + Some(value_def) => Some(value_def.privacy) + } + } + } + } + + fn span_for_namespace(&self, namespace: Namespace) -> Option { + if self.defined_in_namespace(namespace) { + match namespace { + TypeNS => self.type_span, + ValueNS => self.value_span, + } + } else { + None + } + } +} + +pub fn NameBindings() -> NameBindings { + NameBindings { + type_def: None, + value_def: None, + type_span: None, + value_span: None + } +} + +/// Interns the names of the primitive types. +pub struct PrimitiveTypeTable { + primitive_types: HashMap, +} + +pub impl PrimitiveTypeTable { + fn intern(&mut self, intr: @ident_interner, string: @~str, + primitive_type: prim_ty) { + let ident = intr.intern(string); + self.primitive_types.insert(ident, primitive_type); + } +} + +pub fn PrimitiveTypeTable(intr: @ident_interner) -> PrimitiveTypeTable { + let mut table = PrimitiveTypeTable { + primitive_types: HashMap::new() + }; + + table.intern(intr, @~"bool", ty_bool); + table.intern(intr, @~"char", ty_int(ty_char)); + table.intern(intr, @~"float", ty_float(ty_f)); + table.intern(intr, @~"f32", ty_float(ty_f32)); + table.intern(intr, @~"f64", ty_float(ty_f64)); + table.intern(intr, @~"int", ty_int(ty_i)); + table.intern(intr, @~"i8", ty_int(ty_i8)); + table.intern(intr, @~"i16", ty_int(ty_i16)); + table.intern(intr, @~"i32", ty_int(ty_i32)); + table.intern(intr, @~"i64", ty_int(ty_i64)); + table.intern(intr, @~"str", ty_str); + table.intern(intr, @~"uint", ty_uint(ty_u)); + table.intern(intr, @~"u8", ty_uint(ty_u8)); + table.intern(intr, @~"u16", ty_uint(ty_u16)); + table.intern(intr, @~"u32", ty_uint(ty_u32)); + table.intern(intr, @~"u64", ty_uint(ty_u64)); + + return table; +} + + +pub fn namespace_to_str(ns: Namespace) -> ~str { + match ns { + TypeNS => ~"type", + ValueNS => ~"value", + } +} + +pub fn Resolver(session: Session, + lang_items: LanguageItems, + crate: @crate) + -> Resolver { + let graph_root = @mut NameBindings(); + + graph_root.define_module(Public, + NoParentLink, + Some(def_id { crate: 0, node: 0 }), + NormalModuleKind, + crate.span); + + let current_module = graph_root.get_module(); + + let self = Resolver { + session: @session, + lang_items: copy lang_items, + crate: crate, + + // The outermost module has def ID 0; this is not reflected in the + // AST. + + graph_root: graph_root, + + trait_info: HashMap::new(), + structs: HashSet::new(), + + unresolved_imports: 0, + + current_module: current_module, + value_ribs: ~[], + type_ribs: ~[], + label_ribs: ~[], + + xray_context: NoXray, + current_trait_refs: None, + + self_ident: special_idents::self_, + type_self_ident: special_idents::type_self, + + primitive_type_table: @PrimitiveTypeTable(session. + parse_sess.interner), + + namespaces: ~[ TypeNS, ValueNS ], + + attr_main_fn: None, + main_fns: ~[], + + start_fn: None, + + def_map: @mut HashMap::new(), + export_map2: @mut HashMap::new(), + trait_map: HashMap::new(), + + intr: session.intr() + }; + + self +} + +/// The main resolver class. +pub struct Resolver { + session: @Session, + lang_items: LanguageItems, + crate: @crate, + + intr: @ident_interner, + + graph_root: @mut NameBindings, + + trait_info: HashMap>, + structs: HashSet, + + // The number of imports that are currently unresolved. + unresolved_imports: uint, + + // The module that represents the current item scope. + current_module: @mut Module, + + // The current set of local scopes, for values. + // FIXME #4948: Reuse ribs to avoid allocation. + value_ribs: ~[@Rib], + + // The current set of local scopes, for types. + type_ribs: ~[@Rib], + + // The current set of local scopes, for labels. + label_ribs: ~[@Rib], + + // Whether the current context is an X-ray context. An X-ray context is + // allowed to access private names of any module. + xray_context: XrayFlag, + + // The trait that the current context can refer to. + current_trait_refs: Option<~[def_id]>, + + // The ident for the keyword "self". + self_ident: ident, + // The ident for the non-keyword "Self". + type_self_ident: ident, + + // The idents for the primitive types. + primitive_type_table: @PrimitiveTypeTable, + + // The four namespaces. + namespaces: ~[Namespace], + + // The function that has attribute named 'main' + attr_main_fn: Option<(node_id, span)>, + + // The functions that could be main functions + main_fns: ~[Option<(node_id, span)>], + + // The function that has the attribute 'start' on it + start_fn: Option<(node_id, span)>, + + def_map: DefMap, + export_map2: ExportMap2, + trait_map: TraitMap, +} + +pub impl Resolver { + /// The main name resolution procedure. + fn resolve(@mut self) { + self.build_reduced_graph(); + self.session.abort_if_errors(); + + self.resolve_imports(); + self.session.abort_if_errors(); + + self.record_exports(); + self.session.abort_if_errors(); + + self.resolve_crate(); + self.session.abort_if_errors(); + + self.check_duplicate_main(); + self.check_for_unused_imports_if_necessary(); + } + + // + // Reduced graph building + // + // Here we build the "reduced graph": the graph of the module tree without + // any imports resolved. + // + + /// Constructs the reduced graph for the entire crate. + fn build_reduced_graph(@mut self) { + let initial_parent = + ModuleReducedGraphParent(self.graph_root.get_module()); + visit_crate(self.crate, initial_parent, mk_vt(@Visitor { + visit_item: |item, context, visitor| + self.build_reduced_graph_for_item(item, context, visitor), + + visit_foreign_item: |foreign_item, context, visitor| + self.build_reduced_graph_for_foreign_item(foreign_item, + context, + visitor), + + visit_view_item: |view_item, context, visitor| + self.build_reduced_graph_for_view_item(view_item, + context, + visitor), + + visit_block: |block, context, visitor| + self.build_reduced_graph_for_block(block, + context, + visitor), + + .. *default_visitor() + })); + } + + /// Returns the current module tracked by the reduced graph parent. + fn get_module_from_parent(@mut self, + reduced_graph_parent: ReducedGraphParent) + -> @mut Module { + match reduced_graph_parent { + ModuleReducedGraphParent(module_) => { + return module_; + } + } + } + + /** + * Adds a new child item to the module definition of the parent node and + * returns its corresponding name bindings as well as the current parent. + * Or, if we're inside a block, creates (or reuses) an anonymous module + * corresponding to the innermost block ID and returns the name bindings + * as well as the newly-created parent. + * + * If this node does not have a module definition and we are not inside + * a block, fails. + */ + fn add_child(@mut self, + name: ident, + reduced_graph_parent: ReducedGraphParent, + duplicate_checking_mode: DuplicateCheckingMode, + // For printing errors + sp: span) + -> (@mut NameBindings, ReducedGraphParent) { + + // If this is the immediate descendant of a module, then we add the + // child name directly. Otherwise, we create or reuse an anonymous + // module and add the child to that. + + let module_; + match reduced_graph_parent { + ModuleReducedGraphParent(parent_module) => { + module_ = parent_module; + } + } + + // Add or reuse the child. + let new_parent = ModuleReducedGraphParent(module_); + match module_.children.find(&name) { + None => { + let child = @mut NameBindings(); + module_.children.insert(name, child); + return (child, new_parent); + } + Some(&child) => { + // Enforce the duplicate checking mode: + // + // * If we're requesting duplicate module checking, check that + // there isn't a module in the module with the same name. + // + // * If we're requesting duplicate type checking, check that + // there isn't a type in the module with the same name. + // + // * If we're requesting duplicate value checking, check that + // there isn't a value in the module with the same name. + // + // * If we're requesting duplicate type checking and duplicate + // value checking, check that there isn't a duplicate type + // and a duplicate value with the same name. + // + // * If no duplicate checking was requested at all, do + // nothing. + + let mut is_duplicate = false; + match duplicate_checking_mode { + ForbidDuplicateModules => { + is_duplicate = + child.get_module_if_available().is_some(); + } + ForbidDuplicateTypes => { + match child.def_for_namespace(TypeNS) { + Some(def_mod(_)) | None => {} + Some(_) => is_duplicate = true + } + } + ForbidDuplicateValues => { + is_duplicate = child.defined_in_namespace(ValueNS); + } + ForbidDuplicateTypesAndValues => { + match child.def_for_namespace(TypeNS) { + Some(def_mod(_)) | None => {} + Some(_) => is_duplicate = true + }; + if child.defined_in_namespace(ValueNS) { + is_duplicate = true; + } + } + OverwriteDuplicates => {} + } + if duplicate_checking_mode != OverwriteDuplicates && + is_duplicate { + // Return an error here by looking up the namespace that + // had the duplicate. + let ns = namespace_for_duplicate_checking_mode( + duplicate_checking_mode); + self.session.span_err(sp, + fmt!("duplicate definition of %s %s", + namespace_to_str(ns), + *self.session.str_of(name))); + for child.span_for_namespace(ns).each |sp| { + self.session.span_note(*sp, + fmt!("first definition of %s %s here:", + namespace_to_str(ns), + *self.session.str_of(name))); + } + } + return (child, new_parent); + } + } + } + + fn block_needs_anonymous_module(@mut self, block: &blk) -> bool { + // If the block has view items, we need an anonymous module. + if block.node.view_items.len() > 0 { + return true; + } + + // Check each statement. + for block.node.stmts.each |statement| { + match statement.node { + stmt_decl(declaration, _) => { + match declaration.node { + decl_item(_) => { + return true; + } + _ => { + // Keep searching. + } + } + } + _ => { + // Keep searching. + } + } + } + + // If we found neither view items nor items, we don't need to create + // an anonymous module. + + return false; + } + + fn get_parent_link(@mut self, + parent: ReducedGraphParent, + name: ident) + -> ParentLink { + match parent { + ModuleReducedGraphParent(module_) => { + return ModuleParentLink(module_, name); + } + } + } + + /// Constructs the reduced graph for one item. + fn build_reduced_graph_for_item(@mut self, + item: @item, + parent: ReducedGraphParent, + visitor: vt) { + let ident = item.ident; + let sp = item.span; + let privacy = visibility_to_privacy(item.vis); + + match item.node { + item_mod(ref module_) => { + let (name_bindings, new_parent) = + self.add_child(ident, parent, ForbidDuplicateModules, sp); + + let parent_link = self.get_parent_link(new_parent, ident); + let def_id = def_id { crate: 0, node: item.id }; + name_bindings.define_module(privacy, + parent_link, + Some(def_id), + NormalModuleKind, + sp); + + let new_parent = + ModuleReducedGraphParent(name_bindings.get_module()); + + visit_mod(module_, sp, item.id, new_parent, visitor); + } + + item_foreign_mod(ref fm) => { + let new_parent = match fm.sort { + named => { + let (name_bindings, new_parent) = + self.add_child(ident, parent, + ForbidDuplicateModules, sp); + + let parent_link = self.get_parent_link(new_parent, + ident); + let def_id = def_id { crate: 0, node: item.id }; + name_bindings.define_module(privacy, + parent_link, + Some(def_id), + ExternModuleKind, + sp); + + ModuleReducedGraphParent(name_bindings.get_module()) + } + + // For anon foreign mods, the contents just go in the + // current scope + anonymous => parent + }; + + visit_item(item, new_parent, visitor); + } + + // These items live in the value namespace. + item_const(*) => { + let (name_bindings, _) = + self.add_child(ident, parent, ForbidDuplicateValues, sp); + + name_bindings.define_value + (privacy, def_const(local_def(item.id)), sp); + } + item_fn(_, purity, _, _, _) => { + let (name_bindings, new_parent) = + self.add_child(ident, parent, ForbidDuplicateValues, sp); + + let def = def_fn(local_def(item.id), purity); + name_bindings.define_value(privacy, def, sp); + visit_item(item, new_parent, visitor); + } + + // These items live in the type namespace. + item_ty(*) => { + let (name_bindings, _) = + self.add_child(ident, parent, ForbidDuplicateTypes, sp); + + name_bindings.define_type + (privacy, def_ty(local_def(item.id)), sp); + } + + item_enum(ref enum_definition, _) => { + let (name_bindings, new_parent) = + self.add_child(ident, parent, ForbidDuplicateTypes, sp); + + name_bindings.define_type + (privacy, def_ty(local_def(item.id)), sp); + + for (*enum_definition).variants.each |variant| { + self.build_reduced_graph_for_variant(variant, + local_def(item.id), + // inherited => privacy of the enum item + variant_visibility_to_privacy(variant.node.vis, + privacy == Public), + new_parent, + visitor); + } + } + + // These items live in both the type and value namespaces. + item_struct(struct_def, _) => { + let (name_bindings, new_parent) = + self.add_child(ident, parent, ForbidDuplicateTypes, sp); + + name_bindings.define_type( + privacy, def_ty(local_def(item.id)), sp); + + // If this struct is tuple-like or enum-like, define a name + // in the value namespace. + match struct_def.ctor_id { + None => {} + Some(ctor_id) => { + name_bindings.define_value( + privacy, + def_struct(local_def(ctor_id)), + sp); + } + } + + // Record the def ID of this struct. + self.structs.insert(local_def(item.id)); + + visit_item(item, new_parent, visitor); + } + + item_impl(_, trait_ref_opt, ty, ref methods) => { + // If this implements an anonymous trait and it has static + // methods, then add all the static methods within to a new + // module, if the type was defined within this module. + // + // FIXME (#3785): This is quite unsatisfactory. Perhaps we + // should modify anonymous traits to only be implementable in + // the same module that declared the type. + + // Bail out early if there are no static methods. + let mut has_static_methods = false; + for methods.each |method| { + match method.self_ty.node { + sty_static => has_static_methods = true, + _ => {} + } + } + + // If there are static methods, then create the module + // and add them. + match (trait_ref_opt, ty) { + (None, @Ty { node: ty_path(path, _), _ }) if + has_static_methods && path.idents.len() == 1 => { + // Create the module. + let name = path_to_ident(path); + let (name_bindings, new_parent) = + self.add_child(name, + parent, + ForbidDuplicateModules, + sp); + + let parent_link = self.get_parent_link(new_parent, + ident); + let def_id = local_def(item.id); + name_bindings.define_module(Public, + parent_link, + Some(def_id), + TraitModuleKind, + sp); + + let new_parent = ModuleReducedGraphParent( + name_bindings.get_module()); + + // For each static method... + for methods.each |method| { + match method.self_ty.node { + sty_static => { + // Add the static method to the + // module. + let ident = method.ident; + let (method_name_bindings, _) = + self.add_child( + ident, + new_parent, + ForbidDuplicateValues, + method.span); + let def = def_fn(local_def(method.id), + method.purity); + method_name_bindings.define_value( + Public, def, method.span); + } + _ => {} + } + } + } + _ => {} + } + + visit_item(item, parent, visitor); + } + + item_trait(_, _, ref methods) => { + let (name_bindings, new_parent) = + self.add_child(ident, parent, ForbidDuplicateTypes, sp); + + // If the trait has static methods, then add all the static + // methods within to a new module. + // + // We only need to create the module if the trait has static + // methods, so check that first. + let mut has_static_methods = false; + for (*methods).each |method| { + let ty_m = trait_method_to_ty_method(method); + match ty_m.self_ty.node { + sty_static => { + has_static_methods = true; + break; + } + _ => {} + } + } + + // Create the module if necessary. + let module_parent_opt; + if has_static_methods { + let parent_link = self.get_parent_link(parent, ident); + name_bindings.define_module(privacy, + parent_link, + Some(local_def(item.id)), + TraitModuleKind, + sp); + module_parent_opt = Some(ModuleReducedGraphParent( + name_bindings.get_module())); + } else { + module_parent_opt = None; + } + + // Add the names of all the methods to the trait info. + let mut method_names = HashSet::new(); + for methods.each |method| { + let ty_m = trait_method_to_ty_method(method); + + let ident = ty_m.ident; + // Add it to the trait info if not static, + // add it as a name in the trait module otherwise. + match ty_m.self_ty.node { + sty_static => { + let def = def_static_method( + local_def(ty_m.id), + Some(local_def(item.id)), + ty_m.purity); + + let (method_name_bindings, _) = + self.add_child(ident, + module_parent_opt.get(), + ForbidDuplicateValues, + ty_m.span); + method_name_bindings.define_value(Public, + def, + ty_m.span); + } + _ => { + method_names.insert(ident); + } + } + } + + let def_id = local_def(item.id); + self.trait_info.insert(def_id, method_names); + + name_bindings.define_type(privacy, def_trait(def_id), sp); + visit_item(item, new_parent, visitor); + } + + item_mac(*) => { + fail!(~"item macros unimplemented") + } + } + } + + // Constructs the reduced graph for one variant. Variants exist in the + // type and/or value namespaces. + fn build_reduced_graph_for_variant(@mut self, + variant: &variant, + item_id: def_id, + parent_privacy: Privacy, + parent: ReducedGraphParent, + _visitor: vt) { + let ident = variant.node.name; + let (child, _) = self.add_child(ident, parent, ForbidDuplicateValues, + variant.span); + + let privacy; + match variant.node.vis { + public => privacy = Public, + private => privacy = Private, + inherited => privacy = parent_privacy + } + + match variant.node.kind { + tuple_variant_kind(_) => { + child.define_value(privacy, + def_variant(item_id, + local_def(variant.node.id)), + variant.span); + } + struct_variant_kind(_) => { + child.define_type(privacy, + def_variant(item_id, + local_def(variant.node.id)), + variant.span); + self.structs.insert(local_def(variant.node.id)); + } + } + } + + /** + * Constructs the reduced graph for one 'view item'. View items consist + * of imports and use directives. + */ + fn build_reduced_graph_for_view_item(@mut self, + view_item: @view_item, + parent: ReducedGraphParent, + _visitor: vt) { + let privacy = visibility_to_privacy(view_item.vis); + match view_item.node { + view_item_use(ref view_paths) => { + for view_paths.each |view_path| { + // Extract and intern the module part of the path. For + // globs and lists, the path is found directly in the AST; + // for simple paths we have to munge the path a little. + + let mut module_path = ~[]; + match view_path.node { + view_path_simple(_, full_path, _) => { + let path_len = full_path.idents.len(); + assert!(path_len != 0); + + for full_path.idents.eachi |i, ident| { + if i != path_len - 1 { + module_path.push(*ident); + } + } + } + + view_path_glob(module_ident_path, _) | + view_path_list(module_ident_path, _, _) => { + for module_ident_path.idents.each |ident| { + module_path.push(*ident); + } + } + } + + // Build up the import directives. + let module_ = self.get_module_from_parent(parent); + match view_path.node { + view_path_simple(binding, full_path, _) => { + let source_ident = *full_path.idents.last(); + let subclass = @SingleImport(binding, + source_ident); + self.build_import_directive(privacy, + module_, + module_path, + subclass, + view_path.span); + } + view_path_list(_, ref source_idents, _) => { + for source_idents.each |source_ident| { + let name = source_ident.node.name; + let subclass = @SingleImport(name, name); + self.build_import_directive(privacy, + module_, + copy module_path, + subclass, + source_ident.span); + } + } + view_path_glob(_, _) => { + self.build_import_directive(privacy, + module_, + module_path, + @GlobImport, + view_path.span); + } + } + } + } + + view_item_extern_mod(name, _, node_id) => { + match find_extern_mod_stmt_cnum(self.session.cstore, + node_id) { + Some(crate_id) => { + let def_id = def_id { crate: crate_id, node: 0 }; + let parent_link = ModuleParentLink + (self.get_module_from_parent(parent), name); + let external_module = @mut Module(parent_link, + Some(def_id), + NormalModuleKind); + + parent.external_module_children.insert( + name, + external_module); + + self.build_reduced_graph_for_external_crate( + external_module); + } + None => {} // Ignore. + } + } + } + } + + /// Constructs the reduced graph for one foreign item. + fn build_reduced_graph_for_foreign_item(@mut self, + foreign_item: @foreign_item, + parent: ReducedGraphParent, + visitor: + vt) { + let name = foreign_item.ident; + let (name_bindings, new_parent) = + self.add_child(name, parent, ForbidDuplicateValues, + foreign_item.span); + + match foreign_item.node { + foreign_item_fn(_, _, ref generics) => { + let def = def_fn(local_def(foreign_item.id), unsafe_fn); + name_bindings.define_value(Public, def, foreign_item.span); + + do self.with_type_parameter_rib( + HasTypeParameters( + generics, foreign_item.id, 0, NormalRibKind)) + { + visit_foreign_item(foreign_item, new_parent, visitor); + } + } + foreign_item_const(*) => { + let def = def_const(local_def(foreign_item.id)); + name_bindings.define_value(Public, def, foreign_item.span); + + visit_foreign_item(foreign_item, new_parent, visitor); + } + } + } + + fn build_reduced_graph_for_block(@mut self, + block: &blk, + parent: ReducedGraphParent, + visitor: vt) { + let new_parent; + if self.block_needs_anonymous_module(block) { + let block_id = block.node.id; + + debug!("(building reduced graph for block) creating a new \ + anonymous module for block %d", + block_id); + + let parent_module = self.get_module_from_parent(parent); + let new_module = @mut Module( + BlockParentLink(parent_module, block_id), + None, + AnonymousModuleKind); + parent_module.anonymous_children.insert(block_id, new_module); + new_parent = ModuleReducedGraphParent(new_module); + } else { + new_parent = parent; + } + + visit_block(block, new_parent, visitor); + } + + fn handle_external_def(@mut self, + def: def, + modules: &mut HashMap, + child_name_bindings: @mut NameBindings, + final_ident: &str, + ident: ident, + new_parent: ReducedGraphParent) { + match def { + def_mod(def_id) | def_foreign_mod(def_id) => { + match child_name_bindings.type_def { + Some(TypeNsDef { module_def: Some(copy module_def), _ }) => { + debug!("(building reduced graph for external crate) \ + already created module"); + module_def.def_id = Some(def_id); + modules.insert(def_id, module_def); + } + Some(_) | None => { + debug!("(building reduced graph for \ + external crate) building module \ + %s", final_ident); + let parent_link = self.get_parent_link(new_parent, ident); + + // FIXME (#5074): this should be a match on find + if !modules.contains_key(&def_id) { + child_name_bindings.define_module(Public, + parent_link, + Some(def_id), + NormalModuleKind, + dummy_sp()); + modules.insert(def_id, + child_name_bindings.get_module()); + } else { + let existing_module = *modules.get(&def_id); + // Create an import resolution to + // avoid creating cycles in the + // module graph. + + let resolution = + @mut ImportResolution(Public, + dummy_sp(), + @mut ImportState()); + resolution.outstanding_references = 0; + + match existing_module.parent_link { + NoParentLink | + BlockParentLink(*) => { + fail!(~"can't happen"); + } + ModuleParentLink(parent_module, ident) => { + let name_bindings = parent_module.children.get( + &ident); + resolution.type_target = + Some(Target(parent_module, *name_bindings)); + } + } + + debug!("(building reduced graph for external crate) \ + ... creating import resolution"); + + new_parent.import_resolutions.insert(ident, resolution); + } + } + } + } + def_fn(*) | def_static_method(*) | def_const(*) | + def_variant(*) => { + debug!("(building reduced graph for external \ + crate) building value %s", final_ident); + child_name_bindings.define_value(Public, def, dummy_sp()); + } + def_trait(def_id) => { + debug!("(building reduced graph for external \ + crate) building type %s", final_ident); + + // If this is a trait, add all the method names + // to the trait info. + + let method_def_ids = get_trait_method_def_ids(self.session.cstore, + def_id); + let mut interned_method_names = HashSet::new(); + for method_def_ids.each |&method_def_id| { + let (method_name, self_ty) = + get_method_name_and_self_ty(self.session.cstore, + method_def_id); + + debug!("(building reduced graph for \ + external crate) ... adding \ + trait method '%s'", + *self.session.str_of(method_name)); + + // Add it to the trait info if not static. + if self_ty != sty_static { + interned_method_names.insert(method_name); + } + } + self.trait_info.insert(def_id, interned_method_names); + + child_name_bindings.define_type(Public, def, dummy_sp()); + } + def_ty(_) => { + debug!("(building reduced graph for external \ + crate) building type %s", final_ident); + + child_name_bindings.define_type(Public, def, dummy_sp()); + } + def_struct(def_id) => { + debug!("(building reduced graph for external \ + crate) building type %s", + final_ident); + child_name_bindings.define_type(Public, def, dummy_sp()); + self.structs.insert(def_id); + } + def_self(*) | def_arg(*) | def_local(*) | + def_prim_ty(*) | def_ty_param(*) | def_binding(*) | + def_use(*) | def_upvar(*) | def_region(*) | + def_typaram_binder(*) | def_label(*) | def_self_ty(*) => { + fail!(fmt!("didn't expect `%?`", def)); + } + } + } + + /** + * Builds the reduced graph rooted at the 'use' directive for an external + * crate. + */ + fn build_reduced_graph_for_external_crate(@mut self, root: @mut Module) { + let mut modules = HashMap::new(); + + // Create all the items reachable by paths. + for each_path(self.session.cstore, root.def_id.get().crate) + |path_string, def_like| { + + debug!("(building reduced graph for external crate) found path \ + entry: %s (%?)", + path_string, def_like); + + let mut pieces = ~[]; + for each_split_str(path_string, "::") |s| { pieces.push(s.to_owned()) } + let final_ident_str = pieces.pop(); + let final_ident = self.session.ident_of(final_ident_str); + + // Find the module we need, creating modules along the way if we + // need to. + + let mut current_module = root; + for pieces.each |ident_str| { + let ident = self.session.ident_of(/*bad*/copy *ident_str); + // Create or reuse a graph node for the child. + let (child_name_bindings, new_parent) = + self.add_child(ident, + ModuleReducedGraphParent(current_module), + OverwriteDuplicates, + dummy_sp()); + + // Define or reuse the module node. + match child_name_bindings.type_def { + None => { + debug!("(building reduced graph for external crate) \ + autovivifying missing type def %s", + *ident_str); + let parent_link = self.get_parent_link(new_parent, + ident); + child_name_bindings.define_module(Public, + parent_link, + None, + NormalModuleKind, + dummy_sp()); + } + Some(copy type_ns_def) + if type_ns_def.module_def.is_none() => { + debug!("(building reduced graph for external crate) \ + autovivifying missing module def %s", + *ident_str); + let parent_link = self.get_parent_link(new_parent, + ident); + child_name_bindings.define_module(Public, + parent_link, + None, + NormalModuleKind, + dummy_sp()); + } + _ => {} // Fall through. + } + + current_module = child_name_bindings.get_module(); + } + + match def_like { + dl_def(def) => { + // Add the new child item. + let (child_name_bindings, new_parent) = + self.add_child(final_ident, + ModuleReducedGraphParent( + current_module), + OverwriteDuplicates, + dummy_sp()); + + self.handle_external_def(def, + &mut modules, + child_name_bindings, + *self.session.str_of( + final_ident), + final_ident, + new_parent); + } + dl_impl(def) => { + // We only process static methods of impls here. + match get_type_name_if_impl(self.session.cstore, def) { + None => {} + Some(final_ident) => { + let static_methods_opt = + get_static_methods_if_impl( + self.session.cstore, def); + match static_methods_opt { + Some(ref static_methods) if + static_methods.len() >= 1 => { + debug!("(building reduced graph for \ + external crate) processing \ + static methods for type name %s", + *self.session.str_of( + final_ident)); + + let (child_name_bindings, new_parent) = + self.add_child(final_ident, + ModuleReducedGraphParent( + current_module), + OverwriteDuplicates, + dummy_sp()); + + // Process the static methods. First, + // create the module. + let type_module; + match child_name_bindings.type_def { + Some(TypeNsDef { + module_def: Some(copy module_def), + _ + }) => { + // We already have a module. This + // is OK. + type_module = module_def; + } + Some(_) | None => { + let parent_link = + self.get_parent_link( + new_parent, final_ident); + child_name_bindings.define_module( + Public, + parent_link, + Some(def), + NormalModuleKind, + dummy_sp()); + type_module = + child_name_bindings. + get_module(); + } + } + + // Add each static method to the module. + let new_parent = ModuleReducedGraphParent( + type_module); + for static_methods.each + |static_method_info| { + let ident = static_method_info.ident; + debug!("(building reduced graph for \ + external crate) creating \ + static method '%s'", + *self.session.str_of(ident)); + + let (method_name_bindings, _) = + self.add_child( + ident, + new_parent, + OverwriteDuplicates, + dummy_sp()); + let def = def_fn( + static_method_info.def_id, + static_method_info.purity); + method_name_bindings.define_value( + Public, def, dummy_sp()); + } + } + + // Otherwise, do nothing. + Some(_) | None => {} + } + } + } + } + dl_field => { + debug!("(building reduced graph for external crate) \ + ignoring field"); + } + } + } + } + + /// Creates and adds an import directive to the given module. + fn build_import_directive(@mut self, + privacy: Privacy, + module_: @mut Module, + module_path: ~[ident], + subclass: @ImportDirectiveSubclass, + span: span) { + let directive = @ImportDirective(privacy, module_path, + subclass, span); + module_.imports.push(directive); + + // Bump the reference count on the name. Or, if this is a glob, set + // the appropriate flag. + + match *subclass { + SingleImport(target, _) => { + debug!("(building import directive) building import \ + directive: privacy %? %s::%s", + privacy, + self.idents_to_str(directive.module_path), + *self.session.str_of(target)); + + match module_.import_resolutions.find(&target) { + Some(&resolution) => { + debug!("(building import directive) bumping \ + reference"); + resolution.outstanding_references += 1; + } + None => { + debug!("(building import directive) creating new"); + let state = @mut ImportState(); + let resolution = @mut ImportResolution(privacy, + span, + state); + let name = self.idents_to_str(directive.module_path); + // Don't warn about unused intrinsics because they're + // automatically appended to all files + if name == ~"intrinsic::rusti" { + resolution.state.warned = true; + } + resolution.outstanding_references = 1; + module_.import_resolutions.insert(target, resolution); + } + } + } + GlobImport => { + // Set the glob flag. This tells us that we don't know the + // module's exports ahead of time. + + module_.glob_count += 1; + } + } + + self.unresolved_imports += 1; + } + + // Import resolution + // + // This is a fixed-point algorithm. We resolve imports until our efforts + // are stymied by an unresolved import; then we bail out of the current + // module and continue. We terminate successfully once no more imports + // remain or unsuccessfully when no forward progress in resolving imports + // is made. + + /** + * Resolves all imports for the crate. This method performs the fixed- + * point iteration. + */ + fn resolve_imports(@mut self) { + let mut i = 0; + let mut prev_unresolved_imports = 0; + loop { + debug!("(resolving imports) iteration %u, %u imports left", + i, self.unresolved_imports); + + let module_root = self.graph_root.get_module(); + self.resolve_imports_for_module_subtree(module_root); + + if self.unresolved_imports == 0 { + debug!("(resolving imports) success"); + break; + } + + if self.unresolved_imports == prev_unresolved_imports { + self.session.err(~"failed to resolve imports"); + self.report_unresolved_imports(module_root); + break; + } + + i += 1; + prev_unresolved_imports = self.unresolved_imports; + } + } + + /// Attempts to resolve imports for the given module and all of its + /// submodules. + fn resolve_imports_for_module_subtree(@mut self, module_: @mut Module) { + debug!("(resolving imports for module subtree) resolving %s", + self.module_to_str(module_)); + self.resolve_imports_for_module(module_); + + for module_.children.each_value |&child_node| { + match child_node.get_module_if_available() { + None => { + // Nothing to do. + } + Some(child_module) => { + self.resolve_imports_for_module_subtree(child_module); + } + } + } + + for module_.anonymous_children.each_value |&child_module| { + self.resolve_imports_for_module_subtree(child_module); + } + } + + /// Attempts to resolve imports for the given module only. + fn resolve_imports_for_module(@mut self, module: @mut Module) { + if module.all_imports_resolved() { + debug!("(resolving imports for module) all imports resolved for \ + %s", + self.module_to_str(module)); + return; + } + + let imports = &mut *module.imports; + let import_count = imports.len(); + while module.resolved_import_count < import_count { + let import_index = module.resolved_import_count; + let import_directive = imports[import_index]; + match self.resolve_import_for_module(module, import_directive) { + Failed => { + // We presumably emitted an error. Continue. + let msg = fmt!("failed to resolve import: %s", + *self.import_path_to_str( + import_directive.module_path, + *import_directive.subclass)); + self.session.span_err(import_directive.span, msg); + } + Indeterminate => { + // Bail out. We'll come around next time. + break; + } + Success(()) => { + // Good. Continue. + } + } + + module.resolved_import_count += 1; + } + } + + fn idents_to_str(@mut self, idents: &[ident]) -> ~str { + let mut first = true; + let mut result = ~""; + for idents.each |ident| { + if first { first = false; } else { result += "::" }; + result += *self.session.str_of(*ident); + }; + return result; + } + + fn import_directive_subclass_to_str(@mut self, + subclass: ImportDirectiveSubclass) + -> @~str { + match subclass { + SingleImport(_target, source) => self.session.str_of(source), + GlobImport => @~"*" + } + } + + fn import_path_to_str(@mut self, + idents: &[ident], + subclass: ImportDirectiveSubclass) + -> @~str { + if idents.is_empty() { + self.import_directive_subclass_to_str(subclass) + } else { + @fmt!("%s::%s", + self.idents_to_str(idents), + *self.import_directive_subclass_to_str(subclass)) + } + } + + /// Attempts to resolve the given import. The return value indicates + /// failure if we're certain the name does not exist, indeterminate if we + /// don't know whether the name exists at the moment due to other + /// currently-unresolved imports, or success if we know the name exists. + /// If successful, the resolved bindings are written into the module. + fn resolve_import_for_module(@mut self, module_: @mut Module, + import_directive: @ImportDirective) + -> ResolveResult<()> { + let mut resolution_result = Failed; + let module_path = &import_directive.module_path; + + debug!("(resolving import for module) resolving import `%s::...` in \ + `%s`", + self.idents_to_str(*module_path), + self.module_to_str(module_)); + + // First, resolve the module path for the directive, if necessary. + let containing_module = if module_path.len() == 0 { + // Use the crate root. + Some(self.graph_root.get_module()) + } else { + match self.resolve_module_path_for_import(module_, + *module_path, + DontUseLexicalScope, + import_directive.span) { + + Failed => None, + Indeterminate => { + resolution_result = Indeterminate; + None + } + Success(containing_module) => Some(containing_module), + } + }; + + match containing_module { + None => {} + Some(containing_module) => { + // We found the module that the target is contained + // within. Attempt to resolve the import within it. + + match *import_directive.subclass { + SingleImport(target, source) => { + resolution_result = + self.resolve_single_import(module_, + containing_module, + target, + source); + } + GlobImport => { + let span = import_directive.span; + let privacy = import_directive.privacy; + resolution_result = + self.resolve_glob_import(privacy, + module_, + containing_module, + span); + } + } + } + } + + // Decrement the count of unresolved imports. + match resolution_result { + Success(()) => { + assert!(self.unresolved_imports >= 1); + self.unresolved_imports -= 1; + } + _ => { + // Nothing to do here; just return the error. + } + } + + // Decrement the count of unresolved globs if necessary. But only if + // the resolution result is indeterminate -- otherwise we'll stop + // processing imports here. (See the loop in + // resolve_imports_for_module.) + + if !resolution_result.indeterminate() { + match *import_directive.subclass { + GlobImport => { + assert!(module_.glob_count >= 1); + module_.glob_count -= 1; + } + SingleImport(*) => { + // Ignore. + } + } + } + + return resolution_result; + } + + fn create_name_bindings_from_module(module: @mut Module) -> NameBindings { + NameBindings { + type_def: Some(TypeNsDef { + privacy: Public, + module_def: Some(module), + type_def: None, + }), + value_def: None, + type_span: None, + value_span: None, + } + } + + fn resolve_single_import(@mut self, + module_: @mut Module, + containing_module: @mut Module, + target: ident, + source: ident) + -> ResolveResult<()> { + debug!("(resolving single import) resolving `%s` = `%s::%s` from \ + `%s`", + *self.session.str_of(target), + self.module_to_str(containing_module), + *self.session.str_of(source), + self.module_to_str(module_)); + + // We need to resolve both namespaces for this to succeed. + // + // FIXME #4949: See if there's some way of handling namespaces in + // a more generic way. We have two of them; it seems worth + // doing... + + let mut value_result = UnknownResult; + let mut type_result = UnknownResult; + + // Search for direct children of the containing module. + match containing_module.children.find(&source) { + None => { + // Continue. + } + Some(child_name_bindings) => { + if child_name_bindings.defined_in_namespace(ValueNS) { + value_result = BoundResult(containing_module, + *child_name_bindings); + } + if child_name_bindings.defined_in_namespace(TypeNS) { + type_result = BoundResult(containing_module, + *child_name_bindings); + } + } + } + + // Unless we managed to find a result in both namespaces (unlikely), + // search imports as well. + match (value_result, type_result) { + (BoundResult(*), BoundResult(*)) => { + // Continue. + } + _ => { + // If there is an unresolved glob at this point in the + // containing module, bail out. We don't know enough to be + // able to resolve this import. + + if containing_module.glob_count > 0 { + debug!("(resolving single import) unresolved glob; \ + bailing out"); + return Indeterminate; + } + + // Now search the exported imports within the containing + // module. + + match containing_module.import_resolutions.find(&source) { + None => { + // The containing module definitely doesn't have an + // exported import with the name in question. We can + // therefore accurately report that the names are + // unbound. + + if value_result.is_unknown() { + value_result = UnboundResult; + } + if type_result.is_unknown() { + type_result = UnboundResult; + } + } + Some(import_resolution) + if import_resolution.outstanding_references + == 0 => { + + fn get_binding(import_resolution: + @mut ImportResolution, + namespace: Namespace) + -> NamespaceResult { + + // Import resolutions must be declared with "pub" + // in order to be exported. + if import_resolution.privacy == Private { + return UnboundResult; + } + + match (*import_resolution). + target_for_namespace(namespace) { + None => { + return UnboundResult; + } + Some(target) => { + import_resolution.state.used = true; + return BoundResult(target.target_module, + target.bindings); + } + } + } + + // The name is an import which has been fully + // resolved. We can, therefore, just follow it. + if value_result.is_unknown() { + value_result = get_binding(*import_resolution, + ValueNS); + } + if type_result.is_unknown() { + type_result = get_binding(*import_resolution, + TypeNS); + } + } + Some(_) => { + // The import is unresolved. Bail out. + debug!("(resolving single import) unresolved import; \ + bailing out"); + return Indeterminate; + } + } + } + } + + // If we didn't find a result in the type namespace, search the + // external modules. + match type_result { + BoundResult(*) => {} + _ => { + match containing_module.external_module_children + .find(&source) { + None => {} // Continue. + Some(module) => { + let name_bindings = + @mut Resolver::create_name_bindings_from_module( + *module); + type_result = BoundResult(containing_module, + name_bindings); + } + } + } + } + + // We've successfully resolved the import. Write the results in. + assert!(module_.import_resolutions.contains_key(&target)); + let import_resolution = module_.import_resolutions.get(&target); + + match value_result { + BoundResult(target_module, name_bindings) => { + import_resolution.value_target = + Some(Target(target_module, name_bindings)); + } + UnboundResult => { /* Continue. */ } + UnknownResult => { + fail!(~"value result should be known at this point"); + } + } + match type_result { + BoundResult(target_module, name_bindings) => { + import_resolution.type_target = + Some(Target(target_module, name_bindings)); + } + UnboundResult => { /* Continue. */ } + UnknownResult => { + fail!(~"type result should be known at this point"); + } + } + + let i = import_resolution; + match (i.value_target, i.type_target) { + // If this name wasn't found in either namespace, it's definitely + // unresolved. + (None, None) => { return Failed; } + // If it's private, it's also unresolved. + (Some(t), None) | (None, Some(t)) => { + let bindings = &mut *t.bindings; + match bindings.type_def { + Some(ref type_def) => { + if type_def.privacy == Private { + return Failed; + } + } + _ => () + } + match bindings.value_def { + Some(ref value_def) => { + if value_def.privacy == Private { + return Failed; + } + } + _ => () + } + } + // It's also an error if there's both a type and a value with this + // name, but both are private + (Some(val), Some(ty)) => { + match (val.bindings.value_def, ty.bindings.value_def) { + (Some(ref value_def), Some(ref type_def)) => + if value_def.privacy == Private + && type_def.privacy == Private { + return Failed; + }, + _ => () + } + } + } + + assert!(import_resolution.outstanding_references >= 1); + import_resolution.outstanding_references -= 1; + + debug!("(resolving single import) successfully resolved import"); + return Success(()); + } + + // Resolves a glob import. Note that this function cannot fail; it either + // succeeds or bails out (as importing * from an empty module or a module + // that exports nothing is valid). + fn resolve_glob_import(@mut self, + privacy: Privacy, + module_: @mut Module, + containing_module: @mut Module, + span: span) + -> ResolveResult<()> { + // This function works in a highly imperative manner; it eagerly adds + // everything it can to the list of import resolutions of the module + // node. + debug!("(resolving glob import) resolving %? glob import", privacy); + let state = @mut ImportState(); + + // We must bail out if the node has unresolved imports of any kind + // (including globs). + if !(*containing_module).all_imports_resolved() { + debug!("(resolving glob import) target module has unresolved \ + imports; bailing out"); + return Indeterminate; + } + + assert!(containing_module.glob_count == 0); + + // Add all resolved imports from the containing module. + for containing_module.import_resolutions.each + |ident, target_import_resolution| { + + debug!("(resolving glob import) writing module resolution \ + %? into `%s`", + target_import_resolution.type_target.is_none(), + self.module_to_str(module_)); + + // Here we merge two import resolutions. + match module_.import_resolutions.find(ident) { + None if target_import_resolution.privacy == Public => { + // Simple: just copy the old import resolution. + let new_import_resolution = + @mut ImportResolution(privacy, + target_import_resolution.span, + state); + new_import_resolution.value_target = + copy target_import_resolution.value_target; + new_import_resolution.type_target = + copy target_import_resolution.type_target; + + module_.import_resolutions.insert + (*ident, new_import_resolution); + } + None => { /* continue ... */ } + Some(&dest_import_resolution) => { + // Merge the two import resolutions at a finer-grained + // level. + + match target_import_resolution.value_target { + None => { + // Continue. + } + Some(copy value_target) => { + dest_import_resolution.value_target = + Some(value_target); + } + } + match target_import_resolution.type_target { + None => { + // Continue. + } + Some(copy type_target) => { + dest_import_resolution.type_target = + Some(type_target); + } + } + } + } + } + + let merge_import_resolution = |ident, + name_bindings: @mut NameBindings| { + let dest_import_resolution; + match module_.import_resolutions.find(ident) { + None => { + // Create a new import resolution from this child. + dest_import_resolution = @mut ImportResolution(privacy, + span, + state); + module_.import_resolutions.insert + (*ident, dest_import_resolution); + } + Some(&existing_import_resolution) => { + dest_import_resolution = existing_import_resolution; + } + } + + debug!("(resolving glob import) writing resolution `%s` in `%s` \ + to `%s`, privacy=%?", + *self.session.str_of(*ident), + self.module_to_str(containing_module), + self.module_to_str(module_), + copy dest_import_resolution.privacy); + + // 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 = + Some(Target(containing_module, name_bindings)); + } + if name_bindings.defined_in_public_namespace(TypeNS) { + debug!("(resolving glob import) ... for type target"); + dest_import_resolution.type_target = + Some(Target(containing_module, name_bindings)); + } + }; + + // Add all children from the containing module. + for containing_module.children.each |ident, name_bindings| { + merge_import_resolution(ident, *name_bindings); + } + + // Add external module children from the containing module. + for containing_module.external_module_children.each + |ident, module| { + let name_bindings = + @mut Resolver::create_name_bindings_from_module(*module); + merge_import_resolution(ident, name_bindings); + } + + debug!("(resolving glob import) successfully resolved import"); + return Success(()); + } + + /// Resolves the given module path from the given root `module_`. + fn resolve_module_path_from_root(@mut self, + module_: @mut Module, + module_path: &[ident], + index: uint, + span: span, + mut name_search_type: NameSearchType) + -> ResolveResult<@mut Module> { + let mut search_module = module_; + let mut index = index; + let module_path_len = module_path.len(); + + // Resolve the module part of the path. This does not involve looking + // upward though scope chains; we simply resolve names directly in + // modules as we go. + + while index < module_path_len { + let name = module_path[index]; + match self.resolve_name_in_module(search_module, + name, + TypeNS, + name_search_type) { + Failed => { + self.session.span_err(span, ~"unresolved name"); + return Failed; + } + Indeterminate => { + debug!("(resolving module path for import) module \ + resolution is indeterminate: %s", + *self.session.str_of(name)); + return Indeterminate; + } + Success(target) => { + // Check to see whether there are type bindings, and, if + // so, whether there is a module within. + match target.bindings.type_def { + Some(copy type_def) => { + match type_def.module_def { + None => { + // Not a module. + self.session.span_err(span, + fmt!("not a \ + module: %s", + *self.session. + str_of( + name))); + return Failed; + } + Some(copy module_def) => { + search_module = module_def; + } + } + } + None => { + // There are no type bindings at all. + self.session.span_err(span, + fmt!("not a module: %s", + *self.session.str_of( + name))); + return Failed; + } + } + } + } + + index += 1; + + // After the first element of the path, allow searching through + // items and imports unconditionally. This allows things like: + // + // pub mod core { + // pub use vec; + // } + // + // pub mod something_else { + // use core::vec; + // } + + name_search_type = SearchItemsAndPublicImports; + } + + return Success(search_module); + } + + /// Attempts to resolve the module part of an import directive or path + /// rooted at the given module. + fn resolve_module_path_for_import(@mut self, + module_: @mut Module, + module_path: &[ident], + use_lexical_scope: UseLexicalScopeFlag, + span: span) + -> ResolveResult<@mut Module> { + let module_path_len = module_path.len(); + assert!(module_path_len > 0); + + debug!("(resolving module path for import) processing `%s` rooted at \ + `%s`", + self.idents_to_str(module_path), + self.module_to_str(module_)); + + // Resolve the module prefix, if any. + let module_prefix_result = self.resolve_module_prefix(module_, + module_path); + + let search_module; + let start_index; + match module_prefix_result { + Failed => { + self.session.span_err(span, ~"unresolved name"); + return Failed; + } + Indeterminate => { + debug!("(resolving module path for import) indeterminate; \ + bailing"); + return Indeterminate; + } + Success(NoPrefixFound) => { + // There was no prefix, so we're considering the first element + // of the path. How we handle this depends on whether we were + // instructed to use lexical scope or not. + match use_lexical_scope { + DontUseLexicalScope => { + // This is a crate-relative path. We will start the + // resolution process at index zero. + search_module = self.graph_root.get_module(); + start_index = 0; + } + UseLexicalScope => { + // This is not a crate-relative path. We resolve the + // first component of the path in the current lexical + // scope and then proceed to resolve below that. + let result = self.resolve_module_in_lexical_scope( + module_, + module_path[0]); + match result { + Failed => { + self.session.span_err(span, + ~"unresolved name"); + return Failed; + } + Indeterminate => { + debug!("(resolving module path for import) \ + indeterminate; bailing"); + return Indeterminate; + } + Success(containing_module) => { + search_module = containing_module; + start_index = 1; + } + } + } + } + } + Success(PrefixFound(containing_module, index)) => { + search_module = containing_module; + start_index = index; + } + } + + self.resolve_module_path_from_root(search_module, + module_path, + start_index, + span, + SearchItemsAndPublicImports) + } + + /// Invariant: This must only be called during main resolution, not during + /// import resolution. + fn resolve_item_in_lexical_scope(@mut self, + module_: @mut Module, + name: ident, + namespace: Namespace, + search_through_modules: + SearchThroughModulesFlag) + -> ResolveResult { + debug!("(resolving item in lexical scope) resolving `%s` in \ + namespace %? in `%s`", + *self.session.str_of(name), + namespace, + self.module_to_str(module_)); + + // The current module node is handled specially. First, check for + // its immediate children. + match module_.children.find(&name) { + Some(name_bindings) + if name_bindings.defined_in_namespace(namespace) => { + return Success(Target(module_, *name_bindings)); + } + Some(_) | None => { /* Not found; continue. */ } + } + + // Now check for its import directives. We don't have to have resolved + // all its imports in the usual way; this is because chains of + // adjacent import statements are processed as though they mutated the + // current scope. + match module_.import_resolutions.find(&name) { + None => { + // Not found; continue. + } + Some(import_resolution) => { + match (*import_resolution).target_for_namespace(namespace) { + None => { + // Not found; continue. + debug!("(resolving item in lexical scope) found \ + import resolution, but not in namespace %?", + namespace); + } + Some(target) => { + debug!("(resolving item in lexical scope) using \ + import resolution"); + import_resolution.state.used = true; + return Success(copy target); + } + } + } + } + + // Search for external modules. + if namespace == TypeNS { + match module_.external_module_children.find(&name) { + None => {} + Some(module) => { + let name_bindings = + @mut Resolver::create_name_bindings_from_module( + *module); + return Success(Target(module_, name_bindings)); + } + } + } + + // Finally, proceed up the scope chain looking for parent modules. + let mut search_module = module_; + loop { + // Go to the next parent. + match search_module.parent_link { + NoParentLink => { + // No more parents. This module was unresolved. + debug!("(resolving item in lexical scope) unresolved \ + module"); + return Failed; + } + ModuleParentLink(parent_module_node, _) => { + match search_through_modules { + DontSearchThroughModules => { + match search_module.kind { + NormalModuleKind => { + // We stop the search here. + debug!("(resolving item in lexical \ + scope) unresolved module: not \ + searching through module \ + parents"); + return Failed; + } + ExternModuleKind | + TraitModuleKind | + AnonymousModuleKind => { + search_module = parent_module_node; + } + } + } + SearchThroughModules => { + search_module = parent_module_node; + } + } + } + BlockParentLink(parent_module_node, _) => { + search_module = parent_module_node; + } + } + + // Resolve the name in the parent module. + match self.resolve_name_in_module(search_module, + name, + namespace, + SearchItemsAndAllImports) { + Failed => { + // Continue up the search chain. + } + Indeterminate => { + // We couldn't see through the higher scope because of an + // unresolved import higher up. Bail. + + debug!("(resolving item in lexical scope) indeterminate \ + higher scope; bailing"); + return Indeterminate; + } + Success(target) => { + // We found the module. + return Success(copy target); + } + } + } + } + + /** Resolves a module name in the current lexical scope. */ + fn resolve_module_in_lexical_scope(@mut self, + module_: @mut Module, + name: ident) + -> ResolveResult<@mut Module> { + // If this module is an anonymous module, resolve the item in the + // lexical scope. Otherwise, resolve the item from the crate root. + let resolve_result = self.resolve_item_in_lexical_scope( + module_, name, TypeNS, DontSearchThroughModules); + match resolve_result { + Success(target) => { + let bindings = &mut *target.bindings; + match bindings.type_def { + Some(ref type_def) => { + match (*type_def).module_def { + None => { + error!("!!! (resolving module in lexical \ + scope) module wasn't actually a \ + module!"); + return Failed; + } + Some(module_def) => { + return Success(module_def); + } + } + } + None => { + error!("!!! (resolving module in lexical scope) module + wasn't actually a module!"); + return Failed; + } + } + } + Indeterminate => { + debug!("(resolving module in lexical scope) indeterminate; \ + bailing"); + return Indeterminate; + } + Failed => { + debug!("(resolving module in lexical scope) failed to \ + resolve"); + return Failed; + } + } + } + + /** + * Returns the nearest normal module parent of the given module. + */ + fn get_nearest_normal_module_parent(@mut self, module_: @mut Module) + -> Option<@mut Module> { + let mut module_ = module_; + loop { + match module_.parent_link { + NoParentLink => return None, + ModuleParentLink(new_module, _) | + BlockParentLink(new_module, _) => { + match new_module.kind { + NormalModuleKind => return Some(new_module), + ExternModuleKind | + TraitModuleKind | + AnonymousModuleKind => module_ = new_module, + } + } + } + } + } + + /** + * Returns the nearest normal module parent of the given module, or the + * module itself if it is a normal module. + */ + fn get_nearest_normal_module_parent_or_self(@mut self, + module_: @mut Module) + -> @mut Module { + match module_.kind { + NormalModuleKind => return module_, + ExternModuleKind | TraitModuleKind | AnonymousModuleKind => { + match self.get_nearest_normal_module_parent(module_) { + None => module_, + Some(new_module) => new_module + } + } + } + } + + /** + * Resolves a "module prefix". A module prefix is one of (a) `self::`; + * (b) some chain of `super::`. + */ + fn resolve_module_prefix(@mut self, + module_: @mut Module, + module_path: &[ident]) + -> ResolveResult { + let interner = self.session.parse_sess.interner; + + // Start at the current module if we see `self` or `super`, or at the + // top of the crate otherwise. + let mut containing_module; + let mut i; + if *interner.get(module_path[0]) == ~"self" { + containing_module = + self.get_nearest_normal_module_parent_or_self(module_); + i = 1; + } else if *interner.get(module_path[0]) == ~"super" { + containing_module = + self.get_nearest_normal_module_parent_or_self(module_); + i = 0; // We'll handle `super` below. + } else { + return Success(NoPrefixFound); + } + + // Now loop through all the `super`s we find. + while i < module_path.len() && + *interner.get(module_path[i]) == ~"super" { + debug!("(resolving module prefix) resolving `super` at %s", + self.module_to_str(containing_module)); + match self.get_nearest_normal_module_parent(containing_module) { + None => return Failed, + Some(new_module) => { + containing_module = new_module; + i += 1; + } + } + } + + debug!("(resolving module prefix) finished resolving prefix at %s", + self.module_to_str(containing_module)); + + return Success(PrefixFound(containing_module, i)); + } + + /// Attempts to resolve the supplied name in the given module for the + /// given namespace. If successful, returns the target corresponding to + /// the name. + fn resolve_name_in_module(@mut self, + module_: @mut Module, + name: ident, + namespace: Namespace, + name_search_type: NameSearchType) + -> ResolveResult { + debug!("(resolving name in module) resolving `%s` in `%s`", + *self.session.str_of(name), + self.module_to_str(module_)); + + // First, check the direct children of the module. + match module_.children.find(&name) { + Some(name_bindings) + if name_bindings.defined_in_namespace(namespace) => { + debug!("(resolving name in module) found node as child"); + return Success(Target(module_, *name_bindings)); + } + Some(_) | None => { + // Continue. + } + } + + // Next, check the module's imports if necessary. + + // If this is a search of all imports, we should be done with glob + // resolution at this point. + if name_search_type == SearchItemsAndAllImports { + assert!(module_.glob_count == 0); + } + + // Check the list of resolved imports. + match module_.import_resolutions.find(&name) { + Some(import_resolution) => { + if import_resolution.privacy == Public && + import_resolution.outstanding_references != 0 { + debug!("(resolving name in module) import \ + unresolved; bailing out"); + return Indeterminate; + } + + match import_resolution.target_for_namespace(namespace) { + None => { + debug!("(resolving name in module) name found, \ + but not in namespace %?", + namespace); + } + Some(target) + if name_search_type == + SearchItemsAndAllImports || + import_resolution.privacy == Public => { + debug!("(resolving name in module) resolved to \ + import"); + import_resolution.state.used = true; + return Success(copy target); + } + Some(_) => { + debug!("(resolving name in module) name found, \ + but not public"); + } + } + } + None => {} // Continue. + } + + // Finally, search through external children. + if namespace == TypeNS { + match module_.external_module_children.find(&name) { + None => {} + Some(module) => { + let name_bindings = + @mut Resolver::create_name_bindings_from_module( + *module); + return Success(Target(module_, name_bindings)); + } + } + } + + // We're out of luck. + debug!("(resolving name in module) failed to resolve %s", + *self.session.str_of(name)); + return Failed; + } + + fn report_unresolved_imports(@mut self, module_: @mut Module) { + let index = module_.resolved_import_count; + let imports: &mut ~[@ImportDirective] = &mut *module_.imports; + let import_count = imports.len(); + if index != import_count { + let sn = self.session.codemap.span_to_snippet(imports[index].span); + if str::contains(sn, "::") { + self.session.span_err(imports[index].span, ~"unresolved import"); + } else { + let err = fmt!("unresolved import (maybe you meant `%s::*`?)", + sn.slice(0, sn.len() - 1)); // -1 to adjust for semicolon + self.session.span_err(imports[index].span, err); + } + } + + // Descend into children and anonymous children. + for module_.children.each_value |&child_node| { + match child_node.get_module_if_available() { + None => { + // Continue. + } + Some(child_module) => { + self.report_unresolved_imports(child_module); + } + } + } + + for module_.anonymous_children.each_value |&module_| { + self.report_unresolved_imports(module_); + } + } + + // Export recording + // + // This pass simply determines what all "export" keywords refer to and + // writes the results into the export map. + // + // FIXME #4953 This pass will be removed once exports change to per-item. + // Then this operation can simply be performed as part of item (or import) + // processing. + + fn record_exports(@mut self) { + let root_module = self.graph_root.get_module(); + self.record_exports_for_module_subtree(root_module); + } + + fn record_exports_for_module_subtree(@mut self, module_: @mut Module) { + // If this isn't a local crate, then bail out. We don't need to record + // exports for nonlocal crates. + + match module_.def_id { + Some(def_id) if def_id.crate == local_crate => { + // OK. Continue. + debug!("(recording exports for module subtree) recording \ + exports for local module"); + } + None => { + // Record exports for the root module. + debug!("(recording exports for module subtree) recording \ + exports for root module"); + } + Some(_) => { + // Bail out. + debug!("(recording exports for module subtree) not recording \ + exports for `%s`", + self.module_to_str(module_)); + return; + } + } + + self.record_exports_for_module(module_); + + for module_.children.each_value |&child_name_bindings| { + match child_name_bindings.get_module_if_available() { + None => { + // Nothing to do. + } + Some(child_module) => { + self.record_exports_for_module_subtree(child_module); + } + } + } + + for module_.anonymous_children.each_value |&child_module| { + self.record_exports_for_module_subtree(child_module); + } + } + + fn record_exports_for_module(@mut self, module_: @mut Module) { + let mut exports2 = ~[]; + + self.add_exports_for_module(&mut exports2, module_); + match /*bad*/copy module_.def_id { + Some(def_id) => { + self.export_map2.insert(def_id.node, exports2); + debug!("(computing exports) writing exports for %d (some)", + def_id.node); + } + None => {} + } + } + + fn add_exports_of_namebindings(@mut self, + exports2: &mut ~[Export2], + ident: ident, + namebindings: @mut NameBindings, + ns: Namespace, + reexport: bool) { + match (namebindings.def_for_namespace(ns), + namebindings.privacy_for_namespace(ns)) { + (Some(d), Some(Public)) => { + debug!("(computing exports) YES: %s '%s' => %?", + if reexport { ~"reexport" } else { ~"export"}, + *self.session.str_of(ident), + def_id_of_def(d)); + exports2.push(Export2 { + reexport: reexport, + name: self.session.str_of(ident), + def_id: def_id_of_def(d) + }); + } + (Some(_), Some(privacy)) => { + debug!("(computing reexports) NO: privacy %?", privacy); + } + (d_opt, p_opt) => { + debug!("(computing reexports) NO: %?, %?", d_opt, p_opt); + } + } + } + + fn add_exports_for_module(@mut self, + exports2: &mut ~[Export2], + module_: @mut Module) { + for module_.children.each |ident, namebindings| { + debug!("(computing exports) maybe export '%s'", + *self.session.str_of(*ident)); + self.add_exports_of_namebindings(&mut *exports2, + *ident, + *namebindings, + TypeNS, + false); + self.add_exports_of_namebindings(&mut *exports2, + *ident, + *namebindings, + ValueNS, + false); + } + + for module_.import_resolutions.each |ident, importresolution| { + if importresolution.privacy != Public { + debug!("(computing exports) not reexporting private `%s`", + *self.session.str_of(*ident)); + loop; + } + for [ TypeNS, ValueNS ].each |ns| { + match importresolution.target_for_namespace(*ns) { + Some(target) => { + debug!("(computing exports) maybe reexport '%s'", + *self.session.str_of(*ident)); + self.add_exports_of_namebindings(&mut *exports2, + *ident, + target.bindings, + *ns, + true) + } + _ => () + } + } + } + } + + // AST resolution + // + // We maintain a list of value ribs and type ribs. + // + // Simultaneously, we keep track of the current position in the module + // graph in the `current_module` pointer. When we go to resolve a name in + // the value or type namespaces, we first look through all the ribs and + // then query the module graph. When we resolve a name in the module + // namespace, we can skip all the ribs (since nested modules are not + // allowed within blocks in Rust) and jump straight to the current module + // graph node. + // + // Named implementations are handled separately. When we find a method + // call, we consult the module node to find all of the implementations in + // scope. This information is lazily cached in the module node. We then + // generate a fake "implementation scope" containing all the + // implementations thus found, for compatibility with old resolve pass. + + fn with_scope(@mut self, name: Option, f: &fn()) { + let orig_module = self.current_module; + + // Move down in the graph. + match name { + None => { + // Nothing to do. + } + Some(name) => { + match orig_module.children.find(&name) { + None => { + debug!("!!! (with scope) didn't find `%s` in `%s`", + *self.session.str_of(name), + self.module_to_str(orig_module)); + } + Some(name_bindings) => { + match (*name_bindings).get_module_if_available() { + None => { + debug!("!!! (with scope) didn't find module \ + for `%s` in `%s`", + *self.session.str_of(name), + self.module_to_str(orig_module)); + } + Some(module_) => { + self.current_module = module_; + } + } + } + } + } + } + + f(); + + self.current_module = orig_module; + } + + // Wraps the given definition in the appropriate number of `def_upvar` + // wrappers. + + fn upvarify(@mut self, + ribs: &mut ~[@Rib], + rib_index: uint, + def_like: def_like, + span: span, + allow_capturing_self: AllowCapturingSelfFlag) + -> Option { + let mut def; + let is_ty_param; + + match def_like { + dl_def(d @ def_local(*)) | dl_def(d @ def_upvar(*)) | + dl_def(d @ def_arg(*)) | dl_def(d @ def_binding(*)) => { + def = d; + is_ty_param = false; + } + dl_def(d @ def_ty_param(*)) => { + def = d; + is_ty_param = true; + } + dl_def(d @ def_self(*)) + if allow_capturing_self == DontAllowCapturingSelf => { + def = d; + is_ty_param = false; + } + _ => { + return Some(def_like); + } + } + + let mut rib_index = rib_index + 1; + while rib_index < ribs.len() { + match ribs[rib_index].kind { + NormalRibKind => { + // Nothing to do. Continue. + } + FunctionRibKind(function_id, body_id) => { + if !is_ty_param { + def = def_upvar(def_id_of_def(def).node, + @def, + function_id, + body_id); + } + } + MethodRibKind(item_id, _) => { + // If the def is a ty param, and came from the parent + // item, it's ok + match def { + def_ty_param(did, _) + if self.def_map.find(&did.node).map_consume(|x| *x) + == Some(def_typaram_binder(item_id)) => { + // ok + } + _ => { + if !is_ty_param { + // This was an attempt to access an upvar inside a + // named function item. This is not allowed, so we + // report an error. + + self.session.span_err( + span, + ~"attempted dynamic environment-capture"); + } else { + // This was an attempt to use a type parameter outside + // its scope. + + self.session.span_err(span, + ~"attempt to use a type \ + argument out of scope"); + } + + return None; + } + } + } + OpaqueFunctionRibKind => { + if !is_ty_param { + // This was an attempt to access an upvar inside a + // named function item. This is not allowed, so we + // report an error. + + self.session.span_err( + span, + ~"attempted dynamic environment-capture"); + } else { + // This was an attempt to use a type parameter outside + // its scope. + + self.session.span_err(span, + ~"attempt to use a type \ + argument out of scope"); + } + + return None; + } + ConstantItemRibKind => { + // Still doesn't deal with upvars + self.session.span_err(span, + ~"attempt to use a non-constant \ + value in a constant"); + + } + } + + rib_index += 1; + } + + return Some(dl_def(def)); + } + + fn search_ribs(@mut self, + ribs: &mut ~[@Rib], + name: ident, + span: span, + allow_capturing_self: AllowCapturingSelfFlag) + -> Option { + // FIXME #4950: This should not use a while loop. + // FIXME #4950: Try caching? + + let mut i = ribs.len(); + while i != 0 { + i -= 1; + match ribs[i].bindings.find(&name) { + Some(&def_like) => { + return self.upvarify(ribs, i, def_like, span, + allow_capturing_self); + } + None => { + // Continue. + } + } + } + + return None; + } + + fn resolve_crate(@mut self) { + debug!("(resolving crate) starting"); + + visit_crate(self.crate, (), mk_vt(@Visitor { + visit_item: |item, _context, visitor| + self.resolve_item(item, visitor), + visit_arm: |arm, _context, visitor| + self.resolve_arm(arm, visitor), + visit_block: |block, _context, visitor| + self.resolve_block(block, visitor), + visit_expr: |expr, _context, visitor| + self.resolve_expr(expr, visitor), + visit_local: |local, _context, visitor| + self.resolve_local(local, visitor), + visit_ty: |ty, _context, visitor| + self.resolve_type(ty, visitor), + .. *default_visitor() + })); + } + + fn resolve_item(@mut self, item: @item, visitor: ResolveVisitor) { + debug!("(resolving item) resolving %s", + *self.session.str_of(item.ident)); + + // Items with the !resolve_unexported attribute are X-ray contexts. + // This is used to allow the test runner to run unexported tests. + let orig_xray_flag = self.xray_context; + if contains_name(attr_metas(item.attrs), + ~"!resolve_unexported") { + self.xray_context = Xray; + } + + match item.node { + + // enum item: resolve all the variants' discrs, + // then resolve the ty params + item_enum(ref enum_def, ref generics) => { + for (*enum_def).variants.each() |variant| { + for variant.node.disr_expr.each |dis_expr| { + // resolve the discriminator expr + // as a constant + self.with_constant_rib(|| { + self.resolve_expr(*dis_expr, visitor); + }); + } + } + + // n.b. the discr expr gets visted twice. + // but maybe it's okay since the first time will signal an + // error if there is one? -- tjc + do self.with_type_parameter_rib( + HasTypeParameters( + generics, item.id, 0, NormalRibKind)) { + visit_item(item, (), visitor); + } + } + + item_ty(_, ref generics) => { + do self.with_type_parameter_rib + (HasTypeParameters(generics, item.id, 0, + NormalRibKind)) + || { + + visit_item(item, (), visitor); + } + } + + item_impl(ref generics, + implemented_traits, + self_type, + ref methods) => { + self.resolve_implementation(item.id, + generics, + implemented_traits, + self_type, + *methods, + visitor); + } + + item_trait(ref generics, ref traits, ref methods) => { + // Create a new rib for the self type. + let self_type_rib = @Rib(NormalRibKind); + self.type_ribs.push(self_type_rib); + self_type_rib.bindings.insert(self.type_self_ident, + dl_def(def_self_ty(item.id))); + + // Create a new rib for the trait-wide type parameters. + do self.with_type_parameter_rib + (HasTypeParameters(generics, item.id, 0, + NormalRibKind)) { + + self.resolve_type_parameters(&generics.ty_params, + visitor); + + // Resolve derived traits. + for traits.each |trt| { + match self.resolve_path(trt.path, TypeNS, true, + visitor) { + None => + self.session.span_err(trt.path.span, + ~"attempt to derive a \ + nonexistent trait"), + Some(def) => { + // Write a mapping from the trait ID to the + // definition of the trait into the definition + // map. + + debug!("(resolving trait) found trait def: \ + %?", def); + + self.record_def(trt.ref_id, def); + } + } + } + + for (*methods).each |method| { + // Create a new rib for the method-specific type + // parameters. + // + // FIXME #4951: Do we need a node ID here? + + match *method { + required(ref ty_m) => { + do self.with_type_parameter_rib + (HasTypeParameters(&ty_m.generics, + item.id, + generics.ty_params.len(), + MethodRibKind(item.id, Required))) { + + // Resolve the method-specific type + // parameters. + self.resolve_type_parameters( + &ty_m.generics.ty_params, + visitor); + + for ty_m.decl.inputs.each |argument| { + self.resolve_type(argument.ty, visitor); + } + + self.resolve_type(ty_m.decl.output, visitor); + } + } + provided(m) => { + self.resolve_method(MethodRibKind(item.id, + Provided(m.id)), + m, + generics.ty_params.len(), + visitor) + } + } + } + } + + self.type_ribs.pop(); + } + + item_struct(ref struct_def, ref generics) => { + self.resolve_struct(item.id, + generics, + struct_def.fields, + &struct_def.dtor, + visitor); + } + + item_mod(ref module_) => { + do self.with_scope(Some(item.ident)) { + self.resolve_module(module_, item.span, item.ident, + item.id, visitor); + } + } + + item_foreign_mod(ref foreign_module) => { + do self.with_scope(Some(item.ident)) { + for foreign_module.items.each |foreign_item| { + match foreign_item.node { + foreign_item_fn(_, _, ref generics) => { + self.with_type_parameter_rib( + HasTypeParameters( + generics, foreign_item.id, 0, + NormalRibKind), + || visit_foreign_item(*foreign_item, (), + visitor)); + } + foreign_item_const(_) => { + visit_foreign_item(*foreign_item, (), + visitor); + } + } + } + } + } + + item_fn(ref fn_decl, _, _, ref generics, ref block) => { + // If this is the main function, we must record it in the + // session. + + // FIXME #4404 android JNI hacks + if !*self.session.building_library || + self.session.targ_cfg.os == session::os_android { + + if self.attr_main_fn.is_none() && + item.ident == special_idents::main { + + self.main_fns.push(Some((item.id, item.span))); + } + + if attrs_contains_name(item.attrs, ~"main") { + if self.attr_main_fn.is_none() { + self.attr_main_fn = Some((item.id, item.span)); + } else { + self.session.span_err( + item.span, + ~"multiple 'main' functions"); + } + } + + if attrs_contains_name(item.attrs, ~"start") { + if self.start_fn.is_none() { + self.start_fn = Some((item.id, item.span)); + } else { + self.session.span_err( + item.span, + ~"multiple 'start' functions"); + } + } + } + + self.resolve_function(OpaqueFunctionRibKind, + Some(fn_decl), + HasTypeParameters + (generics, + item.id, + 0, + OpaqueFunctionRibKind), + block, + NoSelfBinding, + visitor); + } + + item_const(*) => { + self.with_constant_rib(|| { + visit_item(item, (), visitor); + }); + } + + item_mac(*) => { + fail!(~"item macros unimplemented") + } + } + + self.xray_context = orig_xray_flag; + } + + fn with_type_parameter_rib(@mut self, + type_parameters: TypeParameters, + f: &fn()) { + match type_parameters { + HasTypeParameters(generics, node_id, initial_index, + rib_kind) => { + + let function_type_rib = @Rib(rib_kind); + self.type_ribs.push(function_type_rib); + + for generics.ty_params.eachi |index, type_parameter| { + let name = type_parameter.ident; + debug!("with_type_parameter_rib: %d %d", node_id, + type_parameter.id); + let def_like = dl_def(def_ty_param + (local_def(type_parameter.id), + index + initial_index)); + // Associate this type parameter with + // the item that bound it + self.record_def(type_parameter.id, + def_typaram_binder(node_id)); + function_type_rib.bindings.insert(name, def_like); + } + } + + NoTypeParameters => { + // Nothing to do. + } + } + + f(); + + match type_parameters { + HasTypeParameters(*) => { + self.type_ribs.pop(); + } + + NoTypeParameters => { + // Nothing to do. + } + } + } + + fn with_label_rib(@mut self, f: &fn()) { + self.label_ribs.push(@Rib(NormalRibKind)); + f(); + self.label_ribs.pop(); + } + + fn with_constant_rib(@mut self, f: &fn()) { + self.value_ribs.push(@Rib(ConstantItemRibKind)); + f(); + self.value_ribs.pop(); + } + + fn resolve_function(@mut self, + rib_kind: RibKind, + optional_declaration: Option<&fn_decl>, + type_parameters: TypeParameters, + block: &blk, + self_binding: SelfBinding, + visitor: ResolveVisitor) { + // Create a value rib for the function. + let function_value_rib = @Rib(rib_kind); + self.value_ribs.push(function_value_rib); + + // Create a label rib for the function. + let function_label_rib = @Rib(rib_kind); + self.label_ribs.push(function_label_rib); + + // If this function has type parameters, add them now. + do self.with_type_parameter_rib(type_parameters) { + // Resolve the type parameters. + match type_parameters { + NoTypeParameters => { + // Continue. + } + HasTypeParameters(ref generics, _, _, _) => { + self.resolve_type_parameters(&generics.ty_params, + visitor); + } + } + + // Add self to the rib, if necessary. + match self_binding { + NoSelfBinding => { + // Nothing to do. + } + HasSelfBinding(self_node_id, is_implicit) => { + let def_like = dl_def(def_self(self_node_id, + is_implicit)); + (*function_value_rib).bindings.insert(self.self_ident, + def_like); + } + } + + // Add each argument to the rib. + match optional_declaration { + None => { + // Nothing to do. + } + Some(declaration) => { + for declaration.inputs.each |argument| { + let binding_mode = ArgumentIrrefutableMode; + let mutability = + if argument.is_mutbl {Mutable} else {Immutable}; + self.resolve_pattern(argument.pat, + binding_mode, + mutability, + None, + visitor); + + self.resolve_type(argument.ty, visitor); + + debug!("(resolving function) recorded argument"); + } + + self.resolve_type(declaration.output, visitor); + } + } + + // Resolve the function body. + self.resolve_block(block, visitor); + + debug!("(resolving function) leaving function"); + } + + self.label_ribs.pop(); + self.value_ribs.pop(); + } + + fn resolve_type_parameters(@mut self, + type_parameters: &OptVec, + visitor: ResolveVisitor) { + for type_parameters.each |type_parameter| { + for type_parameter.bounds.each |&bound| { + match bound { + TraitTyParamBound(tref) => { + self.resolve_trait_reference(tref, visitor) + } + RegionTyParamBound => {} + } + } + } + } + + fn resolve_trait_reference(@mut self, + trait_reference: &trait_ref, + visitor: ResolveVisitor) { + match self.resolve_path(trait_reference.path, TypeNS, true, visitor) { + None => { + self.session.span_err(trait_reference.path.span, + ~"attempt to implement an \ + unknown trait"); + } + Some(def) => { + self.record_def(trait_reference.ref_id, def); + } + } + } + + fn resolve_struct(@mut self, + id: node_id, + generics: &Generics, + fields: &[@struct_field], + optional_destructor: &Option, + visitor: ResolveVisitor) { + // If applicable, create a rib for the type parameters. + do self.with_type_parameter_rib(HasTypeParameters + (generics, id, 0, + OpaqueFunctionRibKind)) { + + // Resolve the type parameters. + self.resolve_type_parameters(&generics.ty_params, visitor); + + // Resolve fields. + for fields.each |field| { + self.resolve_type(field.node.ty, visitor); + } + + // Resolve the destructor, if applicable. + match *optional_destructor { + None => { + // Nothing to do. + } + Some(ref destructor) => { + self.resolve_function(NormalRibKind, + None, + NoTypeParameters, + &destructor.node.body, + HasSelfBinding + ((*destructor).node.self_id, + true), + visitor); + } + } + } + } + + // Does this really need to take a RibKind or is it always going + // to be NormalRibKind? + fn resolve_method(@mut self, + rib_kind: RibKind, + method: @method, + outer_type_parameter_count: uint, + visitor: ResolveVisitor) { + let method_generics = &method.generics; + let type_parameters = + HasTypeParameters(method_generics, + method.id, + outer_type_parameter_count, + rib_kind); + // we only have self ty if it is a non static method + let self_binding = match method.self_ty.node { + sty_static => { NoSelfBinding } + _ => { HasSelfBinding(method.self_id, false) } + }; + + self.resolve_function(rib_kind, + Some(&method.decl), + type_parameters, + &method.body, + self_binding, + visitor); + } + + fn resolve_implementation(@mut self, + id: node_id, + generics: &Generics, + opt_trait_reference: Option<@trait_ref>, + self_type: @Ty, + methods: &[@method], + visitor: ResolveVisitor) { + // If applicable, create a rib for the type parameters. + let outer_type_parameter_count = generics.ty_params.len(); + do self.with_type_parameter_rib(HasTypeParameters + (generics, id, 0, + NormalRibKind)) { + // Resolve the type parameters. + self.resolve_type_parameters(&generics.ty_params, + visitor); + + // Resolve the trait reference, if necessary. + let original_trait_refs; + match opt_trait_reference { + Some(trait_reference) => { + self.resolve_trait_reference(trait_reference, visitor); + + // Record the current set of trait references. + let mut new_trait_refs = ~[]; + for self.def_map.find(&trait_reference.ref_id).each |&def| { + new_trait_refs.push(def_id_of_def(*def)); + } + original_trait_refs = Some(util::replace( + &mut self.current_trait_refs, + Some(new_trait_refs))); + } + None => { + original_trait_refs = None; + } + } + + // Resolve the self type. + self.resolve_type(self_type, visitor); + + for methods.each |method| { + // We also need a new scope for the method-specific + // type parameters. + self.resolve_method(MethodRibKind( + id, + Provided(method.id)), + *method, + outer_type_parameter_count, + visitor); +/* + let borrowed_type_parameters = &method.tps; + self.resolve_function(MethodRibKind( + id, + Provided(method.id)), + Some(@method.decl), + HasTypeParameters + (borrowed_type_parameters, + method.id, + outer_type_parameter_count, + NormalRibKind), + method.body, + HasSelfBinding(method.self_id), + visitor); +*/ + } + + // Restore the original trait references. + match original_trait_refs { + Some(r) => { self.current_trait_refs = r; } + None => () + } + } + } + + fn resolve_module(@mut self, + module_: &_mod, + span: span, + _name: ident, + id: node_id, + visitor: ResolveVisitor) { + // Write the implementations in scope into the module metadata. + debug!("(resolving module) resolving module ID %d", id); + visit_mod(module_, span, id, (), visitor); + } + + fn resolve_local(@mut self, local: @local, visitor: ResolveVisitor) { + let mutability = if local.node.is_mutbl {Mutable} else {Immutable}; + + // Resolve the type. + self.resolve_type(local.node.ty, visitor); + + // Resolve the initializer, if necessary. + match local.node.init { + None => { + // Nothing to do. + } + Some(initializer) => { + self.resolve_expr(initializer, visitor); + } + } + + // Resolve the pattern. + self.resolve_pattern(local.node.pat, LocalIrrefutableMode, mutability, + None, visitor); + } + + fn binding_mode_map(@mut self, pat: @pat) -> BindingMap { + let mut result = HashMap::new(); + do pat_bindings(self.def_map, pat) |binding_mode, _id, sp, path| { + let ident = path_to_ident(path); + result.insert(ident, + binding_info {span: sp, + binding_mode: binding_mode}); + } + return result; + } + + fn check_consistent_bindings(@mut self, arm: &arm) { + if arm.pats.len() == 0 { return; } + let map_0 = self.binding_mode_map(arm.pats[0]); + for arm.pats.eachi() |i, p| { + let map_i = self.binding_mode_map(*p); + + for map_0.each |&key, &binding_0| { + match map_i.find(&key) { + None => { + self.session.span_err( + p.span, + fmt!("variable `%s` from pattern #1 is \ + not bound in pattern #%u", + *self.session.str_of(key), i + 1)); + } + Some(binding_i) => { + if binding_0.binding_mode != binding_i.binding_mode { + self.session.span_err( + binding_i.span, + fmt!("variable `%s` is bound with different \ + mode in pattern #%u than in pattern #1", + *self.session.str_of(key), i + 1)); + } + } + } + } + + for map_i.each |&key, &binding| { + if !map_0.contains_key(&key) { + self.session.span_err( + binding.span, + fmt!("variable `%s` from pattern #%u is \ + not bound in pattern #1", + *self.session.str_of(key), i + 1)); + } + } + } + } + + fn resolve_arm(@mut self, arm: &arm, visitor: ResolveVisitor) { + self.value_ribs.push(@Rib(NormalRibKind)); + + let bindings_list = @mut HashMap::new(); + for arm.pats.each |pattern| { + self.resolve_pattern(*pattern, RefutableMode, Immutable, + Some(bindings_list), visitor); + } + + // This has to happen *after* we determine which + // pat_idents are variants + self.check_consistent_bindings(arm); + + visit_expr_opt(arm.guard, (), visitor); + self.resolve_block(&arm.body, visitor); + + self.value_ribs.pop(); + } + + fn resolve_block(@mut self, block: &blk, visitor: ResolveVisitor) { + debug!("(resolving block) entering block"); + self.value_ribs.push(@Rib(NormalRibKind)); + + // Move down in the graph, if there's an anonymous module rooted here. + let orig_module = self.current_module; + match self.current_module.anonymous_children.find(&block.node.id) { + None => { /* Nothing to do. */ } + Some(&anonymous_module) => { + debug!("(resolving block) found anonymous module, moving \ + down"); + self.current_module = anonymous_module; + } + } + + // Descend into the block. + visit_block(block, (), visitor); + + // Move back up. + self.current_module = orig_module; + + self.value_ribs.pop(); + debug!("(resolving block) leaving block"); + } + + fn resolve_type(@mut self, ty: @Ty, visitor: ResolveVisitor) { + match ty.node { + // Like path expressions, the interpretation of path types depends + // on whether the path has multiple elements in it or not. + + ty_path(path, path_id) => { + // This is a path in the type namespace. Walk through scopes + // scopes looking for it. + let mut result_def = None; + + // First, check to see whether the name is a primitive type. + if path.idents.len() == 1 { + let name = *path.idents.last(); + + match self.primitive_type_table + .primitive_types + .find(&name) { + + Some(&primitive_type) => { + result_def = + Some(def_prim_ty(primitive_type)); + } + None => { + // Continue. + } + } + } + + match result_def { + None => { + match self.resolve_path(path, TypeNS, true, visitor) { + Some(def) => { + debug!("(resolving type) resolved `%s` to \ + type %?", + *self.session.str_of( + *path.idents.last()), + def); + result_def = Some(def); + } + None => { + result_def = None; + } + } + } + Some(_) => { + // Continue. + } + } + + match result_def { + Some(def) => { + // Write the result into the def map. + debug!("(resolving type) writing resolution for `%s` \ + (id %d)", + self.idents_to_str(path.idents), + path_id); + self.record_def(path_id, def); + } + None => { + self.session.span_err + (ty.span, fmt!("use of undeclared type name `%s`", + self.idents_to_str(path.idents))); + } + } + } + + _ => { + // Just resolve embedded types. + visit_ty(ty, (), visitor); + } + } + } + + fn resolve_pattern(@mut self, + pattern: @pat, + mode: PatternBindingMode, + mutability: Mutability, + // Maps idents to the node ID for the (outermost) + // pattern that binds them + bindings_list: Option<@mut HashMap>, + visitor: ResolveVisitor) { + let pat_id = pattern.id; + do walk_pat(pattern) |pattern| { + match pattern.node { + pat_ident(binding_mode, path, _) + if !path.global && path.idents.len() == 1 => { + + // The meaning of pat_ident with no type parameters + // depends on whether an enum variant or unit-like struct + // with that name is in scope. The probing lookup has to + // be careful not to emit spurious errors. Only matching + // patterns (match) can match nullary variants or + // unit-like structs. For binding patterns (let), matching + // such a value is simply disallowed (since it's rarely + // what you want). + + let ident = path.idents[0]; + + match self.resolve_bare_identifier_pattern(ident) { + FoundStructOrEnumVariant(def) + if mode == RefutableMode => { + debug!("(resolving pattern) resolving `%s` to \ + struct or enum variant", + *self.session.str_of(ident)); + + self.enforce_default_binding_mode( + pattern, + binding_mode, + "an enum variant"); + self.record_def(pattern.id, def); + } + FoundStructOrEnumVariant(_) => { + self.session.span_err(pattern.span, + fmt!("declaration of `%s` \ + shadows an enum \ + variant or unit-like \ + struct in scope", + *self.session + .str_of(ident))); + } + FoundConst(def) if mode == RefutableMode => { + debug!("(resolving pattern) resolving `%s` to \ + constant", + *self.session.str_of(ident)); + + self.enforce_default_binding_mode( + pattern, + binding_mode, + "a constant"); + self.record_def(pattern.id, def); + } + FoundConst(_) => { + self.session.span_err(pattern.span, + ~"only refutable patterns \ + allowed here"); + } + BareIdentifierPatternUnresolved => { + debug!("(resolving pattern) binding `%s`", + *self.session.str_of(ident)); + + let is_mutable = mutability == Mutable; + + let def = match mode { + RefutableMode => { + // For pattern arms, we must use + // `def_binding` definitions. + + def_binding(pattern.id, binding_mode) + } + LocalIrrefutableMode => { + // But for locals, we use `def_local`. + def_local(pattern.id, is_mutable) + } + ArgumentIrrefutableMode => { + // And for function arguments, `def_arg`. + def_arg(pattern.id, is_mutable) + } + }; + + // Record the definition so that later passes + // will be able to distinguish variants from + // locals in patterns. + + self.record_def(pattern.id, def); + + // Add the binding to the local ribs, if it + // doesn't already exist in the bindings list. (We + // must not add it if it's in the bindings list + // because that breaks the assumptions later + // passes make about or-patterns.) + + match bindings_list { + Some(bindings_list) + if !bindings_list.contains_key(&ident) => { + let this = &mut *self; + let last_rib = this.value_ribs[ + this.value_ribs.len() - 1]; + last_rib.bindings.insert(ident, + dl_def(def)); + bindings_list.insert(ident, pat_id); + } + Some(b) => { + if b.find(&ident) == Some(&pat_id) { + // Then this is a duplicate variable + // in the same disjunct, which is an + // error + self.session.span_err(pattern.span, + fmt!("Identifier %s is bound more \ + than once in the same pattern", + path_to_str(path, self.session + .intr()))); + } + // Not bound in the same pattern: do nothing + } + None => { + let this = &mut *self; + let last_rib = this.value_ribs[ + this.value_ribs.len() - 1]; + last_rib.bindings.insert(ident, + dl_def(def)); + } + } + } + } + + // Check the types in the path pattern. + for path.types.each |ty| { + self.resolve_type(*ty, visitor); + } + } + + pat_ident(binding_mode, path, _) => { + // This must be an enum variant, struct, or constant. + match self.resolve_path(path, ValueNS, false, visitor) { + Some(def @ def_variant(*)) | + Some(def @ def_struct(*)) => { + self.record_def(pattern.id, def); + } + Some(def @ def_const(*)) => { + self.enforce_default_binding_mode( + pattern, + binding_mode, + "a constant"); + self.record_def(pattern.id, def); + } + Some(_) => { + self.session.span_err( + path.span, + fmt!("not an enum variant or constant: %s", + *self.session.str_of( + *path.idents.last()))); + } + None => { + self.session.span_err(path.span, + ~"unresolved enum variant"); + } + } + + // Check the types in the path pattern. + for path.types.each |ty| { + self.resolve_type(*ty, visitor); + } + } + + pat_enum(path, _) => { + // This must be an enum variant, struct or const. + match self.resolve_path(path, ValueNS, false, visitor) { + Some(def @ def_fn(*)) | + Some(def @ def_variant(*)) | + Some(def @ def_struct(*)) | + Some(def @ def_const(*)) => { + self.record_def(pattern.id, def); + } + Some(_) => { + self.session.span_err( + path.span, + fmt!("not an enum variant, struct or const: %s", + *self.session.str_of( + *path.idents.last()))); + } + None => { + self.session.span_err(path.span, + ~"unresolved enum variant, \ + struct or const"); + } + } + + // Check the types in the path pattern. + for path.types.each |ty| { + self.resolve_type(*ty, visitor); + } + } + + pat_lit(expr) => { + self.resolve_expr(expr, visitor); + } + + pat_range(first_expr, last_expr) => { + self.resolve_expr(first_expr, visitor); + self.resolve_expr(last_expr, visitor); + } + + pat_struct(path, _, _) => { + let structs: &mut HashSet = &mut self.structs; + match self.resolve_path(path, TypeNS, false, visitor) { + Some(def_ty(class_id)) + if structs.contains(&class_id) => { + let class_def = def_struct(class_id); + self.record_def(pattern.id, class_def); + } + Some(definition @ def_struct(class_id)) + if structs.contains(&class_id) => { + self.record_def(pattern.id, definition); + } + Some(definition @ def_variant(_, variant_id)) + if structs.contains(&variant_id) => { + self.record_def(pattern.id, definition); + } + result => { + debug!("(resolving pattern) didn't find struct \ + def: %?", result); + self.session.span_err( + path.span, + fmt!("`%s` does not name a structure", + self.idents_to_str(path.idents))); + } + } + } + + _ => { + // Nothing to do. + } + } + } + } + + fn resolve_bare_identifier_pattern(@mut self, name: ident) + -> BareIdentifierPatternResolution { + match self.resolve_item_in_lexical_scope(self.current_module, + name, + ValueNS, + SearchThroughModules) { + Success(target) => { + match target.bindings.value_def { + None => { + fail!(~"resolved name in the value namespace to a \ + set of name bindings with no def?!"); + } + Some(def) => { + match def.def { + def @ def_variant(*) | def @ def_struct(*) => { + return FoundStructOrEnumVariant(def); + } + def @ def_const(*) => { + return FoundConst(def); + } + _ => { + return BareIdentifierPatternUnresolved; + } + } + } + } + } + + Indeterminate => { + fail!(~"unexpected indeterminate result"); + } + + Failed => { + return BareIdentifierPatternUnresolved; + } + } + } + + /// If `check_ribs` is true, checks the local definitions first; i.e. + /// doesn't skip straight to the containing module. + fn resolve_path(@mut self, + path: @Path, + namespace: Namespace, + check_ribs: bool, + visitor: ResolveVisitor) + -> Option { + // First, resolve the types. + for path.types.each |ty| { + self.resolve_type(*ty, visitor); + } + + if path.global { + return self.resolve_crate_relative_path(path, + self.xray_context, + namespace); + } + + if path.idents.len() > 1 { + return self.resolve_module_relative_path(path, + self.xray_context, + namespace); + } + + return self.resolve_identifier(*path.idents.last(), + namespace, + check_ribs, + path.span); + } + + fn resolve_identifier(@mut self, + identifier: ident, + namespace: Namespace, + check_ribs: bool, + span: span) + -> Option { + if check_ribs { + match self.resolve_identifier_in_local_ribs(identifier, + namespace, + span) { + Some(def) => { + return Some(def); + } + None => { + // Continue. + } + } + } + + return self.resolve_item_by_identifier_in_lexical_scope(identifier, + namespace); + } + + // FIXME #4952: Merge me with resolve_name_in_module? + fn resolve_definition_of_name_in_module(@mut self, + containing_module: @mut Module, + name: ident, + namespace: Namespace, + xray: XrayFlag) + -> NameDefinition { + // First, search children. + match containing_module.children.find(&name) { + Some(child_name_bindings) => { + match (child_name_bindings.def_for_namespace(namespace), + child_name_bindings.privacy_for_namespace(namespace)) { + (Some(def), Some(Public)) => { + // Found it. Stop the search here. + return ChildNameDefinition(def); + } + (Some(def), _) if xray == Xray => { + // Found it. Stop the search here. + return ChildNameDefinition(def); + } + (Some(_), _) | (None, _) => { + // Continue. + } + } + } + None => { + // Continue. + } + } + + // Next, search import resolutions. + match containing_module.import_resolutions.find(&name) { + Some(import_resolution) if import_resolution.privacy == Public || + xray == Xray => { + match (*import_resolution).target_for_namespace(namespace) { + Some(target) => { + match (target.bindings.def_for_namespace(namespace), + target.bindings.privacy_for_namespace( + namespace)) { + (Some(def), Some(Public)) => { + // Found it. + import_resolution.state.used = true; + return ImportNameDefinition(def); + } + (Some(_), _) | (None, _) => { + // This can happen with external impls, due to + // the imperfect way we read the metadata. + } + } + } + None => {} + } + } + Some(_) | None => {} // Continue. + } + + // Finally, search through external children. + if namespace == TypeNS { + match containing_module.external_module_children.find(&name) { + None => {} + Some(module) => { + match module.def_id { + None => {} // Continue. + Some(def_id) => { + return ChildNameDefinition(def_mod(def_id)); + } + } + } + } + } + + return NoNameDefinition; + } + + fn intern_module_part_of_path(@mut self, path: @Path) -> ~[ident] { + let mut module_path_idents = ~[]; + for path.idents.eachi |index, ident| { + if index == path.idents.len() - 1 { + break; + } + + module_path_idents.push(*ident); + } + + return module_path_idents; + } + + fn resolve_module_relative_path(@mut self, + path: @Path, + xray: XrayFlag, + namespace: Namespace) + -> Option { + let module_path_idents = self.intern_module_part_of_path(path); + + let containing_module; + match self.resolve_module_path_for_import(self.current_module, + module_path_idents, + UseLexicalScope, + path.span) { + Failed => { + self.session.span_err(path.span, + fmt!("use of undeclared module `%s`", + self.idents_to_str( + module_path_idents))); + return None; + } + + Indeterminate => { + fail!(~"indeterminate unexpected"); + } + + Success(resulting_module) => { + containing_module = resulting_module; + } + } + + let name = *path.idents.last(); + match self.resolve_definition_of_name_in_module(containing_module, + name, + namespace, + xray) { + NoNameDefinition => { + // We failed to resolve the name. Report an error. + return None; + } + ChildNameDefinition(def) | ImportNameDefinition(def) => { + return Some(def); + } + } + } + + /// Invariant: This must be called only during main resolution, not during + /// import resolution. + fn resolve_crate_relative_path(@mut self, + path: @Path, + xray: XrayFlag, + namespace: Namespace) + -> Option { + let module_path_idents = self.intern_module_part_of_path(path); + + let root_module = self.graph_root.get_module(); + + let containing_module; + match self.resolve_module_path_from_root(root_module, + module_path_idents, + 0, + path.span, + SearchItemsAndAllImports) { + Failed => { + self.session.span_err(path.span, + fmt!("use of undeclared module `::%s`", + self.idents_to_str( + module_path_idents))); + return None; + } + + Indeterminate => { + fail!(~"indeterminate unexpected"); + } + + Success(resulting_module) => { + containing_module = resulting_module; + } + } + + let name = *path.idents.last(); + match self.resolve_definition_of_name_in_module(containing_module, + name, + namespace, + xray) { + NoNameDefinition => { + // We failed to resolve the name. Report an error. + return None; + } + ChildNameDefinition(def) | ImportNameDefinition(def) => { + return Some(def); + } + } + } + + fn resolve_identifier_in_local_ribs(@mut self, + ident: ident, + namespace: Namespace, + span: span) + -> Option { + // Check the local set of ribs. + let search_result; + match namespace { + ValueNS => { + search_result = self.search_ribs(&mut self.value_ribs, ident, + span, + DontAllowCapturingSelf); + } + TypeNS => { + search_result = self.search_ribs(&mut self.type_ribs, ident, + span, AllowCapturingSelf); + } + } + + match search_result { + Some(dl_def(def)) => { + debug!("(resolving path in local ribs) resolved `%s` to \ + local: %?", + *self.session.str_of(ident), + def); + return Some(def); + } + Some(dl_field) | Some(dl_impl(_)) | None => { + return None; + } + } + } + + fn resolve_item_by_identifier_in_lexical_scope(@mut self, + ident: ident, + namespace: Namespace) + -> Option { + // Check the items. + match self.resolve_item_in_lexical_scope(self.current_module, + ident, + namespace, + DontSearchThroughModules) { + Success(target) => { + match (*target.bindings).def_for_namespace(namespace) { + None => { + // This can happen if we were looking for a type and + // found a module instead. Modules don't have defs. + return None; + } + Some(def) => { + debug!("(resolving item path in lexical scope) \ + resolved `%s` to item", + *self.session.str_of(ident)); + return Some(def); + } + } + } + Indeterminate => { + fail!(~"unexpected indeterminate result"); + } + Failed => { + return None; + } + } + } + + fn find_best_match_for_name(@mut self, name: &str, max_distance: uint) -> Option<~str> { + let this = &mut *self; + + let mut maybes: ~[~str] = ~[]; + let mut values: ~[uint] = ~[]; + + let mut j = this.value_ribs.len(); + while j != 0 { + j -= 1; + for this.value_ribs[j].bindings.each_key |&k| { + vec::push(&mut maybes, copy *this.session.str_of(k)); + vec::push(&mut values, uint::max_value); + } + } + + let mut smallest = 0; + for vec::eachi(maybes) |i, &other| { + + values[i] = str::levdistance(name, other); + + if values[i] <= values[smallest] { + smallest = i; + } + } + + if vec::len(values) > 0 && + values[smallest] != uint::max_value && + values[smallest] < str::len(name) + 2 && + values[smallest] <= max_distance && + maybes[smallest] != name.to_owned() { + + Some(vec::swap_remove(&mut maybes, smallest)) + + } else { + None + } + } + + fn name_exists_in_scope_struct(@mut self, name: &str) -> bool { + let this = &mut *self; + + let mut i = this.type_ribs.len(); + while i != 0 { + i -= 1; + match this.type_ribs[i].kind { + MethodRibKind(node_id, _) => + for this.crate.node.module.items.each |item| { + if item.id == node_id { + match item.node { + item_struct(class_def, _) => { + for vec::each(class_def.fields) |field| { + match field.node.kind { + unnamed_field => {}, + named_field(ident, _, _) => { + if str::eq_slice(*this.session.str_of(ident), + name) { + return true + } + } + } + } + } + _ => {} + } + } + }, + _ => {} + } + } + return false; + } + + fn resolve_expr(@mut self, expr: @expr, visitor: ResolveVisitor) { + // First, record candidate traits for this expression if it could + // result in the invocation of a method call. + + self.record_candidate_traits_for_expr_if_necessary(expr); + + // Next, resolve the node. + match expr.node { + // The interpretation of paths depends on whether the path has + // multiple elements in it or not. + + expr_path(path) => { + // This is a local path in the value namespace. Walk through + // scopes looking for it. + + match self.resolve_path(path, ValueNS, true, visitor) { + Some(def) => { + // Write the result into the def map. + debug!("(resolving expr) resolved `%s`", + self.idents_to_str(path.idents)); + self.record_def(expr.id, def); + } + None => { + let wrong_name = self.idents_to_str( + path.idents); + if self.name_exists_in_scope_struct(wrong_name) { + self.session.span_err(expr.span, + fmt!("unresolved name: `%s`. \ + Did you mean: `self.%s`?", + wrong_name, + wrong_name)); + } + else { + // limit search to 5 to reduce the number + // of stupid suggestions + match self.find_best_match_for_name(wrong_name, 5) { + Some(m) => { + self.session.span_err(expr.span, + fmt!("unresolved name: `%s`. \ + Did you mean: `%s`?", + wrong_name, m)); + } + None => { + self.session.span_err(expr.span, + fmt!("unresolved name: `%s`.", + wrong_name)); + } + } + } + } + } + + visit_expr(expr, (), visitor); + } + + expr_fn_block(ref fn_decl, ref block) => { + self.resolve_function(FunctionRibKind(expr.id, block.node.id), + Some(fn_decl), + NoTypeParameters, + block, + NoSelfBinding, + visitor); + } + + expr_struct(path, _, _) => { + // Resolve the path to the structure it goes to. + let structs: &mut HashSet = &mut self.structs; + match self.resolve_path(path, TypeNS, false, visitor) { + Some(def_ty(class_id)) | Some(def_struct(class_id)) + if structs.contains(&class_id) => { + let class_def = def_struct(class_id); + self.record_def(expr.id, class_def); + } + Some(definition @ def_variant(_, class_id)) + if structs.contains(&class_id) => { + self.record_def(expr.id, definition); + } + _ => { + self.session.span_err( + path.span, + fmt!("`%s` does not name a structure", + self.idents_to_str(path.idents))); + } + } + + visit_expr(expr, (), visitor); + } + + expr_loop(_, Some(label)) => { + do self.with_label_rib { + let this = &mut *self; + let def_like = dl_def(def_label(expr.id)); + let rib = this.label_ribs[this.label_ribs.len() - 1]; + rib.bindings.insert(label, def_like); + + visit_expr(expr, (), visitor); + } + } + + expr_break(Some(label)) | expr_again(Some(label)) => { + match self.search_ribs(&mut self.label_ribs, label, expr.span, + DontAllowCapturingSelf) { + None => + self.session.span_err(expr.span, + fmt!("use of undeclared label \ + `%s`", + *self.session.str_of( + label))), + Some(dl_def(def @ def_label(_))) => + self.record_def(expr.id, def), + Some(_) => + self.session.span_bug(expr.span, + ~"label wasn't mapped to a \ + label def!") + } + } + + _ => { + visit_expr(expr, (), visitor); + } + } + } + + fn record_candidate_traits_for_expr_if_necessary(@mut self, expr: @expr) { + match expr.node { + expr_field(_, ident, _) => { + let traits = self.search_for_traits_containing_method(ident); + self.trait_map.insert(expr.id, @mut traits); + } + expr_method_call(_, ident, _, _, _) => { + let traits = self.search_for_traits_containing_method(ident); + self.trait_map.insert(expr.id, @mut traits); + } + expr_binary(add, _, _) | expr_assign_op(add, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.add_trait()); + } + expr_binary(subtract, _, _) | expr_assign_op(subtract, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.sub_trait()); + } + expr_binary(mul, _, _) | expr_assign_op(mul, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.mul_trait()); + } + expr_binary(quot, _, _) | expr_assign_op(quot, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.quot_trait()); + } + expr_binary(rem, _, _) | expr_assign_op(rem, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.rem_trait()); + } + expr_binary(bitxor, _, _) | expr_assign_op(bitxor, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.bitxor_trait()); + } + expr_binary(bitand, _, _) | expr_assign_op(bitand, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.bitand_trait()); + } + expr_binary(bitor, _, _) | expr_assign_op(bitor, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.bitor_trait()); + } + expr_binary(shl, _, _) | expr_assign_op(shl, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.shl_trait()); + } + expr_binary(shr, _, _) | expr_assign_op(shr, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.shr_trait()); + } + expr_binary(lt, _, _) | expr_binary(le, _, _) | + expr_binary(ge, _, _) | expr_binary(gt, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.ord_trait()); + } + expr_binary(eq, _, _) | expr_binary(ne, _, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.eq_trait()); + } + expr_unary(neg, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.neg_trait()); + } + expr_unary(not, _) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.not_trait()); + } + expr_index(*) => { + self.add_fixed_trait_for_expr(expr.id, + self.lang_items.index_trait()); + } + _ => { + // Nothing to do. + } + } + } + + fn search_for_traits_containing_method(@mut self, + name: ident) + -> ~[def_id] { + debug!("(searching for traits containing method) looking for '%s'", + *self.session.str_of(name)); + + let mut found_traits = ~[]; + let mut search_module = self.current_module; + loop { + // Look for the current trait. + match /*bad*/copy self.current_trait_refs { + Some(trait_def_ids) => { + for trait_def_ids.each |trait_def_id| { + self.add_trait_info_if_containing_method( + &mut found_traits, *trait_def_id, name); + } + } + None => { + // Nothing to do. + } + } + + // Look for trait children. + for search_module.children.each_value |&child_name_bindings| { + match child_name_bindings.def_for_namespace(TypeNS) { + Some(def) => { + match def { + def_trait(trait_def_id) => { + self.add_trait_info_if_containing_method( + &mut found_traits, trait_def_id, name); + } + _ => { + // Continue. + } + } + } + None => { + // Continue. + } + } + } + + // Look for imports. + for search_module.import_resolutions.each_value + |&import_resolution| { + + match import_resolution.target_for_namespace(TypeNS) { + None => { + // Continue. + } + Some(target) => { + match target.bindings.def_for_namespace(TypeNS) { + Some(def) => { + match def { + def_trait(trait_def_id) => { + let added = self. + add_trait_info_if_containing_method( + &mut found_traits, + trait_def_id, name); + if added { + import_resolution.state.used = + true; + } + } + _ => { + // Continue. + } + } + } + None => { + // Continue. + } + } + } + } + } + + // Move to the next parent. + match search_module.parent_link { + NoParentLink => { + // Done. + break; + } + ModuleParentLink(parent_module, _) | + BlockParentLink(parent_module, _) => { + search_module = parent_module; + } + } + } + + return found_traits; + } + + fn add_trait_info_if_containing_method(&self, + found_traits: &mut ~[def_id], + trait_def_id: def_id, + name: ident) + -> bool { + debug!("(adding trait info if containing method) trying trait %d:%d \ + for method '%s'", + trait_def_id.crate, + trait_def_id.node, + *self.session.str_of(name)); + + match self.trait_info.find(&trait_def_id) { + Some(trait_info) if trait_info.contains(&name) => { + debug!("(adding trait info if containing method) found trait \ + %d:%d for method '%s'", + trait_def_id.crate, + trait_def_id.node, + *self.session.str_of(name)); + found_traits.push(trait_def_id); + true + } + Some(_) | None => { + false + } + } + } + + fn add_fixed_trait_for_expr(@mut self, + expr_id: node_id, + trait_id: def_id) { + self.trait_map.insert(expr_id, @mut ~[trait_id]); + } + + fn record_def(@mut self, node_id: node_id, def: def) { + debug!("(recording def) recording %? for %?", def, node_id); + self.def_map.insert(node_id, def); + } + + fn enforce_default_binding_mode(@mut self, + pat: @pat, + pat_binding_mode: binding_mode, + descr: &str) { + match pat_binding_mode { + bind_infer => {} + bind_by_copy => { + self.session.span_err( + pat.span, + fmt!("cannot use `copy` binding mode with %s", + descr)); + } + bind_by_ref(*) => { + self.session.span_err( + pat.span, + fmt!("cannot use `ref` binding mode with %s", + descr)); + } + } + } + + // + // main function checking + // + // be sure that there is only one main function + // + fn check_duplicate_main(@mut self) { + let this = &mut *self; + if this.attr_main_fn.is_none() && this.start_fn.is_none() { + if this.main_fns.len() >= 1u { + let mut i = 1u; + while i < this.main_fns.len() { + let (_, dup_main_span) = this.main_fns[i].unwrap(); + this.session.span_err( + dup_main_span, + ~"multiple 'main' functions"); + i += 1; + } + *this.session.entry_fn = this.main_fns[0]; + *this.session.entry_type = Some(session::EntryMain); + } + } else if !this.start_fn.is_none() { + *this.session.entry_fn = this.start_fn; + *this.session.entry_type = Some(session::EntryStart); + } else { + *this.session.entry_fn = this.attr_main_fn; + *this.session.entry_type = Some(session::EntryMain); + } + } + + // + // Unused import checking + // + // Although this is a lint pass, it lives in here because it depends on + // resolve data structures. + // + + fn unused_import_lint_level(@mut self, m: @mut Module) -> level { + let settings = self.session.lint_settings; + match m.def_id { + Some(def) => get_lint_settings_level(settings, unused_imports, + def.node, def.node), + None => get_lint_level(settings.default_settings, unused_imports) + } + } + + fn check_for_unused_imports_if_necessary(@mut self) { + if self.unused_import_lint_level(self.current_module) == allow { + return; + } + + let root_module = self.graph_root.get_module(); + self.check_for_unused_imports_in_module_subtree(root_module); + } + + fn check_for_unused_imports_in_module_subtree(@mut self, + module_: @mut Module) { + // If this isn't a local crate, then bail out. We don't need to check + // for unused imports in external crates. + + match module_.def_id { + Some(def_id) if def_id.crate == local_crate => { + // OK. Continue. + } + None => { + // Check for unused imports in the root module. + } + Some(_) => { + // Bail out. + debug!("(checking for unused imports in module subtree) not \ + checking for unused imports for `%s`", + self.module_to_str(module_)); + return; + } + } + + self.check_for_unused_imports_in_module(module_); + + for module_.children.each_value |&child_name_bindings| { + match (*child_name_bindings).get_module_if_available() { + None => { + // Nothing to do. + } + Some(child_module) => { + self.check_for_unused_imports_in_module_subtree + (child_module); + } + } + } + + for module_.anonymous_children.each_value |&child_module| { + self.check_for_unused_imports_in_module_subtree(child_module); + } + } + + fn check_for_unused_imports_in_module(@mut self, module_: @mut Module) { + for module_.import_resolutions.each_value |&import_resolution| { + // Ignore dummy spans for things like automatically injected + // imports for the prelude, and also don't warn about the same + // import statement being unused more than once. Furthermore, if + // the import is public, then we can't be sure whether it's unused + // or not so don't warn about it. + if !import_resolution.state.used && + !import_resolution.state.warned && + import_resolution.span != dummy_sp() && + import_resolution.privacy != Public { + import_resolution.state.warned = true; + let span = import_resolution.span; + self.session.span_lint_level( + self.unused_import_lint_level(module_), + span, + ~"unused import"); + } + } + } + + + // + // Diagnostics + // + // Diagnostics are not particularly efficient, because they're rarely + // hit. + // + + /// A somewhat inefficient routine to obtain the name of a module. + fn module_to_str(@mut self, module_: @mut Module) -> ~str { + let mut idents = ~[]; + let mut current_module = module_; + loop { + match current_module.parent_link { + NoParentLink => { + break; + } + ModuleParentLink(module_, name) => { + idents.push(name); + current_module = module_; + } + BlockParentLink(module_, _) => { + idents.push(special_idents::opaque); + current_module = module_; + } + } + } + + if idents.len() == 0 { + return ~"???"; + } + return self.idents_to_str(vec::reversed(idents)); + } + + fn dump_module(@mut self, module_: @mut Module) { + debug!("Dump of module `%s`:", self.module_to_str(module_)); + + debug!("Children:"); + for module_.children.each_key |&name| { + debug!("* %s", *self.session.str_of(name)); + } + + debug!("Import resolutions:"); + for module_.import_resolutions.each |name, import_resolution| { + let value_repr; + match import_resolution.target_for_namespace(ValueNS) { + None => { value_repr = ~""; } + Some(_) => { + value_repr = ~" value:?"; + // FIXME #4954 + } + } + + let type_repr; + match import_resolution.target_for_namespace(TypeNS) { + None => { type_repr = ~""; } + Some(_) => { + type_repr = ~" type:?"; + // FIXME #4954 + } + } + + debug!("* %s:%s%s", *self.session.str_of(*name), + value_repr, type_repr); + } + } +} + +pub struct CrateMap { + def_map: DefMap, + exp_map2: ExportMap2, + trait_map: TraitMap +} + +/// Entry point to crate resolution. +pub fn resolve_crate(session: Session, + lang_items: LanguageItems, + crate: @crate) + -> CrateMap { + let resolver = @mut Resolver(session, lang_items, crate); + resolver.resolve(); + let @Resolver{def_map, export_map2, trait_map, _} = resolver; + CrateMap { + def_map: def_map, + exp_map2: export_map2, + trait_map: trait_map + } +} diff --git a/src/librustc/middle/trans/write_guard.rs b/src/librustc/middle/trans/write_guard.rs new file mode 100644 index 0000000000000..18f21b489b0b8 --- /dev/null +++ b/src/librustc/middle/trans/write_guard.rs @@ -0,0 +1,201 @@ +// Copyright 2012 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. + +//! Logic relating to rooting and write guards for managed values +//! (`@` and `@mut`). This code is primarily for use by datum; +//! it exists in its own module both to keep datum.rs bite-sized +//! and for each in debugging (e.g., so you can use +//! `RUST_LOG=rustc::middle::trans::write_guard`). + +use lib::llvm::ValueRef; +use middle::borrowck::{RootInfo, root_map_key, DynaImm, DynaMut}; +use middle::trans::base::*; +use middle::trans::build::*; +use middle::trans::callee; +use middle::trans::common::*; +use middle::trans::datum::*; +use middle::trans::expr; +use middle::ty; +use driver::session; +use syntax::codemap::span; +use syntax::ast; + +pub fn root_and_write_guard(datum: &Datum, + mut bcx: block, + span: span, + expr_id: ast::node_id, + derefs: uint) -> block { + let key = root_map_key { id: expr_id, derefs: derefs }; + debug!("write_guard::root_and_write_guard(key=%?)", key); + + // root the autoderef'd value, if necessary: + // + // (Note: root'd values are always boxes) + let ccx = bcx.ccx(); + bcx = match ccx.maps.root_map.find(&key) { + None => bcx, + Some(&root_info) => root(datum, bcx, span, key, root_info) + }; + + // Perform the write guard, if necessary. + // + // (Note: write-guarded values are always boxes) + if ccx.maps.write_guard_map.contains(&key) { + perform_write_guard(datum, bcx, span) + } else { + bcx + } +} + +pub fn return_to_mut(mut bcx: block, + root_key: root_map_key, + frozen_val_ref: ValueRef, + bits_val_ref: ValueRef, + filename_val: ValueRef, + line_val: ValueRef) -> block { + debug!("write_guard::return_to_mut(root_key=%?, %s, %s, %s)", + root_key, + bcx.to_str(), + val_str(bcx.ccx().tn, frozen_val_ref), + val_str(bcx.ccx().tn, bits_val_ref)); + + let box_ptr = + Load(bcx, PointerCast(bcx, + frozen_val_ref, + T_ptr(T_ptr(T_i8())))); + + let bits_val = + Load(bcx, bits_val_ref); + + if bcx.tcx().sess.opts.optimize == session::No { + bcx = callee::trans_lang_call( + bcx, + bcx.tcx().lang_items.unrecord_borrow_fn(), + ~[ + box_ptr, + bits_val, + filename_val, + line_val + ], + expr::Ignore); + } + + callee::trans_lang_call( + bcx, + bcx.tcx().lang_items.return_to_mut_fn(), + ~[ + box_ptr, + bits_val, + filename_val, + line_val + ], + expr::Ignore + ) +} + +fn root(datum: &Datum, + mut bcx: block, + span: span, + root_key: root_map_key, + root_info: RootInfo) -> block { + //! In some cases, borrowck will decide that an @T/@[]/@str + //! value must be rooted for the program to be safe. In that + //! case, we will call this function, which will stash a copy + //! away until we exit the scope `scope_id`. + + debug!("write_guard::root(root_key=%?, root_info=%?, datum=%?)", + root_key, root_info, datum.to_str(bcx.ccx())); + + if bcx.sess().trace() { + trans_trace( + bcx, None, + @fmt!("preserving until end of scope %d", + root_info.scope)); + } + + // First, root the datum. Note that we must zero this value, + // because sometimes we root on one path but not another. + // See e.g. #4904. + let scratch = scratch_datum(bcx, datum.ty, true); + datum.copy_to_datum(bcx, INIT, scratch); + let cleanup_bcx = find_bcx_for_scope(bcx, root_info.scope); + add_clean_temp_mem(cleanup_bcx, scratch.val, scratch.ty); + + // Now, consider also freezing it. + match root_info.freeze { + None => {} + Some(freeze_kind) => { + let (filename, line) = filename_and_line_num_from_span(bcx, span); + + // in this case, we don't have to zero, because + // scratch.val will be NULL should the cleanup get + // called without the freezing actually occurring, and + // return_to_mut checks for this condition. + let scratch_bits = scratch_datum(bcx, ty::mk_uint(), false); + + let freeze_did = match freeze_kind { + DynaImm => bcx.tcx().lang_items.borrow_as_imm_fn(), + DynaMut => bcx.tcx().lang_items.borrow_as_mut_fn(), + }; + + let box_ptr = Load(bcx, + PointerCast(bcx, + scratch.val, + T_ptr(T_ptr(T_i8())))); + + bcx = callee::trans_lang_call( + bcx, + freeze_did, + ~[ + box_ptr, + filename, + line + ], + expr::SaveIn(scratch_bits.val)); + + if bcx.tcx().sess.opts.optimize == session::No { + bcx = callee::trans_lang_call( + bcx, + bcx.tcx().lang_items.record_borrow_fn(), + ~[ + box_ptr, + Load(bcx, scratch_bits.val), + filename, + line + ], + expr::Ignore); + } + + add_clean_return_to_mut( + cleanup_bcx, root_key, scratch.val, scratch_bits.val, + filename, line); + } + } + + bcx +} + +fn perform_write_guard(datum: &Datum, + bcx: block, + span: span) -> block { + debug!("perform_write_guard"); + + let llval = datum.to_value_llval(bcx); + let (filename, line) = filename_and_line_num_from_span(bcx, span); + + callee::trans_lang_call( + bcx, + bcx.tcx().lang_items.check_not_borrowed_fn(), + ~[PointerCast(bcx, llval, T_ptr(T_i8())), + filename, + line], + expr::Ignore) +} + From bf2d3c71e37d3b7aabe57a3d9ea3fada449715c1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 May 2013 14:25:15 -0400 Subject: [PATCH 32/46] improve DEBUG_BORROW printouts --- src/libcore/cleanup.rs | 17 +++-- src/libcore/rt/env.rs | 2 + src/libcore/unstable/lang.rs | 139 ++++++++++++++++++++++++----------- src/rt/rust_env.cpp | 2 + src/rt/rust_env.h | 1 + 5 files changed, 109 insertions(+), 52 deletions(-) diff --git a/src/libcore/cleanup.rs b/src/libcore/cleanup.rs index 5e2f4af184dc4..3f7366c6c452b 100644 --- a/src/libcore/cleanup.rs +++ b/src/libcore/cleanup.rs @@ -167,7 +167,8 @@ fn debug_mem() -> bool { #[cfg(notest)] #[lang="annihilate"] pub unsafe fn annihilate() { - use unstable::lang::{local_free, debug_ptr}; + use unstable::lang::{local_free}; + use unstable::lang; use io::WriterUtil; use io; use libc; @@ -191,10 +192,10 @@ pub unsafe fn annihilate() { for each_live_alloc(true) |box, uniq| { stats.n_total_boxes += 1; if uniq { - debug_ptr("Managed-uniq: ", &*box); + lang::debug_mem("Managed-uniq: ", &*box); stats.n_unique_boxes += 1; } else { - debug_ptr("Immortalizing: ", &*box); + lang::debug_mem("Immortalizing: ", &*box); (*box).header.ref_count = managed::raw::RC_IMMORTAL; } } @@ -206,13 +207,13 @@ pub unsafe fn annihilate() { // callback, as the original value may have been freed. for each_live_alloc(false) |box, uniq| { if !uniq { - debug_ptr("Invoking tydesc/glue on: ", &*box); + lang::debug_mem("Invoking tydesc/glue on: ", &*box); let tydesc: *TypeDesc = transmute(copy (*box).header.type_desc); let drop_glue: DropGlue = transmute(((*tydesc).drop_glue, 0)); - debug_ptr("Box data: ", &(*box).data); - debug_ptr("Type descriptor: ", tydesc); + lang::debug_mem("Box data: ", &(*box).data); + lang::debug_mem("Type descriptor: ", tydesc); drop_glue(to_unsafe_ptr(&tydesc), transmute(&(*box).data)); - debug_ptr("Dropped ", &*box); + lang::debug_mem("Dropped ", &*box); } } @@ -224,7 +225,7 @@ pub unsafe fn annihilate() { // not be valid after. for each_live_alloc(true) |box, uniq| { if !uniq { - debug_ptr("About to free: ", &*box); + lang::debug_mem("About to free: ", &*box); stats.n_bytes_freed += (*((*box).header.type_desc)).size + sys::size_of::(); diff --git a/src/libcore/rt/env.rs b/src/libcore/rt/env.rs index e479375401a3b..1d7ff17314901 100644 --- a/src/libcore/rt/env.rs +++ b/src/libcore/rt/env.rs @@ -33,6 +33,8 @@ pub struct Environment { argv: **c_char, /// Print GC debugging info (true if env var RUST_DEBUG_MEM is set) debug_mem: bool, + /// Print GC debugging info (true if env var RUST_DEBUG_BORROW is set) + debug_borrow: bool, } /// Get the global environment settings diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index 01ab2345918b1..5a65a5c24bb57 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -20,6 +20,7 @@ use unstable::exchange_alloc; use cast::transmute; use task::rt::rust_get_task; use option::{Option, Some, None}; +use io; #[allow(non_camel_case_types)] pub type rust_task = c_void; @@ -109,8 +110,8 @@ pub unsafe fn clear_task_borrow_list() { let _ = try_take_task_borrow_list(); } -fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { - debug_ptr("fail_borrowed: ", box); +unsafe fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { + debug_borrow("fail_borrowed: ", box, 0, 0, file, line); match try_take_task_borrow_list() { None => { // not recording borrows @@ -145,42 +146,95 @@ fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { #[inline(always)] pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char { let result = transmute(exchange_alloc::malloc(transmute(td), transmute(size))); - debug_ptr("exchange_malloc: ", result); + debug_mem("exchange_malloc: ", result); return result; } /// Because this code is so perf. sensitive, use a static constant so that /// debug printouts are compiled out most of the time. -static ENABLE_DEBUG_PTR: bool = true; +static ENABLE_DEBUG: bool = true; #[inline] -pub fn debug_ptr(tag: &'static str, p: *const T) { +pub fn debug_mem(tag: &'static str, p: *const T) { //! A useful debugging function that prints a pointer + tag + newline //! without allocating memory. - if ENABLE_DEBUG_PTR && ::rt::env::get().debug_mem { - debug_ptr_slow(tag, p); + if ENABLE_DEBUG && ::rt::env::get().debug_mem { + debug_mem_slow(tag, p); } - fn debug_ptr_slow(tag: &'static str, p: *const T) { - use io; + fn debug_mem_slow(tag: &'static str, p: *const T) { let dbg = STDERR_FILENO as io::fd_t; - let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', - '9', 'a', 'b', 'c', 'd', 'e', 'f']; dbg.write_str(tag); + dbg.write_hex(p as uint); + dbg.write_str("\n"); + } +} + +#[inline] +unsafe fn debug_borrow(tag: &'static str, + p: *const T, + old_bits: uint, + new_bits: uint, + filename: *c_char, + line: size_t) { + //! A useful debugging function that prints a pointer + tag + newline + //! without allocating memory. + + if ENABLE_DEBUG && ::rt::env::get().debug_borrow { + debug_borrow_slow(tag, p, old_bits, new_bits, filename, line); + } + + unsafe fn debug_borrow_slow(tag: &'static str, + p: *const T, + old_bits: uint, + new_bits: uint, + filename: *c_char, + line: size_t) { + let dbg = STDERR_FILENO as io::fd_t; + dbg.write_str(tag); + dbg.write_hex(p as uint); + dbg.write_str(" "); + dbg.write_hex(old_bits); + dbg.write_str(" "); + dbg.write_hex(new_bits); + dbg.write_str(" "); + dbg.write_cstr(filename); + dbg.write_str(":"); + dbg.write_hex(line as uint); + dbg.write_str("\n"); + } +} + +trait DebugPrints { + fn write_hex(&self, val: uint); + unsafe fn write_cstr(&self, str: *c_char); +} +impl DebugPrints for io::fd_t { + fn write_hex(&self, mut i: uint) { + let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'a', 'b', 'c', 'd', 'e', 'f']; static uint_nibbles: uint = ::uint::bytes << 1; let mut buffer = [0_u8, ..uint_nibbles+1]; - let mut i = p as uint; let mut c = uint_nibbles; while c > 0 { c -= 1; buffer[c] = letters[i & 0xF] as u8; i >>= 4; } - dbg.write(buffer.slice(0, uint_nibbles)); + self.write(buffer.slice(0, uint_nibbles)); + } - dbg.write_str("\n"); + unsafe fn write_cstr(&self, p: *c_char) { + use libc::strlen; + use vec; + + let len = strlen(p); + let p: *u8 = transmute(p); + do vec::raw::buf_as_slice(p, len as uint) |s| { + self.write(s); + } } } @@ -190,7 +244,7 @@ pub fn debug_ptr(tag: &'static str, p: *const T) { #[lang="exchange_free"] #[inline(always)] pub unsafe fn exchange_free(ptr: *c_char) { - debug_ptr("exchange_free: ", ptr); + debug_mem("exchange_free: ", ptr); exchange_alloc::free(transmute(ptr)) } @@ -198,7 +252,7 @@ pub unsafe fn exchange_free(ptr: *c_char) { #[inline(always)] pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char { let result = rustrt::rust_upcall_malloc_noswitch(td, size); - debug_ptr("local_malloc: ", result); + debug_mem("local_malloc: ", result); return result; } @@ -208,7 +262,7 @@ pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char { #[lang="free"] #[inline(always)] pub unsafe fn local_free(ptr: *c_char) { - debug_ptr("local_free: ", ptr); + debug_mem("local_free: ", ptr); rustrt::rust_upcall_free_noswitch(ptr); } @@ -225,19 +279,18 @@ pub unsafe fn borrow_as_imm(a: *u8) { #[inline(always)] pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint { let a: *mut BoxRepr = transmute(a); - let ref_count = (*a).header.ref_count; + let old_ref_count = (*a).header.ref_count; + let new_ref_count = old_ref_count | FROZEN_BIT; - debug_ptr("borrow_as_imm (ptr) :", a); - debug_ptr(" (ref) :", ref_count as *()); - debug_ptr(" (line): ", line as *()); + debug_borrow("borrow_as_imm:", a, old_ref_count, new_ref_count, file, line); - if (ref_count & MUT_BIT) != 0 { + if (old_ref_count & MUT_BIT) != 0 { fail_borrowed(a, file, line); } - (*a).header.ref_count = ref_count | FROZEN_BIT; + (*a).header.ref_count = new_ref_count; - ref_count + old_ref_count } #[cfg(not(stage0))] @@ -245,18 +298,18 @@ pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint { #[inline(always)] pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint { let a: *mut BoxRepr = transmute(a); + let old_ref_count = (*a).header.ref_count; + let new_ref_count = old_ref_count | MUT_BIT | FROZEN_BIT; - debug_ptr("borrow_as_mut (ptr): ", a); - debug_ptr(" (line): ", line as *()); + debug_borrow("borrow_as_mut:", a, old_ref_count, new_ref_count, file, line); - let ref_count = (*a).header.ref_count; - if (ref_count & (MUT_BIT|FROZEN_BIT)) != 0 { + if (old_ref_count & (MUT_BIT|FROZEN_BIT)) != 0 { fail_borrowed(a, file, line); } - (*a).header.ref_count = ref_count | MUT_BIT | FROZEN_BIT; + (*a).header.ref_count = new_ref_count; - ref_count + old_ref_count } @@ -267,6 +320,7 @@ pub unsafe fn record_borrow(a: *u8, old_ref_count: uint, if (old_ref_count & ALL_BITS) == 0 { // was not borrowed before let a: *mut BoxRepr = transmute(a); + debug_borrow("record_borrow:", a, old_ref_count, 0, file, line); do swap_task_borrow_list |borrow_list| { let mut borrow_list = borrow_list; borrow_list.push(BorrowRecord {box: a, file: file, line: line}); @@ -282,6 +336,7 @@ pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint, if (old_ref_count & ALL_BITS) == 0 { // was not borrowed before let a: *mut BoxRepr = transmute(a); + debug_borrow("unrecord_borrow:", a, old_ref_count, 0, file, line); do swap_task_borrow_list |borrow_list| { let mut borrow_list = borrow_list; let br = BorrowRecord {box: a, file: file, line: line}; @@ -317,21 +372,20 @@ pub unsafe fn return_to_mut(a: *u8) { #[cfg(not(stage0))] #[lang="return_to_mut"] #[inline(always)] -pub unsafe fn return_to_mut(a: *u8, old_ref_count: uint, +pub unsafe fn return_to_mut(a: *u8, orig_ref_count: uint, file: *c_char, line: size_t) { // Sometimes the box is null, if it is conditionally frozen. // See e.g. #4904. if !a.is_null() { let a: *mut BoxRepr = transmute(a); - let ref_count = (*a).header.ref_count; - let combined = (ref_count & !ALL_BITS) | (old_ref_count & ALL_BITS); - (*a).header.ref_count = combined; - - debug_ptr("return_to_mut (ptr) : ", a); - debug_ptr(" (line): ", line as *()); - debug_ptr(" (old) : ", old_ref_count as *()); - debug_ptr(" (new) : ", ref_count as *()); - debug_ptr(" (comb): ", combined as *()); + let old_ref_count = (*a).header.ref_count; + let new_ref_count = + (old_ref_count & !ALL_BITS) | (orig_ref_count & ALL_BITS); + + debug_borrow("return_to_mut:", + a, old_ref_count, new_ref_count, file, line); + + (*a).header.ref_count = new_ref_count; } } @@ -355,10 +409,7 @@ pub unsafe fn check_not_borrowed(a: *u8, line: size_t) { let a: *mut BoxRepr = transmute(a); let ref_count = (*a).header.ref_count; - debug_ptr("check_not_borrowed (ptr) : ", a); - debug_ptr(" (line): ", line as *()); - debug_ptr(" (rc) : ", ref_count as *()); - + debug_borrow("check_not_borrowed:", a, ref_count, 0, file, line); if (ref_count & FROZEN_BIT) != 0 { fail_borrowed(a, file, line); } diff --git a/src/rt/rust_env.cpp b/src/rt/rust_env.cpp index 041b4efac52a2..360d611492853 100644 --- a/src/rt/rust_env.cpp +++ b/src/rt/rust_env.cpp @@ -24,6 +24,7 @@ #define RUST_SEED "RUST_SEED" #define RUST_POISON_ON_FREE "RUST_POISON_ON_FREE" #define RUST_DEBUG_MEM "RUST_DEBUG_MEM" +#define RUST_DEBUG_BORROW "RUST_DEBUG_BORROW" #if defined(__WIN32__) static int @@ -130,6 +131,7 @@ load_env(int argc, char **argv) { env->argc = argc; env->argv = argv; env->debug_mem = getenv(RUST_DEBUG_MEM) != NULL; + env->debug_borrow = getenv(RUST_DEBUG_BORROW) != NULL; return env; } diff --git a/src/rt/rust_env.h b/src/rt/rust_env.h index df27f7674f265..b897f0c09a90b 100644 --- a/src/rt/rust_env.h +++ b/src/rt/rust_env.h @@ -28,6 +28,7 @@ struct rust_env { int argc; char **argv; rust_bool debug_mem; + rust_bool debug_borrow; }; rust_env* load_env(int argc, char **argv); From ccf2f7b979ad4e4defd9b856f6d16108c5760829 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 May 2013 14:29:08 -0400 Subject: [PATCH 33/46] make asm_comments something that you opt in to --- src/librustc/driver/driver.rs | 5 ----- src/librustc/driver/session.rs | 6 +++--- src/librustc/middle/borrowck/check_loans.rs | 2 +- src/librustc/middle/mem_categorization.rs | 1 - src/librustc/middle/trans/base.rs | 2 +- src/librustc/middle/trans/build.rs | 4 ++-- src/librustc/middle/trans/closure.rs | 2 +- 7 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index e899b1abc2648..6ce62a1382d3d 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -600,11 +600,6 @@ pub fn build_session_options(binary: @~str, let target_opt = getopts::opt_maybe_str(matches, ~"target"); let target_feature_opt = getopts::opt_maybe_str(matches, ~"target-feature"); let save_temps = getopts::opt_present(matches, ~"save-temps"); - match output_type { - // unless we're emitting huamn-readable assembly, omit comments. - link::output_type_llvm_assembly | link::output_type_assembly => (), - _ => debugging_opts |= session::no_asm_comments - } let opt_level = { if (debugging_opts & session::no_opt) != 0 { No diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index fff97d2436af3..15067b785d9ca 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -45,7 +45,7 @@ pub static time_passes: uint = 1 << 1; pub static count_llvm_insns: uint = 1 << 2; pub static time_llvm_passes: uint = 1 << 3; pub static trans_stats: uint = 1 << 4; -pub static no_asm_comments: uint = 1 << 5; +pub static asm_comments: uint = 1 << 5; pub static no_verify: uint = 1 << 6; pub static trace: uint = 1 << 7; pub static coherence: uint = 1 << 8; @@ -72,7 +72,7 @@ pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] { (~"time-llvm-passes", ~"measure time of each LLVM pass", time_llvm_passes), (~"trans-stats", ~"gather trans statistics", trans_stats), - (~"no-asm-comments", ~"omit comments when using -S", no_asm_comments), + (~"asm-comments", ~"generate comments into the assembly (may change behavior)", asm_comments), (~"no-verify", ~"skip LLVM verification", no_verify), (~"trace", ~"emit trace logs", trace), (~"coherence", ~"perform coherence checking", coherence), @@ -267,7 +267,7 @@ pub impl Session_ { } fn trans_stats(@self) -> bool { self.debugging_opt(trans_stats) } fn meta_stats(@self) -> bool { self.debugging_opt(meta_stats) } - fn no_asm_comments(@self) -> bool { self.debugging_opt(no_asm_comments) } + fn asm_comments(@self) -> bool { self.debugging_opt(asm_comments) } fn no_verify(@self) -> bool { self.debugging_opt(no_verify) } fn trace(@self) -> bool { self.debugging_opt(trace) } fn coherence(@self) -> bool { self.debugging_opt(coherence) } diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index c2dc2fb22ab5b..25d57662c6e07 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -374,7 +374,7 @@ pub impl<'self> CheckLoanCtxt<'self> { } } - mc::cat_deref(base, deref_count, mc::gc_ptr(ast::m_mutbl)) => { + mc::cat_deref(_, deref_count, mc::gc_ptr(ast::m_mutbl)) => { // Dynamically check writes to `@mut` let key = root_map_key { diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index f1c337125d704..2e5e53654a455 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -378,7 +378,6 @@ pub impl mem_categorization_ctxt { debug!("cat_expr: id=%d expr=%s", expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr())); - let tcx = self.tcx; let expr_ty = self.expr_ty(expr); match expr.node { ast::expr_unary(ast::deref, e_base) => { diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 8a21d9116f5e6..47363aa9263f2 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1133,7 +1133,7 @@ pub fn trans_stmt(cx: block, s: &ast::stmt) -> block { let _icx = cx.insn_ctxt("trans_stmt"); debug!("trans_stmt(%s)", stmt_to_str(s, cx.tcx().sess.intr())); - if !cx.sess().no_asm_comments() { + if cx.sess().asm_comments() { add_span_comment(cx, s.span, stmt_to_str(s, cx.ccx().sess.intr())); } diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs index f5c496484a037..a9ed80d1eaaf7 100644 --- a/src/librustc/middle/trans/build.rs +++ b/src/librustc/middle/trans/build.rs @@ -846,7 +846,7 @@ pub fn _UndefReturn(cx: block, Fn: ValueRef) -> ValueRef { pub fn add_span_comment(bcx: block, sp: span, text: &str) { let ccx = bcx.ccx(); - if !ccx.sess.no_asm_comments() { + if ccx.sess.asm_comments() { let s = fmt!("%s (%s)", text, ccx.sess.codemap.span_to_str(sp)); debug!("%s", copy s); add_comment(bcx, s); @@ -856,7 +856,7 @@ pub fn add_span_comment(bcx: block, sp: span, text: &str) { pub fn add_comment(bcx: block, text: &str) { unsafe { let ccx = bcx.ccx(); - if !ccx.sess.no_asm_comments() { + if ccx.sess.asm_comments() { let sanitized = str::replace(text, ~"$", ~""); let comment_text = ~"# " + str::replace(sanitized, ~"\n", ~"\n\t# "); diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index e35fef6b6f66a..acd52907b9f73 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -224,7 +224,7 @@ pub fn store_environment(bcx: block, for vec::eachi(bound_values) |i, bv| { debug!("Copy %s into closure", bv.to_str(ccx)); - if !ccx.sess.no_asm_comments() { + if ccx.sess.asm_comments() { add_comment(bcx, fmt!("Copy %s into closure", bv.to_str(ccx))); } From 989d008124d62f7c1284633e6619db1a9e8b6598 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 May 2013 14:29:32 -0400 Subject: [PATCH 34/46] separate out write_guard code into its own module --- Makefile.in | 3 + src/librustc/middle/trans/_match.rs | 23 +--- src/librustc/middle/trans/common.rs | 58 +++------ src/librustc/middle/trans/controlflow.rs | 8 +- src/librustc/middle/trans/datum.rs | 152 ++--------------------- src/librustc/rustc.rc | 1 + 6 files changed, 41 insertions(+), 204 deletions(-) diff --git a/Makefile.in b/Makefile.in index dd2e6a95861bd..111ad1369deb9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -110,6 +110,9 @@ endif ifdef SAVE_TEMPS CFG_RUSTC_FLAGS += --save-temps endif +ifdef ASM_COMMENTS + CFG_RUSTC_FLAGS += -z asm-comments +endif ifdef TIME_PASSES CFG_RUSTC_FLAGS += -Z time-passes endif diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 3b1cdf0ba47f7..1a81d483dfc3c 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -969,30 +969,17 @@ pub fn pats_require_rooting(bcx: block, }) } -pub fn root_pats_as_necessary(bcx: block, +pub fn root_pats_as_necessary(mut bcx: block, m: &[@Match], col: uint, val: ValueRef) -> block { - let mut bcx = bcx; for vec::each(m) |br| { let pat_id = br.pats[col].id; - - let key = root_map_key {id: pat_id, derefs: 0u }; - match bcx.ccx().maps.root_map.find(&key) { - None => (), - Some(&root_info) => { - // Note: the scope_id will always be the id of the match. See - // the extended comment in rustc::middle::borrowck::preserve() - // for details (look for the case covering cat_discr). - - let datum = Datum {val: val, ty: node_id_type(bcx, pat_id), - mode: ByRef, source: ZeroMem}; - bcx = datum.root(bcx, br.pats[col].span, key, root_info); - // If we kept going, we'd only re-root the same value, so - // return now. - return bcx; - } + if pat_id != 0 { + let datum = Datum {val: val, ty: node_id_type(bcx, pat_id), + mode: ByRef, source: ZeroMem}; + bcx = datum.root_and_write_guard(bcx, br.pats[col].span, pat_id, 0); } } return bcx; diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 2a13cf73f8bba..f8b75838b8726 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -27,18 +27,18 @@ use middle::resolve; use middle::trans::adt; use middle::trans::base; use middle::trans::build; -use middle::trans::callee; use middle::trans::datum; use middle::trans::debuginfo; -use middle::trans::expr; use middle::trans::glue; use middle::trans::reachable; use middle::trans::shape; use middle::trans::type_of; use middle::trans::type_use; +use middle::trans::write_guard; use middle::ty::substs; use middle::ty; use middle::typeck; +use middle::borrowck::root_map_key; use util::ppaux::{Repr}; use core::cast::transmute; @@ -468,6 +468,7 @@ pub fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) { } } pub fn add_clean_return_to_mut(bcx: block, + root_key: root_map_key, frozen_val_ref: ValueRef, bits_val_ref: ValueRef, filename_val: ValueRef, @@ -488,44 +489,12 @@ pub fn add_clean_return_to_mut(bcx: block, scope_info.cleanups.push( clean_temp( frozen_val_ref, - |bcx| { - let mut bcx = bcx; - - let box_ptr = - build::Load(bcx, - build::PointerCast(bcx, - frozen_val_ref, - T_ptr(T_ptr(T_i8())))); - - let bits_val = - build::Load(bcx, - bits_val_ref); - - if bcx.tcx().sess.opts.optimize == session::No { - bcx = callee::trans_lang_call( - bcx, - bcx.tcx().lang_items.unrecord_borrow_fn(), - ~[ - box_ptr, - bits_val, - filename_val, - line_val - ], - expr::Ignore); - } - - callee::trans_lang_call( - bcx, - bcx.tcx().lang_items.return_to_mut_fn(), - ~[ - box_ptr, - bits_val, - filename_val, - line_val - ], - expr::Ignore - ) - }, + |bcx| write_guard::return_to_mut(bcx, + root_key, + frozen_val_ref, + bits_val_ref, + filename_val, + line_val), normal_exit_only)); scope_clean_changed(scope_info); } @@ -1563,6 +1532,15 @@ pub fn dummy_substs(tps: ~[ty::t]) -> ty::substs { } } +pub fn filename_and_line_num_from_span(bcx: block, + span: span) -> (ValueRef, ValueRef) { + let loc = bcx.sess().parse_sess.cm.lookup_char_pos(span.lo); + let filename_cstr = C_cstr(bcx.ccx(), @/*bad*/copy loc.file.name); + let filename = build::PointerCast(bcx, filename_cstr, T_ptr(T_i8())); + let line = C_int(bcx.ccx(), loc.line as int); + (filename, line) +} + // Casts a Rust bool value to an i1. pub fn bool_to_i1(bcx: block, llval: ValueRef) -> ValueRef { build::ICmp(bcx, lib::llvm::IntNE, llval, C_bool(false)) diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index c8699cc6371bc..e91bec5efed4a 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -385,13 +385,7 @@ fn trans_fail_value(bcx: block, pub fn trans_fail_bounds_check(bcx: block, sp: span, index: ValueRef, len: ValueRef) -> block { let _icx = bcx.insn_ctxt("trans_fail_bounds_check"); - let ccx = bcx.ccx(); - - let loc = bcx.sess().parse_sess.cm.lookup_char_pos(sp.lo); - let line = C_int(ccx, loc.line as int); - let filename_cstr = C_cstr(bcx.ccx(), @/*bad*/copy loc.file.name); - let filename = PointerCast(bcx, filename_cstr, T_ptr(T_i8())); - + let (filename, line) = filename_and_line_num_from_span(bcx, sp); let args = ~[filename, line, index, len]; let bcx = callee::trans_lang_call( bcx, bcx.tcx().lang_items.fail_bounds_check_fn(), args, expr::Ignore); diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index 6ffe504b804fb..d4ca0f3c4bed2 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -87,21 +87,19 @@ use lib; use lib::llvm::ValueRef; -use middle::borrowck::{RootInfo, root_map_key, DynaImm, DynaMut}; use middle::trans::adt; use middle::trans::base::*; use middle::trans::build::*; -use middle::trans::callee; use middle::trans::common::*; use middle::trans::common; use middle::trans::expr; use middle::trans::glue; use middle::trans::tvec; use middle::trans::type_of; +use middle::trans::write_guard; use middle::ty; use util::common::indenter; use util::ppaux::ty_to_str; -use driver::session; use core::container::Set; // XXX: this should not be necessary use core::to_bytes; @@ -518,113 +516,6 @@ pub impl Datum { } } - fn root(&self, mut bcx: block, span: span, - root_key: root_map_key, root_info: RootInfo) -> block { - /*! - * - * In some cases, borrowck will decide that an @T/@[]/@str - * value must be rooted for the program to be safe. In that - * case, we will call this function, which will stash a copy - * away until we exit the scope `scope_id`. */ - - debug!("root(root_map_key=%?, root_info=%?, self=%?)", - root_key, root_info, self.to_str(bcx.ccx())); - - if bcx.sess().trace() { - trans_trace( - bcx, None, - @fmt!("preserving until end of scope %d", - root_info.scope)); - } - - // First, root the datum. Note that we must zero this value, - // because sometimes we root on one path but not another. - // See e.g. #4904. - let scratch = scratch_datum(bcx, self.ty, true); - self.copy_to_datum(bcx, INIT, scratch); - let cleanup_bcx = find_bcx_for_scope(bcx, root_info.scope); - add_clean_temp_mem(cleanup_bcx, scratch.val, scratch.ty); - - // Now, consider also freezing it. - match root_info.freeze { - None => {} - Some(freeze_kind) => { - let loc = bcx.sess().parse_sess.cm.lookup_char_pos(span.lo); - let line = C_int(bcx.ccx(), loc.line as int); - let filename_cstr = C_cstr(bcx.ccx(), @/*bad*/copy loc.file.name); - let filename = PointerCast(bcx, filename_cstr, T_ptr(T_i8())); - - // in this case, we don't have to zero, because - // scratch.val will be NULL should the cleanup get - // called without the freezing actually occurring, and - // return_to_mut checks for this condition. - let scratch_bits = scratch_datum(bcx, ty::mk_uint(), false); - - let freeze_did = match freeze_kind { - DynaImm => bcx.tcx().lang_items.borrow_as_imm_fn(), - DynaMut => bcx.tcx().lang_items.borrow_as_mut_fn(), - }; - - let box_ptr = Load(bcx, - PointerCast(bcx, - scratch.val, - T_ptr(T_ptr(T_i8())))); - - bcx = callee::trans_lang_call( - bcx, - freeze_did, - ~[ - box_ptr, - filename, - line - ], - expr::SaveIn(scratch_bits.val)); - - if bcx.tcx().sess.opts.optimize == session::No { - bcx = callee::trans_lang_call( - bcx, - bcx.tcx().lang_items.record_borrow_fn(), - ~[ - box_ptr, - Load(bcx, scratch_bits.val), - filename, - line - ], - expr::Ignore); - } - - add_clean_return_to_mut( - cleanup_bcx, scratch.val, scratch_bits.val, - filename, line); - } - } - - bcx - } - - fn perform_write_guard(&self, bcx: block, span: span) -> block { - debug!("perform_write_guard"); - - // Create scratch space, but do not root it. - let llval = match self.mode { - ByValue => self.val, - ByRef => Load(bcx, self.val), - }; - - let loc = bcx.sess().parse_sess.cm.lookup_char_pos(span.lo); - let line = C_int(bcx.ccx(), loc.line as int); - let filename_cstr = C_cstr(bcx.ccx(), @/*bad*/copy loc.file.name); - let filename = PointerCast(bcx, filename_cstr, T_ptr(T_i8())); - - callee::trans_lang_call( - bcx, - bcx.tcx().lang_items.check_not_borrowed_fn(), - ~[PointerCast(bcx, llval, T_ptr(T_i8())), - filename, - line], - expr::Ignore) - } - fn drop_val(&self, bcx: block) -> block { if !ty::type_needs_drop(bcx.tcx(), self.ty) { return bcx; @@ -687,7 +578,9 @@ pub impl Datum { debug!("try_deref(expr_id=%?, derefs=%?, is_auto=%b, self=%?)", expr_id, derefs, is_auto, self.to_str(bcx.ccx())); - let bcx = self.root_and_write_guard(bcx, span, expr_id, derefs); + let bcx = + write_guard::root_and_write_guard( + self, bcx, span, expr_id, derefs); match ty::get(self.ty).sty { ty::ty_box(_) | ty::ty_uniq(_) => { @@ -841,33 +734,6 @@ pub impl Datum { DatumBlock { bcx: bcx, datum: datum } } - fn root_and_write_guard(&self, - mut bcx: block, - span: span, - expr_id: ast::node_id, - derefs: uint) -> block { - let key = root_map_key { id: expr_id, derefs: derefs }; - debug!("root_and_write_guard(key=%?)", key); - - // root the autoderef'd value, if necessary: - // - // (Note: root'd values are always boxes) - let ccx = bcx.ccx(); - bcx = match ccx.maps.root_map.find(&key) { - None => bcx, - Some(&root_info) => self.root(bcx, span, key, root_info) - }; - - // Perform the write guard, if necessary. - // - // (Note: write-guarded values are always boxes) - if ccx.maps.write_guard_map.contains(&key) { - self.perform_write_guard(bcx, span) - } else { - bcx - } - } - fn get_vec_base_and_len(&self, mut bcx: block, span: span, @@ -877,7 +743,7 @@ pub impl Datum { //! and write guards checks. // only imp't for @[] and @str, but harmless - bcx = self.root_and_write_guard(bcx, span, expr_id, 0); + bcx = write_guard::root_and_write_guard(self, bcx, span, expr_id, 0); let (base, len) = self.get_vec_base_and_len_no_root(bcx); (bcx, base, len) } @@ -890,6 +756,14 @@ pub impl Datum { tvec::get_base_and_len(bcx, llval, self.ty) } + fn root_and_write_guard(&self, + bcx: block, + span: span, + expr_id: ast::node_id, + derefs: uint) -> block { + write_guard::root_and_write_guard(self, bcx, span, expr_id, derefs) + } + fn to_result(&self, bcx: block) -> common::Result { rslt(bcx, self.to_appropriate_llval(bcx)) } diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc index 1ecb38854c815..7191a98e5dbc6 100644 --- a/src/librustc/rustc.rc +++ b/src/librustc/rustc.rc @@ -47,6 +47,7 @@ pub mod middle { pub mod controlflow; pub mod glue; pub mod datum; + pub mod write_guard; pub mod callee; pub mod expr; pub mod common; From 6806900a7c63950feb2540347fc3f94d83074bbd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 May 2013 07:43:43 -0400 Subject: [PATCH 35/46] disable lang debug for perf --- src/libcore/unstable/lang.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index 5a65a5c24bb57..6b61df31fdc87 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -152,7 +152,7 @@ pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char { /// Because this code is so perf. sensitive, use a static constant so that /// debug printouts are compiled out most of the time. -static ENABLE_DEBUG: bool = true; +static ENABLE_DEBUG: bool = false; #[inline] pub fn debug_mem(tag: &'static str, p: *const T) { From 0b0b8018a6a1271e6c8e82230e2e8496eebbba3f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 May 2013 12:17:59 -0400 Subject: [PATCH 36/46] add warning for #6248 and remove instances of it --- src/libcore/hashmap.rs | 13 +++++++ src/librustc/metadata/encoder.rs | 8 ++--- .../middle/borrowck/gather_loans/lifetime.rs | 36 +++++++++++++++---- src/librustc/middle/check_const.rs | 2 +- src/librustc/middle/check_match.rs | 4 +-- src/librustc/middle/kind.rs | 8 ++--- src/librustc/middle/lint.rs | 2 +- src/librustc/middle/liveness.rs | 8 ++--- src/librustc/middle/mem_categorization.rs | 4 +-- src/librustc/middle/moves.rs | 2 +- src/librustc/middle/privacy.rs | 4 +-- src/librustc/middle/region.rs | 2 +- src/librustc/middle/trans/_match.rs | 6 ++-- src/librustc/middle/trans/base.rs | 6 ++-- src/librustc/middle/trans/closure.rs | 2 +- src/librustc/middle/trans/consts.rs | 6 ++-- src/librustc/middle/trans/controlflow.rs | 2 +- src/librustc/middle/trans/debuginfo.rs | 6 ++-- src/librustc/middle/trans/expr.rs | 4 +-- src/librustc/middle/trans/foreign.rs | 2 +- src/librustc/middle/trans/machine.rs | 2 +- src/librustc/middle/trans/meth.rs | 8 ++--- src/librustc/middle/trans/monomorphize.rs | 14 ++++---- src/librustc/middle/trans/reachable.rs | 19 ++++++---- src/librustc/middle/trans/reflect.rs | 7 ++-- src/librustc/middle/trans/type_use.rs | 33 +++++++++-------- src/librustc/middle/ty.rs | 4 +-- src/librustc/middle/typeck/check/_match.rs | 2 +- src/librustc/middle/typeck/check/mod.rs | 8 ++--- src/librustc/middle/typeck/check/vtable.rs | 2 +- src/librustc/middle/typeck/coherence.rs | 6 ++-- src/librustc/middle/typeck/collect.rs | 2 +- 32 files changed, 142 insertions(+), 92 deletions(-) diff --git a/src/libcore/hashmap.rs b/src/libcore/hashmap.rs index ad1994a92d2bb..8ed54741f1276 100644 --- a/src/libcore/hashmap.rs +++ b/src/libcore/hashmap.rs @@ -25,6 +25,7 @@ use rand; use uint; use vec; use util::unreachable; +use kinds::Copy; static INITIAL_CAPACITY: uint = 32u; // 2^5 @@ -529,6 +530,18 @@ pub impl HashMap { } } +pub impl HashMap { + /// Like `find`, but returns a copy of the value. + fn find_copy(&self, k: &K) -> Option { + self.find(k).map_consume(|v| copy *v) + } + + /// Like `get`, but returns a copy of the value. + fn get_copy(&self, k: &K) -> V { + copy *self.get(k) + } +} + impl Eq for HashMap { fn eq(&self, other: &HashMap) -> bool { if self.len() != other.len() { return false; } diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index dd4ef0d2e688f..6d7442b1ed5ee 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -190,7 +190,7 @@ fn encode_type_param_bounds(ebml_w: &writer::Encoder, ecx: @EncodeContext, params: &OptVec) { let ty_param_defs = - @params.map_to_vec(|param| *ecx.tcx.ty_param_defs.get(¶m.id)); + @params.map_to_vec(|param| ecx.tcx.ty_param_defs.get_copy(¶m.id)); encode_ty_type_param_defs(ebml_w, ecx, ty_param_defs, tag_items_data_item_ty_param_bounds); } @@ -275,7 +275,7 @@ fn encode_symbol(ecx: @EncodeContext, ebml_w: &writer::Encoder, id: node_id) { fn encode_discriminant(ecx: @EncodeContext, ebml_w: &writer::Encoder, id: node_id) { ebml_w.start_tag(tag_items_data_item_symbol); - ebml_w.writer.write(str::to_bytes(**ecx.discrim_symbols.get(&id))); + ebml_w.writer.write(str::to_bytes(*ecx.discrim_symbols.get_copy(&id))); ebml_w.end_tag(); } @@ -1035,7 +1035,7 @@ fn encode_info_for_items(ecx: @EncodeContext, ebml_w: &writer::Encoder, let ebml_w = copy *ebml_w; |i, cx, v| { visit::visit_item(i, cx, v); - match *ecx.tcx.items.get(&i.id) { + match ecx.tcx.items.get_copy(&i.id) { ast_map::node_item(_, pt) => { encode_info_for_item(ecx, &ebml_w, i, index, *pt); @@ -1048,7 +1048,7 @@ fn encode_info_for_items(ecx: @EncodeContext, ebml_w: &writer::Encoder, let ebml_w = copy *ebml_w; |ni, cx, v| { visit::visit_foreign_item(ni, cx, v); - match *ecx.tcx.items.get(&ni.id) { + match ecx.tcx.items.get_copy(&ni.id) { ast_map::node_foreign_item(_, abi, _, pt) => { encode_info_for_foreign_item(ecx, &ebml_w, ni, index, /*bad*/copy *pt, diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs index 43fff110a7a7e..330d60a59d3ae 100644 --- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs +++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs @@ -18,6 +18,7 @@ use middle::ty; use syntax::ast::{m_const, m_imm, m_mutbl}; use syntax::ast; use syntax::codemap::span; +use util::ppaux::{note_and_explain_region}; pub fn guarantee_lifetime(bccx: @BorrowckCtxt, item_scope_id: ast::node_id, @@ -215,13 +216,6 @@ impl GuaranteeLifetimeContext { } }; - // FIXME(#3511) grow to the nearest cleanup scope---this can - // cause observable errors if freezing! - if !self.bccx.tcx.region_maps.is_cleanup_scope(root_scope) { - debug!("%? is not a cleanup scope, adjusting", root_scope); - root_scope = self.bccx.tcx.region_maps.cleanup_scope(root_scope); - } - // If we are borrowing the inside of an `@mut` box, // we need to dynamically mark it to prevent incompatible // borrows from happening later. @@ -235,6 +229,34 @@ impl GuaranteeLifetimeContext { } }; + // FIXME(#3511) grow to the nearest cleanup scope---this can + // cause observable errors if freezing! + if !self.bccx.tcx.region_maps.is_cleanup_scope(root_scope) { + debug!("%? is not a cleanup scope, adjusting", root_scope); + + let cleanup_scope = + self.bccx.tcx.region_maps.cleanup_scope(root_scope); + + if opt_dyna.is_some() { + self.tcx().sess.span_warn( + self.span, + fmt!("Dynamic freeze scope artifically extended \ + (see Issue #6248)")); + note_and_explain_region( + self.bccx.tcx, + "managed value only needs to be frozen for ", + ty::re_scope(root_scope), + "..."); + note_and_explain_region( + self.bccx.tcx, + "...but due to Issue #6248, it will be frozen for ", + ty::re_scope(cleanup_scope), + ""); + } + + root_scope = cleanup_scope; + } + // Add a record of what is required let rm_key = root_map_key {id: cmt_deref.id, derefs: derefs}; let root_info = RootInfo {scope: root_scope, freeze: opt_dyna}; diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 6a47eedcea8c3..0bb156dd5f642 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -237,7 +237,7 @@ pub fn check_item_recursion(sess: Session, match env.def_map.find(&e.id) { Some(&def_const(def_id)) => { if ast_util::is_local(def_id) { - match *env.ast_map.get(&def_id.node) { + match env.ast_map.get_copy(&def_id.node) { ast_map::node_item(it, _) => { (v.visit_item)(it, env, v); } diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 852eb1b50a499..5be0c09f4ae4a 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -523,7 +523,7 @@ pub fn specialize(cx: @MatchCheckCtxt, } } pat_enum(_, args) => { - match *cx.tcx.def_map.get(&pat_id) { + match cx.tcx.def_map.get_copy(&pat_id) { def_const(did) => { let const_expr = lookup_const_by_id(cx.tcx, did).get(); @@ -567,7 +567,7 @@ pub fn specialize(cx: @MatchCheckCtxt, } pat_struct(_, ref flds, _) => { // Is this a struct or an enum variant? - match *cx.tcx.def_map.get(&pat_id) { + match cx.tcx.def_map.get_copy(&pat_id) { def_variant(_, variant_id) => { if variant(variant_id) == *ctor_id { // FIXME #4731: Is this right? --pcw diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index 199eb274ab9e1..3afe8c3b9d68f 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -128,7 +128,7 @@ fn check_item(item: @item, cx: Context, visitor: visit::vt) { // Yes, it's a destructor. match self_type.node { ty_path(_, path_node_id) => { - let struct_def = *cx.tcx.def_map.get( + let struct_def = cx.tcx.def_map.get_copy( &path_node_id); let struct_did = ast_util::def_id_of_def(struct_def); @@ -272,7 +272,7 @@ pub fn check_expr(e: @expr, cx: Context, v: visit::vt) { let ts = /*bad*/ copy **ts; let type_param_defs = match e.node { expr_path(_) => { - let did = ast_util::def_id_of_def(*cx.tcx.def_map.get(&e.id)); + let did = ast_util::def_id_of_def(cx.tcx.def_map.get_copy(&e.id)); ty::lookup_item_type(cx.tcx, did).generics.type_param_defs } _ => { @@ -333,7 +333,7 @@ fn check_ty(aty: @Ty, cx: Context, v: visit::vt) { for cx.tcx.node_type_substs.find(&id).each |ts| { // FIXME(#5562): removing this copy causes a segfault before stage2 let ts = /*bad*/ copy **ts; - let did = ast_util::def_id_of_def(*cx.tcx.def_map.get(&id)); + let did = ast_util::def_id_of_def(cx.tcx.def_map.get_copy(&id)); let type_param_defs = ty::lookup_item_type(cx.tcx, did).generics.type_param_defs; for vec::each2(ts, *type_param_defs) |&ty, type_param_def| { @@ -399,7 +399,7 @@ pub fn check_bounds(cx: Context, fn is_nullary_variant(cx: Context, ex: @expr) -> bool { match ex.node { expr_path(_) => { - match *cx.tcx.def_map.get(&ex.id) { + match cx.tcx.def_map.get_copy(&ex.id) { def_variant(edid, vdid) => { vec::len(ty::enum_variant_with_id(cx.tcx, edid, vdid).args) == 0u } diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index faf4b1c31061b..c54ff6075faa7 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -696,7 +696,7 @@ fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) { for vec::each(vec::append_one(tys, decl.output)) |ty| { match ty.node { ast::ty_path(_, id) => { - match *cx.def_map.get(&id) { + match cx.def_map.get_copy(&id) { ast::def_prim_ty(ast::ty_int(ast::ty_i)) => { cx.sess.span_lint( ctypes, id, fn_id, diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 59a6e6469e2bb..60ce34c3af271 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -469,7 +469,7 @@ fn visit_expr(expr: @expr, self: @mut IrMaps, vt: vt<@mut IrMaps>) { match expr.node { // live nodes required for uses or definitions of variables: expr_path(_) => { - let def = *self.tcx.def_map.get(&expr.id); + let def = self.tcx.def_map.get_copy(&expr.id); debug!("expr %d: path that leads to %?", expr.id, def); if moves::moved_variable_node_id_from_def(def).is_some() { self.add_live_node_for_node(expr.id, ExprNode(expr.span)); @@ -616,7 +616,7 @@ pub impl Liveness { fn variable_from_path(&self, expr: @expr) -> Option { match expr.node { expr_path(_) => { - let def = *self.tcx.def_map.get(&expr.id); + let def = self.tcx.def_map.get_copy(&expr.id); moves::moved_variable_node_id_from_def(def).map( |rdef| self.variable(*rdef, expr.span) ) @@ -1338,7 +1338,7 @@ pub impl Liveness { fn access_path(&self, expr: @expr, succ: LiveNode, acc: uint) -> LiveNode { - let def = *self.tcx.def_map.get(&expr.id); + let def = self.tcx.def_map.get_copy(&expr.id); match moves::moved_variable_node_id_from_def(def) { Some(nid) => { let ln = self.live_node(expr.id, expr.span); @@ -1605,7 +1605,7 @@ pub impl Liveness { fn check_lvalue(@self, expr: @expr, vt: vt<@Liveness>) { match expr.node { expr_path(_) => { - match *self.tcx.def_map.get(&expr.id) { + match self.tcx.def_map.get_copy(&expr.id) { def_local(nid, mutbl) => { // Assignment to an immutable variable or argument: only legal // if there is no later assignment. If this local is actually diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 2e5e53654a455..da0a3ba25d071 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -409,7 +409,7 @@ pub impl mem_categorization_ctxt { } ast::expr_path(_) => { - let def = *self.tcx.def_map.get(&expr.id); + let def = self.tcx.def_map.get_copy(&expr.id); self.cat_def(expr.id, expr.span, expr_ty, def) } @@ -977,7 +977,7 @@ pub fn field_mutbl(tcx: ty::ctxt, } } ty::ty_enum(*) => { - match *tcx.def_map.get(&node_id) { + match tcx.def_map.get_copy(&node_id) { ast::def_variant(_, variant_id) => { for ty::lookup_struct_fields(tcx, variant_id).each |fld| { if fld.ident == f_name { diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs index 0daad80af5db7..58345302d16fb 100644 --- a/src/librustc/middle/moves.rs +++ b/src/librustc/middle/moves.rs @@ -441,7 +441,7 @@ pub impl VisitContext { self.move_maps.variable_moves_map.insert( expr.id, entire_expr); - let def = *self.tcx.def_map.get(&expr.id); + let def = self.tcx.def_map.get_copy(&expr.id); for moved_variable_node_id_from_def(def).each |&id| { self.move_maps.moved_variables_set.insert(id); } diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index a37ebdcfaa263..0df09553ad014 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -481,7 +481,7 @@ pub fn check_crate(tcx: ty::ctxt, } } expr_path(path) => { - check_path(expr.span, *tcx.def_map.get(&expr.id), path); + check_path(expr.span, tcx.def_map.get_copy(&expr.id), path); } expr_struct(_, ref fields, _) => { match ty::get(ty::expr_ty(tcx, expr)).sty { @@ -499,7 +499,7 @@ pub fn check_crate(tcx: ty::ctxt, ty_enum(id, _) => { if id.crate != local_crate || !privileged_items.contains(&(id.node)) { - match *tcx.def_map.get(&expr.id) { + match tcx.def_map.get_copy(&expr.id) { def_variant(_, variant_id) => { for (*fields).each |field| { debug!("(privacy checking) \ diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 06eb2542235e4..d23a798b6239e 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -949,7 +949,7 @@ pub fn determine_rp_in_crate(sess: Session, let cx = &mut *cx; while cx.worklist.len() != 0 { let c_id = cx.worklist.pop(); - let c_variance = { *cx.region_paramd_items.get(&c_id) }; + let c_variance = cx.region_paramd_items.get_copy(&c_id); // NOTE cleanup scopes cause an exaggerated lock here debug!("popped %d from worklist", c_id); match cx.dep_map.find(&c_id) { diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 1a81d483dfc3c..61a8b367d6cf6 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -280,7 +280,7 @@ pub fn trans_opt(bcx: block, o: &Opt) -> opt_result { pub fn variant_opt(bcx: block, pat_id: ast::node_id) -> Opt { let ccx = bcx.ccx(); - match *ccx.tcx.def_map.get(&pat_id) { + match ccx.tcx.def_map.get_copy(&pat_id) { ast::def_variant(enum_id, var_id) => { let variants = ty::enum_variants(ccx.tcx, enum_id); for vec::each(*variants) |v| { @@ -516,7 +516,7 @@ pub fn enter_opt<'r>(bcx: block, match p.node { ast::pat_enum(*) | ast::pat_ident(_, _, None) if pat_is_const(tcx.def_map, p) => { - let const_def = *tcx.def_map.get(&p.id); + let const_def = tcx.def_map.get_copy(&p.id); let const_def_id = ast_util::def_id_of_def(const_def); if opt_eq(tcx, &lit(ConstLit(const_def_id)), opt) { Some(~[]) @@ -552,7 +552,7 @@ pub fn enter_opt<'r>(bcx: block, if opt_eq(tcx, &variant_opt(bcx, p.id), opt) { // Look up the struct variant ID. let struct_id; - match *tcx.def_map.get(&p.id) { + match tcx.def_map.get_copy(&p.id) { ast::def_variant(_, found_struct_id) => { struct_id = found_struct_id; } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 47363aa9263f2..5419628cd958a 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2052,7 +2052,7 @@ pub fn trans_tuple_struct(ccx: @CrateContext, fcx.llretptr.get(), 0, i); - let llarg = match *fcx.llargs.get(&field.node.id) { + let llarg = match fcx.llargs.get_copy(&field.node.id) { local_mem(x) => x, _ => { ccx.tcx.sess.bug(~"trans_tuple_struct: llarg wasn't \ @@ -2141,7 +2141,7 @@ pub fn trans_enum_def(ccx: @CrateContext, enum_definition: &ast::enum_def, pub fn trans_item(ccx: @CrateContext, item: &ast::item) { let _icx = ccx.insn_ctxt("trans_item"); - let path = match *ccx.tcx.items.get(&item.id) { + let path = match ccx.tcx.items.get_copy(&item.id) { ast_map::node_item(_, p) => p, // tjc: ? _ => fail!(~"trans_item"), @@ -2443,7 +2443,7 @@ pub fn fill_fn_pair(bcx: block, pair: ValueRef, llfn: ValueRef, } pub fn item_path(ccx: @CrateContext, i: @ast::item) -> path { - let base = match *ccx.tcx.items.get(&i.id) { + let base = match ccx.tcx.items.get_copy(&i.id) { ast_map::node_item(_, p) => p, // separate map for paths? _ => fail!(~"item_path") diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index acd52907b9f73..a2a1f3d8b72fb 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -424,7 +424,7 @@ pub fn trans_expr_fn(bcx: block, let Result {bcx: bcx, val: closure} = match sigil { ast::BorrowedSigil | ast::ManagedSigil | ast::OwnedSigil => { - let cap_vars = *ccx.maps.capture_map.get(&user_id); + let cap_vars = ccx.maps.capture_map.get_copy(&user_id); let ret_handle = match is_loop_body {Some(x) => x, None => None}; let ClosureResult {llbox, cdata_ty, bcx} diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 3a331e8791ba2..dd68670287b7b 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -158,7 +158,7 @@ pub fn get_const_val(cx: @CrateContext, def_id: ast::def_id) -> ValueRef { if !ast_util::is_local(def_id) { def_id = inline::maybe_instantiate_inline(cx, def_id, true); } - match *cx.tcx.items.get(&def_id.node) { + match cx.tcx.items.get_copy(&def_id.node) { ast_map::node_item(@ast::item { node: ast::item_const(_, subexpr), _ }, _) => { @@ -167,7 +167,7 @@ pub fn get_const_val(cx: @CrateContext, def_id: ast::def_id) -> ValueRef { _ => cx.tcx.sess.bug(~"expected a const to be an item") } } - *cx.const_values.get(&def_id.node) + cx.const_values.get_copy(&def_id.node) } pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef { @@ -560,7 +560,7 @@ pub fn trans_const(ccx: @CrateContext, _e: @ast::expr, id: ast::node_id) { let g = base::get_item_val(ccx, id); // At this point, get_item_val has already translated the // constant's initializer to determine its LLVM type. - let v = *ccx.const_values.get(&id); + let v = ccx.const_values.get_copy(&id); llvm::LLVMSetInitializer(g, v); llvm::LLVMSetGlobalConstant(g, True); } diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index e91bec5efed4a..b16b77320860c 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -193,7 +193,7 @@ pub fn trans_log(log_ex: @ast::expr, }; let global = if ccx.module_data.contains_key(&modname) { - *ccx.module_data.get(&modname) + ccx.module_data.get_copy(&modname) } else { let s = link::mangle_internal_name_by_path_and_seq( ccx, modpath, ~"loglevel"); diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 2a2bf7ba4ad68..1571fd71152cc 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -864,7 +864,7 @@ pub fn create_local_var(bcx: block, local: @ast::local) something weird"); } option::None => { - match *bcx.fcx.lllocals.get(&local.node.pat.id) { + match bcx.fcx.lllocals.get_copy(&local.node.pat.id) { local_imm(v) => v, _ => bcx.tcx().sess.span_bug(local.span, ~"local is bound to \ something weird") @@ -917,7 +917,7 @@ pub fn create_arg(bcx: block, arg: ast::arg, sp: span) }; update_cache(cache, tg, argument_metadata(mdval)); - let llptr = match *fcx.llargs.get(&arg.id) { + let llptr = match fcx.llargs.get_copy(&arg.id) { local_mem(v) | local_imm(v) => v, }; let declargs = ~[llmdnode(~[llptr]), mdnode]; @@ -960,7 +960,7 @@ pub fn create_function(fcx: fn_ctxt) -> @Metadata { let sp = fcx.span.get(); debug!("%s", cx.sess.codemap.span_to_str(sp)); - let (ident, ret_ty, id) = match *cx.tcx.items.get(&fcx.id) { + let (ident, ret_ty, id) = match cx.tcx.items.get_copy(&fcx.id) { ast_map::node_item(item, _) => { match item.node { ast::item_fn(ref decl, _, _, _, _) => { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index b8cdfeb796db0..d961c0705e44c 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -1117,7 +1117,7 @@ pub fn with_field_tys(tcx: ty::ctxt, ty.repr(tcx))); } Some(node_id) => { - match *tcx.def_map.get(&node_id) { + match tcx.def_map.get_copy(&node_id) { ast::def_variant(enum_id, variant_id) => { let variant_info = ty::enum_variant_with_id( tcx, enum_id, variant_id); @@ -1536,7 +1536,7 @@ fn trans_overloaded_op(bcx: block, ret_ty: ty::t, dest: Dest) -> block { - let origin = *bcx.ccx().maps.method_map.get(&expr.id); + let origin = bcx.ccx().maps.method_map.get_copy(&expr.id); let fty = node_id_type(bcx, expr.callee_id); callee::trans_call_inner(bcx, expr.info(), diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index c45ba64c58470..f49a7fb0de41f 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -724,7 +724,7 @@ pub fn trans_intrinsic(ccx: @CrateContext, let in_type_size = machine::llbitsize_of_real(ccx, llintype); let out_type_size = machine::llbitsize_of_real(ccx, llouttype); if in_type_size != out_type_size { - let sp = match *ccx.tcx.items.get(&ref_id.get()) { + let sp = match ccx.tcx.items.get_copy(&ref_id.get()) { ast_map::node_expr(e) => e.span, _ => fail!(~"transmute has non-expr arg"), }; diff --git a/src/librustc/middle/trans/machine.rs b/src/librustc/middle/trans/machine.rs index 3ae2421a55589..73b79fa37e2ee 100644 --- a/src/librustc/middle/trans/machine.rs +++ b/src/librustc/middle/trans/machine.rs @@ -118,7 +118,7 @@ pub fn llalign_of(cx: @CrateContext, t: TypeRef) -> ValueRef { // Computes the size of the data part of an enum. pub fn static_size_of_enum(cx: @CrateContext, t: ty::t) -> uint { if cx.enum_sizes.contains_key(&t) { - return *cx.enum_sizes.get(&t); + return cx.enum_sizes.get_copy(&t); } debug!("static_size_of_enum %s", ty_to_str(cx.tcx, t)); diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 86b087b937f25..693947d7e99bd 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -312,7 +312,7 @@ pub fn trans_static_method_callee(bcx: block, }; let mname = if method_id.crate == ast::local_crate { - match *bcx.tcx().items.get(&method_id.node) { + match bcx.tcx().items.get_copy(&method_id.node) { ast_map::node_trait_method(trait_method, _, _) => { ast_util::trait_method_to_ty_method(trait_method).ident } @@ -329,7 +329,7 @@ pub fn trans_static_method_callee(bcx: block, name=%s", method_id, callee_id, *ccx.sess.str_of(mname)); let vtbls = resolve_vtables_in_fn_ctxt( - bcx.fcx, *ccx.maps.vtable_map.get(&callee_id)); + bcx.fcx, ccx.maps.vtable_map.get_copy(&callee_id)); match vtbls[bound_index] { typeck::vtable_static(impl_did, ref rcvr_substs, rcvr_origins) => { @@ -367,7 +367,7 @@ pub fn method_from_methods(ms: &[@ast::method], name: ast::ident) pub fn method_with_name(ccx: @CrateContext, impl_id: ast::def_id, name: ast::ident) -> ast::def_id { if impl_id.crate == ast::local_crate { - match *ccx.tcx.items.get(&impl_id.node) { + match ccx.tcx.items.get_copy(&impl_id.node) { ast_map::node_item(@ast::item { node: ast::item_impl(_, _, _, ref ms), _ @@ -385,7 +385,7 @@ pub fn method_with_name_or_default(ccx: @CrateContext, impl_id: ast::def_id, name: ast::ident) -> ast::def_id { if impl_id.crate == ast::local_crate { - match *ccx.tcx.items.get(&impl_id.node) { + match ccx.tcx.items.get_copy(&impl_id.node) { ast_map::node_item(@ast::item { node: ast::item_impl(_, _, _, ref ms), _ }, _) => { diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index 98db829370c0c..e1b81933e6881 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -101,12 +101,14 @@ pub fn monomorphic_fn(ccx: @CrateContext, let tpt = ty::lookup_item_type(ccx.tcx, fn_id); let llitem_ty = tpt.ty; - let map_node = session::expect(ccx.sess, ccx.tcx.items.find(&fn_id.node), - || fmt!("While monomorphizing %?, couldn't find it in the item map \ - (may have attempted to monomorphize an item defined in a different \ - crate?)", fn_id)); + let map_node = session::expect( + ccx.sess, + ccx.tcx.items.find_copy(&fn_id.node), + || fmt!("While monomorphizing %?, couldn't find it in the item map \ + (may have attempted to monomorphize an item \ + defined in a different crate?)", fn_id)); // Get the path so that we can create a symbol - let (pt, name, span) = match *map_node { + let (pt, name, span) = match map_node { ast_map::node_item(i, pt) => (pt, i.ident, i.span), ast_map::node_variant(ref v, enm, pt) => (pt, (*v).node.name, enm.span), ast_map::node_method(m, _, pt) => (pt, m.ident, m.span), @@ -188,7 +190,7 @@ pub fn monomorphic_fn(ccx: @CrateContext, self_ty: impl_ty_opt }); - let lldecl = match *map_node { + let lldecl = match map_node { ast_map::node_item(i@@ast::item { node: ast::item_fn(ref decl, _, _, _, ref body), _ diff --git a/src/librustc/middle/trans/reachable.rs b/src/librustc/middle/trans/reachable.rs index a446408d00a10..058ce638030b9 100644 --- a/src/librustc/middle/trans/reachable.rs +++ b/src/librustc/middle/trans/reachable.rs @@ -109,7 +109,8 @@ fn traverse_public_item(cx: @mut ctx, item: @item) { item_foreign_mod(ref nm) => { if !traverse_exports(cx, item.id) { for nm.items.each |item| { - (&mut *cx).rmap.insert(item.id); // NOTE reborrow @mut + let cx = &mut *cx; // NOTE reborrow @mut + cx.rmap.insert(item.id); } } } @@ -125,17 +126,24 @@ fn traverse_public_item(cx: @mut ctx, item: @item) { m.generics.ty_params.len() > 0u || attr::find_inline_attr(m.attrs) != attr::ia_none { - (&mut *cx).rmap.insert(m.id); // NOTE reborrow @mut + { + let cx = &mut *cx; // NOTE reborrow @mut + cx.rmap.insert(m.id); + } traverse_inline_body(cx, &m.body); } } } item_struct(ref struct_def, ref generics) => { for struct_def.ctor_id.each |&ctor_id| { - (&mut *cx).rmap.insert(ctor_id); // NOTE reborrow @mut + let cx = &mut *cx; // NOTE reborrow @mut + cx.rmap.insert(ctor_id); } for struct_def.dtor.each |dtor| { - (&mut *cx).rmap.insert(dtor.node.id); + { + let cx = &mut *cx; // NOTE reborrow @mut + cx.rmap.insert(dtor.node.id); + } if generics.ty_params.len() > 0u || attr::find_inline_attr(dtor.node.attrs) != attr::ia_none { @@ -156,8 +164,7 @@ fn traverse_public_item(cx: @mut ctx, item: @item) { fn traverse_ty<'a>(ty: @Ty, cx: @mut ctx<'a>, v: visit::vt<@mut ctx<'a>>) { { - // FIXME #6021: naming rmap shouldn't be necessary - let cx = &mut *cx; + let cx = &mut *cx; // NOTE reborrow @mut if cx.rmap.contains(&ty.id) { return; } cx.rmap.insert(ty.id); } diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index 7e59f580a2c3c..5f77173f56321 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -274,8 +274,9 @@ pub impl Reflector { let repr = adt::represent_type(bcx.ccx(), t); let variants = ty::substd_enum_variants(ccx.tcx, did, substs); let llptrty = T_ptr(type_of(ccx, t)); - let (_, opaquety) = *(ccx.tcx.intrinsic_defs.find(&ccx.sess.ident_of(~"Opaque")) - .expect("Failed to resolve intrinsic::Opaque")); + let (_, opaquety) = + ccx.tcx.intrinsic_defs.find_copy(&ccx.sess.ident_of(~"Opaque")) + .expect("Failed to resolve intrinsic::Opaque"); let opaqueptrty = ty::mk_ptr(ccx.tcx, ty::mt { ty: opaquety, mutbl: ast::m_imm }); let make_get_disr = || { @@ -374,7 +375,7 @@ pub fn emit_calls_to_trait_visit_ty(bcx: block, use syntax::parse::token::special_idents::tydesc; let final = sub_block(bcx, ~"final"); assert!(bcx.ccx().tcx.intrinsic_defs.contains_key(&tydesc)); - let (_, tydesc_ty) = *bcx.ccx().tcx.intrinsic_defs.get(&tydesc); + let (_, tydesc_ty) = bcx.ccx().tcx.intrinsic_defs.get_copy(&tydesc); let tydesc_ty = type_of(bcx.ccx(), tydesc_ty); let mut r = Reflector { visitor_val: visitor_val, diff --git a/src/librustc/middle/trans/type_use.rs b/src/librustc/middle/trans/type_use.rs index 33145dd4334a5..fb2358a57e2a7 100644 --- a/src/librustc/middle/trans/type_use.rs +++ b/src/librustc/middle/trans/type_use.rs @@ -239,18 +239,11 @@ pub fn node_type_needs(cx: Context, use_: uint, id: node_id) { } pub fn mark_for_method_call(cx: Context, e_id: node_id, callee_id: node_id) { + let mut opt_static_did = None; for cx.ccx.maps.method_map.find(&e_id).each |mth| { match mth.origin { typeck::method_static(did) => { - for cx.ccx.tcx.node_type_substs.find(&callee_id).each |ts| { - // FIXME(#5562): removing this copy causes a segfault - // before stage2 - let ts = /*bad*/ copy **ts; - let type_uses = type_uses_for(cx.ccx, did, ts.len()); - for vec::each2(*type_uses, ts) |uses, subst| { - type_needs(cx, *uses, *subst) - } - } + opt_static_did = Some(did); } typeck::method_param(typeck::method_param { param_num: param, @@ -262,6 +255,19 @@ pub fn mark_for_method_call(cx: Context, e_id: node_id, callee_id: node_id) { | typeck::method_super(*) => (), } } + + // Note: we do not execute this code from within the each() call + // above because the recursive call to `type_needs` can trigger + // inlining and hence can cause `method_map` and + // `node_type_substs` to be modified. + for opt_static_did.each |did| { + for cx.ccx.tcx.node_type_substs.find_copy(&callee_id).each |ts| { + let type_uses = type_uses_for(cx.ccx, did, ts.len()); + for vec::each2(*type_uses, ts) |uses, subst| { + type_needs(cx, *uses, *subst) + } + } + } } pub fn mark_for_expr(cx: Context, e: @expr) { @@ -291,12 +297,11 @@ pub fn mark_for_expr(cx: Context, e: @expr) { } } expr_path(_) => { - for cx.ccx.tcx.node_type_substs.find(&e.id).each |ts| { - // FIXME(#5562): removing this copy causes a segfault before stage2 - let ts = copy **ts; - let id = ast_util::def_id_of_def(*cx.ccx.tcx.def_map.get(&e.id)); + let opt_ts = cx.ccx.tcx.node_type_substs.find_copy(&e.id); + for opt_ts.each |ts| { + let id = ast_util::def_id_of_def(cx.ccx.tcx.def_map.get_copy(&e.id)); let uses_for_ts = type_uses_for(cx.ccx, id, ts.len()); - for vec::each2(*uses_for_ts, ts) |uses, subst| { + for vec::each2(*uses_for_ts, *ts) |uses, subst| { type_needs(cx, *uses, *subst) } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index b17dac82048bf..eac73ce1a2e0a 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3893,7 +3893,7 @@ pub fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[VariantInfo] { call eval_const_expr, it should never get called twice for the same expr, since check_enum_variants also updates the enum_var_cache */ - match *cx.items.get(&id.node) { + match cx.items.get_copy(&id.node) { ast_map::node_item(@ast::item { node: ast::item_enum(ref enum_definition, _), _ @@ -4424,7 +4424,7 @@ pub fn get_impl_id(tcx: ctxt, trait_id: def_id, self_ty: t) -> def_id { pub fn visitor_object_ty(tcx: ctxt) -> (@TraitRef, t) { let ty_visitor_name = special_idents::ty_visitor; assert!(tcx.intrinsic_traits.contains_key(&ty_visitor_name)); - let trait_ref = *tcx.intrinsic_traits.get(&ty_visitor_name); + let trait_ref = tcx.intrinsic_traits.get_copy(&ty_visitor_name); (trait_ref, mk_trait(tcx, trait_ref.def_id, copy trait_ref.substs, BoxTraitStore, ast::m_imm)) } diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index 0c9b61164d231..a3bf1a5ef52d4 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -404,7 +404,7 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) { } ast::pat_enum(*) | ast::pat_ident(*) if pat_is_const(tcx.def_map, pat) => { - let const_did = ast_util::def_id_of_def(*tcx.def_map.get(&pat.id)); + let const_did = ast_util::def_id_of_def(tcx.def_map.get_copy(&pat.id)); let const_tpt = ty::lookup_item_type(tcx, const_did); demand::suptype(fcx, pat.span, expected, const_tpt.ty); fcx.write_ty(pat.id, const_tpt.ty); diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 70282fdc57cfe..09022b5829a14 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -433,7 +433,7 @@ pub fn check_fn(ccx: @mut CrateCtxt, assign(self_info.self_id, Some(self_info.self_ty)); debug!("self is assigned to %s", fcx.infcx().ty_to_str( - *fcx.inh.locals.get(&self_info.self_id))); + fcx.inh.locals.get_copy(&self_info.self_id))); } // Add formal parameters. @@ -466,7 +466,7 @@ pub fn check_fn(ccx: @mut CrateCtxt, debug!("Local variable %s is assigned type %s", fcx.pat_to_str(local.node.pat), fcx.infcx().ty_to_str( - *fcx.inh.locals.get(&local.node.id))); + fcx.inh.locals.get_copy(&local.node.id))); visit::visit_local(local, e, v); }; @@ -479,7 +479,7 @@ pub fn check_fn(ccx: @mut CrateCtxt, debug!("Pattern binding %s is assigned to %s", *tcx.sess.str_of(path.idents[0]), fcx.infcx().ty_to_str( - *fcx.inh.locals.get(&p.id))); + fcx.inh.locals.get_copy(&p.id))); } _ => {} } @@ -3492,7 +3492,7 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) { ~"visit_tydesc" => { let tydesc_name = special_idents::tydesc; assert!(tcx.intrinsic_defs.contains_key(&tydesc_name)); - let (_, tydesc_ty) = *tcx.intrinsic_defs.get(&tydesc_name); + let (_, tydesc_ty) = tcx.intrinsic_defs.get_copy(&tydesc_name); let (_, visitor_object_ty) = ty::visitor_object_ty(tcx); let td_ptr = ty::mk_ptr(ccx.tcx, ty::mt { ty: tydesc_ty, diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index c177d5ab0eb3a..100e23e024c45 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -490,7 +490,7 @@ pub fn early_resolve_expr(ex: @ast::expr, for fcx.opt_node_ty_substs(ex.id) |substs| { debug!("vtable resolution on parameter bounds for expr %s", ex.repr(fcx.tcx())); - let def = *cx.tcx.def_map.get(&ex.id); + let def = cx.tcx.def_map.get_copy(&ex.id); let did = ast_util::def_id_of_def(def); let item_ty = ty::lookup_item_type(cx.tcx, did); debug!("early resolve expr: def %? %?, %?, %s", ex.id, did, def, diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index d779c20b3e81c..09f0b83e61689 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -651,7 +651,7 @@ pub impl CoherenceChecker { fn get_self_type_for_implementation(&self, implementation: @Impl) -> ty_param_bounds_and_ty { - return *self.crate_context.tcx.tcache.get(&implementation.did); + return self.crate_context.tcx.tcache.get_copy(&implementation.did); } // Privileged scope checking @@ -711,7 +711,7 @@ pub impl CoherenceChecker { fn trait_ref_to_trait_def_id(&self, trait_ref: @trait_ref) -> def_id { let def_map = self.crate_context.tcx.def_map; - let trait_def = *def_map.get(&trait_ref.ref_id); + let trait_def = def_map.get_copy(&trait_ref.ref_id); let trait_id = def_id_of_def(trait_def); return trait_id; } @@ -751,7 +751,7 @@ pub impl CoherenceChecker { -> bool { match original_type.node { ty_path(_, path_id) => { - match *self.crate_context.tcx.def_map.get(&path_id) { + match self.crate_context.tcx.def_map.get_copy(&path_id) { def_ty(def_id) | def_struct(def_id) => { if def_id.crate != local_crate { return false; diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 0ffd398d03c19..3da6995b42362 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -220,7 +220,7 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, { let tcx = ccx.tcx; let region_paramd = tcx.region_paramd_items.find(&trait_id).map(|&x| *x); - match *tcx.items.get(&trait_id) { + match tcx.items.get_copy(&trait_id) { ast_map::node_item(@ast::item { node: ast::item_trait(ref generics, _, ref ms), _ From 6cb273ed4efb6724b1c713c3ac35d14e52999fb1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 May 2013 13:50:10 -0400 Subject: [PATCH 37/46] Address all FIXMEs from #5562 --- src/librustc/middle/astencode.rs | 8 +++----- src/librustc/middle/kind.rs | 8 ++------ src/librustc/middle/trans/base.rs | 2 +- src/librustc/middle/trans/callee.rs | 6 +----- src/librustc/middle/trans/type_of.rs | 6 ++---- src/librustc/middle/trans/type_use.rs | 4 ++-- 6 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 7a3bdce875da2..fad1af749ea7e 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -739,7 +739,7 @@ trait ebml_writer_helpers { fn emit_arg(&self, ecx: @e::EncodeContext, arg: ty::arg); fn emit_ty(&self, ecx: @e::EncodeContext, ty: ty::t); fn emit_vstore(&self, ecx: @e::EncodeContext, vstore: ty::vstore); - fn emit_tys(&self, ecx: @e::EncodeContext, tys: ~[ty::t]); + fn emit_tys(&self, ecx: @e::EncodeContext, tys: &[ty::t]); fn emit_type_param_def(&self, ecx: @e::EncodeContext, type_param_def: &ty::TypeParameterDef); @@ -766,7 +766,7 @@ impl ebml_writer_helpers for writer::Encoder { } } - fn emit_tys(&self, ecx: @e::EncodeContext, tys: ~[ty::t]) { + fn emit_tys(&self, ecx: @e::EncodeContext, tys: &[ty::t]) { do self.emit_from_vec(tys) |ty| { self.emit_ty(ecx, *ty) } @@ -868,9 +868,7 @@ fn encode_side_tables_for_id(ecx: @e::EncodeContext, do ebml_w.tag(c::tag_table_node_type_subst) { ebml_w.id(id); do ebml_w.tag(c::tag_table_val) { - // FIXME(#5562): removing this copy causes a segfault - // before stage2 - ebml_w.emit_tys(ecx, /*bad*/copy **tys) + ebml_w.emit_tys(ecx, **tys) } } } diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index 3afe8c3b9d68f..44090e32880bc 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -268,8 +268,6 @@ pub fn check_expr(e: @expr, cx: Context, v: visit::vt) { _ => e.id }; for cx.tcx.node_type_substs.find(&type_parameter_id).each |ts| { - // FIXME(#5562): removing this copy causes a segfault before stage2 - let ts = /*bad*/ copy **ts; let type_param_defs = match e.node { expr_path(_) => { let did = ast_util::def_id_of_def(cx.tcx.def_map.get_copy(&e.id)); @@ -293,7 +291,7 @@ pub fn check_expr(e: @expr, cx: Context, v: visit::vt) { ts.repr(cx.tcx), type_param_defs.repr(cx.tcx))); } - for vec::each2(ts, *type_param_defs) |&ty, type_param_def| { + for vec::each2(**ts, *type_param_defs) |&ty, type_param_def| { check_bounds(cx, type_parameter_id, e.span, ty, type_param_def) } } @@ -331,12 +329,10 @@ fn check_ty(aty: @Ty, cx: Context, v: visit::vt) { match aty.node { ty_path(_, id) => { for cx.tcx.node_type_substs.find(&id).each |ts| { - // FIXME(#5562): removing this copy causes a segfault before stage2 - let ts = /*bad*/ copy **ts; let did = ast_util::def_id_of_def(cx.tcx.def_map.get_copy(&id)); let type_param_defs = ty::lookup_item_type(cx.tcx, did).generics.type_param_defs; - for vec::each2(ts, *type_param_defs) |&ty, type_param_def| { + for vec::each2(**ts, *type_param_defs) |&ty, type_param_def| { check_bounds(cx, aty.id, aty.span, ty, type_param_def) } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 5419628cd958a..8082e9cce5171 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2501,7 +2501,7 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::node_id) -> ValueRef { Some(&v) => v, None => { let mut exprt = false; - let val = match *ccx.tcx.items.get(&id) { + let val = match ccx.tcx.items.get_copy(&id) { ast_map::node_item(i, pth) => { let my_path = vec::append(/*bad*/copy *pth, ~[path_name(i.ident)]); diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index c4c6133b405c0..12f91fb8597bd 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -339,16 +339,12 @@ pub fn trans_method_call(in_cx: block, node_id_type(in_cx, call_ex.callee_id), expr_ty(in_cx, call_ex), |cx| { - match cx.ccx().maps.method_map.find(&call_ex.id) { + match cx.ccx().maps.method_map.find_copy(&call_ex.id) { Some(origin) => { debug!("origin for %s: %s", call_ex.repr(in_cx.tcx()), origin.repr(in_cx.tcx())); - // FIXME(#5562): removing this copy causes a segfault - // before stage2 - let origin = /*bad*/ copy *origin; - meth::trans_method_callee(cx, call_ex.callee_id, rcvr, diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index a842f91f0ed6e..fc27c11c06f24 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -110,8 +110,7 @@ pub fn type_of_non_gc_box(cx: @CrateContext, t: ty::t) -> TypeRef { pub fn sizing_type_of(cx: @CrateContext, t: ty::t) -> TypeRef { match cx.llsizingtypes.find(&t) { - // FIXME(#5562): removing this copy causes a segfault in stage1 core - Some(t) => return /*bad*/ copy *t, + Some(t) => return *t, None => () } @@ -178,8 +177,7 @@ pub fn type_of(cx: @CrateContext, t: ty::t) -> TypeRef { // Check the cache. match cx.lltypes.find(&t) { - // FIXME(#5562): removing this copy causes a segfault in stage1 core - Some(t) => return /*bad*/ copy *t, + Some(&t) => return t, None => () } diff --git a/src/librustc/middle/trans/type_use.rs b/src/librustc/middle/trans/type_use.rs index fb2358a57e2a7..46cc99e71cc11 100644 --- a/src/librustc/middle/trans/type_use.rs +++ b/src/librustc/middle/trans/type_use.rs @@ -260,10 +260,10 @@ pub fn mark_for_method_call(cx: Context, e_id: node_id, callee_id: node_id) { // above because the recursive call to `type_needs` can trigger // inlining and hence can cause `method_map` and // `node_type_substs` to be modified. - for opt_static_did.each |did| { + for opt_static_did.each |&did| { for cx.ccx.tcx.node_type_substs.find_copy(&callee_id).each |ts| { let type_uses = type_uses_for(cx.ccx, did, ts.len()); - for vec::each2(*type_uses, ts) |uses, subst| { + for vec::each2(*type_uses, *ts) |uses, subst| { type_needs(cx, *uses, *subst) } } From 7b36e34c89372b4a159d4ad565ce11d412fbea04 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 May 2013 21:05:37 -0400 Subject: [PATCH 38/46] Fix two more write guard failures --- src/librustc/middle/resolve.rs | 10 ++-- src/libsyntax/ext/pipes/liveness.rs | 2 +- src/test/run-fail/borrowck-wg-imm-then-mut.rs | 19 +++++++ src/test/run-fail/borrowck-wg-mut-then-imm.rs | 19 +++++++ src/test/run-pass/regions-mock-trans-impls.rs | 54 ------------------- 5 files changed, 45 insertions(+), 59 deletions(-) create mode 100644 src/test/run-fail/borrowck-wg-imm-then-mut.rs create mode 100644 src/test/run-fail/borrowck-wg-mut-then-imm.rs delete mode 100644 src/test/run-pass/regions-mock-trans-impls.rs diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 911e265e6163c..946bf26fd2713 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -4825,10 +4825,12 @@ pub impl Resolver { expr_loop(_, Some(label)) => { do self.with_label_rib { - let this = &mut *self; - let def_like = dl_def(def_label(expr.id)); - let rib = this.label_ribs[this.label_ribs.len() - 1]; - rib.bindings.insert(label, def_like); + { + let this = &mut *self; + let def_like = dl_def(def_label(expr.id)); + let rib = this.label_ribs[this.label_ribs.len() - 1]; + rib.bindings.insert(label, def_like); + } visit_expr(expr, (), visitor); } diff --git a/src/libsyntax/ext/pipes/liveness.rs b/src/libsyntax/ext/pipes/liveness.rs index bd5353b210031..8799bd064f658 100644 --- a/src/libsyntax/ext/pipes/liveness.rs +++ b/src/libsyntax/ext/pipes/liveness.rs @@ -42,7 +42,7 @@ use ext::pipes::proto::{protocol_}; use std::bitv::Bitv; -pub fn analyze(proto: &mut protocol_, _cx: @ext_ctxt) { +pub fn analyze(proto: @mut protocol_, _cx: @ext_ctxt) { debug!("initializing colive analysis"); let num_states = proto.num_states(); let mut colive = do (copy proto.states).map_to_vec |state| { diff --git a/src/test/run-fail/borrowck-wg-imm-then-mut.rs b/src/test/run-fail/borrowck-wg-imm-then-mut.rs new file mode 100644 index 0000000000000..e2c8a0b549c36 --- /dev/null +++ b/src/test/run-fail/borrowck-wg-imm-then-mut.rs @@ -0,0 +1,19 @@ +// error-pattern:borrowed + +// Test that if you imm borrow then mut borrow it fails. + +fn add1(a:@mut int) +{ + add2(a); // already frozen +} + +fn add2(_:&mut int) +{ +} + +pub fn main() +{ + let a = @mut 3; + let b = &*a; // freezes a + add1(a); +} diff --git a/src/test/run-fail/borrowck-wg-mut-then-imm.rs b/src/test/run-fail/borrowck-wg-mut-then-imm.rs new file mode 100644 index 0000000000000..58b2a1d87beed --- /dev/null +++ b/src/test/run-fail/borrowck-wg-mut-then-imm.rs @@ -0,0 +1,19 @@ +// error-pattern:borrowed + +// Test that if you mut borrow then imm borrow it fails. + +fn add1(a:@mut int) +{ + add2(a); // already frozen +} + +fn add2(_:&int) +{ +} + +pub fn main() +{ + let a = @mut 3; + let b = &mut *a; // freezes a + add1(a); +} diff --git a/src/test/run-pass/regions-mock-trans-impls.rs b/src/test/run-pass/regions-mock-trans-impls.rs deleted file mode 100644 index d54aae7bb6337..0000000000000 --- a/src/test/run-pass/regions-mock-trans-impls.rs +++ /dev/null @@ -1,54 +0,0 @@ -// xfail-fast - -// Copyright 2012 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. - -extern mod std; -use core::libc; -use core::sys; -use core::cast; -use std::arena::Arena; - -struct Bcx<'self> { - fcx: &'self Fcx<'self> -} - -struct Fcx<'self> { - arena: &'self mut Arena, - ccx: &'self Ccx -} - -struct Ccx { - x: int -} - -fn h<'r>(bcx : &'r mut Bcx<'r>) -> &'r mut Bcx<'r> { - // XXX: Arena has a bad interface here; it should return mutable pointers. - // But this patch is too big to roll that in. - unsafe { - cast::transmute(bcx.fcx.arena.alloc(|| Bcx { fcx: bcx.fcx })) - } -} - -fn g(fcx: &mut Fcx) { - let mut bcx = Bcx { fcx: fcx }; - h(&mut bcx); -} - -fn f(ccx: &mut Ccx) { - let mut a = Arena(); - let mut fcx = Fcx { arena: &mut a, ccx: ccx }; - return g(&mut fcx); -} - -pub fn main() { - let mut ccx = Ccx { x: 0 }; - f(&mut ccx); -} From 4dc62dfcf35bda8c8edf424c6c50f8584f554217 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 May 2013 05:17:36 -0400 Subject: [PATCH 39/46] do not run regionck if there have been type errors --- src/librustc/middle/typeck/check/regionck.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 36606ab7d8990..b4a1cab7b219c 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -138,15 +138,19 @@ pub impl Rcx { pub fn regionck_expr(fcx: @mut FnCtxt, e: @ast::expr) { let rcx = @mut Rcx { fcx: fcx, errors_reported: 0 }; - let v = regionck_visitor(); - (v.visit_expr)(e, rcx, v); + if !fcx.tcx().sess.has_errors() { // regionck assumes typeck succeeded + let v = regionck_visitor(); + (v.visit_expr)(e, rcx, v); + } fcx.infcx().resolve_regions(); } pub fn regionck_fn(fcx: @mut FnCtxt, blk: &ast::blk) { let rcx = @mut Rcx { fcx: fcx, errors_reported: 0 }; - let v = regionck_visitor(); - (v.visit_block)(blk, rcx, v); + if !fcx.tcx().sess.has_errors() { // regionck assumes typeck succeeded + let v = regionck_visitor(); + (v.visit_block)(blk, rcx, v); + } fcx.infcx().resolve_regions(); } From e235f6ca53bac14158a6320aab49f31bd8e8bbe0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 May 2013 05:18:23 -0400 Subject: [PATCH 40/46] remove some unused mut decls and vars --- src/librustc/back/link.rs | 2 +- src/librustc/middle/trans/closure.rs | 4 ---- src/librustc/middle/trans/reachable.rs | 2 +- src/librustc/middle/ty.rs | 2 +- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index de6469e81807d..c34c7fe303ee2 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -788,7 +788,7 @@ pub fn link_binary(sess: Session, }; debug!("output: %s", output.to_str()); - let mut cc_args = link_args(sess, obj_filename, out_filename, lm); + let cc_args = link_args(sess, obj_filename, out_filename, lm); debug!("%s link args: %s", cc_prog, str::connect(cc_args, ~" ")); // We run 'cc' here let prog = run::program_output(cc_prog, cc_args); diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index 5742463174f65..e0a20f6490715 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -208,7 +208,6 @@ pub fn store_environment(bcx: block, // allocate closure in the heap let Result {bcx: bcx, val: llbox} = allocate_cbox(bcx, sigil, cdata_ty); - let mut temp_cleanups = ~[]; // cbox_ty has the form of a tuple: (a, b, c) we want a ptr to a // tuple. This could be a ptr in uniq or a box or on stack, @@ -244,9 +243,6 @@ pub fn store_environment(bcx: block, } } - for vec::each(temp_cleanups) |cleanup| { - revoke_clean(bcx, *cleanup); - } ClosureResult { llbox: llbox, cdata_ty: cdata_ty, bcx: bcx } } diff --git a/src/librustc/middle/trans/reachable.rs b/src/librustc/middle/trans/reachable.rs index 4d5a7a72a8d5c..1dd73f76da735 100644 --- a/src/librustc/middle/trans/reachable.rs +++ b/src/librustc/middle/trans/reachable.rs @@ -42,7 +42,7 @@ pub fn find_reachable(crate_mod: &_mod, exp_map2: resolve::ExportMap2, tcx: ty::ctxt, method_map: typeck::method_map) -> map { let mut rmap = HashSet::new(); { - let mut cx = @mut ctx { + let cx = @mut ctx { exp_map2: exp_map2, tcx: tcx, method_map: method_map, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 03704afdb861f..d3875bad13a7b 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1950,7 +1950,7 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents { let _i = indenter(); - let mut result = match get(ty).sty { + let result = match get(ty).sty { // Scalar and unique types are sendable, constant, and owned ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_bare_fn(_) | ty_ptr(_) => { From 2ea52a38e59b85b4b6998661b38425ce29839aed Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 May 2013 09:00:37 -0400 Subject: [PATCH 41/46] refinement to technique used to not run regionck --- src/libcore/cleanup.rs | 2 +- src/librustc/driver/session.rs | 3 ++ src/librustc/middle/typeck/check/mod.rs | 18 +++++-- src/librustc/middle/typeck/check/regionck.rs | 54 +++++++++---------- src/librustc/middle/typeck/mod.rs | 6 ++- src/libsyntax/diagnostic.rs | 8 ++- src/test/compile-fail/dead-code-ret.rs | 12 +++-- src/test/compile-fail/for-loop-decl.rs | 35 ------------ src/test/compile-fail/regions-bounds.rs | 6 +-- .../compile-fail/regions-ret-borrowed-1.rs | 2 - src/test/compile-fail/regions-ret-borrowed.rs | 2 - src/test/compile-fail/type-shadow.rs | 7 +-- 12 files changed, 66 insertions(+), 89 deletions(-) delete mode 100644 src/test/compile-fail/for-loop-decl.rs diff --git a/src/libcore/cleanup.rs b/src/libcore/cleanup.rs index 435b1cb7f34c3..06f6185586d85 100644 --- a/src/libcore/cleanup.rs +++ b/src/libcore/cleanup.rs @@ -15,7 +15,7 @@ use ptr::mut_null; use repr::BoxRepr; use sys::TypeDesc; use cast::transmute; -use unstable::lang::clear_task_borrow_list; +#[cfg(notest)] use unstable::lang::clear_task_borrow_list; #[cfg(notest)] use ptr::to_unsafe_ptr; diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 3b9bbbb9f1c5d..582e1d606bca6 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -188,6 +188,9 @@ pub impl Session_ { fn err(@self, msg: &str) { self.span_diagnostic.handler().err(msg) } + fn err_count(@self) -> uint { + self.span_diagnostic.handler().err_count() + } fn has_errors(@self) -> bool { self.span_diagnostic.handler().has_errors() } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 5357d092a9077..7e63db89edbcc 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -207,9 +207,11 @@ pub impl PurityState { } pub struct FnCtxt { - // var_bindings, locals and next_var_id are shared - // with any nested functions that capture the environment - // (and with any functions whose environment is being captured). + // Number of errors that had been reported when we started + // checking this function. On exit, if we find that *more* errors + // have been reported, we will skip regionck and other work that + // expects the types within the function to be consistent. + err_count_on_creation: uint, ret_ty: ty::t, // Used by loop bodies that return from the outer function @@ -263,6 +265,7 @@ pub fn blank_fn_ctxt(ccx: @mut CrateCtxt, // It's kind of a kludge to manufacture a fake function context // and statement context, but we might as well do write the code only once @mut FnCtxt { + err_count_on_creation: ccx.tcx.sess.err_count(), ret_ty: rty, indirect_ret_ty: None, ps: PurityState::function(ast::pure_fn, 0), @@ -328,6 +331,7 @@ pub fn check_fn(ccx: @mut CrateCtxt, */ let tcx = ccx.tcx; + let err_count_on_creation = tcx.sess.err_count(); // ______________________________________________________________________ // First, we have to replace any bound regions in the fn and self @@ -368,6 +372,7 @@ pub fn check_fn(ccx: @mut CrateCtxt, }; @mut FnCtxt { + err_count_on_creation: err_count_on_creation, ret_ty: ret_ty, indirect_ret_ty: indirect_ret_ty, ps: PurityState::function(purity, id), @@ -642,7 +647,12 @@ impl AstConv for FnCtxt { } pub impl FnCtxt { - fn infcx(&self) -> @mut infer::InferCtxt { self.inh.infcx } + fn infcx(&self) -> @mut infer::InferCtxt { + self.inh.infcx + } + fn err_count_since_creation(&self) -> uint { + self.ccx.tcx.sess.err_count() - self.err_count_on_creation + } fn search_in_scope_regions( &self, span: span, diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index b4a1cab7b219c..03dd32353db6a 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -138,7 +138,8 @@ pub impl Rcx { pub fn regionck_expr(fcx: @mut FnCtxt, e: @ast::expr) { let rcx = @mut Rcx { fcx: fcx, errors_reported: 0 }; - if !fcx.tcx().sess.has_errors() { // regionck assumes typeck succeeded + if fcx.err_count_since_creation() == 0 { + // regionck assumes typeck succeeded let v = regionck_visitor(); (v.visit_expr)(e, rcx, v); } @@ -147,7 +148,8 @@ pub fn regionck_expr(fcx: @mut FnCtxt, e: @ast::expr) { pub fn regionck_fn(fcx: @mut FnCtxt, blk: &ast::blk) { let rcx = @mut Rcx { fcx: fcx, errors_reported: 0 }; - if !fcx.tcx().sess.has_errors() { // regionck assumes typeck succeeded + if fcx.err_count_since_creation() == 0 { + // regionck assumes typeck succeeded let v = regionck_visitor(); (v.visit_block)(blk, rcx, v); } @@ -409,10 +411,6 @@ fn constrain_callee(rcx: @mut Rcx, let call_region = ty::re_scope(call_expr.id); let callee_ty = rcx.resolve_node_type(call_expr.callee_id); - if ty::type_is_error(callee_ty) { - return; - } - match ty::get(callee_ty).sty { ty::ty_bare_fn(*) => { } ty::ty_closure(ref closure_ty) => { @@ -432,9 +430,12 @@ fn constrain_callee(rcx: @mut Rcx, } } _ => { - tcx.sess.span_bug( - callee_expr.span, - fmt!("Calling non-function: %s", callee_ty.repr(tcx))); + // this should not happen, but it does if the program is + // erroneous + // + // tcx.sess.span_bug( + // callee_expr.span, + // fmt!("Calling non-function: %s", callee_ty.repr(tcx))); } } } @@ -456,9 +457,6 @@ fn constrain_call(rcx: @mut Rcx, debug!("constrain_call(call_expr=%s, implicitly_ref_args=%?)", call_expr.repr(tcx), implicitly_ref_args); let callee_ty = rcx.resolve_node_type(call_expr.callee_id); - if ty::type_is_error(callee_ty) { - return; - } let fn_sig = ty::ty_fn_sig(callee_ty); // `callee_region` is the scope representing the time in which the @@ -919,7 +917,7 @@ pub mod guarantor { // 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_error(rptr_ty) && !ty::type_is_bot(rptr_ty) { + if !ty::type_is_bot(rptr_ty) { let tcx = rcx.fcx.ccx.tcx; debug!("rptr_ty=%s", ty_to_str(tcx, rptr_ty)); let r = ty::ty_region(tcx, span, rptr_ty); @@ -1216,29 +1214,25 @@ pub mod guarantor { } ast::pat_region(p) => { let rptr_ty = rcx.resolve_node_type(pat.id); - if !ty::type_is_error(rptr_ty) { - let r = ty::ty_region(rcx.fcx.tcx(), pat.span, rptr_ty); - link_ref_bindings_in_pat(rcx, p, Some(r)); - } + let r = ty::ty_region(rcx.fcx.tcx(), pat.span, rptr_ty); + link_ref_bindings_in_pat(rcx, p, Some(r)); } ast::pat_lit(*) => {} ast::pat_range(*) => {} ast::pat_vec(ref before, ref slice, ref after) => { let vec_ty = rcx.resolve_node_type(pat.id); - if !ty::type_is_error(vec_ty) { - let vstore = ty::ty_vstore(vec_ty); - let guarantor1 = match vstore { - ty::vstore_fixed(_) | ty::vstore_uniq => guarantor, - ty::vstore_slice(r) => Some(r), - ty::vstore_box => None - }; - - link_ref_bindings_in_pats(rcx, before, guarantor1); - for slice.each |&p| { - link_ref_bindings_in_pat(rcx, p, guarantor); - } - link_ref_bindings_in_pats(rcx, after, guarantor1); + let vstore = ty::ty_vstore(vec_ty); + let guarantor1 = match vstore { + ty::vstore_fixed(_) | ty::vstore_uniq => guarantor, + ty::vstore_slice(r) => Some(r), + ty::vstore_box => None + }; + + link_ref_bindings_in_pats(rcx, before, guarantor1); + for slice.each |&p| { + link_ref_bindings_in_pat(rcx, p, guarantor); } + link_ref_bindings_in_pats(rcx, after, guarantor1); } } } diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 0012eb700302a..1a152f3c29119 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -414,7 +414,11 @@ pub fn check_crate(tcx: ty::ctxt, time(time_passes, ~"type collecting", || collect::collect_item_types(ccx, crate)); - time(time_passes, ~"method resolution", || + // this ensures that later parts of type checking can assume that items + // have valid types and not error + tcx.sess.abort_if_errors(); + + time(time_passes, ~"coherence checking", || coherence::check_coherence(ccx, crate)); time(time_passes, ~"type checking", || diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs index 0f2374a892b4a..b313a2fc6fcc9 100644 --- a/src/libsyntax/diagnostic.rs +++ b/src/libsyntax/diagnostic.rs @@ -24,6 +24,7 @@ pub trait handler { fn fatal(@mut self, msg: &str) -> !; fn err(@mut self, msg: &str); fn bump_err_count(@mut self); + fn err_count(@mut self) -> uint; fn has_errors(@mut self) -> bool; fn abort_if_errors(@mut self); fn warn(@mut self, msg: &str); @@ -98,7 +99,12 @@ impl handler for HandlerT { fn bump_err_count(@mut self) { self.err_count += 1u; } - fn has_errors(@mut self) -> bool { self.err_count > 0u } + fn err_count(@mut self) -> uint { + self.err_count + } + fn has_errors(@mut self) -> bool { + self.err_count > 0u + } fn abort_if_errors(@mut self) { let s; match self.err_count { diff --git a/src/test/compile-fail/dead-code-ret.rs b/src/test/compile-fail/dead-code-ret.rs index 97f6149b162fc..5fa796db88444 100644 --- a/src/test/compile-fail/dead-code-ret.rs +++ b/src/test/compile-fail/dead-code-ret.rs @@ -10,8 +10,12 @@ // except according to those terms. -// error-pattern: dead +fn f(caller: &str) { + debug!(caller); + let x: uint = 0u32; // induce type error //~ ERROR mismatched types +} -fn f(caller: str) { debug!(caller); } - -fn main() { return f("main"); debug!("Paul is dead"); } +fn main() { + return f("main"); + debug!("Paul is dead"); //~ WARNING unreachable +} diff --git a/src/test/compile-fail/for-loop-decl.rs b/src/test/compile-fail/for-loop-decl.rs deleted file mode 100644 index de28d72677728..0000000000000 --- a/src/test/compile-fail/for-loop-decl.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2012 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. - -// error-pattern: mismatched types -extern mod std; -use std::bitv; -use core::hashmap::HashMap; - -struct FnInfo { - vars: HashMap -} - -struct VarInfo { - a: uint, - b: uint, -} - -fn bitv_to_str(enclosing: FnInfo, v: ~bitv::Bitv) -> str { - let s = ""; - - // error is that the value type in the hash map is var_info, not a box - for enclosing.vars.each_value |val| { - if *v.get(val) { s += "foo"; } - } - return s; -} - -fn main() { debug!("OK"); } diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs index cccd135e9f836..ab2620d46fdc5 100644 --- a/src/test/compile-fail/regions-bounds.rs +++ b/src/test/compile-fail/regions-bounds.rs @@ -23,10 +23,8 @@ 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 ` } -fn a_fn4<'a,'b>(e: int<'a>) -> int<'b> { - //~^ ERROR region parameters are not allowed on this type - //~^^ ERROR region parameters are not allowed on this type - return e; +fn a_fn4<'a,'b>() { + let _: int<'a> = 1; //~ ERROR region parameters are not allowed on this type } fn main() { } diff --git a/src/test/compile-fail/regions-ret-borrowed-1.rs b/src/test/compile-fail/regions-ret-borrowed-1.rs index f600f6f6edd92..a572d90313b6a 100644 --- a/src/test/compile-fail/regions-ret-borrowed-1.rs +++ b/src/test/compile-fail/regions-ret-borrowed-1.rs @@ -18,8 +18,6 @@ fn with<'a, R>(f: &fn(x: &'a int) -> R) -> R { fn return_it<'a>() -> &'a int { with(|o| o) //~ ERROR mismatched types - //~^ ERROR reference is not valid outside of its lifetime - //~^^ ERROR reference is not valid outside of its lifetime } fn main() { diff --git a/src/test/compile-fail/regions-ret-borrowed.rs b/src/test/compile-fail/regions-ret-borrowed.rs index a3540f8f931f9..ec9a908ba9876 100644 --- a/src/test/compile-fail/regions-ret-borrowed.rs +++ b/src/test/compile-fail/regions-ret-borrowed.rs @@ -21,8 +21,6 @@ fn with(f: &fn(x: &int) -> R) -> R { fn return_it() -> &int { with(|o| o) //~ ERROR mismatched types - //~^ ERROR reference is not valid outside of its lifetime - //~^^ ERROR reference is not valid outside of its lifetime } fn main() { diff --git a/src/test/compile-fail/type-shadow.rs b/src/test/compile-fail/type-shadow.rs index a9b4a85e6385c..c4a412f64c8d4 100644 --- a/src/test/compile-fail/type-shadow.rs +++ b/src/test/compile-fail/type-shadow.rs @@ -9,14 +9,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -// error-pattern: mismatched types - fn main() { type X = int; type Y = X; if true { - type X = str; - let y: Y = "hello"; + type X = &'static str; + let y: Y = "hello"; //~ ERROR mismatched types } } From 84f7ecce5c8b727309d0d48e84a965a5a3437fd1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 May 2013 09:56:17 -0400 Subject: [PATCH 42/46] Adust arena test: can no longer allocate recursively --- src/libstd/arena.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libstd/arena.rs b/src/libstd/arena.rs index b9a09323f81d0..da882d53fcffa 100644 --- a/src/libstd/arena.rs +++ b/src/libstd/arena.rs @@ -315,9 +315,6 @@ fn test_arena_destructors_fail() { } // Now, fail while allocating do arena.alloc::<@int> { - // First, recursively allocate something else; that needs to - // get freed too. - do arena.alloc { @20 }; // Now fail. fail!(); }; From c50a9d5b664478e533ba1d1d353213d70c8ad589 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 May 2013 11:16:17 -0400 Subject: [PATCH 43/46] Use rust_try_get_task for compat with new rt, and strenghten assumptions about borrow list --- src/libcore/unstable/lang.rs | 62 +++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index deff06d46f6c9..934874968d716 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -22,7 +22,6 @@ use rt::{context, OldTaskContext}; use rt::local_services::borrow_local_services; use option::{Option, Some, None}; use io; -use task::rt::rust_get_task; #[allow(non_camel_case_types)] pub type rust_task = c_void; @@ -56,6 +55,9 @@ pub mod rustrt { #[rust_stack] fn rust_set_task_borrow_list(task: *rust_task, map: *c_void); + #[rust_stack] + fn rust_try_get_task() -> *rust_task; + fn rust_dbg_breakpoint(); } } @@ -84,26 +86,32 @@ struct BorrowRecord { fn try_take_task_borrow_list() -> Option<~[BorrowRecord]> { unsafe { - let cur_task = rust_get_task(); - let ptr = rustrt::rust_take_task_borrow_list(cur_task); - if ptr.is_null() { - None + let cur_task: *rust_task = rustrt::rust_try_get_task(); + if cur_task.is_not_null() { + let ptr = rustrt::rust_take_task_borrow_list(cur_task); + if ptr.is_null() { + None + } else { + let v: ~[BorrowRecord] = transmute(ptr); + Some(v) + } } else { - let v: ~[BorrowRecord] = transmute(ptr); - Some(v) + None } } } fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) { unsafe { - let cur_task = rust_get_task(); - let mut borrow_list: ~[BorrowRecord] = { - let ptr = rustrt::rust_take_task_borrow_list(cur_task); - if ptr.is_null() { ~[] } else { transmute(ptr) } - }; - borrow_list = f(borrow_list); - rustrt::rust_set_task_borrow_list(cur_task, transmute(borrow_list)); + let cur_task: *rust_task = rustrt::rust_try_get_task(); + if cur_task.is_not_null() { + let mut borrow_list: ~[BorrowRecord] = { + let ptr = rustrt::rust_take_task_borrow_list(cur_task); + if ptr.is_null() { ~[] } else { transmute(ptr) } + }; + borrow_list = f(borrow_list); + rustrt::rust_set_task_borrow_list(cur_task, transmute(borrow_list)); + } } } @@ -128,9 +136,7 @@ unsafe fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { for borrow_list.each_reverse |entry| { if entry.box == box { str::push_str(&mut msg, sep); - let filename = unsafe { - str::raw::from_c_str(entry.file) - }; + let filename = str::raw::from_c_str(entry.file); str::push_str(&mut msg, filename); str::push_str(&mut msg, fmt!(":%u", entry.line as uint)); sep = " and at "; @@ -351,25 +357,21 @@ pub unsafe fn record_borrow(a: *u8, old_ref_count: uint, pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint, file: *c_char, line: size_t) { if (old_ref_count & ALL_BITS) == 0 { - // was not borrowed before + // was not borrowed before, so we should find the record at + // the end of the list let a: *mut BoxRepr = transmute(a); debug_borrow("unrecord_borrow:", a, old_ref_count, 0, file, line); do swap_task_borrow_list |borrow_list| { let mut borrow_list = borrow_list; - let br = BorrowRecord {box: a, file: file, line: line}; - match borrow_list.rposition_elem(&br) { - Some(idx) => { - borrow_list.remove(idx); - borrow_list - } - None => { - let err = fmt!("no borrow found, br=%?, borrow_list=%?", - br, borrow_list); - do str::as_buf(err) |msg_p, _| { - fail_(msg_p as *c_char, file, line) - } + assert!(!borrow_list.is_empty()); + let br = borrow_list.pop(); + if br.box != a || br.file != file || br.line != line { + let err = fmt!("wrong borrow found, br=%?", br); + do str::as_buf(err) |msg_p, _| { + fail_(msg_p as *c_char, file, line) } } + borrow_list } } } From 0ef4e860da1fb755b9fff5d1b30bee3974514ea2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 May 2013 14:02:28 -0400 Subject: [PATCH 44/46] Replace NOTE with FIXME --- src/librustc/middle/borrowck/check_loans.rs | 4 ++-- .../middle/borrowck/gather_loans/mod.rs | 2 +- src/librustc/middle/borrowck/mod.rs | 2 +- src/librustc/middle/dataflow.rs | 2 +- src/librustc/middle/region.rs | 3 +-- src/librustc/middle/trans/base.rs | 2 +- src/librustc/middle/trans/expr.rs | 3 ++- src/librustc/middle/trans/reachable.rs | 12 ++++++------ src/librustc/middle/typeck/check/mod.rs | 11 +++++------ src/librustc/middle/typeck/check/regionck.rs | 19 +++++++++++-------- 10 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index b9a3f4bd6fcbb..ba719fe34d719 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -579,7 +579,7 @@ pub impl<'self> CheckLoanCtxt<'self> { } } - // NOTE inadequare if/when we permit `move a.b` + // FIXME(#4384) inadequare if/when we permit `move a.b` // check for a conflicting loan: for opt_loan_path(cmt).each |&lp| { @@ -604,7 +604,7 @@ pub impl<'self> CheckLoanCtxt<'self> { // However, I added it for consistency and lest the system // should change in the future. // - // FIXME(#5074) nested method calls + // FIXME(#6268) nested method calls // self.check_for_conflicting_loans(callee_id); } } diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index 922af0cadec6c..5f3c5d977fef5 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -389,7 +389,7 @@ pub impl GatherLoanCtxt { self.all_loans.push(loan); // if loan_gen_scope != borrow_id { - // NOTE handle case where gen_scope is not borrow_id + // FIXME(#6268) Nested method calls // // Typically, the scope of the loan includes the point at // which the loan is originated. This diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index c9a4de3830719..68e70d245f779 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -516,7 +516,7 @@ pub impl BorrowckCtxt { fmt!("%s in an aliasable location", prefix)); } mc::AliasableManaged(ast::m_mutbl) => { - // FIXME(#5074) we should prob do this borrow + // FIXME(#6269) reborrow @mut to &mut self.tcx.sess.span_err( span, fmt!("%s in a `@mut` pointer; \ diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index 9d032a1839e23..ccb34851046bd 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -808,7 +808,7 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> { self.walk_expr(arg0, in_out, loop_scopes); self.walk_exprs(args, in_out, loop_scopes); - // FIXME(#5074) nested method calls + // FIXME(#6268) nested method calls // self.merge_with_entry_set(callee_id, in_out); // self.dfcx.apply_gen_kill(callee_id, in_out); diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 5834ae1d78055..cdc3aa9fedb7e 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -393,7 +393,7 @@ pub fn resolve_expr(expr: @ast::expr, cx: Context, visitor: visit::vt) match expr.node { ast::expr_assign_op(*) | ast::expr_index(*) | ast::expr_binary(*) | ast::expr_unary(*) | ast::expr_call(*) | ast::expr_method_call(*) => { - // FIXME(#5074) Nested method calls + // FIXME(#6268) Nested method calls // // The lifetimes for a call or method call look as follows: // @@ -949,7 +949,6 @@ pub fn determine_rp_in_crate(sess: Session, while cx.worklist.len() != 0 { let c_id = cx.worklist.pop(); let c_variance = cx.region_paramd_items.get_copy(&c_id); - // NOTE cleanup scopes cause an exaggerated lock here debug!("popped %d from worklist", c_id); match cx.dep_map.find(&c_id) { None => {} diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 90dd9103011d5..34f798ec7a631 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -998,7 +998,7 @@ pub fn find_bcx_for_scope(bcx: block, scope_id: ast::node_id) -> block { return bcx_sid } - // NOTE This is messier than it ought to be and not really right + // FIXME(#6268, #6248) hacky cleanup for nested method calls Some(NodeInfo { callee_id: Some(id), _ }) if id == scope_id => { return bcx_sid } diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 6f1dbd8c2fec4..0e8b2e0474661 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -250,7 +250,8 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock { let tcx = bcx.tcx(); let unit_ty = ty::sequence_element_type(tcx, datum.ty); - // NOTE prob need to distinguish "auto-slice" from explicit index? + + // FIXME(#6272) need to distinguish "auto-slice" from explicit index? let (bcx, base, len) = datum.get_vec_base_and_len(bcx, expr.span, expr.id); diff --git a/src/librustc/middle/trans/reachable.rs b/src/librustc/middle/trans/reachable.rs index 1dd73f76da735..9bbf50397c35a 100644 --- a/src/librustc/middle/trans/reachable.rs +++ b/src/librustc/middle/trans/reachable.rs @@ -75,11 +75,11 @@ fn traverse_def_id(cx: @mut ctx, did: def_id) { Some(&ast_map::node_item(item, _)) => traverse_public_item(cx, item), Some(&ast_map::node_method(_, impl_id, _)) => traverse_def_id(cx, impl_id), Some(&ast_map::node_foreign_item(item, _, _, _)) => { - let cx = &mut *cx; // NOTE reborrow @mut + let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut cx.rmap.insert(item.id); } Some(&ast_map::node_variant(ref v, _, _)) => { - let cx = &mut *cx; // NOTE reborrow @mut + let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut cx.rmap.insert(v.node.id); } _ => () @@ -109,7 +109,7 @@ fn traverse_public_item(cx: @mut ctx, item: @item) { item_foreign_mod(ref nm) => { if !traverse_exports(cx, item.id) { for nm.items.each |item| { - let cx = &mut *cx; // NOTE reborrow @mut + let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut cx.rmap.insert(item.id); } } @@ -127,7 +127,7 @@ fn traverse_public_item(cx: @mut ctx, item: @item) { attr::find_inline_attr(m.attrs) != attr::ia_none { { - let cx = &mut *cx; // NOTE reborrow @mut + let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut cx.rmap.insert(m.id); } traverse_inline_body(cx, &m.body); @@ -136,7 +136,7 @@ fn traverse_public_item(cx: @mut ctx, item: @item) { } item_struct(ref struct_def, _) => { for struct_def.ctor_id.each |&ctor_id| { - let cx = &mut *cx; // NOTE reborrow @mut + let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut cx.rmap.insert(ctor_id); } } @@ -153,7 +153,7 @@ fn traverse_public_item(cx: @mut ctx, item: @item) { fn traverse_ty<'a>(ty: @Ty, cx: @mut ctx<'a>, v: visit::vt<@mut ctx<'a>>) { { - let cx = &mut *cx; // NOTE reborrow @mut + let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut if cx.rmap.contains(&ty.id) { return; } cx.rmap.insert(ty.id); } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 7e63db89edbcc..e171765ef6c4e 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -1301,12 +1301,11 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, // Store the type of `f` as the type of the callee let fn_ty = fcx.expr_ty(f); - // NOTE here we write the callee type before regions have been - // substituted; in the method case, we write the type after - // regions have been substituted. Methods are correct, but it - // is awkward to deal with this now. Best thing would I think - // be to just have a separate "callee table" that contains the - // FnSig and not a general purpose ty::t + // FIXME(#6273) should write callee type AFTER regions have + // been subst'd. However, it is awkward to deal with this + // now. Best thing would I think be to just have a separate + // "callee table" that contains the FnSig and not a general + // purpose ty::t fcx.write_ty(call_expr.callee_id, fn_ty); // Extract the function signature from `in_fty`. diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 03dd32353db6a..491c7fadb18bd 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -157,14 +157,17 @@ pub fn regionck_fn(fcx: @mut FnCtxt, blk: &ast::blk) { } fn regionck_visitor() -> rvt { + // FIXME(#3238) should use visit_pat, not visit_arm/visit_local, + // However, right now we run into an issue whereby some free + // regions are not properly related if they appear within the + // types of arguments that must be inferred. This could be + // addressed by deferring the construction of the region + // hierarchy, and in particular the relationships between free + // regions, until regionck, as described in #3238. visit::mk_vt(@visit::Visitor {visit_item: visit_item, visit_expr: visit_expr, - // NOTE this should be visit_pat - // but causes errors in formal - // arguments in closures due to - // #XYZ! - //visit_pat: visit_pat, + //visit_pat: visit_pat, // (*) see FIXME above visit_arm: visit_arm, visit_local: visit_local, @@ -294,7 +297,7 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) { // Require that the resulting region encompasses // the current node. // - // FIXME(#5074) remove to support nested method calls + // FIXME(#6268) remove to support nested method calls constrain_regions_in_type_of_node( rcx, expr.id, ty::re_scope(expr.id), expr.span); } @@ -374,7 +377,7 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) { // the type of the node expr.id here *before applying // adjustments*. // - // FIXME(#5074) nested method calls requires that this rule change + // FIXME(#6268) nested method calls requires that this rule change let ty0 = rcx.resolve_node_type(expr.id); constrain_regions_in_type(rcx, ty::re_scope(expr.id), expr.span, ty0); } @@ -462,7 +465,7 @@ fn constrain_call(rcx: @mut Rcx, // `callee_region` is the scope representing the time in which the // call occurs. // - // FIXME(#5074) to support nested method calls, should be callee_id + // FIXME(#6268) to support nested method calls, should be callee_id let callee_scope = call_expr.id; let callee_region = ty::re_scope(callee_scope); From 8b32bde408bb6fdf6fcf4e7c2727fff3a31dd8b1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 May 2013 20:10:11 -0400 Subject: [PATCH 45/46] add rust_take_task_borrow_list and rust_set_task_borrow_list to rustrt.def.in --- src/rt/rustrt.def.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 3ca05b94711e8..1c3f6370deda4 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -233,3 +233,5 @@ rust_boxed_region_malloc rust_boxed_region_free rust_try rust_begin_unwind +rust_take_task_borrow_list +rust_set_task_borrow_list From ce45f390dd55fbd8ebaf1be07ad1b3b3bb5d2f4d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 May 2013 20:14:54 -0400 Subject: [PATCH 46/46] Remove debug_mem since @graydon said it conflicted with GC changes --- src/libcore/cleanup.rs | 10 +--------- src/libcore/unstable/lang.rs | 22 +--------------------- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/src/libcore/cleanup.rs b/src/libcore/cleanup.rs index 06f6185586d85..424cc3483092d 100644 --- a/src/libcore/cleanup.rs +++ b/src/libcore/cleanup.rs @@ -167,8 +167,7 @@ fn debug_mem() -> bool { #[cfg(notest)] #[lang="annihilate"] pub unsafe fn annihilate() { - use unstable::lang::{local_free}; - use unstable::lang; + use unstable::lang::local_free; use io::WriterUtil; use io; use libc; @@ -192,10 +191,8 @@ pub unsafe fn annihilate() { for each_live_alloc(true) |box, uniq| { stats.n_total_boxes += 1; if uniq { - lang::debug_mem("Managed-uniq: ", &*box); stats.n_unique_boxes += 1; } else { - lang::debug_mem("Immortalizing: ", &*box); (*box).header.ref_count = managed::raw::RC_IMMORTAL; } } @@ -207,13 +204,9 @@ pub unsafe fn annihilate() { // callback, as the original value may have been freed. for each_live_alloc(false) |box, uniq| { if !uniq { - lang::debug_mem("Invoking tydesc/glue on: ", &*box); let tydesc: *TypeDesc = transmute(copy (*box).header.type_desc); let drop_glue: DropGlue = transmute(((*tydesc).drop_glue, 0)); - lang::debug_mem("Box data: ", &(*box).data); - lang::debug_mem("Type descriptor: ", tydesc); drop_glue(to_unsafe_ptr(&tydesc), transmute(&(*box).data)); - lang::debug_mem("Dropped ", &*box); } } @@ -225,7 +218,6 @@ pub unsafe fn annihilate() { // not be valid after. for each_live_alloc(true) |box, uniq| { if !uniq { - lang::debug_mem("About to free: ", &*box); stats.n_bytes_freed += (*((*box).header.type_desc)).size + sys::size_of::(); diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index 934874968d716..8153c2d43d998 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -153,32 +153,13 @@ unsafe fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { #[lang="exchange_malloc"] #[inline(always)] pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char { - let result = transmute(exchange_alloc::malloc(transmute(td), transmute(size))); - debug_mem("exchange_malloc: ", result); - return result; + transmute(exchange_alloc::malloc(transmute(td), transmute(size))) } /// Because this code is so perf. sensitive, use a static constant so that /// debug printouts are compiled out most of the time. static ENABLE_DEBUG: bool = false; -#[inline] -pub fn debug_mem(tag: &'static str, p: *const T) { - //! A useful debugging function that prints a pointer + tag + newline - //! without allocating memory. - - if ENABLE_DEBUG && ::rt::env::get().debug_mem { - debug_mem_slow(tag, p); - } - - fn debug_mem_slow(tag: &'static str, p: *const T) { - let dbg = STDERR_FILENO as io::fd_t; - dbg.write_str(tag); - dbg.write_hex(p as uint); - dbg.write_str("\n"); - } -} - #[inline] unsafe fn debug_borrow(tag: &'static str, p: *const T, @@ -252,7 +233,6 @@ impl DebugPrints for io::fd_t { #[lang="exchange_free"] #[inline(always)] pub unsafe fn exchange_free(ptr: *c_char) { - debug_mem("exchange_free: ", ptr); exchange_alloc::free(transmute(ptr)) }