44//! These are the lang items used by format_args!().
55
66use super :: * ;
7+ use crate :: hint:: unreachable_unchecked;
78
89#[ lang = "format_placeholder" ]
910#[ derive( Copy , Clone ) ]
@@ -63,18 +64,26 @@ pub(super) enum Flag {
6364 DebugUpperHex ,
6465}
6566
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!().
7074///
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.
7383#[ lang = "format_argument" ]
7484#[ derive( Copy , Clone ) ]
7585pub struct Argument < ' a > {
76- value : & ' a Opaque ,
77- formatter : fn ( & Opaque , & mut Formatter < ' _ > ) -> Result ,
86+ ty : ArgumentType < ' a > ,
7887}
7988
8089#[ rustc_diagnostic_item = "ArgumentMethods" ]
@@ -89,7 +98,14 @@ impl<'a> Argument<'a> {
8998 // `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result`
9099 // and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI
91100 // (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+ }
93109 }
94110
95111 #[ inline( always) ]
@@ -130,30 +146,33 @@ impl<'a> Argument<'a> {
130146 }
131147 #[ inline( always) ]
132148 pub fn from_usize ( x : & usize ) -> Argument < ' _ > {
133- Self :: new ( x , USIZE_MARKER )
149+ Argument { ty : ArgumentType :: Count ( * x ) }
134150 }
135151
152+ /// Format this placeholder argument.
153+ ///
154+ /// # Safety
155+ ///
156+ /// This argument must actually be a placeholer argument.
157+ ///
136158 // FIXME: Transmuting formatter in new and indirectly branching to/calling
137159 // it here is an explicit CFI violation.
138160 #[ allow( inline_no_sanitize) ]
139161 #[ no_sanitize( cfi, kcfi) ]
140162 #[ 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+ }
143169 }
144170
145171 #[ inline( always) ]
146172 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 ,
157176 }
158177 }
159178
@@ -193,24 +212,3 @@ impl UnsafeArg {
193212extern "C" {
194213 type Opaque ;
195214}
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