Skip to content

Commit e7f5424

Browse files
committed
Use private generic type for drop behavior
This patch avoids the thread-local to choose dropping behavior by instead typecasting among private generic types for the inner value type. It's not clear if that's actually sound: rust-lang/unsafe-code-guidelines#35 (comment) The current implementation does not compile due to a conflict between the need for the concrete drop behavior types to be private and the need for the (public) bound: Aliased<T, crate::aliasing::NoDrop>: Borrow<_> I don't quite know how to work around that yet. What we really want is to write: for<U: DropBehavior> Aliased<T, U>: Borrow<_> But I don't think that's current expressible? Maybe through some more trait magic?
1 parent b6c7a31 commit e7f5424

File tree

7 files changed

+217
-114
lines changed

7 files changed

+217
-114
lines changed

evmap/src/aliasing.rs

Lines changed: 112 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
1-
use std::cell::Cell;
21
use std::marker::PhantomData;
32
use std::mem::MaybeUninit;
43
use std::ops::Deref;
54

5+
// NOTE: These types _cannot_ be public, as doing so may cause external implementations to
6+
// implement different behavior for them, which would make transmutes between
7+
// SomeWrapper<Aliased<T, NoDrop>> and SomeWrapper<Aliased<T, DoDrop>> unsound.
8+
pub(crate) struct NoDrop;
9+
pub(crate) struct DoDrop;
10+
11+
pub trait DropBehavior {
12+
fn do_drop() -> bool;
13+
}
14+
impl DropBehavior for NoDrop {
15+
fn do_drop() -> bool {
16+
false
17+
}
18+
}
19+
impl DropBehavior for DoDrop {
20+
fn do_drop() -> bool {
21+
true
22+
}
23+
}
24+
625
/// A `T` that is aliased across the two map copies.
726
///
827
/// You should be able to mostly ignore this type, as it can generally be treated exactly like a
@@ -13,14 +32,19 @@ use std::ops::Deref;
1332
/// implementing your trait specifically for `Aliased<T>` where possible, or by manually
1433
/// dereferencing to get the `&T` before using the trait.
1534
#[repr(transparent)]
16-
pub struct Aliased<T> {
35+
pub struct Aliased<T, U>
36+
where
37+
U: DropBehavior,
38+
{
1739
aliased: MaybeUninit<T>,
1840

41+
drop_behavior: PhantomData<U>,
42+
1943
// We cannot implement Send just because T is Send since we're aliasing it.
2044
_no_auto_send: PhantomData<*const T>,
2145
}
2246

23-
impl<T> Aliased<T> {
47+
impl<T> Aliased<T, NoDrop> {
2448
/// Create an alias of the inner `T`.
2549
///
2650
/// # Safety
@@ -36,6 +60,7 @@ impl<T> Aliased<T> {
3660
// b) we only expose _either_ &T while aliased, or &mut after the aliasing ends.
3761
Aliased {
3862
aliased: unsafe { std::ptr::read(&self.aliased) },
63+
drop_behavior: PhantomData,
3964
_no_auto_send: PhantomData,
4065
}
4166
}
@@ -48,6 +73,21 @@ impl<T> Aliased<T> {
4873
pub(crate) fn from(t: T) -> Self {
4974
Self {
5075
aliased: MaybeUninit::new(t),
76+
drop_behavior: PhantomData,
77+
_no_auto_send: PhantomData,
78+
}
79+
}
80+
81+
/// Turn this aliased `T` into one that will drop the `T` when it is dropped.
82+
///
83+
/// # Safety
84+
///
85+
/// This is safe if and only if `self` is the last alias for the `T`.
86+
pub(crate) unsafe fn dropping(self) -> Aliased<T, DoDrop> {
87+
Aliased {
88+
// safety:
89+
aliased: std::ptr::read(&self.aliased),
90+
drop_behavior: PhantomData,
5191
_no_auto_send: PhantomData,
5292
}
5393
}
@@ -58,72 +98,63 @@ impl<T> Aliased<T> {
5898
// This implies that we are only Send if T is Send+Sync, and Sync if T is Sync.
5999
//
60100
// Note that these bounds are stricter than what the compiler would auto-generate for the type.
61-
unsafe impl<T> Send for Aliased<T> where T: Send + Sync {}
62-
unsafe impl<T> Sync for Aliased<T> where T: Sync {}
63-
64-
// XXX: Is this a problem if people start nesting evmaps?
65-
// I feel like the answer is yes.
66-
thread_local! {
67-
static DROP_FOR_REAL: Cell<bool> = Cell::new(false);
101+
unsafe impl<T, U> Send for Aliased<T, U>
102+
where
103+
U: DropBehavior,
104+
T: Send + Sync,
105+
{
68106
}
69-
70-
/// Make _any_ dropped `Aliased` actually drop their inner `T`.
71-
///
72-
/// Be very careful: this function will cause _all_ dropped `Aliased` to drop their `T`.
73-
///
74-
/// When the return value is dropped, dropping `Aliased` will have no effect again.
75-
///
76-
/// # Safety
77-
///
78-
/// Only set this when any following `Aliased` that are dropped are no longer aliased.
79-
pub(crate) unsafe fn drop_copies() -> impl Drop {
80-
struct DropGuard;
81-
impl Drop for DropGuard {
82-
fn drop(&mut self) {
83-
DROP_FOR_REAL.with(|dfr| dfr.set(false));
84-
}
85-
}
86-
let guard = DropGuard;
87-
DROP_FOR_REAL.with(|dfr| dfr.set(true));
88-
guard
107+
unsafe impl<T, U> Sync for Aliased<T, U>
108+
where
109+
U: DropBehavior,
110+
T: Sync,
111+
{
89112
}
90113

91-
impl<T> Drop for Aliased<T> {
114+
impl<T, U> Drop for Aliased<T, U>
115+
where
116+
U: DropBehavior,
117+
{
92118
fn drop(&mut self) {
93-
DROP_FOR_REAL.with(move |drop_for_real| {
94-
if drop_for_real.get() {
95-
// safety:
96-
// MaybeUninit<T> was created from a valid T.
97-
// That T has not been dropped (drop_copies is unsafe).
98-
// T is no longer aliased (drop_copies is unsafe),
99-
// so we are allowed to re-take ownership of the T.
100-
unsafe { std::ptr::drop_in_place(self.aliased.as_mut_ptr()) }
101-
}
102-
})
119+
if U::do_drop() {
120+
// safety:
121+
// MaybeUninit<T> was created from a valid T.
122+
// That T has not been dropped (getting a Aliased<T, DoDrop> is unsafe).
123+
// T is no longer aliased (by the safety assumption of getting a Aliased<T, DoDrop>),
124+
// so we are allowed to re-take ownership of the T.
125+
unsafe { std::ptr::drop_in_place(self.aliased.as_mut_ptr()) }
126+
}
103127
}
104128
}
105129

106-
impl<T> AsRef<T> for Aliased<T> {
130+
impl<T, U> AsRef<T> for Aliased<T, U>
131+
where
132+
U: DropBehavior,
133+
{
107134
fn as_ref(&self) -> &T {
108135
// safety:
109136
// MaybeUninit<T> was created from a valid T.
110-
// That T has not been dropped (drop_copies is unsafe).
137+
// That T has not been dropped (getting a Aliased<T, DoDrop> is unsafe).
111138
// All we have done to T is alias it. But, since we only give out &T
112139
// (which should be legal anyway), we're fine.
113140
unsafe { &*self.aliased.as_ptr() }
114141
}
115142
}
116143

117-
impl<T> Deref for Aliased<T> {
144+
impl<T, U> Deref for Aliased<T, U>
145+
where
146+
U: DropBehavior,
147+
{
118148
type Target = T;
119149
fn deref(&self) -> &Self::Target {
120150
self.as_ref()
121151
}
122152
}
123153

124154
use std::hash::{Hash, Hasher};
125-
impl<T> Hash for Aliased<T>
155+
impl<T, U> Hash for Aliased<T, U>
126156
where
157+
U: DropBehavior,
127158
T: Hash,
128159
{
129160
fn hash<H>(&self, state: &mut H)
@@ -135,17 +166,19 @@ where
135166
}
136167

137168
use std::fmt;
138-
impl<T> fmt::Debug for Aliased<T>
169+
impl<T, U> fmt::Debug for Aliased<T, U>
139170
where
171+
U: DropBehavior,
140172
T: fmt::Debug,
141173
{
142174
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143175
self.as_ref().fmt(f)
144176
}
145177
}
146178

147-
impl<T> PartialEq for Aliased<T>
179+
impl<T, U> PartialEq for Aliased<T, U>
148180
where
181+
U: DropBehavior,
149182
T: PartialEq,
150183
{
151184
fn eq(&self, other: &Self) -> bool {
@@ -157,10 +190,16 @@ where
157190
}
158191
}
159192

160-
impl<T> Eq for Aliased<T> where T: Eq {}
193+
impl<T, U> Eq for Aliased<T, U>
194+
where
195+
U: DropBehavior,
196+
T: Eq,
197+
{
198+
}
161199

162-
impl<T> PartialOrd for Aliased<T>
200+
impl<T, U> PartialOrd for Aliased<T, U>
163201
where
202+
U: DropBehavior,
164203
T: PartialOrd,
165204
{
166205
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
@@ -184,8 +223,9 @@ where
184223
}
185224
}
186225

187-
impl<T> Ord for Aliased<T>
226+
impl<T, U> Ord for Aliased<T, U>
188227
where
228+
U: DropBehavior,
189229
T: Ord,
190230
{
191231
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
@@ -194,7 +234,10 @@ where
194234
}
195235

196236
use std::borrow::Borrow;
197-
impl<T> Borrow<T> for Aliased<T> {
237+
impl<T, U> Borrow<T> for Aliased<T, U>
238+
where
239+
U: DropBehavior,
240+
{
198241
fn borrow(&self) -> &T {
199242
self.as_ref()
200243
}
@@ -213,40 +256,52 @@ impl<T> Borrow<T> for Aliased<T> {
213256
// But unfortunately that won't work due to trait coherence.
214257
// Instead, we manually write the nice Borrow impls from std.
215258
// This won't help with custom Borrow impls, but gets you pretty far.
216-
impl Borrow<str> for Aliased<String> {
259+
impl<U> Borrow<str> for Aliased<String, U>
260+
where
261+
U: DropBehavior,
262+
{
217263
fn borrow(&self) -> &str {
218264
self.as_ref()
219265
}
220266
}
221-
impl Borrow<std::path::Path> for Aliased<std::path::PathBuf> {
267+
impl<U> Borrow<std::path::Path> for Aliased<std::path::PathBuf, U>
268+
where
269+
U: DropBehavior,
270+
{
222271
fn borrow(&self) -> &std::path::Path {
223272
self.as_ref()
224273
}
225274
}
226-
impl<T> Borrow<[T]> for Aliased<Vec<T>> {
275+
impl<T, U> Borrow<[T]> for Aliased<Vec<T>, U>
276+
where
277+
U: DropBehavior,
278+
{
227279
fn borrow(&self) -> &[T] {
228280
self.as_ref()
229281
}
230282
}
231-
impl<T> Borrow<T> for Aliased<Box<T>>
283+
impl<T, U> Borrow<T> for Aliased<Box<T>, U>
232284
where
233285
T: ?Sized,
286+
U: DropBehavior,
234287
{
235288
fn borrow(&self) -> &T {
236289
self.as_ref()
237290
}
238291
}
239-
impl<T> Borrow<T> for Aliased<std::sync::Arc<T>>
292+
impl<T, U> Borrow<T> for Aliased<std::sync::Arc<T>, U>
240293
where
241294
T: ?Sized,
295+
U: DropBehavior,
242296
{
243297
fn borrow(&self) -> &T {
244298
self.as_ref()
245299
}
246300
}
247-
impl<T> Borrow<T> for Aliased<std::rc::Rc<T>>
301+
impl<T, U> Borrow<T> for Aliased<std::rc::Rc<T>, U>
248302
where
249303
T: ?Sized,
304+
U: DropBehavior,
250305
{
251306
fn borrow(&self) -> &T {
252307
self.as_ref()

evmap/src/inner.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@ pub(crate) use indexmap::IndexMap as MapImpl;
66
#[cfg(not(feature = "indexed"))]
77
pub(crate) use std::collections::HashMap as MapImpl;
88

9+
use crate::aliasing::DropBehavior;
910
use crate::values::Values;
1011

11-
pub(crate) struct Inner<K, V, M, S>
12+
pub(crate) struct Inner<K, V, M, S, D = crate::aliasing::NoDrop>
1213
where
1314
K: Eq + Hash,
1415
S: BuildHasher,
16+
D: DropBehavior,
1517
{
16-
pub(crate) data: MapImpl<K, Values<V, S>, S>,
18+
pub(crate) data: MapImpl<K, Values<V, S, D>, S>,
1719
pub(crate) meta: M,
1820
pub(crate) ready: bool,
1921
}

evmap/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,9 +257,9 @@ impl<V: ?Sized> fmt::Debug for Predicate<V> {
257257
#[non_exhaustive]
258258
pub(crate) enum Operation<K, V, M> {
259259
/// Replace the set of entries for this key with this value.
260-
Replace(K, Aliased<V>),
260+
Replace(K, Aliased<V, crate::aliasing::NoDrop>),
261261
/// Add this value to the set of entries for this key.
262-
Add(K, Aliased<V>),
262+
Add(K, Aliased<V, crate::aliasing::NoDrop>),
263263
/// Remove this value from the set of entries for this key.
264264
RemoveValue(K, V),
265265
/// Remove the value set for this key.

evmap/src/read.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{inner::Inner, Aliased, Values};
1+
use crate::{aliasing::DropBehavior, inner::Inner, Aliased, Values};
22
use left_right::ReadGuard;
33

44
use std::borrow::Borrow;
@@ -208,7 +208,7 @@ where
208208
pub fn contains_value<Q: ?Sized, W: ?Sized>(&self, key: &Q, value: &W) -> bool
209209
where
210210
K: Borrow<Q>,
211-
Aliased<V>: Borrow<W>,
211+
Aliased<V, crate::aliasing::NoDrop>: Borrow<W>,
212212
Q: Hash + Eq,
213213
W: Hash + Eq,
214214
V: Hash + Eq,

evmap/src/read/read_ref.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ where
147147
pub fn contains_value<Q: ?Sized, W: ?Sized>(&self, key: &Q, value: &W) -> bool
148148
where
149149
K: Borrow<Q>,
150-
Aliased<V>: Borrow<W>,
150+
Aliased<V, crate::aliasing::NoDrop>: Borrow<W>,
151151
Q: Hash + Eq,
152152
W: Hash + Eq,
153153
{

0 commit comments

Comments
 (0)