• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

facet-rs / facet / 20134418410

11 Dec 2025 01:17PM UTC coverage: 57.796% (-0.1%) from 57.906%
20134418410

Pull #1252

github

web-flow
Merge f2f88ac1a into c030d09c4
Pull Request #1252: Introduce the concept of truthiness

61 of 197 new or added lines in 14 files covered. (30.96%)

1 existing line in 1 file now uncovered.

28969 of 50123 relevant lines covered (57.8%)

6281.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.83
/facet-core/src/impls/alloc/vec.rs
1
use crate::*;
2

3
use alloc::boxed::Box;
4
use alloc::vec::Vec;
5

6
/// Helper for Debug formatting via Shape vtable
7
struct DebugViaShape(&'static Shape, PtrConst);
8

9
impl core::fmt::Debug for DebugViaShape {
10
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
27✔
11
        match unsafe { self.0.call_debug(self.1, f) } {
27✔
12
            Some(result) => result,
27✔
13
            None => write!(f, "???"),
×
14
        }
15
    }
27✔
16
}
17

18
// =============================================================================
19
// Type-erased vtable functions for Vec<T> - shared across all instantiations
20
// =============================================================================
21

22
/// Vec memory layout (stable across all T)
23
/// Layout is determined at const time by probing a Vec.
24
#[repr(C)]
25
struct VecLayout {
26
    #[allow(dead_code)]
27
    cap: usize,
28
    ptr: *mut u8,
29
    len: usize,
30
}
31

32
// Compile-time assertion that our VecLayout matches the actual Vec layout
33
const _: () = {
34
    // Create a Vec and transmute to probe the layout
35
    let v: Vec<u8> = Vec::new();
36
    let fields: [usize; 3] = unsafe { core::mem::transmute(v) };
37

38
    // Vec::new() has cap=0, len=0, ptr=non-null (dangling)
39
    // We can't easily distinguish cap from len when both are 0,
40
    // but we can at least verify the size matches
41
    assert!(
42
        core::mem::size_of::<Vec<u8>>() == core::mem::size_of::<VecLayout>(),
43
        "VecLayout size mismatch"
44
    );
45
    assert!(
46
        core::mem::align_of::<Vec<u8>>() == core::mem::align_of::<VecLayout>(),
47
        "VecLayout align mismatch"
48
    );
49

50
    // The pointer field should be non-null even for empty vec (dangling pointer)
51
    // Fields 0 and 2 should be 0 (cap and len)
52
    // Field 1 should be non-zero (ptr)
53
    // This validates our layout: [cap, ptr, len]
54
    assert!(fields[0] == 0, "expected cap=0 at offset 0");
55
    assert!(fields[1] != 0, "expected non-null ptr at offset 1");
56
    assert!(fields[2] == 0, "expected len=0 at offset 2");
57
};
58

59
/// Type-erased len implementation - works for any `Vec<T>`
60
unsafe fn vec_len_erased(ptr: PtrConst) -> usize {
378,586✔
61
    unsafe {
62
        let layout = ptr.as_byte_ptr() as *const VecLayout;
378,586✔
63
        (*layout).len
378,586✔
64
    }
65
}
378,586✔
66

67
/// Type-erased get implementation - works for any `Vec<T>` using shape info
68
unsafe fn vec_get_erased(ptr: PtrConst, index: usize, shape: &'static Shape) -> Option<PtrConst> {
133✔
69
    unsafe {
70
        let layout = ptr.as_byte_ptr() as *const VecLayout;
133✔
71
        let len = (*layout).len;
133✔
72
        if index >= len {
133✔
73
            return None;
2✔
74
        }
131✔
75
        let elem_size = shape
131✔
76
            .type_params
131✔
77
            .first()?
131✔
78
            .shape
79
            .layout
80
            .sized_layout()
131✔
81
            .ok()?
131✔
82
            .size();
131✔
83
        let data_ptr = (*layout).ptr;
131✔
84
        Some(PtrConst::new(data_ptr.add(index * elem_size)))
131✔
85
    }
86
}
133✔
87

88
/// Type-erased get_mut implementation - works for any `Vec<T>` using shape info
89
unsafe fn vec_get_mut_erased(ptr: PtrMut, index: usize, shape: &'static Shape) -> Option<PtrMut> {
×
90
    unsafe {
91
        let layout = ptr.as_byte_ptr() as *const VecLayout;
×
92
        let len = (*layout).len;
×
93
        if index >= len {
×
94
            return None;
×
95
        }
×
96
        let elem_size = shape
×
97
            .type_params
×
98
            .first()?
×
99
            .shape
100
            .layout
101
            .sized_layout()
×
102
            .ok()?
×
103
            .size();
×
104
        let data_ptr = (*layout).ptr;
×
105
        Some(PtrMut::new(data_ptr.add(index * elem_size)))
×
106
    }
107
}
×
108

109
/// Shared ListVTable for ALL `Vec<T>` instantiations
110
///
111
/// This single vtable is used by every `Vec<T>` regardless of T, eliminating
112
/// the need to generate separate `vec_len`, `vec_get`, etc. functions for
113
/// each element type.
114
static VEC_LIST_VTABLE: ListVTable = ListVTable {
115
    len: vec_len_erased,
116
    get: vec_get_erased,
117
    get_mut: Some(vec_get_mut_erased),
118
    as_ptr: Some(vec_as_ptr_erased),
119
    as_mut_ptr: Some(vec_as_mut_ptr_erased),
120
};
121

122
/// Type-erased as_ptr implementation - works for any `Vec<T>`
123
unsafe fn vec_as_ptr_erased(ptr: PtrConst) -> PtrConst {
378,150✔
124
    unsafe {
125
        let layout = ptr.as_byte_ptr() as *const VecLayout;
378,150✔
126
        PtrConst::new((*layout).ptr)
378,150✔
127
    }
128
}
378,150✔
129

130
/// Type-erased as_mut_ptr implementation - works for any `Vec<T>`
131
unsafe fn vec_as_mut_ptr_erased(ptr: PtrMut) -> PtrMut {
×
132
    unsafe {
133
        let layout = ptr.as_byte_ptr() as *const VecLayout;
×
134
        PtrMut::new((*layout).ptr)
×
135
    }
136
}
×
137

138
/// Type-erased type_name implementation for Vec
139
fn vec_type_name(
365✔
140
    shape: &'static Shape,
365✔
141
    f: &mut core::fmt::Formatter<'_>,
365✔
142
    opts: TypeNameOpts,
365✔
143
) -> core::fmt::Result {
365✔
144
    write!(f, "Vec")?;
365✔
145
    if let Some(opts) = opts.for_children() {
365✔
146
        write!(f, "<")?;
365✔
147
        if let Some(tp) = shape.type_params.first() {
365✔
148
            tp.shape.write_type_name(f, opts)?;
365✔
149
        }
×
150
        write!(f, ">")?;
365✔
151
    } else {
152
        write!(f, "<…>")?;
×
153
    }
154
    Ok(())
365✔
155
}
365✔
156

157
/// Get the ListDef from a shape, panics if not a list
158
#[inline]
159
fn get_list_def(shape: &'static Shape) -> &'static ListDef {
192✔
160
    match &shape.def {
192✔
161
        Def::List(list_def) => list_def,
192✔
162
        _ => panic!("expected List def"),
×
163
    }
164
}
192✔
165

166
/// Type-erased debug implementation for Vec
167
unsafe fn vec_debug_erased(
10✔
168
    ox: OxPtrConst,
10✔
169
    f: &mut core::fmt::Formatter<'_>,
10✔
170
) -> Option<core::fmt::Result> {
10✔
171
    let shape = ox.shape();
10✔
172
    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
10✔
173
    if !elem_shape.vtable.has_debug() {
10✔
174
        return None;
×
175
    }
10✔
176

177
    let list_def = get_list_def(shape);
10✔
178
    let ptr = ox.ptr();
10✔
179
    let len = unsafe { (list_def.vtable.len)(ptr) };
10✔
180

181
    let mut list = f.debug_list();
10✔
182
    for i in 0..len {
27✔
183
        if let Some(item_ptr) = unsafe { (list_def.vtable.get)(ptr, i, shape) } {
27✔
184
            list.entry(&DebugViaShape(elem_shape, item_ptr));
27✔
185
        }
27✔
186
    }
187
    Some(list.finish())
10✔
188
}
10✔
189

190
/// Type-erased partial_eq implementation for Vec
191
unsafe fn vec_partial_eq_erased(ox_a: OxPtrConst, ox_b: OxPtrConst) -> Option<bool> {
188,912✔
192
    let shape = ox_a.shape();
188,912✔
193
    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
188,912✔
194
    if !elem_shape.vtable.has_partial_eq() {
188,912✔
195
        return None;
188,734✔
196
    }
178✔
197

198
    let list_def = get_list_def(shape);
178✔
199
    let ptr_a = ox_a.ptr();
178✔
200
    let ptr_b = ox_b.ptr();
178✔
201
    let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
178✔
202
    let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
178✔
203

204
    if len_a != len_b {
178✔
205
        return Some(false);
6✔
206
    }
172✔
207

208
    for i in 0..len_a {
172✔
209
        let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
45✔
210
        let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
45✔
211
        match unsafe { elem_shape.call_partial_eq(item_a, item_b) } {
45✔
212
            Some(true) => continue,
32✔
213
            Some(false) => return Some(false),
13✔
214
            None => return None,
×
215
        }
216
    }
217
    Some(true)
159✔
218
}
188,912✔
219

220
/// Type-erased partial_cmp implementation for Vec
221
unsafe fn vec_partial_cmp_erased(
2✔
222
    ox_a: OxPtrConst,
2✔
223
    ox_b: OxPtrConst,
2✔
224
) -> Option<Option<core::cmp::Ordering>> {
2✔
225
    let shape = ox_a.shape();
2✔
226
    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
2✔
227
    if !elem_shape.vtable.has_partial_ord() {
2✔
228
        return None;
×
229
    }
2✔
230

231
    let list_def = get_list_def(shape);
2✔
232
    let ptr_a = ox_a.ptr();
2✔
233
    let ptr_b = ox_b.ptr();
2✔
234
    let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
2✔
235
    let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
2✔
236

237
    let min_len = len_a.min(len_b);
2✔
238

239
    for i in 0..min_len {
2✔
240
        let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
2✔
241
        let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
2✔
242
        match unsafe { elem_shape.call_partial_cmp(item_a, item_b) } {
2✔
243
            Some(Some(core::cmp::Ordering::Equal)) => continue,
×
244
            Some(ord) => return Some(ord),
2✔
245
            None => return None,
×
246
        }
247
    }
248
    Some(Some(len_a.cmp(&len_b)))
×
249
}
2✔
250

251
/// Type-erased cmp implementation for Vec
252
unsafe fn vec_cmp_erased(ox_a: OxPtrConst, ox_b: OxPtrConst) -> Option<core::cmp::Ordering> {
2✔
253
    let shape = ox_a.shape();
2✔
254
    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
2✔
255
    if !elem_shape.vtable.has_ord() {
2✔
256
        return None;
×
257
    }
2✔
258

259
    let list_def = get_list_def(shape);
2✔
260
    let ptr_a = ox_a.ptr();
2✔
261
    let ptr_b = ox_b.ptr();
2✔
262
    let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
2✔
263
    let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
2✔
264

265
    let min_len = len_a.min(len_b);
2✔
266

267
    for i in 0..min_len {
2✔
268
        let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
2✔
269
        let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
2✔
270
        match unsafe { elem_shape.call_cmp(item_a, item_b) } {
2✔
271
            Some(core::cmp::Ordering::Equal) => continue,
×
272
            Some(ord) => return Some(ord),
2✔
273
            None => return None,
×
274
        }
275
    }
276
    Some(len_a.cmp(&len_b))
×
277
}
2✔
278

279
// =============================================================================
280
// Generic functions that still need T
281
// =============================================================================
282

283
type VecIterator<'mem, T> = core::slice::Iter<'mem, T>;
284

285
unsafe fn vec_init_in_place_with_capacity<T>(uninit: PtrUninit, capacity: usize) -> PtrMut {
813✔
286
    unsafe { uninit.put(Vec::<T>::with_capacity(capacity)) }
813✔
287
}
813✔
288

289
unsafe fn vec_push<T: 'static>(ptr: PtrMut, item: PtrMut) {
13,088✔
290
    unsafe {
13,088✔
291
        let vec = ptr.as_mut::<Vec<T>>();
13,088✔
292
        let item = item.read::<T>();
13,088✔
293
        vec.push(item);
13,088✔
294
    }
13,088✔
295
}
13,088✔
296

297
unsafe fn vec_iter_init<T: 'static>(ptr: PtrConst) -> PtrMut {
×
298
    unsafe {
299
        let vec = ptr.get::<Vec<T>>();
×
300
        let iter: VecIterator<T> = vec.iter();
×
301
        let iter_state = Box::new(iter);
×
302
        PtrMut::new(Box::into_raw(iter_state) as *mut u8)
×
303
    }
304
}
×
305

306
unsafe fn vec_iter_next<T: 'static>(iter_ptr: PtrMut) -> Option<PtrConst> {
×
307
    unsafe {
308
        let state = iter_ptr.as_mut::<VecIterator<'static, T>>();
×
309
        state.next().map(|value| PtrConst::new(value as *const T))
×
310
    }
311
}
×
312

313
unsafe fn vec_iter_next_back<T: 'static>(iter_ptr: PtrMut) -> Option<PtrConst> {
×
314
    unsafe {
315
        let state = iter_ptr.as_mut::<VecIterator<'static, T>>();
×
316
        state
×
317
            .next_back()
×
318
            .map(|value| PtrConst::new(value as *const T))
×
319
    }
320
}
×
321

322
unsafe fn vec_iter_dealloc<T>(iter_ptr: PtrMut) {
×
323
    unsafe {
324
        drop(Box::from_raw(
×
325
            iter_ptr.as_ptr::<VecIterator<'_, T>>() as *mut VecIterator<'_, T>
×
326
        ))
327
    }
328
}
×
329

330
unsafe impl<'a, T> Facet<'a> for Vec<T>
331
where
332
    T: Facet<'a> + 'static,
333
{
334
    const SHAPE: &'static Shape =
335
        &const {
336
            // Per-T operations that must be monomorphized
337
            const fn build_list_type_ops<T: 'static>() -> ListTypeOps {
×
338
                ListTypeOps::builder()
×
339
                    .init_in_place_with_capacity(vec_init_in_place_with_capacity::<T>)
×
340
                    .push(vec_push::<T>)
×
341
                    .iter_vtable(IterVTable {
×
342
                        init_with_value: Some(vec_iter_init::<T>),
×
343
                        next: vec_iter_next::<T>,
×
344
                        next_back: Some(vec_iter_next_back::<T>),
×
345
                        size_hint: None,
×
346
                        dealloc: vec_iter_dealloc::<T>,
×
347
                    })
×
348
                    .build()
×
349
            }
×
350

351
            ShapeBuilder::for_sized::<Self>("Vec")
352
            .type_name(vec_type_name)
353
            .ty(Type::User(UserType::Opaque))
354
            .def(Def::List(ListDef::with_type_ops(
355
                // Use the SHARED vtable for all Vec<T>!
356
                &VEC_LIST_VTABLE,
357
                &const { build_list_type_ops::<T>() },
358
                T::SHAPE,
359
            )))
360
            .type_params(&[TypeParam {
361
                name: "T",
362
                shape: T::SHAPE,
363
            }])
364
            .vtable_indirect(&const {
365
                VTableIndirect {
366
                    debug: Some(vec_debug_erased),
367
                    partial_eq: Some(vec_partial_eq_erased),
368
                    partial_cmp: Some(vec_partial_cmp_erased),
369
                    cmp: Some(vec_cmp_erased),
370
                    display: None,
371
                    hash: None,
372
                    invariants: None,
373
                    parse: None,
374
                    try_from: None,
375
                    try_into_inner: None,
376
                    try_borrow_inner: None,
377
                }
378
            })
379
            .type_ops_indirect(&const {
380
                unsafe fn drop_in_place<T>(ox: OxPtrMut) {
30✔
381
                    unsafe {
30✔
382
                        core::ptr::drop_in_place(ox.ptr().as_ptr::<Vec<T>>() as *mut Vec<T>);
30✔
383
                    }
30✔
384
                }
30✔
385

386
                unsafe fn default_in_place<T>(ox: OxPtrMut) {
16✔
387
                    unsafe { ox.ptr().as_uninit().put(Vec::<T>::new()) };
16✔
388
                }
16✔
389

NEW
390
                unsafe fn truthy<T>(ptr: PtrConst) -> bool {
×
NEW
391
                    !unsafe { ptr.get::<Vec<T>>() }.is_empty()
×
NEW
392
                }
×
393

394
                TypeOpsIndirect {
395
                    drop_in_place: drop_in_place::<T>,
396
                    default_in_place: Some(default_in_place::<T>),
397
                    clone_into: None,
398
                    is_truthy: Some(truthy::<T>),
399
                }
400
            })
401
            .build()
402
        };
403
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc