new borrow checker (mass squash)
This commit is contained in:
parent
b5a7e8b353
commit
a896440ca1
172 changed files with 6475 additions and 4303 deletions
|
@ -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::<BoxRepr>();
|
||||
|
|
|
@ -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<W:Writer,C> Writer for Wrapper<W, C> {
|
||||
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<W:Writer,C> Writer for Wrapper<W, C> {
|
|||
}
|
||||
|
||||
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<T>(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);
|
||||
|
||||
|
|
|
@ -304,7 +304,7 @@ pub impl Scheduler {
|
|||
unsafe {
|
||||
let last_task = transmute::<Option<&Task>, 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
|
||||
|
|
|
@ -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<char> for StrCharIterator<'self> {
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<char> {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<T>(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<T>(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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<T> {
|
|||
}
|
||||
|
||||
/// Extension methods for vectors
|
||||
impl<'self,T:Copy> CopyableVector<T> for &'self const [T] {
|
||||
impl<'self,T:Copy> CopyableVector<T> for &'self [T] {
|
||||
/// Returns a copy of `v`.
|
||||
#[inline]
|
||||
fn to_owned(&self) -> ~[T] {
|
||||
|
@ -2710,18 +2707,12 @@ impl<A:Clone> 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> {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) -> ! {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
|
|
File diff suppressed because it is too large
Load diff
750
src/librustc/middle/borrowck/doc.rs
Normal file
750
src/librustc/middle/borrowck/doc.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
|
||||
# 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.
|
||||
|
||||
*/
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// 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<ast::node_id>
|
||||
}
|
||||
|
||||
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<PreserveCondition> = {
|
||||
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<PreserveCondition> {
|
||||
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);
|
||||
}
|
||||
|
322
src/librustc/middle/borrowck/gather_loans/lifetime.rs
Normal file
322
src/librustc/middle/borrowck/gather_loans/lifetime.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! 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<ast::node_id>) {
|
||||
//! 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<ast::node_id>) {
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
710
src/librustc/middle/borrowck/gather_loans/mod.rs
Normal file
710
src/librustc/middle/borrowck/gather_loans/mod.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// 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<ast::node_id>
|
||||
}
|
||||
|
||||
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
|
||||
// <compile-fail/borrowck-bad-nested-calls-free.rs>
|
||||
// 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);
|
||||
}
|
251
src/librustc/middle/borrowck/gather_loans/restrictions.rs
Normal file
251
src/librustc/middle/borrowck/gather_loans/restrictions.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! 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});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
|
||||
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)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load diff
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// 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<PreserveCondition>
|
||||
{
|
||||
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<PreserveCondition> {
|
||||
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<PreserveCondition> {
|
||||
// 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<PreserveCondition> {
|
||||
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<PreserveCondition> {
|
||||
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)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
|
|
1009
src/librustc/middle/dataflow.rs
Normal file
1009
src/librustc/middle/dataflow.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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<node_id, @mut ~[node_id]>;
|
||||
|
||||
#[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<node_id> {
|
||||
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<Variable> {
|
||||
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<node_id>) {
|
||||
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| {
|
||||
|
|
|
@ -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<deref_kind> {
|
||||
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<deref_kind> {
|
|||
}
|
||||
|
||||
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<ast::node_id> {
|
||||
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<N:ast_node>(elt: N, of_cmt: cmt,
|
||||
vect: ty::t, mutbl: MutabilityCategory,
|
||||
mt: ty::mt) -> cmt
|
||||
fn interior<N: ast_node>(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 => @~"<anonymous field>",
|
||||
comp_variant(_) => @~"<enum>"
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
cat_deref(subcmt, _, _) |
|
||||
cat_comp(subcmt, _) |
|
||||
cat_discr(subcmt, _) |
|
||||
cat_stack_upvar(subcmt) => {
|
||||
subcmt.cat.derefs_through_mutable_box()
|
||||
}
|
||||
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_special(*) |
|
||||
cat_static_item |
|
||||
cat_implicit_self |
|
||||
cat_copied_upvar(*) |
|
||||
cat_local(*) |
|
||||
cat_binding(*) |
|
||||
cat_self(*) |
|
||||
cat_arg(*) |
|
||||
cat_self(*) => {
|
||||
false
|
||||
cat_deref(_, _, unsafe_ptr(*)) |
|
||||
cat_deref(_, _, gc_ptr(*)) |
|
||||
cat_deref(_, _, region_ptr(*)) => {
|
||||
self
|
||||
}
|
||||
cat_stack_upvar(b) |
|
||||
cat_discr(b, _) |
|
||||
cat_interior(b, _) |
|
||||
cat_deref(b, _, uniq_ptr(*)) => {
|
||||
b.guarantor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_mutable_box(&const self) -> bool {
|
||||
match *self {
|
||||
cat_deref(_, _, gc_ptr(ast::m_mutbl)) => true,
|
||||
_ => false
|
||||
fn is_freely_aliasable(&self) -> bool {
|
||||
self.freely_aliasable().is_some()
|
||||
}
|
||||
|
||||
fn freely_aliasable(&self) -> Option<AliasableReason> {
|
||||
//! 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_copied_upvar(*) |
|
||||
cat_local(*) |
|
||||
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 => ~"*"
|
||||
}
|
||||
}
|
||||
|
||||
impl Repr for interior_kind {
|
||||
fn repr(&self, tcx: ty::ctxt) -> ~str {
|
||||
match *self {
|
||||
interior_field(fld, _) => copy *tcx.sess.str_of(fld),
|
||||
interior_index(*) => ~"[]",
|
||||
interior_tuple => ~"()",
|
||||
interior_anon_field => ~"<anonymous field>",
|
||||
interior_variant(_) => ~"<enum>"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -246,10 +246,19 @@ pub type MovesMap = @mut HashSet<node_id>;
|
|||
* expression */
|
||||
pub type VariableMovesMap = @mut HashMap<node_id, @expr>;
|
||||
|
||||
/**
|
||||
* 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<node_id>;
|
||||
|
||||
/** 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<node_id> {
|
||||
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 => {
|
||||
|
|
|
@ -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<ast::node_id, ast::node_id>,
|
||||
priv free_region_map: HashMap<FreeRegion, ~[FreeRegion]>,
|
||||
priv cleanup_scopes: HashSet<ast::node_id>
|
||||
}
|
||||
|
||||
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<ast::node_id>,
|
||||
// Scope where variables should be parented to
|
||||
var_parent: parent,
|
||||
|
||||
// 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)
|
||||
// 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<ast::node_id>
|
||||
{
|
||||
|
@ -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<ctxt>) {
|
||||
pub fn resolve_block(blk: &ast::blk, cx: Context, visitor: visit::vt<Context>) {
|
||||
// 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<ctxt>) {
|
||||
pub fn resolve_arm(arm: &ast::arm, cx: Context, visitor: visit::vt<Context>) {
|
||||
visit::visit_arm(arm, cx, visitor);
|
||||
}
|
||||
|
||||
pub fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
|
||||
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<Context>) {
|
||||
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<ctxt>) {
|
||||
pub fn resolve_stmt(stmt: @ast::stmt, cx: Context, visitor: visit::vt<Context>) {
|
||||
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<ctxt>) {
|
||||
record_parent(cx, expr.id);
|
||||
pub fn resolve_expr(expr: @ast::expr, cx: Context, visitor: visit::vt<Context>) {
|
||||
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<ctxt>) {
|
||||
record_parent(cx, local.node.id);
|
||||
cx: Context,
|
||||
visitor: visit::vt<Context>) {
|
||||
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<ctxt>) {
|
||||
pub fn resolve_item(item: @ast::item, cx: Context, visitor: visit::vt<Context>) {
|
||||
// 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<ctxt>) {
|
||||
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}
|
||||
}
|
||||
cx: Context,
|
||||
visitor: visit::vt<Context>) {
|
||||
debug!("region::resolve_fn(id=%?, body.node.id=%?, cx.parent=%?)",
|
||||
id, body.node.id, cx.parent);
|
||||
|
||||
visit::fk_anon(*) | visit::fk_fn_block(*) => {
|
||||
// Closures continue with the inherited scope.
|
||||
cx
|
||||
}
|
||||
};
|
||||
|
||||
// Record the ID of `self`.
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 => {}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -574,13 +574,17 @@ pub trait get_node_info {
|
|||
|
||||
impl get_node_info for @ast::expr {
|
||||
fn info(&self) -> Option<NodeInfo> {
|
||||
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<NodeInfo> {
|
||||
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<ast::node_id>,
|
||||
span: span
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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<R>(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<R>(tcx: ty::ctxt,
|
|||
_ => {
|
||||
tcx.sess.bug(fmt!(
|
||||
"cannot get field types from the type %s",
|
||||
ty_to_str(tcx, ty)));
|
||||
ty.repr(tcx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,11 @@ pub fn trans_impl(ccx: @CrateContext, path: path, name: ast::ident,
|
|||
methods: &[@ast::method], generics: &ast::Generics,
|
||||
self_ty: Option<ty::t>, 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| {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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<node_id> = 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<node_id> = 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<node_id> = 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(),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -119,7 +119,8 @@ pub fn lookup(
|
|||
// In a call `a.b::<X, Y, ...>(...)`:
|
||||
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<method_map_entry> {
|
||||
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<def_id>,
|
||||
impl_dups: @mut HashSet<def_id>,
|
||||
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));
|
||||
|
|
|
@ -923,11 +923,9 @@ pub impl FnCtxt {
|
|||
|
||||
fn region_var_if_parameterized(&self,
|
||||
rp: Option<ty::region_variance>,
|
||||
span: span,
|
||||
lower_bound: ty::Region)
|
||||
span: span)
|
||||
-> Option<ty::Region> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
(v.visit_ty)(l.node.ty, rcx, v);
|
||||
for l.node.init.each |i| {
|
||||
(v.visit_expr)(*i, rcx, v);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
fn visit_block(b: &ast::blk, rcx: @mut Rcx, v: rvt) {
|
||||
visit::visit_block(b, 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_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, _) => {
|
||||
// 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);
|
||||
}
|
||||
|
||||
for args.each |arg| {
|
||||
constrain_auto_ref(rcx, *arg);
|
||||
}
|
||||
constrain_callee(rcx, expr, callee);
|
||||
constrain_call(rcx, expr, None, *args, false);
|
||||
}
|
||||
|
||||
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_method_call(arg0, _, _, ref args, _) => {
|
||||
constrain_call(rcx, expr, Some(arg0), *args, false);
|
||||
}
|
||||
|
||||
constrain_auto_ref(rcx, rcvr);
|
||||
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_unary(_, lhs) if has_method_map => {
|
||||
// As above.
|
||||
constrain_call(rcx, expr, Some(lhs), [], true);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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 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)
|
||||
}
|
||||
|
||||
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) {
|
||||
fn constrain_free_variables(rcx: @mut Rcx,
|
||||
region: ty::Region,
|
||||
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) {
|
||||
/*!
|
||||
*
|
||||
* 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);
|
||||
|
|
|
@ -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(*) |
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -339,7 +339,7 @@ pub fn fixup_err_to_str(f: fixup_err) -> ~str {
|
|||
|
||||
fn new_ValsAndBindings<V:Copy,T:Copy>() -> ValsAndBindings<V, T> {
|
||||
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<T:Copy>(&self, f: &fn() -> Result<T,ty::type_err>)
|
||||
-> Result<T,ty::type_err>;
|
||||
|
@ -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<T,E>(@mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
|
||||
fn commit<T,E>(&mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
|
||||
assert!(!self.in_snapshot());
|
||||
|
||||
debug!("commit()");
|
||||
|
@ -611,7 +589,7 @@ pub impl InferCtxt {
|
|||
}
|
||||
|
||||
/// Execute `f`, unroll bindings on failure
|
||||
fn try<T,E>(@mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
|
||||
fn try<T,E>(&mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
|
||||
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<T,E>(@mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
|
||||
fn probe<T,E>(&mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
|
||||
debug!("probe()");
|
||||
do indent {
|
||||
let snapshot = self.start_snapshot();
|
||||
|
@ -647,45 +625,47 @@ fn next_simple_var<V:Copy,T:Copy>(
|
|||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -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<TwoRegions>;
|
||||
|
||||
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<RegionVid>,
|
||||
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,
|
||||
|
|
|
@ -23,7 +23,7 @@ pub enum VarValue<V, T> {
|
|||
}
|
||||
|
||||
pub struct ValsAndBindings<V, T> {
|
||||
vals: @mut SmallIntMap<VarValue<V, T>>,
|
||||
vals: SmallIntMap<VarValue<V, T>>,
|
||||
bindings: ~[(V, VarValue<V, T>)],
|
||||
}
|
||||
|
||||
|
@ -60,26 +60,25 @@ pub impl InferCtxt {
|
|||
vid: V) -> Node<V, T>
|
||||
{
|
||||
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<V,T> = 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<V,T> = 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'<empty> ", 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;
|
||||
|
|
|
@ -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<U>(&self, blk: &fn(x: &mut T) -> U) -> U {
|
||||
fn write<U>(&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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<D: Decoder> Decodable<D> for TrieSet {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(stage1)]
|
||||
#[cfg(stage2)]
|
||||
#[cfg(stage3)]
|
||||
impl<
|
||||
E: Encoder,
|
||||
K: Encodable<E> + Eq + TotalOrd,
|
||||
|
@ -750,9 +744,6 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(stage1)]
|
||||
#[cfg(stage2)]
|
||||
#[cfg(stage3)]
|
||||
impl<
|
||||
D: Decoder,
|
||||
K: Decodable<D> + Eq + TotalOrd,
|
||||
|
@ -771,9 +762,6 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(stage1)]
|
||||
#[cfg(stage2)]
|
||||
#[cfg(stage3)]
|
||||
impl<
|
||||
S: Encoder,
|
||||
T: Encodable<S> + Eq + TotalOrd
|
||||
|
@ -789,9 +777,6 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(stage1)]
|
||||
#[cfg(stage2)]
|
||||
#[cfg(stage3)]
|
||||
impl<
|
||||
D: Decoder,
|
||||
T: Decodable<D> + Eq + TotalOrd
|
||||
|
|
|
@ -61,6 +61,7 @@ pub fn merge_sort<T:Copy>(v: &const [T], le: Le<T>) -> ~[T] {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(stage0)]
|
||||
fn part<T>(arr: &mut [T], left: uint,
|
||||
right: uint, pivot: uint, compare_func: Le<T>) -> uint {
|
||||
arr[pivot] <-> arr[right];
|
||||
|
@ -79,6 +80,23 @@ fn part<T>(arr: &mut [T], left: uint,
|
|||
return storage_index;
|
||||
}
|
||||
|
||||
#[cfg(not(stage0))]
|
||||
fn part<T>(arr: &mut [T], left: uint,
|
||||
right: uint, pivot: uint, compare_func: Le<T>) -> 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<T>(arr: &mut [T], left: uint,
|
||||
right: uint, compare_func: Le<T>) {
|
||||
if right > left {
|
||||
|
@ -162,7 +180,8 @@ fn qsort3<T:Copy + Ord + Eq>(arr: &mut [T], left: int, right: int) {
|
|||
*/
|
||||
pub fn quick_sort3<T:Copy + Ord + Eq>(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<T:Copy + Ord>(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);
|
||||
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;
|
||||
}
|
||||
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<T:Copy + Ord>(array: &mut [T], start: uint) {
|
|||
fn reverse_slice<T>(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<T:Copy + Ord> MergeState<T> {
|
|||
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<T:Copy + Ord> MergeState<T> {
|
|||
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<T:Copy + Ord> MergeState<T> {
|
|||
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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<node_id, ast_node>;
|
||||
|
@ -104,7 +104,6 @@ pub type map = @mut HashMap<node_id, ast_node>;
|
|||
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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -44,10 +44,10 @@ pub impl<T:Eq + IterBytes + Hash + Const + Copy> Interner<T> {
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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); }
|
||||
}
|
||||
|
|
|
@ -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); }
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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!()
|
||||
|
|
43
src/test/compile-fail/borrowck-bad-nested-calls-free.rs
Normal file
43
src/test/compile-fail/borrowck-bad-nested-calls-free.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test 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() {}
|
43
src/test/compile-fail/borrowck-bad-nested-calls-move.rs
Normal file
43
src/test/compile-fail/borrowck-bad-nested-calls-move.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test 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() {}
|
|
@ -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() {}
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use core::hashmap::HashMap;
|
|||
fn main() {
|
||||
let mut buggy_map :HashMap<uint, &uint> =
|
||||
HashMap::new::<uint, &uint>();
|
||||
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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
52
src/test/compile-fail/borrowck-lend-flow-if.rs
Normal file
52
src/test/compile-fail/borrowck-lend-flow-if.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// 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>() -> 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() {}
|
164
src/test/compile-fail/borrowck-lend-flow-loop.rs
Normal file
164
src/test/compile-fail/borrowck-lend-flow-loop.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// 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>() -> 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() {}
|
60
src/test/compile-fail/borrowck-lend-flow-match.rs
Normal file
60
src/test/compile-fail/borrowck-lend-flow-match.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// 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() {}
|
|
@ -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>() -> T { fail!(); }
|
||||
|
||||
fn inc(v: &mut ~int) {
|
||||
*v = ~(**v + 1);
|
||||
}
|
||||
|
||||
fn post_aliased_const() {
|
||||
fn pre_freeze() {
|
||||
// In this instance, the freeze starts before the mut borrow.
|
||||
|
||||
let mut v = ~3;
|
||||
let _w = &v;
|
||||
borrow_mut(v); //~ ERROR cannot borrow
|
||||
}
|
||||
|
||||
fn pre_const() {
|
||||
// In this instance, the freeze starts before the mut borrow.
|
||||
|
||||
let mut v = ~3;
|
||||
borrow(v);
|
||||
let _w = &const v;
|
||||
borrow_mut(v);
|
||||
}
|
||||
|
||||
fn post_aliased_mut() {
|
||||
// SPURIOUS--flow
|
||||
fn post_freeze() {
|
||||
// In this instance, the const alias starts after the borrow.
|
||||
|
||||
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 post_aliased_scope(cond: bool) {
|
||||
let mut v = ~3;
|
||||
borrow(v);
|
||||
if cond { inc(&mut v); }
|
||||
}
|
||||
|
||||
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 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
|
||||
}
|
||||
}
|
||||
|
||||
fn at_most_once_block() {
|
||||
fn at_most_once(f: &fn()) { f() }
|
||||
|
||||
// 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
|
||||
}
|
||||
borrow_mut(v);
|
||||
let _w = &v;
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue