From 90645c187c9d640893a8b5d1cf61c4d29dfabde8 Mon Sep 17 00:00:00 2001
From: Mara Bos <m-ou.se@m-ou.se>
Date: Wed, 12 Mar 2025 16:32:00 +0100
Subject: [PATCH 1/4] Reduce FormattingOptions to 64 bits.

---
 compiler/rustc_ast_lowering/src/expr.rs   |   5 -
 compiler/rustc_ast_lowering/src/format.rs |  40 +--
 compiler/rustc_hir/src/lang_items.rs      |   1 -
 compiler/rustc_span/src/symbol.rs         |   1 -
 library/core/src/fmt/float.rs             |   6 +-
 library/core/src/fmt/mod.rs               | 392 ++++++++++++----------
 library/core/src/fmt/rt.rs                |  21 +-
 7 files changed, 253 insertions(+), 213 deletions(-)

diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 5bb6704dde45a..6e32f8bc28fa1 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -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
diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs
index faa47274f96ce..343895984ca42 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -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)
 }
 
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 29f4d5b807699..e625514e9ffa1 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -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;
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 8a8bec35d8194..e6b305d2ffd37 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1002,7 +1002,6 @@ symbols! {
         forbid,
         forget,
         format,
-        format_alignment,
         format_args,
         format_args_capture,
         format_args_macro,
diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs
index 4a43c12be9aaa..870ad9df4fd33 100644
--- a/library/core/src/fmt/float.rs
+++ b/library/core/src/fmt/float.rs
@@ -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 {
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index e0dc632df7051..e464247000875 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -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
 }
diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs
index 080fc6ddfc9b8..d27f7e6e0d8e6 100644
--- a/library/core/src/fmt/rt.rs
+++ b/library/core/src/fmt/rt.rs
@@ -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 {

From ba2809e08574c608b6b2cd146649af83d7c354ed Mon Sep 17 00:00:00 2001
From: Mara Bos <m-ou.se@m-ou.se>
Date: Wed, 12 Mar 2025 16:32:11 +0100
Subject: [PATCH 2/4] Update tests.

---
 ...onential_common.GVN.32bit.panic-abort.diff | 49 +++++++++----------
 ...nential_common.GVN.32bit.panic-unwind.diff | 49 +++++++++----------
 ...onential_common.GVN.64bit.panic-abort.diff | 49 +++++++++----------
 ...nential_common.GVN.64bit.panic-unwind.diff | 49 +++++++++----------
 4 files changed, 88 insertions(+), 108 deletions(-)

diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff
index 45fc7365d8d6c..6baa902b6f4bd 100644
--- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff
@@ -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) {
diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff
index 578d2c2194b0e..36540e038654f 100644
--- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff
+++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff
@@ -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) {
diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff
index 5f0f7d6cc74fb..41c350f3eaeb5 100644
--- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff
@@ -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) {
diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff
index 10cc46a8b8285..b839bf81eaf45 100644
--- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff
+++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff
@@ -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) {

From 9b7060ad3123eb21e7d93b59e2a21482afe36b6c Mon Sep 17 00:00:00 2001
From: Mara Bos <m-ou.se@m-ou.se>
Date: Fri, 21 Mar 2025 16:51:14 +0100
Subject: [PATCH 3/4] Add todo comment on using a niche type for fmt flags.

---
 library/core/src/fmt/mod.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index e464247000875..54c84b07d06f6 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -299,6 +299,8 @@ pub struct FormattingOptions {
     ///      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,
     /// Width if width flag (bit 27) above is set. Otherwise, always 0.
     width: u16,

From d7685f0d0b530f39c318713f90bc4c2ba8832f47 Mon Sep 17 00:00:00 2001
From: Mara Bos <m-ou.se@m-ou.se>
Date: Fri, 21 Mar 2025 17:04:29 +0100
Subject: [PATCH 4/4] Add test for Formatter flags.

---
 library/coretests/tests/fmt/mod.rs | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/library/coretests/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs
index 025c69c4f6236..f08545d8bdcfe 100644
--- a/library/coretests/tests/fmt/mod.rs
+++ b/library/coretests/tests/fmt/mod.rs
@@ -58,6 +58,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)] {
@@ -75,6 +76,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);
                 }
             }
         }