Reduce FormattingOptions to 64 bits.

This commit is contained in:
Mara Bos 2025-03-12 16:32:00 +01:00
parent 0998d4095b
commit 90645c187c
7 changed files with 253 additions and 213 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

@ -1002,7 +1002,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,50 @@ 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.)
/// ```
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 +337,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 +354,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 +368,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 +385,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 +399,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 +408,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 +425,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 +445,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 +534,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")]
@ -1478,15 +1506,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());
@ -1494,17 +1532,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());
@ -1515,6 +1554,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 {
@ -1628,40 +1681,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)
}
}
@ -1697,13 +1738,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,
@ -1719,12 +1760,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 {
@ -1743,19 +1783,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.
@ -1767,12 +1808,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;
@ -1781,8 +1826,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.
@ -1799,13 +1843,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) }
}
}
@ -1926,7 +1965,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.
@ -1959,7 +2000,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.
@ -1994,7 +2035,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.
@ -2024,7 +2065,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.
@ -2055,7 +2100,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.
@ -2087,7 +2136,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.
@ -2116,7 +2165,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.
@ -2144,7 +2193,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.
@ -2170,17 +2219,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
@ -2760,7 +2808,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]))
@ -2784,26 +2832,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 {