Skip to content

Commit 220733c

Browse files
Radvendiiyannham
andauthored
correctly drop Array::IntoIter (#1773)
* correctly drop Array::IntoIter If you called Array::into_iter() you could leak elements in two circumstances: 1. If you did not iterate through all elements before dropping, since they had been converted to `ManuallyDrop` 2. If the Array had multiple references when the iterator was created, but the other references were dropped before the iterator was dropped, then the existence of the iterator would prevent those Rcs from dropping their contents, but then because the iterator is of type ManuallyDrop, it wouldn't drop those elements either * eta reduction Co-authored-by: Yann Hamdaoui <[email protected]> * update comments and fix eta-reduction error --------- Co-authored-by: Yann Hamdaoui <[email protected]>
1 parent 266f609 commit 220733c

File tree

1 file changed

+52
-18
lines changed

1 file changed

+52
-18
lines changed

core/src/term/array.rs

+52-18
Original file line numberDiff line numberDiff line change
@@ -157,29 +157,45 @@ 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.
161162

162-
unsafe {
163-
let mut inner: Rc<[ManuallyDrop<RichTerm>]> = transmute(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] {
163+
let inner = if Rc::strong_count(&self.inner) != 1 || Rc::weak_count(&self.inner) != 0 {
164+
IntoIterInner::Shared(self.inner)
165+
} else {
166+
unsafe {
167+
let mut inner =
168+
transmute::<Rc<[RichTerm]>, Rc<[ManuallyDrop<RichTerm>]>>(self.inner);
169+
let slice =
170+
Rc::get_mut(&mut inner).expect("non-unique Rc after checking for uniqueness");
171+
for term in &mut slice[..self.start] {
169172
ManuallyDrop::drop(term)
170173
}
171-
for term in &mut slice[end..] {
174+
for term in &mut slice[self.end..] {
172175
ManuallyDrop::drop(term)
173176
}
174-
}
175177

176-
IntoIter { inner, idx, end }
178+
IntoIterInner::Owned(inner)
179+
}
180+
};
181+
IntoIter {
182+
inner,
183+
idx: self.start,
184+
end: self.end,
177185
}
178186
}
179187
}
180188

189+
enum IntoIterInner {
190+
Shared(Rc<[RichTerm]>),
191+
// This shouldn't really be an Rc. It should only ever have one reference.
192+
// There may be a way to rewrite this once unsized locals are stabilized.
193+
// But for now, there is no good way to repackage the inner array, so we
194+
// keep it in an Rc.
195+
Owned(Rc<[ManuallyDrop<RichTerm>]>),
196+
}
181197
pub struct IntoIter {
182-
inner: Rc<[ManuallyDrop<RichTerm>]>,
198+
inner: IntoIterInner,
183199
idx: usize,
184200
end: usize,
185201
}
@@ -191,22 +207,40 @@ impl Iterator for IntoIter {
191207
if self.idx == self.end {
192208
None
193209
} 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]
210+
let term = match &mut self.inner {
211+
IntoIterInner::Shared(inner) => inner.get(self.idx).cloned(),
212+
IntoIterInner::Owned(inner) => Rc::get_mut(inner)
213+
.expect("non-unique Rc after checking for uniqueness")
200214
.get_mut(self.idx)
215+
// SAFETY: We already checcked that we have the only
216+
// reference to inner, and after this we increment idx, and
217+
// we never access elements with indexes less than idx
201218
.map(|t| unsafe { ManuallyDrop::take(t) }),
202219
};
203-
204220
self.idx += 1;
205221
term
206222
}
207223
}
208224
}
209225

226+
impl Drop for IntoIter {
227+
fn drop(&mut self) {
228+
// drop the rest of the items we haven't iterated through
229+
if let IntoIterInner::Owned(inner) = &mut self.inner {
230+
let inner = Rc::get_mut(inner).expect("non-unique Rc after checking for uniqueness");
231+
for term in &mut inner[self.idx..self.end] {
232+
// SAFETY: Wwe already checked that we have the only reference
233+
// to inner, and once we are done dropping the remaining
234+
// elements, `inner` will get dropped and there will be no
235+
// remaining references.
236+
unsafe {
237+
let _ = ManuallyDrop::take(term);
238+
}
239+
}
240+
}
241+
}
242+
}
243+
210244
impl ExactSizeIterator for IntoIter {
211245
fn len(&self) -> usize {
212246
self.end - self.idx

0 commit comments

Comments
 (0)