core: avoid extern types in formatting infrastructure

This commit is contained in:
joboet 2024-06-26 00:06:16 +02:00
parent 2c9556d28a
commit 23d1cc4b84
No known key found for this signature in database
GPG key ID: 704E0149B0194B3C

View file

@ -5,6 +5,7 @@
use super::*; use super::*;
use crate::hint::unreachable_unchecked; use crate::hint::unreachable_unchecked;
use crate::ptr::NonNull;
#[lang = "format_placeholder"] #[lang = "format_placeholder"]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -66,7 +67,13 @@ pub(super) enum Flag {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
enum ArgumentType<'a> { enum ArgumentType<'a> {
Placeholder { value: &'a Opaque, formatter: fn(&Opaque, &mut Formatter<'_>) -> Result }, Placeholder {
// INVARIANT: if `formatter` had type `fn(&T, _) -> _` then `value`
// was derived from a `&T` with lifetime `'a`.
value: NonNull<()>,
formatter: unsafe fn(NonNull<()>, &mut Formatter<'_>) -> Result,
_lifetime: PhantomData<&'a ()>,
},
Count(usize), Count(usize),
} }
@ -90,21 +97,15 @@ pub struct Argument<'a> {
impl<'a> Argument<'a> { impl<'a> Argument<'a> {
#[inline(always)] #[inline(always)]
fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> { fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
// SAFETY: `mem::transmute(x)` is safe because Argument {
// 1. `&'b T` keeps the lifetime it originated with `'b` // INVARIANT: this creates an `ArgumentType<'b>` from a `&'b T` and
// (so as to not have an unbounded lifetime) // a `fn(&T, ...)`, so the invariant is maintained.
// 2. `&'b T` and `&'b Opaque` have the same memory layout ty: ArgumentType::Placeholder {
// (when `T` is `Sized`, as it is here) value: NonNull::from(x).cast(),
// `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result` // SAFETY: function pointers always have the same layout.
// and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI formatter: unsafe { mem::transmute(f) },
// (as long as `T` is `Sized`) _lifetime: PhantomData,
unsafe { },
Argument {
ty: ArgumentType::Placeholder {
formatter: mem::transmute(f),
value: mem::transmute(x),
},
}
} }
} }
@ -162,7 +163,14 @@ pub fn from_usize(x: &usize) -> Argument<'_> {
#[inline(always)] #[inline(always)]
pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result { pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self.ty { match self.ty {
ArgumentType::Placeholder { formatter, value } => formatter(value, f), // SAFETY:
// Because of the invariant that if `formatter` had the type
// `fn(&T, _) -> _` then `value` has type `&'b T` where `'b` is
// the lifetime of the `ArgumentType`, and because references
// and `NonNull` are ABI-compatible, this is completely equivalent
// to calling the original function passed to `new` with the
// original reference, which is sound.
ArgumentType::Placeholder { formatter, value, .. } => unsafe { formatter(value, f) },
// SAFETY: the caller promised this. // SAFETY: the caller promised this.
ArgumentType::Count(_) => unsafe { unreachable_unchecked() }, ArgumentType::Count(_) => unsafe { unreachable_unchecked() },
} }
@ -208,7 +216,3 @@ pub unsafe fn new() -> Self {
Self { _private: () } Self { _private: () }
} }
} }
extern "C" {
type Opaque;
}