From c92b4fd9e10b4dd48afa0efe1f86ec0e9861a071 Mon Sep 17 00:00:00 2001 From: panstromek Date: Sat, 15 Aug 2026 15:12:36 +0200 Subject: [PATCH 1/8] remove unused variable --- compiler/rustc_macros/src/serialize.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 5ae6fb241c98d..9017d8ce1bd34 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -241,7 +241,6 @@ fn encodable_body( } }; - let mut variant_idx = 0usize; let encode_inner = s.each_variant(|vi| { let encode_fields: TokenStream = vi .bindings() @@ -257,7 +256,6 @@ fn encodable_body( result }) .collect(); - variant_idx += 1; encode_fields }); quote! { From df3231a12ce62d6568c31669d44154afa61363a0 Mon Sep 17 00:00:00 2001 From: panstromek Date: Sat, 15 Aug 2026 15:35:34 +0200 Subject: [PATCH 2/8] add comment explaining why we generate two match statements in Encodable derive --- compiler/rustc_macros/src/serialize.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 9017d8ce1bd34..8cfde859bc0b5 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -215,6 +215,9 @@ fn encodable_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(|_| { From 71d2bedfcad3361cb799a4accd8720f76fb140cc Mon Sep 17 00:00:00 2001 From: panstromek Date: Sun, 16 Aug 2026 13:06:19 +0200 Subject: [PATCH 3/8] add test to capture Encodable derive output --- tests/ui-fulldeps/derive-encodable.rs | 43 ++++++++ tests/ui-fulldeps/derive-encodable.stdout | 114 ++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 tests/ui-fulldeps/derive-encodable.rs create mode 100644 tests/ui-fulldeps/derive-encodable.stdout 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..a6891b62d36d7 --- /dev/null +++ b/tests/ui-fulldeps/derive-encodable.stdout @@ -0,0 +1,114 @@ +#![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) { + match *self { UnitStruct => {} } + } + } + }; + +struct EmptyStruct {} +const _: () = + { + impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> + for EmptyStruct { + fn encode(&self, __encoder: &mut __E) { + match *self { EmptyStruct {} => {} } + } + } + }; + +enum EmptyEnum {} +const _: () = + { + impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> + for EmptyEnum { + fn encode(&self, __encoder: &mut __E) { match *self {} } + } + }; + +enum SingleFieldlessEnum { A, } +const _: () = + { + impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> + for SingleFieldlessEnum { + fn encode(&self, __encoder: &mut __E) { + match *self { SingleFieldlessEnum::A => {} } + } + } + }; + +enum SingleEnum { A(u32), } +const _: () = + { + impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> + for SingleEnum { + fn encode(&self, __encoder: &mut __E) { + match *self { + SingleEnum::A(ref __binding_0) => { + ::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); + match *self { FieldlessEnum::A => {} FieldlessEnum::B => {} } + } + } + }; + +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); + } + } + } + } + }; From 26ec2ac85289e3dc699be75946a3278ad35ab5f5 Mon Sep 17 00:00:00 2001 From: panstromek Date: Sat, 15 Aug 2026 15:44:41 +0200 Subject: [PATCH 4/8] Don't generate empty match for fieldless enums in Encodable derive --- compiler/rustc_macros/src/serialize.rs | 47 +++++++++++++---------- tests/ui-fulldeps/derive-encodable.stdout | 1 - 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 8cfde859bc0b5..57383a1cdfce6 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -244,27 +244,32 @@ fn encodable_body( } }; - 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(); - 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| { + 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(); + encode_fields + }); + quote! { + #disc + match *self { + #encode_inner + } } } } diff --git a/tests/ui-fulldeps/derive-encodable.stdout b/tests/ui-fulldeps/derive-encodable.stdout index a6891b62d36d7..94795d3a2320e 100644 --- a/tests/ui-fulldeps/derive-encodable.stdout +++ b/tests/ui-fulldeps/derive-encodable.stdout @@ -85,7 +85,6 @@ const _: () = FieldlessEnum::B => { 1usize } }; ::rustc_serialize::Encoder::emit_u8(__encoder, disc as u8); - match *self { FieldlessEnum::A => {} FieldlessEnum::B => {} } } } }; From e5f1dea36328128e88c0d7559ab49ac0c6dd10ee Mon Sep 17 00:00:00 2001 From: panstromek Date: Sat, 15 Aug 2026 15:46:01 +0200 Subject: [PATCH 5/8] remove redundant variable --- compiler/rustc_macros/src/serialize.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 57383a1cdfce6..8956fca51b442 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -248,9 +248,8 @@ fn encodable_body( // Avoid generating second match statement if all variants are fieldless disc } else { - let encode_inner = s.each_variant(|vi| { - let encode_fields: TokenStream = vi - .bindings() + let encode_inner = s.each_variant(|vi| -> TokenStream { + vi.bindings() .iter() .map(|binding| { let bind_ident = &binding.binding; @@ -262,8 +261,7 @@ fn encodable_body( }; result }) - .collect(); - encode_fields + .collect() }); quote! { #disc From 3efc459948c5bd73906eb68cc2d81b61e63599ad Mon Sep 17 00:00:00 2001 From: panstromek Date: Sat, 15 Aug 2026 16:03:04 +0200 Subject: [PATCH 6/8] Avoid generating match for structs in Encodable derive --- compiler/rustc_macros/src/serialize.rs | 36 ++++++++++++----------- tests/ui-fulldeps/derive-encodable.stdout | 17 ++++------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 8956fca51b442..8de2e1748f1e2 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -194,24 +194,26 @@ fn encodable_body( match *self {} } } - [_] => { - 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::() - }); + [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 } } _ => { diff --git a/tests/ui-fulldeps/derive-encodable.stdout b/tests/ui-fulldeps/derive-encodable.stdout index 94795d3a2320e..aae4fb5c8b67e 100644 --- a/tests/ui-fulldeps/derive-encodable.stdout +++ b/tests/ui-fulldeps/derive-encodable.stdout @@ -20,9 +20,7 @@ const _: () = { impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> for UnitStruct { - fn encode(&self, __encoder: &mut __E) { - match *self { UnitStruct => {} } - } + fn encode(&self, __encoder: &mut __E) { let UnitStruct = *self; } } }; @@ -32,7 +30,7 @@ const _: () = impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> for EmptyStruct { fn encode(&self, __encoder: &mut __E) { - match *self { EmptyStruct {} => {} } + let EmptyStruct {} = *self; } } }; @@ -52,7 +50,7 @@ const _: () = impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> for SingleFieldlessEnum { fn encode(&self, __encoder: &mut __E) { - match *self { SingleFieldlessEnum::A => {} } + let SingleFieldlessEnum::A = *self; } } }; @@ -63,12 +61,9 @@ const _: () = impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> for SingleEnum { fn encode(&self, __encoder: &mut __E) { - match *self { - SingleEnum::A(ref __binding_0) => { - ::rustc_serialize::Encodable::<__E>::encode(__binding_0, - __encoder); - } - } + let SingleEnum::A(ref __binding_0) = *self; + ::rustc_serialize::Encodable::<__E>::encode(__binding_0, + __encoder); } } }; From 50804da5224ac6dfb0c3561ef4391eb0fdbf3d44 Mon Sep 17 00:00:00 2001 From: panstromek Date: Sat, 15 Aug 2026 16:05:48 +0200 Subject: [PATCH 7/8] don't generate empty match for fieldless enums in Encodable derive --- compiler/rustc_macros/src/serialize.rs | 4 +--- tests/ui-fulldeps/derive-encodable.stdout | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 8de2e1748f1e2..e8be5963898f0 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -190,9 +190,7 @@ fn encodable_body( let encode_body = match s.variants() { [] => { - quote! { - match *self {} - } + quote! {} } [vi] => { let pat = vi.pat(); diff --git a/tests/ui-fulldeps/derive-encodable.stdout b/tests/ui-fulldeps/derive-encodable.stdout index aae4fb5c8b67e..d0485b6d1e64c 100644 --- a/tests/ui-fulldeps/derive-encodable.stdout +++ b/tests/ui-fulldeps/derive-encodable.stdout @@ -40,7 +40,7 @@ const _: () = { impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> for EmptyEnum { - fn encode(&self, __encoder: &mut __E) { match *self {} } + fn encode(&self, __encoder: &mut __E) {} } }; From 93510b82535de603850bd67595b901624b257d97 Mon Sep 17 00:00:00 2001 From: panstromek Date: Sun, 16 Aug 2026 13:19:24 +0200 Subject: [PATCH 8/8] Don't generate code in for all Unit-like types Encodable derive --- compiler/rustc_macros/src/serialize.rs | 5 +++++ tests/ui-fulldeps/derive-encodable.stdout | 10 +++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index e8be5963898f0..801c12e776770 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -192,6 +192,11 @@ fn encodable_body( [] => { quote! {} } + // 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 diff --git a/tests/ui-fulldeps/derive-encodable.stdout b/tests/ui-fulldeps/derive-encodable.stdout index d0485b6d1e64c..50700ab15f9c5 100644 --- a/tests/ui-fulldeps/derive-encodable.stdout +++ b/tests/ui-fulldeps/derive-encodable.stdout @@ -20,7 +20,7 @@ const _: () = { impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> for UnitStruct { - fn encode(&self, __encoder: &mut __E) { let UnitStruct = *self; } + fn encode(&self, __encoder: &mut __E) {} } }; @@ -29,9 +29,7 @@ const _: () = { impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> for EmptyStruct { - fn encode(&self, __encoder: &mut __E) { - let EmptyStruct {} = *self; - } + fn encode(&self, __encoder: &mut __E) {} } }; @@ -49,9 +47,7 @@ const _: () = { impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E> for SingleFieldlessEnum { - fn encode(&self, __encoder: &mut __E) { - let SingleFieldlessEnum::A = *self; - } + fn encode(&self, __encoder: &mut __E) {} } };