Skip to content

Commit

Permalink
Merge pull request #1247 from dtolnay/punctdrop
Browse files Browse the repository at this point in the history
Infer may_dangle on type parameters of Punctuated iterator Drop impls
  • Loading branch information
dtolnay committed Dec 1, 2022
2 parents 17f9a5c + 9113ad0 commit 02e2a21
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 12 deletions.
58 changes: 58 additions & 0 deletions src/drops.rs
@@ -0,0 +1,58 @@
use std::iter;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
use std::option;
use std::slice;

#[repr(transparent)]
pub(crate) struct NoDrop<T: ?Sized>(ManuallyDrop<T>);

impl<T> NoDrop<T> {
pub(crate) fn new(value: T) -> Self
where
T: TrivialDrop,
{
NoDrop(ManuallyDrop::new(value))
}
}

impl<T: ?Sized> Deref for NoDrop<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T: ?Sized> DerefMut for NoDrop<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

pub(crate) trait TrivialDrop {}

impl<T> TrivialDrop for iter::Empty<T> {}
impl<'a, T> TrivialDrop for slice::Iter<'a, T> {}
impl<'a, T> TrivialDrop for slice::IterMut<'a, T> {}
impl<'a, T> TrivialDrop for option::IntoIter<&'a T> {}
impl<'a, T> TrivialDrop for option::IntoIter<&'a mut T> {}

#[test]
fn test_needs_drop() {
use std::mem::needs_drop;

struct NeedsDrop;

impl Drop for NeedsDrop {
fn drop(&mut self) {}
}

assert!(needs_drop::<NeedsDrop>());

// Test each of the types with a handwritten TrivialDrop impl above.
assert!(!needs_drop::<iter::Empty<NeedsDrop>>());
assert!(!needs_drop::<slice::Iter<NeedsDrop>>());
assert!(!needs_drop::<slice::IterMut<NeedsDrop>>());
assert!(!needs_drop::<option::IntoIter<&NeedsDrop>>());
assert!(!needs_drop::<option::IntoIter<&mut NeedsDrop>>());
}
1 change: 1 addition & 0 deletions src/lib.rs
Expand Up @@ -429,6 +429,7 @@ pub use crate::path::{
#[cfg(feature = "parsing")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
pub mod buffer;
mod drops;
#[cfg(feature = "parsing")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
pub mod ext;
Expand Down
43 changes: 31 additions & 12 deletions src/punctuated.rs
Expand Up @@ -32,6 +32,7 @@ use std::option;
use std::slice;
use std::vec;

use crate::drops::{NoDrop, TrivialDrop};
#[cfg(feature = "parsing")]
use crate::parse::{Parse, ParseStream, Result};
#[cfg(feature = "parsing")]
Expand Down Expand Up @@ -104,21 +105,21 @@ impl<T, P> Punctuated<T, P> {
/// Returns an iterator over borrowed syntax tree nodes of type `&T`.
pub fn iter(&self) -> Iter<T> {
Iter {
inner: Box::new(PrivateIter {
inner: Box::new(NoDrop::new(PrivateIter {
inner: self.inner.iter(),
last: self.last.as_ref().map(Box::as_ref).into_iter(),
}),
})),
}
}

/// Returns an iterator over mutably borrowed syntax tree nodes of type
/// `&mut T`.
pub fn iter_mut(&mut self) -> IterMut<T> {
IterMut {
inner: Box::new(PrivateIterMut {
inner: Box::new(NoDrop::new(PrivateIterMut {
inner: self.inner.iter_mut(),
last: self.last.as_mut().map(Box::as_mut).into_iter(),
}),
})),
}
}

Expand Down Expand Up @@ -721,24 +722,31 @@ pub struct Iter<'a, T: 'a> {
// The `Item = &'a T` needs to be specified to support rustc 1.31 and older.
// On modern compilers we would be able to write just IterTrait<'a, T> where
// Item can be inferred unambiguously from the supertrait.
inner: Box<dyn IterTrait<'a, T, Item = &'a T> + 'a>,
inner: Box<NoDrop<dyn IterTrait<'a, T, Item = &'a T> + 'a>>,
}

trait IterTrait<'a, T: 'a>:
DoubleEndedIterator<Item = &'a T> + ExactSizeIterator<Item = &'a T>
{
fn clone_box(&self) -> Box<dyn IterTrait<'a, T, Item = &'a T> + 'a>;
fn clone_box(&self) -> Box<NoDrop<dyn IterTrait<'a, T, Item = &'a T> + 'a>>;
}

struct PrivateIter<'a, T: 'a, P: 'a> {
inner: slice::Iter<'a, (T, P)>,
last: option::IntoIter<&'a T>,
}

impl<'a, T, P> TrivialDrop for PrivateIter<'a, T, P>
where
slice::Iter<'a, (T, P)>: TrivialDrop,
option::IntoIter<&'a T>: TrivialDrop,
{
}

#[cfg(any(feature = "full", feature = "derive"))]
pub(crate) fn empty_punctuated_iter<'a, T>() -> Iter<'a, T> {
Iter {
inner: Box::new(iter::empty()),
inner: Box::new(NoDrop::new(iter::empty())),
}
}

Expand Down Expand Up @@ -813,10 +821,14 @@ impl<'a, T, P> Clone for PrivateIter<'a, T, P> {
impl<'a, T, I> IterTrait<'a, T> for I
where
T: 'a,
I: DoubleEndedIterator<Item = &'a T> + ExactSizeIterator<Item = &'a T> + Clone + 'a,
I: DoubleEndedIterator<Item = &'a T>
+ ExactSizeIterator<Item = &'a T>
+ Clone
+ TrivialDrop
+ 'a,
{
fn clone_box(&self) -> Box<dyn IterTrait<'a, T, Item = &'a T> + 'a> {
Box::new(self.clone())
fn clone_box(&self) -> Box<NoDrop<dyn IterTrait<'a, T, Item = &'a T> + 'a>> {
Box::new(NoDrop::new(self.clone()))
}
}

Expand All @@ -826,7 +838,7 @@ where
///
/// [module documentation]: self
pub struct IterMut<'a, T: 'a> {
inner: Box<dyn IterMutTrait<'a, T, Item = &'a mut T> + 'a>,
inner: Box<NoDrop<dyn IterMutTrait<'a, T, Item = &'a mut T> + 'a>>,
}

trait IterMutTrait<'a, T: 'a>:
Expand All @@ -839,10 +851,17 @@ struct PrivateIterMut<'a, T: 'a, P: 'a> {
last: option::IntoIter<&'a mut T>,
}

impl<'a, T, P> TrivialDrop for PrivateIterMut<'a, T, P>
where
slice::IterMut<'a, (T, P)>: TrivialDrop,
option::IntoIter<&'a mut T>: TrivialDrop,
{
}

#[cfg(any(feature = "full", feature = "derive"))]
pub(crate) fn empty_punctuated_iter_mut<'a, T>() -> IterMut<'a, T> {
IterMut {
inner: Box::new(iter::empty()),
inner: Box::new(NoDrop::new(iter::empty())),
}
}

Expand Down
19 changes: 19 additions & 0 deletions tests/test_iterators.rs
Expand Up @@ -47,3 +47,22 @@ fn iter() {
assert_eq!(p.iter_mut().next_back(), Some(&mut 4));
assert_eq!(p.into_iter().next_back(), Some(4));
}

#[test]
fn may_dangle() {
let p: Punctuated<_, Token![,]> = punctuated!(2, 3, 4);
for element in &p {
if *element == 2 {
drop(p);
break;
}
}

let mut p: Punctuated<_, Token![,]> = punctuated!(2, 3, 4);
for element in &mut p {
if *element == 2 {
drop(p);
break;
}
}
}

0 comments on commit 02e2a21

Please sign in to comment.