facet_core/impls_core/
option.rs

1use core::{cmp::Ordering, hash::Hash, mem::MaybeUninit, ptr::NonNull};
2
3use crate::{
4    Def, EnumRepr, EnumType, Facet, Field, FieldFlags, OptionDef, OptionVTable, PtrConst, PtrMut,
5    PtrUninit, Repr, Shape, StructKind, StructType, TryBorrowInnerError, TryFromError,
6    TryIntoInnerError, Type, TypedPtrUninit, UserType, VTableView, Variant, shape_util,
7    value_vtable,
8};
9unsafe impl<'a, T: Facet<'a>> Facet<'a> for Option<T> {
10    const SHAPE: &'static Shape = &const {
11        Shape::builder_for_sized::<Self>()
12            .vtable({
13                // Define the functions for transparent conversion between Option<T> and T
14                unsafe fn try_from<'a, 'src, 'dst, T: Facet<'a>>(
15                    src_ptr: PtrConst<'src>,
16                    src_shape: &'static Shape,
17                    dst: PtrUninit<'dst>,
18                ) -> Result<PtrMut<'dst>, TryFromError> {
19                    if src_shape.id != T::SHAPE.id {
20                        return Err(TryFromError::UnsupportedSourceShape {
21                            src_shape,
22                            expected: &[T::SHAPE],
23                        });
24                    }
25                    let t = unsafe { src_ptr.read::<T>() };
26                    let option = Some(t);
27                    Ok(unsafe { dst.put(option) })
28                }
29
30                unsafe fn try_into_inner<'a, 'src, 'dst, T: Facet<'a>>(
31                    src_ptr: PtrMut<'src>,
32                    dst: PtrUninit<'dst>,
33                ) -> Result<PtrMut<'dst>, TryIntoInnerError> {
34                    let option = unsafe { src_ptr.read::<Option<T>>() };
35                    match option {
36                        Some(t) => Ok(unsafe { dst.put(t) }),
37                        None => Err(TryIntoInnerError::Unavailable),
38                    }
39                }
40
41                unsafe fn try_borrow_inner<'a, 'src, T: Facet<'a>>(
42                    src_ptr: PtrConst<'src>,
43                ) -> Result<PtrConst<'src>, TryBorrowInnerError> {
44                    let option = unsafe { src_ptr.get::<Option<T>>() };
45                    match option {
46                        Some(t) => Ok(PtrConst::new(NonNull::from(t))),
47                        None => Err(TryBorrowInnerError::Unavailable),
48                    }
49                }
50
51                let mut vtable = value_vtable!(core::option::Option<T>, |f, opts| {
52                    write!(f, "{}", Self::SHAPE.type_identifier)?;
53                    if let Some(opts) = opts.for_children() {
54                        write!(f, "<")?;
55                        (T::SHAPE.vtable.type_name())(f, opts)?;
56                        write!(f, ">")?;
57                    } else {
58                        write!(f, "<…>")?;
59                    }
60                    Ok(())
61                });
62
63                {
64                    let vtable_sized = &mut vtable;
65                    vtable_sized.debug = if T::SHAPE.is_debug() {
66                        Some(|this, f| {
67                            let this = unsafe { this.get::<Self>() };
68                            if let Some(value) = &this {
69                                f.debug_tuple("Some")
70                                    .field(&shape_util::Debug {
71                                        ptr: PtrConst::new(value.into()),
72                                        f: T::SHAPE.vtable.debug.unwrap(),
73                                    })
74                                    .finish()
75                            } else {
76                                write!(f, "None")
77                            }
78                        })
79                    } else {
80                        None
81                    };
82
83                    vtable_sized.hash = if T::SHAPE.is_hash() {
84                        Some(|this, hasher| unsafe {
85                            let this = this.get::<Self>();
86                            this.as_ref()
87                                .map(|this| shape_util::Hash {
88                                    ptr: PtrConst::new(this.into()),
89                                    f: T::SHAPE.vtable.hash.unwrap(),
90                                })
91                                .hash(&mut { hasher });
92                        })
93                    } else {
94                        None
95                    };
96
97                    vtable_sized.partial_eq = if T::SHAPE.is_partial_eq() {
98                        Some(|a, b| unsafe {
99                            let a = a.get::<Self>();
100                            let b = b.get::<Self>();
101                            match (a, b) {
102                                (None, None) => true,
103                                (Some(a), Some(b)) => T::SHAPE.vtable.partial_eq.unwrap()(
104                                    PtrConst::new(a.into()),
105                                    PtrConst::new(b.into()),
106                                ),
107                                _ => false,
108                            }
109                        })
110                    } else {
111                        None
112                    };
113
114                    vtable_sized.partial_ord = if T::SHAPE.is_partial_ord() {
115                        Some(|a, b| unsafe {
116                            let a = a.get::<Self>();
117                            let b = b.get::<Self>();
118                            match (a, b) {
119                                (None, None) => Some(Ordering::Equal),
120                                (None, Some(_)) => Some(Ordering::Less),
121                                (Some(_), None) => Some(Ordering::Greater),
122                                (Some(a), Some(b)) => T::SHAPE.vtable.partial_ord.unwrap()(
123                                    PtrConst::new(a.into()),
124                                    PtrConst::new(b.into()),
125                                ),
126                            }
127                        })
128                    } else {
129                        None
130                    };
131
132                    vtable_sized.ord = if T::SHAPE.is_ord() {
133                        Some(|a, b| unsafe {
134                            let a = a.get::<Self>();
135                            let b = b.get::<Self>();
136                            match (a, b) {
137                                (None, None) => Ordering::Equal,
138                                (None, Some(_)) => Ordering::Less,
139                                (Some(_), None) => Ordering::Greater,
140                                (Some(a), Some(b)) => T::SHAPE.vtable.ord.unwrap()(
141                                    PtrConst::new(a.into()),
142                                    PtrConst::new(b.into()),
143                                ),
144                            }
145                        })
146                    } else {
147                        None
148                    };
149
150                    vtable_sized.parse = {
151                        if T::SHAPE.is_from_str() {
152                            Some(|str, target| {
153                                let mut t = MaybeUninit::<T>::uninit();
154                                let parse = <VTableView<T>>::of().parse().unwrap();
155                                let _res = (parse)(
156                                    str,
157                                    TypedPtrUninit::new(NonNull::from(&mut t).cast()),
158                                )?;
159                                // res points to t so we can't drop it yet. the option is not initialized though
160                                unsafe {
161                                    target.put(Some(t.assume_init()));
162                                    Ok(target.assume_init())
163                                }
164                            })
165                        } else {
166                            None
167                        }
168                    };
169
170                    vtable_sized.try_from = Some(try_from::<T>);
171                    vtable_sized.try_into_inner = Some(try_into_inner::<T>);
172                    vtable_sized.try_borrow_inner = Some(try_borrow_inner::<T>);
173                }
174
175                vtable
176            })
177            .type_identifier("Option")
178            .type_params(&[crate::TypeParam {
179                name: "T",
180                shape: T::SHAPE,
181            }])
182            .ty(Type::User(
183                // Null-Pointer-Optimization - we verify that this Option variant has no
184                // discriminant.
185                //
186                // See: https://doc.rust-lang.org/std/option/index.html#representation
187                if core::mem::size_of::<T>() == core::mem::size_of::<Option<T>>()
188                    && core::mem::size_of::<T>() <= core::mem::size_of::<usize>()
189                {
190                    UserType::Enum(EnumType {
191                        repr: Repr::default(),
192                        enum_repr: EnumRepr::RustNPO,
193                        variants: &const {
194                            [
195                                Variant::builder()
196                                    .name("None")
197                                    .discriminant(0)
198                                    .data(
199                                        StructType::builder()
200                                            .repr(Repr::default())
201                                            .kind(StructKind::Unit)
202                                            .build(),
203                                    )
204                                    .build(),
205                                Variant::builder()
206                                    .name("Some")
207                                    .discriminant(0)
208                                    .data(
209                                        StructType::builder()
210                                            .repr(Repr::default())
211                                            .kind(StructKind::TupleStruct)
212                                            .fields(
213                                                &const {
214                                                    [Field::builder()
215                                                        .name("0")
216                                                        .shape(|| T::SHAPE)
217                                                        .offset(0)
218                                                        .flags(FieldFlags::EMPTY)
219                                                        .build()]
220                                                },
221                                            )
222                                            .build(),
223                                    )
224                                    .build(),
225                            ]
226                        },
227                    })
228                } else {
229                    UserType::Opaque
230                },
231            ))
232            .def(Def::Option(
233                OptionDef::builder()
234                    .t(T::SHAPE)
235                    .vtable(
236                        const {
237                            &OptionVTable::builder()
238                                .is_some(|option| unsafe { option.get::<Option<T>>().is_some() })
239                                .get_value(|option| unsafe {
240                                    option
241                                        .get::<Option<T>>()
242                                        .as_ref()
243                                        .map(|t| PtrConst::new(NonNull::from(t)))
244                                })
245                                .init_some(|option, value| unsafe {
246                                    option.put(Option::Some(value.read::<T>()))
247                                })
248                                .init_none(|option| unsafe { option.put(<Option<T>>::None) })
249                                .replace_with(|option, value| unsafe {
250                                    let option = option.as_mut::<Option<T>>();
251                                    match value {
252                                        Some(value) => option.replace(value.read::<T>()),
253                                        None => option.take(),
254                                    };
255                                })
256                                .build()
257                        },
258                    )
259                    .build(),
260            ))
261            .inner(T::SHAPE)
262            .build()
263    };
264}