auto merge of #7262 : nikomatsakis/rust/ref-bindings-in-irrefut-patterns, r=catamorphism

Correct treatment of irrefutable patterns. The old code was wrong in many, many ways. `ref` bindings didn't work, it sometimes copied when it should have moved, the borrow checker didn't even look at such patterns at all, we weren't consistent about preventing values with destructors from being pulled apart, etc.

Fixes #3224.
Fixes #3225.
Fixes #3255.
Fixes #6225.
Fixes #6386.

r? @catamorphism
This commit is contained in:
bors 2013-07-08 18:49:46 -07:00
commit a48ca3290d
72 changed files with 1140 additions and 696 deletions

View file

@ -56,8 +56,8 @@ fn sort_and_fmt(mm: &HashMap<~[u8], uint>, total: uint) -> ~str {
let mut pairs = ~[];
// map -> [(k,%)]
for mm.iter().advance |(&key, &val)| {
pairs.push((key, pct(val, total)));
for mm.iter().advance |(key, &val)| {
pairs.push((copy *key, pct(val, total)));
}
let pairs_sorted = sortKV(pairs);

View file

@ -0,0 +1,16 @@
fn with(f: &fn(&~str)) {}
fn arg_item(&_x: &~str) {}
//~^ ERROR cannot move out of dereference of & pointer
fn arg_closure() {
with(|&_x| ())
//~^ ERROR cannot move out of dereference of & pointer
}
fn let_pat() {
let &_x = &~"hi";
//~^ ERROR cannot move out of dereference of & pointer
}
pub fn main() {}

View file

@ -0,0 +1,22 @@
struct S {f:~str}
impl Drop for S {
fn drop(&self) { println(self.f); }
}
fn move_in_match() {
match S {f:~"foo"} {
S {f:_s} => {}
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
}
fn move_in_let() {
let S {f:_s} = S {f:~"foo"};
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
fn move_in_fn_arg(S {f:_s}: S) {
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
fn main() {}

View file

@ -0,0 +1,22 @@
struct S(~str);
impl Drop for S {
fn drop(&self) { println(**self); }
}
fn move_in_match() {
match S(~"foo") {
S(_s) => {}
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
}
fn move_in_let() {
let S(_s) = S(~"foo");
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
fn move_in_fn_arg(S(_s): S) {
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
fn main() {}

View file

@ -11,7 +11,7 @@ pub fn main() {
Foo { string: ~"baz" }
];
match x {
[first, ..tail] => {
[_, ..tail] => {
match tail {
[Foo { string: a }, Foo { string: b }] => {
//~^ ERROR cannot move out of dereference of & pointer

View file

@ -17,4 +17,41 @@ fn b() {
}
}
fn c() {
let mut vec = [~1, ~2, ~3];
match vec {
[_a, .._b] => {
//~^ ERROR cannot move out
// Note: `_a` is *moved* here, but `b` is borrowing,
// hence illegal.
//
// See comment in middle/borrowck/gather_loans/mod.rs
// in the case covering these sorts of vectors.
}
_ => {}
}
let a = vec[0]; //~ ERROR use of partially moved value: `vec`
}
fn d() {
let mut vec = [~1, ~2, ~3];
match vec {
[.._a, _b] => {
//~^ ERROR cannot move out
}
_ => {}
}
let a = vec[0]; //~ ERROR use of partially moved value: `vec`
}
fn e() {
let mut vec = [~1, ~2, ~3];
match vec {
[_a, _b, _c] => {}
_ => {}
}
let a = vec[0]; //~ ERROR use of partially moved value: `vec`
}
fn main() {}

View file

@ -0,0 +1,11 @@
fn arg_item(~ref x: ~int) -> &'static int {
x //~^ ERROR borrowed value does not live long enough
}
fn with<R>(f: &fn(~int) -> R) -> R { f(~3) }
fn arg_closure() -> &'static int {
with(|~ref x| x) //~ ERROR borrowed value does not live long enough
}
fn main() {}

View file

@ -68,7 +68,7 @@ fn main() {
check_pp(ext_cx, *stmt, pprust::print_stmt, ~"let x = 20;");
let pat = quote_pat!(Some(_));
check_pp(ext_cx, pat, pprust::print_refutable_pat, ~"Some(_)");
check_pp(ext_cx, pat, pprust::print_pat, ~"Some(_)");
}

View file

@ -1,32 +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.
struct foo {bar: baz}
struct baz_ {baz: int}
type baz = @mut baz_;
trait frob {
fn frob(&self);
}
impl frob for foo {
fn frob(&self) {
really_impure(self.bar);
}
}
// Override default mode so that we are passing by value
fn really_impure(bar: baz) {
bar.baz = 3;
}
pub fn main() {}

View file

@ -28,12 +28,12 @@
fn main() {
let a = @mut [3i];
let a = @mut 3i;
let b = @mut [a];
let c = @mut b;
let c = @mut [3];
// this should freeze `a` only
let _x: &mut [int] = c[0];
let _x: &mut int = a;
// hence these writes should not fail:
b[0] = b[0];

View file

@ -0,0 +1,20 @@
// Test that we do not leak when the arg pattern must drop part of the
// argument (in this case, the `y` field).
struct Foo {
x: ~uint,
y: ~uint,
}
fn foo(Foo {x, _}: Foo) -> *uint {
let addr: *uint = &*x;
addr
}
fn main() {
let obj = ~1;
let objptr: *uint = &*obj;
let f = Foo {x: obj, y: ~2};
let xptr = foo(f);
assert_eq!(objptr, xptr);
}

View file

@ -0,0 +1,24 @@
// exec-env:RUST_POISON_ON_FREE=1
// Test argument patterns where we create refs to the inside of `~`
// boxes. Make sure that we don't free the box as we match the
// pattern.
fn getaddr(~ref x: ~uint) -> *uint {
let addr: *uint = &*x;
addr
}
fn checkval(~ref x: ~uint) -> uint {
*x
}
fn main() {
let obj = ~1;
let objptr: *uint = &*obj;
let xptr = getaddr(obj);
assert_eq!(objptr, xptr);
let obj = ~22;
assert_eq!(checkval(obj), 22);
}

View file

@ -0,0 +1,10 @@
// Test that we can compile code that uses a `_` in function argument
// patterns.
fn foo((x, _): (int, int)) -> int {
x
}
fn main() {
assert_eq!(foo((22, 23)), 22);
}

View file

@ -0,0 +1,5 @@
fn main() {
let x = ~"hello";
let ref y = x;
assert_eq!(x.slice(0, x.len()), y.slice(0, y.len()));
}

View file

@ -0,0 +1,27 @@
// Tests a tricky scenario involving string matching,
// copying, and moving to ensure that we don't segfault
// or double-free, as we were wont to do in the past.
use std::io;
use std::os;
fn parse_args() -> ~str {
let args = os::args();
let mut n = 0;
while n < args.len() {
match copy args[n] {
~"-v" => (),
s => {
return s;
}
}
n += 1;
}
return ~""
}
fn main() {
io::println(parse_args());
}

View file

@ -14,8 +14,11 @@
enum t { make_t(@int), clam, }
fn foo(s: @int) {
debug!(::std::sys::refcount(s));
let count = ::std::sys::refcount(s);
let x: t = make_t(s); // ref up
assert_eq!(::std::sys::refcount(s), count + 1u);
debug!(::std::sys::refcount(s));
match x {
make_t(y) => {
@ -38,6 +41,5 @@ pub fn main() {
debug!("%u", ::std::sys::refcount(s));
let count2 = ::std::sys::refcount(s);
let _ = ::std::sys::refcount(s); // don't get bitten by last-use.
assert_eq!(count, count2);
}

View file

@ -163,8 +163,8 @@ pub fn main() {
visit_ty::<i16>(vv);
visit_ty::<~[int]>(vv);
for v.types.iter().advance |&s| {
println(fmt!("type: %s", s));
for v.types.iter().advance |s| {
println(fmt!("type: %s", copy *s));
}
assert_eq!((*v.types).clone(), ~[~"bool", ~"int", ~"i8", ~"i16", ~"[", ~"int", ~"]"]);
}

View file

@ -9,7 +9,7 @@ pub fn main() {
Foo { string: ~"baz" }
];
match x {
[first, ..tail] => {
[ref first, ..tail] => {
assert!(first.string == ~"foo");
assert_eq!(tail.len(), 2);
assert!(tail[0].string == ~"bar");