rt.rs - source
Description: Source of the Rust file `library/core/src/fmt/rt.rs`.

#
core/fmt/
rt.rs

    1#![allow(missing_debug_implementations)]
    2#![unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
    3
    4//! All types and methods in this file are used by the compiler in
    5//! the expansion/lowering of format_args!().
    6//!
    7//! Do not modify them without understanding the consequences for the format_args!() macro.
    8
    9use super::*;
    10use crate::hint::unreachable_unchecked;
    11use crate::ptr::NonNull;
    12
    13#[derive(Copy, Clone)]
    14enum ArgumentType<'a> {
    15    Placeholder {
    16        // INVARIANT: `formatter` has type `fn(&T, _) -> _` for some `T`, and `value`
    17        // was derived from a `&'a T`.
    18        value: NonNull<()>,
    19        formatter: unsafe fn(NonNull<()>, &mut Formatter<'_>) -> Result,
    20        _lifetime: PhantomData<&'a ()>,
    21    },
    22    Count(u16),
    23}
    24
    25/// This struct represents a generic "argument" which is taken by format_args!().
    26///
    27/// This can be either a placeholder argument or a count argument.
    28/// * A placeholder argument contains a function to format the given value. At
    29///   compile time it is ensured that the function and the value have the correct
    30///   types, and then this struct is used to canonicalize arguments to one type.
    31///   Placeholder arguments are essentially an optimized partially applied formatting
    32///   function, equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`.
    33/// * A count argument contains a count for dynamic formatting parameters like
    34///   precision and width.
    35#[lang = "format_argument"]
    36#[derive(Copy, Clone)]
    37#[repr(align(2))] // To ensure pointers to this struct always have their lowest bit cleared.
    38pub struct Argument<'a> {
    39    ty: ArgumentType<'a>,
    40}
    41
    42macro_rules! argument_new {
    43    ($t:ty, $x:expr, $f:expr) => {
    44        Argument {
    45            // INVARIANT: this creates an `ArgumentType<'a>` from a `&'a T` and
    46            // a `fn(&T, ...)`, so the invariant is maintained.
    47            ty: ArgumentType::Placeholder {
    48                value: NonNull::<$t>::from_ref($x).cast(),
    49                // The Rust ABI considers all pointers to be equivalent, so transmuting a fn(&T) to
    50                // fn(NonNull<()>) and calling it with a NonNull<()> that points at a T is allowed.
    51                // However, the CFI sanitizer does not allow this, and triggers a crash when it
    52                // happens.
    53                //
    54                // To avoid this crash, we use a helper function when CFI is enabled. To avoid the
    55                // cost of this helper function (mainly code-size) when it is not needed, we
    56                // transmute the function pointer otherwise.
    57                //
    58                // This is similar to what the Rust compiler does internally with vtables when KCFI
    59                // is enabled, where it generates trampoline functions that only serve to adjust the
    60                // expected type of the argument. `ArgumentType::Placeholder` is a bit like a
    61                // manually constructed trait object, so it is not surprising that the same approach
    62                // has to be applied here as well.
    63                //
    64                // It is still considered problematic (from the Rust side) that CFI rejects entirely
    65                // legal Rust programs, so we do not consider anything done here a stable guarantee,
    66                // but meanwhile we carry this work-around to keep Rust compatible with CFI and
    67                // KCFI.
    68                #[cfg(not(any(sanitize = "cfi", sanitize = "kcfi")))]
    69                formatter: {
    70                    let f: fn(&$t, &mut Formatter<'_>) -> Result = $f;
    71                    // SAFETY: This is only called with `value`, which has the right type.
    72                    unsafe { core::mem::transmute(f) }
    73                },
    74                #[cfg(any(sanitize = "cfi", sanitize = "kcfi"))]
    75                formatter: |ptr: NonNull<()>, fmt: &mut Formatter<'_>| {
    76                    let func = $f;
    77                    // SAFETY: This is the same type as the `value` field.
    78                    let r = unsafe { ptr.cast::<$t>().as_ref() };
    79                    (func)(r, fmt)
    80                },
    81                _lifetime: PhantomData,
    82            },
    83        }
    84    };
    85}
    86
    87impl Argument<'_> {
    88    #[inline]
    89    pub const fn new_display<T: Display>(x: &T) -> Argument<'_> {
    90        argument_new!(T, x, <T as Display>::fmt)
    91    }
    92    #[inline]
    93    pub const fn new_debug<T: Debug>(x: &T) -> Argument<'_> {
    94        argument_new!(T, x, <T as Debug>::fmt)
    95    }
    96    #[inline]
    97    pub const fn new_debug_noop<T: Debug>(x: &T) -> Argument<'_> {
    98        argument_new!(T, x, |_: &T, _| Ok(()))
    99    }
    100    #[inline]
    101    pub const fn new_octal<T: Octal>(x: &T) -> Argument<'_> {
    102        argument_new!(T, x, <T as Octal>::fmt)
    103    }
    104    #[inline]
    105    pub const fn new_lower_hex<T: LowerHex>(x: &T) -> Argument<'_> {
    106        argument_new!(T, x, <T as LowerHex>::fmt)
    107    }
    108    #[inline]
    109    pub const fn new_upper_hex<T: UpperHex>(x: &T) -> Argument<'_> {
    110        argument_new!(T, x, <T as UpperHex>::fmt)
    111    }
    112    #[inline]
    113    pub const fn new_pointer<T: Pointer>(x: &T) -> Argument<'_> {
    114        argument_new!(T, x, <T as Pointer>::fmt)
    115    }
    116    #[inline]
    117    pub const fn new_binary<T: Binary>(x: &T) -> Argument<'_> {
    118        argument_new!(T, x, <T as Binary>::fmt)
    119    }
    120    #[inline]
    121    pub const fn new_lower_exp<T: LowerExp>(x: &T) -> Argument<'_> {
    122        argument_new!(T, x, <T as LowerExp>::fmt)
    123    }
    124    #[inline]
    125    pub const fn new_upper_exp<T: UpperExp>(x: &T) -> Argument<'_> {
    126        argument_new!(T, x, <T as UpperExp>::fmt)
    127    }
    128    #[inline]
    129    #[track_caller]
    130    pub const fn from_usize(x: &usize) -> Argument<'_> {
    131        if *x > u16::MAX as usize {
    132            panic!("Formatting argument out of range");
    133        }
    134        Argument { ty: ArgumentType::Count(*x as u16) }
    135    }
    136
    137    /// Format this placeholder argument.
    138    ///
    139    /// # Safety
    140    ///
    141    /// This argument must actually be a placeholder argument.
    142    #[inline]
    143    pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result {
    144        match self.ty {
    145            // SAFETY:
    146            // Because of the invariant that if `formatter` had the type
    147            // `fn(&T, _) -> _` then `value` has type `&'b T` where `'b` is
    148            // the lifetime of the `ArgumentType`, and because references
    149            // and `NonNull` are ABI-compatible, this is completely equivalent
    150            // to calling the original function passed to `new` with the
    151            // original reference, which is sound.
    152            ArgumentType::Placeholder { formatter, value, .. } => unsafe { formatter(value, f) },
    153            // SAFETY: the caller promised this.
    154            ArgumentType::Count(_) => unsafe { unreachable_unchecked() },
    155        }
    156    }
    157
    158    #[inline]
    159    pub(super) const fn as_u16(&self) -> Option<u16> {
    160        match self.ty {
    161            ArgumentType::Count(count) => Some(count),
    162            ArgumentType::Placeholder { .. } => None,
    163        }
    164    }
    165}