Auto merge of #136974 - m-ou-se:fmt-options-64-bit, r=scottmcm

Reduce FormattingOptions to 64 bits

This is part of https://github.com/rust-lang/rust/issues/99012

This reduces FormattingOptions from 6-7 machine words (384 bits on 64-bit platforms, 224 bits on 32-bit platforms) to just 64 bits (a single register on 64-bit platforms).

Before:

```rust
pub struct FormattingOptions {
    flags: u32, // only 6 bits used
    fill: char,
    align: Option<Alignment>,
    width: Option<usize>,
    precision: Option<usize>,
}
```

After:

```rust
pub struct FormattingOptions {
    /// Bits:
    ///  - 0-20: fill character (21 bits, a full `char`)
    ///  - 21: `+` flag
    ///  - 22: `-` flag
    ///  - 23: `#` flag
    ///  - 24: `0` flag
    ///  - 25: `x?` flag
    ///  - 26: `X?` flag
    ///  - 27: Width flag (if set, the width field below is used)
    ///  - 28: Precision flag (if set, the precision field below is used)
    ///  - 29-30: Alignment (0: Left, 1: Right, 2: Center, 3: Unknown)
    ///  - 31: Always set to 1
    flags: u32,
    /// Width if width flag above is set. Otherwise, always 0.
    width: u16,
    /// Precision if precision flag above is set. Otherwise, always 0.
    precision: u16,
}
```
This commit is contained in:
bors 2025-03-22 10:56:14 +00:00
commit 0ce1369bde
12 changed files with 363 additions and 321 deletions

View file

@ -2150,11 +2150,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr_uint(sp, ast::UintTy::U16, value as u128)
}
pub(super) fn expr_char(&mut self, sp: Span, value: char) -> hir::Expr<'hir> {
let lit = self.arena.alloc(hir::Lit { span: sp, node: ast::LitKind::Char(value) });
self.expr(sp, hir::ExprKind::Lit(lit))
}
pub(super) fn expr_str(&mut self, sp: Span, value: Symbol) -> hir::Expr<'hir> {
let lit = self
.arena

View file

@ -361,24 +361,26 @@ fn make_format_spec<'hir>(
zero_pad,
debug_hex,
} = &placeholder.format_options;
let fill = ctx.expr_char(sp, fill.unwrap_or(' '));
let align = ctx.expr_lang_item_type_relative(
sp,
hir::LangItem::FormatAlignment,
match alignment {
Some(FormatAlignment::Left) => sym::Left,
Some(FormatAlignment::Right) => sym::Right,
Some(FormatAlignment::Center) => sym::Center,
None => sym::Unknown,
},
);
// 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
| (zero_pad as u32) << 3
| ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4
| ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5;
let fill = fill.unwrap_or(' ');
// These need to match the constants in library/core/src/fmt/rt.rs.
let align = match alignment {
Some(FormatAlignment::Left) => 0,
Some(FormatAlignment::Right) => 1,
Some(FormatAlignment::Center) => 2,
None => 3,
};
// This needs to match the constants in library/core/src/fmt/rt.rs.
let flags: u32 = fill as u32
| ((sign == Some(FormatSign::Plus)) as u32) << 21
| ((sign == Some(FormatSign::Minus)) as u32) << 22
| (alternate as u32) << 23
| (zero_pad as u32) << 24
| ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25
| ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26
| (width.is_some() as u32) << 27
| (precision.is_some() as u32) << 28
| align << 29
| 1 << 31; // Highest bit always set.
let flags = ctx.expr_u32(sp, flags);
let precision = make_count(ctx, sp, precision, argmap);
let width = make_count(ctx, sp, width, argmap);
@ -387,7 +389,7 @@ fn make_format_spec<'hir>(
hir::LangItem::FormatPlaceholder,
sym::new,
));
let args = ctx.arena.alloc_from_iter([position, fill, align, flags, precision, width]);
let args = ctx.arena.alloc_from_iter([position, flags, precision, width]);
ctx.expr_call_mut(sp, format_placeholder_new, args)
}

View file

@ -322,7 +322,6 @@ language_item_table! {
BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
// Lang items needed for `format_args!()`.
FormatAlignment, sym::format_alignment, format_alignment, Target::Enum, GenericRequirement::None;
FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None;
FormatArguments, sym::format_arguments, format_arguments, Target::Struct, GenericRequirement::None;
FormatCount, sym::format_count, format_count, Target::Enum, GenericRequirement::None;

View file

@ -1003,7 +1003,6 @@ symbols! {
forbid,
forget,
format,
format_alignment,
format_args,
format_args_capture,
format_args_macro,

View file

@ -86,7 +86,7 @@ where
true => flt2dec::Sign::MinusPlus,
};
if let Some(precision) = fmt.options.precision {
if let Some(precision) = fmt.options.get_precision() {
float_to_decimal_common_exact(fmt, num, sign, precision)
} else {
let min_precision = 0;
@ -162,7 +162,7 @@ where
true => flt2dec::Sign::MinusPlus,
};
if let Some(precision) = fmt.options.precision {
if let Some(precision) = fmt.options.get_precision() {
// 1 integral digit + `precision` fractional digits = `precision + 1` total digits
float_to_exponential_common_exact(fmt, num, sign, precision + 1, upper)
} else {
@ -180,7 +180,7 @@ where
true => flt2dec::Sign::MinusPlus,
};
if let Some(precision) = fmt.options.precision {
if let Some(precision) = fmt.options.get_precision() {
// this behavior of {:.PREC?} predates exponential formatting for {:?}
float_to_decimal_common_exact(fmt, num, sign, precision)
} else {

View file

@ -33,19 +33,6 @@ pub enum Alignment {
Center,
}
#[doc(hidden)]
#[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")]
impl From<rt::Alignment> for Option<Alignment> {
fn from(value: rt::Alignment) -> Self {
match value {
rt::Alignment::Left => Some(Alignment::Left),
rt::Alignment::Right => Some(Alignment::Right),
rt::Alignment::Center => Some(Alignment::Center),
rt::Alignment::Unknown => None,
}
}
}
#[stable(feature = "debug_builders", since = "1.2.0")]
pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
#[unstable(feature = "debug_closure_helpers", issue = "117729")]
@ -291,11 +278,52 @@ pub enum DebugAsHex {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[unstable(feature = "formatting_options", issue = "118117")]
pub struct FormattingOptions {
/// Flags, with the following bit fields:
///
/// ```text
/// 31 30 29 28 27 26 25 24 23 22 21 20 0
/// ┌───┬───────┬───┬───┬───┬───┬───┬───┬───┬───┬──────────────────────────────────┐
/// │ 1 │ align │ p │ w │ X?│ x?│'0'│ # │ - │ + │ fill │
/// └───┴───────┴───┴───┴───┴───┴───┴───┴───┴───┴──────────────────────────────────┘
/// │ │ │ │ └─┬───────────────────┘ └─┬──────────────────────────────┘
/// │ │ │ │ │ └─ The fill character (21 bits char).
/// │ │ │ │ └─ The debug upper/lower hex, zero pad, alternate, and plus/minus flags.
/// │ │ │ └─ Whether a width is set. (The value is stored separately.)
/// │ │ └─ Whether a precision is set. (The value is stored separately.)
/// │ ├─ 0: Align left. (<)
/// │ ├─ 1: Align right. (>)
/// │ ├─ 2: Align center. (^)
/// │ └─ 3: Alignment not set. (default)
/// └─ Always set.
/// This makes it possible to distinguish formatting flags from
/// a &str size when stored in (the upper bits of) the same field.
/// (fmt::Arguments will make use of this property in the future.)
/// ```
// Note: This could use a special niche type with range 0x8000_0000..=0xfdd0ffff.
// It's unclear if that's useful, though.
flags: u32,
fill: char,
align: Option<Alignment>,
width: Option<u16>,
precision: Option<u16>,
/// Width if width flag (bit 27) above is set. Otherwise, always 0.
width: u16,
/// Precision if precision flag (bit 28) above is set. Otherwise, always 0.
precision: u16,
}
// This needs to match with compiler/rustc_ast_lowering/src/format.rs.
mod flags {
pub(super) const SIGN_PLUS_FLAG: u32 = 1 << 21;
pub(super) const SIGN_MINUS_FLAG: u32 = 1 << 22;
pub(super) const ALTERNATE_FLAG: u32 = 1 << 23;
pub(super) const SIGN_AWARE_ZERO_PAD_FLAG: u32 = 1 << 24;
pub(super) const DEBUG_LOWER_HEX_FLAG: u32 = 1 << 25;
pub(super) const DEBUG_UPPER_HEX_FLAG: u32 = 1 << 26;
pub(super) const WIDTH_FLAG: u32 = 1 << 27;
pub(super) const PRECISION_FLAG: u32 = 1 << 28;
pub(super) const ALIGN_BITS: u32 = 0b11 << 29;
pub(super) const ALIGN_LEFT: u32 = 0 << 29;
pub(super) const ALIGN_RIGHT: u32 = 1 << 29;
pub(super) const ALIGN_CENTER: u32 = 2 << 29;
pub(super) const ALIGN_UNKNOWN: u32 = 3 << 29;
pub(super) const ALWAYS_SET: u32 = 1 << 31;
}
impl FormattingOptions {
@ -311,7 +339,11 @@ impl FormattingOptions {
/// - no [`DebugAsHex`] output mode.
#[unstable(feature = "formatting_options", issue = "118117")]
pub const fn new() -> Self {
Self { flags: 0, fill: ' ', align: None, width: None, precision: None }
Self {
flags: ' ' as u32 | flags::ALIGN_UNKNOWN | flags::ALWAYS_SET,
width: 0,
precision: 0,
}
}
/// Sets or removes the sign (the `+` or the `-` flag).
@ -324,13 +356,12 @@ impl FormattingOptions {
/// - `-`: Currently not used
#[unstable(feature = "formatting_options", issue = "118117")]
pub fn sign(&mut self, sign: Option<Sign>) -> &mut Self {
self.flags =
self.flags & !(1 << rt::Flag::SignMinus as u32 | 1 << rt::Flag::SignPlus as u32);
match sign {
None => {}
Some(Sign::Plus) => self.flags |= 1 << rt::Flag::SignPlus as u32,
Some(Sign::Minus) => self.flags |= 1 << rt::Flag::SignMinus as u32,
}
let sign = match sign {
None => 0,
Some(Sign::Plus) => flags::SIGN_PLUS_FLAG,
Some(Sign::Minus) => flags::SIGN_MINUS_FLAG,
};
self.flags = self.flags & !(flags::SIGN_PLUS_FLAG | flags::SIGN_MINUS_FLAG) | sign;
self
}
/// Sets or unsets the `0` flag.
@ -339,9 +370,9 @@ impl FormattingOptions {
#[unstable(feature = "formatting_options", issue = "118117")]
pub fn sign_aware_zero_pad(&mut self, sign_aware_zero_pad: bool) -> &mut Self {
if sign_aware_zero_pad {
self.flags |= 1 << rt::Flag::SignAwareZeroPad as u32
self.flags |= flags::SIGN_AWARE_ZERO_PAD_FLAG;
} else {
self.flags &= !(1 << rt::Flag::SignAwareZeroPad as u32)
self.flags &= !flags::SIGN_AWARE_ZERO_PAD_FLAG;
}
self
}
@ -356,9 +387,9 @@ impl FormattingOptions {
#[unstable(feature = "formatting_options", issue = "118117")]
pub fn alternate(&mut self, alternate: bool) -> &mut Self {
if alternate {
self.flags |= 1 << rt::Flag::Alternate as u32
self.flags |= flags::ALTERNATE_FLAG;
} else {
self.flags &= !(1 << rt::Flag::Alternate as u32)
self.flags &= !flags::ALTERNATE_FLAG;
}
self
}
@ -370,7 +401,7 @@ impl FormattingOptions {
/// printed around it.
#[unstable(feature = "formatting_options", issue = "118117")]
pub fn fill(&mut self, fill: char) -> &mut Self {
self.fill = fill;
self.flags = self.flags & (u32::MAX << 21) | fill as u32;
self
}
/// Sets or removes the alignment.
@ -379,7 +410,13 @@ impl FormattingOptions {
/// positioned if it is smaller than the width of the formatter.
#[unstable(feature = "formatting_options", issue = "118117")]
pub fn align(&mut self, align: Option<Alignment>) -> &mut Self {
self.align = align;
let align: u32 = match align {
Some(Alignment::Left) => flags::ALIGN_LEFT,
Some(Alignment::Right) => flags::ALIGN_RIGHT,
Some(Alignment::Center) => flags::ALIGN_CENTER,
None => flags::ALIGN_UNKNOWN,
};
self.flags = self.flags & !flags::ALIGN_BITS | align;
self
}
/// Sets or removes the width.
@ -390,7 +427,13 @@ impl FormattingOptions {
/// will be used to take up the required space.
#[unstable(feature = "formatting_options", issue = "118117")]
pub fn width(&mut self, width: Option<u16>) -> &mut Self {
self.width = width;
if let Some(width) = width {
self.flags |= flags::WIDTH_FLAG;
self.width = width;
} else {
self.flags &= !flags::WIDTH_FLAG;
self.width = 0;
}
self
}
/// Sets or removes the precision.
@ -404,77 +447,85 @@ impl FormattingOptions {
/// decimal point should be printed.
#[unstable(feature = "formatting_options", issue = "118117")]
pub fn precision(&mut self, precision: Option<u16>) -> &mut Self {
self.precision = precision;
if let Some(precision) = precision {
self.flags |= flags::PRECISION_FLAG;
self.precision = precision;
} else {
self.flags &= !flags::PRECISION_FLAG;
self.precision = 0;
}
self
}
/// Specifies whether the [`Debug`] trait should use lower-/upper-case
/// hexadecimal or normal integers
#[unstable(feature = "formatting_options", issue = "118117")]
pub fn debug_as_hex(&mut self, debug_as_hex: Option<DebugAsHex>) -> &mut Self {
self.flags = self.flags
& !(1 << rt::Flag::DebugUpperHex as u32 | 1 << rt::Flag::DebugLowerHex as u32);
match debug_as_hex {
None => {}
Some(DebugAsHex::Upper) => self.flags |= 1 << rt::Flag::DebugUpperHex as u32,
Some(DebugAsHex::Lower) => self.flags |= 1 << rt::Flag::DebugLowerHex as u32,
}
let debug_as_hex = match debug_as_hex {
None => 0,
Some(DebugAsHex::Lower) => flags::DEBUG_LOWER_HEX_FLAG,
Some(DebugAsHex::Upper) => flags::DEBUG_UPPER_HEX_FLAG,
};
self.flags = self.flags & !(flags::DEBUG_LOWER_HEX_FLAG | flags::DEBUG_UPPER_HEX_FLAG)
| debug_as_hex;
self
}
/// Returns the current sign (the `+` or the `-` flag).
#[unstable(feature = "formatting_options", issue = "118117")]
pub const fn get_sign(&self) -> Option<Sign> {
const SIGN_PLUS_BITFIELD: u32 = 1 << rt::Flag::SignPlus as u32;
const SIGN_MINUS_BITFIELD: u32 = 1 << rt::Flag::SignMinus as u32;
match self.flags & ((1 << rt::Flag::SignPlus as u32) | (1 << rt::Flag::SignMinus as u32)) {
SIGN_PLUS_BITFIELD => Some(Sign::Plus),
SIGN_MINUS_BITFIELD => Some(Sign::Minus),
0 => None,
_ => panic!("Invalid sign bits set in flags"),
if self.flags & flags::SIGN_PLUS_FLAG != 0 {
Some(Sign::Plus)
} else if self.flags & flags::SIGN_MINUS_FLAG != 0 {
Some(Sign::Minus)
} else {
None
}
}
/// Returns the current `0` flag.
#[unstable(feature = "formatting_options", issue = "118117")]
pub const fn get_sign_aware_zero_pad(&self) -> bool {
self.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0
self.flags & flags::SIGN_AWARE_ZERO_PAD_FLAG != 0
}
/// Returns the current `#` flag.
#[unstable(feature = "formatting_options", issue = "118117")]
pub const fn get_alternate(&self) -> bool {
self.flags & (1 << rt::Flag::Alternate as u32) != 0
self.flags & flags::ALTERNATE_FLAG != 0
}
/// Returns the current fill character.
#[unstable(feature = "formatting_options", issue = "118117")]
pub const fn get_fill(&self) -> char {
self.fill
// SAFETY: We only ever put a valid `char` in the lower 21 bits of the flags field.
unsafe { char::from_u32_unchecked(self.flags & 0x1FFFFF) }
}
/// Returns the current alignment.
#[unstable(feature = "formatting_options", issue = "118117")]
pub const fn get_align(&self) -> Option<Alignment> {
self.align
match self.flags & flags::ALIGN_BITS {
flags::ALIGN_LEFT => Some(Alignment::Left),
flags::ALIGN_RIGHT => Some(Alignment::Right),
flags::ALIGN_CENTER => Some(Alignment::Center),
_ => None,
}
}
/// Returns the current width.
#[unstable(feature = "formatting_options", issue = "118117")]
pub const fn get_width(&self) -> Option<u16> {
self.width
if self.flags & flags::WIDTH_FLAG != 0 { Some(self.width) } else { None }
}
/// Returns the current precision.
#[unstable(feature = "formatting_options", issue = "118117")]
pub const fn get_precision(&self) -> Option<u16> {
self.precision
if self.flags & flags::PRECISION_FLAG != 0 { Some(self.precision) } else { None }
}
/// Returns the current precision.
#[unstable(feature = "formatting_options", issue = "118117")]
pub const fn get_debug_as_hex(&self) -> Option<DebugAsHex> {
const DEBUG_UPPER_BITFIELD: u32 = 1 << rt::Flag::DebugUpperHex as u32;
const DEBUG_LOWER_BITFIELD: u32 = 1 << rt::Flag::DebugLowerHex as u32;
match self.flags
& ((1 << rt::Flag::DebugUpperHex as u32) | (1 << rt::Flag::DebugLowerHex as u32))
{
DEBUG_UPPER_BITFIELD => Some(DebugAsHex::Upper),
DEBUG_LOWER_BITFIELD => Some(DebugAsHex::Lower),
0 => None,
_ => panic!("Invalid hex debug bits set in flags"),
if self.flags & flags::DEBUG_LOWER_HEX_FLAG != 0 {
Some(DebugAsHex::Lower)
} else if self.flags & flags::DEBUG_UPPER_HEX_FLAG != 0 {
Some(DebugAsHex::Upper)
} else {
None
}
}
@ -485,27 +536,6 @@ impl FormattingOptions {
pub fn create_formatter<'a>(self, write: &'a mut (dyn Write + 'a)) -> Formatter<'a> {
Formatter { options: self, buf: write }
}
#[doc(hidden)]
#[unstable(
feature = "fmt_internals",
reason = "internal routines only exposed for testing",
issue = "none"
)]
/// Flags for formatting
pub fn flags(&mut self, flags: u32) {
self.flags = flags
}
#[doc(hidden)]
#[unstable(
feature = "fmt_internals",
reason = "internal routines only exposed for testing",
issue = "none"
)]
/// Flags for formatting
pub fn get_flags(&self) -> u32 {
self.flags
}
}
#[unstable(feature = "formatting_options", issue = "118117")]
@ -1479,15 +1509,25 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
}
unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argument<'_>]) -> Result {
fmt.options.fill = arg.fill;
fmt.options.align = arg.align.into();
fmt.options.flags = arg.flags;
// SAFETY: arg and args come from the same Arguments,
// which guarantees the indexes are always within bounds.
unsafe {
fmt.options.width = getcount(args, &arg.width);
fmt.options.precision = getcount(args, &arg.precision);
}
let (width, precision) =
// SAFETY: arg and args come from the same Arguments,
// which guarantees the indexes are always within bounds.
unsafe { (getcount(args, &arg.width), getcount(args, &arg.precision)) };
#[cfg(bootstrap)]
let options =
*FormattingOptions { flags: flags::ALWAYS_SET | arg.flags << 21, width: 0, precision: 0 }
.align(match arg.align {
rt::Alignment::Left => Some(Alignment::Left),
rt::Alignment::Right => Some(Alignment::Right),
rt::Alignment::Center => Some(Alignment::Center),
rt::Alignment::Unknown => None,
})
.fill(arg.fill)
.width(width)
.precision(precision);
#[cfg(not(bootstrap))]
let options = FormattingOptions { flags: arg.flags, width, precision };
// Extract the correct argument
debug_assert!(arg.position < args.len());
@ -1495,17 +1535,18 @@ unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argume
// which guarantees its index is always within bounds.
let value = unsafe { args.get_unchecked(arg.position) };
// Set all the formatting options.
fmt.options = options;
// Then actually do some printing
// SAFETY: this is a placeholder argument.
unsafe { value.fmt(fmt) }
}
#[cfg(bootstrap)]
unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option<u16> {
match *cnt {
#[cfg(bootstrap)]
rt::Count::Is(n) => Some(n as u16),
#[cfg(not(bootstrap))]
rt::Count::Is(n) => Some(n),
rt::Count::Implied => None,
rt::Count::Param(i) => {
debug_assert!(i < args.len());
@ -1516,6 +1557,20 @@ unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option<u16> {
}
}
#[cfg(not(bootstrap))]
unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> u16 {
match *cnt {
rt::Count::Is(n) => n,
rt::Count::Implied => 0,
rt::Count::Param(i) => {
debug_assert!(i < args.len());
// SAFETY: cnt and args come from the same Arguments,
// which guarantees this index is always within bounds.
unsafe { args.get_unchecked(i).as_u16().unwrap_unchecked() }
}
}
}
/// Padding after the end of something. Returned by `Formatter::padding`.
#[must_use = "don't forget to write the post padding"]
pub(crate) struct PostPadding {
@ -1629,40 +1684,28 @@ impl<'a> Formatter<'a> {
}
// The `width` field is more of a `min-width` parameter at this point.
match self.options.width {
// If there's no minimum length requirements then we can just
// write the bytes.
None => {
write_prefix(self, sign, prefix)?;
self.buf.write_str(buf)
}
// Check if we're over the minimum width, if so then we can also
// just write the bytes.
Some(min) if width >= usize::from(min) => {
write_prefix(self, sign, prefix)?;
self.buf.write_str(buf)
}
let min = self.options.width;
if width >= usize::from(min) {
// We're over the minimum width, so then we can just write the bytes.
write_prefix(self, sign, prefix)?;
self.buf.write_str(buf)
} else if self.sign_aware_zero_pad() {
// The sign and prefix goes before the padding if the fill character
// is zero
Some(min) if self.sign_aware_zero_pad() => {
let old_fill = crate::mem::replace(&mut self.options.fill, '0');
let old_align =
crate::mem::replace(&mut self.options.align, Some(Alignment::Right));
write_prefix(self, sign, prefix)?;
let post_padding = self.padding(min - width as u16, Alignment::Right)?;
self.buf.write_str(buf)?;
post_padding.write(self)?;
self.options.fill = old_fill;
self.options.align = old_align;
Ok(())
}
let old_options = self.options;
self.options.fill('0').align(Some(Alignment::Right));
write_prefix(self, sign, prefix)?;
let post_padding = self.padding(min - width as u16, Alignment::Right)?;
self.buf.write_str(buf)?;
post_padding.write(self)?;
self.options = old_options;
Ok(())
} else {
// Otherwise, the sign and prefix goes after the padding
Some(min) => {
let post_padding = self.padding(min - width as u16, Alignment::Right)?;
write_prefix(self, sign, prefix)?;
self.buf.write_str(buf)?;
post_padding.write(self)
}
let post_padding = self.padding(min - width as u16, Alignment::Right)?;
write_prefix(self, sign, prefix)?;
self.buf.write_str(buf)?;
post_padding.write(self)
}
}
@ -1698,13 +1741,13 @@ impl<'a> Formatter<'a> {
#[stable(feature = "rust1", since = "1.0.0")]
pub fn pad(&mut self, s: &str) -> Result {
// Make sure there's a fast path up front.
if self.options.width.is_none() && self.options.precision.is_none() {
if self.options.flags & (flags::WIDTH_FLAG | flags::PRECISION_FLAG) == 0 {
return self.buf.write_str(s);
}
// The `precision` field can be interpreted as a maximum width for the
// string being formatted.
let (s, char_count) = if let Some(max_char_count) = self.options.precision {
let (s, char_count) = if let Some(max_char_count) = self.options.get_precision() {
let mut iter = s.char_indices();
let remaining = match iter.advance_by(usize::from(max_char_count)) {
Ok(()) => 0,
@ -1720,12 +1763,11 @@ impl<'a> Formatter<'a> {
};
// The `width` field is more of a minimum width parameter at this point.
if let Some(width) = self.options.width
&& char_count < usize::from(width)
{
if char_count < usize::from(self.options.width) {
// If we're under the minimum width, then fill up the minimum width
// with the specified string + some alignment.
let post_padding = self.padding(width - char_count as u16, Alignment::Left)?;
let post_padding =
self.padding(self.options.width - char_count as u16, Alignment::Left)?;
self.buf.write_str(s)?;
post_padding.write(self)
} else {
@ -1744,19 +1786,20 @@ impl<'a> Formatter<'a> {
padding: u16,
default: Alignment,
) -> result::Result<PostPadding, Error> {
let align = self.align().unwrap_or(default);
let align = self.options.get_align().unwrap_or(default);
let fill = self.options.get_fill();
let (pre_pad, post_pad) = match align {
Alignment::Left => (0, padding),
Alignment::Right => (padding, 0),
Alignment::Center => (padding / 2, (padding + 1) / 2),
let padding_left = match align {
Alignment::Left => 0,
Alignment::Right => padding,
Alignment::Center => padding / 2,
};
for _ in 0..pre_pad {
self.buf.write_char(self.options.fill)?;
for _ in 0..padding_left {
self.buf.write_char(fill)?;
}
Ok(PostPadding::new(self.options.fill, post_pad))
Ok(PostPadding::new(fill, padding - padding_left))
}
/// Takes the formatted parts and applies the padding.
@ -1768,12 +1811,16 @@ impl<'a> Formatter<'a> {
///
/// Any `numfmt::Part::Copy` parts in `formatted` must contain valid UTF-8.
unsafe fn pad_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result {
if let Some(mut width) = self.options.width {
if self.options.width == 0 {
// this is the common case and we take a shortcut
// SAFETY: Per the precondition.
unsafe { self.write_formatted_parts(formatted) }
} else {
// for the sign-aware zero padding, we render the sign first and
// behave as if we had no sign from the beginning.
let mut formatted = formatted.clone();
let old_fill = self.options.fill;
let old_align = self.options.align;
let mut width = self.options.width;
let old_options = self.options;
if self.sign_aware_zero_pad() {
// a sign always goes first
let sign = formatted.sign;
@ -1782,8 +1829,7 @@ impl<'a> Formatter<'a> {
// remove the sign from the formatted parts
formatted.sign = "";
width = width.saturating_sub(sign.len() as u16);
self.options.fill = '0';
self.options.align = Some(Alignment::Right);
self.options.fill('0').align(Some(Alignment::Right));
}
// remaining parts go through the ordinary padding process.
@ -1800,13 +1846,8 @@ impl<'a> Formatter<'a> {
}
post_padding.write(self)
};
self.options.fill = old_fill;
self.options.align = old_align;
self.options = old_options;
ret
} else {
// this is the common case and we take a shortcut
// SAFETY: Per the precondition.
unsafe { self.write_formatted_parts(formatted) }
}
}
@ -1927,7 +1968,9 @@ impl<'a> Formatter<'a> {
or `sign_aware_zero_pad` methods instead"
)]
pub fn flags(&self) -> u32 {
self.options.flags
// Extract the debug upper/lower hex, zero pad, alternate, and plus/minus flags
// to stay compatible with older versions of Rust.
self.options.flags >> 21 & 0x3F
}
/// Returns the character used as 'fill' whenever there is alignment.
@ -1960,7 +2003,7 @@ impl<'a> Formatter<'a> {
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
pub fn fill(&self) -> char {
self.options.fill
self.options.get_fill()
}
/// Returns a flag indicating what form of alignment was requested.
@ -1995,7 +2038,7 @@ impl<'a> Formatter<'a> {
#[must_use]
#[stable(feature = "fmt_flags_align", since = "1.28.0")]
pub fn align(&self) -> Option<Alignment> {
self.options.align
self.options.get_align()
}
/// Returns the optionally specified integer width that the output should be.
@ -2025,7 +2068,11 @@ impl<'a> Formatter<'a> {
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
pub fn width(&self) -> Option<usize> {
self.options.width.map(|x| x as usize)
if self.options.flags & flags::WIDTH_FLAG == 0 {
None
} else {
Some(self.options.width as usize)
}
}
/// Returns the optionally specified precision for numeric types.
@ -2056,7 +2103,11 @@ impl<'a> Formatter<'a> {
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
pub fn precision(&self) -> Option<usize> {
self.options.precision.map(|x| x as usize)
if self.options.flags & flags::PRECISION_FLAG == 0 {
None
} else {
Some(self.options.precision as usize)
}
}
/// Determines if the `+` flag was specified.
@ -2088,7 +2139,7 @@ impl<'a> Formatter<'a> {
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
pub fn sign_plus(&self) -> bool {
self.options.flags & (1 << rt::Flag::SignPlus as u32) != 0
self.options.flags & flags::SIGN_PLUS_FLAG != 0
}
/// Determines if the `-` flag was specified.
@ -2117,7 +2168,7 @@ impl<'a> Formatter<'a> {
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
pub fn sign_minus(&self) -> bool {
self.options.flags & (1 << rt::Flag::SignMinus as u32) != 0
self.options.flags & flags::SIGN_MINUS_FLAG != 0
}
/// Determines if the `#` flag was specified.
@ -2145,7 +2196,7 @@ impl<'a> Formatter<'a> {
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
pub fn alternate(&self) -> bool {
self.options.flags & (1 << rt::Flag::Alternate as u32) != 0
self.options.flags & flags::ALTERNATE_FLAG != 0
}
/// Determines if the `0` flag was specified.
@ -2171,17 +2222,16 @@ impl<'a> Formatter<'a> {
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
pub fn sign_aware_zero_pad(&self) -> bool {
self.options.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0
self.options.flags & flags::SIGN_AWARE_ZERO_PAD_FLAG != 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.options.flags & (1 << rt::Flag::DebugLowerHex as u32) != 0
self.options.flags & flags::DEBUG_LOWER_HEX_FLAG != 0
}
fn debug_upper_hex(&self) -> bool {
self.options.flags & (1 << rt::Flag::DebugUpperHex as u32) != 0
self.options.flags & flags::DEBUG_UPPER_HEX_FLAG != 0
}
/// Creates a [`DebugStruct`] builder designed to assist with creation of
@ -2761,7 +2811,7 @@ impl Debug for char {
#[stable(feature = "rust1", since = "1.0.0")]
impl Display for char {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
if f.options.width.is_none() && f.options.precision.is_none() {
if f.options.flags & (flags::WIDTH_FLAG | flags::PRECISION_FLAG) == 0 {
f.write_char(*self)
} else {
f.pad(self.encode_utf8(&mut [0; MAX_LEN_UTF8]))
@ -2792,26 +2842,24 @@ impl<T: ?Sized> Pointer for *const T {
///
/// [problematic]: https://github.com/rust-lang/rust/issues/95489
pub(crate) fn pointer_fmt_inner(ptr_addr: usize, f: &mut Formatter<'_>) -> Result {
let old_width = f.options.width;
let old_flags = f.options.flags;
let old_options = f.options;
// The alternate flag is already treated by LowerHex as being special-
// it denotes whether to prefix with 0x. We use it to work out whether
// or not to zero extend, and then unconditionally set it to get the
// prefix.
if f.alternate() {
f.options.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32);
if f.options.get_alternate() {
f.options.sign_aware_zero_pad(true);
if f.options.width.is_none() {
f.options.width = Some((usize::BITS / 4) as u16 + 2);
if f.options.get_width().is_none() {
f.options.width(Some((usize::BITS / 4) as u16 + 2));
}
}
f.options.flags |= 1 << (rt::Flag::Alternate as u32);
f.options.alternate(true);
let ret = LowerHex::fmt(&ptr_addr, f);
f.options.width = old_width;
f.options.flags = old_flags;
f.options = old_options;
ret
}

View file

@ -11,7 +11,9 @@ use crate::ptr::NonNull;
#[derive(Copy, Clone)]
pub struct Placeholder {
pub position: usize,
#[cfg(bootstrap)]
pub fill: char,
#[cfg(bootstrap)]
pub align: Alignment,
pub flags: u32,
pub precision: Count,
@ -19,6 +21,7 @@ pub struct Placeholder {
}
impl Placeholder {
#[cfg(bootstrap)]
#[inline]
pub const fn new(
position: usize,
@ -30,8 +33,15 @@ impl Placeholder {
) -> Self {
Self { position, fill, align, flags, precision, width }
}
#[cfg(not(bootstrap))]
#[inline]
pub const fn new(position: usize, flags: u32, precision: Count, width: Count) -> Self {
Self { position, flags, precision, width }
}
}
#[cfg(bootstrap)]
#[lang = "format_alignment"]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Alignment {
@ -58,17 +68,6 @@ pub enum Count {
Implied,
}
// This needs to match the order of flags in compiler/rustc_ast_lowering/src/format.rs.
#[derive(Copy, Clone)]
pub(super) enum Flag {
SignPlus,
SignMinus,
Alternate,
SignAwareZeroPad,
DebugLowerHex,
DebugUpperHex,
}
#[derive(Copy, Clone)]
enum ArgumentType<'a> {
Placeholder {

View file

@ -81,6 +81,7 @@ fn formatting_options_ctor() {
}
#[test]
#[allow(deprecated)]
fn formatting_options_flags() {
use core::fmt::*;
for sign in [None, Some(Sign::Plus), Some(Sign::Minus)] {
@ -98,6 +99,25 @@ fn formatting_options_flags() {
assert_eq!(formatting_options.get_alternate(), alternate);
assert_eq!(formatting_options.get_sign_aware_zero_pad(), sign_aware_zero_pad);
assert_eq!(formatting_options.get_debug_as_hex(), debug_as_hex);
let mut output = String::new();
let fmt = Formatter::new(&mut output, formatting_options);
assert_eq!(fmt.options(), formatting_options);
assert_eq!(fmt.sign_minus(), sign == Some(Sign::Minus));
assert_eq!(fmt.sign_plus(), sign == Some(Sign::Plus));
assert_eq!(fmt.alternate(), alternate);
assert_eq!(fmt.sign_aware_zero_pad(), sign_aware_zero_pad);
// The flags method is deprecated.
// This checks compatibility with older versions of Rust.
assert_eq!(fmt.flags() & 1 != 0, sign == Some(Sign::Plus));
assert_eq!(fmt.flags() & 2 != 0, sign == Some(Sign::Minus));
assert_eq!(fmt.flags() & 4 != 0, alternate);
assert_eq!(fmt.flags() & 8 != 0, sign_aware_zero_pad);
assert_eq!(fmt.flags() & 16 != 0, debug_as_hex == Some(DebugAsHex::Lower));
assert_eq!(fmt.flags() & 32 != 0, debug_as_hex == Some(DebugAsHex::Upper));
assert_eq!(fmt.flags() & 0xFFFF_FFC0, 0);
}
}
}

View file

@ -29,16 +29,10 @@
debug precision => _8;
let _8: usize;
scope 5 (inlined Formatter::<'_>::precision) {
let mut _22: std::option::Option<u16>;
scope 6 (inlined Option::<u16>::map::<usize, {closure@Formatter<'_>::precision::{closure#0}}>) {
let mut _23: isize;
let _24: u16;
let mut _25: usize;
scope 7 {
scope 8 (inlined Formatter::<'_>::precision::{closure#0}) {
}
}
}
let mut _22: u32;
let mut _23: u32;
let mut _24: usize;
let mut _25: u16;
}
}
}
@ -53,7 +47,7 @@
StorageLive(_20);
StorageLive(_21);
_21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
_20 = BitAnd(move _21, const 1_u32);
_20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG);
StorageDead(_21);
_4 = Ne(move _20, const 0_u32);
StorageDead(_20);
@ -75,12 +69,12 @@
bb3: {
StorageLive(_6);
StorageLive(_24);
StorageLive(_22);
_22 = copy (((*_1).0: std::fmt::FormattingOptions).4: std::option::Option<u16>);
StorageLive(_23);
_23 = discriminant(_22);
switchInt(move _23) -> [0: bb11, 1: bb12, otherwise: bb10];
_23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
_22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG);
StorageDead(_23);
switchInt(move _22) -> [0: bb10, otherwise: bb11];
}
bb4: {
@ -148,30 +142,31 @@
}
bb9: {
StorageDead(_23);
StorageDead(_22);
StorageDead(_24);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb10];
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb12];
}
bb10: {
unreachable;
}
bb11: {
StorageDead(_22);
_6 = const Option::<usize>::None;
goto -> bb9;
}
bb12: {
_24 = move ((_22 as Some).0: u16);
bb11: {
StorageDead(_22);
StorageLive(_24);
StorageLive(_25);
_25 = copy _24 as usize (IntToInt);
_6 = Option::<usize>::Some(move _25);
_25 = copy (((*_1).0: std::fmt::FormattingOptions).2: u16);
_24 = move _25 as usize (IntToInt);
StorageDead(_25);
_6 = Option::<usize>::Some(move _24);
StorageDead(_24);
goto -> bb9;
}
bb12: {
unreachable;
}
}
ALLOC0 (size: 8, align: 4) {

View file

@ -29,16 +29,10 @@
debug precision => _8;
let _8: usize;
scope 5 (inlined Formatter::<'_>::precision) {
let mut _22: std::option::Option<u16>;
scope 6 (inlined Option::<u16>::map::<usize, {closure@Formatter<'_>::precision::{closure#0}}>) {
let mut _23: isize;
let _24: u16;
let mut _25: usize;
scope 7 {
scope 8 (inlined Formatter::<'_>::precision::{closure#0}) {
}
}
}
let mut _22: u32;
let mut _23: u32;
let mut _24: usize;
let mut _25: u16;
}
}
}
@ -53,7 +47,7 @@
StorageLive(_20);
StorageLive(_21);
_21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
_20 = BitAnd(move _21, const 1_u32);
_20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG);
StorageDead(_21);
_4 = Ne(move _20, const 0_u32);
StorageDead(_20);
@ -75,12 +69,12 @@
bb3: {
StorageLive(_6);
StorageLive(_24);
StorageLive(_22);
_22 = copy (((*_1).0: std::fmt::FormattingOptions).4: std::option::Option<u16>);
StorageLive(_23);
_23 = discriminant(_22);
switchInt(move _23) -> [0: bb11, 1: bb12, otherwise: bb10];
_23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
_22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG);
StorageDead(_23);
switchInt(move _22) -> [0: bb10, otherwise: bb11];
}
bb4: {
@ -148,30 +142,31 @@
}
bb9: {
StorageDead(_23);
StorageDead(_22);
StorageDead(_24);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb10];
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb12];
}
bb10: {
unreachable;
}
bb11: {
StorageDead(_22);
_6 = const Option::<usize>::None;
goto -> bb9;
}
bb12: {
_24 = move ((_22 as Some).0: u16);
bb11: {
StorageDead(_22);
StorageLive(_24);
StorageLive(_25);
_25 = copy _24 as usize (IntToInt);
_6 = Option::<usize>::Some(move _25);
_25 = copy (((*_1).0: std::fmt::FormattingOptions).2: u16);
_24 = move _25 as usize (IntToInt);
StorageDead(_25);
_6 = Option::<usize>::Some(move _24);
StorageDead(_24);
goto -> bb9;
}
bb12: {
unreachable;
}
}
ALLOC0 (size: 8, align: 4) {

View file

@ -29,16 +29,10 @@
debug precision => _8;
let _8: usize;
scope 5 (inlined Formatter::<'_>::precision) {
let mut _22: std::option::Option<u16>;
scope 6 (inlined Option::<u16>::map::<usize, {closure@Formatter<'_>::precision::{closure#0}}>) {
let mut _23: isize;
let _24: u16;
let mut _25: usize;
scope 7 {
scope 8 (inlined Formatter::<'_>::precision::{closure#0}) {
}
}
}
let mut _22: u32;
let mut _23: u32;
let mut _24: usize;
let mut _25: u16;
}
}
}
@ -53,7 +47,7 @@
StorageLive(_20);
StorageLive(_21);
_21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
_20 = BitAnd(move _21, const 1_u32);
_20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG);
StorageDead(_21);
_4 = Ne(move _20, const 0_u32);
StorageDead(_20);
@ -75,12 +69,12 @@
bb3: {
StorageLive(_6);
StorageLive(_24);
StorageLive(_22);
_22 = copy (((*_1).0: std::fmt::FormattingOptions).4: std::option::Option<u16>);
StorageLive(_23);
_23 = discriminant(_22);
switchInt(move _23) -> [0: bb11, 1: bb12, otherwise: bb10];
_23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
_22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG);
StorageDead(_23);
switchInt(move _22) -> [0: bb10, otherwise: bb11];
}
bb4: {
@ -148,30 +142,31 @@
}
bb9: {
StorageDead(_23);
StorageDead(_22);
StorageDead(_24);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb10];
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb12];
}
bb10: {
unreachable;
}
bb11: {
StorageDead(_22);
_6 = const Option::<usize>::None;
goto -> bb9;
}
bb12: {
_24 = move ((_22 as Some).0: u16);
bb11: {
StorageDead(_22);
StorageLive(_24);
StorageLive(_25);
_25 = copy _24 as usize (IntToInt);
_6 = Option::<usize>::Some(move _25);
_25 = copy (((*_1).0: std::fmt::FormattingOptions).2: u16);
_24 = move _25 as usize (IntToInt);
StorageDead(_25);
_6 = Option::<usize>::Some(move _24);
StorageDead(_24);
goto -> bb9;
}
bb12: {
unreachable;
}
}
ALLOC0 (size: 16, align: 8) {

View file

@ -29,16 +29,10 @@
debug precision => _8;
let _8: usize;
scope 5 (inlined Formatter::<'_>::precision) {
let mut _22: std::option::Option<u16>;
scope 6 (inlined Option::<u16>::map::<usize, {closure@Formatter<'_>::precision::{closure#0}}>) {
let mut _23: isize;
let _24: u16;
let mut _25: usize;
scope 7 {
scope 8 (inlined Formatter::<'_>::precision::{closure#0}) {
}
}
}
let mut _22: u32;
let mut _23: u32;
let mut _24: usize;
let mut _25: u16;
}
}
}
@ -53,7 +47,7 @@
StorageLive(_20);
StorageLive(_21);
_21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
_20 = BitAnd(move _21, const 1_u32);
_20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG);
StorageDead(_21);
_4 = Ne(move _20, const 0_u32);
StorageDead(_20);
@ -75,12 +69,12 @@
bb3: {
StorageLive(_6);
StorageLive(_24);
StorageLive(_22);
_22 = copy (((*_1).0: std::fmt::FormattingOptions).4: std::option::Option<u16>);
StorageLive(_23);
_23 = discriminant(_22);
switchInt(move _23) -> [0: bb11, 1: bb12, otherwise: bb10];
_23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
_22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG);
StorageDead(_23);
switchInt(move _22) -> [0: bb10, otherwise: bb11];
}
bb4: {
@ -148,30 +142,31 @@
}
bb9: {
StorageDead(_23);
StorageDead(_22);
StorageDead(_24);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb10];
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb12];
}
bb10: {
unreachable;
}
bb11: {
StorageDead(_22);
_6 = const Option::<usize>::None;
goto -> bb9;
}
bb12: {
_24 = move ((_22 as Some).0: u16);
bb11: {
StorageDead(_22);
StorageLive(_24);
StorageLive(_25);
_25 = copy _24 as usize (IntToInt);
_6 = Option::<usize>::Some(move _25);
_25 = copy (((*_1).0: std::fmt::FormattingOptions).2: u16);
_24 = move _25 as usize (IntToInt);
StorageDead(_25);
_6 = Option::<usize>::Some(move _24);
StorageDead(_24);
goto -> bb9;
}
bb12: {
unreachable;
}
}
ALLOC0 (size: 16, align: 8) {