Skip to content

Commit c1cda07

Browse files
committed
compile-fail tests.
Some compile-fail tests illustrated cases to be rejected by dropck, including ones that check cyclic data cases designed to exposed bugs if they are actually tricked into running by an unsound analysis. E.g. these exposed bugs in earlier broken ways of handling `Vec<T>`. (Note that all the uses of `unsafe_destructor` are just placating the simple analysis used for that feature, which will eventually go away once we have put the dropck through its paces.)
1 parent 4459a43 commit c1cda07

8 files changed

+663
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Tests the new destructor semantics.
12+
13+
use std::cell::RefCell;
14+
15+
fn main() {
16+
let b = {
17+
let a = Box::new(RefCell::new(4i8));
18+
*a.borrow() + 1i8 //~ ERROR `*a` does not live long enough
19+
};
20+
println!("{}", b);
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Reject mixing cyclic structure and Drop when using fixed length
12+
// arrays.
13+
//
14+
// (Compare against compile-fail/dropck_vec_cycle_checked.rs)
15+
16+
#![feature(unsafe_destructor)]
17+
18+
use std::cell::Cell;
19+
use id::Id;
20+
21+
mod s {
22+
#![allow(unstable)]
23+
use std::sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
24+
25+
static S_COUNT: AtomicUint = ATOMIC_UINT_INIT;
26+
27+
pub fn next_count() -> usize {
28+
S_COUNT.fetch_add(1, Ordering::SeqCst) + 1
29+
}
30+
}
31+
32+
mod id {
33+
use s;
34+
#[derive(Debug)]
35+
pub struct Id {
36+
orig_count: usize,
37+
count: usize,
38+
}
39+
40+
impl Id {
41+
pub fn new() -> Id {
42+
let c = s::next_count();
43+
println!("building Id {}", c);
44+
Id { orig_count: c, count: c }
45+
}
46+
pub fn count(&self) -> usize {
47+
println!("Id::count on {} returns {}", self.orig_count, self.count);
48+
self.count
49+
}
50+
}
51+
52+
impl Drop for Id {
53+
fn drop(&mut self) {
54+
println!("dropping Id {}", self.count);
55+
self.count = 0;
56+
}
57+
}
58+
}
59+
60+
trait HasId {
61+
fn count(&self) -> usize;
62+
}
63+
64+
#[derive(Debug)]
65+
struct CheckId<T:HasId> {
66+
v: T
67+
}
68+
69+
#[allow(non_snake_case)]
70+
fn CheckId<T:HasId>(t: T) -> CheckId<T> { CheckId{ v: t } }
71+
72+
#[unsafe_destructor]
73+
impl<T:HasId> Drop for CheckId<T> {
74+
fn drop(&mut self) {
75+
assert!(self.v.count() > 0);
76+
}
77+
}
78+
79+
#[derive(Debug)]
80+
struct B<'a> {
81+
id: Id,
82+
a: [CheckId<Cell<Option<&'a B<'a>>>>; 2]
83+
}
84+
85+
impl<'a> HasId for Cell<Option<&'a B<'a>>> {
86+
fn count(&self) -> usize {
87+
match self.get() {
88+
None => 1,
89+
Some(b) => b.id.count(),
90+
}
91+
}
92+
}
93+
94+
impl<'a> B<'a> {
95+
fn new() -> B<'a> {
96+
B { id: Id::new(), a: [CheckId(Cell::new(None)), CheckId(Cell::new(None))] }
97+
}
98+
}
99+
100+
fn f() {
101+
let (b1, b2, b3);
102+
b1 = B::new();
103+
b2 = B::new();
104+
b3 = B::new();
105+
b1.a[0].v.set(Some(&b2)); //~ ERROR `b2` does not live long enough
106+
b1.a[1].v.set(Some(&b3)); //~ ERROR `b3` does not live long enough
107+
b2.a[0].v.set(Some(&b2)); //~ ERROR `b2` does not live long enough
108+
b2.a[1].v.set(Some(&b3)); //~ ERROR `b3` does not live long enough
109+
b3.a[0].v.set(Some(&b1)); //~ ERROR `b1` does not live long enough
110+
b3.a[1].v.set(Some(&b2)); //~ ERROR `b2` does not live long enough
111+
}
112+
113+
fn main() {
114+
f();
115+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// A simple example of an unsound mixing of cyclic structure and Drop.
12+
//
13+
// Each `D` has a name and an optional reference to another `D`
14+
// sibling, but also implements a drop method that prints out its own
15+
// name as well as the name of its sibling.
16+
//
17+
// By setting up a cyclic structure, the drop code cannot possibly
18+
// work. Therefore this code must be rejected.
19+
//
20+
// (As it turns out, essentially any attempt to install a sibling here
21+
// will be rejected, regardless of whether it forms a cyclic
22+
// structure or not. This is because the use of the same lifetime
23+
// `'a` in `&'a D<'a>` cannot be satisfied when `D<'a>` implements
24+
// `Drop`.)
25+
26+
#![feature(unsafe_destructor)]
27+
28+
use std::cell::Cell;
29+
30+
struct D<'a> {
31+
name: String,
32+
p: Cell<Option<&'a D<'a>>>,
33+
}
34+
35+
impl<'a> D<'a> {
36+
fn new(name: String) -> D<'a> { D { name: name, p: Cell::new(None) } }
37+
}
38+
39+
#[unsafe_destructor]
40+
impl<'a> Drop for D<'a> {
41+
fn drop(&mut self) {
42+
println!("dropping {} whose sibling is {:?}",
43+
self.name, self.p.get().map(|d| &d.name));
44+
}
45+
}
46+
47+
fn g() {
48+
let (d1, d2) = (D::new(format!("d1")), D::new(format!("d2")));
49+
d1.p.set(Some(&d2)); //~ ERROR `d2` does not live long enough
50+
d2.p.set(Some(&d1)); //~ ERROR `d1` does not live long enough
51+
}
52+
53+
fn main() {
54+
g();
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Reject mixing cyclic structure and Drop when using TypedArena.
12+
//
13+
// (Compare against compile-fail/dropck_vec_cycle_checked.rs)
14+
//
15+
// (Also compare against compile-fail/dropck_tarena_unsound_drop.rs,
16+
// which is a reduction of this code to more directly show the reason
17+
// for the error message we see here.)
18+
19+
#![allow(unstable)]
20+
#![feature(unsafe_destructor)]
21+
22+
extern crate arena;
23+
24+
use arena::TypedArena;
25+
use std::cell::Cell;
26+
use id::Id;
27+
28+
mod s {
29+
#![allow(unstable)]
30+
use std::sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
31+
32+
static S_COUNT: AtomicUint = ATOMIC_UINT_INIT;
33+
34+
pub fn next_count() -> usize {
35+
S_COUNT.fetch_add(1, Ordering::SeqCst) + 1
36+
}
37+
}
38+
39+
mod id {
40+
use s;
41+
#[derive(Debug)]
42+
pub struct Id {
43+
orig_count: usize,
44+
count: usize,
45+
}
46+
47+
impl Id {
48+
pub fn new() -> Id {
49+
let c = s::next_count();
50+
println!("building Id {}", c);
51+
Id { orig_count: c, count: c }
52+
}
53+
pub fn count(&self) -> usize {
54+
println!("Id::count on {} returns {}", self.orig_count, self.count);
55+
self.count
56+
}
57+
}
58+
59+
impl Drop for Id {
60+
fn drop(&mut self) {
61+
println!("dropping Id {}", self.count);
62+
self.count = 0;
63+
}
64+
}
65+
}
66+
67+
trait HasId {
68+
fn count(&self) -> usize;
69+
}
70+
71+
#[derive(Debug)]
72+
struct CheckId<T:HasId> {
73+
v: T
74+
}
75+
76+
#[allow(non_snake_case)]
77+
fn CheckId<T:HasId>(t: T) -> CheckId<T> { CheckId{ v: t } }
78+
79+
#[unsafe_destructor]
80+
impl<T:HasId> Drop for CheckId<T> {
81+
fn drop(&mut self) {
82+
assert!(self.v.count() > 0);
83+
}
84+
}
85+
86+
#[derive(Debug)]
87+
struct C<'a> {
88+
id: Id,
89+
v: Vec<CheckId<Cell<Option<&'a C<'a>>>>>,
90+
}
91+
92+
impl<'a> HasId for Cell<Option<&'a C<'a>>> {
93+
fn count(&self) -> usize {
94+
match self.get() {
95+
None => 1,
96+
Some(c) => c.id.count(),
97+
}
98+
}
99+
}
100+
101+
impl<'a> C<'a> {
102+
fn new() -> C<'a> {
103+
C { id: Id::new(), v: Vec::new() }
104+
}
105+
}
106+
107+
fn f<'a>(arena: &'a TypedArena<C<'a>>) {
108+
let c1 = arena.alloc(C::new());
109+
let c2 = arena.alloc(C::new());
110+
let c3 = arena.alloc(C::new());
111+
112+
c1.v.push(CheckId(Cell::new(None)));
113+
c1.v.push(CheckId(Cell::new(None)));
114+
c2.v.push(CheckId(Cell::new(None)));
115+
c2.v.push(CheckId(Cell::new(None)));
116+
c3.v.push(CheckId(Cell::new(None)));
117+
c3.v.push(CheckId(Cell::new(None)));
118+
119+
c1.v[0].v.set(Some(c2));
120+
c1.v[1].v.set(Some(c3));
121+
c2.v[0].v.set(Some(c2));
122+
c2.v[1].v.set(Some(c3));
123+
c3.v[0].v.set(Some(c1));
124+
c3.v[1].v.set(Some(c2));
125+
}
126+
127+
fn main() {
128+
let arena = TypedArena::new();
129+
f(&arena); //~ ERROR `arena` does not live long enough
130+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that a arena (TypedArena) cannot carry elements whose drop
12+
// methods might access borrowed data of lifetime that does not
13+
// strictly outlive the arena itself.
14+
//
15+
// Compare against run-pass/dropck_tarena_sound_drop.rs, which shows a
16+
// similar setup, but loosens `f` so that the struct `C<'a>` can be
17+
// fed a lifetime longer than that of the arena.
18+
//
19+
// (Also compare against dropck_tarena_cycle_checked.rs, from which
20+
// this was reduced to better understand its error message.)
21+
22+
#![allow(unstable)]
23+
#![feature(unsafe_destructor)]
24+
25+
extern crate arena;
26+
27+
use arena::TypedArena;
28+
29+
trait HasId { fn count(&self) -> usize; }
30+
31+
struct CheckId<T:HasId> { v: T }
32+
33+
// In the code below, the impl of HasId for `&'a usize` does not
34+
// actually access the borrowed data, but the point is that the
35+
// interface to CheckId does not (and cannot) know that, and therefore
36+
// when encountering the a value V of type CheckId<S>, we must
37+
// conservatively force the type S to strictly outlive V.
38+
#[unsafe_destructor]
39+
impl<T:HasId> Drop for CheckId<T> {
40+
fn drop(&mut self) {
41+
assert!(self.v.count() > 0);
42+
}
43+
}
44+
45+
struct C<'a> { v: CheckId<&'a usize>, }
46+
47+
impl<'a> HasId for &'a usize { fn count(&self) -> usize { 1 } }
48+
49+
fn f<'a>(_arena: &'a TypedArena<C<'a>>) {}
50+
51+
fn main() {
52+
let arena: TypedArena<C> = TypedArena::new();
53+
f(&arena); //~ ERROR `arena` does not live long enough
54+
}

0 commit comments

Comments
 (0)