Merge pull request #1247 from dtolnay/punctdrop

Infer may_dangle on type parameters of Punctuated iterator Drop impls
This commit is contained in:
David Tolnay 2022-11-30 23:44:48 -08:00 committed by GitHub
commit 02e2a2190b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 109 additions and 12 deletions

58
src/drops.rs Normal file
View File

@ -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>>());
}

View File

@ -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;

View File

@ -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")]
@ -104,10 +105,10 @@ 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(),
}),
})),
}
}
@ -115,10 +116,10 @@ impl<T, P> Punctuated<T, P> {
/// `&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(),
}),
})),
}
}
@ -721,13 +722,13 @@ 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> {
@ -735,10 +736,17 @@ struct PrivateIter<'a, T: 'a, P: 'a> {
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())),
}
}
@ -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()))
}
}
@ -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>:
@ -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())),
}
}

View File

@ -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;
}
}
}