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 {}