Emit the enum discriminant separately for the Encodable macro

This commit is contained in:
John Kåre Alsaker 2023-02-24 23:58:32 +01:00
parent 07c993eba8
commit 7de205ea3a
2 changed files with 30 additions and 29 deletions

View File

@ -42,6 +42,12 @@ fn decodable_body(
}
let ty_name = s.ast().ident.to_string();
let decode_body = match s.variants() {
[] => {
let message = format!("`{}` has no variants to decode", ty_name);
quote! {
panic!(#message)
}
}
[vi] => vi.construct(|field, _index| decode_field(field)),
variants => {
let match_inner: TokenStream = variants
@ -139,6 +145,11 @@ fn encodable_body(
});
let encode_body = match s.variants() {
[] => {
quote! {
match *self {}
}
}
[_] => {
let encode_inner = s.each_variant(|vi| {
vi.bindings()
@ -160,6 +171,23 @@ fn encodable_body(
}
}
_ => {
let disc = {
let mut variant_idx = 0usize;
let encode_inner = s.each_variant(|_| {
let result = quote! {
#variant_idx
};
variant_idx += 1;
result
});
quote! {
let disc = match *self {
#encode_inner
};
::rustc_serialize::Encoder::emit_usize(__encoder, disc);
}
};
let mut variant_idx = 0usize;
let encode_inner = s.each_variant(|vi| {
let encode_fields: TokenStream = vi
@ -176,26 +204,11 @@ fn encodable_body(
result
})
.collect();
let result = if !vi.bindings().is_empty() {
quote! {
::rustc_serialize::Encoder::emit_enum_variant(
__encoder,
#variant_idx,
|__encoder| { #encode_fields }
)
}
} else {
quote! {
::rustc_serialize::Encoder::emit_fieldless_enum_variant::<#variant_idx>(
__encoder,
)
}
};
variant_idx += 1;
result
encode_fields
});
quote! {
#disc
match *self {
#encode_inner
}

View File

@ -43,7 +43,6 @@ pub trait Encoder {
fn emit_str(&mut self, v: &str);
fn emit_raw_bytes(&mut self, s: &[u8]);
// Convenience for the derive macro:
fn emit_enum_variant<F>(&mut self, v_id: usize, f: F)
where
F: FnOnce(&mut Self),
@ -51,17 +50,6 @@ pub trait Encoder {
self.emit_usize(v_id);
f(self);
}
// We put the field index in a const generic to allow the emit_usize to be
// compiled into a more efficient form. In practice, the variant index is
// known at compile-time, and that knowledge allows much more efficient
// codegen than we'd otherwise get. LLVM isn't always able to make the
// optimization that would otherwise be necessary here, likely due to the
// multiple levels of inlining and const-prop that are needed.
#[inline]
fn emit_fieldless_enum_variant<const ID: usize>(&mut self) {
self.emit_usize(ID)
}
}
// Note: all the methods in this trait are infallible, which may be surprising.