Rust: Gunakan #[repr(C)] HList untuk menyimpulkan pointer fmt fn yang terhapus tipe dalam data statis format_args!.

Dibuat pada 5 Sep 2017  ·  3Komentar  ·  Sumber: rust-lang/rust

Saat ini format_args! menggunakan, misalnya ArgumentV1::new(&runtime_data, Debug::fmt) (untuk {:?} ), saat runtime, menggunakan dua pointer per argumen saat runtime, bukan hanya satu ( &runtime_data ) .

Dengan allow_internal_unsafe dan #44240, kita dapat menempatkan pointer (misalnya Debug::fmt ) fn dalam data (dipromosikan nilai) 'static , rintangan yang tersisa adalah bagaimana untuk menyimpulkan jenis data runtime.
Artinya, Debug::fmt benar-benar <_ as Debug>::fmt dan _ itu sekarang disimpulkan karena tanda tangan ArgumentV1::new mengetiknya bersama-sama. Jika mereka terpisah, kita membutuhkan sesuatu yang baru.

Saya mengusulkan menggunakan pola HList ( struct HCons<H, T>(H, T); struct HNil; - jadi untuk 3 elemen, dari tipe A , B dan C Anda akan memiliki HCons<A, HCons<B, HCons<C, HNil>>> ), dengan #[repr(C)] , yang akan memberikan tata letak deterministik yang cocok dengan array, yaitu, dua ini:

  • &'static HCons<fn(&A), HCons<fn(&B), HCons<fn(&C), HNil>>>
  • &'static [unsafe fn(*const Opaque); 3]

memiliki representasi yang sama, dan yang terakhir dapat diubah ukurannya menjadi irisan. Transformasi dari HList ke array (dan kemudian irisan) ini dapat dilakukan di atas HCons aman yang dipromosikan nilai, yang merupakan persyaratan yang diperlukan untuk memindahkan pointer fn menjadi 'static data sama sekali.

Untuk inferensi, kita cukup memasukkan beberapa pemanggilan fungsi untuk mencocokkan jenisnya, misalnya untuk menyimpulkan B kita bisa melakukan fmt::unify_fn_with_data((list.1).0, &b) , yang akan membuat B menjadi typeof b .

Mungkin sebenarnya lebih mudah untuk memiliki antarmuka "pembuat" yang sepenuhnya aman, yang menggabungkan HList formatter dengan HList referensi runtime, menyatukan jenisnya, tetapi saya agak khawatir tentang waktu kompilasi karena semua pengiriman sifat - dalam hal apa pun, dampaknya harus diukur.


A-fmt C-enhancement I-heavy T-libs

Komentar yang paling membantu

Untuk inferensi, kita cukup memasukkan beberapa pemanggilan fungsi untuk mencocokkan jenisnya, misalnya untuk menyimpulkan B kita bisa melakukan fmt::unify_fn_with_data((list.1).0, &b) , yang akan membuat B menjadi typeof b .

Tidak yakin apa yang saya pikirkan di sana, seharusnya jauh lebih mudah dari itu!

struct ArgMetadata<T: ?Sized> {
    // Only `unsafe` because of the later cast we do from `T` to `Opaque`.
    fmt: unsafe fn(&T, &mut Formatter<'_>) -> Result,
    // ... flags, constant string fragments, etc.
}

// TODO: maybe name this something else to emphasize repr(C)?
#[repr(C)]
struct HCons<T, Rest>(T, Rest);

// This would have to be in a "sealed module" to make it impossible to implement on more types.
trait MetadataFor<D> {
    const LEN: usize;
}
impl MetadataFor<()> for () {
    const LEN: usize = 0;
}
impl<'a, T: ?Sized, D, M> MetadataFor<HCons<&'a T, D>> for HCons<ArgMetadata<T>, M>
    where M: MetadataFor<D>
{
    const LEN: usize = M::LEN;
}

impl<'a> Arguments<'a> {
    fn new<M, D>(meta: &'a M, data: &'a D) -> Self
        where M: MetadataFor<D>
    {
        Self {
            meta: unsafe { &*(meta as *const _ as *const [ArgMetadata<Opaque>; M::LEN]) },
            data: unsafe { &*(data as *const _ as *const [&Opaque; M::LEN]) },
        }
    }
}

yaitu kita membangun dua HList "secara paralel", satu dengan metadata yang sepenuhnya konstan, dan yang lainnya dengan referensi ke data runtime, dan kemudian semua jenis inferensi dapat berasal dari where klausa pada fmt::Arguments::new , dengan nol codegen cruft!

EDIT : @m-ou-se harus mengingatkan saya mengapa saya menggunakan trik inferensi eksplisit sejak awal: argumen akses acak :kecewa:
(mungkin dengan penggunaan const generics ab yang cukup kita bisa mendapatkan D: IndexHList<i, Output = T> tapi itu banyak usaha)

Semua 3 komentar

klaim @rustbot

Untuk inferensi, kita cukup memasukkan beberapa pemanggilan fungsi untuk mencocokkan jenisnya, misalnya untuk menyimpulkan B kita bisa melakukan fmt::unify_fn_with_data((list.1).0, &b) , yang akan membuat B menjadi typeof b .

Tidak yakin apa yang saya pikirkan di sana, seharusnya jauh lebih mudah dari itu!

struct ArgMetadata<T: ?Sized> {
    // Only `unsafe` because of the later cast we do from `T` to `Opaque`.
    fmt: unsafe fn(&T, &mut Formatter<'_>) -> Result,
    // ... flags, constant string fragments, etc.
}

// TODO: maybe name this something else to emphasize repr(C)?
#[repr(C)]
struct HCons<T, Rest>(T, Rest);

// This would have to be in a "sealed module" to make it impossible to implement on more types.
trait MetadataFor<D> {
    const LEN: usize;
}
impl MetadataFor<()> for () {
    const LEN: usize = 0;
}
impl<'a, T: ?Sized, D, M> MetadataFor<HCons<&'a T, D>> for HCons<ArgMetadata<T>, M>
    where M: MetadataFor<D>
{
    const LEN: usize = M::LEN;
}

impl<'a> Arguments<'a> {
    fn new<M, D>(meta: &'a M, data: &'a D) -> Self
        where M: MetadataFor<D>
    {
        Self {
            meta: unsafe { &*(meta as *const _ as *const [ArgMetadata<Opaque>; M::LEN]) },
            data: unsafe { &*(data as *const _ as *const [&Opaque; M::LEN]) },
        }
    }
}

yaitu kita membangun dua HList "secara paralel", satu dengan metadata yang sepenuhnya konstan, dan yang lainnya dengan referensi ke data runtime, dan kemudian semua jenis inferensi dapat berasal dari where klausa pada fmt::Arguments::new , dengan nol codegen cruft!

EDIT : @m-ou-se harus mengingatkan saya mengapa saya menggunakan trik inferensi eksplisit sejak awal: argumen akses acak :kecewa:
(mungkin dengan penggunaan const generics ab yang cukup kita bisa mendapatkan D: IndexHList<i, Output = T> tapi itu banyak usaha)

Saya memiliki implementasi baru fmt::Arguments yang hanya berukuran dua pointer dengan menggunakan bentuk baru 'metadata statis yang berisi potongan string dan opsi pemformatan apa pun (jika ada). (Jadi sekarang cocok dengan pasangan register, yang sangat bagus.) Ini juga hanya membutuhkan satu penunjuk di tumpukan per argumen, bukan dua, seperti yang tampaknya sudah disarankan oleh @eddyb tiga tahun lalu dalam edisi ini. ^^ (Benar-benar melewatkan masalah ini sampai @eddyb menunjukkannya kemarin. ^^')

Apa yang masih tersisa adalah memperbarui format_args!() untuk menghasilkan tipe baru ini sebagai gantinya, yang akan mengalami masalah pemisahan penunjuk objek dan penunjuk fungsi (yang saat ini bersama-sama dalam ArgumentV1 ), sebagai penunjuk fungsi sekarang harus masuk ke 'metadata statis sebagai gantinya. Saran dalam masalah ini sepertinya cara yang baik untuk melakukan itu. Akan mencoba menerapkannya segera. Menantikan perf run :)

Apakah halaman ini membantu?
0 / 5 - 0 peringkat