Skip to content

Commit 15cc12f

Browse files
committed
tests: internal: add tests for tuple struct support
Add initial tests to validate the basic functionality of tuple struct support. Mainly, focusing on init syntax and pin data. Tests include: - `tests/tuple_struct.rs` - `tests/ui/compile-fail/tuple_{duplicate,invalid,missing}_field.rs` - `tests/ui/compile-fail/no_tuple_shorthand.rs` - `tests/ui/expand/tuple_struct.rs` Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
1 parent 3788d0f commit 15cc12f

11 files changed

+334
-0
lines changed

tests/tuple_struct.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))]
2+
#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))]
3+
#![cfg_attr(feature = "alloc", feature(allocator_api))]
4+
5+
use core::ptr;
6+
7+
use pin_init::*;
8+
9+
#[pin_data]
10+
struct TupleStruct(#[pin] i32, i32);
11+
12+
fn init_i32(value: i32) -> impl PinInit<i32> {
13+
// SAFETY: The closure always initializes `slot` with a valid `i32` value.
14+
unsafe {
15+
pin_init_from_closure(move |slot| {
16+
// SAFETY: `slot` is provided by the initialization framework and valid for write.
17+
ptr::write(slot, value);
18+
Ok(())
19+
})
20+
}
21+
}
22+
23+
#[test]
24+
fn tuple_struct_values() {
25+
stack_pin_init!(let foo = pin_init!(TupleStruct { 0: 42, 1: 24 }));
26+
assert_eq!(foo.as_ref().get_ref().0, 42);
27+
assert_eq!(foo.as_ref().get_ref().1, 24);
28+
}
29+
30+
#[test]
31+
#[allow(clippy::redundant_locals)]
32+
fn tuple_struct_init_arrow_and_projection() {
33+
stack_pin_init!(let foo = pin_init!(TupleStruct { 0 <- init_i32(7), 1: 13 }));
34+
let mut foo = foo;
35+
let projected = foo.as_mut().project();
36+
assert_eq!(*projected._0.as_ref().get_ref(), 7);
37+
assert_eq!(*projected._1, 13);
38+
}
39+
40+
#[test]
41+
fn tuple_struct_constructor_form() {
42+
stack_pin_init!(let foo = pin_init!(TupleStruct(11, 29)));
43+
assert_eq!(foo.as_ref().get_ref().0, 11);
44+
assert_eq!(foo.as_ref().get_ref().1, 29);
45+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use pin_init::*;
2+
3+
#[pin_data]
4+
struct Tuple(#[pin] i32, i32);
5+
6+
fn main() {
7+
let _ = pin_init!(Tuple { 0, 1: 24 });
8+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: expected `<-` or `:`
2+
--> tests/ui/compile-fail/init/no_tuple_shorthand.rs:7:32
3+
|
4+
7 | let _ = pin_init!(Tuple { 0, 1: 24 });
5+
| ^
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use pin_init::*;
2+
3+
#[pin_data]
4+
struct Tuple(#[pin] i32, i32);
5+
6+
fn main() {
7+
let _ = pin_init!(Tuple { 0: 1, 0: 2, 1: 3 });
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error[E0062]: field `0` specified more than once
2+
--> tests/ui/compile-fail/init/tuple_duplicate_field.rs:7:37
3+
|
4+
7 | let _ = pin_init!(Tuple { 0: 1, 0: 2, 1: 3 });
5+
| ------------------------^------------
6+
| | |
7+
| | used more than once
8+
| first use of `0`
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use pin_init::*;
2+
3+
#[pin_data]
4+
struct Tuple(#[pin] i32, i32);
5+
6+
fn main() {
7+
let _ = pin_init!(Tuple { 0: 1, 1: 2, 2: 3 });
8+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error[E0609]: no field `2` on type `Tuple`
2+
--> tests/ui/compile-fail/init/tuple_invalid_field.rs:7:43
3+
|
4+
7 | let _ = pin_init!(Tuple { 0: 1, 1: 2, 2: 3 });
5+
| ^ unknown field
6+
|
7+
= note: available fields are: `0`, `1`
8+
9+
error[E0599]: no method named `__project_2` found for struct `__ThePinData` in the current scope
10+
--> tests/ui/compile-fail/init/tuple_invalid_field.rs:7:13
11+
|
12+
3 | #[pin_data]
13+
| ----------- method `__project_2` not found for this struct
14+
...
15+
7 | let _ = pin_init!(Tuple { 0: 1, 1: 2, 2: 3 });
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
17+
|
18+
= note: this error originates in the macro `pin_init` (in Nightly builds, run with -Z macro-backtrace for more info)
19+
20+
error[E0560]: struct `Tuple` has no field named `2`
21+
--> tests/ui/compile-fail/init/tuple_invalid_field.rs:7:43
22+
|
23+
4 | struct Tuple(#[pin] i32, i32);
24+
| ----- `Tuple` defined here
25+
...
26+
7 | let _ = pin_init!(Tuple { 0: 1, 1: 2, 2: 3 });
27+
| ^ field does not exist
28+
|
29+
help: `Tuple` is a tuple struct, use the appropriate syntax
30+
|
31+
7 - let _ = pin_init!(Tuple { 0: 1, 1: 2, 2: 3 });
32+
7 + let _ = Tuple(/* i32 */, /* i32 */);
33+
|
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use pin_init::*;
2+
3+
#[pin_data]
4+
struct Tuple(#[pin] i32, i32);
5+
6+
fn main() {
7+
let _ = pin_init!(Tuple { 0: 1 });
8+
let _ = init!(Tuple { 0: 1 });
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0063]: missing field `1` in initializer of `Tuple`
2+
--> tests/ui/compile-fail/init/tuple_missing_field.rs:7:23
3+
|
4+
7 | let _ = pin_init!(Tuple { 0: 1 });
5+
| ^^^^^ missing `1`
6+
7+
error[E0063]: missing field `1` in initializer of `Tuple`
8+
--> tests/ui/compile-fail/init/tuple_missing_field.rs:8:19
9+
|
10+
8 | let _ = init!(Tuple { 0: 1 });
11+
| ^^^^^ missing `1`
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
use pin_init::*;
2+
struct Foo([u8; 3], i32);
3+
/// Pin-projections of [`Foo`]
4+
#[allow(dead_code)]
5+
#[doc(hidden)]
6+
struct FooProjection<'__pin> {
7+
_0: &'__pin mut [u8; 3],
8+
_1: ::core::pin::Pin<&'__pin mut i32>,
9+
___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
10+
}
11+
impl Foo {
12+
/// Pin-projects all fields of `Self`.
13+
///
14+
/// These fields are structurally pinned:
15+
/// - index `1`
16+
///
17+
/// These fields are **not** structurally pinned:
18+
/// - index `0`
19+
#[inline]
20+
fn project<'__pin>(
21+
self: ::core::pin::Pin<&'__pin mut Self>,
22+
) -> FooProjection<'__pin> {
23+
let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
24+
FooProjection {
25+
_0: &mut this.0,
26+
_1: unsafe { ::core::pin::Pin::new_unchecked(&mut this.1) },
27+
___pin_phantom_data: ::core::marker::PhantomData,
28+
}
29+
}
30+
}
31+
const _: () = {
32+
#[doc(hidden)]
33+
struct __ThePinData {
34+
__phantom: ::core::marker::PhantomData<fn(Foo) -> Foo>,
35+
}
36+
impl ::core::clone::Clone for __ThePinData {
37+
fn clone(&self) -> Self {
38+
*self
39+
}
40+
}
41+
impl ::core::marker::Copy for __ThePinData {}
42+
#[allow(dead_code)]
43+
#[expect(clippy::missing_safety_doc)]
44+
impl __ThePinData {
45+
/// # Safety
46+
///
47+
/// - `slot` is a valid pointer to uninitialized memory.
48+
/// - the caller does not touch `slot` when `Err` is returned, they are only permitted
49+
/// to deallocate.
50+
unsafe fn _0<E>(
51+
self,
52+
slot: *mut [u8; 3],
53+
init: impl ::pin_init::Init<[u8; 3], E>,
54+
) -> ::core::result::Result<(), E> {
55+
unsafe { ::pin_init::Init::__init(init, slot) }
56+
}
57+
/// # Safety
58+
///
59+
/// `slot` points at the field index `0` inside of `Foo`, which is pinned.
60+
unsafe fn __project_0<'__slot>(
61+
self,
62+
slot: &'__slot mut [u8; 3],
63+
) -> &'__slot mut [u8; 3] {
64+
slot
65+
}
66+
/// # Safety
67+
///
68+
/// - `slot` is a valid pointer to uninitialized memory.
69+
/// - the caller does not touch `slot` when `Err` is returned, they are only permitted
70+
/// to deallocate.
71+
/// - `slot` will not move until it is dropped, i.e. it will be pinned.
72+
unsafe fn _1<E>(
73+
self,
74+
slot: *mut i32,
75+
init: impl ::pin_init::PinInit<i32, E>,
76+
) -> ::core::result::Result<(), E> {
77+
unsafe { ::pin_init::PinInit::__pinned_init(init, slot) }
78+
}
79+
/// # Safety
80+
///
81+
/// `slot` points at the field index `1` inside of `Foo`, which is pinned.
82+
unsafe fn __project_1<'__slot>(
83+
self,
84+
slot: &'__slot mut i32,
85+
) -> ::core::pin::Pin<&'__slot mut i32> {
86+
unsafe { ::core::pin::Pin::new_unchecked(slot) }
87+
}
88+
}
89+
unsafe impl ::pin_init::__internal::HasPinData for Foo {
90+
type PinData = __ThePinData;
91+
unsafe fn __pin_data() -> Self::PinData {
92+
__ThePinData {
93+
__phantom: ::core::marker::PhantomData,
94+
}
95+
}
96+
}
97+
unsafe impl ::pin_init::__internal::PinData for __ThePinData {
98+
type Datee = Foo;
99+
}
100+
#[allow(dead_code)]
101+
struct __Unpin<'__pin> {
102+
__phantom_pin: ::core::marker::PhantomData<fn(&'__pin ()) -> &'__pin ()>,
103+
__phantom: ::core::marker::PhantomData<fn(Foo) -> Foo>,
104+
_1: i32,
105+
}
106+
#[doc(hidden)]
107+
impl<'__pin> ::core::marker::Unpin for Foo
108+
where
109+
__Unpin<'__pin>: ::core::marker::Unpin,
110+
{}
111+
trait MustNotImplDrop {}
112+
#[expect(drop_bounds)]
113+
impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}
114+
impl MustNotImplDrop for Foo {}
115+
#[expect(non_camel_case_types)]
116+
trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
117+
impl<
118+
T: ::pin_init::PinnedDrop + ?::core::marker::Sized,
119+
> UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
120+
impl UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for Foo {}
121+
};
122+
fn main() {
123+
let _ = {
124+
struct __InitOk;
125+
let __data = unsafe {
126+
use ::pin_init::__internal::HasInitData;
127+
Foo::__init_data()
128+
};
129+
let init = ::pin_init::__internal::InitData::make_closure::<
130+
_,
131+
__InitOk,
132+
::core::convert::Infallible,
133+
>(
134+
__data,
135+
move |slot| {
136+
{
137+
struct __InitOk;
138+
{
139+
#[allow(clippy::just_underscores_and_digits)]
140+
let _0 = [1, 2, 3];
141+
unsafe { ::core::ptr::write(&raw mut (*slot).0, _0) };
142+
}
143+
#[allow(unused_variables)]
144+
#[allow(clippy::just_underscores_and_digits)]
145+
let _0 = unsafe { &mut (*slot).0 };
146+
let ___0_guard = unsafe {
147+
::pin_init::__internal::DropGuard::new(&raw mut (*slot).0)
148+
};
149+
{
150+
let init = 42;
151+
unsafe { ::pin_init::Init::__init(init, &raw mut (*slot).1)? };
152+
}
153+
#[allow(unused_variables)]
154+
#[allow(clippy::just_underscores_and_digits)]
155+
let _1 = unsafe { &mut (*slot).1 };
156+
let ___1_guard = unsafe {
157+
::pin_init::__internal::DropGuard::new(&raw mut (*slot).1)
158+
};
159+
::core::mem::forget(___0_guard);
160+
::core::mem::forget(___1_guard);
161+
#[allow(unreachable_code, clippy::diverging_sub_expression)]
162+
let _ = || unsafe {
163+
::core::ptr::write(
164+
slot,
165+
Foo {
166+
0: ::core::panicking::panic("explicit panic"),
167+
1: ::core::panicking::panic("explicit panic"),
168+
},
169+
)
170+
};
171+
}
172+
Ok(__InitOk)
173+
},
174+
);
175+
let init = move |
176+
slot,
177+
| -> ::core::result::Result<(), ::core::convert::Infallible> {
178+
init(slot).map(|__InitOk| ())
179+
};
180+
let init = unsafe {
181+
::pin_init::init_from_closure::<_, ::core::convert::Infallible>(init)
182+
};
183+
#[allow(
184+
clippy::let_and_return,
185+
reason = "some clippy versions warn about the let binding"
186+
)] init
187+
};
188+
}

0 commit comments

Comments
 (0)