facet_core/impls/core/
option.rs

1//! Facet implementation for `Option<T>`
2
3use core::cmp::Ordering;
4
5use crate::{
6    Def, EnumRepr, EnumType, Facet, FieldBuilder, HashProxy, OptionDef, OptionVTable, OxPtrConst,
7    OxPtrMut, OxRef, PtrConst, Repr, Shape, ShapeBuilder, Type, TypeOpsIndirect, TypeParam,
8    UserType, VTableIndirect, VariantBuilder,
9};
10
11/// Extract the OptionDef from a shape, returns None if not an Option
12#[inline]
13fn get_option_def(shape: &'static Shape) -> Option<&'static OptionDef> {
14    match shape.def {
15        Def::Option(ref def) => Some(def),
16        _ => None,
17    }
18}
19
20/// Display for `Option<T>` - delegates to inner T's display if available
21unsafe fn option_display(
22    ox: OxPtrConst,
23    f: &mut core::fmt::Formatter<'_>,
24) -> Option<core::fmt::Result> {
25    let shape = ox.shape();
26    let def = get_option_def(shape)?;
27    let ptr = ox.ptr();
28
29    if unsafe { (def.vtable.is_some)(ptr) } {
30        // Get the inner value using the vtable
31        let inner_ptr = unsafe { (def.vtable.get_value)(ptr)? };
32        // Delegate to inner type's display
33        unsafe { def.t.call_display(inner_ptr, f) }
34    } else {
35        Some(f.write_str("None"))
36    }
37}
38
39/// Debug for `Option<T>` - delegates to inner T's debug if available
40unsafe fn option_debug(
41    ox: OxPtrConst,
42    f: &mut core::fmt::Formatter<'_>,
43) -> Option<core::fmt::Result> {
44    let shape = ox.shape();
45    let def = get_option_def(shape)?;
46    let ptr = ox.ptr();
47
48    if unsafe { (def.vtable.is_some)(ptr) } {
49        // Get the inner value using the vtable
50        // SAFETY: is_some returned true, so get_value returns a valid pointer.
51        // The caller guarantees the OxPtrConst points to a valid Option.
52        let inner_ptr = unsafe { (def.vtable.get_value)(ptr)? };
53        let inner_ox = unsafe { OxRef::new(inner_ptr, def.t) };
54        Some(f.debug_tuple("Some").field(&inner_ox).finish())
55    } else {
56        Some(f.write_str("None"))
57    }
58}
59
60/// Hash for `Option<T>` - delegates to inner T's hash if available
61unsafe fn option_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
62    let shape = ox.shape();
63    let def = get_option_def(shape)?;
64    let ptr = ox.ptr();
65
66    use core::hash::Hash;
67    if unsafe { (def.vtable.is_some)(ptr) } {
68        1u8.hash(hasher);
69        let inner_ptr = unsafe { (def.vtable.get_value)(ptr)? };
70        unsafe { def.t.call_hash(inner_ptr, hasher)? };
71    } else {
72        0u8.hash(hasher);
73    }
74    Some(())
75}
76
77/// PartialEq for `Option<T>`
78unsafe fn option_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
79    let shape = a.shape();
80    let def = get_option_def(shape)?;
81
82    let a_ptr = a.ptr();
83    let b_ptr = b.ptr();
84    let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
85    let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
86
87    Some(match (a_is_some, b_is_some) {
88        (false, false) => true,
89        (true, true) => {
90            let a_inner = unsafe { (def.vtable.get_value)(a_ptr)? };
91            let b_inner = unsafe { (def.vtable.get_value)(b_ptr)? };
92            unsafe { def.t.call_partial_eq(a_inner, b_inner)? }
93        }
94        _ => false,
95    })
96}
97
98/// PartialOrd for `Option<T>`
99unsafe fn option_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
100    let shape = a.shape();
101    let def = get_option_def(shape)?;
102
103    let a_ptr = a.ptr();
104    let b_ptr = b.ptr();
105    let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
106    let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
107
108    Some(match (a_is_some, b_is_some) {
109        (false, false) => Some(Ordering::Equal),
110        (false, true) => Some(Ordering::Less),
111        (true, false) => Some(Ordering::Greater),
112        (true, true) => {
113            let a_inner = unsafe { (def.vtable.get_value)(a_ptr)? };
114            let b_inner = unsafe { (def.vtable.get_value)(b_ptr)? };
115            unsafe { def.t.call_partial_cmp(a_inner, b_inner)? }
116        }
117    })
118}
119
120/// Ord for `Option<T>`
121unsafe fn option_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
122    let shape = a.shape();
123    let def = get_option_def(shape)?;
124
125    let a_ptr = a.ptr();
126    let b_ptr = b.ptr();
127    let a_is_some = unsafe { (def.vtable.is_some)(a_ptr) };
128    let b_is_some = unsafe { (def.vtable.is_some)(b_ptr) };
129
130    Some(match (a_is_some, b_is_some) {
131        (false, false) => Ordering::Equal,
132        (false, true) => Ordering::Less,
133        (true, false) => Ordering::Greater,
134        (true, true) => {
135            let a_inner = unsafe { (def.vtable.get_value)(a_ptr)? };
136            let b_inner = unsafe { (def.vtable.get_value)(b_ptr)? };
137            unsafe { def.t.call_cmp(a_inner, b_inner)? }
138        }
139    })
140}
141
142/// Drop for `Option<T>`
143unsafe fn option_drop(ox: OxPtrMut) {
144    let shape = ox.shape();
145    let Some(def) = get_option_def(shape) else {
146        return;
147    };
148    let ptr = ox.ptr();
149
150    if unsafe { (def.vtable.is_some)(ptr.as_const()) } {
151        // Get a mutable pointer to the inner value directly.
152        // We can't use get_value() because it creates a shared reference via as_ref(),
153        // which would violate Stacked Borrows when we try to drop through a mutable pointer.
154        // Instead, call the typed drop function which takes a mutable pointer.
155        unsafe { option_drop_inner(ptr, def) };
156    }
157}
158
159/// Helper to drop the inner value of a Some option with proper mutable access
160unsafe fn option_drop_inner(ptr: crate::PtrMut, def: &OptionDef) {
161    // Use the replace_with vtable function to replace Some with None.
162    // This properly handles the drop of the inner value.
163    unsafe { (def.vtable.replace_with)(ptr, None) };
164}
165
166/// Default for `Option<T>` - always None (no `T::Default` requirement)
167unsafe fn option_default<T>(ox: OxPtrMut) {
168    let ptr = ox.ptr();
169    unsafe { ptr.as_uninit().put(Option::<T>::None) };
170}
171
172/// Check if `Option<T>` is Some
173unsafe fn option_is_some<T>(option: PtrConst) -> bool {
174    unsafe { option.get::<Option<T>>().is_some() }
175}
176
177/// Get the value from `Option<T>` if present
178unsafe fn option_get_value<T>(option: PtrConst) -> Option<PtrConst> {
179    unsafe {
180        option
181            .get::<Option<T>>()
182            .as_ref()
183            .map(|t| PtrConst::new(t as *const T))
184    }
185}
186
187/// Initialize `Option<T>` with Some(value)
188unsafe fn option_init_some<T>(option: crate::PtrUninit, value: PtrConst) -> crate::PtrMut {
189    unsafe { option.put(Option::Some(value.read::<T>())) }
190}
191
192/// Initialize `Option<T>` with None
193unsafe fn option_init_none<T>(option: crate::PtrUninit) -> crate::PtrMut {
194    unsafe { option.put(<Option<T>>::None) }
195}
196
197/// Replace `Option<T>` with a new value
198unsafe fn option_replace_with<T>(option: crate::PtrMut, value: Option<PtrConst>) {
199    unsafe {
200        let option = option.as_mut::<Option<T>>();
201        match value {
202            Some(value) => {
203                option.replace(value.read::<T>());
204            }
205            None => {
206                option.take();
207            }
208        };
209    }
210}
211
212unsafe impl<'a, T: Facet<'a>> Facet<'a> for Option<T> {
213    const SHAPE: &'static Shape = &const {
214        const fn build_option_vtable<T>() -> OptionVTable {
215            OptionVTable::builder()
216                .is_some(option_is_some::<T>)
217                .get_value(option_get_value::<T>)
218                .init_some(option_init_some::<T>)
219                .init_none(option_init_none::<T>)
220                .replace_with(option_replace_with::<T>)
221                .build()
222        }
223
224        const fn build_vtable() -> VTableIndirect {
225            VTableIndirect {
226                display: Some(option_display),
227                debug: Some(option_debug),
228                hash: Some(option_hash),
229                invariants: None,
230                parse: None,
231                parse_bytes: None,
232                try_from: None,
233                try_into_inner: None,
234                try_borrow_inner: None,
235                partial_eq: Some(option_partial_eq),
236                partial_cmp: Some(option_partial_cmp),
237                cmp: Some(option_cmp),
238            }
239        }
240
241        const fn build_type_ops<T>() -> TypeOpsIndirect {
242            TypeOpsIndirect {
243                drop_in_place: option_drop,
244                default_in_place: Some(option_default::<T>),
245                clone_into: None,
246                is_truthy: Some(option_is_some::<T>),
247            }
248        }
249
250        ShapeBuilder::for_sized::<Option<T>>("Option")
251            .ty(Type::User(
252                // Null-Pointer-Optimization check
253                if core::mem::size_of::<T>() == core::mem::size_of::<Option<T>>()
254                    && core::mem::size_of::<T>() <= core::mem::size_of::<usize>()
255                {
256                    UserType::Enum(EnumType {
257                        repr: Repr::default(),
258                        enum_repr: EnumRepr::RustNPO,
259                        variants: &const {
260                            [
261                                VariantBuilder::unit("None").discriminant(0).build(),
262                                VariantBuilder::tuple(
263                                    "Some",
264                                    &const { [FieldBuilder::new("0", crate::shape_of::<T>, 0).build()] },
265                                )
266                                .discriminant(0)
267                                .build(),
268                            ]
269                        },
270                    })
271                } else {
272                    UserType::Opaque
273                },
274            ))
275            .def(Def::Option(OptionDef::new(
276                &const { build_option_vtable::<T>() },
277                T::SHAPE,
278            )))
279            .type_params(&[TypeParam {
280                name: "T",
281                shape: T::SHAPE,
282            }])
283            .inner(T::SHAPE)
284            .vtable_indirect(&const { build_vtable() })
285            .type_ops_indirect(&const { build_type_ops::<T>() })
286            // Option<T> propagates T's variance
287            .variance(Shape::computed_variance)
288            .build()
289    };
290}