#![allow(missing_debug_implementations)] #![unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] //! All types and methods in this file are used by the compiler in //! the expansion/lowering of format_args!(). //! //! Do not modify them without understanding the consequences for the format_args!() macro. use super::*; use crate::hint::unreachable_unchecked; use crate::ptr::NonNull; #[lang = "format_placeholder"] #[derive(Copy, Clone)] pub struct Placeholder { pub position: usize, pub flags: u32, pub precision: Count, pub width: Count, } /// Used by [width](https://doc.rust-lang.org/std/fmt/#width) /// and [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers. #[lang = "format_count"] #[derive(Copy, Clone)] pub enum Count { /// Specified with a literal number, stores the value Is(u16), /// Specified using `$` and `*` syntaxes, stores the index into `args` Param(usize), /// Not specified Implied, } #[derive(Copy, Clone)] enum ArgumentType<'a> { Placeholder { // INVARIANT: `formatter` has type `fn(&T, _) -> _` for some `T`, and `value` // was derived from a `&'a T`. value: NonNull<()>, formatter: unsafe fn(NonNull<()>, &mut Formatter<'_>) -> Result, _lifetime: PhantomData<&'a ()>, }, Count(u16), } /// This struct represents a generic "argument" which is taken by format_args!(). /// /// This can be either a placeholder argument or a count argument. /// * A placeholder argument contains a function to format the given value. At /// compile time it is ensured that the function and the value have the correct /// types, and then this struct is used to canonicalize arguments to one type. /// Placeholder arguments are essentially an optimized partially applied formatting /// function, equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`. /// * A count argument contains a count for dynamic formatting parameters like /// precision and width. #[lang = "format_argument"] #[derive(Copy, Clone)] pub struct Argument<'a> { ty: ArgumentType<'a>, } macro_rules! argument_new { ($t:ty, $x:expr, $f:expr) => { Argument { // INVARIANT: this creates an `ArgumentType<'a>` from a `&'a T` and // a `fn(&T, ...)`, so the invariant is maintained. ty: ArgumentType::Placeholder { value: NonNull::<$t>::from_ref($x).cast(), // The Rust ABI considers all pointers to be equivalent, so transmuting a fn(&T) to // fn(NonNull<()>) and calling it with a NonNull<()> that points at a T is allowed. // However, the CFI sanitizer does not allow this, and triggers a crash when it // happens. // // To avoid this crash, we use a helper function when CFI is enabled. To avoid the // cost of this helper function (mainly code-size) when it is not needed, we // transmute the function pointer otherwise. // // This is similar to what the Rust compiler does internally with vtables when KCFI // is enabled, where it generates trampoline functions that only serve to adjust the // expected type of the argument. `ArgumentType::Placeholder` is a bit like a // manually constructed trait object, so it is not surprising that the same approach // has to be applied here as well. // // It is still considered problematic (from the Rust side) that CFI rejects entirely // legal Rust programs, so we do not consider anything done here a stable guarantee, // but meanwhile we carry this work-around to keep Rust compatible with CFI and // KCFI. #[cfg(not(any(sanitize = "cfi", sanitize = "kcfi")))] formatter: { let f: fn(&$t, &mut Formatter<'_>) -> Result = $f; // SAFETY: This is only called with `value`, which has the right type. unsafe { core::mem::transmute(f) } }, #[cfg(any(sanitize = "cfi", sanitize = "kcfi"))] formatter: |ptr: NonNull<()>, fmt: &mut Formatter<'_>| { let func = $f; // SAFETY: This is the same type as the `value` field. let r = unsafe { ptr.cast::<$t>().as_ref() }; (func)(r, fmt) }, _lifetime: PhantomData, }, } }; } impl Argument<'_> { #[inline] pub const fn new_display(x: &T) -> Argument<'_> { argument_new!(T, x, ::fmt) } #[inline] pub const fn new_debug(x: &T) -> Argument<'_> { argument_new!(T, x, ::fmt) } #[inline] pub const fn new_debug_noop(x: &T) -> Argument<'_> { argument_new!(T, x, |_: &T, _| Ok(())) } #[inline] pub const fn new_octal(x: &T) -> Argument<'_> { argument_new!(T, x, ::fmt) } #[inline] pub const fn new_lower_hex(x: &T) -> Argument<'_> { argument_new!(T, x, ::fmt) } #[inline] pub const fn new_upper_hex(x: &T) -> Argument<'_> { argument_new!(T, x, ::fmt) } #[inline] pub const fn new_pointer(x: &T) -> Argument<'_> { argument_new!(T, x, ::fmt) } #[inline] pub const fn new_binary(x: &T) -> Argument<'_> { argument_new!(T, x, ::fmt) } #[inline] pub const fn new_lower_exp(x: &T) -> Argument<'_> { argument_new!(T, x, ::fmt) } #[inline] pub const fn new_upper_exp(x: &T) -> Argument<'_> { argument_new!(T, x, ::fmt) } #[inline] #[track_caller] pub const fn from_usize(x: &usize) -> Argument<'_> { if *x > u16::MAX as usize { panic!("Formatting argument out of range"); } Argument { ty: ArgumentType::Count(*x as u16) } } /// Format this placeholder argument. /// /// # Safety /// /// This argument must actually be a placeholder argument. #[inline] pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self.ty { // SAFETY: // Because of the invariant that if `formatter` had the type // `fn(&T, _) -> _` then `value` has type `&'b T` where `'b` is // the lifetime of the `ArgumentType`, and because references // and `NonNull` are ABI-compatible, this is completely equivalent // to calling the original function passed to `new` with the // original reference, which is sound. ArgumentType::Placeholder { formatter, value, .. } => unsafe { formatter(value, f) }, // SAFETY: the caller promised this. ArgumentType::Count(_) => unsafe { unreachable_unchecked() }, } } #[inline] pub(super) const fn as_u16(&self) -> Option { match self.ty { ArgumentType::Count(count) => Some(count), ArgumentType::Placeholder { .. } => None, } } } /// Used by the format_args!() macro to create a fmt::Arguments object. #[doc(hidden)] #[unstable(feature = "fmt_internals", issue = "none")] #[rustc_diagnostic_item = "FmtArgumentsNew"] impl<'a> Arguments<'a> { #[inline] pub const fn new_const(pieces: &'a [&'static str; N]) -> Self { const { assert!(N <= 1) }; Arguments { pieces, fmt: None, args: &[] } } /// When using the format_args!() macro, this function is used to generate the /// Arguments structure. /// /// This function should _not_ be const, to make sure we don't accept /// format_args!() and panic!() with arguments in const, even when not evaluated: /// /// ```compile_fail,E0015 /// const _: () = if false { panic!("a {}", "a") }; /// ``` #[inline] pub fn new_v1( pieces: &'a [&'static str; P], args: &'a [rt::Argument<'a>; A], ) -> Arguments<'a> { const { assert!(P >= A && P <= A + 1, "invalid args") } Arguments { pieces, fmt: None, args } } /// Specifies nonstandard formatting parameters. /// /// SAFETY: the following invariants must be held: /// 1. The `pieces` slice must be at least as long as `fmt`. /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`. /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`. /// /// This function should _not_ be const, to make sure we don't accept /// format_args!() and panic!() with arguments in const, even when not evaluated: /// /// ```compile_fail,E0015 /// const _: () = if false { panic!("a {:1}", "a") }; /// ``` #[inline] pub unsafe fn new_v1_formatted( pieces: &'a [&'static str], args: &'a [rt::Argument<'a>], fmt: &'a [rt::Placeholder], ) -> Arguments<'a> { Arguments { pieces, fmt: Some(fmt), args } } }