new borrow checker (mass squash)

This commit is contained in:
Niko Matsakis 2013-03-15 15:24:24 -04:00
parent b5a7e8b353
commit a896440ca1
172 changed files with 6475 additions and 4303 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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) = &LT_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) = &LT_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) = &LT_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) = &LT_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) = &LT_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.
*/

View file

@ -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: &int;
/// 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);
}

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

View 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: &int;
/// 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);
}

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

@ -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 => {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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(*) |

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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!()

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
}
_ => {}
}

View file

@ -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
}
_ => {}
}

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

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

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

View file

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