Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit fbdb07f

Browse files
committedJul 8, 2022
Auto merge of #98758 - nnethercote:more-derive-output-improvements, r=Mark-Simulacrum
More derive output improvements This PR includes: - Some test improvements. - Some cosmetic changes to derive output that make the code look more like what a human would write. - Some more fundamental improvements to `cmp` and `partial_cmp` generation. r? `@Mark-Simulacrum`
2 parents 1dcff2d + 0da063c commit fbdb07f

File tree

15 files changed

+655
-568
lines changed

15 files changed

+655
-568
lines changed
 

‎compiler/rustc_ast/src/ast.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,6 +2036,14 @@ impl TyKind {
20362036
pub fn is_unit(&self) -> bool {
20372037
matches!(self, TyKind::Tup(tys) if tys.is_empty())
20382038
}
2039+
2040+
pub fn is_simple_path(&self) -> Option<Symbol> {
2041+
if let TyKind::Path(None, Path { segments, .. }) = &self && segments.len() == 1 {
2042+
Some(segments[0].ident.name)
2043+
} else {
2044+
None
2045+
}
2046+
}
20392047
}
20402048

20412049
/// Syntax used to declare a trait object.

‎compiler/rustc_builtin_macros/src/deriving/clone.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::deriving::generic::ty::*;
22
use crate::deriving::generic::*;
33
use crate::deriving::path_std;
44

5-
use rustc_ast::ptr::P;
6-
use rustc_ast::{self as ast, Expr, Generics, ItemKind, MetaItem, VariantData};
5+
use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData};
6+
use rustc_data_structures::fx::FxHashSet;
77
use rustc_expand::base::{Annotatable, ExtCtxt};
88
use rustc_span::symbol::{kw, sym, Ident};
99
use rustc_span::Span;
@@ -98,22 +98,31 @@ fn cs_clone_simple(
9898
trait_span: Span,
9999
substr: &Substructure<'_>,
100100
is_union: bool,
101-
) -> P<Expr> {
101+
) -> BlockOrExpr {
102102
let mut stmts = Vec::new();
103+
let mut seen_type_names = FxHashSet::default();
103104
let mut process_variant = |variant: &VariantData| {
104105
for field in variant.fields() {
105-
// let _: AssertParamIsClone<FieldTy>;
106-
super::assert_ty_bounds(
107-
cx,
108-
&mut stmts,
109-
field.ty.clone(),
110-
field.span,
111-
&[sym::clone, sym::AssertParamIsClone],
112-
);
106+
// This basic redundancy checking only prevents duplication of
107+
// assertions like `AssertParamIsClone<Foo>` where the type is a
108+
// simple name. That's enough to get a lot of cases, though.
109+
if let Some(name) = field.ty.kind.is_simple_path() && !seen_type_names.insert(name) {
110+
// Already produced an assertion for this type.
111+
} else {
112+
// let _: AssertParamIsClone<FieldTy>;
113+
super::assert_ty_bounds(
114+
cx,
115+
&mut stmts,
116+
field.ty.clone(),
117+
field.span,
118+
&[sym::clone, sym::AssertParamIsClone],
119+
);
120+
}
113121
}
114122
};
115123

116124
if is_union {
125+
// Just a single assertion for unions, that the union impls `Copy`.
117126
// let _: AssertParamIsCopy<Self>;
118127
let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper)));
119128
super::assert_ty_bounds(
@@ -139,16 +148,15 @@ fn cs_clone_simple(
139148
),
140149
}
141150
}
142-
stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
143-
cx.expr_block(cx.block(trait_span, stmts))
151+
BlockOrExpr::new_mixed(stmts, cx.expr_deref(trait_span, cx.expr_self(trait_span)))
144152
}
145153

146154
fn cs_clone(
147155
name: &str,
148156
cx: &mut ExtCtxt<'_>,
149157
trait_span: Span,
150158
substr: &Substructure<'_>,
151-
) -> P<Expr> {
159+
) -> BlockOrExpr {
152160
let ctor_path;
153161
let all_fields;
154162
let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
@@ -177,7 +185,7 @@ fn cs_clone(
177185
}
178186
}
179187

180-
match *vdata {
188+
let expr = match *vdata {
181189
VariantData::Struct(..) => {
182190
let fields = all_fields
183191
.iter()
@@ -201,5 +209,6 @@ fn cs_clone(
201209
cx.expr_call(trait_span, path, subcalls)
202210
}
203211
VariantData::Unit(..) => cx.expr_path(ctor_path),
204-
}
212+
};
213+
BlockOrExpr::new_expr(expr)
205214
}

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

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::deriving::generic::ty::*;
22
use crate::deriving::generic::*;
33
use crate::deriving::path_std;
44

5-
use rustc_ast::ptr::P;
6-
use rustc_ast::{self as ast, Expr, MetaItem};
5+
use rustc_ast::{self as ast, MetaItem};
6+
use rustc_data_structures::fx::FxHashSet;
77
use rustc_expand::base::{Annotatable, ExtCtxt};
88
use rustc_span::symbol::{sym, Ident};
99
use rustc_span::Span;
@@ -52,18 +52,26 @@ fn cs_total_eq_assert(
5252
cx: &mut ExtCtxt<'_>,
5353
trait_span: Span,
5454
substr: &Substructure<'_>,
55-
) -> P<Expr> {
55+
) -> BlockOrExpr {
5656
let mut stmts = Vec::new();
57+
let mut seen_type_names = FxHashSet::default();
5758
let mut process_variant = |variant: &ast::VariantData| {
5859
for field in variant.fields() {
59-
// let _: AssertParamIsEq<FieldTy>;
60-
super::assert_ty_bounds(
61-
cx,
62-
&mut stmts,
63-
field.ty.clone(),
64-
field.span,
65-
&[sym::cmp, sym::AssertParamIsEq],
66-
);
60+
// This basic redundancy checking only prevents duplication of
61+
// assertions like `AssertParamIsEq<Foo>` where the type is a
62+
// simple name. That's enough to get a lot of cases, though.
63+
if let Some(name) = field.ty.kind.is_simple_path() && !seen_type_names.insert(name) {
64+
// Already produced an assertion for this type.
65+
} else {
66+
// let _: AssertParamIsEq<FieldTy>;
67+
super::assert_ty_bounds(
68+
cx,
69+
&mut stmts,
70+
field.ty.clone(),
71+
field.span,
72+
&[sym::cmp, sym::AssertParamIsEq],
73+
);
74+
}
6775
}
6876
};
6977

@@ -78,5 +86,5 @@ fn cs_total_eq_assert(
7886
}
7987
_ => cx.span_bug(trait_span, "unexpected substructure in `derive(Eq)`"),
8088
}
81-
cx.expr_block(cx.block(trait_span, stmts))
89+
BlockOrExpr::new_stmts(stmts)
8290
}

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::deriving::generic::*;
33
use crate::deriving::path_std;
44

55
use rustc_ast::ptr::P;
6-
use rustc_ast::{self as ast, Expr, MetaItem};
6+
use rustc_ast::{self as ast, MetaItem};
77
use rustc_expand::base::{Annotatable, ExtCtxt};
88
use rustc_span::symbol::{sym, Ident};
99
use rustc_span::Span;
@@ -51,7 +51,7 @@ pub fn ordering_collapsed(
5151
cx.expr_call_global(span, fn_cmp_path, vec![lft, rgt])
5252
}
5353

54-
pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
54+
pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
5555
let test_id = Ident::new(sym::cmp, span);
5656
let equals_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal]));
5757

@@ -70,7 +70,7 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<
7070
// cmp => cmp
7171
// }
7272
//
73-
cs_fold(
73+
let expr = cs_fold(
7474
// foldr nests the if-elses correctly, leaving the first field
7575
// as the outermost one, and the last as the innermost.
7676
false,
@@ -79,15 +79,12 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<
7979
// ::std::cmp::Ordering::Equal => old,
8080
// cmp => cmp
8181
// }
82-
8382
let new = {
8483
let [other_f] = other_fs else {
8584
cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`");
8685
};
87-
8886
let args =
8987
vec![cx.expr_addr_of(span, self_f), cx.expr_addr_of(span, other_f.clone())];
90-
9188
cx.expr_call_global(span, cmp_path.clone(), args)
9289
};
9390

@@ -96,7 +93,21 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<
9693

9794
cx.expr_match(span, new, vec![eq_arm, neq_arm])
9895
},
99-
cx.expr_path(equals_path.clone()),
96+
|cx, args| match args {
97+
Some((span, self_f, other_fs)) => {
98+
let new = {
99+
let [other_f] = other_fs else {
100+
cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`");
101+
};
102+
let args =
103+
vec![cx.expr_addr_of(span, self_f), cx.expr_addr_of(span, other_f.clone())];
104+
cx.expr_call_global(span, cmp_path.clone(), args)
105+
};
106+
107+
new
108+
}
109+
None => cx.expr_path(equals_path.clone()),
110+
},
100111
Box::new(|cx, span, tag_tuple| {
101112
if tag_tuple.len() != 2 {
102113
cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`")
@@ -107,5 +118,6 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<
107118
cx,
108119
span,
109120
substr,
110-
)
121+
);
122+
BlockOrExpr::new_expr(expr)
111123
}

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,14 @@ pub fn expand_deriving_partial_eq(
1515
item: &Annotatable,
1616
push: &mut dyn FnMut(Annotatable),
1717
) {
18-
// structures are equal if all fields are equal, and non equal, if
19-
// any fields are not equal or if the enum variants are different
2018
fn cs_op(
2119
cx: &mut ExtCtxt<'_>,
2220
span: Span,
2321
substr: &Substructure<'_>,
2422
op: BinOpKind,
2523
combiner: BinOpKind,
2624
base: bool,
27-
) -> P<Expr> {
25+
) -> BlockOrExpr {
2826
let op = |cx: &mut ExtCtxt<'_>, span: Span, self_f: P<Expr>, other_fs: &[P<Expr>]| {
2927
let [other_f] = other_fs else {
3028
cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`");
@@ -33,7 +31,7 @@ pub fn expand_deriving_partial_eq(
3331
cx.expr_binary(span, op, self_f, other_f.clone())
3432
};
3533

36-
cs_fold1(
34+
let expr = cs_fold(
3735
true, // use foldl
3836
|cx, span, subexpr, self_f, other_fs| {
3937
let eq = op(cx, span, self_f, other_fs);
@@ -52,13 +50,14 @@ pub fn expand_deriving_partial_eq(
5250
cx,
5351
span,
5452
substr,
55-
)
53+
);
54+
BlockOrExpr::new_expr(expr)
5655
}
5756

58-
fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
57+
fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
5958
cs_op(cx, span, substr, BinOpKind::Eq, BinOpKind::And, true)
6059
}
61-
fn cs_ne(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
60+
fn cs_ne(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
6261
cs_op(cx, span, substr, BinOpKind::Ne, BinOpKind::Or, false)
6362
}
6463

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,10 @@ pub fn expand_deriving_partial_ord(
4848
trait_def.expand(cx, mitem, item, push)
4949
}
5050

51-
pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
51+
pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
5252
let test_id = Ident::new(sym::cmp, span);
5353
let ordering = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal]));
5454
let ordering_expr = cx.expr_path(ordering.clone());
55-
let equals_expr = cx.expr_some(span, ordering_expr);
5655

5756
let partial_cmp_path = cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]);
5857

@@ -69,7 +68,7 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_
6968
// cmp => cmp
7069
// }
7170
//
72-
cs_fold(
71+
let expr = cs_fold(
7372
// foldr nests the if-elses correctly, leaving the first field
7473
// as the outermost one, and the last as the innermost.
7574
false,
@@ -95,7 +94,21 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_
9594

9695
cx.expr_match(span, new, vec![eq_arm, neq_arm])
9796
},
98-
equals_expr,
97+
|cx: &mut ExtCtxt<'_>, args: Option<(Span, P<Expr>, &[P<Expr>])>| match args {
98+
Some((span, self_f, other_fs)) => {
99+
let new = {
100+
let [other_f] = other_fs else {
101+
cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`");
102+
};
103+
let args =
104+
vec![cx.expr_addr_of(span, self_f), cx.expr_addr_of(span, other_f.clone())];
105+
cx.expr_call_global(span, partial_cmp_path.clone(), args)
106+
};
107+
108+
new
109+
}
110+
None => cx.expr_some(span, ordering_expr.clone()),
111+
},
99112
Box::new(|cx, span, tag_tuple| {
100113
if tag_tuple.len() != 2 {
101114
cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
@@ -110,5 +123,6 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_
110123
cx,
111124
span,
112125
substr,
113-
)
126+
);
127+
BlockOrExpr::new_expr(expr)
114128
}

‎compiler/rustc_builtin_macros/src/deriving/debug.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*;
22
use crate::deriving::generic::*;
33
use crate::deriving::path_std;
44

5-
use rustc_ast::ptr::P;
6-
use rustc_ast::{self as ast, Expr, MetaItem};
5+
use rustc_ast::{self as ast, MetaItem};
76
use rustc_expand::base::{Annotatable, ExtCtxt};
87
use rustc_span::symbol::{sym, Ident, Symbol};
98
use rustc_span::Span;
@@ -42,7 +41,7 @@ pub fn expand_deriving_debug(
4241
trait_def.expand(cx, mitem, item, push)
4342
}
4443

45-
fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
44+
fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
4645
let (ident, vdata, fields) = match substr.fields {
4746
Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
4847
EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
@@ -74,7 +73,8 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
7473
if fields.is_empty() {
7574
// Special case for no fields.
7675
let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
77-
cx.expr_call_global(span, fn_path_write_str, vec![fmt, name])
76+
let expr = cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]);
77+
BlockOrExpr::new_expr(expr)
7878
} else if fields.len() <= CUTOFF {
7979
// Few enough fields that we can use a specific-length method.
8080
let debug = if is_struct {
@@ -100,7 +100,8 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
100100
let field = cx.expr_addr_of(field.span, field);
101101
args.push(field);
102102
}
103-
cx.expr_call_global(span, fn_path_debug, args)
103+
let expr = cx.expr_call_global(span, fn_path_debug, args);
104+
BlockOrExpr::new_expr(expr)
104105
} else {
105106
// Enough fields that we must use the any-length method.
106107
let mut name_exprs = Vec::with_capacity(fields.len());
@@ -176,8 +177,6 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
176177
stmts.push(names_let.unwrap());
177178
}
178179
stmts.push(values_let);
179-
stmts.push(cx.stmt_expr(expr));
180-
181-
cx.expr_block(cx.block(span, stmts))
180+
BlockOrExpr::new_mixed(stmts, expr)
182181
}
183182
}

‎compiler/rustc_builtin_macros/src/deriving/decodable.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ fn decodable_substructure(
6262
trait_span: Span,
6363
substr: &Substructure<'_>,
6464
krate: Symbol,
65-
) -> P<Expr> {
65+
) -> BlockOrExpr {
6666
let decoder = substr.nonself_args[0].clone();
6767
let recurse = vec![
6868
Ident::new(krate, trait_span),
@@ -74,7 +74,7 @@ fn decodable_substructure(
7474
let blkarg = Ident::new(sym::_d, trait_span);
7575
let blkdecoder = cx.expr_ident(trait_span, blkarg);
7676

77-
match *substr.fields {
77+
let expr = match *substr.fields {
7878
StaticStruct(_, ref summary) => {
7979
let nfields = match *summary {
8080
Unnamed(ref fields, _) => fields.len(),
@@ -173,7 +173,8 @@ fn decodable_substructure(
173173
)
174174
}
175175
_ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)"),
176-
}
176+
};
177+
BlockOrExpr::new_expr(expr)
177178
}
178179

179180
/// Creates a decoder for a single enum variant/struct:

‎compiler/rustc_builtin_macros/src/deriving/default.rs

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use crate::deriving::generic::ty::*;
22
use crate::deriving::generic::*;
33

4-
use rustc_ast::ptr::P;
4+
use rustc_ast as ast;
55
use rustc_ast::walk_list;
66
use rustc_ast::EnumDef;
77
use rustc_ast::VariantData;
8-
use rustc_ast::{Expr, MetaItem};
98
use rustc_errors::Applicability;
109
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
1110
use rustc_span::symbol::Ident;
@@ -16,7 +15,7 @@ use smallvec::SmallVec;
1615
pub fn expand_deriving_default(
1716
cx: &mut ExtCtxt<'_>,
1817
span: Span,
19-
mitem: &MetaItem,
18+
mitem: &ast::MetaItem,
2019
item: &Annotatable,
2120
push: &mut dyn FnMut(Annotatable),
2221
) {
@@ -59,12 +58,12 @@ fn default_struct_substructure(
5958
trait_span: Span,
6059
substr: &Substructure<'_>,
6160
summary: &StaticFields,
62-
) -> P<Expr> {
61+
) -> BlockOrExpr {
6362
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
6463
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
6564
let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new());
6665

67-
match summary {
66+
let expr = match summary {
6867
Unnamed(ref fields, is_tuple) => {
6968
if !is_tuple {
7069
cx.expr_ident(trait_span, substr.type_ident)
@@ -80,31 +79,27 @@ fn default_struct_substructure(
8079
.collect();
8180
cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
8281
}
83-
}
82+
};
83+
BlockOrExpr::new_expr(expr)
8484
}
8585

8686
fn default_enum_substructure(
8787
cx: &mut ExtCtxt<'_>,
8888
trait_span: Span,
8989
enum_def: &EnumDef,
90-
) -> P<Expr> {
91-
let Ok(default_variant) = extract_default_variant(cx, enum_def, trait_span) else {
92-
return DummyResult::raw_expr(trait_span, true);
90+
) -> BlockOrExpr {
91+
let expr = if let Ok(default_variant) = extract_default_variant(cx, enum_def, trait_span)
92+
&& let Ok(_) = validate_default_attribute(cx, default_variant)
93+
{
94+
// We now know there is exactly one unit variant with exactly one `#[default]` attribute.
95+
cx.expr_path(cx.path(
96+
default_variant.span,
97+
vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
98+
))
99+
} else {
100+
DummyResult::raw_expr(trait_span, true)
93101
};
94-
95-
// At this point, we know that there is exactly one variant with a `#[default]` attribute. The
96-
// attribute hasn't yet been validated.
97-
98-
if let Err(()) = validate_default_attribute(cx, default_variant) {
99-
return DummyResult::raw_expr(trait_span, true);
100-
}
101-
102-
// We now know there is exactly one unit variant with exactly one `#[default]` attribute.
103-
104-
cx.expr_path(cx.path(
105-
default_variant.span,
106-
vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
107-
))
102+
BlockOrExpr::new_expr(expr)
108103
}
109104

110105
fn extract_default_variant<'a>(

‎compiler/rustc_builtin_macros/src/deriving/encodable.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ use crate::deriving::generic::ty::*;
8989
use crate::deriving::generic::*;
9090
use crate::deriving::pathvec_std;
9191

92-
use rustc_ast::ptr::P;
93-
use rustc_ast::{Expr, ExprKind, MetaItem, Mutability};
92+
use rustc_ast::{ExprKind, MetaItem, Mutability};
9493
use rustc_expand::base::{Annotatable, ExtCtxt};
9594
use rustc_span::symbol::{sym, Ident, Symbol};
9695
use rustc_span::Span;
@@ -147,7 +146,7 @@ fn encodable_substructure(
147146
trait_span: Span,
148147
substr: &Substructure<'_>,
149148
krate: Symbol,
150-
) -> P<Expr> {
149+
) -> BlockOrExpr {
151150
let encoder = substr.nonself_args[0].clone();
152151
// throw an underscore in front to suppress unused variable warnings
153152
let blkarg = Ident::new(sym::_e, trait_span);
@@ -208,7 +207,7 @@ fn encodable_substructure(
208207
let fn_emit_struct_path =
209208
cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct]);
210209

211-
cx.expr_call_global(
210+
let expr = cx.expr_call_global(
212211
trait_span,
213212
fn_emit_struct_path,
214213
vec![
@@ -217,7 +216,8 @@ fn encodable_substructure(
217216
cx.expr_usize(trait_span, fields.len()),
218217
blk,
219218
],
220-
)
219+
);
220+
BlockOrExpr::new_expr(expr)
221221
}
222222

223223
EnumMatching(idx, _, variant, ref fields) => {
@@ -279,12 +279,12 @@ fn encodable_substructure(
279279
let blk = cx.lambda1(trait_span, call, blkarg);
280280
let fn_emit_enum_path: Vec<_> =
281281
cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum]);
282-
let ret = cx.expr_call_global(
282+
let expr = cx.expr_call_global(
283283
trait_span,
284284
fn_emit_enum_path,
285285
vec![encoder, cx.expr_str(trait_span, substr.type_ident.name), blk],
286286
);
287-
cx.expr_block(cx.block(trait_span, vec![me, cx.stmt_expr(ret)]))
287+
BlockOrExpr::new_mixed(vec![me], expr)
288288
}
289289

290290
_ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"),

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

Lines changed: 101 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ pub enum SubstructureFields<'a> {
296296
/// Combine the values of all the fields together. The last argument is
297297
/// all the fields of all the structures.
298298
pub type CombineSubstructureFunc<'a> =
299-
Box<dyn FnMut(&mut ExtCtxt<'_>, Span, &Substructure<'_>) -> P<Expr> + 'a>;
299+
Box<dyn FnMut(&mut ExtCtxt<'_>, Span, &Substructure<'_>) -> BlockOrExpr + 'a>;
300300

301301
/// Deal with non-matching enum variants. The slice is the identifiers holding
302302
/// the variant index value for each of the `Self` arguments.
@@ -314,6 +314,48 @@ struct TypeParameter {
314314
ty: P<ast::Ty>,
315315
}
316316

317+
// The code snippets built up for derived code are sometimes used as blocks
318+
// (e.g. in a function body) and sometimes used as expressions (e.g. in a match
319+
// arm). This structure avoids committing to either form until necessary,
320+
// avoiding the insertion of any unnecessary blocks.
321+
//
322+
// The statements come before the expression.
323+
pub struct BlockOrExpr(Vec<ast::Stmt>, Option<P<Expr>>);
324+
325+
impl BlockOrExpr {
326+
pub fn new_stmts(stmts: Vec<ast::Stmt>) -> BlockOrExpr {
327+
BlockOrExpr(stmts, None)
328+
}
329+
330+
pub fn new_expr(expr: P<Expr>) -> BlockOrExpr {
331+
BlockOrExpr(vec![], Some(expr))
332+
}
333+
334+
pub fn new_mixed(stmts: Vec<ast::Stmt>, expr: P<Expr>) -> BlockOrExpr {
335+
BlockOrExpr(stmts, Some(expr))
336+
}
337+
338+
// Converts it into a block.
339+
fn into_block(mut self, cx: &ExtCtxt<'_>, span: Span) -> P<ast::Block> {
340+
if let Some(expr) = self.1 {
341+
self.0.push(cx.stmt_expr(expr));
342+
}
343+
cx.block(span, self.0)
344+
}
345+
346+
// Converts it into an expression.
347+
fn into_expr(self, cx: &ExtCtxt<'_>, span: Span) -> P<Expr> {
348+
if self.0.is_empty() {
349+
match self.1 {
350+
None => cx.expr_block(cx.block(span, vec![])),
351+
Some(expr) => expr,
352+
}
353+
} else {
354+
cx.expr_block(self.into_block(cx, span))
355+
}
356+
}
357+
}
358+
317359
/// This method helps to extract all the type parameters referenced from a
318360
/// type. For a type parameter `<T>`, it looks for either a `TyPath` that
319361
/// is not global and starts with `T`, or a `TyQPath`.
@@ -827,7 +869,7 @@ impl<'a> MethodDef<'a> {
827869
type_ident: Ident,
828870
nonself_args: &[P<Expr>],
829871
fields: &SubstructureFields<'_>,
830-
) -> P<Expr> {
872+
) -> BlockOrExpr {
831873
let span = trait_.span;
832874
let substructure = Substructure { type_ident, nonself_args, fields };
833875
let mut f = self.combine_substructure.borrow_mut();
@@ -902,7 +944,7 @@ impl<'a> MethodDef<'a> {
902944
generics: &Generics,
903945
explicit_self: Option<ast::ExplicitSelf>,
904946
arg_types: Vec<(Ident, P<ast::Ty>)>,
905-
body: P<Expr>,
947+
body: BlockOrExpr,
906948
) -> P<ast::AssocItem> {
907949
let span = trait_.span;
908950
// Create the generics that aren't for `Self`.
@@ -921,7 +963,7 @@ impl<'a> MethodDef<'a> {
921963

922964
let method_ident = Ident::new(self.name, span);
923965
let fn_decl = cx.fn_decl(args, ast::FnRetTy::Ty(ret_type));
924-
let body_block = cx.block_expr(body);
966+
let body_block = body.into_block(cx, span);
925967

926968
let trait_lo_sp = span.shrink_to_lo();
927969

@@ -986,7 +1028,7 @@ impl<'a> MethodDef<'a> {
9861028
nonself_args: &[P<Expr>],
9871029
use_temporaries: bool,
9881030
is_packed: bool,
989-
) -> P<Expr> {
1031+
) -> BlockOrExpr {
9901032
let mut raw_fields = Vec::new(); // Vec<[fields of self], [fields of next Self arg], [etc]>
9911033
let span = trait_.span;
9921034
let mut patterns = Vec::new();
@@ -1047,16 +1089,14 @@ impl<'a> MethodDef<'a> {
10471089
);
10481090

10491091
if !is_packed {
1050-
body.span = span;
10511092
body
10521093
} else {
10531094
// Do the let-destructuring.
10541095
let mut stmts: Vec<_> = iter::zip(self_args, patterns)
10551096
.map(|(arg_expr, pat)| cx.stmt_let_pat(span, pat, arg_expr.clone()))
10561097
.collect();
1057-
stmts.push(cx.stmt_expr(body));
1058-
1059-
cx.expr_block(cx.block(span, stmts))
1098+
stmts.extend(std::mem::take(&mut body.0));
1099+
BlockOrExpr(stmts, body.1)
10601100
}
10611101
}
10621102

@@ -1067,7 +1107,7 @@ impl<'a> MethodDef<'a> {
10671107
struct_def: &VariantData,
10681108
type_ident: Ident,
10691109
nonself_args: &[P<Expr>],
1070-
) -> P<Expr> {
1110+
) -> BlockOrExpr {
10711111
let summary = trait_.summarise_struct(cx, struct_def);
10721112

10731113
self.call_substructure_method(
@@ -1130,7 +1170,7 @@ impl<'a> MethodDef<'a> {
11301170
type_ident: Ident,
11311171
mut self_args: Vec<P<Expr>>,
11321172
nonself_args: &[P<Expr>],
1133-
) -> P<Expr> {
1173+
) -> BlockOrExpr {
11341174
let span = trait_.span;
11351175
let variants = &enum_def.variants;
11361176

@@ -1199,7 +1239,11 @@ impl<'a> MethodDef<'a> {
11991239
}
12001240

12011241
// Here is the pat = `(&VariantK, &VariantK, ...)`
1202-
let single_pat = cx.pat_tuple(span, subpats);
1242+
let single_pat = if subpats.len() == 1 {
1243+
subpats.pop().unwrap()
1244+
} else {
1245+
cx.pat_tuple(span, subpats)
1246+
};
12031247

12041248
// For the BodyK, we need to delegate to our caller,
12051249
// passing it an EnumMatching to indicate which case
@@ -1253,13 +1297,9 @@ impl<'a> MethodDef<'a> {
12531297
// Self arg, assuming all are instances of VariantK.
12541298
// Build up code associated with such a case.
12551299
let substructure = EnumMatching(index, variants.len(), variant, field_tuples);
1256-
let arm_expr = self.call_substructure_method(
1257-
cx,
1258-
trait_,
1259-
type_ident,
1260-
nonself_args,
1261-
&substructure,
1262-
);
1300+
let arm_expr = self
1301+
.call_substructure_method(cx, trait_, type_ident, nonself_args, &substructure)
1302+
.into_expr(cx, span);
12631303

12641304
cx.arm(span, single_pat, arm_expr)
12651305
})
@@ -1271,13 +1311,16 @@ impl<'a> MethodDef<'a> {
12711311
// The index and actual variant aren't meaningful in this case,
12721312
// so just use whatever
12731313
let substructure = EnumMatching(0, variants.len(), v, Vec::new());
1274-
Some(self.call_substructure_method(
1275-
cx,
1276-
trait_,
1277-
type_ident,
1278-
nonself_args,
1279-
&substructure,
1280-
))
1314+
Some(
1315+
self.call_substructure_method(
1316+
cx,
1317+
trait_,
1318+
type_ident,
1319+
nonself_args,
1320+
&substructure,
1321+
)
1322+
.into_expr(cx, span),
1323+
)
12811324
}
12821325
_ if variants.len() > 1 && self_args.len() > 1 => {
12831326
// Since we know that all the arguments will match if we reach
@@ -1341,13 +1384,15 @@ impl<'a> MethodDef<'a> {
13411384
}
13421385
}
13431386

1344-
let arm_expr = self.call_substructure_method(
1345-
cx,
1346-
trait_,
1347-
type_ident,
1348-
nonself_args,
1349-
&catch_all_substructure,
1350-
);
1387+
let arm_expr = self
1388+
.call_substructure_method(
1389+
cx,
1390+
trait_,
1391+
type_ident,
1392+
nonself_args,
1393+
&catch_all_substructure,
1394+
)
1395+
.into_expr(cx, span);
13511396

13521397
// Final wrinkle: the self_args are expressions that deref
13531398
// down to desired places, but we cannot actually deref
@@ -1371,8 +1416,7 @@ impl<'a> MethodDef<'a> {
13711416
// }
13721417
let all_match = cx.expr_match(span, match_arg, match_arms);
13731418
let arm_expr = cx.expr_if(span, discriminant_test, all_match, Some(arm_expr));
1374-
index_let_stmts.push(cx.stmt_expr(arm_expr));
1375-
cx.expr_block(cx.block(span, index_let_stmts))
1419+
BlockOrExpr(index_let_stmts, Some(arm_expr))
13761420
} else if variants.is_empty() {
13771421
// As an additional wrinkle, For a zero-variant enum A,
13781422
// currently the compiler
@@ -1423,16 +1467,20 @@ impl<'a> MethodDef<'a> {
14231467
// derive Debug on such a type could here generate code
14241468
// that needs the feature gate enabled.)
14251469

1426-
deriving::call_unreachable(cx, span)
1470+
BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span)))
14271471
} else {
14281472
// Final wrinkle: the self_args are expressions that deref
14291473
// down to desired places, but we cannot actually deref
14301474
// them when they are fed as r-values into a tuple
14311475
// expression; here add a layer of borrowing, turning
14321476
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
14331477
self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg));
1434-
let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args));
1435-
cx.expr_match(span, match_arg, match_arms)
1478+
let match_arg = if self_args.len() == 1 {
1479+
self_args.pop().unwrap()
1480+
} else {
1481+
cx.expr(span, ast::ExprKind::Tup(self_args))
1482+
};
1483+
BlockOrExpr(vec![], Some(cx.expr_match(span, match_arg, match_arms)))
14361484
}
14371485
}
14381486

@@ -1443,7 +1491,7 @@ impl<'a> MethodDef<'a> {
14431491
enum_def: &EnumDef,
14441492
type_ident: Ident,
14451493
nonself_args: &[P<Expr>],
1446-
) -> P<Expr> {
1494+
) -> BlockOrExpr {
14471495
let summary = enum_def
14481496
.variants
14491497
.iter()
@@ -1606,71 +1654,6 @@ impl<'a> TraitDef<'a> {
16061654
}
16071655
}
16081656

1609-
// helpful premade recipes
1610-
1611-
fn cs_fold_fields<'a, F>(
1612-
use_foldl: bool,
1613-
mut f: F,
1614-
base: P<Expr>,
1615-
cx: &mut ExtCtxt<'_>,
1616-
all_fields: &[FieldInfo<'a>],
1617-
) -> P<Expr>
1618-
where
1619-
F: FnMut(&mut ExtCtxt<'_>, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>,
1620-
{
1621-
if use_foldl {
1622-
all_fields
1623-
.iter()
1624-
.fold(base, |old, field| f(cx, field.span, old, field.self_.clone(), &field.other))
1625-
} else {
1626-
all_fields
1627-
.iter()
1628-
.rev()
1629-
.fold(base, |old, field| f(cx, field.span, old, field.self_.clone(), &field.other))
1630-
}
1631-
}
1632-
1633-
fn cs_fold_enumnonmatch(
1634-
mut enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>,
1635-
cx: &mut ExtCtxt<'_>,
1636-
trait_span: Span,
1637-
substructure: &Substructure<'_>,
1638-
) -> P<Expr> {
1639-
match *substructure.fields {
1640-
EnumNonMatchingCollapsed(tuple) => enum_nonmatch_f(cx, trait_span, tuple),
1641-
_ => cx.span_bug(trait_span, "cs_fold_enumnonmatch expected an EnumNonMatchingCollapsed"),
1642-
}
1643-
}
1644-
1645-
fn cs_fold_static(cx: &mut ExtCtxt<'_>, trait_span: Span) -> P<Expr> {
1646-
cx.span_bug(trait_span, "static function in `derive`")
1647-
}
1648-
1649-
/// Fold the fields. `use_foldl` controls whether this is done
1650-
/// left-to-right (`true`) or right-to-left (`false`).
1651-
pub fn cs_fold<F>(
1652-
use_foldl: bool,
1653-
f: F,
1654-
base: P<Expr>,
1655-
enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>,
1656-
cx: &mut ExtCtxt<'_>,
1657-
trait_span: Span,
1658-
substructure: &Substructure<'_>,
1659-
) -> P<Expr>
1660-
where
1661-
F: FnMut(&mut ExtCtxt<'_>, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>,
1662-
{
1663-
match *substructure.fields {
1664-
EnumMatching(.., ref all_fields) | Struct(_, ref all_fields) => {
1665-
cs_fold_fields(use_foldl, f, base, cx, all_fields)
1666-
}
1667-
EnumNonMatchingCollapsed(..) => {
1668-
cs_fold_enumnonmatch(enum_nonmatch_f, cx, trait_span, substructure)
1669-
}
1670-
StaticEnum(..) | StaticStruct(..) => cs_fold_static(cx, trait_span),
1671-
}
1672-
}
1673-
16741657
/// Function to fold over fields, with three cases, to generate more efficient and concise code.
16751658
/// When the `substructure` has grouped fields, there are two cases:
16761659
/// Zero fields: call the base case function with `None` (like the usual base case of `cs_fold`).
@@ -1679,11 +1662,11 @@ where
16791662
/// fields.
16801663
/// When the `substructure` is an `EnumNonMatchingCollapsed`, the result of `enum_nonmatch_f`
16811664
/// is returned. Statics may not be folded over.
1682-
pub fn cs_fold1<F, B>(
1665+
pub fn cs_fold<F, B>(
16831666
use_foldl: bool,
1684-
f: F,
1667+
mut f: F,
16851668
mut b: B,
1686-
enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>,
1669+
mut enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>,
16871670
cx: &mut ExtCtxt<'_>,
16881671
trait_span: Span,
16891672
substructure: &Substructure<'_>,
@@ -1708,12 +1691,18 @@ where
17081691
(true, _) => (b(cx, None), &all_fields[..]),
17091692
};
17101693

1711-
cs_fold_fields(use_foldl, f, base, cx, rest)
1712-
}
1713-
EnumNonMatchingCollapsed(..) => {
1714-
cs_fold_enumnonmatch(enum_nonmatch_f, cx, trait_span, substructure)
1694+
if use_foldl {
1695+
rest.iter().fold(base, |old, field| {
1696+
f(cx, field.span, old, field.self_.clone(), &field.other)
1697+
})
1698+
} else {
1699+
rest.iter().rev().fold(base, |old, field| {
1700+
f(cx, field.span, old, field.self_.clone(), &field.other)
1701+
})
1702+
}
17151703
}
1716-
StaticEnum(..) | StaticStruct(..) => cs_fold_static(cx, trait_span),
1704+
EnumNonMatchingCollapsed(tuple) => enum_nonmatch_f(cx, trait_span, tuple),
1705+
StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"),
17171706
}
17181707
}
17191708

‎compiler/rustc_builtin_macros/src/deriving/hash.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*;
22
use crate::deriving::generic::*;
33
use crate::deriving::{self, path_std, pathvec_std};
44

5-
use rustc_ast::ptr::P;
6-
use rustc_ast::{Expr, MetaItem, Mutability};
5+
use rustc_ast::{MetaItem, Mutability};
76
use rustc_expand::base::{Annotatable, ExtCtxt};
87
use rustc_span::symbol::sym;
98
use rustc_span::Span;
@@ -45,7 +44,11 @@ pub fn expand_deriving_hash(
4544
hash_trait_def.expand(cx, mitem, item, push);
4645
}
4746

48-
fn hash_substructure(cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>) -> P<Expr> {
47+
fn hash_substructure(
48+
cx: &mut ExtCtxt<'_>,
49+
trait_span: Span,
50+
substr: &Substructure<'_>,
51+
) -> BlockOrExpr {
4952
let [state_expr] = substr.nonself_args else {
5053
cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`");
5154
};
@@ -81,6 +84,5 @@ fn hash_substructure(cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructu
8184
stmts.extend(
8285
fields.iter().map(|FieldInfo { ref self_, span, .. }| call_hash(*span, self_.clone())),
8386
);
84-
85-
cx.expr_block(cx.block(trait_span, stmts))
87+
BlockOrExpr::new_stmts(stmts)
8688
}

‎src/test/codegen/consts.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// CHECK: @STATIC = {{.*}}, align 4
1111

1212
// This checks the constants from inline_enum_const
13-
// CHECK: @alloc14 = {{.*}}, align 2
13+
// CHECK: @alloc12 = {{.*}}, align 2
1414

1515
// This checks the constants from {low,high}_align_const, they share the same
1616
// constant, but the alignment differs, so the higher one should be used

‎src/test/ui/deriving/deriving-all-codegen.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ struct Big {
3939
#[repr(packed)]
4040
struct Packed(u32);
4141

42+
// An empty enum.
43+
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
44+
enum Enum0 {}
45+
46+
// A single-variant enum.
47+
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
48+
enum Enum1 {
49+
Single { x: u32 }
50+
}
51+
4252
// A C-like, fieldless enum.
4353
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
4454
enum Fieldless {
@@ -66,3 +76,11 @@ enum Fielded {
6676
Y(bool),
6777
Z(Option<i32>),
6878
}
79+
80+
// A union. Most builtin traits are not derivable for unions.
81+
#[derive(Clone, Copy)]
82+
pub union Union {
83+
pub b: bool,
84+
pub u: u32,
85+
pub i: i32,
86+
}

‎src/test/ui/deriving/deriving-all-codegen.stdout

Lines changed: 394 additions & 361 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.