Skip to main content

rootcause_internals/attachment/
data.rs

1//! `AttachmentData<A>` wrapper and field access.
2//!
3//! This module encapsulates the fields of [`AttachmentData`], ensuring they are
4//! only visible within this module. This visibility restriction guarantees the
5//! safety invariant: **the vtable type must always match the actual attachment
6//! type**.
7//!
8//! # Safety Invariant
9//!
10//! Since `AttachmentData` can only be constructed via [`AttachmentData::new`]
11//! (which creates matching vtable and attachment), and fields cannot be
12//! modified after construction (no `pub` or `pub(crate)` fields), the types
13//! remain in sync throughout the value's lifetime.
14//!
15//! # `#[repr(C)]` Layout
16//!
17//! The `#[repr(C)]` attribute enables safe field projection even when the type
18//! parameter `A` is erased. This allows accessing the vtable field from a
19//! pointer to `AttachmentData<Erased>` without constructing an invalid
20//! reference to the full struct.
21
22use crate::{
23    attachment::{
24        raw::{RawAttachmentMut, RawAttachmentRef},
25        vtable::AttachmentVtable,
26    },
27    handlers::AttachmentHandler,
28};
29
30/// Type-erased attachment data structure with vtable-based dispatch.
31///
32/// This struct uses `#[repr(C)]` to enable safe field access in type-erased
33/// contexts, allowing access to the vtable field even when the concrete
34/// attachment type `A` is unknown.
35#[repr(C)]
36pub(crate) struct AttachmentData<A: 'static> {
37    /// The Vtable of this attachment
38    ///
39    /// # Safety
40    ///
41    /// The following safety invariants are guaranteed to be upheld as long as
42    /// this struct exists:
43    ///
44    /// 1. The vtable must always point to an `AttachmentVtable` created for the
45    ///    actual attachment type `A` stored below. This is true even when
46    ///    accessed via type-erased pointers.
47    vtable: &'static AttachmentVtable,
48    /// The actual attachment data
49    attachment: A,
50}
51
52impl<A: 'static> AttachmentData<A> {
53    /// Creates a new [`AttachmentData`] with the specified handler and
54    /// attachment.
55    ///
56    /// This method creates the vtable for type-erased dispatch and pairs it
57    /// with the attachment data.
58    #[inline]
59    pub(super) fn new<H: AttachmentHandler<A>>(attachment: A) -> Self {
60        Self {
61            vtable: AttachmentVtable::new::<A, H>(),
62            attachment,
63        }
64    }
65}
66
67impl<'a> RawAttachmentRef<'a> {
68    /// Returns a reference to the [`AttachmentVtable`] of the
69    /// [`AttachmentData`] instance.
70    ///
71    /// The returned vtable is guaranteed to be a vtable for the
72    /// attachment type stored in the [`AttachmentData`].
73    #[inline]
74    pub(super) fn vtable(self) -> &'static AttachmentVtable {
75        let ptr = self.as_ptr();
76        // SAFETY: The safety requirements for `&raw const (*ptr).vtable` are upheld:
77        // 1. `ptr` is a valid pointer to a live `AttachmentData<A>` (for some `A`) as
78        //    guaranteed by `RawAttachmentRef`'s invariants
79        // 2. `AttachmentData<A>` is `#[repr(C)]`, so the `vtable` field is at a
80        //    consistent offset regardless of the type parameter `A`
81        // 3. We avoid creating a reference to the full `AttachmentData` struct, which
82        //    would be UB since we don't know the correct type parameter
83        let vtable_ptr: *const &'static AttachmentVtable = unsafe {
84            // @add-unsafe-context: AttachmentData
85            // @add-unsafe-context: crate::util::Erased
86            &raw const (*ptr).vtable
87        };
88
89        // SAFETY: The safety requirements for dereferencing `vtable_ptr` are upheld:
90        // 1. The pointer is valid and properly aligned because it points to the first
91        //    field of a valid `AttachmentData<A>` instance
92        // 2. The `vtable` field is initialized in `AttachmentData::new` and never
93        //    modified, so it contains a valid `&'static AttachmentVtable` value
94        unsafe { *vtable_ptr }
95    }
96
97    /// Accesses the inner attachment of the [`AttachmentData`] instance as a
98    /// reference to the specified type.
99    ///
100    /// # Safety
101    ///
102    /// The caller must ensure:
103    ///
104    /// 1. The type `A` matches the actual attachment type stored in the
105    ///    [`AttachmentData`].
106    #[inline]
107    pub unsafe fn attachment_downcast_unchecked<A: 'static>(self) -> &'a A {
108        // SAFETY:
109        // 1. Guaranteed by the caller
110        let this = unsafe {
111            // @add-unsafe-context: AttachmentData
112            self.cast_inner::<A>()
113        };
114        &this.attachment
115    }
116}
117
118impl<'a> RawAttachmentMut<'a> {
119    /// Accesses the inner attachment of the [`AttachmentData`] instance as a
120    /// reference to the specified type.
121    ///
122    /// # Safety
123    ///
124    /// The caller must ensure:
125    ///
126    /// 1. The type `A` matches the actual attachment type stored in the
127    ///    [`AttachmentData`].
128    #[inline]
129    pub unsafe fn into_attachment_downcast_unchecked<A: 'static>(self) -> &'a mut A {
130        // SAFETY:
131        // 1. Guaranteed by the caller
132        let this = unsafe {
133            // @add-unsafe-context: AttachmentData
134            self.cast_inner::<A>()
135        };
136        &mut this.attachment
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn test_attachment_field_offsets() {
146        use core::mem::{offset_of, size_of};
147
148        #[repr(align(32))]
149        struct LargeAlignment {
150            _value: u8,
151        }
152
153        assert_eq!(offset_of!(AttachmentData<u8>, vtable), 0);
154        assert_eq!(offset_of!(AttachmentData<u32>, vtable), 0);
155        assert_eq!(offset_of!(AttachmentData<[u64; 4]>, vtable), 0);
156        assert_eq!(offset_of!(AttachmentData<LargeAlignment>, vtable), 0);
157
158        assert!(
159            offset_of!(AttachmentData<u8>, attachment) >= size_of::<&'static AttachmentVtable>()
160        );
161        assert!(
162            offset_of!(AttachmentData<u32>, attachment) >= size_of::<&'static AttachmentVtable>()
163        );
164        assert!(
165            offset_of!(AttachmentData<[u64; 4]>, attachment)
166                >= size_of::<&'static AttachmentVtable>()
167        );
168        assert!(
169            offset_of!(AttachmentData<LargeAlignment>, attachment)
170                >= size_of::<&'static AttachmentVtable>()
171        );
172    }
173}