facet_core/impls/core/
tuple.rs

1use core::{cmp::Ordering, fmt, mem};
2
3use crate::{
4    Def, Facet, FieldBuilder, HashProxy, OxPtrConst, OxPtrMut, OxRef, PtrConst, PtrMut, Repr,
5    Shape, ShapeBuilder, StructKind, StructType, Type, TypeNameOpts, TypeOpsIndirect, UserType,
6    VTableIndirect,
7};
8
9/// Debug for tuples - formats as tuple literal
10unsafe fn tuple_debug(
11    ox: OxPtrConst,
12    f: &mut core::fmt::Formatter<'_>,
13) -> Option<core::fmt::Result> {
14    let shape = ox.shape();
15    let ty = match shape.ty {
16        Type::User(UserType::Struct(ref st)) => st,
17        _ => return None,
18    };
19
20    let ptr = ox.ptr();
21    let mut tuple = f.debug_tuple("");
22
23    for field in ty.fields {
24        // SAFETY: Field offset is valid, and the caller guarantees the OxPtrConst
25        // points to a valid tuple.
26        let field_ptr = unsafe { PtrConst::new(ptr.as_byte_ptr().add(field.offset)) };
27        let field_ox = unsafe { OxRef::new(field_ptr, field.shape.get()) };
28        tuple.field(&field_ox);
29    }
30
31    Some(tuple.finish())
32}
33
34/// Hash for tuples - hashes each element
35unsafe fn tuple_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
36    let shape = ox.shape();
37    let ty = match shape.ty {
38        Type::User(UserType::Struct(ref st)) => st,
39        _ => return None,
40    };
41
42    let ptr = ox.ptr();
43
44    for field in ty.fields {
45        let field_ptr = unsafe { PtrConst::new(ptr.as_byte_ptr().add(field.offset)) };
46        let field_shape = field.shape.get();
47        unsafe { field_shape.call_hash(field_ptr, hasher)? };
48    }
49
50    Some(())
51}
52
53/// PartialEq for tuples
54unsafe fn tuple_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
55    let shape = a.shape();
56    let ty = match shape.ty {
57        Type::User(UserType::Struct(ref st)) => st,
58        _ => return None,
59    };
60
61    let a_ptr = a.ptr();
62    let b_ptr = b.ptr();
63
64    for field in ty.fields {
65        let a_field = unsafe { PtrConst::new(a_ptr.as_byte_ptr().add(field.offset)) };
66        let b_field = unsafe { PtrConst::new(b_ptr.as_byte_ptr().add(field.offset)) };
67        let field_shape = field.shape.get();
68        if !unsafe { field_shape.call_partial_eq(a_field, b_field)? } {
69            return Some(false);
70        }
71    }
72
73    Some(true)
74}
75
76/// PartialOrd for tuples
77unsafe fn tuple_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
78    let shape = a.shape();
79    let ty = match shape.ty {
80        Type::User(UserType::Struct(ref st)) => st,
81        _ => return None,
82    };
83
84    let a_ptr = a.ptr();
85    let b_ptr = b.ptr();
86
87    for field in ty.fields {
88        let a_field = unsafe { PtrConst::new(a_ptr.as_byte_ptr().add(field.offset)) };
89        let b_field = unsafe { PtrConst::new(b_ptr.as_byte_ptr().add(field.offset)) };
90        let field_shape = field.shape.get();
91        match unsafe { field_shape.call_partial_cmp(a_field, b_field)? } {
92            Some(Ordering::Equal) => continue,
93            other => return Some(other),
94        }
95    }
96
97    Some(Some(Ordering::Equal))
98}
99
100/// Ord for tuples
101unsafe fn tuple_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
102    let shape = a.shape();
103    let ty = match shape.ty {
104        Type::User(UserType::Struct(ref st)) => st,
105        _ => return None,
106    };
107
108    let a_ptr = a.ptr();
109    let b_ptr = b.ptr();
110
111    for field in ty.fields {
112        let a_field = unsafe { PtrConst::new(a_ptr.as_byte_ptr().add(field.offset)) };
113        let b_field = unsafe { PtrConst::new(b_ptr.as_byte_ptr().add(field.offset)) };
114        let field_shape = field.shape.get();
115        match unsafe { field_shape.call_cmp(a_field, b_field)? } {
116            Ordering::Equal => continue,
117            other => return Some(other),
118        }
119    }
120
121    Some(Ordering::Equal)
122}
123
124/// Drop for tuples
125unsafe fn tuple_drop(ox: OxPtrMut) {
126    let shape = ox.shape();
127    let ty = match shape.ty {
128        Type::User(UserType::Struct(ref st)) => st,
129        _ => return,
130    };
131
132    let ptr = ox.ptr();
133
134    for field in ty.fields {
135        let field_ptr = unsafe { PtrMut::new((ptr.as_byte_ptr() as *mut u8).add(field.offset)) };
136        let field_shape = field.shape.get();
137        unsafe { field_shape.call_drop_in_place(field_ptr) };
138    }
139}
140
141// Shared vtable for all tuples
142const TUPLE_VTABLE: VTableIndirect = VTableIndirect {
143    display: None,
144    debug: Some(tuple_debug),
145    hash: Some(tuple_hash),
146    invariants: None,
147    parse: None,
148    parse_bytes: None,
149    try_from: None,
150    try_into_inner: None,
151    try_borrow_inner: None,
152    partial_eq: Some(tuple_partial_eq),
153    partial_cmp: Some(tuple_partial_cmp),
154    cmp: Some(tuple_cmp),
155};
156
157// Type operations for all tuples
158static TUPLE_TYPE_OPS: TypeOpsIndirect = TypeOpsIndirect {
159    drop_in_place: tuple_drop,
160    default_in_place: None,
161    clone_into: None,
162    is_truthy: None,
163};
164
165/// Type-erased type_name for tuples - reads field types from the shape
166fn tuple_type_name(
167    shape: &'static Shape,
168    f: &mut fmt::Formatter<'_>,
169    opts: TypeNameOpts,
170) -> fmt::Result {
171    let st = match &shape.ty {
172        Type::User(UserType::Struct(st)) if st.kind == StructKind::Tuple => st,
173        _ => return write!(f, "(?)"),
174    };
175
176    write!(f, "(")?;
177    // Use for_children() for the element types since they're nested inside the tuple
178    let child_opts = opts.for_children();
179    for (i, field) in st.fields.iter().enumerate() {
180        if i > 0 {
181            write!(f, ", ")?;
182        }
183        if let Some(opts) = child_opts {
184            field.shape.get().write_type_name(f, opts)?;
185        } else {
186            write!(f, "…")?;
187        }
188    }
189    // Trailing comma for single-element tuples
190    if st.fields.len() == 1 {
191        write!(f, ",")?;
192    }
193    write!(f, ")")?;
194    Ok(())
195}
196
197macro_rules! impl_facet_for_tuple {
198    // Used to implement the next bigger tuple type, by taking the next typename & associated index
199    // out of `remaining`, if it exists.
200    {
201        continue from ($($elems:ident.$idx:tt,)+),
202        remaining ()
203    } => {};
204    {
205        continue from ($($elems:ident.$idx:tt,)+),
206        remaining ($next:ident.$nextidx:tt, $($remaining:ident.$remainingidx:tt,)*)
207    } => {
208        impl_facet_for_tuple! {
209            impl ($($elems.$idx,)+ $next.$nextidx,),
210            remaining ($($remaining.$remainingidx,)*)
211        }
212    };
213    // Actually generate the trait implementation, and keep the remaining possible elements around
214    {
215        impl ($($elems:ident.$idx:tt,)+),
216        remaining ($($remaining:ident.$remainingidx:tt,)*)
217    } => {
218        unsafe impl<'a $(, $elems)+> Facet<'a> for ($($elems,)+)
219        where
220            $($elems: Facet<'a>,)+
221        {
222            const SHAPE: &'static Shape = &const {
223                ShapeBuilder::for_sized::<Self>(
224                    if 1 == [$($elems::SHAPE),+].len() {
225                        "(_,)"
226                    } else {
227                        "(…)"
228                    }
229                )
230                .type_name(tuple_type_name)
231                .ty(Type::User(UserType::Struct(StructType {
232                    repr: Repr::default(),
233                    kind: StructKind::Tuple,
234                    fields: &const {[
235                        $(FieldBuilder::new(stringify!($idx), crate::shape_of::<$elems>, mem::offset_of!(Self, $idx)).build(),)+
236                    ]}
237                })))
238                .def(Def::Undefined)
239                .vtable_indirect(&TUPLE_VTABLE)
240                .type_ops_indirect(&TUPLE_TYPE_OPS)
241                .build()
242            };
243        }
244
245        impl_facet_for_tuple! {
246            continue from ($($elems.$idx,)+),
247            remaining ($($remaining.$remainingidx,)*)
248        }
249    };
250    // The entry point into this macro, all smaller tuple types get implemented as well.
251    { ($first:ident.$firstidx:tt $(, $remaining:ident.$remainingidx:tt)* $(,)?) } => {
252        impl_facet_for_tuple! {
253            impl ($first.$firstidx,),
254            remaining ($($remaining.$remainingidx,)*)
255        }
256    };
257}
258
259#[cfg(feature = "tuples-12")]
260impl_facet_for_tuple! {
261    (T0.0, T1.1, T2.2, T3.3, T4.4, T5.5, T6.6, T7.7, T8.8, T9.9, T10.10, T11.11)
262}
263
264#[cfg(not(feature = "tuples-12"))]
265impl_facet_for_tuple! {
266    (T0.0, T1.1, T2.2, T3.3)
267}