Skip to content

Commit b22aa57

Browse files
committed
Auto merge of rust-lang#106884 - clubby789:fieldless-enum-debug, r=michaelwoerister
Simplify `derive(Debug)` output for fieldless enums Fixes rust-lang#106875
2 parents e098eb1 + 95a824c commit b22aa57

File tree

12 files changed

+130
-47
lines changed

12 files changed

+130
-47
lines changed

compiler/rustc_builtin_macros/src/deriving/clone.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ pub fn expand_deriving_clone(
8282
nonself_args: Vec::new(),
8383
ret_ty: Self_,
8484
attributes: attrs,
85-
unify_fieldless_variants: false,
85+
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
8686
combine_substructure: substructure,
8787
}],
8888
associated_types: Vec::new(),
@@ -177,7 +177,9 @@ fn cs_clone(
177177
all_fields = af;
178178
vdata = &variant.data;
179179
}
180-
EnumTag(..) => cx.span_bug(trait_span, &format!("enum tags in `derive({})`", name,)),
180+
EnumTag(..) | AllFieldlessEnum(..) => {
181+
cx.span_bug(trait_span, &format!("enum tags in `derive({})`", name,))
182+
}
181183
StaticEnum(..) | StaticStruct(..) => {
182184
cx.span_bug(trait_span, &format!("associated function in `derive({})`", name))
183185
}

compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub fn expand_deriving_eq(
3636
nonself_args: vec![],
3737
ret_ty: Unit,
3838
attributes: attrs,
39-
unify_fieldless_variants: true,
39+
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
4040
combine_substructure: combine_substructure(Box::new(|a, b, c| {
4141
cs_total_eq_assert(a, b, c)
4242
})),

compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ pub fn expand_deriving_ord(
2929
nonself_args: vec![(self_ref(), sym::other)],
3030
ret_ty: Path(path_std!(cmp::Ordering)),
3131
attributes: attrs,
32-
unify_fieldless_variants: true,
32+
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
3333
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_cmp(a, b, c))),
3434
}],
3535
associated_types: Vec::new(),

compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub fn expand_deriving_partial_eq(
7676
nonself_args: vec![(self_ref(), sym::other)],
7777
ret_ty: Path(path_local!(bool)),
7878
attributes: attrs,
79-
unify_fieldless_variants: true,
79+
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
8080
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_eq(a, b, c))),
8181
}];
8282

compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub fn expand_deriving_partial_ord(
2828
nonself_args: vec![(self_ref(), sym::other)],
2929
ret_ty,
3030
attributes: attrs,
31-
unify_fieldless_variants: true,
31+
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
3232
combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
3333
cs_partial_cmp(cx, span, substr)
3434
})),

compiler/rustc_builtin_macros/src/deriving/debug.rs

+51-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::deriving::generic::ty::*;
22
use crate::deriving::generic::*;
33
use crate::deriving::path_std;
44

5+
use ast::EnumDef;
56
use rustc_ast::{self as ast, MetaItem};
67
use rustc_expand::base::{Annotatable, ExtCtxt};
78
use rustc_span::symbol::{sym, Ident, Symbol};
@@ -31,7 +32,8 @@ pub fn expand_deriving_debug(
3132
nonself_args: vec![(fmtr, sym::f)],
3233
ret_ty: Path(path_std!(fmt::Result)),
3334
attributes: ast::AttrVec::new(),
34-
unify_fieldless_variants: false,
35+
fieldless_variants_strategy:
36+
FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless,
3537
combine_substructure: combine_substructure(Box::new(|a, b, c| {
3638
show_substructure(a, b, c)
3739
})),
@@ -43,16 +45,18 @@ pub fn expand_deriving_debug(
4345
}
4446

4547
fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
48+
// We want to make sure we have the ctxt set so that we can use unstable methods
49+
let span = cx.with_def_site_ctxt(span);
50+
4651
let (ident, vdata, fields) = match substr.fields {
4752
Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
4853
EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
54+
AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr),
4955
EnumTag(..) | StaticStruct(..) | StaticEnum(..) => {
5056
cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
5157
}
5258
};
5359

54-
// We want to make sure we have the ctxt set so that we can use unstable methods
55-
let span = cx.with_def_site_ctxt(span);
5660
let name = cx.expr_str(span, ident.name);
5761
let fmt = substr.nonselflike_args[0].clone();
5862

@@ -173,3 +177,47 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
173177
BlockOrExpr::new_mixed(stmts, Some(expr))
174178
}
175179
}
180+
181+
/// Special case for enums with no fields. Builds:
182+
/// ```text
183+
/// impl ::core::fmt::Debug for A {
184+
/// fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
185+
/// ::core::fmt::Formatter::write_str(f,
186+
/// match self {
187+
/// A::A => "A",
188+
/// A::B() => "B",
189+
/// A::C {} => "C",
190+
/// })
191+
/// }
192+
/// }
193+
/// ```
194+
fn show_fieldless_enum(
195+
cx: &mut ExtCtxt<'_>,
196+
span: Span,
197+
def: &EnumDef,
198+
substr: &Substructure<'_>,
199+
) -> BlockOrExpr {
200+
let fmt = substr.nonselflike_args[0].clone();
201+
let arms = def
202+
.variants
203+
.iter()
204+
.map(|v| {
205+
let variant_path = cx.path(span, vec![substr.type_ident, v.ident]);
206+
let pat = match &v.data {
207+
ast::VariantData::Tuple(fields, _) => {
208+
debug_assert!(fields.is_empty());
209+
cx.pat_tuple_struct(span, variant_path, vec![])
210+
}
211+
ast::VariantData::Struct(fields, _) => {
212+
debug_assert!(fields.is_empty());
213+
cx.pat_struct(span, variant_path, vec![])
214+
}
215+
ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
216+
};
217+
cx.arm(span, pat, cx.expr_str(span, v.ident.name))
218+
})
219+
.collect::<Vec<_>>();
220+
let name = cx.expr_match(span, cx.expr_self(span), arms);
221+
let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
222+
BlockOrExpr::new_expr(cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]))
223+
}

compiler/rustc_builtin_macros/src/deriving/decodable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub fn expand_deriving_rustc_decodable(
4949
PathKind::Std,
5050
)),
5151
attributes: ast::AttrVec::new(),
52-
unify_fieldless_variants: false,
52+
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
5353
combine_substructure: combine_substructure(Box::new(|a, b, c| {
5454
decodable_substructure(a, b, c, krate)
5555
})),

compiler/rustc_builtin_macros/src/deriving/default.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub fn expand_deriving_default(
3434
nonself_args: Vec::new(),
3535
ret_ty: Self_,
3636
attributes: attrs,
37-
unify_fieldless_variants: false,
37+
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
3838
combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| {
3939
match substr.fields {
4040
StaticStruct(_, fields) => {

compiler/rustc_builtin_macros/src/deriving/encodable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ pub fn expand_deriving_rustc_encodable(
133133
PathKind::Std,
134134
)),
135135
attributes: AttrVec::new(),
136-
unify_fieldless_variants: false,
136+
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
137137
combine_substructure: combine_substructure(Box::new(|a, b, c| {
138138
encodable_substructure(a, b, c, krate)
139139
})),

compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

+61-29
Original file line numberDiff line numberDiff line change
@@ -222,14 +222,27 @@ pub struct MethodDef<'a> {
222222

223223
pub attributes: ast::AttrVec,
224224

225-
/// Can we combine fieldless variants for enums into a single match arm?
226-
/// If true, indicates that the trait operation uses the enum tag in some
227-
/// way.
228-
pub unify_fieldless_variants: bool,
225+
pub fieldless_variants_strategy: FieldlessVariantsStrategy,
229226

230227
pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>,
231228
}
232229

230+
/// How to handle fieldless enum variants.
231+
#[derive(PartialEq)]
232+
pub enum FieldlessVariantsStrategy {
233+
/// Combine fieldless variants into a single match arm.
234+
/// This assumes that relevant information has been handled
235+
/// by looking at the enum's discriminant.
236+
Unify,
237+
/// Don't do anything special about fieldless variants. They are
238+
/// handled like any other variant.
239+
Default,
240+
/// If all variants of the enum are fieldless, expand the special
241+
/// `AllFieldLessEnum` substructure, so that the entire enum can be handled
242+
/// at once.
243+
SpecializeIfAllVariantsFieldless,
244+
}
245+
233246
/// All the data about the data structure/method being derived upon.
234247
pub struct Substructure<'a> {
235248
/// ident of self
@@ -264,9 +277,14 @@ pub enum StaticFields {
264277

265278
/// A summary of the possible sets of fields.
266279
pub enum SubstructureFields<'a> {
267-
/// A non-static method with `Self` is a struct.
280+
/// A non-static method where `Self` is a struct.
268281
Struct(&'a ast::VariantData, Vec<FieldInfo>),
269282

283+
/// A non-static method handling the entire enum at once
284+
/// (after it has been determined that none of the enum
285+
/// variants has any fields).
286+
AllFieldlessEnum(&'a ast::EnumDef),
287+
270288
/// Matching variants of the enum: variant index, variant count, ast::Variant,
271289
/// fields: the field name is only non-`None` in the case of a struct
272290
/// variant.
@@ -1086,8 +1104,8 @@ impl<'a> MethodDef<'a> {
10861104
/// ```
10871105
/// Creates a tag check combined with a match for a tuple of all
10881106
/// `selflike_args`, with an arm for each variant with fields, possibly an
1089-
/// arm for each fieldless variant (if `!unify_fieldless_variants` is not
1090-
/// true), and possibly a default arm.
1107+
/// arm for each fieldless variant (if `unify_fieldless_variants` is not
1108+
/// `Unify`), and possibly a default arm.
10911109
fn expand_enum_method_body<'b>(
10921110
&self,
10931111
cx: &mut ExtCtxt<'_>,
@@ -1101,7 +1119,8 @@ impl<'a> MethodDef<'a> {
11011119
let variants = &enum_def.variants;
11021120

11031121
// Traits that unify fieldless variants always use the tag(s).
1104-
let uses_tags = self.unify_fieldless_variants;
1122+
let unify_fieldless_variants =
1123+
self.fieldless_variants_strategy == FieldlessVariantsStrategy::Unify;
11051124

11061125
// There is no sensible code to be generated for *any* deriving on a
11071126
// zero-variant enum. So we just generate a failing expression.
@@ -1161,23 +1180,35 @@ impl<'a> MethodDef<'a> {
11611180
// match is necessary.
11621181
let all_fieldless = variants.iter().all(|v| v.data.fields().is_empty());
11631182
if all_fieldless {
1164-
if uses_tags && variants.len() > 1 {
1165-
// If the type is fieldless and the trait uses the tag and
1166-
// there are multiple variants, we need just an operation on
1167-
// the tag(s).
1168-
let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
1169-
let mut tag_check = self.call_substructure_method(
1170-
cx,
1171-
trait_,
1172-
type_ident,
1173-
nonselflike_args,
1174-
&EnumTag(tag_field, None),
1175-
);
1176-
tag_let_stmts.append(&mut tag_check.0);
1177-
return BlockOrExpr(tag_let_stmts, tag_check.1);
1178-
}
1179-
1180-
if variants.len() == 1 {
1183+
if variants.len() > 1 {
1184+
match self.fieldless_variants_strategy {
1185+
FieldlessVariantsStrategy::Unify => {
1186+
// If the type is fieldless and the trait uses the tag and
1187+
// there are multiple variants, we need just an operation on
1188+
// the tag(s).
1189+
let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
1190+
let mut tag_check = self.call_substructure_method(
1191+
cx,
1192+
trait_,
1193+
type_ident,
1194+
nonselflike_args,
1195+
&EnumTag(tag_field, None),
1196+
);
1197+
tag_let_stmts.append(&mut tag_check.0);
1198+
return BlockOrExpr(tag_let_stmts, tag_check.1);
1199+
}
1200+
FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless => {
1201+
return self.call_substructure_method(
1202+
cx,
1203+
trait_,
1204+
type_ident,
1205+
nonselflike_args,
1206+
&AllFieldlessEnum(enum_def),
1207+
);
1208+
}
1209+
FieldlessVariantsStrategy::Default => (),
1210+
}
1211+
} else if variants.len() == 1 {
11811212
// If there is a single variant, we don't need an operation on
11821213
// the tag(s). Just use the most degenerate result.
11831214
return self.call_substructure_method(
@@ -1187,7 +1218,7 @@ impl<'a> MethodDef<'a> {
11871218
nonselflike_args,
11881219
&EnumMatching(0, 1, &variants[0], Vec::new()),
11891220
);
1190-
};
1221+
}
11911222
}
11921223

11931224
// These arms are of the form:
@@ -1198,7 +1229,7 @@ impl<'a> MethodDef<'a> {
11981229
let mut match_arms: Vec<ast::Arm> = variants
11991230
.iter()
12001231
.enumerate()
1201-
.filter(|&(_, v)| !(self.unify_fieldless_variants && v.data.fields().is_empty()))
1232+
.filter(|&(_, v)| !(unify_fieldless_variants && v.data.fields().is_empty()))
12021233
.map(|(index, variant)| {
12031234
// A single arm has form (&VariantK, &VariantK, ...) => BodyK
12041235
// (see "Final wrinkle" note below for why.)
@@ -1249,7 +1280,7 @@ impl<'a> MethodDef<'a> {
12491280
// Add a default arm to the match, if necessary.
12501281
let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty());
12511282
let default = match first_fieldless {
1252-
Some(v) if self.unify_fieldless_variants => {
1283+
Some(v) if unify_fieldless_variants => {
12531284
// We need a default case that handles all the fieldless
12541285
// variants. The index and actual variant aren't meaningful in
12551286
// this case, so just use dummy values.
@@ -1296,7 +1327,7 @@ impl<'a> MethodDef<'a> {
12961327
// If the trait uses the tag and there are multiple variants, we need
12971328
// to add a tag check operation before the match. Otherwise, the match
12981329
// is enough.
1299-
if uses_tags && variants.len() > 1 {
1330+
if unify_fieldless_variants && variants.len() > 1 {
13001331
let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
13011332

13021333
// Combine a tag check with the match.
@@ -1580,5 +1611,6 @@ where
15801611
}
15811612
}
15821613
StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"),
1614+
AllFieldlessEnum(..) => cx.span_bug(trait_span, "fieldless enum in `derive`"),
15831615
}
15841616
}

compiler/rustc_builtin_macros/src/deriving/hash.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub fn expand_deriving_hash(
3333
nonself_args: vec![(Ref(Box::new(Path(arg)), Mutability::Mut), sym::state)],
3434
ret_ty: Unit,
3535
attributes: AttrVec::new(),
36-
unify_fieldless_variants: true,
36+
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
3737
combine_substructure: combine_substructure(Box::new(|a, b, c| {
3838
hash_substructure(a, b, c)
3939
})),

tests/ui/deriving/deriving-all-codegen.stdout

+6-5
Original file line numberDiff line numberDiff line change
@@ -731,11 +731,12 @@ impl ::core::marker::Copy for Fieldless { }
731731
#[automatically_derived]
732732
impl ::core::fmt::Debug for Fieldless {
733733
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
734-
match self {
735-
Fieldless::A => ::core::fmt::Formatter::write_str(f, "A"),
736-
Fieldless::B => ::core::fmt::Formatter::write_str(f, "B"),
737-
Fieldless::C => ::core::fmt::Formatter::write_str(f, "C"),
738-
}
734+
::core::fmt::Formatter::write_str(f,
735+
match self {
736+
Fieldless::A => "A",
737+
Fieldless::B => "B",
738+
Fieldless::C => "C",
739+
})
739740
}
740741
}
741742
#[automatically_derived]

0 commit comments

Comments
 (0)