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 | |
9 | use super::*; |
10 | use crate::hint::unreachable_unchecked; |
11 | use crate::ptr::NonNull; |
12 | |
13 | #[lang= "format_placeholder"] |
14 | #[derive(Copy, Clone)] |
15 | pub 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)] |
26 | pub 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)] |
36 | enum 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)] |
59 | pub struct Argument<'a> { |
60 | ty: ArgumentType<'a>, |
61 | } |
62 | |
63 | macro_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 | |
108 | impl 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"] |
207 | pub struct UnsafeArg { |
208 | _private: (), |
209 | } |
210 | |
211 | impl 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"] |
224 | impl<'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 | } |
273 |
Definitions
- Placeholder
- position
- flags
- precision
- width
- Count
- Is
- Param
- Implied
- ArgumentType
- Placeholder
- value
- formatter
- _lifetime
- Count
- Argument
- ty
- argument_new
- new_display
- new_debug
- new_debug_noop
- new_octal
- new_lower_hex
- new_upper_hex
- new_pointer
- new_binary
- new_lower_exp
- new_upper_exp
- from_usize
- fmt
- formatter
- value
- as_u16
- none
- UnsafeArg
- _private
- new
- new_const
- new_v1
Learn Rust with the experts
Find out more