Skip to content

Commit e476688

Browse files
committed
replace uninitialized() with homemade MaybeUninit
Resolves #12
1 parent 1352eb7 commit e476688

File tree

8 files changed

+318
-33
lines changed

8 files changed

+318
-33
lines changed

build.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use std::env;
2+
use std::io::Write;
3+
use std::process::{Command, Stdio};
4+
5+
fn main() {
6+
// we need to output *some* file to opt out of the default
7+
println!("cargo:rerun-if-changed=build.rs");
8+
9+
detect_maybe_uninit();
10+
}
11+
12+
fn detect_maybe_uninit() {
13+
let has_unstable_union_with_md = probe(&maybe_uninit_code(true));
14+
if has_unstable_union_with_md {
15+
println!("cargo:rustc-cfg=has_manually_drop_in_union");
16+
println!("cargo:rustc-cfg=has_union_feature");
17+
return;
18+
}
19+
20+
let has_stable_union_with_md = probe(&maybe_uninit_code(false));
21+
if has_stable_union_with_md {
22+
println!("cargo:rustc-cfg=has_manually_drop_in_union");
23+
}
24+
}
25+
26+
// To guard against changes in this currently unstable feature, use
27+
// a detection tests instead of a Rustc version and/or date test.
28+
fn maybe_uninit_code(use_feature: bool) -> String {
29+
let feature = if use_feature {
30+
"#![feature(untagged_unions)]"
31+
} else {
32+
""
33+
};
34+
35+
let code = "
36+
#![allow(warnings)]
37+
use std::mem::ManuallyDrop;
38+
39+
#[derive(Copy)]
40+
pub union MaybeUninit<T> {
41+
empty: (),
42+
value: ManuallyDrop<T>,
43+
}
44+
45+
impl<T> Clone for MaybeUninit<T> where T: Copy
46+
{
47+
fn clone(&self) -> Self { *self }
48+
}
49+
50+
fn main() {
51+
let value1 = MaybeUninit::<[i32; 3]> { empty: () };
52+
let value2 = MaybeUninit { value: ManuallyDrop::new([1, 2, 3]) };
53+
}
54+
";
55+
56+
[feature, code].concat()
57+
}
58+
59+
/// Test if a code snippet can be compiled
60+
fn probe(code: &str) -> bool {
61+
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
62+
let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR");
63+
64+
let mut child = Command::new(rustc)
65+
.arg("--out-dir")
66+
.arg(out_dir)
67+
.arg("--emit=obj")
68+
.arg("-")
69+
.stdin(Stdio::piped())
70+
.spawn()
71+
.expect("rustc probe");
72+
73+
child
74+
.stdin
75+
.as_mut()
76+
.expect("rustc stdin")
77+
.write_all(code.as_bytes())
78+
.expect("write rustc stdin");
79+
80+
child.wait().expect("rustc probe").success()
81+
}

src/array.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,19 @@ macro_rules! fix_array_impl {
9393
($index_type:ty, $len:expr) => {
9494
unsafe impl<T> Array for [T; $len] {
9595
type Item = T;
96-
96+
9797
type Index = $index_type;
98-
98+
9999
#[inline(always)]
100100
fn as_ptr(&self) -> *const T {
101-
self as *const _ as *const _
101+
self as *const _ as *const T
102102
}
103-
103+
104104
#[inline(always)]
105105
fn as_mut_ptr(&mut self) -> *mut T {
106-
self as *mut _ as *mut _
106+
self as *mut _ as *mut T
107107
}
108-
108+
109109
#[inline(always)]
110110
fn capacity() -> usize {
111111
$len

src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::fmt;
21
#[cfg(feature = "std")]
32
use std::error::Error;
3+
use std::fmt;
44

55
/// Error value indicating insufficient capacity
66
///

src/lib.rs

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,36 +50,38 @@
5050
//! See the [behavior module documentation](behavior/index.html) for more.
5151
5252
#![cfg_attr(not(any(feature = "std", test)), no_std)]
53+
#![cfg_attr(has_union_feature, feature(untagged_unions))]
5354
#![deny(missing_docs)]
5455

5556
#[cfg(not(any(feature = "std", test)))]
5657
extern crate core as std;
5758
#[cfg(feature = "use_generic_array")]
5859
extern crate generic_array;
5960

60-
use std::mem;
61-
use std::mem::ManuallyDrop;
6261
use std::cmp;
6362
use std::cmp::Ordering;
64-
use std::hash::{Hash, Hasher};
65-
use std::marker;
6663
use std::fmt;
67-
use std::ptr;
64+
use std::hash::{Hash, Hasher};
6865
use std::iter::FromIterator;
66+
use std::marker;
6967
use std::ops::Index;
7068
use std::ops::IndexMut;
69+
use std::ptr;
70+
71+
use array::Index as ArrayIndex;
72+
use behavior::Behavior;
73+
use maybe_uninit::MaybeUninit;
7174

7275
mod array;
76+
pub mod behavior;
7377
mod error;
78+
mod maybe_uninit;
7479
mod range;
75-
pub mod behavior;
7680

7781
pub use array::Array;
82+
pub use behavior::{Saturating, Wrapping};
7883
pub use error::CapacityError;
7984
pub use range::RangeArgument;
80-
pub use behavior::{Saturating, Wrapping};
81-
use behavior::Behavior;
82-
use array::Index as ArrayIndex;
8385

8486
/// A fixed capacity ring buffer.
8587
///
@@ -89,7 +91,7 @@ use array::Index as ArrayIndex;
8991
/// the queue, and `pop_front` to remove from the queue. Iterating over `ArrayDeque` goes front
9092
/// to back.
9193
pub struct ArrayDeque<A: Array, B: Behavior = Saturating> {
92-
xs: ManuallyDrop<A>,
94+
xs: MaybeUninit<A>,
9395
tail: A::Index,
9496
len: A::Index,
9597
marker: marker::PhantomData<B>,
@@ -1011,7 +1013,7 @@ impl<A: Array, B: Behavior> ArrayDeque<A, B> {
10111013
pub fn new() -> ArrayDeque<A, B> {
10121014
unsafe {
10131015
ArrayDeque {
1014-
xs: ManuallyDrop::new(mem::uninitialized()),
1016+
xs: MaybeUninit::uninitialized(),
10151017
tail: ArrayIndex::from(0),
10161018
len: ArrayIndex::from(0),
10171019
marker: marker::PhantomData,
@@ -2023,11 +2025,7 @@ where
20232025
}
20242026
}
20252027

2026-
impl<A: Array, B: Behavior> Eq for ArrayDeque<A, B>
2027-
where
2028-
A::Item: Eq,
2029-
{
2030-
}
2028+
impl<A: Array, B: Behavior> Eq for ArrayDeque<A, B> where A::Item: Eq {}
20312029

20322030
impl<A: Array, B: Behavior> PartialOrd for ArrayDeque<A, B>
20332031
where
@@ -2072,8 +2070,7 @@ impl<A: Array, B: Behavior> Index<usize> for ArrayDeque<A, B> {
20722070
"index out of bounds: the len is {} but the index is {}",
20732071
len, index
20742072
)
2075-
})
2076-
.unwrap()
2073+
}).unwrap()
20772074
}
20782075
}
20792076

@@ -2087,8 +2084,7 @@ impl<A: Array, B: Behavior> IndexMut<usize> for ArrayDeque<A, B> {
20872084
"index out of bounds: the len is {} but the index is {}",
20882085
len, index
20892086
)
2090-
})
2091-
.unwrap()
2087+
}).unwrap()
20922088
}
20932089
}
20942090

@@ -2357,8 +2353,7 @@ where
23572353
A: Array,
23582354
A::Item: 'a,
23592355
B: Behavior,
2360-
{
2361-
}
2356+
{}
23622357

23632358
#[cfg(test)]
23642359
mod tests {
@@ -2368,7 +2363,7 @@ mod tests {
23682363
#[test]
23692364
fn test_simple() {
23702365
macro_rules! test {
2371-
($behavior:ident) => ({
2366+
($behavior:ident) => {{
23722367
let mut tester: ArrayDeque<[_; 7], $behavior> = ArrayDeque::new();
23732368
assert_eq!(tester.capacity(), 7);
23742369
assert_eq!(tester.len(), 0);
@@ -2384,7 +2379,7 @@ mod tests {
23842379
assert_eq!(tester.pop_front(), Some(3));
23852380
assert_eq!(tester.pop_front(), Some(4));
23862381
assert_eq!(tester.pop_front(), None);
2387-
})
2382+
}};
23882383
}
23892384

23902385
test!(Saturating);
@@ -2394,7 +2389,7 @@ mod tests {
23942389
#[test]
23952390
fn test_simple_reversely() {
23962391
macro_rules! test {
2397-
($behavior:ident) => ({
2392+
($behavior:ident) => {{
23982393
let mut tester: ArrayDeque<[_; 7], $behavior> = ArrayDeque::new();
23992394
assert_eq!(tester.capacity(), 7);
24002395
assert_eq!(tester.len(), 0);
@@ -2410,7 +2405,7 @@ mod tests {
24102405
assert_eq!(tester.pop_back(), Some(3));
24112406
assert_eq!(tester.pop_back(), Some(4));
24122407
assert_eq!(tester.pop_back(), None);
2413-
})
2408+
}};
24142409
}
24152410

24162411
test!(Saturating);
@@ -2835,6 +2830,12 @@ mod tests {
28352830
assert_eq!(tester, cloned)
28362831
}
28372832

2833+
#[test]
2834+
fn test_option_encoding() {
2835+
let tester: ArrayDeque<[Box<()>; 100]> = ArrayDeque::new();
2836+
assert!(Some(tester).is_some());
2837+
}
2838+
28382839
#[test]
28392840
fn test_insert_unchecked() {
28402841
const CAP: usize = 16;

src/maybe_uninit/maybe_uninit.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use array::Array;
2+
use std::mem::ManuallyDrop;
3+
use std::ops::{Deref, DerefMut};
4+
5+
/// A combination of ManuallyDrop and “maybe uninitialized”;
6+
/// this wraps a value that can be wholly or partially uninitialized;
7+
/// it also has no drop regardless of the type of Array.
8+
#[repr(C)] // for cast from self ptr to value
9+
pub union MaybeUninit<A: Array> {
10+
empty: (),
11+
value: ManuallyDrop<A>,
12+
}
13+
14+
impl<A: Array> MaybeUninit<A> {
15+
/// Create a new MaybeUninit with uninitialized interior
16+
pub unsafe fn uninitialized() -> Self {
17+
MaybeUninit { empty: () }
18+
}
19+
}
20+
21+
impl<A: Array> Deref for MaybeUninit<A> {
22+
type Target = A;
23+
24+
#[inline(always)]
25+
fn deref(&self) -> &A {
26+
unsafe { &self.value }
27+
}
28+
}
29+
30+
impl<A: Array> DerefMut for MaybeUninit<A> {
31+
#[inline(always)]
32+
fn deref_mut(&mut self) -> &mut A {
33+
unsafe { &mut self.value }
34+
}
35+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use super::nodrop::NoDrop;
2+
use array::Array;
3+
use std::mem;
4+
use std::ops::{Deref, DerefMut};
5+
6+
/// A combination of NoDrop and “maybe uninitialized”;
7+
/// this wraps a value that can be wholly or partially uninitialized.
8+
///
9+
/// NOTE: This is known to not be a good solution, but it's the one we have kept
10+
/// working on stable Rust. Stable improvements are encouraged, in any form,
11+
/// but of course we are waiting for a real, stable, MaybeUninit.
12+
#[repr(C)] // for cast from self ptr to value
13+
pub struct MaybeUninit<T>(NoDrop<T>);
14+
// why don't we use ManuallyDrop here: It doesn't inhibit
15+
// enum layout optimizations that depend on T, and we support older Rust.
16+
17+
impl<T> MaybeUninit<T> {
18+
/// Create a new MaybeUninit with uninitialized interior
19+
pub unsafe fn uninitialized() -> Self {
20+
Self(NoDrop::new(mem::uninitialized()))
21+
}
22+
}
23+
24+
impl<A: Array> Deref for MaybeUninit<A> {
25+
type Target = A;
26+
27+
#[inline(always)]
28+
fn deref(&self) -> &A {
29+
&self.0
30+
}
31+
}
32+
33+
impl<A: Array> DerefMut for MaybeUninit<A> {
34+
#[inline(always)]
35+
fn deref_mut(&mut self) -> &mut A {
36+
&mut self.0
37+
}
38+
}

src/maybe_uninit/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#[cfg(has_manually_drop_in_union)]
2+
mod maybe_uninit;
3+
#[cfg(not(has_manually_drop_in_union))]
4+
#[path = "maybe_uninit_nodrop.rs"]
5+
mod maybe_uninit;
6+
#[cfg(not(has_manually_drop_in_union))]
7+
mod nodrop;
8+
9+
pub use self::maybe_uninit::MaybeUninit;

0 commit comments

Comments
 (0)