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#[lang = "format_placeholder"]
14#[derive(Copy, Clone)]
15pub struct Placeholder {
16    pub position: usize,
17    pub flags: u32,
18    pub precision: Count,
19    pub width: Count,
20}
21
22/// Used by [width](https://doc.rust-lang.org/std/fmt/#width)
23/// and [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
24#[lang = "format_count"]
25#[derive(Copy, Clone)]
26pub enum Count {
27    /// Specified with a literal number, stores the value
28    Is(u16),
29    /// Specified using `$` and `*` syntaxes, stores the index into `args`
30    Param(usize),
31    /// Not specified
32    Implied,
33}
34
35#[derive(Copy, Clone)]
36enum ArgumentType<'a> {
37    Placeholder {
38        // INVARIANT: `formatter` has type `fn(&T, _) -> _` for some `T`, and `value`
39        // was derived from a `&'a T`.
40        value: NonNull<()>,
41        formatter: unsafe fn(NonNull<()>, &mut Formatter<'_>) -> Result,
42        _lifetime: PhantomData<&'a ()>,
43    },
44    Count(u16),
45}
46
47/// This struct represents a generic "argument" which is taken by format_args!().
48///
49/// This can be either a placeholder argument or a count argument.
50/// * A placeholder argument contains a function to format the given value. At
51///   compile time it is ensured that the function and the value have the correct
52///   types, and then this struct is used to canonicalize arguments to one type.
53///   Placeholder arguments are essentially an optimized partially applied formatting
54///   function, equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`.
55/// * A count argument contains a count for dynamic formatting parameters like
56///   precision and width.
57#[lang = "format_argument"]
58#[derive(Copy, Clone)]
59pub struct Argument<'a> {
60    ty: ArgumentType<'a>,
61}
62
63macro_rules! argument_new {
64    ($t:ty, $x:expr, $f:expr) => {
65        Argument {
66            // INVARIANT: this creates an `ArgumentType<'a>` from a `&'a T` and
67            // a `fn(&T, ...)`, so the invariant is maintained.
68            ty: ArgumentType::Placeholder {
69                value: NonNull::<$t>::from_ref($x).cast(),
70                // The Rust ABI considers all pointers to be equivalent, so transmuting a fn(&T) to
71                // fn(NonNull<()>) and calling it with a NonNull<()> that points at a T is allowed.
72                // However, the CFI sanitizer does not allow this, and triggers a crash when it
73                // happens.
74                //
75                // To avoid this crash, we use a helper function when CFI is enabled. To avoid the
76                // cost of this helper function (mainly code-size) when it is not needed, we
77                // transmute the function pointer otherwise.
78                //
79                // This is similar to what the Rust compiler does internally with vtables when KCFI
80                // is enabled, where it generates trampoline functions that only serve to adjust the
81                // expected type of the argument. `ArgumentType::Placeholder` is a bit like a
82                // manually constructed trait object, so it is not surprising that the same approach
83                // has to be applied here as well.
84                //
85                // It is still considered problematic (from the Rust side) that CFI rejects entirely
86                // legal Rust programs, so we do not consider anything done here a stable guarantee,
87                // but meanwhile we carry this work-around to keep Rust compatible with CFI and
88                // KCFI.
89                #[cfg(not(any(sanitize = "cfi", sanitize = "kcfi")))]
90                formatter: {
91                    let f: fn(&$t, &mut Formatter<'_>) -> Result = $f;
92                    // SAFETY: This is only called with `value`, which has the right type.
93                    unsafe { core::mem::transmute(f) }
94                },
95                #[cfg(any(sanitize = "cfi", sanitize = "kcfi"))]
96                formatter: |ptr: NonNull<()>, fmt: &mut Formatter<'_>| {
97                    let func = $f;
98                    // SAFETY: This is the same type as the `value` field.
99                    let r = unsafe { ptr.cast::<$t>().as_ref() };
100                    (func)(r, fmt)
101                },
102                _lifetime: PhantomData,
103            },
104        }
105    };
106}
107
108impl Argument<'_> {
109    #[inline]
110    pub const fn new_display<T: Display>(x: &T) -> Argument<'_> {
111        argument_new!(T, x, <T as Display>::fmt)
112    }
113    #[inline]
114    pub const fn new_debug<T: Debug>(x: &T) -> Argument<'_> {
115        argument_new!(T, x, <T as Debug>::fmt)
116    }
117    #[inline]
118    pub const fn new_debug_noop<T: Debug>(x: &T) -> Argument<'_> {
119        argument_new!(T, x, |_: &T, _| Ok(()))
120    }
121    #[inline]
122    pub const fn new_octal<T: Octal>(x: &T) -> Argument<'_> {
123        argument_new!(T, x, <T as Octal>::fmt)
124    }
125    #[inline]
126    pub const fn new_lower_hex<T: LowerHex>(x: &T) -> Argument<'_> {
127        argument_new!(T, x, <T as LowerHex>::fmt)
128    }
129    #[inline]
130    pub const fn new_upper_hex<T: UpperHex>(x: &T) -> Argument<'_> {
131        argument_new!(T, x, <T as UpperHex>::fmt)
132    }
133    #[inline]
134    pub const fn new_pointer<T: Pointer>(x: &T) -> Argument<'_> {
135        argument_new!(T, x, <T as Pointer>::fmt)
136    }
137    #[inline]
138    pub const fn new_binary<T: Binary>(x: &T) -> Argument<'_> {
139        argument_new!(T, x, <T as Binary>::fmt)
140    }
141    #[inline]
142    pub const fn new_lower_exp<T: LowerExp>(x: &T) -> Argument<'_> {
143        argument_new!(T, x, <T as LowerExp>::fmt)
144    }
145    #[inline]
146    pub const fn new_upper_exp<T: UpperExp>(x: &T) -> Argument<'_> {
147        argument_new!(T, x, <T as UpperExp>::fmt)
148    }
149    #[inline]
150    #[track_caller]
151    pub const fn from_usize(x: &usize) -> Argument<'_> {
152        if *x > u16::MAX as usize {
153            panic!("Formatting argument out of range");
154        }
155        Argument { ty: ArgumentType::Count(*x as u16) }
156    }
157
158    /// Format this placeholder argument.
159    ///
160    /// # Safety
161    ///
162    /// This argument must actually be a placeholder argument.
163    #[inline]
164    pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result {
165        match self.ty {
166            // SAFETY:
167            // Because of the invariant that if `formatter` had the type
168            // `fn(&T, _) -> _` then `value` has type `&'b T` where `'b` is
169            // the lifetime of the `ArgumentType`, and because references
170            // and `NonNull` are ABI-compatible, this is completely equivalent
171            // to calling the original function passed to `new` with the
172            // original reference, which is sound.
173            ArgumentType::Placeholder { formatter, value, .. } => unsafe { formatter(value, f) },
174            // SAFETY: the caller promised this.
175            ArgumentType::Count(_) => unsafe { unreachable_unchecked() },
176        }
177    }
178
179    #[inline]
180    pub(super) const fn as_u16(&self) -> Option<u16> {
181        match self.ty {
182            ArgumentType::Count(count) => Some(count),
183            ArgumentType::Placeholder { .. } => None,
184        }
185    }
186
187    /// Used by `format_args` when all arguments are gone after inlining,
188    /// when using `&[]` would incorrectly allow for a bigger lifetime.
189    ///
190    /// This fails without format argument inlining, and that shouldn't be different
191    /// when the argument is inlined:
192    ///
193    /// ```compile_fail,E0716
194    /// let f = format_args!("{}", "a");
195    /// println!("{f}");
196    /// ```
197    #[inline]
198    pub const fn none() -> [Self; 0] {
199        []
200    }
201}
202
203/// This struct represents the unsafety of constructing an `Arguments`.
204/// It exists, rather than an unsafe function, in order to simplify the expansion
205/// of `format_args!(..)` and reduce the scope of the `unsafe` block.
206#[lang = "format_unsafe_arg"]
207pub struct UnsafeArg {
208    _private: (),
209}
210
211impl UnsafeArg {
212    /// See documentation where `UnsafeArg` is required to know when it is safe to
213    /// create and use `UnsafeArg`.
214    #[inline]
215    pub const unsafe fn new() -> Self {
216        Self { _private: () }
217    }
218}
219
220/// Used by the format_args!() macro to create a fmt::Arguments object.
221#[doc(hidden)]
222#[unstable(feature = "fmt_internals", issue = "none")]
223#[rustc_diagnostic_item = "FmtArgumentsNew"]
224impl<'a> Arguments<'a> {
225    #[inline]
226    pub const fn new_const<const N: usize>(pieces: &'a [&'static str; N]) -> Self {
227        const { assert!(N <= 1) };
228        Arguments { pieces, fmt: None, args: &[] }
229    }
230
231    /// When using the format_args!() macro, this function is used to generate the
232    /// Arguments structure.
233    ///
234    /// This function should _not_ be const, to make sure we don't accept
235    /// format_args!() and panic!() with arguments in const, even when not evaluated:
236    ///
237    /// ```compile_fail,E0015
238    /// const _: () = if false { panic!("a {}", "a") };
239    /// ```
240    #[inline]
241    pub fn new_v1<const P: usize, const A: usize>(
242        pieces: &'a [&'static str; P],
243        args: &'a [rt::Argument<'a>; A],
244    ) -> Arguments<'a> {
245        const { assert!(P >= A && P <= A + 1, "invalid args") }
246        Arguments { pieces, fmt: None, args }
247    }
248
249    /// Specifies nonstandard formatting parameters.
250    ///
251    /// An `rt::UnsafeArg` is required because the following invariants must be held
252    /// in order for this function to be safe:
253    /// 1. The `pieces` slice must be at least as long as `fmt`.
254    /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`.
255    /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`.
256    ///
257    /// This function should _not_ be const, to make sure we don't accept
258    /// format_args!() and panic!() with arguments in const, even when not evaluated:
259    ///
260    /// ```compile_fail,E0015
261    /// const _: () = if false { panic!("a {:1}", "a") };
262    /// ```
263    #[inline]
264    pub fn new_v1_formatted(
265        pieces: &'a [&'static str],
266        args: &'a [rt::Argument<'a>],
267        fmt: &'a [rt::Placeholder],
268        _unsafe_arg: rt::UnsafeArg,
269    ) -> Arguments<'a> {
270        Arguments { pieces, fmt: Some(fmt), args }
271    }
272}