rootcause/
markers.rs

1//! Marker types and traits for defining ownership and thread-safety semantics.
2//!
3//! This module provides type-level markers that control how reports and
4//! attachments behave with respect to ownership, cloning, and thread safety.
5//! These markers are used as generic parameters in types like [`Report<C, O,
6//! T>`](crate::Report) to encode compile-time guarantees about how data can be
7//! accessed and shared.
8//!
9//! # Design Philosophy
10//!
11//! The constraints encoded by these markers are enforced at construction time.
12//! It is impossible to construct a [`Report`](crate::Report),
13//! [`ReportRef`], or
14//! [`ReportCollection`] that violates the invariants
15//! associated with its marker types. This means you can trust that a
16//! `Report<_, _, SendSync>` truly is `Send + Sync`, and that a `Report<_,
17//! Mutable>` truly has unique ownership.
18//!
19//! [`ReportRef`]: crate::ReportRef
20//! [`ReportCollection`]: crate::report_collection::ReportCollection
21//!
22//! # Context Marker
23//!
24//! The context marker is the first type parameter (`C`) in [`Report<C, O,
25//! T>`](crate::Report) and determines what type of error is stored at the root:
26//!
27//! - [`Dynamic`]: Type-erased context - the root error can be any type (like
28//!   [`anyhow::Error`]). This is the default when you write just [`Report`].
29//! - Any concrete type: Typed context - the root error must be that specific
30//!   type (like [`error-stack`]).
31//!
32//! See the [`Dynamic`] documentation for a detailed comparison and usage
33//! examples.
34//!
35//! [`anyhow::Error`]: https://docs.rs/anyhow
36//! [`error-stack`]: https://docs.rs/error-stack
37//! [`Report`]: crate::Report
38//!
39//! # Ownership Markers
40//!
41//! Ownership markers control whether reports and report references can be
42//! mutated and cloned.
43//!
44//! For owned reports ([`Report<C, O, T>`](crate::Report)), the ownership marker
45//! `O` can be:
46//! - [`Mutable`]: Unique ownership - the report can be mutated but not cloned
47//! - [`Cloneable`]: Shared ownership - the report can be cloned but not mutated
48//!
49//! For report references ([`ReportRef<C, O>`](crate::ReportRef)), the ownership
50//! marker `O` can be:
51//! - [`Cloneable`]: Enables [`clone_arc`](crate::ReportRef::clone_arc) to get
52//!   an owned report
53//! - [`Uncloneable`]: Does not provide
54//!   [`clone_arc`](crate::ReportRef::clone_arc)
55//!
56//! # Thread Safety Markers
57//!
58//! Thread safety markers control whether reports can be sent between threads or
59//! shared across threads. These appear as the third type parameter (`T`) in
60//! [`Report<C, O, T>`](crate::Report):
61//!
62//! - [`SendSync`]: The report and all its contents are `Send + Sync`, allowing
63//!   the report to cross thread boundaries.
64//! - [`Local`]: The report contains non-thread-safe data (like `Rc` or raw
65//!   pointers) and cannot be sent between threads.
66//!
67//! # Examples
68//!
69//! ## Creating Reports with Different Ownership Semantics
70//!
71//! ```
72//! use rootcause::prelude::*;
73//!
74//! // Mutable report - can be modified by adding context and attachments
75//! let mut report: Report<String, markers::Mutable> = report!("Error".to_string());
76//! let report: Report<String, markers::Mutable> = report.attach("Additional context");
77//!
78//! // Convert to cloneable report - can be cloned but not mutated
79//! let cloneable: Report<String, markers::Cloneable> = report.into_cloneable();
80//! let cloned: Report<String, markers::Cloneable> = cloneable.clone();
81//! assert_eq!(format!("{}", cloneable), format!("{}", cloned));
82//! ```
83//!
84//! ## Working with Thread Safety
85//!
86//! ```
87//! use std::rc::Rc;
88//!
89//! use rootcause::prelude::*;
90//!
91//! // Thread-safe report with String (String is Send + Sync)
92//! let thread_safe: Report<String, markers::Mutable, markers::SendSync> =
93//!     report!("Thread-safe error".to_string());
94//!
95//! // Can be sent to another thread
96//! std::thread::spawn(move || {
97//!     println!("{}", thread_safe);
98//! });
99//!
100//! // Local report with Rc (Rc is !Send + !Sync)
101//! let local_data: Rc<String> = Rc::new("Not thread-safe".to_string());
102//! let local_report: Report<Rc<String>, markers::Mutable, markers::Local> = report!(local_data);
103//! // local_report cannot be sent to another thread - won't compile
104//! ```
105
106use crate::ReportMut;
107
108/// Marker type for reports with dynamic (type-erased) context.
109///
110/// `Dynamic` is used as the context type parameter in
111/// [`Report<Dynamic, O, T>`](crate::Report) to indicate that the actual error
112/// type at the root is not known at compile time. This is the default behavior
113/// and is similar to [`anyhow::Error`] - any error type
114/// can be stored and the `?` operator works automatically.
115///
116/// # Key Properties
117///
118/// - **Zero-cost type erasure**: No actual instance of `Dynamic` is ever
119///   stored. It's purely a marker type used at the type level.
120/// - **Automatic conversions**: The `?` operator automatically converts any
121///   error type into `Report<Dynamic>`.
122/// - **Flexible propagation**: When you just need errors to propagate upward
123///   with context, `Dynamic` is the right choice.
124///
125/// # When to Use Dynamic
126///
127/// Use `Report<Dynamic>` (or just [`Report`](crate::Report), which defaults to
128/// `Dynamic`) when:
129/// - You're propagating errors upward and don't need to pattern match on
130///   specific error types
131/// - You want the flexibility to return different error types from the same
132///   function
133/// - You're building applications where error handling is about logging and
134///   displaying, not recovery
135///
136/// Use `Report<YourErrorType>` instead when:
137/// - Callers need to pattern match on specific error variants for recovery
138/// - You want compile-time guarantees about which errors a function can return
139/// - You're building libraries with well-defined error types
140///
141/// See [`examples/typed_reports.rs`] for when typed errors are appropriate.
142///
143/// [`examples/typed_reports.rs`]: https://github.com/rootcause-rs/rootcause/blob/main/examples/typed_reports.rs
144///
145/// # Examples
146///
147/// ## Basic Usage
148///
149/// ```
150/// use rootcause::prelude::*;
151///
152/// // Report<Dynamic> is the default - these are equivalent:
153/// fn might_fail() -> Result<(), Report> {
154///     // Can return any error type via ?
155///     std::fs::read_to_string("/nonexistent")?;
156///     Ok(())
157/// }
158///
159/// fn might_fail_explicit() -> Result<(), Report<markers::Dynamic>> {
160///     std::fs::read_to_string("/nonexistent")?;
161///     Ok(())
162/// }
163/// ```
164///
165/// ## Mixing Error Types
166///
167/// ```
168/// use std::{fs, io};
169///
170/// use rootcause::prelude::*;
171///
172/// #[derive(Debug)]
173/// struct ConfigError(String);
174/// impl std::fmt::Display for ConfigError {
175///     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
176///         write!(f, "Config error: {}", self.0)
177///     }
178/// }
179/// impl std::error::Error for ConfigError {}
180///
181/// fn load_and_parse(path: &str) -> Result<u32, Report> {
182///     // io::Error from fs operations
183///     let contents = fs::read_to_string(path)?;
184///
185///     // Custom ConfigError
186///     if contents.is_empty() {
187///         Err(ConfigError("empty file".into()))?;
188///     }
189///
190///     // ParseIntError from parse
191///     let value: u32 = contents.trim().parse()?;
192///
193///     Ok(value)
194/// }
195/// ```
196///
197/// ## Converting Between Typed and Dynamic
198///
199/// ```
200/// use rootcause::prelude::*;
201///
202/// #[derive(Debug)]
203/// struct MyError;
204/// impl std::fmt::Display for MyError {
205///     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
206///         write!(f, "MyError")
207///     }
208/// }
209/// impl std::error::Error for MyError {}
210///
211/// fn typed_error() -> Result<(), Report<MyError>> {
212///     Err(report!(MyError))
213/// }
214///
215/// fn dynamic_error() -> Result<(), Report> {
216///     // Typed report automatically coerces to dynamic via ?
217///     typed_error()?;
218///     Ok(())
219/// }
220///
221/// // Or convert explicitly:
222/// fn explicit_conversion() -> Result<(), Report> {
223///     let typed: Report<MyError> = report!(MyError);
224///     let dynamic: Report = typed.into_dynamic();
225///     Err(dynamic)
226/// }
227/// ```
228///
229/// See [`examples/error_coercion.rs`] for a complete guide to type conversions.
230///
231/// [`examples/error_coercion.rs`]: https://github.com/rootcause-rs/rootcause/blob/main/examples/error_coercion.rs
232pub struct Dynamic {
233    /// This field ensures `Dynamic` is an unsized type.
234    ///
235    /// Since the `Dynamic` type itself is never instantiated, the choice
236    /// of an unsized type here is purely to enforce that property at the type
237    /// level.
238    _phantom_unsized: [()],
239}
240
241/// Marker type for owned reports with unique ownership.
242///
243/// This marker is used exclusively with [`Report<C, Mutable, T>`] (not
244/// [`ReportRef`]). It indicates that the report has unique ownership of its
245/// data, which allows mutation operations but prevents cloning.
246///
247/// [`Report<C, Mutable, T>`]: crate::Report
248/// [`ReportRef`]: crate::ReportRef
249///
250/// # Available Operations
251///
252/// With `Mutable` ownership, you can:
253/// - Add attachments with [`attach`](crate::Report::attach)
254/// - Add parent context with [`context`](crate::Report::context)
255/// - Get mutable access with [`as_mut`](crate::Report::as_mut)
256/// - Convert to [`Cloneable`] with
257///   [`into_cloneable`](crate::Report::into_cloneable)
258///
259/// # Examples
260///
261/// ```
262/// use rootcause::prelude::*;
263///
264/// let report: Report<String, markers::Mutable> = report!("Database error".to_string());
265///
266/// // Can add attachments (consumes and returns the report)
267/// let report: Report<String, markers::Mutable> = report.attach("connection timeout");
268///
269/// // Can add parent context
270/// let report: Report<String, markers::Mutable> =
271///     report.context("Failed to fetch user data".to_string());
272///
273/// // Cannot clone - Mutable reports don't implement Clone
274/// // let cloned = report.clone(); // ❌ Won't compile
275/// ```
276#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
277pub struct Mutable;
278
279/// Marker type for cloneable reports and report references.
280///
281/// This marker is used with both [`Report<C, Cloneable, T>`](crate::Report) and
282/// [`ReportRef<C, Cloneable>`](crate::ReportRef). It indicates shared ownership
283/// using reference counting (`Arc` internally), which allows cheap cloning but
284/// prevents mutation.
285///
286/// # Usage with Report
287///
288/// For [`Report<C, Cloneable, T>`](crate::Report), this marker means the report
289/// itself implements `Clone`, allowing you to cheaply clone the entire report
290/// (shallow copy via `Arc`).
291///
292/// # Usage with ReportRef
293///
294/// For [`ReportRef<C, Cloneable>`](crate::ReportRef), the marker enables the
295/// [`clone_arc`](crate::ReportRef::clone_arc) method, which clones the
296/// underlying `Arc` to produce an owned [`Report<C, Cloneable,
297/// T>`](crate::Report). Note that `ReportRef` itself is always `Copy` and
298/// `Clone` regardless of the ownership marker - the `Cloneable`
299/// marker specifically enables converting the reference back to an owned
300/// report.
301///
302/// # When to Use
303///
304/// Use `Cloneable` reports when you need to:
305/// - Share an error report across multiple code paths
306/// - Store reports in collections that require `Clone`
307/// - Return the same error from multiple places without deep copying
308///
309/// # Converting to Cloneable
310///
311/// Convert a [`Mutable`] report to `Cloneable` using
312/// [`into_cloneable`](crate::Report::into_cloneable):
313///
314/// ```
315/// use rootcause::prelude::*;
316///
317/// let report: Report<String, markers::Mutable> = report!("Error".to_string());
318///
319/// // Convert to cloneable
320/// let cloneable: Report<String, markers::Cloneable> = report.into_cloneable();
321///
322/// // Now can clone cheaply (shallow clone via Arc)
323/// let clone1: Report<String, markers::Cloneable> = cloneable.clone();
324/// let clone2: Report<String, markers::Cloneable> = cloneable.clone();
325/// ```
326///
327/// # Examples
328///
329/// Cloning owned reports:
330///
331/// ```
332/// use rootcause::prelude::*;
333///
334/// fn process_error(error: Report<String, markers::Cloneable>) {
335///     // Can clone the error to pass to multiple handlers
336///     let for_logging = error.clone();
337///     let for_metrics = error.clone();
338///
339///     println!("Logging: {}", for_logging);
340///     println!("Metrics: {}", for_metrics);
341/// }
342///
343/// let report: Report<String> = report!("An error occurred".to_string());
344/// process_error(report.into_cloneable());
345/// ```
346///
347/// Using `clone_arc` on report references:
348///
349/// ```
350/// use rootcause::{ReportRef, prelude::*};
351///
352/// let report: Report<String, markers::Cloneable> = report!("Error".to_string()).into_cloneable();
353///
354/// // Get a reference (ReportRef is Copy, so this is cheap)
355/// let report_ref: ReportRef<String, markers::Cloneable> = report.as_ref();
356///
357/// // Clone the underlying Arc to get an owned Report
358/// let owned: Report<String, markers::Cloneable> = report_ref.clone_arc();
359/// ```
360#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
361pub struct Cloneable;
362
363/// Marker type for non-cloneable report references.
364///
365/// This marker is used exclusively with [`ReportRef<C,
366/// Uncloneable>`](crate::ReportRef) (not [`Report`](crate::Report)). It
367/// indicates that the reference does not provide the
368/// [`clone_arc`](crate::ReportRef::clone_arc) method to obtain an owned report.
369///
370/// Note that `ReportRef` itself is always `Copy` and `Clone` - you can always
371/// copy the reference itself. The `Uncloneable` marker only prevents cloning
372/// the underlying `Arc` to get an owned `Report`.
373///
374/// # Common Uses
375///
376/// `Uncloneable` references typically arise in two situations:
377///
378/// 1. **Taking a reference to a `Mutable` report**: When you call
379///    [`as_ref`](crate::Report::as_ref) on a `Report<C, Mutable>`, you get a
380///    `ReportRef<C, Uncloneable>` because the underlying report has unique
381///    ownership.
382///
383/// 2. **Explicitly restricting cloneability**: You can convert a `ReportRef<C,
384///    Cloneable>` to `ReportRef<C, Uncloneable>` when you want to pass a
385///    reference that explicitly cannot use `clone_arc`, ensuring the recipient
386///    can only inspect the report without obtaining ownership. This can be
387///    useful in APIs that need to accept both cloneable and uncloneable
388///    references.
389///
390///
391/// # Examples
392///
393/// Taking a reference to a `Mutable` report:
394///
395/// ```
396/// use rootcause::{ReportRef, prelude::*};
397///
398/// let report: Report<String, markers::Mutable> = report!("An error occurred".to_string());
399///
400/// // Taking a reference to a Mutable report gives an Uncloneable reference
401/// let report_ref: ReportRef<String, markers::Uncloneable> = report.as_ref();
402///
403/// // The reference itself can be copied (ReportRef is Copy)
404/// let copy = report_ref;
405///
406/// // But you cannot clone the underlying Arc to get an owned Report
407/// // let owned = report_ref.clone_arc(); // ❌ Method not available
408/// ```
409///
410/// Explicitly converting to `Uncloneable`:
411///
412/// ```
413/// use rootcause::{ReportRef, prelude::*};
414///
415/// let report: Report<String, markers::Cloneable> = report!("Error".to_string()).into_cloneable();
416///
417/// let cloneable_ref: ReportRef<String, markers::Cloneable> = report.as_ref();
418///
419/// // Convert to uncloneable to restrict the recipient's ability to clone
420/// let uncloneable_ref: ReportRef<String, markers::Uncloneable> = cloneable_ref.into_uncloneable();
421/// ```
422#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
423pub struct Uncloneable;
424
425/// Marker type indicating that a report and all its contents are `Send + Sync`.
426///
427/// This is the default thread-safety marker for reports. When all the context
428/// objects and attachments in a report implement `Send + Sync`, the report
429/// itself can be safely sent to other threads and shared between threads.
430///
431/// # When to Use
432///
433/// Most standard types in Rust are `Send + Sync`, including:
434/// - Primitive types (`i32`, `String`, `Vec`, etc.)
435/// - Most standard library types
436/// - Types explicitly designed for concurrent use
437///
438/// Use `SendSync` (the default) unless you have a specific need for
439/// thread-local data.
440///
441/// # Examples
442///
443/// ```
444/// use std::thread;
445///
446/// use rootcause::prelude::*;
447///
448/// // String is Send + Sync, so the report is too
449/// let report: Report<String, markers::Mutable, markers::SendSync> =
450///     report!("Thread-safe error".to_string());
451///
452/// // Can send to another thread
453/// thread::spawn(move || {
454///     println!("Error in thread: {}", report);
455/// })
456/// .join()
457/// .unwrap();
458/// ```
459#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
460pub struct SendSync;
461
462/// Marker type indicating that a report is not `Send` or `Sync`.
463///
464/// This marker is used when a report contains thread-local data that cannot be
465/// safely sent between threads or shared across threads. Common examples
466/// include `Rc`, raw pointers, or types that explicitly opt out of
467/// `Send`/`Sync`.
468///
469/// # When to Use
470///
471/// Use `Local` when your error context or attachments contain:
472/// - `Rc<T>` or `Weak<T>` (use `Arc<T>` for thread-safe alternative)
473/// - Raw pointers (`*const T`, `*mut T`)
474/// - Types that wrap thread-local storage
475/// - Any type that is `!Send` or `!Sync`
476///
477/// # Converting to Local
478///
479/// You can convert a thread-safe report to a local one using
480/// [`into_local`](crate::Report::into_local), or create a local report directly
481/// when the context type is not `Send + Sync`.
482///
483/// # Examples
484///
485/// ```
486/// use std::rc::Rc;
487///
488/// use rootcause::prelude::*;
489///
490/// // Rc is not Send or Sync, so the report must be Local
491/// let local_data: Rc<String> = Rc::new("Thread-local error".to_string());
492/// let report: Report<Rc<String>, markers::Mutable, markers::Local> = report!(local_data);
493///
494/// // This report cannot be sent to another thread
495/// // std::thread::spawn(move || {
496/// //     println!("{}", report); // ❌ Won't compile
497/// // });
498/// ```
499///
500/// Converting a thread-safe report to local:
501///
502/// ```
503/// use std::rc::Rc;
504///
505/// use rootcause::prelude::*;
506///
507/// let report: Report<String> = report!("Error".to_string());
508///
509/// // Convert to local report so we can use thread-local data
510/// let local_report: Report<String, markers::Mutable, markers::Local> = report.into_local();
511/// ```
512#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
513pub struct Local;
514
515mod sealed_report_ownership_marker {
516    use super::*;
517
518    pub trait Sealed: 'static {}
519
520    impl Sealed for Mutable {}
521    impl Sealed for Cloneable {}
522}
523
524mod sealed_object_marker {
525    pub trait Sealed: 'static {}
526
527    impl<C: Sized + 'static> Sealed for C {}
528}
529
530/// Marker trait for ownership semantics of owned reports.
531///
532/// This trait is implemented for [`Mutable`] and [`Cloneable`], the two
533/// ownership markers that can be used with [`Report<C, O, T>`](crate::Report).
534/// It exists primarily to enable the associated type `RefMarker`, which
535/// determines the ownership marker used when creating a reference to the
536/// report.
537///
538/// # Relationship to Report Construction
539///
540/// While this trait defines the ownership modes, the actual enforcement of
541/// ownership invariants happens at report construction time. It is impossible
542/// to construct a `Report<_, Mutable>` that doesn't have unique ownership, or a
543/// `Report<_, Cloneable>` that hasn't been properly set up for shared
544/// ownership.
545///
546/// # Ownership Modes
547///
548/// - [`Mutable`]: Indicates unique ownership. The report can be mutated but not
549///   cloned. Methods like [`attach`](crate::Report::attach) and
550///   [`as_mut`](crate::Report::as_mut) are only available in this mode.
551///
552/// - [`Cloneable`]: Indicates shared ownership via reference counting. The
553///   report can be cloned cheaply but cannot be mutated since there may be
554///   multiple references.
555///
556/// # Associated Types
557///
558/// The `RefMarker` associated type determines what kind of reference you get
559/// when calling [`as_ref`](crate::Report::as_ref):
560/// - For [`Mutable`], this is [`Uncloneable`] (the reference cannot use
561///   `clone_arc`)
562/// - For [`Cloneable`], this is [`Cloneable`] (the reference can use
563///   `clone_arc`)
564///
565/// # Implementation
566///
567/// This trait is sealed and cannot be implemented outside of this crate. You
568/// should use the provided implementations for [`Mutable`] and [`Cloneable`].
569pub trait ReportOwnershipMarker: sealed_report_ownership_marker::Sealed {
570    /// The ownership marker for references to this report type.
571    ///
572    /// This determines what type of reference is created when you call
573    /// [`Report::as_ref`](crate::Report::as_ref):
574    ///
575    /// - For [`Mutable`]: Returns [`ReportRef<C,
576    ///   Uncloneable>`](crate::ReportRef) because the underlying report has
577    ///   unique ownership
578    /// - For [`Cloneable`]: Returns [`ReportRef<C,
579    ///   Cloneable>`](crate::ReportRef) because the underlying report already
580    ///   uses shared ownership
581    type RefMarker;
582}
583impl ReportOwnershipMarker for Mutable {
584    type RefMarker = Uncloneable;
585}
586impl ReportOwnershipMarker for Cloneable {
587    type RefMarker = Cloneable;
588}
589
590/// Marker trait combining object and thread-safety requirements.
591///
592/// This trait enforces thread-safety constraints for context and attachment
593/// data at report construction time. Reports can only be constructed when their
594/// context and attachments satisfy the requirements of the thread-safety
595/// marker.
596///
597/// # Implementations
598///
599/// - For `T = Local`: Implemented for all `Sized + 'static` types, regardless
600///   of their `Send`/`Sync` status. This allows using types like `Rc` in local
601///   reports.
602///
603/// - For `T = SendSync`: Implemented only for `Sized + 'static` types that are
604///   also `Send + Sync`. This ensures thread-safe reports can only be
605///   constructed with thread-safe data.
606///
607/// # Enforcement at Construction
608///
609/// The key insight is that this trait is used as a bound during report
610/// construction. You cannot create a `Report<C, _, SendSync>` unless `C:
611/// ObjectMarkerFor<SendSync>`, which requires `C: Send + Sync`. This makes it
612/// impossible to accidentally create an invalid report:
613///
614/// ```compile_fail
615/// use std::rc::Rc;
616/// use rootcause::prelude::*;
617///
618/// // This won't compile because Rc is not Send + Sync
619/// let rc_data: Rc<String> = Rc::new("error".to_string());
620/// let report: Report<Rc<String>, markers::Mutable, markers::SendSync> = report!(rc_data);
621/// ```
622///
623/// Use [`Local`] instead for non-thread-safe data:
624///
625/// ```
626/// use std::rc::Rc;
627///
628/// use rootcause::prelude::*;
629///
630/// let rc_data: Rc<String> = Rc::new("error".to_string());
631/// let report: Report<Rc<String>, markers::Mutable, markers::Local> = report!(rc_data);
632/// ```
633//
634// # Safety:
635//
636// This trait is sealed and cannot be implemented outside of this crate. It is
637// guaranteed to only be implemented for the combinations of types and
638// thread-safety markers listed above.
639pub trait ObjectMarkerFor<T>: sealed_object_marker::Sealed + Sized + 'static {
640    /// Runs report creation hooks specific to this thread-safety marker.
641    #[doc(hidden)]
642    #[track_caller]
643    fn run_creation_hooks(report: ReportMut<'_, Dynamic, T>);
644}
645
646impl<O: Sized + 'static> ObjectMarkerFor<Local> for O {
647    #[inline(always)]
648    fn run_creation_hooks(report: ReportMut<'_, Dynamic, Local>) {
649        crate::hooks::report_creation::run_creation_hooks_local(report);
650    }
651}
652
653impl<O: Sized + 'static> ObjectMarkerFor<SendSync> for O
654where
655    O: Send + Sync,
656{
657    #[inline(always)]
658    fn run_creation_hooks(report: ReportMut<'_, Dynamic, SendSync>) {
659        crate::hooks::report_creation::run_creation_hooks_sendsync(report);
660    }
661}