Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 52 additions & 43 deletions compiler/rustc_macros/src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,31 +190,39 @@ fn encodable_body(

let encode_body = match s.variants() {
[] => {
quote! {
match *self {}
}
quote! {}
}
[_] => {
let encode_inner = s.each_variant(|vi| {
vi.bindings()
.iter()
.map(|binding| {
let bind_ident = &binding.binding;
let result = quote! {
::rustc_serialize::Encodable::<#encoder_ty>::encode(
#bind_ident,
__encoder,
);
};
result
})
.collect::<TokenStream>()
});
// Unit-like types don't need to encode anything.
// This covers fieldless structs and enums with zero or one fieldless variant.
[vi] if vi.bindings().is_empty() => {
quote! {}
}
[vi] => {
let pat = vi.pat();
let body = vi
.bindings()
.iter()
.map(|binding| {
let bind_ident = &binding.binding;
let result = quote! {
::rustc_serialize::Encodable::<#encoder_ty>::encode(
#bind_ident,
__encoder,
);
};
result
})
.collect::<TokenStream>();

quote! {
match *self { #encode_inner }
let #pat = *self;
#body
}
}
_ => {
// This code generates two separate match statements on purpose, because
// LLVM can optimize the first one into direct discriminant read.
// See: https://github.com/rust-lang/rust/pull/108440
let disc = {
let mut variant_idx = 0usize;
let encode_inner = s.each_variant(|_| {
Expand All @@ -241,29 +249,30 @@ fn encodable_body(
}
};

let mut variant_idx = 0usize;
let encode_inner = s.each_variant(|vi| {
let encode_fields: TokenStream = vi
.bindings()
.iter()
.map(|binding| {
let bind_ident = &binding.binding;
let result = quote! {
::rustc_serialize::Encodable::<#encoder_ty>::encode(
#bind_ident,
__encoder,
);
};
result
})
.collect();
variant_idx += 1;
encode_fields
});
quote! {
#disc
match *self {
#encode_inner
if s.variants().iter().all(|v| v.bindings().is_empty()) {
// Avoid generating second match statement if all variants are fieldless
disc
Comment thread
panstromek marked this conversation as resolved.
} else {
let encode_inner = s.each_variant(|vi| -> TokenStream {
vi.bindings()
.iter()
.map(|binding| {
let bind_ident = &binding.binding;
let result = quote! {
::rustc_serialize::Encodable::<#encoder_ty>::encode(
#bind_ident,
__encoder,
);
};
result
})
.collect()
});
quote! {
#disc
match *self {
#encode_inner
}
}
}
}
Expand Down
43 changes: 43 additions & 0 deletions tests/ui-fulldeps/derive-encodable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//@ edition: 2024
//@ check-pass
//@ compile-flags: -Zunpretty=expanded

#![crate_type = "rlib"]
#![feature(rustc_private)]

extern crate rustc_macros;
extern crate rustc_serialize;
extern crate rustc_span;

use rustc_macros::Encodable;

#[derive(Encodable)]
struct UnitStruct;

#[derive(Encodable)]
struct EmptyStruct {}

#[derive(Encodable)]
enum EmptyEnum {}

#[derive(Encodable)]
enum SingleFieldlessEnum {
A,
}

#[derive(Encodable)]
enum SingleEnum {
A(u32),
}

#[derive(Encodable)]
enum FieldlessEnum {
A,
B,
}

#[derive(Encodable)]
enum PartlyFieldlessEnum {
A,
B(u32),
}
104 changes: 104 additions & 0 deletions tests/ui-fulldeps/derive-encodable.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#![feature(prelude_import)]
//@ edition: 2024
//@ check-pass
//@ compile-flags: -Zunpretty=expanded

#![crate_type = "rlib"]
#![feature(rustc_private)]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;

extern crate rustc_macros;
extern crate rustc_serialize;
extern crate rustc_span;

use rustc_macros::Encodable;

struct UnitStruct;
const _: () =
{
impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
for UnitStruct {
fn encode(&self, __encoder: &mut __E) {}
}
};

struct EmptyStruct {}
const _: () =
{
impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
for EmptyStruct {
fn encode(&self, __encoder: &mut __E) {}
}
};

enum EmptyEnum {}
const _: () =
{
impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
for EmptyEnum {
fn encode(&self, __encoder: &mut __E) {}
}
};

enum SingleFieldlessEnum { A, }
const _: () =
{
impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
for SingleFieldlessEnum {
fn encode(&self, __encoder: &mut __E) {}
}
};

enum SingleEnum { A(u32), }
const _: () =
{
impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
for SingleEnum {
fn encode(&self, __encoder: &mut __E) {
let SingleEnum::A(ref __binding_0) = *self;
::rustc_serialize::Encodable::<__E>::encode(__binding_0,
__encoder);
}
}
};

enum FieldlessEnum { A, B, }
const _: () =
{
impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
for FieldlessEnum {
fn encode(&self, __encoder: &mut __E) {
let disc =
match *self {
FieldlessEnum::A => { 0usize }
FieldlessEnum::B => { 1usize }
};
::rustc_serialize::Encoder::emit_u8(__encoder, disc as u8);
}
}
};

enum PartlyFieldlessEnum { A, B(u32), }
const _: () =
{
impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
for PartlyFieldlessEnum {
fn encode(&self, __encoder: &mut __E) {
let disc =
match *self {
PartlyFieldlessEnum::A => { 0usize }
PartlyFieldlessEnum::B(ref __binding_0) => { 1usize }
};
::rustc_serialize::Encoder::emit_u8(__encoder, disc as u8);
match *self {
PartlyFieldlessEnum::A => {}
PartlyFieldlessEnum::B(ref __binding_0) => {
::rustc_serialize::Encodable::<__E>::encode(__binding_0,
__encoder);
}
}
}
}
};
Loading