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}