diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index ccf481cb9b3..c081162ea14 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -186,7 +186,7 @@ enum ArgumentType { /// Generates: /// /// ```text -/// ::new_…(arg) +/// ::new_…(arg) /// ``` fn make_argument<'hir>( ctx: &mut LoweringContext<'_, 'hir>, @@ -327,7 +327,7 @@ fn make_format_spec<'hir>( None => sym::Unknown, }, ); - // This needs to match `FlagV1` in library/core/src/fmt/mod.rs. + // 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 @@ -438,7 +438,7 @@ fn expand_format_args<'hir>( // If the args array contains exactly all the original arguments once, // in order, we can use a simple array instead of a `match` construction. // However, if there's a yield point in any argument except the first one, - // we don't do this, because an ArgumentV1 cannot be kept across yield points. + // we don't do this, because an Argument cannot be kept across yield points. // // This is an optimization, speeding up compilation about 1-2% in some cases. // See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609 @@ -449,9 +449,9 @@ fn expand_format_args<'hir>( let args = if use_simple_array { // Generate: // &[ - // ::new_display(&arg0), - // ::new_lower_hex(&arg1), - // ::new_debug(&arg2), + // ::new_display(&arg0), + // ::new_lower_hex(&arg1), + // ::new_debug(&arg2), // … // ] let elements: Vec<_> = arguments @@ -477,9 +477,9 @@ fn expand_format_args<'hir>( // Generate: // &match (&arg0, &arg1, &…) { // args => [ - // ::new_display(args.0), - // ::new_lower_hex(args.1), - // ::new_debug(args.0), + // ::new_display(args.0), + // ::new_lower_hex(args.1), + // ::new_debug(args.0), // … // ] // } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 6c11edb742c..4fb66854571 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -296,7 +296,7 @@ macro_rules! error { diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span))); err } - _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentV1Methods) => ccx + _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => ccx .tcx .sess .create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind() }), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index abf19c30e3d..c2619956219 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -129,8 +129,7 @@ Any, Arc, Argument, - ArgumentV1, - ArgumentV1Methods, + ArgumentMethods, Arguments, AsMut, AsRef, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index a901ae72669..f4f32232570 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -251,223 +251,6 @@ pub fn new(buf: &'a mut (dyn Write + 'a)) -> Formatter<'a> { } } -// NB. Argument is essentially an optimized partially applied formatting function, -// equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`. - -extern "C" { - type Opaque; -} - -/// This struct represents the generic "argument" which is taken by the Xprintf -/// family of functions. It contains a function to format the given value. At -/// compile time it is ensured that the function and the value have the correct -/// types, and then this struct is used to canonicalize arguments to one type. -#[lang = "format_argument"] -#[derive(Copy, Clone)] -#[allow(missing_debug_implementations)] -#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] -#[doc(hidden)] -pub struct ArgumentV1<'a> { - value: &'a Opaque, - formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, -} - -/// This struct represents the unsafety of constructing an `Arguments`. -/// It exists, rather than an unsafe function, in order to simplify the expansion -/// of `format_args!(..)` and reduce the scope of the `unsafe` block. -#[lang = "format_unsafe_arg"] -#[allow(missing_debug_implementations)] -#[doc(hidden)] -#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] -pub struct UnsafeArg { - _private: (), -} - -impl UnsafeArg { - /// See documentation where `UnsafeArg` is required to know when it is safe to - /// create and use `UnsafeArg`. - #[doc(hidden)] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - #[inline(always)] - pub unsafe fn new() -> Self { - Self { _private: () } - } -} - -// This guarantees a single stable value for the function pointer associated with -// indices/counts in the formatting infrastructure. -// -// Note that a function defined as such would not be correct as functions are -// always tagged unnamed_addr with the current lowering to LLVM IR, so their -// address is not considered important to LLVM and as such the as_usize cast -// could have been miscompiled. In practice, we never call as_usize on non-usize -// containing data (as a matter of static generation of the formatting -// arguments), so this is merely an additional check. -// -// We primarily want to ensure that the function pointer at `USIZE_MARKER` has -// an address corresponding *only* to functions that also take `&usize` as their -// first argument. The read_volatile here ensures that we can safely ready out a -// usize from the passed reference and that this address does not point at a -// non-usize taking function. -#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] -static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| { - // SAFETY: ptr is a reference - let _v: usize = unsafe { crate::ptr::read_volatile(ptr) }; - loop {} -}; - -macro_rules! arg_new { - ($f: ident, $t: ident) => { - #[doc(hidden)] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - #[inline] - pub fn $f<'b, T: $t>(x: &'b T) -> ArgumentV1<'_> { - Self::new(x, $t::fmt) - } - }; -} - -#[rustc_diagnostic_item = "ArgumentV1Methods"] -impl<'a> ArgumentV1<'a> { - #[doc(hidden)] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - #[inline] - pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> { - // SAFETY: `mem::transmute(x)` is safe because - // 1. `&'b T` keeps the lifetime it originated with `'b` - // (so as to not have an unbounded lifetime) - // 2. `&'b T` and `&'b Opaque` have the same memory layout - // (when `T` is `Sized`, as it is here) - // `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result` - // and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI - // (as long as `T` is `Sized`) - unsafe { ArgumentV1 { formatter: mem::transmute(f), value: mem::transmute(x) } } - } - - arg_new!(new_display, Display); - arg_new!(new_debug, Debug); - arg_new!(new_octal, Octal); - arg_new!(new_lower_hex, LowerHex); - arg_new!(new_upper_hex, UpperHex); - arg_new!(new_pointer, Pointer); - arg_new!(new_binary, Binary); - arg_new!(new_lower_exp, LowerExp); - arg_new!(new_upper_exp, UpperExp); - - #[doc(hidden)] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn from_usize(x: &usize) -> ArgumentV1<'_> { - ArgumentV1::new(x, USIZE_MARKER) - } - - fn as_usize(&self) -> Option { - // We are type punning a bit here: USIZE_MARKER only takes an &usize but - // formatter takes an &Opaque. Rust understandably doesn't think we should compare - // the function pointers if they don't have the same signature, so we cast to - // usizes to tell it that we just want to compare addresses. - if self.formatter as usize == USIZE_MARKER as usize { - // SAFETY: The `formatter` field is only set to USIZE_MARKER if - // the value is a usize, so this is safe - Some(unsafe { *(self.value as *const _ as *const usize) }) - } else { - None - } - } -} - -// flags available in the v1 format of format_args -#[derive(Copy, Clone)] -enum FlagV1 { - SignPlus, - SignMinus, - Alternate, - SignAwareZeroPad, - DebugLowerHex, - DebugUpperHex, -} - -impl<'a> Arguments<'a> { - #[doc(hidden)] - #[inline] - #[unstable(feature = "fmt_internals", issue = "none")] - #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] - pub const fn new_const(pieces: &'a [&'static str]) -> Self { - if pieces.len() > 1 { - panic!("invalid args"); - } - Arguments { pieces, fmt: None, args: &[] } - } - - /// When using the format_args!() macro, this function is used to generate the - /// Arguments structure. - #[cfg(not(bootstrap))] - #[doc(hidden)] - #[inline] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> { - if pieces.len() < args.len() || pieces.len() > args.len() + 1 { - panic!("invalid args"); - } - Arguments { pieces, fmt: None, args } - } - - #[cfg(bootstrap)] - #[doc(hidden)] - #[inline] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] - pub const fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> { - if pieces.len() < args.len() || pieces.len() > args.len() + 1 { - panic!("invalid args"); - } - Arguments { pieces, fmt: None, args } - } - - /// This function is used to specify nonstandard formatting parameters. - /// - /// An `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`. - #[doc(hidden)] - #[inline] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn new_v1_formatted( - pieces: &'a [&'static str], - args: &'a [ArgumentV1<'a>], - fmt: &'a [rt::Placeholder], - _unsafe_arg: 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 - /// when using `format!`. Note: this is neither the lower nor upper bound. - #[doc(hidden)] - #[inline] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn estimated_capacity(&self) -> usize { - let pieces_length: usize = self.pieces.iter().map(|x| x.len()).sum(); - - if self.args.is_empty() { - pieces_length - } else if !self.pieces.is_empty() && self.pieces[0].is_empty() && pieces_length < 16 { - // If the format string starts with an argument, - // don't preallocate anything, unless length - // of pieces is significant. - 0 - } else { - // There are some arguments, so any additional push - // will reallocate the string. To avoid that, - // we're "pre-doubling" the capacity here. - pieces_length.checked_mul(2).unwrap_or(0) - } - } -} - /// This structure represents a safely precompiled version of a format string /// and its arguments. This cannot be generated at runtime because it cannot /// safely be done, so no constructors are given and the fields are private @@ -502,7 +285,82 @@ pub struct Arguments<'a> { // Dynamic arguments for interpolation, to be interleaved with string // pieces. (Every argument is preceded by a string piece.) - args: &'a [ArgumentV1<'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] + #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] + pub const fn new_const(pieces: &'a [&'static str]) -> Self { + if pieces.len() > 1 { + panic!("invalid args"); + } + Arguments { pieces, fmt: None, args: &[] } + } + + /// When using the format_args!() macro, this function is used to generate the + /// Arguments structure. + #[cfg(not(bootstrap))] + #[inline] + pub fn new_v1(pieces: &'a [&'static str], args: &'a [rt::Argument<'a>]) -> Arguments<'a> { + if pieces.len() < args.len() || pieces.len() > args.len() + 1 { + panic!("invalid args"); + } + Arguments { pieces, fmt: None, args } + } + + #[cfg(bootstrap)] + #[inline] + #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] + pub const fn new_v1(pieces: &'a [&'static str], args: &'a [rt::Argument<'a>]) -> Arguments<'a> { + if pieces.len() < args.len() || pieces.len() > args.len() + 1 { + panic!("invalid args"); + } + Arguments { pieces, fmt: None, args } + } + + /// This function is used to specify 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 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 + /// when using `format!`. Note: this is neither the lower nor upper bound. + #[inline] + pub fn estimated_capacity(&self) -> usize { + let pieces_length: usize = self.pieces.iter().map(|x| x.len()).sum(); + + if self.args.is_empty() { + pieces_length + } else if !self.pieces.is_empty() && self.pieces[0].is_empty() && pieces_length < 16 { + // If the format string starts with an argument, + // don't preallocate anything, unless length + // of pieces is significant. + 0 + } else { + // There are some arguments, so any additional push + // will reallocate the string. To avoid that, + // we're "pre-doubling" the capacity here. + pieces_length.checked_mul(2).unwrap_or(0) + } + } } impl<'a> Arguments<'a> { @@ -1244,7 +1102,7 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { if !piece.is_empty() { formatter.buf.write_str(*piece)?; } - (arg.formatter)(arg.value, &mut formatter)?; + arg.fmt(&mut formatter)?; idx += 1; } } @@ -1274,7 +1132,7 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { Ok(()) } -unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[ArgumentV1<'_>]) -> Result { +unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argument<'_>]) -> Result { fmt.fill = arg.fill; fmt.align = arg.align; fmt.flags = arg.flags; @@ -1292,10 +1150,10 @@ unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[ArgumentV1 let value = unsafe { args.get_unchecked(arg.position) }; // Then actually do some printing - (value.formatter)(value.value, fmt) + value.fmt(fmt) } -unsafe fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::Count) -> Option { +unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option { match *cnt { rt::Count::Is(n) => Some(n), rt::Count::Implied => None, @@ -1878,7 +1736,7 @@ pub fn precision(&self) -> Option { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_plus(&self) -> bool { - self.flags & (1 << FlagV1::SignPlus as u32) != 0 + self.flags & (1 << rt::Flag::SignPlus as u32) != 0 } /// Determines if the `-` flag was specified. @@ -1907,7 +1765,7 @@ pub fn sign_plus(&self) -> bool { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_minus(&self) -> bool { - self.flags & (1 << FlagV1::SignMinus as u32) != 0 + self.flags & (1 << rt::Flag::SignMinus as u32) != 0 } /// Determines if the `#` flag was specified. @@ -1935,7 +1793,7 @@ pub fn sign_minus(&self) -> bool { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn alternate(&self) -> bool { - self.flags & (1 << FlagV1::Alternate as u32) != 0 + self.flags & (1 << rt::Flag::Alternate as u32) != 0 } /// Determines if the `0` flag was specified. @@ -1961,17 +1819,17 @@ pub fn alternate(&self) -> bool { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_aware_zero_pad(&self) -> bool { - self.flags & (1 << FlagV1::SignAwareZeroPad as u32) != 0 + self.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 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.flags & (1 << FlagV1::DebugLowerHex as u32) != 0 + self.flags & (1 << rt::Flag::DebugLowerHex as u32) != 0 } fn debug_upper_hex(&self) -> bool { - self.flags & (1 << FlagV1::DebugUpperHex as u32) != 0 + self.flags & (1 << rt::Flag::DebugUpperHex as u32) != 0 } /// Creates a [`DebugStruct`] builder designed to assist with creation of @@ -2531,13 +2389,13 @@ pub(crate) fn pointer_fmt_inner(ptr_addr: usize, f: &mut Formatter<'_>) -> Resul // or not to zero extend, and then unconditionally set it to get the // prefix. if f.alternate() { - f.flags |= 1 << (FlagV1::SignAwareZeroPad as u32); + f.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32); if f.width.is_none() { f.width = Some((usize::BITS / 4) as usize + 2); } } - f.flags |= 1 << (FlagV1::Alternate as u32); + f.flags |= 1 << (rt::Flag::Alternate as u32); let ret = LowerHex::fmt(&ptr_addr, f); diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index 2c1a767691b..0596f6c30ce 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -3,6 +3,8 @@ //! These are the lang items used by format_args!(). +use super::*; + #[lang = "format_placeholder"] #[derive(Copy, Clone)] pub struct Placeholder { @@ -28,21 +30,17 @@ pub const fn new( } } -/// Possible alignments that can be requested as part of a formatting directive. #[lang = "format_alignment"] #[derive(Copy, Clone, PartialEq, Eq)] pub enum Alignment { - /// Indication that contents should be left-aligned. Left, - /// Indication that contents should be right-aligned. Right, - /// Indication that contents should be center-aligned. Center, - /// No alignment was requested. Unknown, } -/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers. +/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) +/// and [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers. #[lang = "format_count"] #[derive(Copy, Clone)] pub enum Count { @@ -53,3 +51,147 @@ pub enum Count { /// Not specified 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, +} + +/// This struct represents the generic "argument" which is taken by format_args!(). +/// It contains a function to format the given value. At compile time it is ensured that the +/// function and the value have the correct types, and then this struct is used to canonicalize +/// arguments to one type. +/// +/// Argument is essentially an optimized partially applied formatting function, +/// equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`. +#[lang = "format_argument"] +#[derive(Copy, Clone)] +pub struct Argument<'a> { + value: &'a Opaque, + formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, +} + +#[rustc_diagnostic_item = "ArgumentMethods"] +impl<'a> Argument<'a> { + #[inline(always)] + fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> { + // SAFETY: `mem::transmute(x)` is safe because + // 1. `&'b T` keeps the lifetime it originated with `'b` + // (so as to not have an unbounded lifetime) + // 2. `&'b T` and `&'b Opaque` have the same memory layout + // (when `T` is `Sized`, as it is here) + // `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result` + // and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI + // (as long as `T` is `Sized`) + unsafe { Argument { formatter: mem::transmute(f), value: mem::transmute(x) } } + } + + #[inline(always)] + pub fn new_display<'b, T: Display>(x: &'b T) -> Argument<'_> { + Self::new(x, Display::fmt) + } + #[inline(always)] + pub fn new_debug<'b, T: Debug>(x: &'b T) -> Argument<'_> { + Self::new(x, Debug::fmt) + } + #[inline(always)] + pub fn new_octal<'b, T: Octal>(x: &'b T) -> Argument<'_> { + Self::new(x, Octal::fmt) + } + #[inline(always)] + pub fn new_lower_hex<'b, T: LowerHex>(x: &'b T) -> Argument<'_> { + Self::new(x, LowerHex::fmt) + } + #[inline(always)] + pub fn new_upper_hex<'b, T: UpperHex>(x: &'b T) -> Argument<'_> { + Self::new(x, UpperHex::fmt) + } + #[inline(always)] + pub fn new_pointer<'b, T: Pointer>(x: &'b T) -> Argument<'_> { + Self::new(x, Pointer::fmt) + } + #[inline(always)] + pub fn new_binary<'b, T: Binary>(x: &'b T) -> Argument<'_> { + Self::new(x, Binary::fmt) + } + #[inline(always)] + pub fn new_lower_exp<'b, T: LowerExp>(x: &'b T) -> Argument<'_> { + Self::new(x, LowerExp::fmt) + } + #[inline(always)] + pub fn new_upper_exp<'b, T: UpperExp>(x: &'b T) -> Argument<'_> { + Self::new(x, UpperExp::fmt) + } + #[inline(always)] + pub fn from_usize(x: &usize) -> Argument<'_> { + Self::new(x, USIZE_MARKER) + } + + #[inline(always)] + pub(super) fn fmt(&self, f: &mut Formatter<'_>) -> Result { + (self.formatter)(self.value, f) + } + + #[inline(always)] + pub(super) fn as_usize(&self) -> Option { + // We are type punning a bit here: USIZE_MARKER only takes an &usize but + // formatter takes an &Opaque. Rust understandably doesn't think we should compare + // the function pointers if they don't have the same signature, so we cast to + // usizes to tell it that we just want to compare addresses. + if self.formatter as usize == USIZE_MARKER as usize { + // SAFETY: The `formatter` field is only set to USIZE_MARKER if + // the value is a usize, so this is safe + Some(unsafe { *(self.value as *const _ as *const usize) }) + } else { + None + } + } +} + +/// This struct represents the unsafety of constructing an `Arguments`. +/// It exists, rather than an unsafe function, in order to simplify the expansion +/// of `format_args!(..)` and reduce the scope of the `unsafe` block. +#[lang = "format_unsafe_arg"] +pub struct UnsafeArg { + _private: (), +} + +impl UnsafeArg { + /// See documentation where `UnsafeArg` is required to know when it is safe to + /// create and use `UnsafeArg`. + #[inline(always)] + pub unsafe fn new() -> Self { + Self { _private: () } + } +} + +extern "C" { + type Opaque; +} + +// This guarantees a single stable value for the function pointer associated with +// indices/counts in the formatting infrastructure. +// +// Note that a function defined as such would not be correct as functions are +// always tagged unnamed_addr with the current lowering to LLVM IR, so their +// address is not considered important to LLVM and as such the as_usize cast +// could have been miscompiled. In practice, we never call as_usize on non-usize +// containing data (as a matter of static generation of the formatting +// arguments), so this is merely an additional check. +// +// We primarily want to ensure that the function pointer at `USIZE_MARKER` has +// an address corresponding *only* to functions that also take `&usize` as their +// first argument. The read_volatile here ensures that we can safely ready out a +// usize from the passed reference and that this address does not point at a +// non-usize taking function. +static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| { + // SAFETY: ptr is a reference + let _v: usize = unsafe { crate::ptr::read_volatile(ptr) }; + loop {} +}; diff --git a/src/tools/miri/tests/fail/panic/double_panic.stderr b/src/tools/miri/tests/fail/panic/double_panic.stderr index 6bf13f21601..f04dfab36ca 100644 --- a/src/tools/miri/tests/fail/panic/double_panic.stderr +++ b/src/tools/miri/tests/fail/panic/double_panic.stderr @@ -12,57 +12,59 @@ stack backtrace: at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC 4: ::fmt at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC - 5: std::fmt::write + 5: core::fmt::rt::Argument::fmt + at RUSTLIB/core/src/fmt/rt.rs:LL:CC + 6: std::fmt::write at RUSTLIB/core/src/fmt/mod.rs:LL:CC - 6: ::write_fmt + 7: ::write_fmt at RUSTLIB/std/src/io/mod.rs:LL:CC - 7: std::sys_common::backtrace::_print + 8: std::sys_common::backtrace::_print at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC - 8: std::sys_common::backtrace::print + 9: std::sys_common::backtrace::print at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC - 9: std::panicking::default_hook::{closure#1} + 10: std::panicking::default_hook::{closure#1} at RUSTLIB/std/src/panicking.rs:LL:CC - 10: std::panicking::default_hook + 11: std::panicking::default_hook at RUSTLIB/std/src/panicking.rs:LL:CC - 11: std::panicking::rust_panic_with_hook + 12: std::panicking::rust_panic_with_hook at RUSTLIB/std/src/panicking.rs:LL:CC - 12: std::rt::begin_panic::{closure#0} + 13: std::rt::begin_panic::{closure#0} at RUSTLIB/std/src/panicking.rs:LL:CC - 13: std::sys_common::backtrace::__rust_end_short_backtrace + 14: std::sys_common::backtrace::__rust_end_short_backtrace at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC - 14: std::rt::begin_panic + 15: std::rt::begin_panic at RUSTLIB/std/src/panicking.rs:LL:CC - 15: ::drop + 16: ::drop at $DIR/double_panic.rs:LL:CC - 16: std::ptr::drop_in_place - shim(Some(Foo)) + 17: std::ptr::drop_in_place - shim(Some(Foo)) at RUSTLIB/core/src/ptr/mod.rs:LL:CC - 17: main + 18: main at $DIR/double_panic.rs:LL:CC - 18: >::call_once - shim(fn()) + 19: >::call_once - shim(fn()) at RUSTLIB/core/src/ops/function.rs:LL:CC - 19: std::sys_common::backtrace::__rust_begin_short_backtrace + 20: std::sys_common::backtrace::__rust_begin_short_backtrace at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC - 20: std::rt::lang_start::{closure#0} + 21: std::rt::lang_start::{closure#0} at RUSTLIB/std/src/rt.rs:LL:CC - 21: std::ops::function::impls::call_once + 22: std::ops::function::impls::call_once at RUSTLIB/core/src/ops/function.rs:LL:CC - 22: std::panicking::r#try::do_call + 23: std::panicking::r#try::do_call at RUSTLIB/std/src/panicking.rs:LL:CC - 23: std::panicking::r#try + 24: std::panicking::r#try at RUSTLIB/std/src/panicking.rs:LL:CC - 24: std::panic::catch_unwind + 25: std::panic::catch_unwind at RUSTLIB/std/src/panic.rs:LL:CC - 25: std::rt::lang_start_internal::{closure#2} + 26: std::rt::lang_start_internal::{closure#2} at RUSTLIB/std/src/rt.rs:LL:CC - 26: std::panicking::r#try::do_call + 27: std::panicking::r#try::do_call at RUSTLIB/std/src/panicking.rs:LL:CC - 27: std::panicking::r#try + 28: std::panicking::r#try at RUSTLIB/std/src/panicking.rs:LL:CC - 28: std::panic::catch_unwind + 29: std::panic::catch_unwind at RUSTLIB/std/src/panic.rs:LL:CC - 29: std::rt::lang_start_internal + 30: std::rt::lang_start_internal at RUSTLIB/std/src/rt.rs:LL:CC - 30: std::rt::lang_start + 31: std::rt::lang_start at RUSTLIB/std/src/rt.rs:LL:CC thread panicked while panicking. aborting. error: abnormal termination: the program aborted execution diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 0b72ca1eec1..5fbd1789b3a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -201,7 +201,7 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(arg2), $crate::fmt::Display::fmt), ]); + $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(arg2), $crate::fmt::Display::fmt), ]); } "#]], ); @@ -229,7 +229,7 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(a::()), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(b), $crate::fmt::Display::fmt), ]); + $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a::()), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(b), $crate::fmt::Display::fmt), ]); } "#]], ); @@ -260,7 +260,7 @@ macro_rules! format_args { fn main() { let _ = /* parse error: expected field name or number */ -$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(a.), $crate::fmt::Display::fmt), ]); +$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a.), $crate::fmt::Display::fmt), ]); } "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index 44510f2b7ff..a9c5e1488aa 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -241,8 +241,8 @@ fn format_args_expand( // We expand `format_args!("", a1, a2)` to // ``` // $crate::fmt::Arguments::new_v1(&[], &[ - // $crate::fmt::ArgumentV1::new(&arg1,$crate::fmt::Display::fmt), - // $crate::fmt::ArgumentV1::new(&arg2,$crate::fmt::Display::fmt), + // $crate::fmt::Argument::new(&arg1,$crate::fmt::Display::fmt), + // $crate::fmt::Argument::new(&arg2,$crate::fmt::Display::fmt), // ]) // ```, // which is still not really correct, but close enough for now @@ -267,7 +267,7 @@ fn format_args_expand( } let _format_string = args.remove(0); let arg_tts = args.into_iter().flat_map(|arg| { - quote! { #DOLLAR_CRATE::fmt::ArgumentV1::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), } + quote! { #DOLLAR_CRATE::fmt::Argument::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), } }.token_trees); let expanded = quote! { #DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts]) diff --git a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff index 579587a430b..9bda5f575c9 100644 --- a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff +++ b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff @@ -15,14 +15,14 @@ let mut _13: &[&str; 3]; // in scope 0 at $DIR/lifetimes.rs:+10:19: +10:28 let _14: &[&str; 3]; // in scope 0 at $DIR/lifetimes.rs:+10:19: +10:28 let _15: [&str; 3]; // in scope 0 at $DIR/lifetimes.rs:+10:19: +10:28 - let mut _16: &[core::fmt::ArgumentV1<'_>]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL - let mut _17: &[core::fmt::ArgumentV1<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL - let _18: &[core::fmt::ArgumentV1<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL - let _19: [core::fmt::ArgumentV1<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL - let mut _20: core::fmt::ArgumentV1<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:20: +10:23 + let mut _16: &[core::fmt::rt::Argument<'_>]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _17: &[core::fmt::rt::Argument<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _18: &[core::fmt::rt::Argument<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _19: [core::fmt::rt::Argument<'_>; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _20: core::fmt::rt::Argument<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:20: +10:23 let mut _21: &std::boxed::Box; // in scope 0 at $DIR/lifetimes.rs:+10:20: +10:23 let _22: &std::boxed::Box; // in scope 0 at $DIR/lifetimes.rs:+10:20: +10:23 - let mut _23: core::fmt::ArgumentV1<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:24: +10:27 + let mut _23: core::fmt::rt::Argument<'_>; // in scope 0 at $DIR/lifetimes.rs:+10:24: +10:27 let mut _24: &u32; // in scope 0 at $DIR/lifetimes.rs:+10:24: +10:27 let _25: &u32; // in scope 0 at $DIR/lifetimes.rs:+10:24: +10:27 let mut _27: bool; // in scope 0 at $DIR/lifetimes.rs:+12:1: +12:2 @@ -113,11 +113,11 @@ StorageLive(_22); // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23 _22 = &_8; // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23 _21 = &(*_22); // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23 - _20 = core::fmt::ArgumentV1::<'_>::new_display::>(move _21) -> [return: bb3, unwind unreachable]; // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23 + _20 = core::fmt::rt::Argument::<'_>::new_display::>(move _21) -> [return: bb3, unwind unreachable]; // scope 4 at $DIR/lifetimes.rs:+10:20: +10:23 // mir::Constant // + span: $DIR/lifetimes.rs:27:20: 27:23 // + user_ty: UserType(4) - // + literal: Const { ty: for<'b> fn(&'b Box) -> core::fmt::ArgumentV1<'b> {core::fmt::ArgumentV1::<'_>::new_display::>}, val: Value() } + // + literal: Const { ty: for<'b> fn(&'b Box) -> core::fmt::rt::Argument<'b> {core::fmt::rt::Argument::<'_>::new_display::>}, val: Value() } } bb3: { @@ -127,11 +127,11 @@ StorageLive(_25); // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27 _25 = &_6; // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27 _24 = &(*_25); // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27 - _23 = core::fmt::ArgumentV1::<'_>::new_display::(move _24) -> [return: bb4, unwind unreachable]; // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27 + _23 = core::fmt::rt::Argument::<'_>::new_display::(move _24) -> [return: bb4, unwind unreachable]; // scope 4 at $DIR/lifetimes.rs:+10:24: +10:27 // mir::Constant // + span: $DIR/lifetimes.rs:27:24: 27:27 // + user_ty: UserType(5) - // + literal: Const { ty: for<'b> fn(&'b u32) -> core::fmt::ArgumentV1<'b> {core::fmt::ArgumentV1::<'_>::new_display::}, val: Value() } + // + literal: Const { ty: for<'b> fn(&'b u32) -> core::fmt::rt::Argument<'b> {core::fmt::rt::Argument::<'_>::new_display::}, val: Value() } } bb4: { @@ -141,13 +141,13 @@ StorageDead(_20); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL _18 = &_19; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL _17 = &(*_18); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL - _16 = move _17 as &[core::fmt::ArgumentV1<'_>] (Pointer(Unsize)); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _16 = move _17 as &[core::fmt::rt::Argument<'_>] (Pointer(Unsize)); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL StorageDead(_17); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL _11 = Arguments::<'_>::new_v1(move _12, move _16) -> [return: bb5, unwind unreachable]; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL // mir::Constant // + span: $SRC_DIR/std/src/macros.rs:LL:COL // + user_ty: UserType(3) - // + literal: Const { ty: fn(&[&'static str], &[core::fmt::ArgumentV1<'_>]) -> Arguments<'_> {Arguments::<'_>::new_v1}, val: Value() } + // + literal: Const { ty: fn(&[&'static str], &[core::fmt::rt::Argument<'_>]) -> Arguments<'_> {Arguments::<'_>::new_v1}, val: Value() } } bb5: { diff --git a/tests/ui/fmt/ifmt-bad-arg.stderr b/tests/ui/fmt/ifmt-bad-arg.stderr index bf18fb315c9..ed008c454a3 100644 --- a/tests/ui/fmt/ifmt-bad-arg.stderr +++ b/tests/ui/fmt/ifmt-bad-arg.stderr @@ -307,7 +307,7 @@ LL | println!("{} {:.*} {}", 1, 3.2, 4); = note: expected reference `&usize` found reference `&{float}` note: associated function defined here - --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL + --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL = 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) error[E0308]: mismatched types @@ -321,7 +321,7 @@ LL | println!("{} {:07$.*} {}", 1, 3.2, 4); = note: expected reference `&usize` found reference `&{float}` note: associated function defined here - --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL + --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL = 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) error: aborting due to 38 previous errors diff --git a/tests/ui/fmt/ifmt-unimpl.stderr b/tests/ui/fmt/ifmt-unimpl.stderr index dc2dee3f341..cc316e55f5c 100644 --- a/tests/ui/fmt/ifmt-unimpl.stderr +++ b/tests/ui/fmt/ifmt-unimpl.stderr @@ -17,9 +17,9 @@ LL | format!("{:X}", "3"); NonZeroIsize and 21 others = note: required for `&str` to implement `UpperHex` -note: required by a bound in `core::fmt::ArgumentV1::<'a>::new_upper_hex` - --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL - = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `arg_new` (in Nightly builds, run with -Z macro-backtrace for more info) +note: required by a bound in `core::fmt::rt::Argument::<'a>::new_upper_hex` + --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL + = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/tests/ui/fmt/send-sync.stderr b/tests/ui/fmt/send-sync.stderr index d43f4f0d957..b517a342e63 100644 --- a/tests/ui/fmt/send-sync.stderr +++ b/tests/ui/fmt/send-sync.stderr @@ -1,16 +1,16 @@ -error[E0277]: `core::fmt::Opaque` cannot be shared between threads safely +error[E0277]: `core::fmt::rt::Opaque` cannot be shared between threads safely --> $DIR/send-sync.rs:8:10 | LL | send(format_args!("{:?}", c)); - | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `core::fmt::Opaque` cannot be shared between threads safely + | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `core::fmt::rt::Opaque` cannot be shared between threads safely | | | required by a bound introduced by this call | - = help: within `[core::fmt::ArgumentV1<'_>]`, the trait `Sync` is not implemented for `core::fmt::Opaque` - = note: required because it appears within the type `&core::fmt::Opaque` - = note: required because it appears within the type `ArgumentV1<'_>` - = note: required because it appears within the type `[ArgumentV1<'_>]` - = note: required for `&[core::fmt::ArgumentV1<'_>]` to implement `Send` + = help: within `[core::fmt::rt::Argument<'_>]`, the trait `Sync` is not implemented for `core::fmt::rt::Opaque` + = note: required because it appears within the type `&core::fmt::rt::Opaque` + = note: required because it appears within the type `Argument<'_>` + = note: required because it appears within the type `[Argument<'_>]` + = note: required for `&[core::fmt::rt::Argument<'_>]` to implement `Send` = note: required because it appears within the type `Arguments<'_>` note: required by a bound in `send` --> $DIR/send-sync.rs:1:12 @@ -18,19 +18,19 @@ note: required by a bound in `send` LL | fn send(_: T) {} | ^^^^ required by this bound in `send` -error[E0277]: `core::fmt::Opaque` cannot be shared between threads safely +error[E0277]: `core::fmt::rt::Opaque` cannot be shared between threads safely --> $DIR/send-sync.rs:9:10 | LL | sync(format_args!("{:?}", c)); - | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `core::fmt::Opaque` cannot be shared between threads safely + | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `core::fmt::rt::Opaque` cannot be shared between threads safely | | | required by a bound introduced by this call | - = help: within `Arguments<'_>`, the trait `Sync` is not implemented for `core::fmt::Opaque` - = note: required because it appears within the type `&core::fmt::Opaque` - = note: required because it appears within the type `ArgumentV1<'_>` - = note: required because it appears within the type `[ArgumentV1<'_>]` - = note: required because it appears within the type `&[ArgumentV1<'_>]` + = help: within `Arguments<'_>`, the trait `Sync` is not implemented for `core::fmt::rt::Opaque` + = note: required because it appears within the type `&core::fmt::rt::Opaque` + = note: required because it appears within the type `Argument<'_>` + = note: required because it appears within the type `[Argument<'_>]` + = note: required because it appears within the type `&[Argument<'_>]` = note: required because it appears within the type `Arguments<'_>` note: required by a bound in `sync` --> $DIR/send-sync.rs:2:12