mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 15:01:51 +00:00
Restructure std::fmt::rt a bit.
This moves more of the internal/lang items into the private rt module.
This commit is contained in:
parent
5cf3cbf3b7
commit
0a28977740
@ -327,7 +327,7 @@ fn make_format_spec<'hir>(
|
||||
None => sym::Unknown,
|
||||
},
|
||||
);
|
||||
// This needs to match `Flag` in library/core/src/fmt/mod.rs.
|
||||
// This needs to match `Flag` in library/core/src/fmt/rt.rs.
|
||||
let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
|
||||
| ((sign == Some(FormatSign::Minus)) as u32) << 1
|
||||
| (alternate as u32) << 2
|
||||
|
@ -251,223 +251,6 @@ impl<'a> Formatter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// NB. Argument is essentially an optimized partially applied formatting function,
|
||||
// equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`.
|
||||
|
||||
extern "C" {
|
||||
type Opaque;
|
||||
}
|
||||
|
||||
/// This struct represents the generic "argument" which is taken by the Xprintf
|
||||
/// family of functions. It contains a function to format the given value. At
|
||||
/// compile time it is ensured that the function and the value have the correct
|
||||
/// types, and then this struct is used to canonicalize arguments to one type.
|
||||
#[lang = "format_argument"]
|
||||
#[derive(Copy, Clone)]
|
||||
#[allow(missing_debug_implementations)]
|
||||
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
||||
#[doc(hidden)]
|
||||
pub struct Argument<'a> {
|
||||
value: &'a Opaque,
|
||||
formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
|
||||
}
|
||||
|
||||
/// This struct represents the unsafety of constructing an `Arguments`.
|
||||
/// It exists, rather than an unsafe function, in order to simplify the expansion
|
||||
/// of `format_args!(..)` and reduce the scope of the `unsafe` block.
|
||||
#[lang = "format_unsafe_arg"]
|
||||
#[allow(missing_debug_implementations)]
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
||||
pub struct UnsafeArg {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl UnsafeArg {
|
||||
/// See documentation where `UnsafeArg` is required to know when it is safe to
|
||||
/// create and use `UnsafeArg`.
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn new() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
// This guarantees a single stable value for the function pointer associated with
|
||||
// indices/counts in the formatting infrastructure.
|
||||
//
|
||||
// Note that a function defined as such would not be correct as functions are
|
||||
// always tagged unnamed_addr with the current lowering to LLVM IR, so their
|
||||
// address is not considered important to LLVM and as such the as_usize cast
|
||||
// could have been miscompiled. In practice, we never call as_usize on non-usize
|
||||
// containing data (as a matter of static generation of the formatting
|
||||
// arguments), so this is merely an additional check.
|
||||
//
|
||||
// We primarily want to ensure that the function pointer at `USIZE_MARKER` has
|
||||
// an address corresponding *only* to functions that also take `&usize` as their
|
||||
// first argument. The read_volatile here ensures that we can safely ready out a
|
||||
// usize from the passed reference and that this address does not point at a
|
||||
// non-usize taking function.
|
||||
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
||||
static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| {
|
||||
// SAFETY: ptr is a reference
|
||||
let _v: usize = unsafe { crate::ptr::read_volatile(ptr) };
|
||||
loop {}
|
||||
};
|
||||
|
||||
macro_rules! arg_new {
|
||||
($f: ident, $t: ident) => {
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
||||
#[inline]
|
||||
pub fn $f<'b, T: $t>(x: &'b T) -> Argument<'_> {
|
||||
Self::new(x, $t::fmt)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[rustc_diagnostic_item = "ArgumentMethods"]
|
||||
impl<'a> Argument<'a> {
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
||||
#[inline]
|
||||
pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
|
||||
// SAFETY: `mem::transmute(x)` is safe because
|
||||
// 1. `&'b T` keeps the lifetime it originated with `'b`
|
||||
// (so as to not have an unbounded lifetime)
|
||||
// 2. `&'b T` and `&'b Opaque` have the same memory layout
|
||||
// (when `T` is `Sized`, as it is here)
|
||||
// `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result`
|
||||
// and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI
|
||||
// (as long as `T` is `Sized`)
|
||||
unsafe { Argument { formatter: mem::transmute(f), value: mem::transmute(x) } }
|
||||
}
|
||||
|
||||
arg_new!(new_display, Display);
|
||||
arg_new!(new_debug, Debug);
|
||||
arg_new!(new_octal, Octal);
|
||||
arg_new!(new_lower_hex, LowerHex);
|
||||
arg_new!(new_upper_hex, UpperHex);
|
||||
arg_new!(new_pointer, Pointer);
|
||||
arg_new!(new_binary, Binary);
|
||||
arg_new!(new_lower_exp, LowerExp);
|
||||
arg_new!(new_upper_exp, UpperExp);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
||||
pub fn from_usize(x: &usize) -> Argument<'_> {
|
||||
Argument::new(x, USIZE_MARKER)
|
||||
}
|
||||
|
||||
fn as_usize(&self) -> Option<usize> {
|
||||
// We are type punning a bit here: USIZE_MARKER only takes an &usize but
|
||||
// formatter takes an &Opaque. Rust understandably doesn't think we should compare
|
||||
// the function pointers if they don't have the same signature, so we cast to
|
||||
// usizes to tell it that we just want to compare addresses.
|
||||
if self.formatter as usize == USIZE_MARKER as usize {
|
||||
// SAFETY: The `formatter` field is only set to USIZE_MARKER if
|
||||
// the value is a usize, so this is safe
|
||||
Some(unsafe { *(self.value as *const _ as *const usize) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flags available in the v1 format of format_args
|
||||
#[derive(Copy, Clone)]
|
||||
enum Flag {
|
||||
SignPlus,
|
||||
SignMinus,
|
||||
Alternate,
|
||||
SignAwareZeroPad,
|
||||
DebugLowerHex,
|
||||
DebugUpperHex,
|
||||
}
|
||||
|
||||
impl<'a> Arguments<'a> {
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
#[unstable(feature = "fmt_internals", issue = "none")]
|
||||
#[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
|
||||
pub const fn new_const(pieces: &'a [&'static str]) -> Self {
|
||||
if pieces.len() > 1 {
|
||||
panic!("invalid args");
|
||||
}
|
||||
Arguments { pieces, fmt: None, args: &[] }
|
||||
}
|
||||
|
||||
/// When using the format_args!() macro, this function is used to generate the
|
||||
/// Arguments structure.
|
||||
#[cfg(not(bootstrap))]
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
||||
pub fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'a>]) -> Arguments<'a> {
|
||||
if pieces.len() < args.len() || pieces.len() > args.len() + 1 {
|
||||
panic!("invalid args");
|
||||
}
|
||||
Arguments { pieces, fmt: None, args }
|
||||
}
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
||||
#[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
|
||||
pub const fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'a>]) -> Arguments<'a> {
|
||||
if pieces.len() < args.len() || pieces.len() > args.len() + 1 {
|
||||
panic!("invalid args");
|
||||
}
|
||||
Arguments { pieces, fmt: None, args }
|
||||
}
|
||||
|
||||
/// This function is used to specify nonstandard formatting parameters.
|
||||
///
|
||||
/// An `UnsafeArg` is required because the following invariants must be held
|
||||
/// in order for this function to be safe:
|
||||
/// 1. The `pieces` slice must be at least as long as `fmt`.
|
||||
/// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`.
|
||||
/// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
||||
pub fn new_v1_formatted(
|
||||
pieces: &'a [&'static str],
|
||||
args: &'a [Argument<'a>],
|
||||
fmt: &'a [rt::Placeholder],
|
||||
_unsafe_arg: UnsafeArg,
|
||||
) -> Arguments<'a> {
|
||||
Arguments { pieces, fmt: Some(fmt), args }
|
||||
}
|
||||
|
||||
/// Estimates the length of the formatted text.
|
||||
///
|
||||
/// This is intended to be used for setting initial `String` capacity
|
||||
/// when using `format!`. Note: this is neither the lower nor upper bound.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
||||
pub fn estimated_capacity(&self) -> usize {
|
||||
let pieces_length: usize = self.pieces.iter().map(|x| x.len()).sum();
|
||||
|
||||
if self.args.is_empty() {
|
||||
pieces_length
|
||||
} else if !self.pieces.is_empty() && self.pieces[0].is_empty() && pieces_length < 16 {
|
||||
// If the format string starts with an argument,
|
||||
// don't preallocate anything, unless length
|
||||
// of pieces is significant.
|
||||
0
|
||||
} else {
|
||||
// There are some arguments, so any additional push
|
||||
// will reallocate the string. To avoid that,
|
||||
// we're "pre-doubling" the capacity here.
|
||||
pieces_length.checked_mul(2).unwrap_or(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This structure represents a safely precompiled version of a format string
|
||||
/// and its arguments. This cannot be generated at runtime because it cannot
|
||||
/// safely be done, so no constructors are given and the fields are private
|
||||
@ -502,7 +285,82 @@ pub struct Arguments<'a> {
|
||||
|
||||
// Dynamic arguments for interpolation, to be interleaved with string
|
||||
// pieces. (Every argument is preceded by a string piece.)
|
||||
args: &'a [Argument<'a>],
|
||||
args: &'a [rt::Argument<'a>],
|
||||
}
|
||||
|
||||
/// Used by the format_args!() macro to create a fmt::Arguments object.
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "fmt_internals", issue = "none")]
|
||||
impl<'a> Arguments<'a> {
|
||||
#[inline]
|
||||
#[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
|
||||
pub const fn new_const(pieces: &'a [&'static str]) -> Self {
|
||||
if pieces.len() > 1 {
|
||||
panic!("invalid args");
|
||||
}
|
||||
Arguments { pieces, fmt: None, args: &[] }
|
||||
}
|
||||
|
||||
/// When using the format_args!() macro, this function is used to generate the
|
||||
/// Arguments structure.
|
||||
#[cfg(not(bootstrap))]
|
||||
#[inline]
|
||||
pub fn new_v1(pieces: &'a [&'static str], args: &'a [rt::Argument<'a>]) -> Arguments<'a> {
|
||||
if pieces.len() < args.len() || pieces.len() > args.len() + 1 {
|
||||
panic!("invalid args");
|
||||
}
|
||||
Arguments { pieces, fmt: None, args }
|
||||
}
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
#[inline]
|
||||
#[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
|
||||
pub const fn new_v1(pieces: &'a [&'static str], args: &'a [rt::Argument<'a>]) -> Arguments<'a> {
|
||||
if pieces.len() < args.len() || pieces.len() > args.len() + 1 {
|
||||
panic!("invalid args");
|
||||
}
|
||||
Arguments { pieces, fmt: None, args }
|
||||
}
|
||||
|
||||
/// This function is used to specify nonstandard formatting parameters.
|
||||
///
|
||||
/// An `rt::UnsafeArg` is required because the following invariants must be held
|
||||
/// in order for this function to be safe:
|
||||
/// 1. The `pieces` slice must be at least as long as `fmt`.
|
||||
/// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`.
|
||||
/// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`.
|
||||
#[inline]
|
||||
pub fn new_v1_formatted(
|
||||
pieces: &'a [&'static str],
|
||||
args: &'a [rt::Argument<'a>],
|
||||
fmt: &'a [rt::Placeholder],
|
||||
_unsafe_arg: rt::UnsafeArg,
|
||||
) -> Arguments<'a> {
|
||||
Arguments { pieces, fmt: Some(fmt), args }
|
||||
}
|
||||
|
||||
/// Estimates the length of the formatted text.
|
||||
///
|
||||
/// This is intended to be used for setting initial `String` capacity
|
||||
/// when using `format!`. Note: this is neither the lower nor upper bound.
|
||||
#[inline]
|
||||
pub fn estimated_capacity(&self) -> usize {
|
||||
let pieces_length: usize = self.pieces.iter().map(|x| x.len()).sum();
|
||||
|
||||
if self.args.is_empty() {
|
||||
pieces_length
|
||||
} else if !self.pieces.is_empty() && self.pieces[0].is_empty() && pieces_length < 16 {
|
||||
// If the format string starts with an argument,
|
||||
// don't preallocate anything, unless length
|
||||
// of pieces is significant.
|
||||
0
|
||||
} else {
|
||||
// There are some arguments, so any additional push
|
||||
// will reallocate the string. To avoid that,
|
||||
// we're "pre-doubling" the capacity here.
|
||||
pieces_length.checked_mul(2).unwrap_or(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Arguments<'a> {
|
||||
@ -1244,7 +1102,7 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
|
||||
if !piece.is_empty() {
|
||||
formatter.buf.write_str(*piece)?;
|
||||
}
|
||||
(arg.formatter)(arg.value, &mut formatter)?;
|
||||
arg.fmt(&mut formatter)?;
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
@ -1274,7 +1132,7 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[Argument<'_>]) -> Result {
|
||||
unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argument<'_>]) -> Result {
|
||||
fmt.fill = arg.fill;
|
||||
fmt.align = arg.align;
|
||||
fmt.flags = arg.flags;
|
||||
@ -1292,10 +1150,10 @@ unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[Argument<'
|
||||
let value = unsafe { args.get_unchecked(arg.position) };
|
||||
|
||||
// Then actually do some printing
|
||||
(value.formatter)(value.value, fmt)
|
||||
value.fmt(fmt)
|
||||
}
|
||||
|
||||
unsafe fn getcount(args: &[Argument<'_>], cnt: &rt::Count) -> Option<usize> {
|
||||
unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option<usize> {
|
||||
match *cnt {
|
||||
rt::Count::Is(n) => Some(n),
|
||||
rt::Count::Implied => None,
|
||||
@ -1878,7 +1736,7 @@ impl<'a> Formatter<'a> {
|
||||
#[must_use]
|
||||
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
||||
pub fn sign_plus(&self) -> bool {
|
||||
self.flags & (1 << Flag::SignPlus as u32) != 0
|
||||
self.flags & (1 << rt::Flag::SignPlus as u32) != 0
|
||||
}
|
||||
|
||||
/// Determines if the `-` flag was specified.
|
||||
@ -1907,7 +1765,7 @@ impl<'a> Formatter<'a> {
|
||||
#[must_use]
|
||||
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
||||
pub fn sign_minus(&self) -> bool {
|
||||
self.flags & (1 << Flag::SignMinus as u32) != 0
|
||||
self.flags & (1 << rt::Flag::SignMinus as u32) != 0
|
||||
}
|
||||
|
||||
/// Determines if the `#` flag was specified.
|
||||
@ -1935,7 +1793,7 @@ impl<'a> Formatter<'a> {
|
||||
#[must_use]
|
||||
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
||||
pub fn alternate(&self) -> bool {
|
||||
self.flags & (1 << Flag::Alternate as u32) != 0
|
||||
self.flags & (1 << rt::Flag::Alternate as u32) != 0
|
||||
}
|
||||
|
||||
/// Determines if the `0` flag was specified.
|
||||
@ -1961,17 +1819,17 @@ impl<'a> Formatter<'a> {
|
||||
#[must_use]
|
||||
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
||||
pub fn sign_aware_zero_pad(&self) -> bool {
|
||||
self.flags & (1 << Flag::SignAwareZeroPad as u32) != 0
|
||||
self.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0
|
||||
}
|
||||
|
||||
// FIXME: Decide what public API we want for these two flags.
|
||||
// https://github.com/rust-lang/rust/issues/48584
|
||||
fn debug_lower_hex(&self) -> bool {
|
||||
self.flags & (1 << Flag::DebugLowerHex as u32) != 0
|
||||
self.flags & (1 << rt::Flag::DebugLowerHex as u32) != 0
|
||||
}
|
||||
|
||||
fn debug_upper_hex(&self) -> bool {
|
||||
self.flags & (1 << Flag::DebugUpperHex as u32) != 0
|
||||
self.flags & (1 << rt::Flag::DebugUpperHex as u32) != 0
|
||||
}
|
||||
|
||||
/// Creates a [`DebugStruct`] builder designed to assist with creation of
|
||||
@ -2531,13 +2389,13 @@ pub(crate) fn pointer_fmt_inner(ptr_addr: usize, f: &mut Formatter<'_>) -> Resul
|
||||
// or not to zero extend, and then unconditionally set it to get the
|
||||
// prefix.
|
||||
if f.alternate() {
|
||||
f.flags |= 1 << (Flag::SignAwareZeroPad as u32);
|
||||
f.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32);
|
||||
|
||||
if f.width.is_none() {
|
||||
f.width = Some((usize::BITS / 4) as usize + 2);
|
||||
}
|
||||
}
|
||||
f.flags |= 1 << (Flag::Alternate as u32);
|
||||
f.flags |= 1 << (rt::Flag::Alternate as u32);
|
||||
|
||||
let ret = LowerHex::fmt(&ptr_addr, f);
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
//! These are the lang items used by format_args!().
|
||||
|
||||
use super::*;
|
||||
|
||||
#[lang = "format_placeholder"]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Placeholder {
|
||||
@ -28,21 +30,17 @@ impl Placeholder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible alignments that can be requested as part of a formatting directive.
|
||||
#[lang = "format_alignment"]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Alignment {
|
||||
/// Indication that contents should be left-aligned.
|
||||
Left,
|
||||
/// Indication that contents should be right-aligned.
|
||||
Right,
|
||||
/// Indication that contents should be center-aligned.
|
||||
Center,
|
||||
/// No alignment was requested.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
|
||||
/// Used by [width](https://doc.rust-lang.org/std/fmt/#width)
|
||||
/// and [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
|
||||
#[lang = "format_count"]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Count {
|
||||
@ -53,3 +51,147 @@ pub enum Count {
|
||||
/// Not specified
|
||||
Implied,
|
||||
}
|
||||
|
||||
// This needs to match the order of flags in compiler/rustc_ast_lowering/src/format.rs.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Flag {
|
||||
SignPlus,
|
||||
SignMinus,
|
||||
Alternate,
|
||||
SignAwareZeroPad,
|
||||
DebugLowerHex,
|
||||
DebugUpperHex,
|
||||
}
|
||||
|
||||
/// This struct represents the generic "argument" which is taken by format_args!().
|
||||
/// It contains a function to format the given value. At compile time it is ensured that the
|
||||
/// function and the value have the correct types, and then this struct is used to canonicalize
|
||||
/// arguments to one type.
|
||||
///
|
||||
/// Argument is essentially an optimized partially applied formatting function,
|
||||
/// equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`.
|
||||
#[lang = "format_argument"]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Argument<'a> {
|
||||
value: &'a Opaque,
|
||||
formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
|
||||
}
|
||||
|
||||
#[rustc_diagnostic_item = "ArgumentMethods"]
|
||||
impl<'a> Argument<'a> {
|
||||
#[inline(always)]
|
||||
fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
|
||||
// SAFETY: `mem::transmute(x)` is safe because
|
||||
// 1. `&'b T` keeps the lifetime it originated with `'b`
|
||||
// (so as to not have an unbounded lifetime)
|
||||
// 2. `&'b T` and `&'b Opaque` have the same memory layout
|
||||
// (when `T` is `Sized`, as it is here)
|
||||
// `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result`
|
||||
// and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI
|
||||
// (as long as `T` is `Sized`)
|
||||
unsafe { Argument { formatter: mem::transmute(f), value: mem::transmute(x) } }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn new_display<'b, T: Display>(x: &'b T) -> Argument<'_> {
|
||||
Self::new(x, Display::fmt)
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn new_debug<'b, T: Debug>(x: &'b T) -> Argument<'_> {
|
||||
Self::new(x, Debug::fmt)
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn new_octal<'b, T: Octal>(x: &'b T) -> Argument<'_> {
|
||||
Self::new(x, Octal::fmt)
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn new_lower_hex<'b, T: LowerHex>(x: &'b T) -> Argument<'_> {
|
||||
Self::new(x, LowerHex::fmt)
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn new_upper_hex<'b, T: UpperHex>(x: &'b T) -> Argument<'_> {
|
||||
Self::new(x, UpperHex::fmt)
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn new_pointer<'b, T: Pointer>(x: &'b T) -> Argument<'_> {
|
||||
Self::new(x, Pointer::fmt)
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn new_binary<'b, T: Binary>(x: &'b T) -> Argument<'_> {
|
||||
Self::new(x, Binary::fmt)
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn new_lower_exp<'b, T: LowerExp>(x: &'b T) -> Argument<'_> {
|
||||
Self::new(x, LowerExp::fmt)
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn new_upper_exp<'b, T: UpperExp>(x: &'b T) -> Argument<'_> {
|
||||
Self::new(x, UpperExp::fmt)
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn from_usize(x: &usize) -> Argument<'_> {
|
||||
Self::new(x, USIZE_MARKER)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
(self.formatter)(self.value, f)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn as_usize(&self) -> Option<usize> {
|
||||
// We are type punning a bit here: USIZE_MARKER only takes an &usize but
|
||||
// formatter takes an &Opaque. Rust understandably doesn't think we should compare
|
||||
// the function pointers if they don't have the same signature, so we cast to
|
||||
// usizes to tell it that we just want to compare addresses.
|
||||
if self.formatter as usize == USIZE_MARKER as usize {
|
||||
// SAFETY: The `formatter` field is only set to USIZE_MARKER if
|
||||
// the value is a usize, so this is safe
|
||||
Some(unsafe { *(self.value as *const _ as *const usize) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct represents the unsafety of constructing an `Arguments`.
|
||||
/// It exists, rather than an unsafe function, in order to simplify the expansion
|
||||
/// of `format_args!(..)` and reduce the scope of the `unsafe` block.
|
||||
#[lang = "format_unsafe_arg"]
|
||||
pub struct UnsafeArg {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl UnsafeArg {
|
||||
/// See documentation where `UnsafeArg` is required to know when it is safe to
|
||||
/// create and use `UnsafeArg`.
|
||||
#[inline(always)]
|
||||
pub unsafe fn new() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
type Opaque;
|
||||
}
|
||||
|
||||
// This guarantees a single stable value for the function pointer associated with
|
||||
// indices/counts in the formatting infrastructure.
|
||||
//
|
||||
// Note that a function defined as such would not be correct as functions are
|
||||
// always tagged unnamed_addr with the current lowering to LLVM IR, so their
|
||||
// address is not considered important to LLVM and as such the as_usize cast
|
||||
// could have been miscompiled. In practice, we never call as_usize on non-usize
|
||||
// containing data (as a matter of static generation of the formatting
|
||||
// arguments), so this is merely an additional check.
|
||||
//
|
||||
// We primarily want to ensure that the function pointer at `USIZE_MARKER` has
|
||||
// an address corresponding *only* to functions that also take `&usize` as their
|
||||
// first argument. The read_volatile here ensures that we can safely ready out a
|
||||
// usize from the passed reference and that this address does not point at a
|
||||
// non-usize taking function.
|
||||
static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| {
|
||||
// SAFETY: ptr is a reference
|
||||
let _v: usize = unsafe { crate::ptr::read_volatile(ptr) };
|
||||
loop {}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user