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:
@@ -352,7 +352,7 @@ fn build_error_for_const_call<'tcx>(
|
|||||||
);
|
);
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
_ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => {
|
_ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::FmtArgumentsNew) => {
|
||||||
ccx.dcx().create_err(errors::NonConstFmtMacroCall {
|
ccx.dcx().create_err(errors::NonConstFmtMacroCall {
|
||||||
span,
|
span,
|
||||||
kind: ccx.const_kind(),
|
kind: ccx.const_kind(),
|
||||||
|
|||||||
@@ -174,7 +174,6 @@ symbols! {
|
|||||||
Arc,
|
Arc,
|
||||||
ArcWeak,
|
ArcWeak,
|
||||||
Argument,
|
Argument,
|
||||||
ArgumentMethods,
|
|
||||||
ArrayIntoIter,
|
ArrayIntoIter,
|
||||||
AsMut,
|
AsMut,
|
||||||
AsRef,
|
AsRef,
|
||||||
@@ -249,6 +248,7 @@ symbols! {
|
|||||||
Error,
|
Error,
|
||||||
File,
|
File,
|
||||||
FileType,
|
FileType,
|
||||||
|
FmtArgumentsNew,
|
||||||
Fn,
|
Fn,
|
||||||
FnMut,
|
FnMut,
|
||||||
FnOnce,
|
FnOnce,
|
||||||
|
|||||||
@@ -622,44 +622,9 @@ pub struct Arguments<'a> {
|
|||||||
args: &'a [rt::Argument<'a>],
|
args: &'a [rt::Argument<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used by the format_args!() macro to create a fmt::Arguments object.
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[unstable(feature = "fmt_internals", issue = "none")]
|
#[unstable(feature = "fmt_internals", issue = "none")]
|
||||||
impl<'a> Arguments<'a> {
|
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.
|
/// Estimates the length of the formatted text.
|
||||||
///
|
///
|
||||||
/// This is intended to be used for setting initial `String` capacity
|
/// This is intended to be used for setting initial `String` capacity
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
#![allow(missing_debug_implementations)]
|
#![allow(missing_debug_implementations)]
|
||||||
#![unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
|
#![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 super::*;
|
||||||
use crate::hint::unreachable_unchecked;
|
use crate::hint::unreachable_unchecked;
|
||||||
@@ -110,46 +113,45 @@ macro_rules! argument_new {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustc_diagnostic_item = "ArgumentMethods"]
|
|
||||||
impl Argument<'_> {
|
impl Argument<'_> {
|
||||||
#[inline]
|
#[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)
|
argument_new!(T, x, <T as Display>::fmt)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[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)
|
argument_new!(T, x, <T as Debug>::fmt)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[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(()))
|
argument_new!(T, x, |_: &T, _| Ok(()))
|
||||||
}
|
}
|
||||||
#[inline]
|
#[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)
|
argument_new!(T, x, <T as Octal>::fmt)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[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)
|
argument_new!(T, x, <T as LowerHex>::fmt)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[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)
|
argument_new!(T, x, <T as UpperHex>::fmt)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[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)
|
argument_new!(T, x, <T as Pointer>::fmt)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[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)
|
argument_new!(T, x, <T as Binary>::fmt)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[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)
|
argument_new!(T, x, <T as LowerExp>::fmt)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[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)
|
argument_new!(T, x, <T as UpperExp>::fmt)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -200,15 +202,8 @@ impl Argument<'_> {
|
|||||||
/// let f = format_args!("{}", "a");
|
/// let f = format_args!("{}", "a");
|
||||||
/// println!("{f}");
|
/// 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]
|
#[inline]
|
||||||
pub fn none() -> [Self; 0] {
|
pub const fn none() -> [Self; 0] {
|
||||||
[]
|
[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,3 +224,57 @@ impl UnsafeArg {
|
|||||||
Self { _private: () }
|
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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -121,7 +121,7 @@
|
|||||||
StorageDead(_18);
|
StorageDead(_18);
|
||||||
_16 = &_17;
|
_16 = &_17;
|
||||||
_15 = &(*_16);
|
_15 = &(*_16);
|
||||||
_11 = Arguments::<'_>::new_v1::<3, 2>(move _12, move _15) -> [return: bb5, unwind unreachable];
|
_11 = core::fmt::rt::<impl Arguments<'_>>::new_v1::<3, 2>(move _12, move _15) -> [return: bb5, unwind unreachable];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb5: {
|
bb5: {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ fn bar() ({
|
|||||||
((::alloc::fmt::format as
|
((::alloc::fmt::format as
|
||||||
for<'a> fn(Arguments<'a>) -> String {format})(((format_arguments::new_const
|
for<'a> fn(Arguments<'a>) -> String {format})(((format_arguments::new_const
|
||||||
as
|
as
|
||||||
fn(&[&'static str; 1]) -> Arguments<'_> {Arguments::<'_>::new_const::<1>})((&([("test"
|
fn(&[&'static str; 1]) -> Arguments<'_> {core::fmt::rt::<impl Arguments<'_>>::new_const::<1>})((&([("test"
|
||||||
as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>))
|
as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>))
|
||||||
as String)
|
as String)
|
||||||
} as String)) as String);
|
} as String)) as String);
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
error[E0015]: cannot call non-const formatting macro in constant functions
|
error[E0015]: cannot call non-const formatting macro in constant functions
|
||||||
--> $DIR/format.rs:2:13
|
--> $DIR/format.rs:2:5
|
||||||
|
|
|
|
||||||
LL | panic!("{:?}", 0);
|
LL | panic!("{:?}", 0);
|
||||||
| ^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||||
|
|
||||||
error[E0015]: cannot call non-const formatting macro in constant functions
|
error[E0015]: cannot call non-const formatting macro in constant functions
|
||||||
--> $DIR/format.rs:7:15
|
--> $DIR/format.rs:7:5
|
||||||
|
|
|
|
||||||
LL | println!("{:?}", 0);
|
LL | println!("{:?}", 0);
|
||||||
| ^^^^
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||||
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|||||||
Reference in New Issue
Block a user