Skip to content

Commit 7ab5eb8

Browse files
committed
Auto merge of rust-lang#123819 - joboet:fmt_usize_marker, r=Mark-Simulacrum
Get rid of `USIZE_MARKER` in formatting infrastructure An alternative to rust-lang#123780. The `USIZE_MARKER` function used to differentiate between placeholder and count arguments is never called anyway, so we can just replace the function-pointer-comparison hack with an `enum` and an `unreachable_unchecked`, hopefully without causing a regression. CC `@RalfJung`
2 parents 0bf471f + 0f52cd0 commit 7ab5eb8

File tree

3 files changed

+53
-45
lines changed

3 files changed

+53
-45
lines changed

library/core/src/fmt/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1150,7 +1150,12 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
11501150
if !piece.is_empty() {
11511151
formatter.buf.write_str(*piece)?;
11521152
}
1153-
arg.fmt(&mut formatter)?;
1153+
1154+
// SAFETY: There are no formatting parameters and hence no
1155+
// count arguments.
1156+
unsafe {
1157+
arg.fmt(&mut formatter)?;
1158+
}
11541159
idx += 1;
11551160
}
11561161
}
@@ -1198,7 +1203,8 @@ unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argume
11981203
let value = unsafe { args.get_unchecked(arg.position) };
11991204

12001205
// Then actually do some printing
1201-
value.fmt(fmt)
1206+
// SAFETY: this is a placeholder argument.
1207+
unsafe { value.fmt(fmt) }
12021208
}
12031209

12041210
unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option<usize> {

library/core/src/fmt/rt.rs

+41-43
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! These are the lang items used by format_args!().
55
66
use super::*;
7+
use crate::hint::unreachable_unchecked;
78

89
#[lang = "format_placeholder"]
910
#[derive(Copy, Clone)]
@@ -63,18 +64,26 @@ pub(super) enum Flag {
6364
DebugUpperHex,
6465
}
6566

66-
/// This struct represents the generic "argument" which is taken by format_args!().
67-
/// It contains a function to format the given value. At compile time it is ensured that the
68-
/// function and the value have the correct types, and then this struct is used to canonicalize
69-
/// arguments to one type.
67+
#[derive(Copy, Clone)]
68+
enum ArgumentType<'a> {
69+
Placeholder { value: &'a Opaque, formatter: fn(&Opaque, &mut Formatter<'_>) -> Result },
70+
Count(usize),
71+
}
72+
73+
/// This struct represents a generic "argument" which is taken by format_args!().
7074
///
71-
/// Argument is essentially an optimized partially applied formatting function,
72-
/// equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`.
75+
/// This can be either a placeholder argument or a count argument.
76+
/// * A placeholder argument contains a function to format the given value. At
77+
/// compile time it is ensured that the function and the value have the correct
78+
/// types, and then this struct is used to canonicalize arguments to one type.
79+
/// Placeholder arguments are essentially an optimized partially applied formatting
80+
/// function, equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`.
81+
/// * A count argument contains a count for dynamic formatting parameters like
82+
/// precision and width.
7383
#[lang = "format_argument"]
7484
#[derive(Copy, Clone)]
7585
pub struct Argument<'a> {
76-
value: &'a Opaque,
77-
formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
86+
ty: ArgumentType<'a>,
7887
}
7988

8089
#[rustc_diagnostic_item = "ArgumentMethods"]
@@ -89,7 +98,14 @@ impl<'a> Argument<'a> {
8998
// `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result`
9099
// and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI
91100
// (as long as `T` is `Sized`)
92-
unsafe { Argument { formatter: mem::transmute(f), value: mem::transmute(x) } }
101+
unsafe {
102+
Argument {
103+
ty: ArgumentType::Placeholder {
104+
formatter: mem::transmute(f),
105+
value: mem::transmute(x),
106+
},
107+
}
108+
}
93109
}
94110

95111
#[inline(always)]
@@ -130,30 +146,33 @@ impl<'a> Argument<'a> {
130146
}
131147
#[inline(always)]
132148
pub fn from_usize(x: &usize) -> Argument<'_> {
133-
Self::new(x, USIZE_MARKER)
149+
Argument { ty: ArgumentType::Count(*x) }
134150
}
135151

152+
/// Format this placeholder argument.
153+
///
154+
/// # Safety
155+
///
156+
/// This argument must actually be a placeholer argument.
157+
///
136158
// FIXME: Transmuting formatter in new and indirectly branching to/calling
137159
// it here is an explicit CFI violation.
138160
#[allow(inline_no_sanitize)]
139161
#[no_sanitize(cfi, kcfi)]
140162
#[inline(always)]
141-
pub(super) fn fmt(&self, f: &mut Formatter<'_>) -> Result {
142-
(self.formatter)(self.value, f)
163+
pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result {
164+
match self.ty {
165+
ArgumentType::Placeholder { formatter, value } => formatter(value, f),
166+
// SAFETY: the caller promised this.
167+
ArgumentType::Count(_) => unsafe { unreachable_unchecked() },
168+
}
143169
}
144170

145171
#[inline(always)]
146172
pub(super) fn as_usize(&self) -> Option<usize> {
147-
// We are type punning a bit here: USIZE_MARKER only takes an &usize but
148-
// formatter takes an &Opaque. Rust understandably doesn't think we should compare
149-
// the function pointers if they don't have the same signature, so we cast to
150-
// usizes to tell it that we just want to compare addresses.
151-
if self.formatter as usize == USIZE_MARKER as usize {
152-
// SAFETY: The `formatter` field is only set to USIZE_MARKER if
153-
// the value is a usize, so this is safe
154-
Some(unsafe { *(self.value as *const _ as *const usize) })
155-
} else {
156-
None
173+
match self.ty {
174+
ArgumentType::Count(count) => Some(count),
175+
ArgumentType::Placeholder { .. } => None,
157176
}
158177
}
159178

@@ -193,24 +212,3 @@ impl UnsafeArg {
193212
extern "C" {
194213
type Opaque;
195214
}
196-
197-
// This guarantees a single stable value for the function pointer associated with
198-
// indices/counts in the formatting infrastructure.
199-
//
200-
// Note that a function defined as such would not be correct as functions are
201-
// always tagged unnamed_addr with the current lowering to LLVM IR, so their
202-
// address is not considered important to LLVM and as such the as_usize cast
203-
// could have been miscompiled. In practice, we never call as_usize on non-usize
204-
// containing data (as a matter of static generation of the formatting
205-
// arguments), so this is merely an additional check.
206-
//
207-
// We primarily want to ensure that the function pointer at `USIZE_MARKER` has
208-
// an address corresponding *only* to functions that also take `&usize` as their
209-
// first argument. The read_volatile here ensures that we can safely ready out a
210-
// usize from the passed reference and that this address does not point at a
211-
// non-usize taking function.
212-
static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| {
213-
// SAFETY: ptr is a reference
214-
let _v: usize = unsafe { crate::ptr::read_volatile(ptr) };
215-
loop {}
216-
};

tests/ui/fmt/send-sync.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ LL | send(format_args!("{:?}", c));
88
|
99
= help: within `[core::fmt::rt::Argument<'_>]`, the trait `Sync` is not implemented for `core::fmt::rt::Opaque`, which is required by `Arguments<'_>: Send`
1010
= note: required because it appears within the type `&core::fmt::rt::Opaque`
11+
note: required because it appears within the type `core::fmt::rt::ArgumentType<'_>`
12+
--> $SRC_DIR/core/src/fmt/rt.rs:LL:COL
1113
note: required because it appears within the type `core::fmt::rt::Argument<'_>`
1214
--> $SRC_DIR/core/src/fmt/rt.rs:LL:COL
1315
= note: required because it appears within the type `[core::fmt::rt::Argument<'_>]`
@@ -30,6 +32,8 @@ LL | sync(format_args!("{:?}", c));
3032
|
3133
= help: within `Arguments<'_>`, the trait `Sync` is not implemented for `core::fmt::rt::Opaque`, which is required by `Arguments<'_>: Sync`
3234
= note: required because it appears within the type `&core::fmt::rt::Opaque`
35+
note: required because it appears within the type `core::fmt::rt::ArgumentType<'_>`
36+
--> $SRC_DIR/core/src/fmt/rt.rs:LL:COL
3337
note: required because it appears within the type `core::fmt::rt::Argument<'_>`
3438
--> $SRC_DIR/core/src/fmt/rt.rs:LL:COL
3539
= note: required because it appears within the type `[core::fmt::rt::Argument<'_>]`

0 commit comments

Comments
 (0)