1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4/*!
5This crate expose the [`FieldOffsets`] derive macro and the types it uses.
6
7The macro allows to get const FieldOffset for member of a `#[repr(C)]` struct.
8
9The [`FieldOffset`] type is re-exported from the `field-offset` crate.
10*/
11#![no_std]
12
13#[cfg(test)]
14extern crate alloc;
15
16use core::pin::Pin;
17
18#[doc(inline)]
19pub use const_field_offset_macro::FieldOffsets;
20
21pub use field_offset::{AllowPin, FieldOffset, NotPinned};
22
23/// This trait needs to be implemented if you use the `#[pin_drop]` attribute. It enables
24/// you to implement Drop for your type safely.
25pub trait PinnedDrop {
26 /// This is the equivalent to the regular Drop trait with the difference that self
27 /// is pinned.
28 fn drop(self: Pin<&mut Self>);
29
30 #[doc(hidden)]
31 fn do_safe_pinned_drop(&mut self) {
32 let p: Pin<&mut Self> = unsafe { Pin::new_unchecked(self) };
33 p.drop()
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use crate as const_field_offset;
41 // ### Structures were change to repr(c) and to inherit FieldOffsets
42
43 // Example structures
44 #[derive(Debug, FieldOffsets)]
45 #[repr(C)]
46 struct Foo {
47 a: u32,
48 b: f64,
49 c: bool,
50 }
51
52 #[derive(Debug, FieldOffsets)]
53 #[repr(C)]
54 struct Bar {
55 x: u32,
56 y: Foo,
57 }
58
59 #[test]
60 #[allow(clippy::float_cmp)] // We want bit-wise equality here
61 fn test_simple() {
62 // Get a pointer to `b` within `Foo`
63 let foo_b = Foo::FIELD_OFFSETS.b;
64
65 // Construct an example `Foo`
66 let mut x = Foo { a: 1, b: 2.0, c: false };
67
68 // Apply the pointer to get at `b` and read it
69 {
70 let y = foo_b.apply(&x);
71 assert_eq!(*y, 2.0);
72 }
73
74 // Apply the pointer to get at `b` and mutate it
75 {
76 let y = foo_b.apply_mut(&mut x);
77 *y = 42.0;
78 }
79 assert_eq!(x.b, 42.0);
80 }
81
82 #[test]
83 #[allow(clippy::float_cmp)] // We want bit-wise equality here
84 fn test_nested() {
85 // Construct an example `Foo`
86 let mut x = Bar { x: 0, y: Foo { a: 1, b: 2.0, c: false } };
87
88 // Combine the pointer-to-members
89 let bar_y_b = Bar::FIELD_OFFSETS.y + Foo::FIELD_OFFSETS.b;
90
91 // Apply the pointer to get at `b` and mutate it
92 {
93 let y = bar_y_b.apply_mut(&mut x);
94 *y = 42.0;
95 }
96 assert_eq!(x.y.b, 42.0);
97 }
98
99 #[test]
100 #[allow(clippy::float_cmp)] // We want bit-wise equality here
101 fn test_pin() {
102 use ::alloc::boxed::Box;
103 // Get a pointer to `b` within `Foo`
104 let foo_b = Foo::FIELD_OFFSETS.b;
105 let foo_b_pin = unsafe { foo_b.as_pinned_projection() };
106 let foo_object = Box::pin(Foo { a: 21, b: 22.0, c: true });
107 let pb: Pin<&f64> = foo_b_pin.apply_pin(foo_object.as_ref());
108 assert_eq!(*pb, 22.0);
109
110 let mut x = Box::pin(Bar { x: 0, y: Foo { a: 1, b: 52.0, c: false } });
111 let bar_y_b = Bar::FIELD_OFFSETS.y + foo_b_pin;
112 assert_eq!(*bar_y_b.apply(&*x), 52.0);
113
114 let bar_y_pin = unsafe { Bar::FIELD_OFFSETS.y.as_pinned_projection() };
115 *(bar_y_pin + foo_b_pin).apply_pin_mut(x.as_mut()) = 12.;
116 assert_eq!(x.y.b, 12.0);
117 }
118}
119
120/**
121Test that one can't implement Unpin for pinned struct
122
123This should work:
124
125```
126#[derive(const_field_offset::FieldOffsets)]
127#[repr(C)]
128#[pin]
129struct MyStructPin { a: u32 }
130```
131
132But this not:
133
134```compile_fail
135#[derive(const_field_offset::FieldOffsets)]
136#[repr(C)]
137#[pin]
138struct MyStructPin { a: u32 }
139impl Unpin for MyStructPin {};
140```
141
142*/
143#[cfg(doctest)]
144const NO_IMPL_UNPIN: u32 = 0;
145
146#[doc(hidden)]
147#[cfg(feature = "field-offset-trait")]
148mod internal {
149 use super::*;
150 pub trait CombineFlag {
151 type Output;
152 }
153 impl CombineFlag for (AllowPin, AllowPin) {
154 type Output = AllowPin;
155 }
156 impl CombineFlag for (NotPinned, AllowPin) {
157 type Output = NotPinned;
158 }
159 impl CombineFlag for (AllowPin, NotPinned) {
160 type Output = NotPinned;
161 }
162 impl CombineFlag for (NotPinned, NotPinned) {
163 type Output = NotPinned;
164 }
165}
166
167#[cfg(feature = "field-offset-trait")]
168pub trait ConstFieldOffset: Copy {
169 /// The type of the container
170 type Container;
171 /// The type of the field
172 type Field;
173
174 /// Can be AllowPin or NotPinned
175 type PinFlag;
176
177 const OFFSET: FieldOffset<Self::Container, Self::Field, Self::PinFlag>;
178
179 fn as_field_offset(self) -> FieldOffset<Self::Container, Self::Field, Self::PinFlag> {
180 Self::OFFSET
181 }
182 fn get_byte_offset(self) -> usize {
183 Self::OFFSET.get_byte_offset()
184 }
185 fn apply(self, x: &Self::Container) -> &Self::Field {
186 Self::OFFSET.apply(x)
187 }
188 fn apply_mut(self, x: &mut Self::Container) -> &mut Self::Field {
189 Self::OFFSET.apply_mut(x)
190 }
191
192 fn apply_pin(self, x: Pin<&Self::Container>) -> Pin<&Self::Field>
193 where
194 Self: ConstFieldOffset<PinFlag = AllowPin>,
195 {
196 Self::OFFSET.apply_pin(x)
197 }
198 fn apply_pin_mut(self, x: Pin<&mut Self::Container>) -> Pin<&mut Self::Field>
199 where
200 Self: ConstFieldOffset<PinFlag = AllowPin>,
201 {
202 Self::OFFSET.apply_pin_mut(x)
203 }
204}
205
206/// This can be used to transmute a FieldOffset from a NotPinned to any pin flag.
207/// This is only valid if we know that the offset is actually valid for this Flag.
208#[cfg(feature = "field-offset-trait")]
209union TransmutePinFlag<Container, Field, PinFlag> {
210 x: FieldOffset<Container, Field, PinFlag>,
211 y: FieldOffset<Container, Field>,
212}
213
214/// Helper class used as the result of the addition of two types that implement the `ConstFieldOffset` trait
215#[derive(Copy, Clone)]
216#[cfg(feature = "field-offset-trait")]
217pub struct ConstFieldOffsetSum<A: ConstFieldOffset, B: ConstFieldOffset>(pub A, pub B);
218
219#[cfg(feature = "field-offset-trait")]
220impl<A: ConstFieldOffset, B: ConstFieldOffset> ConstFieldOffset for ConstFieldOffsetSum<A, B>
221where
222 A: ConstFieldOffset<Field = B::Container>,
223 (A::PinFlag, B::PinFlag): internal::CombineFlag,
224{
225 type Container = A::Container;
226 type Field = B::Field;
227 type PinFlag = <(A::PinFlag, B::PinFlag) as internal::CombineFlag>::Output;
228 const OFFSET: FieldOffset<Self::Container, Self::Field, Self::PinFlag> = unsafe {
229 TransmutePinFlag {
230 y: FieldOffset::new_from_offset(
231 A::OFFSET.get_byte_offset() + B::OFFSET.get_byte_offset(),
232 ),
233 }
234 .x
235 };
236}
237
238#[cfg(feature = "field-offset-trait")]
239impl<A: ConstFieldOffset, B: ConstFieldOffset, Other> ::core::ops::Add<Other>
240 for ConstFieldOffsetSum<A, B>
241where
242 Self: ConstFieldOffset,
243 Other: ConstFieldOffset<Container = <Self as ConstFieldOffset>::Field>,
244{
245 type Output = ConstFieldOffsetSum<Self, Other>;
246 fn add(self, other: Other) -> Self::Output {
247 ConstFieldOffsetSum(self, other)
248 }
249}
250