Skip to content

Commit f6fdef3

Browse files
committed
core: avoid extern types in formatting infrastructure
1 parent 43a865a commit f6fdef3

File tree

1 file changed

+25
-21
lines changed

1 file changed

+25
-21
lines changed

core/src/fmt/rt.rs

+25-21
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
use super::*;
77
use crate::hint::unreachable_unchecked;
8+
use crate::ptr::NonNull;
89

910
#[lang = "format_placeholder"]
1011
#[derive(Copy, Clone)]
@@ -66,7 +67,13 @@ pub(super) enum Flag {
6667

6768
#[derive(Copy, Clone)]
6869
enum ArgumentType<'a> {
69-
Placeholder { value: &'a Opaque, formatter: fn(&Opaque, &mut Formatter<'_>) -> Result },
70+
Placeholder {
71+
// INVARIANT: if `formatter` had type `fn(&T, _) -> _` then `value`
72+
// was derived from a `&T` with lifetime `'a`.
73+
value: NonNull<()>,
74+
formatter: unsafe fn(NonNull<()>, &mut Formatter<'_>) -> Result,
75+
_lifetime: PhantomData<&'a ()>,
76+
},
7077
Count(usize),
7178
}
7279

@@ -90,21 +97,15 @@ pub struct Argument<'a> {
9097
impl<'a> Argument<'a> {
9198
#[inline(always)]
9299
fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
93-
// SAFETY: `mem::transmute(x)` is safe because
94-
// 1. `&'b T` keeps the lifetime it originated with `'b`
95-
// (so as to not have an unbounded lifetime)
96-
// 2. `&'b T` and `&'b Opaque` have the same memory layout
97-
// (when `T` is `Sized`, as it is here)
98-
// `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result`
99-
// and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI
100-
// (as long as `T` is `Sized`)
101-
unsafe {
102-
Argument {
103-
ty: ArgumentType::Placeholder {
104-
formatter: mem::transmute(f),
105-
value: mem::transmute(x),
106-
},
107-
}
100+
Argument {
101+
// INVARIANT: this creates an `ArgumentType<'b>` from a `&'b T` and
102+
// a `fn(&T, ...)`, so the invariant is maintained.
103+
ty: ArgumentType::Placeholder {
104+
value: NonNull::from(x).cast(),
105+
// SAFETY: function pointers always have the same layout.
106+
formatter: unsafe { mem::transmute(f) },
107+
_lifetime: PhantomData,
108+
},
108109
}
109110
}
110111

@@ -162,7 +163,14 @@ impl<'a> Argument<'a> {
162163
#[inline(always)]
163164
pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result {
164165
match self.ty {
165-
ArgumentType::Placeholder { formatter, value } => formatter(value, f),
166+
// SAFETY:
167+
// Because of the invariant that if `formatter` had the type
168+
// `fn(&T, _) -> _` then `value` has type `&'b T` where `'b` is
169+
// the lifetime of the `ArgumentType`, and because references
170+
// and `NonNull` are ABI-compatible, this is completely equivalent
171+
// to calling the original function passed to `new` with the
172+
// original reference, which is sound.
173+
ArgumentType::Placeholder { formatter, value, .. } => unsafe { formatter(value, f) },
166174
// SAFETY: the caller promised this.
167175
ArgumentType::Count(_) => unsafe { unreachable_unchecked() },
168176
}
@@ -208,7 +216,3 @@ impl UnsafeArg {
208216
Self { _private: () }
209217
}
210218
}
211-
212-
extern "C" {
213-
type Opaque;
214-
}

0 commit comments

Comments
 (0)