Skip to content

Commit b652cbe

Browse files
committed
correctly drop Array::IntoIter
1 parent d5970b0 commit b652cbe

File tree

1 file changed

+53
-20
lines changed

1 file changed

+53
-20
lines changed

core/src/term/array.rs

+53-20
Original file line numberDiff line numberDiff line change
@@ -157,29 +157,50 @@ impl IntoIterator for Array {
157157
// to the inner slice, then we can:
158158
// - Drop the elements outside our inner view,
159159
// - Move out the elements inside out inner view.
160+
// - Drop the rest of the elements when we're dropped
160161
// Otherwise, we clone everything.
161-
162-
unsafe {
163-
let mut inner = transmute::<Rc<[RichTerm]>, Rc<[ManuallyDrop<RichTerm>]>>(self.inner);
164-
let idx = self.start;
165-
let end = self.end;
166-
167-
if let Some(slice) = Rc::get_mut(&mut inner) {
168-
for term in &mut slice[..idx] {
162+
//
163+
// If we start as a shared reference, we could become the only reference
164+
// later, but we clone everything anyways, so we don't end up in an
165+
// invalid in-between state where some terms have been freed manually
166+
// and others haven't.
167+
168+
let inner = if Rc::strong_count(&self.inner) != 1 || Rc::weak_count(&self.inner) != 0 {
169+
IntoIterInner::Shared(self.inner)
170+
} else {
171+
unsafe {
172+
let mut inner =
173+
transmute::<Rc<[RichTerm]>, Rc<[ManuallyDrop<RichTerm>]>>(self.inner);
174+
let slice =
175+
Rc::get_mut(&mut inner).expect("non-unique Rc after checking for uniqueness");
176+
for term in &mut slice[..self.start] {
169177
ManuallyDrop::drop(term)
170178
}
171-
for term in &mut slice[end..] {
179+
for term in &mut slice[self.end..] {
172180
ManuallyDrop::drop(term)
173181
}
174-
}
175182

176-
IntoIter { inner, idx, end }
183+
IntoIterInner::Owned(inner)
184+
}
185+
};
186+
IntoIter {
187+
inner,
188+
idx: self.start,
189+
end: self.end,
177190
}
178191
}
179192
}
180193

194+
enum IntoIterInner {
195+
Shared(Rc<[RichTerm]>),
196+
// This shouldn't really be an Rc. It should only ever have one reference.
197+
// There may be a way to rewrite this once unsized locals are stabilized.
198+
// But for now, there is no good way to repackage the inner array, so we
199+
// keep it in an Rc.
200+
Owned(Rc<[ManuallyDrop<RichTerm>]>),
201+
}
181202
pub struct IntoIter {
182-
inner: Rc<[ManuallyDrop<RichTerm>]>,
203+
inner: IntoIterInner,
183204
idx: usize,
184205
end: usize,
185206
}
@@ -191,14 +212,14 @@ impl Iterator for IntoIter {
191212
if self.idx == self.end {
192213
None
193214
} else {
194-
let term = match Rc::get_mut(&mut self.inner) {
195-
None => self.inner[..self.end]
196-
.get(self.idx)
197-
.cloned()
198-
.map(ManuallyDrop::into_inner),
199-
Some(slice) => slice[..self.end]
200-
.get_mut(self.idx)
201-
.map(|t| unsafe { ManuallyDrop::take(t) }),
215+
let term = match &mut self.inner {
216+
IntoIterInner::Shared(inner) => inner.get(self.idx).cloned(),
217+
IntoIterInner::Owned(inner) => unsafe {
218+
Rc::get_mut(inner)
219+
.expect("non-unique Rc after checking for uniqueness")
220+
.get_mut(self.idx)
221+
.map(|t| ManuallyDrop::take(t))
222+
},
202223
};
203224

204225
self.idx += 1;
@@ -207,6 +228,18 @@ impl Iterator for IntoIter {
207228
}
208229
}
209230

231+
impl Drop for IntoIter {
232+
fn drop(&mut self) {
233+
// drop the rest of the items we haven't iterated through
234+
if let IntoIterInner::Owned(inner) = &mut self.inner {
235+
let inner = Rc::get_mut(inner).expect("non-unique Rc after checking for uniqueness");
236+
for term in &mut inner[self.idx..self.end] {
237+
unsafe { ManuallyDrop::drop(term) }
238+
}
239+
}
240+
}
241+
}
242+
210243
impl ExactSizeIterator for IntoIter {
211244
fn len(&self) -> usize {
212245
self.end - self.idx

0 commit comments

Comments
 (0)