1
Fork 0

Auto merge of #136932 - m-ou-se:fmt-width-precision-u16, r=scottmcm

Reduce formatting `width` and `precision` to 16 bits

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

This is reduces the `width` and `precision` fields in format strings to 16 bits. They are currently full `usize`s, but it's a bit nonsensical that we need to support the case where someone wants to pad their value to eighteen quintillion spaces and/or have eighteen quintillion digits of precision.

By reducing these fields to 16 bit, we can reduce `FormattingOptions` to 64 bits (see https://github.com/rust-lang/rust/pull/136974) and improve the in memory representation of `format_args!()`. (See additional context below.)

This also fixes a bug where the width or precision is silently truncated when cross-compiling to a target with a smaller `usize`. By reducing the width and precision fields to the minimum guaranteed size of `usize`, 16 bits, this bug is eliminated.

This is a breaking change, but affects almost no existing code.

---

Details of this change:

There are three ways to set a width or precision today:

1. Directly a formatting string, e.g. `println!("{a:1234}")`
2. Indirectly in a formatting string, e.g. `println!("{a:width$}", width=1234)`
3. Through the unstable `FormattingOptions::width` method.

This PR:

- Adds a compiler error for 1. (`println!("{a:9999999}")` no longer compiles and gives a clear error.)
- Adds a runtime check for 2. (`println!("{a:width$}, width=9999999)` will panic.)
- Changes the signatures of the (unstable) `FormattingOptions::[get_]width` methods to use a `u16` instead.

---

Additional context for improving `FormattingOptions` and `fmt::Arguments`:

All the formatting flags and options are currently:

- The `+` flag (1 bit)
- The `-` flag (1 bit)
- The `#` flag (1 bit)
- The `0` flag (1 bit)
- The `x?` flag (1 bit)
- The `X?` flag (1 bit)
- The alignment (2 bits)
- The fill character (21 bits)
- Whether a width is specified (1 bit)
- Whether a precision is specified (1 bit)
- If used, the width (a full usize)
- If used, the precision (a full usize)

Everything except the last two can simply fit in a `u32` (those add up to 31 bits in total).

If we can accept a max width and precision of u16::MAX, we can make a `FormattingOptions` that is exactly 64 bits in size; the same size as a thin reference on most platforms.

If, additionally, we also limit the number of formatting arguments, we can also reduce the size of `fmt::Arguments` (that is, of a `format_args!()` expression).
This commit is contained in:
bors 2025-03-11 04:07:05 +00:00
commit 374ce1f909
16 changed files with 530 additions and 80 deletions

View file

@ -266,7 +266,7 @@ pub enum FormatAlignment {
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)] #[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
pub enum FormatCount { pub enum FormatCount {
/// `{:5}` or `{:.5}` /// `{:5}` or `{:.5}`
Literal(usize), Literal(u16),
/// `{:.*}`, `{:.5$}`, or `{:a$}`, etc. /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
Argument(FormatArgPosition), Argument(FormatArgPosition),
} }

View file

@ -2130,26 +2130,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[]))) self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[])))
} }
pub(super) fn expr_usize(&mut self, sp: Span, value: usize) -> hir::Expr<'hir> { fn expr_uint(&mut self, sp: Span, ty: ast::UintTy, value: u128) -> hir::Expr<'hir> {
let lit = self.arena.alloc(hir::Lit { let lit = self.arena.alloc(hir::Lit {
span: sp, span: sp,
node: ast::LitKind::Int( node: ast::LitKind::Int(value.into(), ast::LitIntType::Unsigned(ty)),
(value as u128).into(),
ast::LitIntType::Unsigned(ast::UintTy::Usize),
),
}); });
self.expr(sp, hir::ExprKind::Lit(lit)) self.expr(sp, hir::ExprKind::Lit(lit))
} }
pub(super) fn expr_usize(&mut self, sp: Span, value: usize) -> hir::Expr<'hir> {
self.expr_uint(sp, ast::UintTy::Usize, value as u128)
}
pub(super) fn expr_u32(&mut self, sp: Span, value: u32) -> hir::Expr<'hir> { pub(super) fn expr_u32(&mut self, sp: Span, value: u32) -> hir::Expr<'hir> {
let lit = self.arena.alloc(hir::Lit { self.expr_uint(sp, ast::UintTy::U32, value as u128)
span: sp, }
node: ast::LitKind::Int(
u128::from(value).into(), pub(super) fn expr_u16(&mut self, sp: Span, value: u16) -> hir::Expr<'hir> {
ast::LitIntType::Unsigned(ast::UintTy::U32), self.expr_uint(sp, ast::UintTy::U16, value as u128)
),
});
self.expr(sp, hir::ExprKind::Lit(lit))
} }
pub(super) fn expr_char(&mut self, sp: Span, value: char) -> hir::Expr<'hir> { pub(super) fn expr_char(&mut self, sp: Span, value: char) -> hir::Expr<'hir> {

View file

@ -292,7 +292,7 @@ fn make_count<'hir>(
hir::LangItem::FormatCount, hir::LangItem::FormatCount,
sym::Is, sym::Is,
)); ));
let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, *n)]); let value = ctx.arena.alloc_from_iter([ctx.expr_u16(sp, *n)]);
ctx.expr_call_mut(sp, count_is, value) ctx.expr_call_mut(sp, count_is, value)
} }
Some(FormatCount::Argument(arg)) => { Some(FormatCount::Argument(arg)) => {

View file

@ -190,7 +190,7 @@ pub enum DebugHex {
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum Count<'a> { pub enum Count<'a> {
/// The count is specified explicitly. /// The count is specified explicitly.
CountIs(usize), CountIs(u16),
/// The count is specified by the argument with the given name. /// The count is specified by the argument with the given name.
CountIsName(&'a str, InnerSpan), CountIsName(&'a str, InnerSpan),
/// The count is specified by the argument at the given index. /// The count is specified by the argument at the given index.
@ -565,7 +565,7 @@ impl<'a> Parser<'a> {
/// consuming a macro argument, `None` if it's the case. /// consuming a macro argument, `None` if it's the case.
fn position(&mut self) -> Option<Position<'a>> { fn position(&mut self) -> Option<Position<'a>> {
if let Some(i) = self.integer() { if let Some(i) = self.integer() {
Some(ArgumentIs(i)) Some(ArgumentIs(i.into()))
} else { } else {
match self.cur.peek() { match self.cur.peek() {
Some(&(lo, c)) if rustc_lexer::is_id_start(c) => { Some(&(lo, c)) if rustc_lexer::is_id_start(c) => {
@ -771,7 +771,7 @@ impl<'a> Parser<'a> {
/// width. /// width.
fn count(&mut self, start: usize) -> Count<'a> { fn count(&mut self, start: usize) -> Count<'a> {
if let Some(i) = self.integer() { if let Some(i) = self.integer() {
if self.consume('$') { CountIsParam(i) } else { CountIs(i) } if self.consume('$') { CountIsParam(i.into()) } else { CountIs(i) }
} else { } else {
let tmp = self.cur.clone(); let tmp = self.cur.clone();
let word = self.word(); let word = self.word();
@ -822,15 +822,15 @@ impl<'a> Parser<'a> {
word word
} }
fn integer(&mut self) -> Option<usize> { fn integer(&mut self) -> Option<u16> {
let mut cur: usize = 0; let mut cur: u16 = 0;
let mut found = false; let mut found = false;
let mut overflow = false; let mut overflow = false;
let start = self.current_pos(); let start = self.current_pos();
while let Some(&(_, c)) = self.cur.peek() { while let Some(&(_, c)) = self.cur.peek() {
if let Some(i) = c.to_digit(10) { if let Some(i) = c.to_digit(10) {
let (tmp, mul_overflow) = cur.overflowing_mul(10); let (tmp, mul_overflow) = cur.overflowing_mul(10);
let (tmp, add_overflow) = tmp.overflowing_add(i as usize); let (tmp, add_overflow) = tmp.overflowing_add(i as u16);
if mul_overflow || add_overflow { if mul_overflow || add_overflow {
overflow = true; overflow = true;
} }
@ -847,11 +847,11 @@ impl<'a> Parser<'a> {
let overflowed_int = &self.input[start..end]; let overflowed_int = &self.input[start..end];
self.err( self.err(
format!( format!(
"integer `{}` does not fit into the type `usize` whose range is `0..={}`", "integer `{}` does not fit into the type `u16` whose range is `0..={}`",
overflowed_int, overflowed_int,
usize::MAX u16::MAX
), ),
"integer out of range for `usize`", "integer out of range for `u16`",
self.span(start, end), self.span(start, end),
); );
} }

View file

@ -1016,6 +1016,7 @@ symbols! {
from_residual, from_residual,
from_size_align_unchecked, from_size_align_unchecked,
from_str_method, from_str_method,
from_u16,
from_usize, from_usize,
from_yeet, from_yeet,
fs_create_dir, fs_create_dir,

View file

@ -29,7 +29,7 @@ fn float_to_decimal_common_exact<T>(
fmt: &mut Formatter<'_>, fmt: &mut Formatter<'_>,
num: &T, num: &T,
sign: flt2dec::Sign, sign: flt2dec::Sign,
precision: usize, precision: u16,
) -> Result ) -> Result
where where
T: flt2dec::DecodableFloat, T: flt2dec::DecodableFloat,
@ -40,7 +40,7 @@ where
flt2dec::strategy::grisu::format_exact, flt2dec::strategy::grisu::format_exact,
*num, *num,
sign, sign,
precision, precision.into(),
&mut buf, &mut buf,
&mut parts, &mut parts,
); );
@ -55,7 +55,7 @@ fn float_to_decimal_common_shortest<T>(
fmt: &mut Formatter<'_>, fmt: &mut Formatter<'_>,
num: &T, num: &T,
sign: flt2dec::Sign, sign: flt2dec::Sign,
precision: usize, precision: u16,
) -> Result ) -> Result
where where
T: flt2dec::DecodableFloat, T: flt2dec::DecodableFloat,
@ -68,7 +68,7 @@ where
flt2dec::strategy::grisu::format_shortest, flt2dec::strategy::grisu::format_shortest,
*num, *num,
sign, sign,
precision, precision.into(),
&mut buf, &mut buf,
&mut parts, &mut parts,
); );
@ -101,7 +101,7 @@ fn float_to_exponential_common_exact<T>(
fmt: &mut Formatter<'_>, fmt: &mut Formatter<'_>,
num: &T, num: &T,
sign: flt2dec::Sign, sign: flt2dec::Sign,
precision: usize, precision: u16,
upper: bool, upper: bool,
) -> Result ) -> Result
where where
@ -113,7 +113,7 @@ where
flt2dec::strategy::grisu::format_exact, flt2dec::strategy::grisu::format_exact,
*num, *num,
sign, sign,
precision, precision.into(),
upper, upper,
&mut buf, &mut buf,
&mut parts, &mut parts,

View file

@ -294,8 +294,8 @@ pub struct FormattingOptions {
flags: u32, flags: u32,
fill: char, fill: char,
align: Option<Alignment>, align: Option<Alignment>,
width: Option<usize>, width: Option<u16>,
precision: Option<usize>, precision: Option<u16>,
} }
impl FormattingOptions { impl FormattingOptions {
@ -389,7 +389,7 @@ impl FormattingOptions {
/// the padding specified by [`FormattingOptions::fill`]/[`FormattingOptions::align`] /// the padding specified by [`FormattingOptions::fill`]/[`FormattingOptions::align`]
/// will be used to take up the required space. /// will be used to take up the required space.
#[unstable(feature = "formatting_options", issue = "118117")] #[unstable(feature = "formatting_options", issue = "118117")]
pub fn width(&mut self, width: Option<usize>) -> &mut Self { pub fn width(&mut self, width: Option<u16>) -> &mut Self {
self.width = width; self.width = width;
self self
} }
@ -403,7 +403,7 @@ impl FormattingOptions {
/// - For floating-point types, this indicates how many digits after the /// - For floating-point types, this indicates how many digits after the
/// decimal point should be printed. /// decimal point should be printed.
#[unstable(feature = "formatting_options", issue = "118117")] #[unstable(feature = "formatting_options", issue = "118117")]
pub fn precision(&mut self, precision: Option<usize>) -> &mut Self { pub fn precision(&mut self, precision: Option<u16>) -> &mut Self {
self.precision = precision; self.precision = precision;
self self
} }
@ -455,12 +455,12 @@ impl FormattingOptions {
} }
/// Returns the current width. /// Returns the current width.
#[unstable(feature = "formatting_options", issue = "118117")] #[unstable(feature = "formatting_options", issue = "118117")]
pub const fn get_width(&self) -> Option<usize> { pub const fn get_width(&self) -> Option<u16> {
self.width self.width
} }
/// Returns the current precision. /// Returns the current precision.
#[unstable(feature = "formatting_options", issue = "118117")] #[unstable(feature = "formatting_options", issue = "118117")]
pub const fn get_precision(&self) -> Option<usize> { pub const fn get_precision(&self) -> Option<u16> {
self.precision self.precision
} }
/// Returns the current precision. /// Returns the current precision.
@ -1499,15 +1499,18 @@ unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argume
unsafe { value.fmt(fmt) } unsafe { value.fmt(fmt) }
} }
unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option<usize> { unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option<u16> {
match *cnt { match *cnt {
#[cfg(bootstrap)]
rt::Count::Is(n) => Some(n as u16),
#[cfg(not(bootstrap))]
rt::Count::Is(n) => Some(n), rt::Count::Is(n) => Some(n),
rt::Count::Implied => None, rt::Count::Implied => None,
rt::Count::Param(i) => { rt::Count::Param(i) => {
debug_assert!(i < args.len()); debug_assert!(i < args.len());
// SAFETY: cnt and args come from the same Arguments, // SAFETY: cnt and args come from the same Arguments,
// which guarantees this index is always within bounds. // which guarantees this index is always within bounds.
unsafe { args.get_unchecked(i).as_usize() } unsafe { args.get_unchecked(i).as_u16() }
} }
} }
} }
@ -1516,11 +1519,11 @@ unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option<usize>
#[must_use = "don't forget to write the post padding"] #[must_use = "don't forget to write the post padding"]
pub(crate) struct PostPadding { pub(crate) struct PostPadding {
fill: char, fill: char,
padding: usize, padding: u16,
} }
impl PostPadding { impl PostPadding {
fn new(fill: char, padding: usize) -> PostPadding { fn new(fill: char, padding: u16) -> PostPadding {
PostPadding { fill, padding } PostPadding { fill, padding }
} }
@ -1634,7 +1637,7 @@ impl<'a> Formatter<'a> {
} }
// Check if we're over the minimum width, if so then we can also // Check if we're over the minimum width, if so then we can also
// just write the bytes. // just write the bytes.
Some(min) if width >= min => { Some(min) if width >= usize::from(min) => {
write_prefix(self, sign, prefix)?; write_prefix(self, sign, prefix)?;
self.buf.write_str(buf) self.buf.write_str(buf)
} }
@ -1645,7 +1648,7 @@ impl<'a> Formatter<'a> {
let old_align = let old_align =
crate::mem::replace(&mut self.options.align, Some(Alignment::Right)); crate::mem::replace(&mut self.options.align, Some(Alignment::Right));
write_prefix(self, sign, prefix)?; write_prefix(self, sign, prefix)?;
let post_padding = self.padding(min - width, Alignment::Right)?; let post_padding = self.padding(min - width as u16, Alignment::Right)?;
self.buf.write_str(buf)?; self.buf.write_str(buf)?;
post_padding.write(self)?; post_padding.write(self)?;
self.options.fill = old_fill; self.options.fill = old_fill;
@ -1654,7 +1657,7 @@ impl<'a> Formatter<'a> {
} }
// Otherwise, the sign and prefix goes after the padding // Otherwise, the sign and prefix goes after the padding
Some(min) => { Some(min) => {
let post_padding = self.padding(min - width, Alignment::Right)?; let post_padding = self.padding(min - width as u16, Alignment::Right)?;
write_prefix(self, sign, prefix)?; write_prefix(self, sign, prefix)?;
self.buf.write_str(buf)?; self.buf.write_str(buf)?;
post_padding.write(self) post_padding.write(self)
@ -1702,14 +1705,14 @@ impl<'a> Formatter<'a> {
// string being formatted. // 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.precision {
let mut iter = s.char_indices(); let mut iter = s.char_indices();
let remaining = match iter.advance_by(max_char_count) { let remaining = match iter.advance_by(usize::from(max_char_count)) {
Ok(()) => 0, Ok(()) => 0,
Err(remaining) => remaining.get(), Err(remaining) => remaining.get(),
}; };
// SAFETY: The offset of `.char_indices()` is guaranteed to be // SAFETY: The offset of `.char_indices()` is guaranteed to be
// in-bounds and between character boundaries. // in-bounds and between character boundaries.
let truncated = unsafe { s.get_unchecked(..iter.offset()) }; let truncated = unsafe { s.get_unchecked(..iter.offset()) };
(truncated, max_char_count - remaining) (truncated, usize::from(max_char_count) - remaining)
} else { } else {
// Use the optimized char counting algorithm for the full string. // Use the optimized char counting algorithm for the full string.
(s, s.chars().count()) (s, s.chars().count())
@ -1717,11 +1720,11 @@ impl<'a> Formatter<'a> {
// The `width` field is more of a minimum width parameter at this point. // The `width` field is more of a minimum width parameter at this point.
if let Some(width) = self.options.width if let Some(width) = self.options.width
&& char_count < width && char_count < usize::from(width)
{ {
// If we're under the minimum width, then fill up the minimum width // If we're under the minimum width, then fill up the minimum width
// with the specified string + some alignment. // with the specified string + some alignment.
let post_padding = self.padding(width - char_count, Alignment::Left)?; let post_padding = self.padding(width - char_count as u16, Alignment::Left)?;
self.buf.write_str(s)?; self.buf.write_str(s)?;
post_padding.write(self) post_padding.write(self)
} else { } else {
@ -1737,7 +1740,7 @@ impl<'a> Formatter<'a> {
/// thing that is being padded. /// thing that is being padded.
pub(crate) fn padding( pub(crate) fn padding(
&mut self, &mut self,
padding: usize, padding: u16,
default: Alignment, default: Alignment,
) -> result::Result<PostPadding, Error> { ) -> result::Result<PostPadding, Error> {
let align = self.align().unwrap_or(default); let align = self.align().unwrap_or(default);
@ -1777,19 +1780,19 @@ impl<'a> Formatter<'a> {
// remove the sign from the formatted parts // remove the sign from the formatted parts
formatted.sign = ""; formatted.sign = "";
width = width.saturating_sub(sign.len()); width = width.saturating_sub(sign.len() as u16);
self.options.fill = '0'; self.options.fill = '0';
self.options.align = Some(Alignment::Right); self.options.align = Some(Alignment::Right);
} }
// remaining parts go through the ordinary padding process. // remaining parts go through the ordinary padding process.
let len = formatted.len(); let len = formatted.len();
let ret = if width <= len { let ret = if usize::from(width) <= len {
// no padding // no padding
// SAFETY: Per the precondition. // SAFETY: Per the precondition.
unsafe { self.write_formatted_parts(&formatted) } unsafe { self.write_formatted_parts(&formatted) }
} else { } else {
let post_padding = self.padding(width - len, Alignment::Right)?; let post_padding = self.padding(width - len as u16, Alignment::Right)?;
// SAFETY: Per the precondition. // SAFETY: Per the precondition.
unsafe { unsafe {
self.write_formatted_parts(&formatted)?; self.write_formatted_parts(&formatted)?;
@ -2021,7 +2024,7 @@ impl<'a> Formatter<'a> {
#[must_use] #[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")] #[stable(feature = "fmt_flags", since = "1.5.0")]
pub fn width(&self) -> Option<usize> { pub fn width(&self) -> Option<usize> {
self.options.width self.options.width.map(|x| x as usize)
} }
/// Returns the optionally specified precision for numeric types. /// Returns the optionally specified precision for numeric types.
@ -2052,7 +2055,7 @@ impl<'a> Formatter<'a> {
#[must_use] #[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")] #[stable(feature = "fmt_flags", since = "1.5.0")]
pub fn precision(&self) -> Option<usize> { pub fn precision(&self) -> Option<usize> {
self.options.precision self.options.precision.map(|x| x as usize)
} }
/// Determines if the `+` flag was specified. /// Determines if the `+` flag was specified.
@ -2792,7 +2795,7 @@ pub(crate) fn pointer_fmt_inner(ptr_addr: usize, f: &mut Formatter<'_>) -> Resul
f.options.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32); f.options.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32);
if f.options.width.is_none() { if f.options.width.is_none() {
f.options.width = Some((usize::BITS / 4) as usize + 2); f.options.width = Some((usize::BITS / 4) as u16 + 2);
} }
} }
f.options.flags |= 1 << (rt::Flag::Alternate as u32); f.options.flags |= 1 << (rt::Flag::Alternate as u32);

View file

@ -47,7 +47,11 @@ pub enum Alignment {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum Count { pub enum Count {
/// Specified with a literal number, stores the value /// Specified with a literal number, stores the value
#[cfg(bootstrap)]
Is(usize), Is(usize),
/// Specified with a literal number, stores the value
#[cfg(not(bootstrap))]
Is(u16),
/// Specified using `$` and `*` syntaxes, stores the index into `args` /// Specified using `$` and `*` syntaxes, stores the index into `args`
Param(usize), Param(usize),
/// Not specified /// Not specified
@ -74,7 +78,7 @@ enum ArgumentType<'a> {
formatter: unsafe fn(NonNull<()>, &mut Formatter<'_>) -> Result, formatter: unsafe fn(NonNull<()>, &mut Formatter<'_>) -> Result,
_lifetime: PhantomData<&'a ()>, _lifetime: PhantomData<&'a ()>,
}, },
Count(usize), Count(u16),
} }
/// This struct represents a generic "argument" which is taken by format_args!(). /// This struct represents a generic "argument" which is taken by format_args!().
@ -150,8 +154,12 @@ impl Argument<'_> {
Self::new(x, UpperExp::fmt) Self::new(x, UpperExp::fmt)
} }
#[inline] #[inline]
#[track_caller]
pub const fn from_usize(x: &usize) -> Argument<'_> { pub const fn from_usize(x: &usize) -> Argument<'_> {
Argument { ty: ArgumentType::Count(*x) } if *x > u16::MAX as usize {
panic!("Formatting argument out of range");
}
Argument { ty: ArgumentType::Count(*x as u16) }
} }
/// Format this placeholder argument. /// Format this placeholder argument.
@ -181,7 +189,7 @@ impl Argument<'_> {
} }
#[inline] #[inline]
pub(super) const fn as_usize(&self) -> Option<usize> { pub(super) const fn as_u16(&self) -> Option<u16> {
match self.ty { match self.ty {
ArgumentType::Count(count) => Some(count), ArgumentType::Count(count) => Some(count),
ArgumentType::Placeholder { .. } => None, ArgumentType::Placeholder { .. } => None,

View file

@ -1377,7 +1377,8 @@ impl fmt::Debug for Duration {
} else { } else {
// We need to add padding. Use the `Formatter::padding` helper function. // We need to add padding. Use the `Formatter::padding` helper function.
let default_align = fmt::Alignment::Left; let default_align = fmt::Alignment::Left;
let post_padding = f.padding(requested_w - actual_w, default_align)?; let post_padding =
f.padding((requested_w - actual_w) as u16, default_align)?;
emit_without_padding(f)?; emit_without_padding(f)?;
post_padding.write(f) post_padding.write(f)
} }

View file

@ -577,7 +577,7 @@ where
} }
// very large output // very large output
assert_eq!(to_string(f, 1.1, Minus, 80000), format!("1.1{:0>79999}", "")); assert_eq!(to_string(f, 1.1, Minus, 50000), format!("1.1{:0>49999}", ""));
} }
pub fn to_shortest_exp_str_test<F>(mut f_: F) pub fn to_shortest_exp_str_test<F>(mut f_: F)
@ -914,22 +914,22 @@ where
); );
// very large output // very large output
assert_eq!(to_string(f, 0.0, Minus, 80000, false), format!("0.{:0>79999}e0", "")); assert_eq!(to_string(f, 0.0, Minus, 50000, false), format!("0.{:0>49999}e0", ""));
assert_eq!(to_string(f, 1.0e1, Minus, 80000, false), format!("1.{:0>79999}e1", "")); assert_eq!(to_string(f, 1.0e1, Minus, 50000, false), format!("1.{:0>49999}e1", ""));
assert_eq!(to_string(f, 1.0e0, Minus, 80000, false), format!("1.{:0>79999}e0", "")); assert_eq!(to_string(f, 1.0e0, Minus, 50000, false), format!("1.{:0>49999}e0", ""));
assert_eq!( assert_eq!(
to_string(f, 1.0e-1, Minus, 80000, false), to_string(f, 1.0e-1, Minus, 50000, false),
format!( format!(
"1.000000000000000055511151231257827021181583404541015625{:0>79945}\ "1.000000000000000055511151231257827021181583404541015625{:0>49945}\
e-1", e-1",
"" ""
) )
); );
assert_eq!( assert_eq!(
to_string(f, 1.0e-20, Minus, 80000, false), to_string(f, 1.0e-20, Minus, 50000, false),
format!( format!(
"9.999999999999999451532714542095716517295037027873924471077157760\ "9.999999999999999451532714542095716517295037027873924471077157760\
66783064379706047475337982177734375{:0>79901}e-21", 66783064379706047475337982177734375{:0>49901}e-21",
"" ""
) )
); );
@ -1150,18 +1150,18 @@ where
); );
// very large output // very large output
assert_eq!(to_string(f, 0.0, Minus, 80000), format!("0.{:0>80000}", "")); assert_eq!(to_string(f, 0.0, Minus, 50000), format!("0.{:0>50000}", ""));
assert_eq!(to_string(f, 1.0e1, Minus, 80000), format!("10.{:0>80000}", "")); assert_eq!(to_string(f, 1.0e1, Minus, 50000), format!("10.{:0>50000}", ""));
assert_eq!(to_string(f, 1.0e0, Minus, 80000), format!("1.{:0>80000}", "")); assert_eq!(to_string(f, 1.0e0, Minus, 50000), format!("1.{:0>50000}", ""));
assert_eq!( assert_eq!(
to_string(f, 1.0e-1, Minus, 80000), to_string(f, 1.0e-1, Minus, 50000),
format!("0.1000000000000000055511151231257827021181583404541015625{:0>79945}", "") format!("0.1000000000000000055511151231257827021181583404541015625{:0>49945}", "")
); );
assert_eq!( assert_eq!(
to_string(f, 1.0e-20, Minus, 80000), to_string(f, 1.0e-20, Minus, 50000),
format!( format!(
"0.0000000000000000000099999999999999994515327145420957165172950370\ "0.0000000000000000000099999999999999994515327145420957165172950370\
2787392447107715776066783064379706047475337982177734375{:0>79881}", 2787392447107715776066783064379706047475337982177734375{:0>49881}",
"" ""
) )
); );

View file

@ -137,7 +137,7 @@ pub enum FormatAlignment {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum FormatCount { pub enum FormatCount {
/// `{:5}` or `{:.5}` /// `{:5}` or `{:.5}`
Literal(usize), Literal(u16),
/// `{:.*}`, `{:.5$}`, or `{:a$}`, etc. /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
Argument(FormatArgPosition), Argument(FormatArgPosition),
} }

View file

@ -29,6 +29,16 @@
debug precision => _8; debug precision => _8;
let _8: usize; let _8: usize;
scope 5 (inlined Formatter::<'_>::precision) { 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}) {
}
}
}
} }
} }
} }
@ -65,9 +75,12 @@
bb3: { bb3: {
StorageLive(_6); StorageLive(_6);
_6 = copy (((*_1).0: std::fmt::FormattingOptions).4: std::option::Option<usize>); StorageLive(_24);
_7 = discriminant(_6); StorageLive(_22);
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb9]; _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];
} }
bb4: { bb4: {
@ -135,7 +148,33 @@
} }
bb9: { bb9: {
StorageDead(_23);
StorageDead(_22);
StorageDead(_24);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb10];
}
bb10: {
unreachable; unreachable;
} }
bb11: {
_6 = const Option::<usize>::None;
goto -> bb9;
}
bb12: {
_24 = move ((_22 as Some).0: u16);
StorageLive(_25);
_25 = copy _24 as usize (IntToInt);
_6 = Option::<usize>::Some(move _25);
StorageDead(_25);
goto -> bb9;
}
}
ALLOC0 (size: 8, align: 4) {
00 00 00 00 __ __ __ __ │ ....░░░░
} }

View file

@ -29,6 +29,16 @@
debug precision => _8; debug precision => _8;
let _8: usize; let _8: usize;
scope 5 (inlined Formatter::<'_>::precision) { 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}) {
}
}
}
} }
} }
} }
@ -65,9 +75,12 @@
bb3: { bb3: {
StorageLive(_6); StorageLive(_6);
_6 = copy (((*_1).0: std::fmt::FormattingOptions).4: std::option::Option<usize>); StorageLive(_24);
_7 = discriminant(_6); StorageLive(_22);
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb9]; _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];
} }
bb4: { bb4: {
@ -135,7 +148,33 @@
} }
bb9: { bb9: {
StorageDead(_23);
StorageDead(_22);
StorageDead(_24);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb10];
}
bb10: {
unreachable; unreachable;
} }
bb11: {
_6 = const Option::<usize>::None;
goto -> bb9;
}
bb12: {
_24 = move ((_22 as Some).0: u16);
StorageLive(_25);
_25 = copy _24 as usize (IntToInt);
_6 = Option::<usize>::Some(move _25);
StorageDead(_25);
goto -> bb9;
}
}
ALLOC0 (size: 8, align: 4) {
00 00 00 00 __ __ __ __ │ ....░░░░
} }

View file

@ -0,0 +1,180 @@
- // MIR for `float_to_exponential_common` before GVN
+ // MIR for `float_to_exponential_common` after GVN
fn float_to_exponential_common(_1: &mut Formatter<'_>, _2: &T, _3: bool) -> Result<(), std::fmt::Error> {
debug fmt => _1;
debug num => _2;
debug upper => _3;
let mut _0: std::result::Result<(), std::fmt::Error>;
let _4: bool;
let mut _6: std::option::Option<usize>;
let mut _7: isize;
let mut _9: &mut std::fmt::Formatter<'_>;
let mut _10: &T;
let mut _11: core::num::flt2dec::Sign;
let mut _12: u32;
let mut _13: u32;
let mut _14: usize;
let mut _15: bool;
let mut _16: &mut std::fmt::Formatter<'_>;
let mut _17: &T;
let mut _18: core::num::flt2dec::Sign;
let mut _19: bool;
scope 1 {
debug force_sign => _4;
let _5: core::num::flt2dec::Sign;
scope 2 {
debug sign => _5;
scope 3 {
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}) {
}
}
}
}
}
}
}
scope 4 (inlined Formatter::<'_>::sign_plus) {
let mut _20: u32;
let mut _21: u32;
}
bb0: {
StorageLive(_4);
StorageLive(_20);
StorageLive(_21);
_21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
_20 = BitAnd(move _21, const 1_u32);
StorageDead(_21);
_4 = Ne(move _20, const 0_u32);
StorageDead(_20);
StorageLive(_5);
switchInt(copy _4) -> [0: bb2, otherwise: bb1];
}
bb1: {
- _5 = MinusPlus;
+ _5 = const MinusPlus;
goto -> bb3;
}
bb2: {
- _5 = core::num::flt2dec::Sign::Minus;
+ _5 = const core::num::flt2dec::Sign::Minus;
goto -> bb3;
}
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];
}
bb4: {
- StorageLive(_8);
+ nop;
_8 = copy ((_6 as Some).0: usize);
StorageLive(_9);
_9 = copy _1;
StorageLive(_10);
_10 = copy _2;
StorageLive(_11);
_11 = copy _5;
StorageLive(_12);
StorageLive(_13);
StorageLive(_14);
_14 = copy _8;
- _13 = move _14 as u32 (IntToInt);
+ _13 = copy _8 as u32 (IntToInt);
StorageDead(_14);
_12 = Add(move _13, const 1_u32);
StorageDead(_13);
StorageLive(_15);
_15 = copy _3;
- _0 = float_to_exponential_common_exact::<T>(move _9, move _10, move _11, move _12, move _15) -> [return: bb5, unwind unreachable];
+ _0 = float_to_exponential_common_exact::<T>(copy _1, copy _2, move _11, move _12, copy _3) -> [return: bb5, unwind unreachable];
}
bb5: {
StorageDead(_15);
StorageDead(_12);
StorageDead(_11);
StorageDead(_10);
StorageDead(_9);
- StorageDead(_8);
+ nop;
goto -> bb8;
}
bb6: {
StorageLive(_16);
_16 = copy _1;
StorageLive(_17);
_17 = copy _2;
StorageLive(_18);
_18 = copy _5;
StorageLive(_19);
_19 = copy _3;
- _0 = float_to_exponential_common_shortest::<T>(move _16, move _17, move _18, move _19) -> [return: bb7, unwind unreachable];
+ _0 = float_to_exponential_common_shortest::<T>(copy _1, copy _2, move _18, copy _3) -> [return: bb7, unwind unreachable];
}
bb7: {
StorageDead(_19);
StorageDead(_18);
StorageDead(_17);
StorageDead(_16);
goto -> bb8;
}
bb8: {
StorageDead(_5);
StorageDead(_4);
StorageDead(_6);
return;
}
bb9: {
StorageDead(_23);
StorageDead(_22);
StorageDead(_24);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb10];
}
bb10: {
unreachable;
}
bb11: {
_6 = const Option::<usize>::None;
goto -> bb9;
}
bb12: {
_24 = move ((_22 as Some).0: u16);
StorageLive(_25);
_25 = copy _24 as usize (IntToInt);
_6 = Option::<usize>::Some(move _25);
StorageDead(_25);
goto -> bb9;
}
}
ALLOC0 (size: 16, align: 8) {
00 00 00 00 00 00 00 00 __ __ __ __ __ __ __ __ │ ........░░░░░░░░
}

View file

@ -0,0 +1,180 @@
- // MIR for `float_to_exponential_common` before GVN
+ // MIR for `float_to_exponential_common` after GVN
fn float_to_exponential_common(_1: &mut Formatter<'_>, _2: &T, _3: bool) -> Result<(), std::fmt::Error> {
debug fmt => _1;
debug num => _2;
debug upper => _3;
let mut _0: std::result::Result<(), std::fmt::Error>;
let _4: bool;
let mut _6: std::option::Option<usize>;
let mut _7: isize;
let mut _9: &mut std::fmt::Formatter<'_>;
let mut _10: &T;
let mut _11: core::num::flt2dec::Sign;
let mut _12: u32;
let mut _13: u32;
let mut _14: usize;
let mut _15: bool;
let mut _16: &mut std::fmt::Formatter<'_>;
let mut _17: &T;
let mut _18: core::num::flt2dec::Sign;
let mut _19: bool;
scope 1 {
debug force_sign => _4;
let _5: core::num::flt2dec::Sign;
scope 2 {
debug sign => _5;
scope 3 {
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}) {
}
}
}
}
}
}
}
scope 4 (inlined Formatter::<'_>::sign_plus) {
let mut _20: u32;
let mut _21: u32;
}
bb0: {
StorageLive(_4);
StorageLive(_20);
StorageLive(_21);
_21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
_20 = BitAnd(move _21, const 1_u32);
StorageDead(_21);
_4 = Ne(move _20, const 0_u32);
StorageDead(_20);
StorageLive(_5);
switchInt(copy _4) -> [0: bb2, otherwise: bb1];
}
bb1: {
- _5 = MinusPlus;
+ _5 = const MinusPlus;
goto -> bb3;
}
bb2: {
- _5 = core::num::flt2dec::Sign::Minus;
+ _5 = const core::num::flt2dec::Sign::Minus;
goto -> bb3;
}
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];
}
bb4: {
- StorageLive(_8);
+ nop;
_8 = copy ((_6 as Some).0: usize);
StorageLive(_9);
_9 = copy _1;
StorageLive(_10);
_10 = copy _2;
StorageLive(_11);
_11 = copy _5;
StorageLive(_12);
StorageLive(_13);
StorageLive(_14);
_14 = copy _8;
- _13 = move _14 as u32 (IntToInt);
+ _13 = copy _8 as u32 (IntToInt);
StorageDead(_14);
_12 = Add(move _13, const 1_u32);
StorageDead(_13);
StorageLive(_15);
_15 = copy _3;
- _0 = float_to_exponential_common_exact::<T>(move _9, move _10, move _11, move _12, move _15) -> [return: bb5, unwind continue];
+ _0 = float_to_exponential_common_exact::<T>(copy _1, copy _2, move _11, move _12, copy _3) -> [return: bb5, unwind continue];
}
bb5: {
StorageDead(_15);
StorageDead(_12);
StorageDead(_11);
StorageDead(_10);
StorageDead(_9);
- StorageDead(_8);
+ nop;
goto -> bb8;
}
bb6: {
StorageLive(_16);
_16 = copy _1;
StorageLive(_17);
_17 = copy _2;
StorageLive(_18);
_18 = copy _5;
StorageLive(_19);
_19 = copy _3;
- _0 = float_to_exponential_common_shortest::<T>(move _16, move _17, move _18, move _19) -> [return: bb7, unwind continue];
+ _0 = float_to_exponential_common_shortest::<T>(copy _1, copy _2, move _18, copy _3) -> [return: bb7, unwind continue];
}
bb7: {
StorageDead(_19);
StorageDead(_18);
StorageDead(_17);
StorageDead(_16);
goto -> bb8;
}
bb8: {
StorageDead(_5);
StorageDead(_4);
StorageDead(_6);
return;
}
bb9: {
StorageDead(_23);
StorageDead(_22);
StorageDead(_24);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb10];
}
bb10: {
unreachable;
}
bb11: {
_6 = const Option::<usize>::None;
goto -> bb9;
}
bb12: {
_24 = move ((_22 as Some).0: u16);
StorageLive(_25);
_25 = copy _24 as usize (IntToInt);
_6 = Option::<usize>::Some(move _25);
StorageDead(_25);
goto -> bb9;
}
}
ALLOC0 (size: 16, align: 8) {
00 00 00 00 00 00 00 00 __ __ __ __ __ __ __ __ │ ........░░░░░░░░
}

View file

@ -1,5 +1,6 @@
// skip-filecheck // skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
// EMIT_MIR_FOR_EACH_BIT_WIDTH
#![feature(flt2dec)] #![feature(flt2dec)]