Skip to content

Commit 3d0287a

Browse files
committed
Auto merge of #149775 - WaffleLapkin:core-mem-maybe-dangling, r=Mark-Simulacrum
Add `MaybeDangling` to `core` This is the library part of adding `MaybeDangling`. Note that it doesn't actually do anything described in its docs (yet), I'll make a separate PR for that. Tracking issue: #118166. r? libs cc `@RalfJung`
2 parents f794a08 + 25476d8 commit 3d0287a

File tree

11 files changed

+392
-200
lines changed

11 files changed

+392
-200
lines changed

library/core/src/mem/manually_drop.rs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::marker::Destruct;
1+
use crate::cmp::Ordering;
2+
use crate::hash::{Hash, Hasher};
3+
use crate::marker::{Destruct, StructuralPartialEq};
4+
use crate::mem::MaybeDangling;
25
use crate::ops::{Deref, DerefMut, DerefPure};
36
use crate::ptr;
47

@@ -152,11 +155,11 @@ use crate::ptr;
152155
/// [`MaybeUninit`]: crate::mem::MaybeUninit
153156
#[stable(feature = "manually_drop", since = "1.20.0")]
154157
#[lang = "manually_drop"]
155-
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
158+
#[derive(Copy, Clone, Debug, Default)]
156159
#[repr(transparent)]
157160
#[rustc_pub_transparent]
158161
pub struct ManuallyDrop<T: ?Sized> {
159-
value: T,
162+
value: MaybeDangling<T>,
160163
}
161164

162165
impl<T> ManuallyDrop<T> {
@@ -179,7 +182,7 @@ impl<T> ManuallyDrop<T> {
179182
#[rustc_const_stable(feature = "const_manually_drop", since = "1.32.0")]
180183
#[inline(always)]
181184
pub const fn new(value: T) -> ManuallyDrop<T> {
182-
ManuallyDrop { value }
185+
ManuallyDrop { value: MaybeDangling::new(value) }
183186
}
184187

185188
/// Extracts the value from the `ManuallyDrop` container.
@@ -197,7 +200,7 @@ impl<T> ManuallyDrop<T> {
197200
#[rustc_const_stable(feature = "const_manually_drop", since = "1.32.0")]
198201
#[inline(always)]
199202
pub const fn into_inner(slot: ManuallyDrop<T>) -> T {
200-
slot.value
203+
slot.value.into_inner()
201204
}
202205

203206
/// Takes the value from the `ManuallyDrop<T>` container out.
@@ -222,7 +225,7 @@ impl<T> ManuallyDrop<T> {
222225
pub const unsafe fn take(slot: &mut ManuallyDrop<T>) -> T {
223226
// SAFETY: we are reading from a reference, which is guaranteed
224227
// to be valid for reads.
225-
unsafe { ptr::read(&slot.value) }
228+
unsafe { ptr::read(slot.value.as_ref()) }
226229
}
227230
}
228231

@@ -259,7 +262,7 @@ impl<T: ?Sized> ManuallyDrop<T> {
259262
// SAFETY: we are dropping the value pointed to by a mutable reference
260263
// which is guaranteed to be valid for writes.
261264
// It is up to the caller to make sure that `slot` isn't dropped again.
262-
unsafe { ptr::drop_in_place(&mut slot.value) }
265+
unsafe { ptr::drop_in_place(slot.value.as_mut()) }
263266
}
264267
}
265268

@@ -269,7 +272,7 @@ impl<T: ?Sized> const Deref for ManuallyDrop<T> {
269272
type Target = T;
270273
#[inline(always)]
271274
fn deref(&self) -> &T {
272-
&self.value
275+
self.value.as_ref()
273276
}
274277
}
275278

@@ -278,9 +281,43 @@ impl<T: ?Sized> const Deref for ManuallyDrop<T> {
278281
impl<T: ?Sized> const DerefMut for ManuallyDrop<T> {
279282
#[inline(always)]
280283
fn deref_mut(&mut self) -> &mut T {
281-
&mut self.value
284+
self.value.as_mut()
282285
}
283286
}
284287

285288
#[unstable(feature = "deref_pure_trait", issue = "87121")]
286289
unsafe impl<T: ?Sized> DerefPure for ManuallyDrop<T> {}
290+
291+
#[stable(feature = "manually_drop", since = "1.20.0")]
292+
impl<T: ?Sized + Eq> Eq for ManuallyDrop<T> {}
293+
294+
#[stable(feature = "manually_drop", since = "1.20.0")]
295+
impl<T: ?Sized + PartialEq> PartialEq for ManuallyDrop<T> {
296+
fn eq(&self, other: &Self) -> bool {
297+
self.value.as_ref().eq(other.value.as_ref())
298+
}
299+
}
300+
301+
#[stable(feature = "manually_drop", since = "1.20.0")]
302+
impl<T: ?Sized> StructuralPartialEq for ManuallyDrop<T> {}
303+
304+
#[stable(feature = "manually_drop", since = "1.20.0")]
305+
impl<T: ?Sized + Ord> Ord for ManuallyDrop<T> {
306+
fn cmp(&self, other: &Self) -> Ordering {
307+
self.value.as_ref().cmp(other.value.as_ref())
308+
}
309+
}
310+
311+
#[stable(feature = "manually_drop", since = "1.20.0")]
312+
impl<T: ?Sized + PartialOrd> PartialOrd for ManuallyDrop<T> {
313+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
314+
self.value.as_ref().partial_cmp(other.value.as_ref())
315+
}
316+
}
317+
318+
#[stable(feature = "manually_drop", since = "1.20.0")]
319+
impl<T: ?Sized + Hash> Hash for ManuallyDrop<T> {
320+
fn hash<H: Hasher>(&self, state: &mut H) {
321+
self.value.as_ref().hash(state);
322+
}
323+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#![unstable(feature = "maybe_dangling", issue = "118166")]
2+
3+
use crate::{mem, ptr};
4+
5+
/// Allows wrapped [references] and [boxes] to dangle.
6+
///
7+
/// <section class="warning">
8+
/// This type is not properly implemented yet, and the documentation below is thus not accurate.
9+
/// </section>
10+
///
11+
/// That is, if a reference (or a `Box`) is wrapped in `MaybeDangling` (including when in a
12+
/// (nested) field of a compound type wrapped in `MaybeDangling`), it does not have to follow
13+
/// pointer aliasing rules or be dereferenceable.
14+
///
15+
/// This can be useful when the value can become dangling while the function holding it is still
16+
/// executing (particularly in concurrent code). As a somewhat absurd example, consider this code:
17+
///
18+
/// ```rust,no_run
19+
/// #![feature(box_as_ptr)]
20+
/// # use std::alloc::{dealloc, Layout};
21+
/// # use std::mem;
22+
///
23+
/// let mut boxed = Box::new(0_u32);
24+
/// let ptr = Box::as_mut_ptr(&mut boxed);
25+
///
26+
/// // Safety: the pointer comes from a box and thus was allocated before; `box` is not used afterwards
27+
/// unsafe { dealloc(ptr.cast(), Layout::new::<u32>()) };
28+
///
29+
/// mem::forget(boxed); // <-- this is UB!
30+
/// ```
31+
///
32+
/// Even though the `Box`e's destructor is not run (and thus we don't have a double free bug), this
33+
/// code is still UB. This is because when moving `boxed` into `forget`, its validity invariants
34+
/// are asserted, causing UB since the `Box` is dangling. The safety comment is as such wrong, as
35+
/// moving the `boxed` variable as part of the `forget` call *is* a use.
36+
///
37+
/// To fix this we could use `MaybeDangling`:
38+
///
39+
/// ```rust
40+
/// #![feature(maybe_dangling, box_as_ptr)]
41+
/// # use std::alloc::{dealloc, Layout};
42+
/// # use std::mem::{self, MaybeDangling};
43+
///
44+
/// let mut boxed = MaybeDangling::new(Box::new(0_u32));
45+
/// let ptr = Box::as_mut_ptr(boxed.as_mut());
46+
///
47+
/// // Safety: the pointer comes from a box and thus was allocated before; `box` is not used afterwards
48+
/// unsafe { dealloc(ptr.cast(), Layout::new::<u32>()) };
49+
///
50+
/// mem::forget(boxed); // <-- this is OK!
51+
/// ```
52+
///
53+
/// Note that the bit pattern must still be valid for the wrapped type. That is, [references]
54+
/// (and [boxes]) still must be aligned and non-null.
55+
///
56+
/// Additionally note that safe code can still assume that the inner value in a `MaybeDangling` is
57+
/// **not** dangling -- functions like [`as_ref`] and [`into_inner`] are safe. It is not sound to
58+
/// return a dangling reference in a `MaybeDangling` to safe code. However, it *is* sound
59+
/// to hold such values internally inside your code -- and there's no way to do that without
60+
/// this type. Note that other types can use this type and thus get the same effect; in particular,
61+
/// [`ManuallyDrop`] will use `MaybeDangling`.
62+
///
63+
/// Note that `MaybeDangling` doesn't prevent drops from being run, which can lead to UB if the
64+
/// drop observes a dangling value. If you need to prevent drops from being run use [`ManuallyDrop`]
65+
/// instead.
66+
///
67+
/// [references]: prim@reference
68+
/// [boxes]: ../../std/boxed/struct.Box.html
69+
/// [`into_inner`]: MaybeDangling::into_inner
70+
/// [`as_ref`]: MaybeDangling::as_ref
71+
/// [`ManuallyDrop`]: crate::mem::ManuallyDrop
72+
#[repr(transparent)]
73+
#[rustc_pub_transparent]
74+
#[derive(Debug, Copy, Clone, Default)]
75+
pub struct MaybeDangling<P: ?Sized>(P);
76+
77+
impl<P: ?Sized> MaybeDangling<P> {
78+
/// Wraps a value in a `MaybeDangling`, allowing it to dangle.
79+
pub const fn new(x: P) -> Self
80+
where
81+
P: Sized,
82+
{
83+
MaybeDangling(x)
84+
}
85+
86+
/// Returns a reference to the inner value.
87+
///
88+
/// Note that this is UB if the inner value is currently dangling.
89+
pub const fn as_ref(&self) -> &P {
90+
&self.0
91+
}
92+
93+
/// Returns a mutable reference to the inner value.
94+
///
95+
/// Note that this is UB if the inner value is currently dangling.
96+
pub const fn as_mut(&mut self) -> &mut P {
97+
&mut self.0
98+
}
99+
100+
/// Extracts the value from the `MaybeDangling` container.
101+
///
102+
/// Note that this is UB if the inner value is currently dangling.
103+
pub const fn into_inner(self) -> P
104+
where
105+
P: Sized,
106+
{
107+
// FIXME: replace this with `self.0` when const checker can figure out that `self` isn't actually dropped
108+
// SAFETY: this is equivalent to `self.0`
109+
let x = unsafe { ptr::read(&self.0) };
110+
mem::forget(self);
111+
x
112+
}
113+
}

library/core/src/mem/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ mod maybe_uninit;
1919
#[stable(feature = "maybe_uninit", since = "1.36.0")]
2020
pub use maybe_uninit::MaybeUninit;
2121

22+
mod maybe_dangling;
23+
#[unstable(feature = "maybe_dangling", issue = "118166")]
24+
pub use maybe_dangling::MaybeDangling;
25+
2226
mod transmutability;
2327
#[unstable(feature = "transmutability", issue = "99571")]
2428
pub use transmutability::{Assume, TransmuteFrom};

src/etc/gdb_providers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,20 +298,20 @@ def cast_to_internal(node):
298298

299299
for i in xrange(0, length + 1):
300300
if height > 0:
301-
child_ptr = edges[i]["value"]["value"]
301+
child_ptr = edges[i]["value"]["value"][ZERO_FIELD]
302302
for child in children_of_node(child_ptr, height - 1):
303303
yield child
304304
if i < length:
305305
# Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays.
306306
key_type_size = keys.type.sizeof
307307
val_type_size = vals.type.sizeof
308308
key = (
309-
keys[i]["value"]["value"]
309+
keys[i]["value"]["value"][ZERO_FIELD]
310310
if key_type_size > 0
311311
else gdb.parse_and_eval("()")
312312
)
313313
val = (
314-
vals[i]["value"]["value"]
314+
vals[i]["value"]["value"][ZERO_FIELD]
315315
if val_type_size > 0
316316
else gdb.parse_and_eval("()")
317317
)
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
0..1: [ SharedReadWrite<TAG> ]
22
0..1: [ SharedReadWrite<TAG> ]
33
0..1: [ SharedReadWrite<TAG> ]
4-
0..1: [ SharedReadWrite<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> ]
5-
0..1: [ SharedReadWrite<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> SharedReadOnly<TAG> ]
4+
0..1: [ SharedReadWrite<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> ]
5+
0..1: [ SharedReadWrite<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> SharedReadOnly<TAG> ]
66
0..1: [ unknown-bottom(..<TAG>) ]

0 commit comments

Comments
 (0)