librustc: Disallow "mut" from distributing over bindings.

This is the backwards-incompatible part of per-binding-site "mut".
This commit is contained in:
Patrick Walton 2013-06-06 18:54:14 -07:00 committed by Corey Richardson
parent 1c0aa78481
commit f9b54541ee
32 changed files with 190 additions and 50 deletions

View File

@ -2862,13 +2862,13 @@ call to the method `make_string`.
Types in Rust are categorized into kinds, based on various properties of the components of the type.
The kinds are:
`Const`
`Freeze`
: Types of this kind are deeply immutable;
they contain no mutable memory locations directly or indirectly via pointers.
`Owned`
`Send`
: Types of this kind can be safely sent between tasks.
This kind includes scalars, owning pointers, owned closures, and
structural types containing only other owned types. All `Owned` types are `Static`.
structural types containing only other owned types. All `Send` types are `Static`.
`Static`
: Types of this kind do not contain any borrowed pointers;
this can be a useful guarantee for code that breaks borrowing assumptions using [`unsafe` operations](#unsafe-functions).
@ -2882,7 +2882,7 @@ The kinds are:
trait provides a single method `finalize` that takes no parameters, and is run
when values of the type are dropped. Such a method is called a "destructor",
and are always executed in "top-down" order: a value is completely destroyed
before any of the values it owns run their destructors. Only `Owned` types
before any of the values it owns run their destructors. Only `Send` types
that do not implement `Copy` can implement `Drop`.
> **Note:** The `finalize` method may be renamed in future versions of Rust.
@ -2968,10 +2968,10 @@ frame they are allocated within.
A task owns all memory it can *safely* reach through local variables,
as well as managed, owning and borrowed pointers.
When a task sends a value that has the `Owned` trait to another task,
When a task sends a value that has the `Send` trait to another task,
it loses ownership of the value sent and can no longer refer to it.
This is statically guaranteed by the combined use of "move semantics",
and the compiler-checked _meaning_ of the `Owned` trait:
and the compiler-checked _meaning_ of the `Send` trait:
it is only instantiated for (transitively) sendable kinds of data constructor and pointers,
never including managed or borrowed pointers.
@ -3116,7 +3116,7 @@ These include:
- read-only and read-write shared variables with various safe mutual exclusion patterns
- simple locks and semaphores
When such facilities carry values, the values are restricted to the [`Owned` type-kind](#type-kinds).
When such facilities carry values, the values are restricted to the [`Send` type-kind](#type-kinds).
Restricting communication interfaces to this kind ensures that no borrowed or managed pointers move between tasks.
Thus access to an entire data structure can be mediated through its owning "root" value;
no further locking or copying is required to avoid data races within the substructure of such a value.

View File

@ -159,7 +159,7 @@ pub struct Unique<T> {
priv ptr: *mut T
}
impl<T: Owned> Unique<T> {
impl<T: Send> Unique<T> {
pub fn new(value: T) -> Unique<T> {
unsafe {
let ptr = malloc(std::sys::size_of::<T>() as size_t) as *mut T;
@ -182,7 +182,7 @@ impl<T: Owned> Unique<T> {
}
#[unsafe_destructor]
impl<T: Owned> Drop for Unique<T> {
impl<T: Send> Drop for Unique<T> {
fn drop(&self) {
unsafe {
let x = intrinsics::init(); // dummy value to swap in

View File

@ -59,7 +59,8 @@ fn rot(r: int, x: u32) -> u32 {
while i < e {
let (aa, bb, cc, dd) = (a, b, c, d);
let mut (j, base) = (0u, i);
let mut j = 0u;
let mut base = i;
while j < 16u {
x[j] = (msg[base] as u32) + (msg[base + 1u] as u32 << 8u32) +
(msg[base + 2u] as u32 << 16u32) +

View File

@ -415,7 +415,9 @@ enum State {
let mut port = None;
let mut colon_count = 0;
let mut (pos, begin, end) = (0, 2, len);
let mut pos = 0;
let mut begin = 2;
let mut end = len;
for rawurl.iter().enumerate().advance |(i,c)| {
if i < 2 { loop; } // ignore the leading //

View File

@ -380,7 +380,10 @@ fn div_mod_floor_inner(a: BigUint, b: BigUint) -> (BigUint, BigUint) {
let mut d = Zero::zero::<BigUint>();
let mut n = 1;
while m >= b {
let mut (d0, d_unit, b_unit) = div_estimate(&m, &b, n);
let (d0, d_unit, b_unit) = div_estimate(&m, &b, n);
let mut d0 = d0;
let mut d_unit = d_unit;
let mut b_unit = b_unit;
let mut prod = b * d0;
while prod > m {
// FIXME(#6050): overloaded operators force moves with generic types
@ -442,7 +445,8 @@ fn div_estimate(a: &BigUint, b: &BigUint, n: uint)
fn gcd(&self, other: &BigUint) -> BigUint {
// Use Euclid's algorithm
let mut (m, n) = (copy *self, copy *other);
let mut m = copy *self;
let mut n = copy *other;
while !m.is_zero() {
let temp = m;
m = n % temp;

View File

@ -123,7 +123,9 @@ pub fn recv_timeout<T:Copy + Send>(iotask: &IoTask,
msecs: uint,
wait_po: &Port<T>)
-> Option<T> {
let mut (timeout_po, timeout_ch) = stream::<()>();
let (timeout_po, timeout_ch) = stream::<()>();
let mut timeout_po = timeout_po;
let mut timeout_ch = timeout_ch;
delayed_send(iotask, msecs, &timeout_ch, ());
// XXX: Workaround due to ports and channels not being &mut. They should

View File

@ -139,12 +139,14 @@ fn compute_info(&self,
attrs.push(attr);
}
let mut (ret_ty, ret_attr) = if ret_def {
let (ret_ty, ret_attr) = if ret_def {
classify_ret_ty(rty)
} else {
(LLVMType { cast: false, ty: Type::void() }, None)
};
let mut ret_ty = ret_ty;
let sret = ret_attr.is_some();
if sret {
arg_tys.unshift(ret_ty);

View File

@ -178,12 +178,14 @@ fn compute_info(&self,
atys: &[Type],
rty: Type,
ret_def: bool) -> FnType {
let mut (ret_ty, ret_attr) = if ret_def {
let (ret_ty, ret_attr) = if ret_def {
classify_ret_ty(rty)
} else {
(LLVMType { cast: false, ty: Type::void() }, None)
};
let mut ret_ty = ret_ty;
let sret = ret_attr.is_some();
let mut arg_tys = ~[];
let mut attrs = ~[];

View File

@ -360,8 +360,9 @@ fn x86_64_ty(ty: Type,
arg_tys.push(ty);
attrs.push(attr);
}
let mut (ret_ty, ret_attr) = x86_64_ty(rty, |cls| cls.is_ret_bysret(),
let (ret_ty, ret_attr) = x86_64_ty(rty, |cls| cls.is_ret_bysret(),
StructRetAttribute);
let mut ret_ty = ret_ty;
let sret = ret_attr.is_some();
if sret {
arg_tys = vec::append(~[ret_ty], arg_tys);

View File

@ -319,9 +319,10 @@ pub fn trans_fn_ref_with_vtables(
// Should be either intra-crate or inlined.
assert_eq!(def_id.crate, ast::local_crate);
let mut (val, must_cast) =
let (val, must_cast) =
monomorphize::monomorphic_fn(ccx, def_id, &substs,
vtables, opt_impl_did, Some(ref_id));
let mut val = val;
if must_cast && ref_id != 0 {
// Monotype of the REFERENCE to the function (type params
// are subst'd)

View File

@ -907,9 +907,12 @@ fn trans_index(bcx: block,
let scaled_ix = Mul(bcx, ix_val, vt.llunit_size);
base::maybe_name_value(bcx.ccx(), scaled_ix, "scaled_ix");
let mut (bcx, base, len) =
let (bcx, base, len) =
base_datum.get_vec_base_and_len(bcx, index_expr.span,
index_expr.id, 0);
let mut bcx = bcx;
let mut base = base;
let mut len = len;
if ty::type_is_str(base_ty) {
// acccount for null terminator in the case of string

View File

@ -771,7 +771,9 @@ fn read_lines(&self) -> ~[~str] {
fn read_le_uint_n(&self, nbytes: uint) -> u64 {
assert!(nbytes > 0 && nbytes <= 8);
let mut (val, pos, i) = (0u64, 0, nbytes);
let mut val = 0u64;
let mut pos = 0;
let mut i = nbytes;
while i > 0 {
val += (self.read_u8() as u64) << pos;
pos += 8;
@ -787,7 +789,8 @@ fn read_le_int_n(&self, nbytes: uint) -> i64 {
fn read_be_uint_n(&self, nbytes: uint) -> u64 {
assert!(nbytes > 0 && nbytes <= 8);
let mut (val, i) = (0u64, nbytes);
let mut val = 0u64;
let mut i = nbytes;
while i > 0 {
i -= 1;
val += (self.read_u8() as u64) << i * 8;

View File

@ -400,7 +400,8 @@ fn div_rem(&self, other: &$T) -> ($T,$T) {
#[inline]
fn gcd(&self, other: &$T) -> $T {
// Use Euclid's algorithm
let mut (m, n) = (*self, *other);
let mut m = *self;
let mut n = *other;
while m != 0 {
let temp = m;
m = n % temp;

View File

@ -237,7 +237,8 @@ fn div_mod_floor(&self, other: &$T) -> ($T,$T) {
#[inline]
fn gcd(&self, other: &$T) -> $T {
// Use Euclid's algorithm
let mut (m, n) = (*self, *other);
let mut m = *self;
let mut n = *other;
while m != 0 {
let temp = m;
m = n % temp;

View File

@ -720,7 +720,8 @@ macro_rules! memloop (
fn isaac(&mut self) {
self.c += 1;
// abbreviations
let mut (a, b) = (self.a, self.b + self.c);
let mut a = self.a;
let mut b = self.b + self.c;
static midpoint: uint = RAND_SIZE as uint / 2;

View File

@ -89,7 +89,8 @@ fn zero_case<R:Rng>(rng: &mut R, u: f64) -> f64 {
// do-while, so the condition should be true on the first
// run, they get overwritten anyway (0 < 1, so these are
// good).
let mut (x, y) = (1.0, 0.0);
let mut x = 1.0;
let mut y = 0.0;
// XXX infinities?
while -2.0*y < x * x {

View File

@ -343,7 +343,9 @@ impl<T: Reader> ReaderByteConversions for T {
fn read_le_uint_n(&mut self, nbytes: uint) -> u64 {
assert!(nbytes > 0 && nbytes <= 8);
let mut (val, pos, i) = (0u64, 0, nbytes);
let mut val = 0u64;
let mut pos = 0;
let mut i = nbytes;
while i > 0 {
val += (self.read_u8() as u64) << pos;
pos += 8;
@ -359,7 +361,8 @@ fn read_le_int_n(&mut self, nbytes: uint) -> i64 {
fn read_be_uint_n(&mut self, nbytes: uint) -> u64 {
assert!(nbytes > 0 && nbytes <= 8);
let mut (val, i) = (0u64, nbytes);
let mut val = 0u64;
let mut i = nbytes;
while i > 0 {
i -= 1;
val += (self.read_u8() as u64) << i * 8;

View File

@ -473,6 +473,31 @@ enum LengthLimit {
return cont;
}
/**
* Replace all occurrences of one string with another
*
* # Arguments
*
* * s - The string containing substrings to replace
* * from - The string to replace
* * to - The replacement string
*
* # Return value
*
* The original string with all occurances of `from` replaced with `to`
*/
pub fn replace(s: &str, from: &str, to: &str) -> ~str {
let mut result = ~"";
let mut last_end = 0;
for s.matches_index_iter(from).advance |(start, end)| {
result.push_str(unsafe{raw::slice_bytes(s, last_end, start)});
result.push_str(to);
last_end = end;
}
result.push_str(unsafe{raw::slice_bytes(s, last_end, s.len())});
result
}
/*
Section: Comparing strings
*/
@ -631,6 +656,48 @@ pub fn with_capacity(capacity: uint) -> ~str {
buf
}
/**
* As char_len but for a slice of a string
*
* # Arguments
*
* * s - A valid string
* * start - The position inside `s` where to start counting in bytes
* * end - The position where to stop counting
*
* # Return value
*
* The number of Unicode characters in `s` between the given indices.
*/
pub fn count_chars(s: &str, start: uint, end: uint) -> uint {
assert!(s.is_char_boundary(start));
assert!(s.is_char_boundary(end));
let mut i = start;
let mut len = 0u;
while i < end {
let next = s.char_range_at(i).next;
len += 1u;
i = next;
}
return len;
}
/// Counts the number of bytes taken by the first `n` chars in `s`
/// starting from `start`.
pub fn count_bytes<'b>(s: &'b str, start: uint, n: uint) -> uint {
assert!(is_char_boundary(s, start));
let mut end = start;
let mut cnt = n;
let l = s.len();
while cnt > 0u {
assert!(end < l);
let next = s.char_range_at(end).next;
cnt -= 1u;
end = next;
}
end - start
}
/// Given a first byte, determine how many bytes are in this UTF-8 character
pub fn utf8_char_width(b: u8) -> uint {
let byte: uint = b as uint;
@ -737,7 +804,8 @@ pub mod raw {
/// Create a Rust string from a null-terminated *u8 buffer
pub unsafe fn from_buf(buf: *u8) -> ~str {
let mut (curr, i) = (buf, 0u);
let mut curr = buf;
let mut i = 0u;
while *curr != 0u8 {
i += 1u;
curr = ptr::offset(buf, i);

View File

@ -636,7 +636,9 @@ fn make_child_wrapper(child: *rust_task, child_arc: TaskGroupArc,
let child_data = Cell::new((notify_chan, child_arc, ancestors));
let result: ~fn() = || {
// Agh. Get move-mode items into the closure. FIXME (#2829)
let mut (notify_chan, child_arc, ancestors) = child_data.take();
let (notify_chan, child_arc, ancestors) = child_data.take();
let mut child_arc = child_arc;
let mut ancestors = ancestors;
// Child task runs this code.
// Even if the below code fails to kick the child off, we must

View File

@ -53,8 +53,9 @@ fn to_str(&self) -> ~str {
impl<A:ToStr+Hash+Eq, B:ToStr+Hash+Eq> ToStr for HashMap<A, B> {
#[inline]
fn to_str(&self) -> ~str {
let mut (acc, first) = (~"{", true);
for self.iter().advance |(key, value)| {
let mut acc = ~"{";
let mut first = true;
for self.iter().advance |key, value| {
if first {
first = false;
}
@ -73,7 +74,8 @@ fn to_str(&self) -> ~str {
impl<A:ToStr+Hash+Eq> ToStr for HashSet<A> {
#[inline]
fn to_str(&self) -> ~str {
let mut (acc, first) = (~"{", true);
let mut acc = ~"{";
let mut first = true;
for self.iter().advance |element| {
if first {
first = false;
@ -121,7 +123,8 @@ fn to_str(&self) -> ~str {
impl<'self,A:ToStr> ToStr for &'self [A] {
#[inline]
fn to_str(&self) -> ~str {
let mut (acc, first) = (~"[", true);
let mut acc = ~"[";
let mut first = true;
for self.iter().advance |elt| {
if first {
first = false;
@ -139,7 +142,8 @@ fn to_str(&self) -> ~str {
impl<A:ToStr> ToStr for ~[A] {
#[inline]
fn to_str(&self) -> ~str {
let mut (acc, first) = (~"[", true);
let mut acc = ~"[";
let mut first = true;
for self.iter().advance |elt| {
if first {
first = false;
@ -157,7 +161,8 @@ fn to_str(&self) -> ~str {
impl<A:ToStr> ToStr for @[A] {
#[inline]
fn to_str(&self) -> ~str {
let mut (acc, first) = (~"[", true);
let mut acc = ~"[";
let mut first = true;
for self.iter().advance |elt| {
if first {
first = false;

View File

@ -348,7 +348,8 @@ pub fn consume_reverse<T>(mut v: ~[T], f: &fn(uint, v: T)) {
pub fn dedup<T:Eq>(v: &mut ~[T]) {
unsafe {
if v.len() < 1 { return; }
let mut (last_written, next_to_read) = (0, 1);
let mut last_written = 0;
let mut next_to_read = 1;
do as_const_buf(*v) |p, ln| {
// We have a mutable reference to v, so we can make arbitrary
// changes. (cf. push and pop)
@ -798,7 +799,8 @@ pub fn bsearch_elem<T:TotalOrd>(v: &[T], x: &T) -> Option<uint> {
* Convert a vector of pairs into a pair of vectors, by reference. As unzip().
*/
pub fn unzip_slice<T:Copy,U:Copy>(v: &[(T, U)]) -> (~[T], ~[U]) {
let mut (ts, us) = (~[], ~[]);
let mut ts = ~[];
let mut us = ~[];
for v.iter().advance |p| {
let (t, u) = copy *p;
ts.push(t);
@ -816,7 +818,8 @@ pub fn unzip_slice<T:Copy,U:Copy>(v: &[(T, U)]) -> (~[T], ~[U]) {
* of the i-th tuple of the input vector.
*/
pub fn unzip<T,U>(v: ~[(T, U)]) -> (~[T], ~[U]) {
let mut (ts, us) = (~[], ~[]);
let mut ts = ~[];
let mut us = ~[];
do consume(v) |_i, p| {
let (t, u) = p;
ts.push(t);

View File

@ -619,6 +619,15 @@ pub enum Privacy {
Public
}
/// Returns true if the given pattern consists solely of an identifier
/// and false otherwise.
pub fn pat_is_ident(pat: @ast::pat) -> bool {
match pat.node {
ast::pat_ident(*) => true,
_ => false,
}
}
// HYGIENE FUNCTIONS
/// Construct an identifier with the given name and an empty context:

View File

@ -62,6 +62,7 @@ pub enum ObsoleteSyntax {
ObsoleteFixedLengthVectorType,
ObsoleteNamedExternModule,
ObsoleteMultipleLocalDecl,
ObsoleteMutWithMultipleBindings,
}
impl to_bytes::IterBytes for ObsoleteSyntax {
@ -223,6 +224,11 @@ pub fn obsolete(&self, sp: span, kind: ObsoleteSyntax) {
"instead of e.g. `let a = 1, b = 2`, write \
`let (a, b) = (1, 2)`."
),
ObsoleteMutWithMultipleBindings => (
"`mut` with multiple bindings",
"use multiple local declarations instead of e.g. `let mut \
(x, y) = ...`."
),
};
self.report(sp, kind, kind_str, desc);

View File

@ -83,7 +83,8 @@
use parse::obsolete::{ObsoletePurity, ObsoleteStaticMethod};
use parse::obsolete::{ObsoleteConstItem, ObsoleteFixedLengthVectorType};
use parse::obsolete::{ObsoleteNamedExternModule, ObsoleteMultipleLocalDecl};
use parse::token::{can_begin_expr, get_ident_interner, ident_to_str, is_ident, is_ident_or_path};
use parse::obsolete::{ObsoleteMutWithMultipleBindings};
use parse::token::{can_begin_expr, get_ident_interner, is_ident, is_ident_or_path};
use parse::token::{is_plain_ident, INTERPOLATED, keywords, special_idents, token_to_binop};
use parse::token;
use parse::{new_sub_parser_from_file, next_node_id, ParseSess};
@ -821,6 +822,11 @@ pub fn parse_arg_general(&self, require_name: bool) -> arg {
self.parse_arg_mode();
is_mutbl = self.eat_keyword(keywords::Mut);
let pat = self.parse_pat();
if is_mutbl && !ast_util::pat_is_ident(pat) {
self.obsolete(*self.span, ObsoleteMutWithMultipleBindings)
}
self.expect(&token::COLON);
pat
} else {
@ -2560,6 +2566,11 @@ fn parse_pat_ident(&self,
fn parse_local(&self, is_mutbl: bool) -> @local {
let lo = self.span.lo;
let pat = self.parse_pat();
if is_mutbl && !ast_util::pat_is_ident(pat) {
self.obsolete(*self.span, ObsoleteMutWithMultipleBindings)
}
let mut ty = @Ty {
id: self.get_id(),
node: ty_infer,
@ -4420,7 +4431,8 @@ fn parse_items_and_view_items(&self,
let mut attrs = vec::append(first_item_attrs,
self.parse_outer_attributes());
// First, parse view items.
let mut (view_items, items) = (~[], ~[]);
let mut view_items = ~[];
let mut items = ~[];
let mut done = false;
// I think this code would probably read better as a single
// loop with a mutable three-state-variable (for extern mods,

View File

@ -23,7 +23,10 @@ fn main() {
for range(0, h) |y| {
let y = y as f64;
for range(0, w) |x| {
let mut (Zr, Zi, Tr, Ti) = (0f64, 0f64, 0f64, 0f64);
let mut Zr = 0f64;
let mut Zi = 0f64;
let mut Tr = 0f64;
let mut Ti = 0f64;
let Cr = 2.0 * (x as f64) / (w as f64) - 1.5;
let Ci = 2.0 * (y as f64) / (h as f64) - 1.0;

View File

@ -2,7 +2,7 @@ struct Foo {
f: @mut int,
}
impl Drop for Foo { //~ ERROR cannot implement a destructor on a struct that is not Send
impl Drop for Foo { //~ ERROR cannot implement a destructor on a structure that does not satisfy Send
fn drop(&self) {
*self.f = 10;
}

View File

@ -12,7 +12,7 @@ fn foo(_x: @uint) {}
fn main() {
let x = @3u;
let _: ~fn() = || foo(x); //~ ERROR does not fulfill `Owned`
let _: ~fn() = || foo(x); //~ ERROR does not fulfill `Owned`
let _: ~fn() = || foo(x); //~ ERROR does not fulfill `Owned`
let _: ~fn() = || foo(x); //~ ERROR does not fulfill `Send`
let _: ~fn() = || foo(x); //~ ERROR does not fulfill `Send`
let _: ~fn() = || foo(x); //~ ERROR does not fulfill `Send`
}

View File

@ -32,7 +32,7 @@ fn foo(x: Port<()>) -> foo {
let x = Cell::new(foo(Port(@())));
do task::spawn {
let y = x.take(); //~ ERROR does not fulfill `Owned`
let y = x.take(); //~ ERROR does not fulfill `Send`
error!(y);
}
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[non_owned]
#[non_sendable]
enum Foo { A }
fn bar<T: Send>(_: T) {}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[non_owned]
#[non_sendable]
struct Foo { a: int }
fn bar<T: Send>(_: T) {}

View File

@ -13,7 +13,9 @@ struct A { a: int }
pub fn main() {
let u = X {x: 10, y: @A {a: 20}};
let mut X {x: x, y: @A {a: a}} = u;
let X {x: x, y: @A {a: a}} = u;
let mut x = x;
let mut a = a;
x = 100;
a = 100;
assert_eq!(x, 100);

View File

@ -22,7 +22,9 @@
)
pub fn main() {
let mut (p, c) = oneshot::init();
let (p, c) = oneshot::init();
let mut p = p;
let mut c = c;
assert!(!pipes::peek(&mut p));