Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer may_dangle on type parameters of Punctuated iterator Drop impls #1247

Merged
merged 2 commits into from Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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;
}
}
}