4
4
//! These are the lang items used by format_args!().
5
5
6
6
use super :: * ;
7
+ use crate :: hint:: unreachable_unchecked;
7
8
8
9
#[ lang = "format_placeholder" ]
9
10
#[ derive( Copy , Clone ) ]
@@ -63,18 +64,26 @@ pub(super) enum Flag {
63
64
DebugUpperHex ,
64
65
}
65
66
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.
67
+ #[ derive( Copy , Clone ) ]
68
+ enum ArgumentType < ' a > {
69
+ Placeholder { value : & ' a Opaque , formatter : fn ( & Opaque , & mut Formatter < ' _ > ) -> Result } ,
70
+ Count ( usize ) ,
71
+ }
72
+
73
+ /// This struct represents a generic "argument" which is taken by format_args!().
70
74
///
71
- /// Argument is essentially an optimized partially applied formatting function,
72
- /// equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`.
75
+ /// This can be either a placeholder argument or a count argument.
76
+ /// * A placeholder argument contains a function to format the given value. At
77
+ /// compile time it is ensured that the function and the value have the correct
78
+ /// types, and then this struct is used to canonicalize arguments to one type.
79
+ /// Placeholder arguments are essentially an optimized partially applied formatting
80
+ /// function, equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`.
81
+ /// * A count argument contains a count for dynamic formatting parameters like
82
+ /// precision and width.
73
83
#[ lang = "format_argument" ]
74
84
#[ derive( Copy , Clone ) ]
75
85
pub struct Argument < ' a > {
76
- value : & ' a Opaque ,
77
- formatter : fn ( & Opaque , & mut Formatter < ' _ > ) -> Result ,
86
+ ty : ArgumentType < ' a > ,
78
87
}
79
88
80
89
#[ rustc_diagnostic_item = "ArgumentMethods" ]
@@ -89,7 +98,14 @@ impl<'a> Argument<'a> {
89
98
// `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result`
90
99
// and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI
91
100
// (as long as `T` is `Sized`)
92
- unsafe { Argument { formatter : mem:: transmute ( f) , value : mem:: transmute ( x) } }
101
+ unsafe {
102
+ Argument {
103
+ ty : ArgumentType :: Placeholder {
104
+ formatter : mem:: transmute ( f) ,
105
+ value : mem:: transmute ( x) ,
106
+ } ,
107
+ }
108
+ }
93
109
}
94
110
95
111
#[ inline( always) ]
@@ -130,30 +146,33 @@ impl<'a> Argument<'a> {
130
146
}
131
147
#[ inline( always) ]
132
148
pub fn from_usize ( x : & usize ) -> Argument < ' _ > {
133
- Self :: new ( x , USIZE_MARKER )
149
+ Argument { ty : ArgumentType :: Count ( * x ) }
134
150
}
135
151
152
+ /// Format this placeholder argument.
153
+ ///
154
+ /// # Safety
155
+ ///
156
+ /// This argument must actually be a placeholer argument.
157
+ ///
136
158
// FIXME: Transmuting formatter in new and indirectly branching to/calling
137
159
// it here is an explicit CFI violation.
138
160
#[ allow( inline_no_sanitize) ]
139
161
#[ no_sanitize( cfi, kcfi) ]
140
162
#[ inline( always) ]
141
- pub ( super ) fn fmt ( & self , f : & mut Formatter < ' _ > ) -> Result {
142
- ( self . formatter ) ( self . value , f)
163
+ pub ( super ) unsafe fn fmt ( & self , f : & mut Formatter < ' _ > ) -> Result {
164
+ match self . ty {
165
+ ArgumentType :: Placeholder { formatter, value } => formatter ( value, f) ,
166
+ // SAFETY: the caller promised this.
167
+ ArgumentType :: Count ( _) => unsafe { unreachable_unchecked ( ) } ,
168
+ }
143
169
}
144
170
145
171
#[ inline( always) ]
146
172
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
173
+ match self . ty {
174
+ ArgumentType :: Count ( count) => Some ( count) ,
175
+ ArgumentType :: Placeholder { .. } => None ,
157
176
}
158
177
}
159
178
@@ -193,24 +212,3 @@ impl UnsafeArg {
193
212
extern "C" {
194
213
type Opaque ;
195
214
}
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, _| {
213
- // SAFETY: ptr is a reference
214
- let _v: usize = unsafe { crate :: ptr:: read_volatile ( ptr) } ;
215
- loop { }
216
- } ;
0 commit comments