mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-01-09 22:42:44 +00:00
170 lines
4.3 KiB
Rust
170 lines
4.3 KiB
Rust
#![warn(rust_2018_idioms, single_use_lifetimes)]
|
|
|
|
// Refs: https://doc.rust-lang.org/reference/destructors.html
|
|
|
|
use std::{cell::Cell, panic, pin::Pin, thread};
|
|
|
|
use pin_project_lite::pin_project;
|
|
|
|
struct D<'a>(&'a Cell<usize>, usize);
|
|
|
|
impl Drop for D<'_> {
|
|
fn drop(&mut self) {
|
|
if !thread::panicking() {
|
|
let old = self.0.replace(self.1);
|
|
assert_eq!(old, self.1 - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
pin_project! {
|
|
#[project = StructPinnedProj]
|
|
#[project_ref = StructPinnedProjRef]
|
|
#[project_replace = StructPinnedProjReplace]
|
|
struct StructPinned<'a> {
|
|
#[pin]
|
|
f1: D<'a>,
|
|
#[pin]
|
|
f2: D<'a>,
|
|
}
|
|
}
|
|
|
|
pin_project! {
|
|
#[project = StructUnpinnedProj]
|
|
#[project_ref = StructUnpinnedProjRef]
|
|
#[project_replace = StructUnpinnedProjReplace]
|
|
struct StructUnpinned<'a> {
|
|
f1: D<'a>,
|
|
f2: D<'a>,
|
|
}
|
|
}
|
|
|
|
pin_project! {
|
|
#[project_replace = EnumProjReplace]
|
|
enum Enum<'a> {
|
|
#[allow(dead_code)] // false positive that fixed in Rust 1.38
|
|
StructPinned {
|
|
#[pin]
|
|
f1: D<'a>,
|
|
#[pin]
|
|
f2: D<'a>,
|
|
},
|
|
#[allow(dead_code)] // false positive that fixed in Rust 1.38
|
|
StructUnpinned {
|
|
f1: D<'a>,
|
|
f2: D<'a>,
|
|
},
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn struct_pinned() {
|
|
{
|
|
let c = Cell::new(0);
|
|
let _x = StructPinned { f1: D(&c, 1), f2: D(&c, 2) };
|
|
}
|
|
{
|
|
let c = Cell::new(0);
|
|
let mut x = StructPinned { f1: D(&c, 1), f2: D(&c, 2) };
|
|
let y = Pin::new(&mut x);
|
|
let _z = y.project_replace(StructPinned { f1: D(&c, 3), f2: D(&c, 4) });
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn struct_unpinned() {
|
|
{
|
|
let c = Cell::new(0);
|
|
let _x = StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) };
|
|
}
|
|
{
|
|
let c = Cell::new(0);
|
|
let mut x = StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) };
|
|
let y = Pin::new(&mut x);
|
|
let _z = y.project_replace(StructUnpinned { f1: D(&c, 3), f2: D(&c, 4) });
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn enum_struct() {
|
|
{
|
|
let c = Cell::new(0);
|
|
let _x = Enum::StructPinned { f1: D(&c, 1), f2: D(&c, 2) };
|
|
}
|
|
{
|
|
let c = Cell::new(0);
|
|
let mut x = Enum::StructPinned { f1: D(&c, 1), f2: D(&c, 2) };
|
|
let y = Pin::new(&mut x);
|
|
let _z = y.project_replace(Enum::StructPinned { f1: D(&c, 3), f2: D(&c, 4) });
|
|
}
|
|
|
|
{
|
|
let c = Cell::new(0);
|
|
let _x = Enum::StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) };
|
|
}
|
|
{
|
|
let c = Cell::new(0);
|
|
let mut x = Enum::StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) };
|
|
let y = Pin::new(&mut x);
|
|
let _z = y.project_replace(Enum::StructUnpinned { f1: D(&c, 3), f2: D(&c, 4) });
|
|
}
|
|
}
|
|
|
|
// https://github.com/rust-lang/rust/issues/47949
|
|
// https://github.com/taiki-e/pin-project/pull/194#discussion_r419098111
|
|
#[allow(clippy::many_single_char_names)]
|
|
#[test]
|
|
fn project_replace_panic() {
|
|
pin_project! {
|
|
#[project_replace = SProjReplace]
|
|
struct S<T, U> {
|
|
#[pin]
|
|
pinned: T,
|
|
unpinned: U,
|
|
}
|
|
}
|
|
|
|
struct D<'a>(&'a mut bool, bool);
|
|
impl Drop for D<'_> {
|
|
fn drop(&mut self) {
|
|
*self.0 = true;
|
|
if self.1 {
|
|
panic!();
|
|
}
|
|
}
|
|
}
|
|
|
|
let (mut a, mut b, mut c, mut d) = (false, false, false, false);
|
|
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
|
let mut x = S { pinned: D(&mut a, true), unpinned: D(&mut b, false) };
|
|
let _y = Pin::new(&mut x)
|
|
.project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) });
|
|
// Previous `x.pinned` was dropped and panicked when `project_replace` is
|
|
// called, so this is unreachable.
|
|
unreachable!();
|
|
}));
|
|
assert!(res.is_err());
|
|
assert!(a);
|
|
assert!(b);
|
|
assert!(c);
|
|
assert!(d);
|
|
|
|
let (mut a, mut b, mut c, mut d) = (false, false, false, false);
|
|
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
|
let mut x = S { pinned: D(&mut a, false), unpinned: D(&mut b, true) };
|
|
{
|
|
let _y = Pin::new(&mut x)
|
|
.project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) });
|
|
// `_y` (previous `x.unpinned`) live to the end of this scope, so
|
|
// this is not unreachable.
|
|
// unreachable!();
|
|
}
|
|
unreachable!();
|
|
}));
|
|
assert!(res.is_err());
|
|
assert!(a);
|
|
assert!(b);
|
|
assert!(c);
|
|
assert!(d);
|
|
}
|