9
9
// except according to those terms.
10
10
11
11
12
- #![ allow( unknown_features) ]
13
12
#![ feature( box_syntax) ]
14
13
#![ feature( intrinsics) ]
15
- // needed to check for drop fill word.
16
- #![ feature( filling_drop) ]
17
-
18
- use std:: mem:: { self , transmute} ;
19
14
20
15
mod rusti {
21
16
extern "rust-intrinsic" {
@@ -26,12 +21,80 @@ mod rusti {
26
21
27
22
pub fn main ( ) {
28
23
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.
32
56
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
+ }
36
99
}
37
100
}
0 commit comments