]> git.proxmox.com Git - rustc.git/blob - vendor/nodrop/src/lib.rs
New upstream version 1.42.0+dfsg0+pve1
[rustc.git] / vendor / nodrop / src / lib.rs
1 //!
2 //! The **nodrop** crate has the following cargo feature flags:
3 //!
4 //! - `std`
5 //! - Optional, enabled by default
6 //! - Use libstd
7 //! - `use_needs_drop`
8 //! - Optional
9 //! - Requires Rust 1.21
10 //! - Use `needs_drop` to skip overwriting if not necessary
11 //! - `use_union`
12 //! - Optional
13 //! - Requires nightly channel
14 //! - Using untagged union, finally we have an implementation of `NoDrop` without hacks,
15 //! for example the fact that `NoDrop<T>` never has a destructor anymore.
16 //!
17
18 #![cfg_attr(not(any(test, feature="std")), no_std)]
19 #[cfg(not(any(test, feature="std")))]
20 extern crate core as std;
21
22 #[cfg(feature = "use_union")]
23 extern crate nodrop_union as imp;
24
25 pub use imp::NoDrop;
26
27
28 #[cfg(not(feature = "use_union"))]
29 mod imp {
30 use std::ptr;
31 use std::mem;
32 use std::ops::{Deref, DerefMut};
33
34 /// repr(u8) - Make sure the non-nullable pointer optimization does not occur!
35 #[repr(u8)]
36 enum Flag<T> {
37 Alive(T),
38 // Dummy u8 field below, again to beat the enum layout opt
39 Dropped(u8),
40 }
41
42
43 /// A type holding **T** that will not call its destructor on drop
44 pub struct NoDrop<T>(Flag<T>);
45
46 impl<T> NoDrop<T> {
47 /// Create a new **NoDrop**.
48 #[inline]
49 pub fn new(value: T) -> NoDrop<T> {
50 NoDrop(Flag::Alive(value))
51 }
52
53 /// Extract the inner value.
54 ///
55 /// Once extracted, the value can of course drop again.
56 #[inline]
57 pub fn into_inner(mut self) -> T {
58 let inner = unsafe {
59 ptr::read(&mut *self)
60 };
61 // skip Drop, so we don't even have to overwrite
62 mem::forget(self);
63 inner
64 }
65 }
66
67 #[cfg(not(feature = "use_needs_drop"))]
68 #[inline]
69 fn needs_drop<T>() -> bool {
70 true
71 }
72
73 #[cfg(feature = "use_needs_drop")]
74 #[inline]
75 fn needs_drop<T>() -> bool {
76 unsafe {
77 ::std::mem::needs_drop::<T>()
78 }
79 }
80
81 impl<T> Drop for NoDrop<T> {
82 fn drop(&mut self) {
83 if needs_drop::<T>() {
84 // inhibit drop
85 unsafe {
86 ptr::write(&mut self.0, Flag::Dropped(0));
87 }
88 }
89 }
90 }
91
92 impl<T> Deref for NoDrop<T> {
93 type Target = T;
94
95 // Use type invariant, always Flag::Alive.
96 #[inline]
97 fn deref(&self) -> &T {
98 match self.0 {
99 Flag::Alive(ref inner) => inner,
100 _ => unsafe { debug_assert_unreachable() }
101 }
102 }
103 }
104
105 impl<T> DerefMut for NoDrop<T> {
106 // Use type invariant, always Flag::Alive.
107 #[inline]
108 fn deref_mut(&mut self) -> &mut T {
109 match self.0 {
110 Flag::Alive(ref mut inner) => inner,
111 _ => unsafe { debug_assert_unreachable() }
112 }
113 }
114 }
115
116 #[cfg(test)]
117 #[test]
118 fn test_no_nonnullable_opt() {
119 // Make sure `Flag` does not apply the non-nullable pointer optimization
120 // as Option would do.
121 assert!(mem::size_of::<Flag<&i32>>() > mem::size_of::<&i32>());
122 assert!(mem::size_of::<Flag<Vec<i32>>>() > mem::size_of::<Vec<i32>>());
123 }
124
125 // copying this code saves us microcrate deps
126 #[inline]
127 unsafe fn debug_assert_unreachable() -> ! {
128 debug_assert!(false, "Reached unreachable section: this is a bug!");
129 enum Void { }
130 match *(1 as *const Void) { }
131 }
132 }
133
134 #[cfg(test)]
135 mod tests {
136 use super::NoDrop;
137
138 #[test]
139 fn test_drop() {
140 use std::cell::Cell;
141
142 let flag = &Cell::new(0);
143
144 struct Bump<'a>(&'a Cell<i32>);
145
146 impl<'a> Drop for Bump<'a> {
147 fn drop(&mut self) {
148 let n = self.0.get();
149 self.0.set(n + 1);
150 }
151 }
152
153 {
154 let _ = NoDrop::new([Bump(flag), Bump(flag)]);
155 }
156 assert_eq!(flag.get(), 0);
157
158 // test something with the nullable pointer optimization
159 flag.set(0);
160
161 {
162 let mut array = NoDrop::new(Vec::new());
163 array.push(vec![Bump(flag)]);
164 array.push(vec![Bump(flag), Bump(flag)]);
165 array.push(vec![]);
166 array.push(vec![Bump(flag)]);
167 drop(array.pop());
168 assert_eq!(flag.get(), 1);
169 drop(array.pop());
170 assert_eq!(flag.get(), 1);
171 drop(array.pop());
172 assert_eq!(flag.get(), 3);
173 }
174
175 // last one didn't drop.
176 assert_eq!(flag.get(), 3);
177
178 flag.set(0);
179 {
180 let array = NoDrop::new(Bump(flag));
181 array.into_inner();
182 assert_eq!(flag.get(), 1);
183 }
184 assert_eq!(flag.get(), 1);
185 }
186 }