abi_stable/abi_stability/
extra_checks.rs

1//! Contains items for adding checks to individual types.
2//!
3//! # Implementing and using ExtraChecks
4//!
5//! To add extra checks to a type follow these steps:
6//!
7//! - Create some type and implement ExtraChecks for it,
8//!
9//! - Apply the `#[sabi(extra_checks = const expression that implements ExtraChecks)]`
10//!     attribute to a type that uses `#[derive(StableAbi)]`.
11//!
12//! # Combination
13//!
14//! This is how an ExtraChecks can be combined across all
15//! dynamic libraries to ensure some property(which can be relied on for safety).
16//!
17//! This is a very similar process to how abi_stable ensures that
18//! vtables and modules are consistent across dynamic libraries.
19//!
20//! ### Failure
21//!
22//! Loading many libraries that contain ExtraChecks trait objects that need
23//! to be combined can fail if the representative version of the trait objects
24//! are incompatible with those of the library,
25//! even if both the library and the binary are otherwise compatible.
26//!
27//! The graphs below uses the `LIBRARY( ExtraChecks trait object )` format,
28//! where the trait object is compatible only if the one in the binary
29//! is a prefix of the string in the library,
30//! and all the libraries have a prefix of the same string.
31//!
32//! This is fine:
33//!
34//! ```text
35//! A("ab")<---B("abc")
36//! \__________C("abcd")
37//! ```
38//!
39//! This is not fine
40//!
41//! ```text
42//!  __________D("abe")
43//! /
44//! A("ab")<---B("abc")
45//! \__________C("abcd")
46//! ```
47//!
48//! The case that is not fine happens when the `ExtraChecks_TO::combine` method returned an error.
49//!
50//!
51//! ### Example
52//!
53//! ```
54//!
55//! use abi_stable::{
56//!     abi_stability::{
57//!         check_layout_compatibility,
58//!         extra_checks::{
59//!             ExtraChecks, ExtraChecksBox, ExtraChecksError, ExtraChecksRef,
60//!             ExtraChecksStaticRef, ForExtraChecksImplementor, TypeCheckerMut,
61//!         },
62//!     },
63//!     marker_type::UnsafeIgnoredType,
64//!     sabi_extern_fn,
65//!     sabi_trait::prelude::TD_Opaque,
66//!     std_types::{RCow, RCowSlice, ROption, RResult, RSome, RStr},
67//!     type_layout::TypeLayout,
68//!     GetStaticEquivalent, StableAbi,
69//! };
70//!
71//! use std::fmt::{self, Display};
72//!
73//! const LAYOUT0: &'static TypeLayout = <WithConstant<V1_0> as StableAbi>::LAYOUT;
74//! const LAYOUT1: &'static TypeLayout = <WithConstant<V1_1> as StableAbi>::LAYOUT;
75//! const LAYOUT1B: &'static TypeLayout =
76//!     <WithConstant<V1_1_Incompatible> as StableAbi>::LAYOUT;
77//! const LAYOUT2: &'static TypeLayout = <WithConstant<V1_2> as StableAbi>::LAYOUT;
78//!
79//! fn main() {
80//!     // Compared LAYOUT0 to LAYOUT1B,
81//!     // then stored LAYOUT0.extra_checks as the ExtraChecks associated with both layouts.
82//!     check_layout_compatibility(LAYOUT0, LAYOUT1B).unwrap();
83//!
84//!     // Compared LAYOUT1 to LAYOUT2,
85//!     // then stored LAYOUT2.extra_checks as the ExtraChecks associated with both layouts.
86//!     check_layout_compatibility(LAYOUT1, LAYOUT2).unwrap();
87//!
88//!     // Compared LAYOUT0 to LAYOUT2:
89//!     // - the comparison succeeded,
90//!     // - then both are combined.
91//!     // - The combined trait object is attempted to be combined with the
92//!     //      ExtraChecks in the global map associated to both LAYOUT0 and LAYOUT2,
93//!     //      which are LAYOUT1B.extra_checks and LAYOUT2.extra_checks respectively.
94//!     // - Combining the trait objects with the ones in the global map fails because
95//!     //      the one from LAYOUT1B is incompatible with the one from LAYOUT2.
96//!     check_layout_compatibility(LAYOUT0, LAYOUT2).unwrap_err();
97//! }
98//!
99//! //////////////////////////////////////////////////////////////////////////////////
100//!
101//! #[repr(C)]
102//! #[derive(StableAbi)]
103//! #[sabi(
104//!     // Replaces the C:StableAbi constraint with `C:GetStaticEquivalent`
105//!     // (a supertrait of StableAbi).
106//!     not_stableabi(C),
107//!     bound(C: GetConstant),
108//!     extra_checks = Self::CHECKER
109//! )]
110//! struct WithConstant<C> {
111//!     // UnsafeIgnoredType is equivalent to PhantomData,
112//!     // except that all `UnsafeIgnoredType` are considered the same type by `StableAbi`.
113//!     _marker: UnsafeIgnoredType<C>,
114//! }
115//!
116//! impl<C> WithConstant<C> {
117//!     const NEW: Self = Self {
118//!         _marker: UnsafeIgnoredType::NEW,
119//!     };
120//! }
121//!
122//! impl<C> WithConstant<C>
123//! where
124//!     C: GetConstant,
125//! {
126//!     const CHECKER: ConstChecker = ConstChecker {
127//!         chars: RStr::from_str(C::CHARS),
128//!     };
129//! }
130//!
131//! trait GetConstant {
132//!     const CHARS: &'static str;
133//! }
134//!
135//! use self::constants::*;
136//!
137//! #[allow(non_camel_case_types)]
138//! mod constants {
139//!     use super::*;
140//!
141//!     #[derive(GetStaticEquivalent)]
142//!     pub struct V1_0;
143//!
144//!     impl GetConstant for V1_0 {
145//!         const CHARS: &'static str = "ab";
146//!     }
147//!
148//!     #[derive(GetStaticEquivalent)]
149//!     pub struct V1_1;
150//!
151//!     impl GetConstant for V1_1 {
152//!         const CHARS: &'static str = "abc";
153//!     }
154//!
155//!     #[derive(GetStaticEquivalent)]
156//!     pub struct V1_1_Incompatible;
157//!
158//!     impl GetConstant for V1_1_Incompatible {
159//!         const CHARS: &'static str = "abd";
160//!     }
161//!
162//!     #[derive(GetStaticEquivalent)]
163//!     pub struct V1_2;
164//!
165//!     impl GetConstant for V1_2 {
166//!         const CHARS: &'static str = "abcd";
167//!     }
168//! }
169//!
170//! /////////////////////////////////////////
171//!
172//! #[repr(C)]
173//! #[derive(Debug, Clone, StableAbi)]
174//! pub struct ConstChecker {
175//!     chars: RStr<'static>,
176//! }
177//!
178//! impl Display for ConstChecker {
179//!     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180//!         writeln!(
181//!             f,
182//!             "ConstChecker: \
183//!         Checks that the associated constant for \
184//!         the other type is compatible with:\n{}\n.\
185//!     ",
186//!             self.chars
187//!         )
188//!     }
189//! }
190//!
191//! impl ConstChecker {
192//!     fn check_compatible_inner(
193//!         &self,
194//!         other: &ConstChecker,
195//!     ) -> Result<(), UnequalConstError> {
196//!         if other.chars.starts_with(&*self.chars) {
197//!             Ok(())
198//!         } else {
199//!             Err(UnequalConstError {
200//!                 expected: self.chars,
201//!                 found: other.chars,
202//!             })
203//!         }
204//!     }
205//! }
206//! unsafe impl ExtraChecks for ConstChecker {
207//!     fn type_layout(&self) -> &'static TypeLayout {
208//!         <Self as StableAbi>::LAYOUT
209//!     }
210//!
211//!     fn check_compatibility(
212//!         &self,
213//!         _layout_containing_self: &'static TypeLayout,
214//!         layout_containing_other: &'static TypeLayout,
215//!         checker: TypeCheckerMut<'_>,
216//!     ) -> RResult<(), ExtraChecksError> {
217//!         Self::downcast_with_layout(layout_containing_other, checker, |other, _| {
218//!             self.check_compatible_inner(other)
219//!         })
220//!     }
221//!
222//!     fn nested_type_layouts(&self) -> RCowSlice<'_, &'static TypeLayout> {
223//!         RCow::from_slice(&[])
224//!     }
225//!
226//!     fn combine(
227//!         &self,
228//!         other: ExtraChecksRef<'_>,
229//!         checker: TypeCheckerMut<'_>,
230//!     ) -> RResult<ROption<ExtraChecksBox>, ExtraChecksError> {
231//!         Self::downcast_with_object(other, checker, |other, _| {
232//!             let (min, max) = min_max_by(self, other, |x| x.chars.len());
233//!             min.check_compatible_inner(max)
234//!                 .map(|_| RSome(ExtraChecksBox::from_value(max.clone(), TD_Opaque)))
235//!         })
236//!     }
237//! }
238//!
239//! #[derive(Debug, Clone)]
240//! pub struct UnequalConstError {
241//!     expected: RStr<'static>,
242//!     found: RStr<'static>,
243//! }
244//!
245//! impl Display for UnequalConstError {
246//!     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247//!         writeln!(
248//!     f,
249//!     "Expected the `GetConstant::CHARS` associated constant to be compatible with:\
250//!      \n    {}\
251//!      \nFound:\
252//!      \n    {}\
253//!     ",
254//!     self.expected,
255//!     self.found,
256//! )
257//!     }
258//! }
259//!
260//! impl std::error::Error for UnequalConstError {}
261//!
262//! pub(crate) fn min_max_by<T, F, K>(l: T, r: T, mut f: F) -> (T, T)
263//! where
264//!     F: FnMut(&T) -> K,
265//!     K: Ord,
266//! {
267//!     if f(&l) < f(&r) {
268//!         (l, r)
269//!     } else {
270//!         (r, l)
271//!     }
272//! }
273//!
274//!
275//! ```
276//!
277//!
278//! # Examples
279//!
280//! ### Alphabetic.
281//!
282//! This defines an ExtraChecks which checks that fields are alphabetically sorted
283//!
284//! ```
285//! use abi_stable::{
286//!     abi_stability::{
287//!         check_layout_compatibility,
288//!         extra_checks::{
289//!             ExtraChecks, ExtraChecksError, ExtraChecksStaticRef,
290//!             ForExtraChecksImplementor, TypeCheckerMut,
291//!         },
292//!     },
293//!     sabi_extern_fn,
294//!     sabi_trait::prelude::TD_Opaque,
295//!     std_types::{RCow, RCowSlice, RDuration, ROption, RResult, RStr, RString},
296//!     type_layout::TypeLayout,
297//!     StableAbi,
298//! };
299//!
300//! use std::fmt::{self, Display};
301//!
302//! fn main() {
303//!     let rect_layout = <Rectangle as StableAbi>::LAYOUT;
304//!     let person_layout = <Person as StableAbi>::LAYOUT;
305//!
306//!     // This passes because the fields are in order
307//!     check_layout_compatibility(rect_layout, rect_layout)
308//!         .unwrap_or_else(|e| panic!("{}", e));
309//!
310//!     // This errors because the struct's fields aren't in order
311//!     check_layout_compatibility(person_layout, person_layout).unwrap_err();
312//! }
313//!
314//! #[repr(C)]
315//! #[derive(StableAbi)]
316//! #[sabi(extra_checks = InOrderChecker)]
317//! struct Rectangle {
318//!     x: u32,
319//!     y: u32,
320//!     z: u32,
321//! }
322//!
323//! #[repr(C)]
324//! #[derive(StableAbi)]
325//! #[sabi(extra_checks = InOrderChecker)]
326//! struct Person {
327//!     name: RString,
328//!     surname: RString,
329//!     age: RDuration,
330//! }
331//!
332//! /////////////////////////////////////////
333//!
334//! #[repr(C)]
335//! #[derive(Debug, Clone, StableAbi)]
336//! pub struct InOrderChecker;
337//!
338//! impl Display for InOrderChecker {
339//!     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340//!         f.write_str(
341//!             "InOrderChecker: Checks that field names are sorted alphabetically.",
342//!         )
343//!     }
344//! }
345//!
346//! unsafe impl ExtraChecks for InOrderChecker {
347//!     fn type_layout(&self) -> &'static TypeLayout {
348//!         <Self as StableAbi>::LAYOUT
349//!     }
350//!
351//!     fn check_compatibility(
352//!         &self,
353//!         layout_containing_self: &'static TypeLayout,
354//!         layout_containing_other: &'static TypeLayout,
355//!         checker: TypeCheckerMut<'_>,
356//!     ) -> RResult<(), ExtraChecksError> {
357//!         Self::downcast_with_layout(layout_containing_other, checker, |_, _| {
358//!             let fields = match layout_containing_self.get_fields() {
359//!                 Some(fields) if !fields.is_empty() => fields,
360//!                 _ => return Ok(()),
361//!             };
362//!
363//!             let mut prev = fields.iter().next().unwrap();
364//!             for curr in fields {
365//!                 if prev.name() > curr.name() {
366//!                     return Err(OutOfOrderError {
367//!                         previous_one: prev.name(),
368//!                         first_one: curr.name(),
369//!                     });
370//!                 }
371//!                 prev = curr;
372//!             }
373//!             Ok(())
374//!         })
375//!     }
376//!
377//!     fn nested_type_layouts(&self) -> RCowSlice<'_, &'static TypeLayout> {
378//!         RCow::from_slice(&[])
379//!     }
380//! }
381//!
382//! #[derive(Debug, Clone)]
383//! pub struct OutOfOrderError {
384//!     previous_one: &'static str,
385//!
386//!     /// The first field that is out of order.
387//!     first_one: &'static str,
388//! }
389//!
390//! impl Display for OutOfOrderError {
391//!     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
392//!         writeln!(
393//!             f,
394//!             "Expected fields to be alphabetically sorted.\n\
395//!          Found field '{}' before '{}'\
396//!         ",
397//!             self.previous_one, self.first_one,
398//!         )
399//!     }
400//! }
401//!
402//! impl std::error::Error for OutOfOrderError {}
403//!
404//!
405//! ```
406//!
407//! ### Associated Constant.
408//!
409//! This defines an ExtraChecks which checks that an associated constant is
410//! the same for both types.
411//!
412//! ```
413//! use abi_stable::{
414//!     abi_stability::{
415//!         check_layout_compatibility,
416//!         extra_checks::{
417//!             ExtraChecks, ExtraChecksError, ExtraChecksStaticRef,
418//!             ForExtraChecksImplementor, TypeCheckerMut,
419//!         },
420//!     },
421//!     marker_type::UnsafeIgnoredType,
422//!     sabi_extern_fn,
423//!     sabi_trait::prelude::TD_Opaque,
424//!     std_types::{RCow, RCowSlice, RDuration, RResult, RStr, RString},
425//!     type_layout::TypeLayout,
426//!     GetStaticEquivalent, StableAbi,
427//! };
428//!
429//! use std::fmt::{self, Display};
430//!
431//! fn main() {
432//!     let const0 = <WithConstant<N0> as StableAbi>::LAYOUT;
433//!     let const_second_0 = <WithConstant<SecondN0> as StableAbi>::LAYOUT;
434//!     let const1 = <WithConstant<N1> as StableAbi>::LAYOUT;
435//!     let const2 = <WithConstant<N2> as StableAbi>::LAYOUT;
436//!
437//!     check_layout_compatibility(const0, const0).unwrap();
438//!     check_layout_compatibility(const_second_0, const_second_0).unwrap();
439//!     check_layout_compatibility(const1, const1).unwrap();
440//!     check_layout_compatibility(const2, const2).unwrap();
441//!
442//!     ////////////
443//!     // WithConstant<SecondN0> and WithConstant<N0> are compatible with each other
444//!     // because their `GetConstant::NUMBER` associated constant is the same value.
445//!     check_layout_compatibility(const0, const_second_0).unwrap();
446//!     check_layout_compatibility(const_second_0, const0).unwrap();
447//!
448//!     ////////////
449//!     // None of the lines below are compatible because their
450//!     // `GetConstant::NUMBER` associated constant isn't the same value.
451//!     check_layout_compatibility(const0, const1).unwrap_err();
452//!     check_layout_compatibility(const0, const2).unwrap_err();
453//!
454//!     check_layout_compatibility(const1, const0).unwrap_err();
455//!     check_layout_compatibility(const1, const2).unwrap_err();
456//!
457//!     check_layout_compatibility(const2, const0).unwrap_err();
458//!     check_layout_compatibility(const2, const1).unwrap_err();
459//! }
460//!
461//! #[repr(C)]
462//! #[derive(StableAbi)]
463//! #[sabi(
464//!     // Replaces the C:StableAbi constraint with `C:GetStaticEquivalent`
465//!     // (a supertrait of StableAbi).
466//!     not_stableabi(C),
467//!     bound(C:GetConstant),
468//!     extra_checks = Self::CHECKER,
469//! )]
470//! struct WithConstant<C> {
471//!     // UnsafeIgnoredType is equivalent to PhantomData,
472//!     // except that all `UnsafeIgnoredType` are considered the same type by `StableAbi`.
473//!     _marker: UnsafeIgnoredType<C>,
474//! }
475//!
476//! impl<C> WithConstant<C> {
477//!     const NEW: Self = Self {
478//!         _marker: UnsafeIgnoredType::NEW,
479//!     };
480//! }
481//!
482//! impl<C> WithConstant<C>
483//! where
484//!     C: GetConstant,
485//! {
486//!     const CHECKER: ConstChecker = ConstChecker { number: C::NUMBER };
487//! }
488//!
489//! trait GetConstant {
490//!     const NUMBER: u64;
491//! }
492//!
493//! #[derive(GetStaticEquivalent)]
494//! struct N0;
495//! impl GetConstant for N0 {
496//!     const NUMBER: u64 = 0;
497//! }
498//!
499//! #[derive(GetStaticEquivalent)]
500//! struct SecondN0;
501//! impl GetConstant for SecondN0 {
502//!     const NUMBER: u64 = 0;
503//! }
504//!
505//! #[derive(GetStaticEquivalent)]
506//! struct N1;
507//! impl GetConstant for N1 {
508//!     const NUMBER: u64 = 1;
509//! }
510//!
511//! #[derive(GetStaticEquivalent)]
512//! struct N2;
513//! impl GetConstant for N2 {
514//!     const NUMBER: u64 = 2;
515//! }
516//!
517//! /////////////////////////////////////////
518//!
519//! #[repr(C)]
520//! #[derive(Debug, Clone, StableAbi)]
521//! pub struct ConstChecker {
522//!     number: u64,
523//! }
524//!
525//! impl Display for ConstChecker {
526//!     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
527//!         writeln!(
528//!             f,
529//!             "ConstChecker: \
530//!             Checks that the associated constant for \
531//!             for the other type is {}.\
532//!         ",
533//!             self.number
534//!         )
535//!     }
536//! }
537//!
538//! unsafe impl ExtraChecks for ConstChecker {
539//!     fn type_layout(&self) -> &'static TypeLayout {
540//!         <Self as StableAbi>::LAYOUT
541//!     }
542//!
543//!     fn check_compatibility(
544//!         &self,
545//!         layout_containing_self: &'static TypeLayout,
546//!         layout_containing_other: &'static TypeLayout,
547//!         checker: TypeCheckerMut<'_>,
548//!     ) -> RResult<(), ExtraChecksError> {
549//!         Self::downcast_with_layout(layout_containing_other, checker, |other, _| {
550//!             if self.number == other.number {
551//!                 Ok(())
552//!             } else {
553//!                 Err(UnequalConstError {
554//!                     expected: self.number,
555//!                     found: other.number,
556//!                 })
557//!             }
558//!         })
559//!     }
560//!
561//!     fn nested_type_layouts(&self) -> RCowSlice<'_, &'static TypeLayout> {
562//!         RCow::from_slice(&[])
563//!     }
564//! }
565//!
566//! #[derive(Debug, Clone)]
567//! pub struct UnequalConstError {
568//!     expected: u64,
569//!     found: u64,
570//! }
571//!
572//! impl Display for UnequalConstError {
573//!     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574//!         writeln!(
575//!             f,
576//!             "Expected the `GetConstant::NUMBER` associated constant to be:\
577//!          \n    {}\
578//!          \nFound:\
579//!          \n    {}\
580//!         ",
581//!             self.expected, self.found,
582//!         )
583//!     }
584//! }
585//!
586//! impl std::error::Error for UnequalConstError {}
587//!
588//!
589//! ```
590//!
591
592use crate::{
593    rtry, sabi_trait,
594    sabi_types::{RMut, RRef},
595    std_types::{RBox, RBoxError, RCowSlice, RNone, ROk, ROption, RResult},
596    traits::IntoReprC,
597    type_layout::TypeLayout,
598    StableAbi,
599};
600
601use std::{
602    error::Error as ErrorTrait,
603    fmt::{self, Display},
604};
605
606use core_extensions::SelfOps;
607
608#[sabi_trait]
609/// This checks that the layout of types coming from dynamic libraries
610/// are compatible with those of the binary/dynlib that loads them.
611///
612/// # Safety
613///
614/// This trait must not be implemented outside of `abi_stable`.
615///
616#[sabi(no_trait_impl)]
617// #[sabi(debug_print_trait)]
618// #[sabi(debug_print)]
619pub unsafe trait TypeChecker: 'static + Send + Sync {
620    /// Checks that `ìnterface` is compatible with `implementation.`
621    ///
622    /// This is equivalent to `check_layout_compatibility`,
623    /// except that it can also be called re-entrantly
624    /// (while `check_layout_compatibility` cannot be called re-entrantly)
625    ///
626    /// # Errors
627    ///
628    /// When calling the implementation of this trait used in `check_layout_compatibility`,
629    /// the errors detected in this method are always propagated by the free function,
630    /// to prevent the propagation of errors call the `local_check_compatibility` method.
631    ///
632    fn check_compatibility(
633        &mut self,
634        interface: &'static TypeLayout,
635        implementation: &'static TypeLayout,
636    ) -> RResult<(), ExtraChecksError>;
637
638    /// Checks that `ìnterface` is compatible with `implementation.`
639    ///
640    /// This is equivalent to the `check_compatibility` method,
641    /// except that it does not propagate errors automatically,
642    /// they must be returned as part of the error of the `ExtraChecks` that calls this.
643    #[sabi(last_prefix_field)]
644    fn local_check_compatibility(
645        &mut self,
646        interface: &'static TypeLayout,
647        implementation: &'static TypeLayout,
648    ) -> RResult<(), ExtraChecksError>;
649}
650
651/// An ffi-safe equivalent of &'b mut dyn TypeChecker
652pub type TypeCheckerMut<'b> = TypeChecker_TO<RMut<'b, ()>>;
653
654#[sabi_trait]
655/// Allows defining extra checks for a type.
656///
657/// Look at the
658/// [module level documentation](./index.html)
659/// for more details.
660///
661/// # Safety
662///
663/// The `type_layout` method must be defined as `<Self as ::abi_stable::StableAbi>::LAYOUT`,
664/// or equivalent.
665///
666/// All of the methods must be deterministic,
667/// always returning the same value with the same arguments.
668///
669#[sabi(no_trait_impl)]
670// #[sabi(debug_print_trait)]
671// #[sabi(debug_print)]
672pub unsafe trait ExtraChecks: 'static + Debug + Display + Clone + Send + Sync {
673    /// Gets the type layout of `Self`(the type that implements ExtraChecks)
674    ///
675    /// This is used to downcast the trait object in
676    /// `ForExtraChecksImplementor::downcast_*` methods,
677    /// by ensuring that its type layout is
678    /// compatible with that of another ExtraChecks trait object
679    ///
680    /// It can't use `UTypeId`s to check compatibility of trait objects
681    /// from different dynamic libraries,
682    /// because `UTypeId`s from different dynamic libraries are incompatible.
683    fn type_layout(&self) -> &'static TypeLayout;
684
685    /// Checks that `self` is compatible another type which implements ExtraChecks.
686    ///
687    /// Calling `check_layout_compatibility` from here will immediately return an error,
688    /// prefer doing `checker.check_compatibility(...)` instead.
689    ///
690    /// # Parameters
691    ///
692    /// `layout_containing_self`:
693    /// The TypeLayout that contains this `ExtraChecks` trait object in the extra_checks field.
694    ///
695    /// `layout_containing_other`:
696    /// The TypeLayout that contains the `other` `ExtraChecks` trait object in the extra_checks field,
697    /// which `self` is compared to.
698    ///
699    /// `checker`:
700    /// The type checker,which allows this function to check type layouts.
701    ///
702    ///
703    fn check_compatibility(
704        &self,
705        layout_containing_self: &'static TypeLayout,
706        layout_containing_other: &'static TypeLayout,
707        checker: TypeCheckerMut<'_>,
708    ) -> RResult<(), ExtraChecksError>;
709
710    /// Returns the `TypeLayout`s owned or referenced by `self`.
711    ///
712    /// This is necessary for the Debug implementation of `TypeLayout`.
713    fn nested_type_layouts(&self) -> RCowSlice<'_, &'static TypeLayout>;
714
715    /// Combines this ExtraChecks trait object with another one.
716    ///
717    /// To guarantee that the `extra_checks`
718    /// associated with a type (inside `<TheType as StableAbi>::LAYOUT.extra_cheks` )
719    /// has a single representative value across all dynamic libraries,
720    /// you must override this method,
721    /// and return `ROk(RSome(_))` by combining `self` and `other` in some way.
722    ///
723    ///
724    /// # Parameters
725    ///
726    /// `other`:
727    /// The other ExtraChecks trait object that this is combined with..
728    ///
729    /// `checker`:
730    /// The trait object of the type checker,which allows this function to check type layouts.
731    ///
732    ///
733    /// # Return value
734    ///
735    /// This returns:
736    ///
737    /// - `ROk(RNone)`:
738    ///     If `self` doesn't need to be unified across all dynamic libraries,
739    ///     or the representative version doesn't need to be updated.
740    ///
741    /// - `ROk(RSome(_))`:
742    ///     If `self` needs to be unified across all dynamic libraries,
743    ///     returning the combined `self` and `other`.
744    ///
745    /// - `RErr(_)`: If there was a problem unifying `self` and `other`.
746    ///
747    #[sabi(last_prefix_field)]
748    fn combine(
749        &self,
750        _other: ExtraChecksRef<'_>,
751        _checker: TypeCheckerMut<'_>,
752    ) -> RResult<ROption<ExtraChecksBox>, ExtraChecksError> {
753        ROk(RNone)
754    }
755}
756
757/// The version of `ExtraChecks` that is stored in `TypeLayout`.
758pub type StoredExtraChecks = ExtraChecks_CTO<'static>;
759
760/// An ffi-safe equivalent of `&'static dyn ExtraChecks`.
761pub type ExtraChecksStaticRef = ExtraChecks_TO<RRef<'static, ()>>;
762
763/// An ffi-safe equivalent of `&'a dyn ExtraChecks`.
764pub type ExtraChecksRef<'a> = ExtraChecks_TO<RRef<'a, ()>>;
765
766/// An ffi-safe equivalent of `Box<dyn ExtraChecks>`.
767pub type ExtraChecksBox = ExtraChecks_TO<RBox<()>>;
768
769/// An extension trait for `ExtraChecks` implementors.
770pub trait ForExtraChecksImplementor: StableAbi + ExtraChecks {
771    /// Accesses the `ExtraChecks` field in `layout_containing_other`, downcasted into `Self`.
772    ///
773    /// If the closure returns an `ExtraChecksError`,it'll be returned unmodified and unwrapped.
774    ///
775    /// # Returns
776    ///
777    /// - ROk(_):
778    ///     If `other` was downcasted to `Self`,and `f` did not return any errors.
779    ///
780    /// - RErr(ExtraChecksError::NoneExtraChecks):
781    ///     If`layout_containing_other` does not contain an ExtraChecks trait object.
782    ///
783    /// - RErr(ExtraChecksError::TypeChecker):
784    ///     If there is an error while type checking.
785    ///
786    /// - RErr(ExtraChecksError::ExtraChecks(_)):
787    ///     If there is an custom error within the function.
788    ///
789    fn downcast_with_layout<F, R, E>(
790        layout_containing_other: &'static TypeLayout,
791        checker: TypeCheckerMut<'_>,
792        f: F,
793    ) -> RResult<R, ExtraChecksError>
794    where
795        Self: 'static,
796        F: FnOnce(&Self, TypeCheckerMut<'_>) -> Result<R, E>,
797        E: Send + Sync + ErrorTrait + 'static,
798    {
799        let other = rtry!(layout_containing_other
800            .extra_checks()
801            .ok_or(ExtraChecksError::NoneExtraChecks));
802
803        Self::downcast_with_object(other, checker, f)
804    }
805
806    /// Allows one to access `other` downcast into `Self`.
807    ///
808    /// If the closure returns an `ExtraChecksError`,it'll be returned unmodified and unwrapped.
809    ///
810    /// # Returns
811    ///
812    /// - ROk(_):
813    ///     If `other` could be downcasted to `Self`,and `f` did not return any errors.
814    ///
815    /// - RErr(ExtraChecksError::TypeChecker):
816    ///     If there is an error while type checking.
817    ///
818    /// - RErr(ExtraChecksError::ExtraChecks(_)):
819    ///     If there is an custom error within the function.
820    fn downcast_with_object<F, R, E>(
821        other: ExtraChecksRef<'_>,
822        mut checker: TypeCheckerMut<'_>,
823        f: F,
824    ) -> RResult<R, ExtraChecksError>
825    where
826        F: FnOnce(&Self, TypeCheckerMut<'_>) -> Result<R, E>,
827        E: Send + Sync + ErrorTrait + 'static,
828    {
829        // This checks that the layouts of `this` and `other` are compatible,
830        // so that calling the `unchecked_downcast_into` method is sound.
831        rtry!(checker.check_compatibility(<Self as StableAbi>::LAYOUT, other.type_layout()));
832        let other_ue = unsafe { other.obj.unchecked_downcast_into::<Self>() };
833
834        f(other_ue.get(), checker)
835            .map_err(|e| match RBoxError::new(e).downcast::<ExtraChecksError>() {
836                Ok(x) => x.piped(RBox::into_inner),
837                Err(e) => ExtraChecksError::from_extra_checks(e),
838            })
839            .into_c()
840    }
841}
842
843impl<This> ForExtraChecksImplementor for This where This: ?Sized + StableAbi + ExtraChecks {}
844
845///////////////////////////////////////////////////////////////////////////////
846
847/// The errors returned from `ExtraChecks` and `ForExtraChecksImplementor` methods.
848#[repr(u8)]
849#[derive(Debug, StableAbi)]
850pub enum ExtraChecksError {
851    /// When a type checking error happens within `TypeChecker`.
852    TypeChecker,
853    /// Errors returned from `TypeChecker::local_check_compatibility`
854    TypeCheckerErrors(RBoxError),
855    /// When trying to get a ExtraChecks trait object from `TypeLayout.extra_checks==None` .
856    NoneExtraChecks,
857    /// A custom error returned by the ExtraChecker or
858    /// the closures in `ForExtraChecksImplementor::downcast_*`.
859    ExtraChecks(RBoxError),
860}
861
862impl ExtraChecksError {
863    /// Constructs a `ExtraChecksError::ExtraChecks` from an error.
864    pub fn from_extra_checks<E>(err: E) -> ExtraChecksError
865    where
866        E: Send + Sync + ErrorTrait + 'static,
867    {
868        let x = RBoxError::new(err);
869        ExtraChecksError::ExtraChecks(x)
870    }
871}
872
873impl Display for ExtraChecksError {
874    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
875        match self {
876            ExtraChecksError::TypeChecker => Display::fmt("A type checker error happened.", f),
877            ExtraChecksError::TypeCheckerErrors(e) => {
878                writeln!(f, "Errors that happened during type checking:\n{}", e)
879            }
880            ExtraChecksError::NoneExtraChecks => {
881                Display::fmt("No `ExtraChecks` in the implementation.", f)
882            }
883            ExtraChecksError::ExtraChecks(e) => Display::fmt(e, f),
884        }
885    }
886}
887
888impl std::error::Error for ExtraChecksError {}