diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 5ae6fb241c98d..801c12e776770 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -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::() - }); + // 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::(); + 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(|_| { @@ -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 + } 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 + } } } } diff --git a/tests/ui-fulldeps/derive-encodable.rs b/tests/ui-fulldeps/derive-encodable.rs new file mode 100644 index 0000000000000..daf27cb17b867 --- /dev/null +++ b/tests/ui-fulldeps/derive-encodable.rs @@ -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), +} diff --git a/tests/ui-fulldeps/derive-encodable.stdout b/tests/ui-fulldeps/derive-encodable.stdout new file mode 100644 index 0000000000000..50700ab15f9c5 --- /dev/null +++ b/tests/ui-fulldeps/derive-encodable.stdout @@ -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); + } + } + } + } + };