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:
commit
374ce1f909
16 changed files with 530 additions and 80 deletions
|
@ -190,7 +190,7 @@ pub enum DebugHex {
|
|||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Count<'a> {
|
||||
/// The count is specified explicitly.
|
||||
CountIs(usize),
|
||||
CountIs(u16),
|
||||
/// The count is specified by the argument with the given name.
|
||||
CountIsName(&'a str, InnerSpan),
|
||||
/// 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.
|
||||
fn position(&mut self) -> Option<Position<'a>> {
|
||||
if let Some(i) = self.integer() {
|
||||
Some(ArgumentIs(i))
|
||||
Some(ArgumentIs(i.into()))
|
||||
} else {
|
||||
match self.cur.peek() {
|
||||
Some(&(lo, c)) if rustc_lexer::is_id_start(c) => {
|
||||
|
@ -771,7 +771,7 @@ impl<'a> Parser<'a> {
|
|||
/// width.
|
||||
fn count(&mut self, start: usize) -> Count<'a> {
|
||||
if let Some(i) = self.integer() {
|
||||
if self.consume('$') { CountIsParam(i) } else { CountIs(i) }
|
||||
if self.consume('$') { CountIsParam(i.into()) } else { CountIs(i) }
|
||||
} else {
|
||||
let tmp = self.cur.clone();
|
||||
let word = self.word();
|
||||
|
@ -822,15 +822,15 @@ impl<'a> Parser<'a> {
|
|||
word
|
||||
}
|
||||
|
||||
fn integer(&mut self) -> Option<usize> {
|
||||
let mut cur: usize = 0;
|
||||
fn integer(&mut self) -> Option<u16> {
|
||||
let mut cur: u16 = 0;
|
||||
let mut found = false;
|
||||
let mut overflow = false;
|
||||
let start = self.current_pos();
|
||||
while let Some(&(_, c)) = self.cur.peek() {
|
||||
if let Some(i) = c.to_digit(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 {
|
||||
overflow = true;
|
||||
}
|
||||
|
@ -847,11 +847,11 @@ impl<'a> Parser<'a> {
|
|||
let overflowed_int = &self.input[start..end];
|
||||
self.err(
|
||||
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,
|
||||
usize::MAX
|
||||
u16::MAX
|
||||
),
|
||||
"integer out of range for `usize`",
|
||||
"integer out of range for `u16`",
|
||||
self.span(start, end),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue