Skip to content

Commit 68bd002

Browse files
committed
Fix cloning Symbols not increasing their ref count
1 parent 3fe815b commit 68bd002

File tree

2 files changed

+60
-28
lines changed

2 files changed

+60
-28
lines changed

crates/intern/src/symbol.rs

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{
55
borrow::Borrow,
66
fmt,
77
hash::{BuildHasherDefault, Hash, Hasher},
8-
mem,
8+
mem::{self, ManuallyDrop},
99
ptr::NonNull,
1010
sync::OnceLock,
1111
};
@@ -25,6 +25,15 @@ const _: () = assert!(std::mem::align_of::<Box<str>>() == std::mem::align_of::<&
2525
const _: () = assert!(std::mem::size_of::<Arc<Box<str>>>() == std::mem::size_of::<&&str>());
2626
const _: () = assert!(std::mem::align_of::<Arc<Box<str>>>() == std::mem::align_of::<&&str>());
2727

28+
const _: () =
29+
assert!(std::mem::size_of::<*const *const str>() == std::mem::size_of::<TaggedArcPtr>());
30+
const _: () =
31+
assert!(std::mem::align_of::<*const *const str>() == std::mem::align_of::<TaggedArcPtr>());
32+
33+
const _: () = assert!(std::mem::size_of::<Arc<Box<str>>>() == std::mem::size_of::<TaggedArcPtr>());
34+
const _: () =
35+
assert!(std::mem::align_of::<Arc<Box<str>>>() == std::mem::align_of::<TaggedArcPtr>());
36+
2837
/// A pointer that points to a pointer to a `str`, it may be backed as a `&'static &'static str` or
2938
/// `Arc<Box<str>>` but its size is that of a thin pointer. The active variant is encoded as a tag
3039
/// in the LSB of the alignment niche.
@@ -38,21 +47,27 @@ unsafe impl Sync for TaggedArcPtr {}
3847

3948
impl TaggedArcPtr {
4049
const BOOL_BITS: usize = true as usize;
50+
const ALIGNMENT_NICHE: usize = mem::align_of::<TaggedArcPtr>().trailing_zeros() as usize;
4151

4252
const fn non_arc(r: &'static &'static str) -> Self {
43-
Self {
44-
// SAFETY: The pointer is non-null as it is derived from a reference
45-
// Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the
46-
// packing stuff requires reading out the pointer to an integer which is not supported
47-
// in const contexts, so here we make use of the fact that for the non-arc version the
48-
// tag is false (0) and thus does not need touching the actual pointer value.ext)
49-
packed: unsafe {
50-
NonNull::new_unchecked((r as *const &str).cast::<*const str>().cast_mut())
51-
},
52-
}
53+
assert!(
54+
mem::align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS
55+
);
56+
// SAFETY: The pointer is non-null as it is derived from a reference
57+
// Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the
58+
// packing stuff requires reading out the pointer to an integer which is not supported
59+
// in const contexts, so here we make use of the fact that for the non-arc version the
60+
// tag is false (0) and thus does not need touching the actual pointer value.ext)
61+
62+
let packed =
63+
unsafe { NonNull::new_unchecked((r as *const &str).cast::<*const str>().cast_mut()) };
64+
Self { packed }
5365
}
5466

5567
fn arc(arc: Arc<Box<str>>) -> Self {
68+
assert!(
69+
mem::align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS
70+
);
5671
Self {
5772
packed: Self::pack_arc(
5873
// Safety: `Arc::into_raw` always returns a non null pointer
@@ -63,12 +78,14 @@ impl TaggedArcPtr {
6378

6479
/// Retrieves the tag.
6580
#[inline]
66-
pub(crate) fn try_as_arc_owned(self) -> Option<Arc<Box<str>>> {
81+
pub(crate) fn try_as_arc_owned(self) -> Option<ManuallyDrop<Arc<Box<str>>>> {
6782
// Unpack the tag from the alignment niche
6883
let tag = Strict::addr(self.packed.as_ptr()) & Self::BOOL_BITS;
6984
if tag != 0 {
7085
// Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc`
71-
Some(unsafe { Arc::from_raw(self.pointer().as_ptr().cast::<Box<str>>()) })
86+
Some(ManuallyDrop::new(unsafe {
87+
Arc::from_raw(self.pointer().as_ptr().cast::<Box<str>>())
88+
}))
7289
} else {
7390
None
7491
}
@@ -122,10 +139,11 @@ impl TaggedArcPtr {
122139
}
123140
}
124141

125-
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
142+
#[derive(PartialEq, Eq, Hash, Debug)]
126143
pub struct Symbol {
127144
repr: TaggedArcPtr,
128145
}
146+
129147
const _: () = assert!(std::mem::size_of::<Symbol>() == std::mem::size_of::<NonNull<()>>());
130148
const _: () = assert!(std::mem::align_of::<Symbol>() == std::mem::align_of::<NonNull<()>>());
131149

@@ -185,19 +203,27 @@ impl Symbol {
185203
fn drop_slow(arc: &Arc<Box<str>>) {
186204
let (mut shard, hash) = Self::select_shard(arc);
187205

188-
if Arc::count(arc) != 2 {
189-
// Another thread has interned another copy
190-
return;
206+
match Arc::count(arc) {
207+
0 => unreachable!(),
208+
1 => unreachable!(),
209+
2 => (),
210+
_ => {
211+
// Another thread has interned another copy
212+
return;
213+
}
191214
}
192215

193-
match shard.raw_entry_mut().from_key_hashed_nocheck::<str>(hash, arc.as_ref()) {
194-
RawEntryMut::Occupied(occ) => occ.remove_entry(),
195-
RawEntryMut::Vacant(_) => unreachable!(),
196-
}
197-
.0
198-
.0
199-
.try_as_arc_owned()
200-
.unwrap();
216+
ManuallyDrop::into_inner(
217+
match shard.raw_entry_mut().from_key_hashed_nocheck::<str>(hash, arc.as_ref()) {
218+
RawEntryMut::Occupied(occ) => occ.remove_entry(),
219+
RawEntryMut::Vacant(_) => unreachable!(),
220+
}
221+
.0
222+
.0
223+
.try_as_arc_owned()
224+
.unwrap(),
225+
);
226+
debug_assert_eq!(Arc::count(&arc), 1);
201227

202228
// Shrink the backing storage if the shard is less than 50% occupied.
203229
if shard.len() * 2 < shard.capacity() {
@@ -219,7 +245,13 @@ impl Drop for Symbol {
219245
Self::drop_slow(&arc);
220246
}
221247
// decrement the ref count
222-
drop(arc);
248+
ManuallyDrop::into_inner(arc);
249+
}
250+
}
251+
252+
impl Clone for Symbol {
253+
fn clone(&self) -> Self {
254+
Self { repr: increase_arc_refcount(self.repr) }
223255
}
224256
}
225257

@@ -228,8 +260,7 @@ fn increase_arc_refcount(repr: TaggedArcPtr) -> TaggedArcPtr {
228260
return repr;
229261
};
230262
// increase the ref count
231-
mem::forget(arc.clone());
232-
mem::forget(arc);
263+
mem::forget(Arc::clone(&arc));
233264
repr
234265
}
235266

crates/intern/src/symbol/symbols.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::{
1010
symbol::{SymbolProxy, TaggedArcPtr},
1111
Symbol,
1212
};
13+
1314
macro_rules! define_symbols {
1415
(@WITH_NAME: $($alias:ident = $value:literal),* $(,)? @PLAIN: $($name:ident),* $(,)?) => {
1516
$(

0 commit comments

Comments
 (0)