diff --git a/src/test/compile-fail/destructor-restrictions.rs b/src/test/compile-fail/destructor-restrictions.rs new file mode 100644 index 00000000000..0836cd1695d --- /dev/null +++ b/src/test/compile-fail/destructor-restrictions.rs @@ -0,0 +1,21 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests the new destructor semantics. + +use std::cell::RefCell; + +fn main() { + let b = { + let a = Box::new(RefCell::new(4i8)); + *a.borrow() + 1i8 //~ ERROR `*a` does not live long enough + }; + println!("{}", b); +} diff --git a/src/test/compile-fail/dropck_arr_cycle_checked.rs b/src/test/compile-fail/dropck_arr_cycle_checked.rs new file mode 100644 index 00000000000..3aa2fae2826 --- /dev/null +++ b/src/test/compile-fail/dropck_arr_cycle_checked.rs @@ -0,0 +1,115 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Reject mixing cyclic structure and Drop when using fixed length +// arrays. +// +// (Compare against compile-fail/dropck_vec_cycle_checked.rs) + +#![feature(unsafe_destructor)] + +use std::cell::Cell; +use id::Id; + +mod s { + #![allow(unstable)] + use std::sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering}; + + static S_COUNT: AtomicUint = ATOMIC_UINT_INIT; + + pub fn next_count() -> usize { + S_COUNT.fetch_add(1, Ordering::SeqCst) + 1 + } +} + +mod id { + use s; + #[derive(Debug)] + pub struct Id { + orig_count: usize, + count: usize, + } + + impl Id { + pub fn new() -> Id { + let c = s::next_count(); + println!("building Id {}", c); + Id { orig_count: c, count: c } + } + pub fn count(&self) -> usize { + println!("Id::count on {} returns {}", self.orig_count, self.count); + self.count + } + } + + impl Drop for Id { + fn drop(&mut self) { + println!("dropping Id {}", self.count); + self.count = 0; + } + } +} + +trait HasId { + fn count(&self) -> usize; +} + +#[derive(Debug)] +struct CheckId { + v: T +} + +#[allow(non_snake_case)] +fn CheckId(t: T) -> CheckId { CheckId{ v: t } } + +#[unsafe_destructor] +impl Drop for CheckId { + fn drop(&mut self) { + assert!(self.v.count() > 0); + } +} + +#[derive(Debug)] +struct B<'a> { + id: Id, + a: [CheckId>>>; 2] +} + +impl<'a> HasId for Cell>> { + fn count(&self) -> usize { + match self.get() { + None => 1, + Some(b) => b.id.count(), + } + } +} + +impl<'a> B<'a> { + fn new() -> B<'a> { + B { id: Id::new(), a: [CheckId(Cell::new(None)), CheckId(Cell::new(None))] } + } +} + +fn f() { + let (b1, b2, b3); + b1 = B::new(); + b2 = B::new(); + b3 = B::new(); + b1.a[0].v.set(Some(&b2)); //~ ERROR `b2` does not live long enough + b1.a[1].v.set(Some(&b3)); //~ ERROR `b3` does not live long enough + b2.a[0].v.set(Some(&b2)); //~ ERROR `b2` does not live long enough + b2.a[1].v.set(Some(&b3)); //~ ERROR `b3` does not live long enough + b3.a[0].v.set(Some(&b1)); //~ ERROR `b1` does not live long enough + b3.a[1].v.set(Some(&b2)); //~ ERROR `b2` does not live long enough +} + +fn main() { + f(); +} diff --git a/src/test/compile-fail/dropck_direct_cycle_with_drop.rs b/src/test/compile-fail/dropck_direct_cycle_with_drop.rs new file mode 100644 index 00000000000..b9df71322ad --- /dev/null +++ b/src/test/compile-fail/dropck_direct_cycle_with_drop.rs @@ -0,0 +1,55 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// A simple example of an unsound mixing of cyclic structure and Drop. +// +// Each `D` has a name and an optional reference to another `D` +// sibling, but also implements a drop method that prints out its own +// name as well as the name of its sibling. +// +// By setting up a cyclic structure, the drop code cannot possibly +// work. Therefore this code must be rejected. +// +// (As it turns out, essentially any attempt to install a sibling here +// will be rejected, regardless of whether it forms a cyclic +// structure or not. This is because the use of the same lifetime +// `'a` in `&'a D<'a>` cannot be satisfied when `D<'a>` implements +// `Drop`.) + +#![feature(unsafe_destructor)] + +use std::cell::Cell; + +struct D<'a> { + name: String, + p: Cell>>, +} + +impl<'a> D<'a> { + fn new(name: String) -> D<'a> { D { name: name, p: Cell::new(None) } } +} + +#[unsafe_destructor] +impl<'a> Drop for D<'a> { + fn drop(&mut self) { + println!("dropping {} whose sibling is {:?}", + self.name, self.p.get().map(|d| &d.name)); + } +} + +fn g() { + let (d1, d2) = (D::new(format!("d1")), D::new(format!("d2"))); + d1.p.set(Some(&d2)); //~ ERROR `d2` does not live long enough + d2.p.set(Some(&d1)); //~ ERROR `d1` does not live long enough +} + +fn main() { + g(); +} diff --git a/src/test/compile-fail/dropck_tarena_cycle_checked.rs b/src/test/compile-fail/dropck_tarena_cycle_checked.rs new file mode 100644 index 00000000000..74e3c724b67 --- /dev/null +++ b/src/test/compile-fail/dropck_tarena_cycle_checked.rs @@ -0,0 +1,130 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Reject mixing cyclic structure and Drop when using TypedArena. +// +// (Compare against compile-fail/dropck_vec_cycle_checked.rs) +// +// (Also compare against compile-fail/dropck_tarena_unsound_drop.rs, +// which is a reduction of this code to more directly show the reason +// for the error message we see here.) + +#![allow(unstable)] +#![feature(unsafe_destructor)] + +extern crate arena; + +use arena::TypedArena; +use std::cell::Cell; +use id::Id; + +mod s { + #![allow(unstable)] + use std::sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering}; + + static S_COUNT: AtomicUint = ATOMIC_UINT_INIT; + + pub fn next_count() -> usize { + S_COUNT.fetch_add(1, Ordering::SeqCst) + 1 + } +} + +mod id { + use s; + #[derive(Debug)] + pub struct Id { + orig_count: usize, + count: usize, + } + + impl Id { + pub fn new() -> Id { + let c = s::next_count(); + println!("building Id {}", c); + Id { orig_count: c, count: c } + } + pub fn count(&self) -> usize { + println!("Id::count on {} returns {}", self.orig_count, self.count); + self.count + } + } + + impl Drop for Id { + fn drop(&mut self) { + println!("dropping Id {}", self.count); + self.count = 0; + } + } +} + +trait HasId { + fn count(&self) -> usize; +} + +#[derive(Debug)] +struct CheckId { + v: T +} + +#[allow(non_snake_case)] +fn CheckId(t: T) -> CheckId { CheckId{ v: t } } + +#[unsafe_destructor] +impl Drop for CheckId { + fn drop(&mut self) { + assert!(self.v.count() > 0); + } +} + +#[derive(Debug)] +struct C<'a> { + id: Id, + v: Vec>>>>, +} + +impl<'a> HasId for Cell>> { + fn count(&self) -> usize { + match self.get() { + None => 1, + Some(c) => c.id.count(), + } + } +} + +impl<'a> C<'a> { + fn new() -> C<'a> { + C { id: Id::new(), v: Vec::new() } + } +} + +fn f<'a>(arena: &'a TypedArena>) { + let c1 = arena.alloc(C::new()); + let c2 = arena.alloc(C::new()); + let c3 = arena.alloc(C::new()); + + c1.v.push(CheckId(Cell::new(None))); + c1.v.push(CheckId(Cell::new(None))); + c2.v.push(CheckId(Cell::new(None))); + c2.v.push(CheckId(Cell::new(None))); + c3.v.push(CheckId(Cell::new(None))); + c3.v.push(CheckId(Cell::new(None))); + + c1.v[0].v.set(Some(c2)); + c1.v[1].v.set(Some(c3)); + c2.v[0].v.set(Some(c2)); + c2.v[1].v.set(Some(c3)); + c3.v[0].v.set(Some(c1)); + c3.v[1].v.set(Some(c2)); +} + +fn main() { + let arena = TypedArena::new(); + f(&arena); //~ ERROR `arena` does not live long enough +} diff --git a/src/test/compile-fail/dropck_tarena_unsound_drop.rs b/src/test/compile-fail/dropck_tarena_unsound_drop.rs new file mode 100644 index 00000000000..64d77e97fa7 --- /dev/null +++ b/src/test/compile-fail/dropck_tarena_unsound_drop.rs @@ -0,0 +1,54 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that a arena (TypedArena) cannot carry elements whose drop +// methods might access borrowed data of lifetime that does not +// strictly outlive the arena itself. +// +// Compare against run-pass/dropck_tarena_sound_drop.rs, which shows a +// similar setup, but loosens `f` so that the struct `C<'a>` can be +// fed a lifetime longer than that of the arena. +// +// (Also compare against dropck_tarena_cycle_checked.rs, from which +// this was reduced to better understand its error message.) + +#![allow(unstable)] +#![feature(unsafe_destructor)] + +extern crate arena; + +use arena::TypedArena; + +trait HasId { fn count(&self) -> usize; } + +struct CheckId { v: T } + +// In the code below, the impl of HasId for `&'a usize` does not +// actually access the borrowed data, but the point is that the +// interface to CheckId does not (and cannot) know that, and therefore +// when encountering the a value V of type CheckId, we must +// conservatively force the type S to strictly outlive V. +#[unsafe_destructor] +impl Drop for CheckId { + fn drop(&mut self) { + assert!(self.v.count() > 0); + } +} + +struct C<'a> { v: CheckId<&'a usize>, } + +impl<'a> HasId for &'a usize { fn count(&self) -> usize { 1 } } + +fn f<'a>(_arena: &'a TypedArena>) {} + +fn main() { + let arena: TypedArena = TypedArena::new(); + f(&arena); //~ ERROR `arena` does not live long enough +} diff --git a/src/test/compile-fail/dropck_vec_cycle_checked.rs b/src/test/compile-fail/dropck_vec_cycle_checked.rs new file mode 100644 index 00000000000..3f69c7d1a9c --- /dev/null +++ b/src/test/compile-fail/dropck_vec_cycle_checked.rs @@ -0,0 +1,122 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Reject mixing cyclic structure and Drop when using Vec. +// +// (Compare against compile-fail/dropck_arr_cycle_checked.rs) + +#![feature(unsafe_destructor)] + +use std::cell::Cell; +use id::Id; + +mod s { + #![allow(unstable)] + use std::sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering}; + + static S_COUNT: AtomicUint = ATOMIC_UINT_INIT; + + pub fn next_count() -> usize { + S_COUNT.fetch_add(1, Ordering::SeqCst) + 1 + } +} + +mod id { + use s; + #[derive(Debug)] + pub struct Id { + orig_count: usize, + count: usize, + } + + impl Id { + pub fn new() -> Id { + let c = s::next_count(); + println!("building Id {}", c); + Id { orig_count: c, count: c } + } + pub fn count(&self) -> usize { + println!("Id::count on {} returns {}", self.orig_count, self.count); + self.count + } + } + + impl Drop for Id { + fn drop(&mut self) { + println!("dropping Id {}", self.count); + self.count = 0; + } + } +} + +trait HasId { + fn count(&self) -> usize; +} + +#[derive(Debug)] +struct CheckId { + v: T +} + +#[allow(non_snake_case)] +fn CheckId(t: T) -> CheckId { CheckId{ v: t } } + +#[unsafe_destructor] +impl Drop for CheckId { + fn drop(&mut self) { + assert!(self.v.count() > 0); + } +} + +#[derive(Debug)] +struct C<'a> { + id: Id, + v: Vec>>>>, +} + +impl<'a> HasId for Cell>> { + fn count(&self) -> usize { + match self.get() { + None => 1, + Some(c) => c.id.count(), + } + } +} + +impl<'a> C<'a> { + fn new() -> C<'a> { + C { id: Id::new(), v: Vec::new() } + } +} + +fn f() { + let (mut c1, mut c2, mut c3); + c1 = C::new(); + c2 = C::new(); + c3 = C::new(); + + c1.v.push(CheckId(Cell::new(None))); + c1.v.push(CheckId(Cell::new(None))); + c2.v.push(CheckId(Cell::new(None))); + c2.v.push(CheckId(Cell::new(None))); + c3.v.push(CheckId(Cell::new(None))); + c3.v.push(CheckId(Cell::new(None))); + + c1.v[0].v.set(Some(&c2)); //~ ERROR `c2` does not live long enough + c1.v[1].v.set(Some(&c3)); //~ ERROR `c3` does not live long enough + c2.v[0].v.set(Some(&c2)); //~ ERROR `c2` does not live long enough + c2.v[1].v.set(Some(&c3)); //~ ERROR `c3` does not live long enough + c3.v[0].v.set(Some(&c1)); //~ ERROR `c1` does not live long enough + c3.v[1].v.set(Some(&c2)); //~ ERROR `c2` does not live long enough +} + +fn main() { + f(); +} diff --git a/src/test/compile-fail/vec-must-not-hide-type-from-dropck.rs b/src/test/compile-fail/vec-must-not-hide-type-from-dropck.rs new file mode 100644 index 00000000000..6aaf51278af --- /dev/null +++ b/src/test/compile-fail/vec-must-not-hide-type-from-dropck.rs @@ -0,0 +1,135 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Checking that `Vec` cannot hide lifetimes within `T` when `T` +// implements `Drop` and might access methods of values that have +// since been deallocated. +// +// In this case, the values in question hold (non-zero) unique-ids +// that zero themselves out when dropped, and are wrapped in another +// type with a destructor that asserts that the ids it references are +// indeed non-zero (i.e., effectively checking that the id's are not +// dropped while there are still any outstanding references). +// +// However, the values in question are also formed into a +// cyclic-structure, ensuring that there is no way for all of the +// conditions above to be satisfied, meaning that if the dropck is +// sound, it should reject this code. + +#![feature(unsafe_destructor)] + +use std::cell::Cell; +use id::Id; + +mod s { + #![allow(unstable)] + use std::sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering}; + + static S_COUNT: AtomicUint = ATOMIC_UINT_INIT; + + /// generates globally unique count (global across the current + /// process, that is) + pub fn next_count() -> usize { + S_COUNT.fetch_add(1, Ordering::SeqCst) + 1 + } +} + +mod id { + use s; + + /// Id represents a globally unique identifier (global across the + /// current process, that is). When dropped, it automatically + /// clears its `count` field, but leaves `orig_count` untouched, + /// so that if there are subsequent (erroneous) invocations of its + /// method (which is unsound), we can observe it by seeing that + /// the `count` is 0 while the `orig_count` is non-zero. + #[derive(Debug)] + pub struct Id { + orig_count: usize, + count: usize, + } + + impl Id { + /// Creates an `Id` with a globally unique count. + pub fn new() -> Id { + let c = s::next_count(); + println!("building Id {}", c); + Id { orig_count: c, count: c } + } + /// returns the `count` of self; should be non-zero if + /// everything is working. + pub fn count(&self) -> usize { + println!("Id::count on {} returns {}", self.orig_count, self.count); + self.count + } + } + + impl Drop for Id { + fn drop(&mut self) { + println!("dropping Id {}", self.count); + self.count = 0; + } + } +} + +trait HasId { + fn count(&self) -> usize; +} + +#[derive(Debug)] +struct CheckId { + v: T +} + +#[allow(non_snake_case)] +fn CheckId(t: T) -> CheckId { CheckId{ v: t } } + +#[unsafe_destructor] +impl Drop for CheckId { + fn drop(&mut self) { + assert!(self.v.count() > 0); + } +} + +#[derive(Debug)] +struct C<'a> { + id: Id, + v: Vec>>>>, +} + +impl<'a> HasId for Cell>> { + fn count(&self) -> usize { + match self.get() { + None => 1, + Some(c) => c.id.count(), + } + } +} + +impl<'a> C<'a> { + fn new() -> C<'a> { + C { id: Id::new(), v: Vec::new() } + } +} + +fn f() { + let (mut c1, mut c2); + c1 = C::new(); + c2 = C::new(); + + c1.v.push(CheckId(Cell::new(None))); + c2.v.push(CheckId(Cell::new(None))); + c1.v[0].v.set(Some(&c2)); //~ ERROR `c2` does not live long enough + c2.v[0].v.set(Some(&c1)); //~ ERROR `c1` does not live long enough +} + +fn main() { + f(); +} diff --git a/src/test/compile-fail/vec_refs_data_with_early_death.rs b/src/test/compile-fail/vec_refs_data_with_early_death.rs new file mode 100644 index 00000000000..a191b3e56c4 --- /dev/null +++ b/src/test/compile-fail/vec_refs_data_with_early_death.rs @@ -0,0 +1,31 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test is a simple example of code that violates the dropck +// rules: it pushes `&x` and `&y` into `v`, but the referenced data +// will be dropped before the vector itself is. + +// (In principle we know that `Vec` does not reference the data it +// owns from within its drop code, apart from calling drop on each +// element it owns; thus, for data like this, it seems like we could +// loosen the restrictions here if we wanted. But it also is not +// clear whether such loosening is terribly important.) + +fn main() { + let mut v = Vec::new(); + + let x: i8 = 3; + let y: i8 = 4; + + v.push(&x); //~ ERROR `x` does not live long enough + v.push(&y); //~ ERROR `y` does not live long enough + + assert_eq!(v.as_slice(), [&3, &4]); +}