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