1 | #![allow (missing_debug_implementations)] |
2 | #![unstable (feature = "fmt_internals" , reason = "internal to format_args!" , issue = "none" )] |
3 | |
4 | //! These are the lang items used by format_args!(). |
5 | |
6 | use super::*; |
7 | |
8 | #[lang = "format_placeholder" ] |
9 | #[derive (Copy, Clone)] |
10 | pub struct Placeholder { |
11 | pub position: usize, |
12 | pub fill: char, |
13 | pub align: Alignment, |
14 | pub flags: u32, |
15 | pub precision: Count, |
16 | pub width: Count, |
17 | } |
18 | |
19 | impl Placeholder { |
20 | #[inline (always)] |
21 | pub const fn new( |
22 | position: usize, |
23 | fill: char, |
24 | align: Alignment, |
25 | flags: u32, |
26 | precision: Count, |
27 | width: Count, |
28 | ) -> Self { |
29 | Self { position, fill, align, flags, precision, width } |
30 | } |
31 | } |
32 | |
33 | #[lang = "format_alignment" ] |
34 | #[derive (Copy, Clone, PartialEq, Eq)] |
35 | pub enum Alignment { |
36 | Left, |
37 | Right, |
38 | Center, |
39 | Unknown, |
40 | } |
41 | |
42 | /// Used by [width](https://doc.rust-lang.org/std/fmt/#width) |
43 | /// and [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers. |
44 | #[lang = "format_count" ] |
45 | #[derive (Copy, Clone)] |
46 | pub enum Count { |
47 | /// Specified with a literal number, stores the value |
48 | Is(usize), |
49 | /// Specified using `$` and `*` syntaxes, stores the index into `args` |
50 | Param(usize), |
51 | /// Not specified |
52 | Implied, |
53 | } |
54 | |
55 | // This needs to match the order of flags in compiler/rustc_ast_lowering/src/format.rs. |
56 | #[derive (Copy, Clone)] |
57 | pub(super) enum Flag { |
58 | SignPlus, |
59 | SignMinus, |
60 | Alternate, |
61 | SignAwareZeroPad, |
62 | DebugLowerHex, |
63 | DebugUpperHex, |
64 | } |
65 | |
66 | /// This struct represents the generic "argument" which is taken by format_args!(). |
67 | /// It contains a function to format the given value. At compile time it is ensured that the |
68 | /// function and the value have the correct types, and then this struct is used to canonicalize |
69 | /// arguments to one type. |
70 | /// |
71 | /// Argument is essentially an optimized partially applied formatting function, |
72 | /// equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`. |
73 | #[lang = "format_argument" ] |
74 | #[derive (Copy, Clone)] |
75 | pub struct Argument<'a> { |
76 | value: &'a Opaque, |
77 | formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, |
78 | } |
79 | |
80 | #[rustc_diagnostic_item = "ArgumentMethods" ] |
81 | impl<'a> Argument<'a> { |
82 | #[inline (always)] |
83 | fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> { |
84 | // SAFETY: `mem::transmute(x)` is safe because |
85 | // 1. `&'b T` keeps the lifetime it originated with `'b` |
86 | // (so as to not have an unbounded lifetime) |
87 | // 2. `&'b T` and `&'b Opaque` have the same memory layout |
88 | // (when `T` is `Sized`, as it is here) |
89 | // `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result` |
90 | // and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI |
91 | // (as long as `T` is `Sized`) |
92 | unsafe { Argument { formatter: mem::transmute(f), value: mem::transmute(x) } } |
93 | } |
94 | |
95 | #[inline (always)] |
96 | pub fn new_display<'b, T: Display>(x: &'b T) -> Argument<'_> { |
97 | Self::new(x, Display::fmt) |
98 | } |
99 | #[inline (always)] |
100 | pub fn new_debug<'b, T: Debug>(x: &'b T) -> Argument<'_> { |
101 | Self::new(x, Debug::fmt) |
102 | } |
103 | #[inline (always)] |
104 | pub fn new_octal<'b, T: Octal>(x: &'b T) -> Argument<'_> { |
105 | Self::new(x, Octal::fmt) |
106 | } |
107 | #[inline (always)] |
108 | pub fn new_lower_hex<'b, T: LowerHex>(x: &'b T) -> Argument<'_> { |
109 | Self::new(x, LowerHex::fmt) |
110 | } |
111 | #[inline (always)] |
112 | pub fn new_upper_hex<'b, T: UpperHex>(x: &'b T) -> Argument<'_> { |
113 | Self::new(x, UpperHex::fmt) |
114 | } |
115 | #[inline (always)] |
116 | pub fn new_pointer<'b, T: Pointer>(x: &'b T) -> Argument<'_> { |
117 | Self::new(x, Pointer::fmt) |
118 | } |
119 | #[inline (always)] |
120 | pub fn new_binary<'b, T: Binary>(x: &'b T) -> Argument<'_> { |
121 | Self::new(x, Binary::fmt) |
122 | } |
123 | #[inline (always)] |
124 | pub fn new_lower_exp<'b, T: LowerExp>(x: &'b T) -> Argument<'_> { |
125 | Self::new(x, LowerExp::fmt) |
126 | } |
127 | #[inline (always)] |
128 | pub fn new_upper_exp<'b, T: UpperExp>(x: &'b T) -> Argument<'_> { |
129 | Self::new(x, UpperExp::fmt) |
130 | } |
131 | #[inline (always)] |
132 | pub fn from_usize(x: &usize) -> Argument<'_> { |
133 | Self::new(x, USIZE_MARKER) |
134 | } |
135 | |
136 | // FIXME: Transmuting formatter in new and indirectly branching to/calling |
137 | // it here is an explicit CFI violation. |
138 | #[allow (inline_no_sanitize)] |
139 | #[no_sanitize (cfi, kcfi)] |
140 | #[inline (always)] |
141 | pub(super) fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
142 | (self.formatter)(self.value, f) |
143 | } |
144 | |
145 | #[inline (always)] |
146 | pub(super) fn as_usize(&self) -> Option<usize> { |
147 | // We are type punning a bit here: USIZE_MARKER only takes an &usize but |
148 | // formatter takes an &Opaque. Rust understandably doesn't think we should compare |
149 | // the function pointers if they don't have the same signature, so we cast to |
150 | // usizes to tell it that we just want to compare addresses. |
151 | if self.formatter as usize == USIZE_MARKER as usize { |
152 | // SAFETY: The `formatter` field is only set to USIZE_MARKER if |
153 | // the value is a usize, so this is safe |
154 | Some(unsafe { *(self.value as *const _ as *const usize) }) |
155 | } else { |
156 | None |
157 | } |
158 | } |
159 | |
160 | /// Used by `format_args` when all arguments are gone after inlining, |
161 | /// when using `&[]` would incorrectly allow for a bigger lifetime. |
162 | /// |
163 | /// This fails without format argument inlining, and that shouldn't be different |
164 | /// when the argument is inlined: |
165 | /// |
166 | /// ```compile_fail,E0716 |
167 | /// let f = format_args!("{}" , "a" ); |
168 | /// println!("{f}" ); |
169 | /// ``` |
170 | #[inline (always)] |
171 | pub fn none() -> [Self; 0] { |
172 | [] |
173 | } |
174 | } |
175 | |
176 | /// This struct represents the unsafety of constructing an `Arguments`. |
177 | /// It exists, rather than an unsafe function, in order to simplify the expansion |
178 | /// of `format_args!(..)` and reduce the scope of the `unsafe` block. |
179 | #[lang = "format_unsafe_arg" ] |
180 | pub struct UnsafeArg { |
181 | _private: (), |
182 | } |
183 | |
184 | impl UnsafeArg { |
185 | /// See documentation where `UnsafeArg` is required to know when it is safe to |
186 | /// create and use `UnsafeArg`. |
187 | #[inline (always)] |
188 | pub unsafe fn new() -> Self { |
189 | Self { _private: () } |
190 | } |
191 | } |
192 | |
193 | extern "C" { |
194 | type Opaque; |
195 | } |
196 | |
197 | // This guarantees a single stable value for the function pointer associated with |
198 | // indices/counts in the formatting infrastructure. |
199 | // |
200 | // Note that a function defined as such would not be correct as functions are |
201 | // always tagged unnamed_addr with the current lowering to LLVM IR, so their |
202 | // address is not considered important to LLVM and as such the as_usize cast |
203 | // could have been miscompiled. In practice, we never call as_usize on non-usize |
204 | // containing data (as a matter of static generation of the formatting |
205 | // arguments), so this is merely an additional check. |
206 | // |
207 | // We primarily want to ensure that the function pointer at `USIZE_MARKER` has |
208 | // an address corresponding *only* to functions that also take `&usize` as their |
209 | // first argument. The read_volatile here ensures that we can safely ready out a |
210 | // usize from the passed reference and that this address does not point at a |
211 | // non-usize taking function. |
212 | static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr: &usize, _| { |
213 | // SAFETY: ptr is a reference |
214 | let _v: usize = unsafe { crate::ptr::read_volatile(src:ptr) }; |
215 | loop {} |
216 | }; |
217 | |