101 lines
3.1 KiB
Rust
101 lines
3.1 KiB
Rust
// 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.
|
|
|
|
|
|
#![feature(box_syntax)]
|
|
#![feature(intrinsics)]
|
|
|
|
mod rusti {
|
|
extern "rust-intrinsic" {
|
|
pub fn init<T>() -> T;
|
|
pub fn move_val_init<T>(dst: *mut T, src: T);
|
|
}
|
|
}
|
|
|
|
pub fn main() {
|
|
unsafe {
|
|
// sanity check
|
|
check_drops_state(0, None);
|
|
|
|
let mut x: Box<D> = box D(1);
|
|
assert_eq!(x.0, 1);
|
|
|
|
// A normal overwrite, to demonstrate `check_drops_state`.
|
|
x = box D(2);
|
|
|
|
// At this point, one destructor has run, because the
|
|
// overwrite of `x` drops its initial value.
|
|
check_drops_state(1, Some(1));
|
|
|
|
let mut y: Box<D> = rusti::init();
|
|
|
|
// An initial binding does not overwrite anything.
|
|
check_drops_state(1, Some(1));
|
|
|
|
// Since `y` has been initialized via the `init` intrinsic, it
|
|
// would be unsound to directly overwrite its value via normal
|
|
// assignment.
|
|
//
|
|
// The code currently generated by the compiler is overly
|
|
// accepting, however, in that it will check if `y` is itself
|
|
// null and thus avoid the unsound action of attempting to
|
|
// free null. In other words, if we were to do a normal
|
|
// assignment like `y = box D(4);` here, it probably would not
|
|
// crash today. But the plan is that it may well crash in the
|
|
// future, (I believe).
|
|
|
|
// `x` is moved here; the manner in which this is tracked by the
|
|
// compiler is hidden.
|
|
rusti::move_val_init(&mut y, x);
|
|
|
|
// In particular, it may be tracked via a drop-flag embedded
|
|
// in the value, or via a null pointer, or via
|
|
// mem::POST_DROP_USIZE, or (most preferably) via a
|
|
// stack-local drop flag.
|
|
//
|
|
// (This test used to build-in knowledge of how it was
|
|
// tracked, and check that the underlying stack slot had been
|
|
// set to `mem::POST_DROP_USIZE`.)
|
|
|
|
// But what we *can* observe is how many times the destructor
|
|
// for `D` is invoked, and what the last value we saw was
|
|
// during such a destructor call. We do so after the end of
|
|
// this scope.
|
|
|
|
assert_eq!(y.0, 2);
|
|
y.0 = 3;
|
|
assert_eq!(y.0, 3);
|
|
|
|
check_drops_state(1, Some(1));
|
|
}
|
|
|
|
check_drops_state(2, Some(3));
|
|
}
|
|
|
|
static mut NUM_DROPS: i32 = 0;
|
|
static mut LAST_DROPPED: Option<i32> = None;
|
|
|
|
fn check_drops_state(num_drops: i32, last_dropped: Option<i32>) {
|
|
unsafe {
|
|
assert_eq!(NUM_DROPS, num_drops);
|
|
assert_eq!(LAST_DROPPED, last_dropped);
|
|
}
|
|
}
|
|
|
|
struct D(i32);
|
|
impl Drop for D {
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
NUM_DROPS += 1;
|
|
LAST_DROPPED = Some(self.0);
|
|
}
|
|
}
|
|
}
|