rustc におけるシリアライズ
rustc は、コンパイル中にさまざまなデータをシリアライズおよびデシリアライズする必要があります。 具体的には、次のとおりです。
- 主にクエリ出力で構成される「クレートメタデータ」は、ライブラリクレートのコンパイル時に出力される
rlibファイルおよびrmetaファイルへ、バイナリ形式でシリアライズされます。これらのrlibファイルおよびrmetaファイルは、そのライブラリに依存するクレートによってデシリアライズされます。 - 特定のクエリ出力は、インクリメンタルコンパイル結果を永続化するために、バイナリ形式でシリアライズされます。
CrateInfoは-Z no-linkフラグが使用されるとJSONへシリアライズされ、-Z link-onlyフラグが使用されるとJSONからデシリアライズされます。
Encodable トレイトと Decodable トレイト
rustc_serialize クレートは、シリアライズ可能な型のために 2 つのトレイトを定義しています。
pub trait Encodable<S: Encoder> {
fn encode(&self, s: &mut S) -> Result<(), S::Error>;
}
pub trait Decodable<D: Decoder>: Sized {
fn decode(d: &mut D) -> Result<Self, D::Error>;
}
また、整数型、浮動小数点型、bool、char、str など、さまざまな一般的な標準ライブラリのプリミティブ型に対するこれらの実装も定義しています。
それらの型から構築される型については、Encodable と Decodable は通常 derive によって実装されます。これらは、構造体または enum のフィールドへデシリアライズを委譲する実装を生成します。構造体の場合、それらの impl はおおよそ次のようになります。
#![feature(rustc_private)]
extern crate rustc_serialize;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
struct MyStruct {
int: u32,
float: f32,
}
impl<E: Encoder> Encodable<E> for MyStruct {
fn encode(&self, s: &mut E) -> Result<(), E::Error> {
s.emit_struct("MyStruct", 2, |s| {
s.emit_struct_field("int", 0, |s| self.int.encode(s))?;
s.emit_struct_field("float", 1, |s| self.float.encode(s))
})
}
}
impl<D: Decoder> Decodable<D> for MyStruct {
fn decode(s: &mut D) -> Result<MyStruct, D::Error> {
s.read_struct("MyStruct", 2, |d| {
let int = d.read_struct_field("int", 0, Decodable::decode)?;
let float = d.read_struct_field("float", 1, Decodable::decode)?;
Ok(MyStruct { int, float })
})
}
}
アリーナに割り当てられた型のエンコードとデコード
rustc には多数のアリーナに割り当てられた型があります。
これらの型をデシリアライズするには、それらを割り当てる必要があるアリーナへのアクセスが必要です。
TyDecoder トレイトと TyEncoder トレイトは、TyCtxt へのアクセスを可能にする Decoder と Encoder のサブトレイトです。
arena に割り当てられた型を含む型は、その Encodable および Decodable 実装の型パラメーターを、これらのトレイトで境界付けることができます。
例:
impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for MyStruct<'tcx> {
/* ... */
}
TyEncodable および TyDecodable の derive マクロは、そのような実装へ展開されます。
実際の arena に割り当てられた型のデコードはより困難です。なぜなら、一部の実装は孤児ルールのために書けないからです。これを回避するために、RefDecodable トレイトが rustc_middle で定義されています。これは任意の型に対して実装できます。TyDecodable マクロは参照をデコードするために RefDecodable を呼び出しますが、さまざまなジェネリックコードでは、型が特定のデコーダーで実際に Decodable である必要があります。
インターン化された型については、RefDecodable を手動で実装する代わりに、ty::Predicate のような newtype ラッパーを使用し、Encodable と Decodable を手動で実装するほうが簡単な場合があります。
Derive マクロ
[rustc_macros] クレートは、Decodable と Encodable の実装を助けるさまざまな derive を定義しています。
EncodableマクロとDecodableマクロは、すべてのEncodersとDecodersに適用される実装を生成します。これらは、rustc_middleに依存しないクレート、またはTyEncoderを実装していない型によってシリアライズされる必要があるクレートで使用すべきです。- [
MetadataEncodable] は、[rustc_metadata::rmeta::encoder::EncodeContext] によるデコードのみを許可する実装を生成します。 - [
BlobDecodable] と [LazyDecodable] は、MetadataEncodableに対応するデコード側として機能します。これらはrustc_metadata::rmeta内のメタデータ blob デコーダーでデコードする実装を生成します。型に遅延メタデータハンドルがない場合はBlobDecodableを使用し、ある場合はLazyDecodableを使用します。 TyEncodableとTyDecodableは、任意のTyEncoderまたはTyDecoderに適用される実装を生成します。これらは、クレートメタデータやインクリメンタルキャッシュでのみシリアライズされる型に使用すべきです。これはrustc_middle内のシリアライズ可能な型の大半に該当します。 [BlobDecodable]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_macros/derive.BlobDecodable.html [LazyDecodable]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_macros/derive.LazyDecodable.html [MetadataEncodable]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_macros/derive.MetadataEncodable.html [rustc_macros]: https://github.com/rust-lang/rust/tree/HEAD/compiler/rustc_macros [rustc_metadata::rmeta]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/rmeta/index.html [rustc_metadata::rmeta::encoder::EncodeContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/rmeta/encoder/struct.EncodeContext.htmlrustc_middle: https://github.com/rust-lang/rust/tree/HEAD/compiler/rustc_middle
短縮表記
Ty は深く再帰的になる可能性があり、各 Ty を素朴にエンコードすると、クレートメタデータは非常に大きくなります。これに対処するため、各 TyEncoder は、型をシリアライズした出力内の位置のキャッシュを持ちます。エンコード中の型がキャッシュ内にある場合、通常どおり型をシリアライズする代わりに、書き込み中のファイル内のバイトオフセットがエンコードされます。同様の方式が ty::Predicate にも使用されます。
LazyValue<T>
クレートメタデータは TyCtxt<'tcx> が作成される前に最初に読み込まれるため、一部のデシリアライズはメタデータの初期読み込みから遅延させる必要があります。LazyValue<T> 型は、T がシリアライズされているクレートメタデータ内の(相対)オフセットをラップします。また、いくつかのバリアント、LazyArray<T> と LazyTable<I, T> もあります。
LazyArray<[T]> 型と LazyTable<I, T> 型は、Lazy<Vec<T>> と Lazy<HashMap<I, T>> に対していくつかの機能を提供します。
- 先に
Vec<T>に収集することなく、Iteratorから直接LazyArray<T>をエンコードできます。 LazyTable<I, T>へのインデックス指定では、読み取っているもの以外のエントリをデコードする必要はありません。
注: LazyValue<T> は、初回デシリアライズ後にその値をキャッシュしません。代わりに、クエリシステム自体がこれらの結果をキャッシュする主な方法です。
特殊化
いくつかの型、とりわけ DefId は、異なる Encoder ごとに異なる実装を持つ必要があります。これは現在、アドホックな特殊化によって処理されています。例: DefId には Encodable<E> の default 実装と、Encodable<CacheEncoder> に特化した実装があります。