99// except according to those terms.
1010
1111
12- #![ allow( unknown_features) ]
1312#![ feature( box_syntax) ]
1413#![ feature( intrinsics) ]
15- // needed to check for drop fill word.
16- #![ feature( filling_drop) ]
17-
18- use std:: mem:: { self , transmute} ;
1914
2015mod rusti {
2116 extern "rust-intrinsic" {
@@ -26,12 +21,80 @@ mod rusti {
2621
2722pub fn main ( ) {
2823 unsafe {
29- let x: Box < _ > = box 1 ;
30- let mut y = rusti:: init ( ) ;
31- let mut z: * const usize = transmute ( & x) ;
24+ // sanity check
25+ check_drops_state ( 0 , None ) ;
26+
27+ let mut x: Box < D > = box D ( 1 ) ;
28+ assert_eq ! ( x. 0 , 1 ) ;
29+
30+ // A normal overwrite, to demonstrate `check_drops_state`.
31+ x = box D ( 2 ) ;
32+
33+ // At this point, one destructor has run, because the
34+ // overwrite of `x` drops its initial value.
35+ check_drops_state ( 1 , Some ( 1 ) ) ;
36+
37+ let mut y: Box < D > = rusti:: init ( ) ;
38+
39+ // An initial binding does not overwrite anything.
40+ check_drops_state ( 1 , Some ( 1 ) ) ;
41+
42+ // Since `y` has been initialized via the `init` intrinsic, it
43+ // would be unsound to directly overwrite its value via normal
44+ // assignment.
45+ //
46+ // The code currently generated by the compiler is overly
47+ // accepting, however, in that it will check if `y` is itself
48+ // null and thus avoid the unsound action of attempting to
49+ // free null. In other words, if we were to do a normal
50+ // assignment like `y = box D(4);` here, it probably would not
51+ // crash today. But the plan is that it may well crash in the
52+ // future, (I believe).
53+
54+ // `x` is moved here; the manner in which this is tracked by the
55+ // compiler is hidden.
3256 rusti:: move_val_init ( & mut y, x) ;
33- assert_eq ! ( * y, 1 ) ;
34- // `x` is nulled out, not directly visible
35- assert_eq ! ( * z, mem:: POST_DROP_USIZE ) ;
57+
58+ // In particular, it may be tracked via a drop-flag embedded
59+ // in the value, or via a null pointer, or via
60+ // mem::POST_DROP_USIZE, or (most preferably) via a
61+ // stack-local drop flag.
62+ //
63+ // (This test used to build-in knowledge of how it was
64+ // tracked, and check that the underlying stack slot had been
65+ // set to `mem::POST_DROP_USIZE`.)
66+
67+ // But what we *can* observe is how many times the destructor
68+ // for `D` is invoked, and what the last value we saw was
69+ // during such a destructor call. We do so after the end of
70+ // this scope.
71+
72+ assert_eq ! ( y. 0 , 2 ) ;
73+ y. 0 = 3 ;
74+ assert_eq ! ( y. 0 , 3 ) ;
75+
76+ check_drops_state ( 1 , Some ( 1 ) ) ;
77+ }
78+
79+ check_drops_state ( 2 , Some ( 3 ) ) ;
80+ }
81+
82+ static mut NUM_DROPS : i32 = 0 ;
83+ static mut LAST_DROPPED : Option < i32 > = None ;
84+
85+ fn check_drops_state ( num_drops : i32 , last_dropped : Option < i32 > ) {
86+ unsafe {
87+ assert_eq ! ( NUM_DROPS , num_drops) ;
88+ assert_eq ! ( LAST_DROPPED , last_dropped) ;
89+ }
90+ }
91+
92+ struct D ( i32 ) ;
93+ impl Drop for D {
94+ fn drop ( & mut self ) {
95+ unsafe {
96+ NUM_DROPS += 1 ;
97+ LAST_DROPPED = Some ( self . 0 ) ;
98+ }
3699 }
37100}
0 commit comments