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}