Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Regression tests for Issue 25199 (dropck and
Box<Trait + 'a>
).
- Loading branch information
Showing
2 changed files
with
214 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
// 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 <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. | ||
|
||
// Reject mixing cyclic structure and Drop when using trait | ||
// objects to hide the the cross-references. | ||
// | ||
// (Compare against compile-fail/dropck_vec_cycle_checked.rs) | ||
|
||
use std::cell::Cell; | ||
use id::Id; | ||
|
||
mod s { | ||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; | ||
|
||
static S_COUNT: AtomicUsize = ATOMIC_USIZE_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<T:HasId> { | ||
v: T | ||
} | ||
|
||
#[allow(non_snake_case)] | ||
fn CheckId<T:HasId>(t: T) -> CheckId<T> { CheckId{ v: t } } | ||
|
||
impl<T:HasId> Drop for CheckId<T> { | ||
fn drop(&mut self) { | ||
assert!(self.v.count() > 0); | ||
} | ||
} | ||
|
||
trait Obj<'a> : HasId { | ||
fn set0(&self, b: &'a Box<Obj<'a>>); | ||
fn set1(&self, b: &'a Box<Obj<'a>>); | ||
} | ||
|
||
struct O<'a> { | ||
id: Id, | ||
obj0: CheckId<Cell<Option<&'a Box<Obj<'a>>>>>, | ||
obj1: CheckId<Cell<Option<&'a Box<Obj<'a>>>>>, | ||
} | ||
|
||
impl<'a> HasId for O<'a> { | ||
fn count(&self) -> usize { self.id.count() } | ||
} | ||
|
||
impl<'a> O<'a> { | ||
fn new() -> Box<O<'a>> { | ||
Box::new(O { | ||
id: Id::new(), | ||
obj0: CheckId(Cell::new(None)), | ||
obj1: CheckId(Cell::new(None)), | ||
}) | ||
} | ||
} | ||
|
||
impl<'a> HasId for Cell<Option<&'a Box<Obj<'a>>>> { | ||
fn count(&self) -> usize { | ||
match self.get() { | ||
None => 1, | ||
Some(c) => c.count(), | ||
} | ||
} | ||
} | ||
|
||
impl<'a> Obj<'a> for O<'a> { | ||
fn set0(&self, b: &'a Box<Obj<'a>>) { | ||
self.obj0.v.set(Some(b)) | ||
} | ||
fn set1(&self, b: &'a Box<Obj<'a>>) { | ||
self.obj1.v.set(Some(b)) | ||
} | ||
} | ||
|
||
|
||
fn f() { | ||
let (o1, o2, o3): (Box<Obj>, Box<Obj>, Box<Obj>) = (O::new(), O::new(), O::new()); | ||
o1.set0(&o2); //~ ERROR `o2` does not live long enough | ||
o1.set1(&o3); //~ ERROR `o3` does not live long enough | ||
o2.set0(&o2); //~ ERROR `o2` does not live long enough | ||
o2.set1(&o3); //~ ERROR `o3` does not live long enough | ||
o3.set0(&o1); //~ ERROR `o1` does not live long enough | ||
o3.set1(&o2); //~ ERROR `o2` does not live long enough | ||
} | ||
|
||
fn main() { | ||
f(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// 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 <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. | ||
|
||
// Regression test for Issue 25199: Check that one cannot hide a | ||
// destructor's access to borrowed data behind a boxed trait object. | ||
// | ||
// Prior to fixing Issue 25199, this example was able to be compiled | ||
// with rustc, and thus when you ran it, you would see the `Drop` impl | ||
// for `Test` accessing state that had already been dropping (which is | ||
// marked explicitly here with checking code within the `Drop` impl | ||
// for `VecHolder`, but in the general case could just do unsound | ||
// things like accessing memory that has been freed). | ||
// | ||
// Note that I would have liked to encode my go-to example of cyclic | ||
// structure that accesses its neighbors in drop (and thus is | ||
// fundamentally unsound) via this trick, but the closest I was able | ||
// to come was dropck_trait_cycle_checked.rs, which is not quite as | ||
// "good" as this regression test because the encoding of that example | ||
// was forced to attach a lifetime to the trait definition itself | ||
// (`trait Obj<'a>`) while *this* example is solely | ||
|
||
use std::cell::RefCell; | ||
|
||
trait Obj { } | ||
|
||
struct VecHolder { | ||
v: Vec<(bool, &'static str)>, | ||
} | ||
|
||
impl Drop for VecHolder { | ||
fn drop(&mut self) { | ||
println!("Dropping Vec"); | ||
self.v[30].0 = false; | ||
self.v[30].1 = "invalid access: VecHolder dropped already"; | ||
} | ||
} | ||
|
||
struct Container<'a> { | ||
v: VecHolder, | ||
d: RefCell<Vec<Box<Obj+'a>>>, | ||
} | ||
|
||
impl<'a> Container<'a> { | ||
fn new() -> Container<'a> { | ||
Container { | ||
d: RefCell::new(Vec::new()), | ||
v: VecHolder { | ||
v: vec![(true, "valid"); 100] | ||
} | ||
} | ||
} | ||
|
||
fn store<T: Obj+'a>(&'a self, val: T) { | ||
self.d.borrow_mut().push(Box::new(val)); | ||
} | ||
} | ||
|
||
struct Test<'a> { | ||
test: &'a Container<'a>, | ||
} | ||
|
||
impl<'a> Obj for Test<'a> { } | ||
impl<'a> Drop for Test<'a> { | ||
fn drop(&mut self) { | ||
for e in &self.test.v.v { | ||
assert!(e.0, e.1); | ||
} | ||
} | ||
} | ||
|
||
fn main() { | ||
let container = Container::new(); | ||
let test = Test{test: &container}; //~ ERROR `container` does not live long enough | ||
println!("container.v[30]: {:?}", container.v.v[30]); | ||
container.store(test); //~ ERROR `container` does not live long enough | ||
} |