Rollup merge of #140544 - m-ou-se:format-args-const-cleanup, r=fee1-dead

Clean up "const" situation in format_args!().

This cleans up the "const" situation in the format_args!() expansion/lowering.

Rather than marking the Argument::new_display etc. functions as non-const, this marks the Arguments::new_v1 functions as non-const.

Example expansion/lowering of format_args!() in const:

```rust
// Error: cannot call non-const formatting macro in constant functions
const {
    fmt::Arguments::new_v1( // Now the error is produced here.
        &["Hello, ", "!\n"],
        &[
            fmt::Argument::new_display(&world) // The error used to be produced here.
        ],
    )
}
```
This commit is contained in:
Guillaume Gomez
2025-05-01 22:27:24 +02:00
committed by GitHub
7 changed files with 77 additions and 63 deletions

View File

@@ -622,44 +622,9 @@ pub struct Arguments<'a> {
args: &'a [rt::Argument<'a>],
}
/// Used by the format_args!() macro to create a fmt::Arguments object.
#[doc(hidden)]
#[unstable(feature = "fmt_internals", issue = "none")]
impl<'a> Arguments<'a> {
#[inline]
pub const fn new_const<const N: usize>(pieces: &'a [&'static str; N]) -> Self {
const { assert!(N <= 1) };
Arguments { pieces, fmt: None, args: &[] }
}
/// When using the format_args!() macro, this function is used to generate the
/// Arguments structure.
#[inline]
pub const fn new_v1<const P: usize, const A: usize>(
pieces: &'a [&'static str; P],
args: &'a [rt::Argument<'a>; A],
) -> Arguments<'a> {
const { assert!(P >= A && P <= A + 1, "invalid args") }
Arguments { pieces, fmt: None, args }
}
/// Specifies nonstandard formatting parameters.
///
/// An `rt::UnsafeArg` is required because the following invariants must be held
/// in order for this function to be safe:
/// 1. The `pieces` slice must be at least as long as `fmt`.
/// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`.
/// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`.
#[inline]
pub const fn new_v1_formatted(
pieces: &'a [&'static str],
args: &'a [rt::Argument<'a>],
fmt: &'a [rt::Placeholder],
_unsafe_arg: rt::UnsafeArg,
) -> Arguments<'a> {
Arguments { pieces, fmt: Some(fmt), args }
}
/// Estimates the length of the formatted text.
///
/// This is intended to be used for setting initial `String` capacity

View File

@@ -1,7 +1,10 @@
#![allow(missing_debug_implementations)]
#![unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
//! These are the lang items used by format_args!().
//! All types and methods in this file are used by the compiler in
//! the expansion/lowering of format_args!().
//!
//! Do not modify them without understanding the consequences for the format_args!() macro.
use super::*;
use crate::hint::unreachable_unchecked;
@@ -110,46 +113,45 @@ macro_rules! argument_new {
};
}
#[rustc_diagnostic_item = "ArgumentMethods"]
impl Argument<'_> {
#[inline]
pub fn new_display<T: Display>(x: &T) -> Argument<'_> {
pub const fn new_display<T: Display>(x: &T) -> Argument<'_> {
argument_new!(T, x, <T as Display>::fmt)
}
#[inline]
pub fn new_debug<T: Debug>(x: &T) -> Argument<'_> {
pub const fn new_debug<T: Debug>(x: &T) -> Argument<'_> {
argument_new!(T, x, <T as Debug>::fmt)
}
#[inline]
pub fn new_debug_noop<T: Debug>(x: &T) -> Argument<'_> {
pub const fn new_debug_noop<T: Debug>(x: &T) -> Argument<'_> {
argument_new!(T, x, |_: &T, _| Ok(()))
}
#[inline]
pub fn new_octal<T: Octal>(x: &T) -> Argument<'_> {
pub const fn new_octal<T: Octal>(x: &T) -> Argument<'_> {
argument_new!(T, x, <T as Octal>::fmt)
}
#[inline]
pub fn new_lower_hex<T: LowerHex>(x: &T) -> Argument<'_> {
pub const fn new_lower_hex<T: LowerHex>(x: &T) -> Argument<'_> {
argument_new!(T, x, <T as LowerHex>::fmt)
}
#[inline]
pub fn new_upper_hex<T: UpperHex>(x: &T) -> Argument<'_> {
pub const fn new_upper_hex<T: UpperHex>(x: &T) -> Argument<'_> {
argument_new!(T, x, <T as UpperHex>::fmt)
}
#[inline]
pub fn new_pointer<T: Pointer>(x: &T) -> Argument<'_> {
pub const fn new_pointer<T: Pointer>(x: &T) -> Argument<'_> {
argument_new!(T, x, <T as Pointer>::fmt)
}
#[inline]
pub fn new_binary<T: Binary>(x: &T) -> Argument<'_> {
pub const fn new_binary<T: Binary>(x: &T) -> Argument<'_> {
argument_new!(T, x, <T as Binary>::fmt)
}
#[inline]
pub fn new_lower_exp<T: LowerExp>(x: &T) -> Argument<'_> {
pub const fn new_lower_exp<T: LowerExp>(x: &T) -> Argument<'_> {
argument_new!(T, x, <T as LowerExp>::fmt)
}
#[inline]
pub fn new_upper_exp<T: UpperExp>(x: &T) -> Argument<'_> {
pub const fn new_upper_exp<T: UpperExp>(x: &T) -> Argument<'_> {
argument_new!(T, x, <T as UpperExp>::fmt)
}
#[inline]
@@ -200,15 +202,8 @@ impl Argument<'_> {
/// let f = format_args!("{}", "a");
/// println!("{f}");
/// ```
///
/// This function should _not_ be const, to make sure we don't accept
/// format_args!() and panic!() with arguments in const, even when not evaluated:
///
/// ```compile_fail,E0015
/// const _: () = if false { panic!("a {}", "a") };
/// ```
#[inline]
pub fn none() -> [Self; 0] {
pub const fn none() -> [Self; 0] {
[]
}
}
@@ -229,3 +224,57 @@ impl UnsafeArg {
Self { _private: () }
}
}
/// Used by the format_args!() macro to create a fmt::Arguments object.
#[doc(hidden)]
#[unstable(feature = "fmt_internals", issue = "none")]
#[rustc_diagnostic_item = "FmtArgumentsNew"]
impl<'a> Arguments<'a> {
#[inline]
pub const fn new_const<const N: usize>(pieces: &'a [&'static str; N]) -> Self {
const { assert!(N <= 1) };
Arguments { pieces, fmt: None, args: &[] }
}
/// When using the format_args!() macro, this function is used to generate the
/// Arguments structure.
///
/// This function should _not_ be const, to make sure we don't accept
/// format_args!() and panic!() with arguments in const, even when not evaluated:
///
/// ```compile_fail,E0015
/// const _: () = if false { panic!("a {}", "a") };
/// ```
#[inline]
pub fn new_v1<const P: usize, const A: usize>(
pieces: &'a [&'static str; P],
args: &'a [rt::Argument<'a>; A],
) -> Arguments<'a> {
const { assert!(P >= A && P <= A + 1, "invalid args") }
Arguments { pieces, fmt: None, args }
}
/// Specifies nonstandard formatting parameters.
///
/// An `rt::UnsafeArg` is required because the following invariants must be held
/// in order for this function to be safe:
/// 1. The `pieces` slice must be at least as long as `fmt`.
/// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`.
/// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`.
///
/// This function should _not_ be const, to make sure we don't accept
/// format_args!() and panic!() with arguments in const, even when not evaluated:
///
/// ```compile_fail,E0015
/// const _: () = if false { panic!("a {:1}", "a") };
/// ```
#[inline]
pub fn new_v1_formatted(
pieces: &'a [&'static str],
args: &'a [rt::Argument<'a>],
fmt: &'a [rt::Placeholder],
_unsafe_arg: rt::UnsafeArg,
) -> Arguments<'a> {
Arguments { pieces, fmt: Some(fmt), args }
}
}