facet_core/impls/core/
reference.rs

1//! Facet implementation for references (&T and &mut T)
2
3use core::fmt;
4
5use crate::{
6    Def, Facet, HashProxy, KnownPointer, OxPtrConst, OxPtrMut, PointerDef, PointerFlags,
7    PointerType, PointerVTable, PtrConst, Shape, ShapeBuilder, Type, TypeNameOpts, TypeOpsIndirect,
8    TypeParam, VTableIndirect, ValuePointerType,
9};
10
11/// Type-erased type_name for &T - reads the pointee type from the shape
12fn ref_type_name(
13    shape: &'static Shape,
14    f: &mut fmt::Formatter<'_>,
15    opts: TypeNameOpts,
16) -> fmt::Result {
17    let pointee = match &shape.def {
18        Def::Pointer(ptr_def) => ptr_def.pointee,
19        _ => None,
20    };
21
22    write!(f, "&")?;
23    if let Some(pointee) = pointee {
24        if let Some(opts) = opts.for_children() {
25            pointee.write_type_name(f, opts)?;
26        } else {
27            write!(f, "…")?;
28        }
29    } else {
30        write!(f, "?")?;
31    }
32    Ok(())
33}
34
35/// Type-erased type_name for &mut T - reads the pointee type from the shape
36fn ref_mut_type_name(
37    shape: &'static Shape,
38    f: &mut fmt::Formatter<'_>,
39    opts: TypeNameOpts,
40) -> fmt::Result {
41    let pointee = match &shape.def {
42        Def::Pointer(ptr_def) => ptr_def.pointee,
43        _ => None,
44    };
45
46    write!(f, "&mut ")?;
47    if let Some(pointee) = pointee {
48        if let Some(opts) = opts.for_children() {
49            pointee.write_type_name(f, opts)?;
50        } else {
51            write!(f, "…")?;
52        }
53    } else {
54        write!(f, "?")?;
55    }
56    Ok(())
57}
58
59/// Helper to dereference a reference and get a PtrConst to the pointee.
60/// Handles both thin and wide pointers correctly.
61///
62/// # Safety
63/// - `ox` must point to a valid reference
64/// - The returned PtrConst is only valid for the lifetime of the referent
65unsafe fn deref_to_pointee(ox: OxPtrConst) -> Option<(&'static Shape, PtrConst)> {
66    let shape = ox.shape();
67    let Def::Pointer(ptr_def) = shape.def else {
68        return None;
69    };
70    let pointee_shape = ptr_def.pointee?;
71
72    // ox.ptr() points to the reference itself (e.g., points to a &T or &str value in memory)
73    // We need to READ that reference value to get the pointee.
74    //
75    // For thin pointers (&T where T: Sized): read a single pointer-sized value
76    // For wide pointers (&str, &[T]): read a fat pointer (ptr + metadata)
77    //
78    // The vtable functions for the pointee expect a PtrConst that points TO the data.
79    // For sized types: just a thin pointer to the data
80    // For unsized types: a wide pointer with the metadata preserved
81
82    let ref_size = shape.layout.sized_layout().ok()?.size();
83
84    if ref_size == core::mem::size_of::<*const ()>() {
85        // Thin pointer - read the pointer value and create thin PtrConst
86        let inner_ptr = unsafe { *(ox.ptr().as_byte_ptr() as *const *const u8) };
87        Some((pointee_shape, PtrConst::new(inner_ptr as *const ())))
88    } else {
89        // Wide/fat pointer - read the fat pointer value from memory
90        // ox.ptr() points to a &str (or &[T], etc.) stored in memory
91        // We need to read that fat pointer and create a PtrConst from it
92        //
93        // The fat pointer is stored at ox.ptr(), we read it as the appropriate reference type
94        // and then create a PtrConst from that reference
95
96        // Read the fat pointer from memory - it's stored as [ptr, metadata]
97        let fat_ptr_location = ox.ptr().as_byte_ptr() as *const [*const u8; 2];
98        let [data_ptr, metadata] = unsafe { *fat_ptr_location };
99
100        // Create a new PtrConst with the proper wide pointer representation
101        Some((
102            pointee_shape,
103            PtrConst::new_wide(data_ptr, metadata as *const ()),
104        ))
105    }
106}
107
108/// Debug for &T - delegates to T's debug
109unsafe fn ref_debug(ox: OxPtrConst, f: &mut core::fmt::Formatter<'_>) -> Option<core::fmt::Result> {
110    let (pointee_shape, inner) = unsafe { deref_to_pointee(ox)? };
111    unsafe { pointee_shape.call_debug(inner, f) }
112}
113
114/// Display for &T - delegates to T's display
115unsafe fn ref_display(
116    ox: OxPtrConst,
117    f: &mut core::fmt::Formatter<'_>,
118) -> Option<core::fmt::Result> {
119    let (pointee_shape, inner) = unsafe { deref_to_pointee(ox)? };
120    unsafe { pointee_shape.call_display(inner, f) }
121}
122
123/// Hash for &T - delegates to T's hash
124unsafe fn ref_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
125    let (pointee_shape, inner) = unsafe { deref_to_pointee(ox)? };
126    unsafe { pointee_shape.call_hash(inner, hasher) }
127}
128
129/// PartialEq for &T - delegates to T's partial_eq
130unsafe fn ref_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
131    let (pointee_shape, a_inner) = unsafe { deref_to_pointee(a)? };
132    let (_, b_inner) = unsafe { deref_to_pointee(b)? };
133    unsafe { pointee_shape.call_partial_eq(a_inner, b_inner) }
134}
135
136/// PartialOrd for &T - delegates to T's partial_cmp
137unsafe fn ref_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<core::cmp::Ordering>> {
138    let (pointee_shape, a_inner) = unsafe { deref_to_pointee(a)? };
139    let (_, b_inner) = unsafe { deref_to_pointee(b)? };
140    unsafe { pointee_shape.call_partial_cmp(a_inner, b_inner) }
141}
142
143/// Ord for &T - delegates to T's cmp
144unsafe fn ref_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<core::cmp::Ordering> {
145    let (pointee_shape, a_inner) = unsafe { deref_to_pointee(a)? };
146    let (_, b_inner) = unsafe { deref_to_pointee(b)? };
147    unsafe { pointee_shape.call_cmp(a_inner, b_inner) }
148}
149
150/// Drop for &T and &mut T (no-op, references don't need dropping)
151unsafe fn ref_drop(_ptr: OxPtrMut) {
152    // References don't need dropping
153}
154
155/// Clone for &T - just copies the reference (since &T is Copy)
156unsafe fn ref_clone(src: OxPtrConst, dst: OxPtrMut) {
157    // For references, clone is just a memcpy of the pointer
158    let Some(size) = src.shape().layout.sized_layout().ok().map(|l| l.size()) else {
159        return;
160    };
161    unsafe {
162        core::ptr::copy_nonoverlapping(src.ptr().as_byte_ptr(), dst.ptr().as_mut_byte_ptr(), size);
163    }
164}
165
166// Shared vtable for all &T (immutable refs are Copy, so Clone is trivial)
167const REF_VTABLE: VTableIndirect = VTableIndirect {
168    display: Some(ref_display),
169    debug: Some(ref_debug),
170    hash: Some(ref_hash),
171    invariants: None,
172    parse: None,
173    try_from: None,
174    try_into_inner: None,
175    try_borrow_inner: None,
176    partial_eq: Some(ref_partial_eq),
177    partial_cmp: Some(ref_partial_cmp),
178    cmp: Some(ref_cmp),
179};
180
181// Type operations for &T (references are Copy, so they can be cloned)
182static REF_TYPE_OPS: TypeOpsIndirect = TypeOpsIndirect {
183    drop_in_place: ref_drop,
184    default_in_place: None,
185    clone_into: Some(ref_clone),
186    is_truthy: None,
187};
188
189// Vtable for &mut T (not Clone since &mut T is not Clone)
190const REF_MUT_VTABLE: VTableIndirect = VTableIndirect {
191    display: Some(ref_display),
192    debug: Some(ref_debug),
193    hash: Some(ref_hash),
194    invariants: None,
195    parse: None,
196    try_from: None,
197    try_into_inner: None,
198    try_borrow_inner: None,
199    partial_eq: Some(ref_partial_eq),
200    partial_cmp: Some(ref_partial_cmp),
201    cmp: Some(ref_cmp),
202};
203
204// Type operations for &mut T (no Clone since &mut T is not Clone)
205static REF_MUT_TYPE_OPS: TypeOpsIndirect = TypeOpsIndirect {
206    drop_in_place: ref_drop,
207    default_in_place: None,
208    clone_into: None,
209    is_truthy: None,
210};
211
212/// Borrow function for &T - dereferences to get inner pointer
213unsafe fn ref_borrow<T: ?Sized>(this: PtrConst) -> PtrConst {
214    let ptr: &&T = unsafe { this.get::<&T>() };
215    let ptr: &T = ptr;
216    // Don't cast to *const u8 - that loses metadata for wide pointers like &str
217    PtrConst::new(ptr as *const T)
218}
219
220/// Borrow function for &mut T - dereferences to get inner pointer
221unsafe fn ref_mut_borrow<T: ?Sized>(this: PtrConst) -> PtrConst {
222    let ptr: &&mut T = unsafe { this.get::<&mut T>() };
223    let ptr: &T = ptr;
224    // Don't cast to *const u8 - that loses metadata for wide pointers like &str
225    PtrConst::new(ptr as *const T)
226}
227
228unsafe impl<'a, T: ?Sized + Facet<'a>> Facet<'a> for &'a T {
229    const SHAPE: &'static Shape = &const {
230        const fn build_pointer_vtable<T: ?Sized>() -> PointerVTable {
231            PointerVTable {
232                borrow_fn: Some(ref_borrow::<T>),
233                ..PointerVTable::new()
234            }
235        }
236
237        ShapeBuilder::for_sized::<&T>("&T")
238            .type_name(ref_type_name)
239            .ty({
240                let vpt = ValuePointerType {
241                    mutable: false,
242                    wide: size_of::<*const T>() != size_of::<*const ()>(),
243                    target: T::SHAPE,
244                };
245                Type::Pointer(PointerType::Reference(vpt))
246            })
247            .def(Def::Pointer(PointerDef {
248                vtable: &const { build_pointer_vtable::<T>() },
249                pointee: Some(T::SHAPE),
250                weak: None,
251                strong: None,
252                flags: PointerFlags::EMPTY,
253                known: Some(KnownPointer::SharedReference),
254            }))
255            .type_params(&[TypeParam {
256                name: "T",
257                shape: T::SHAPE,
258            }])
259            .inner(T::SHAPE)
260            .vtable_indirect(&REF_VTABLE)
261            .type_ops_indirect(&REF_TYPE_OPS)
262            .build()
263    };
264}
265
266unsafe impl<'a, T: ?Sized + Facet<'a>> Facet<'a> for &'a mut T {
267    const SHAPE: &'static Shape = &const {
268        const fn build_pointer_vtable<T: ?Sized>() -> PointerVTable {
269            PointerVTable {
270                borrow_fn: Some(ref_mut_borrow::<T>),
271                ..PointerVTable::new()
272            }
273        }
274
275        ShapeBuilder::for_sized::<&mut T>("&mut T")
276            .type_name(ref_mut_type_name)
277            .ty({
278                let vpt = ValuePointerType {
279                    mutable: true,
280                    wide: size_of::<*const T>() != size_of::<*const ()>(),
281                    target: T::SHAPE,
282                };
283                Type::Pointer(PointerType::Reference(vpt))
284            })
285            .def(Def::Pointer(PointerDef {
286                vtable: &const { build_pointer_vtable::<T>() },
287                pointee: Some(T::SHAPE),
288                weak: None,
289                strong: None,
290                flags: PointerFlags::EMPTY,
291                known: Some(KnownPointer::ExclusiveReference),
292            }))
293            .type_params(&[TypeParam {
294                name: "T",
295                shape: T::SHAPE,
296            }])
297            .inner(T::SHAPE)
298            .vtable_indirect(&REF_MUT_VTABLE)
299            .type_ops_indirect(&REF_MUT_TYPE_OPS)
300            .build()
301    };
302}