Skip to content

Commit bc001fa

Browse files
committed
Auto merge of #49881 - varkor:partialord-opt, r=Manishearth
Fix derive(PartialOrd) and optimise final field operation ```rust // Before (`lt` on 2-field struct) self.f1 < other.f1 || (!(other.f1 < self.f1) && (self.f2 < other.f2 || (!(other.f2 < self.f2) && (false) )) ) // After self.f1 < other.f1 || (!(other.f1 < self.f1) && self.f2 < other.f2 ) // Before (`le` on 2-field struct) self.f1 < other.f1 || (!(other.f1 < self.f1) && (self.f2 < other.f2 || (!(other.f2 < self.f2) && (true) )) ) // After self.f1 < other.f1 || (self.f1 == other.f1 && self.f2 <= other.f2 ) ``` (The big diff is mainly because of a past faulty rustfmt application that I corrected 😒) Fixes #49650 and fixes #49505.
2 parents d4d43e2 + 105c518 commit bc001fa

10 files changed

+245
-96
lines changed

src/etc/generate-deriving-span-tests.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def write_file(name, string):
122122

123123
for (trait, supers, errs) in [('Clone', [], 1),
124124
('PartialEq', [], 2),
125-
('PartialOrd', ['PartialEq'], 3),
125+
('PartialOrd', ['PartialEq'], 5),
126126
('Eq', ['PartialEq'], 1),
127127
('Ord', ['Eq', 'PartialOrd', 'PartialEq'], 1),
128128
('Debug', [], 1),

src/libsyntax_ext/deriving/cmp/partial_eq.rs

+34-27
Original file line numberDiff line numberDiff line change
@@ -26,41 +26,48 @@ pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt,
2626
push: &mut FnMut(Annotatable)) {
2727
// structures are equal if all fields are equal, and non equal, if
2828
// any fields are not equal or if the enum variants are different
29-
fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
30-
cs_fold(true, // use foldl
31-
|cx, span, subexpr, self_f, other_fs| {
29+
fn cs_op(cx: &mut ExtCtxt,
30+
span: Span,
31+
substr: &Substructure,
32+
op: BinOpKind,
33+
combiner: BinOpKind,
34+
base: bool)
35+
-> P<Expr>
36+
{
37+
let op = |cx: &mut ExtCtxt, span: Span, self_f: P<Expr>, other_fs: &[P<Expr>]| {
3238
let other_f = match (other_fs.len(), other_fs.get(0)) {
3339
(1, Some(o_f)) => o_f,
3440
_ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`"),
3541
};
3642

37-
let eq = cx.expr_binary(span, BinOpKind::Eq, self_f, other_f.clone());
43+
cx.expr_binary(span, op, self_f, other_f.clone())
44+
};
3845

39-
cx.expr_binary(span, BinOpKind::And, subexpr, eq)
40-
},
41-
cx.expr_bool(span, true),
42-
Box::new(|cx, span, _, _| cx.expr_bool(span, false)),
43-
cx,
44-
span,
45-
substr)
46+
cs_fold1(true, // use foldl
47+
|cx, span, subexpr, self_f, other_fs| {
48+
let eq = op(cx, span, self_f, other_fs);
49+
cx.expr_binary(span, combiner, subexpr, eq)
50+
},
51+
|cx, args| {
52+
match args {
53+
Some((span, self_f, other_fs)) => {
54+
// Special-case the base case to generate cleaner code.
55+
op(cx, span, self_f, other_fs)
56+
}
57+
None => cx.expr_bool(span, base),
58+
}
59+
},
60+
Box::new(|cx, span, _, _| cx.expr_bool(span, !base)),
61+
cx,
62+
span,
63+
substr)
4664
}
47-
fn cs_ne(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
48-
cs_fold(true, // use foldl
49-
|cx, span, subexpr, self_f, other_fs| {
50-
let other_f = match (other_fs.len(), other_fs.get(0)) {
51-
(1, Some(o_f)) => o_f,
52-
_ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`"),
53-
};
54-
55-
let eq = cx.expr_binary(span, BinOpKind::Ne, self_f, other_f.clone());
5665

57-
cx.expr_binary(span, BinOpKind::Or, subexpr, eq)
58-
},
59-
cx.expr_bool(span, false),
60-
Box::new(|cx, span, _, _| cx.expr_bool(span, true)),
61-
cx,
62-
span,
63-
substr)
66+
fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
67+
cs_op(cx, span, substr, BinOpKind::Eq, BinOpKind::And, true)
68+
}
69+
fn cs_ne(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
70+
cs_op(cx, span, substr, BinOpKind::Ne, BinOpKind::Or, false)
6471
}
6572

6673
macro_rules! md {

src/libsyntax_ext/deriving/cmp/partial_ord.rs

+79-47
Original file line numberDiff line numberDiff line change
@@ -190,54 +190,86 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<
190190

191191
/// Strict inequality.
192192
fn cs_op(less: bool, equal: bool, cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
193-
let op = if less { BinOpKind::Lt } else { BinOpKind::Gt };
194-
cs_fold(false, // need foldr,
195-
|cx, span, subexpr, self_f, other_fs| {
196-
// build up a series of chain ||'s and &&'s from the inside
197-
// out (hence foldr) to get lexical ordering, i.e. for op ==
198-
// `ast::lt`
199-
//
200-
// ```
201-
// self.f1 < other.f1 || (!(other.f1 < self.f1) &&
202-
// (self.f2 < other.f2 || (!(other.f2 < self.f2) &&
203-
// (false)
204-
// ))
205-
// )
206-
// ```
207-
//
208-
// The optimiser should remove the redundancy. We explicitly
209-
// get use the binops to avoid auto-deref dereferencing too many
210-
// layers of pointers, if the type includes pointers.
211-
//
212-
let other_f = match (other_fs.len(), other_fs.get(0)) {
213-
(1, Some(o_f)) => o_f,
214-
_ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
215-
};
216-
217-
let cmp = cx.expr_binary(span, op, self_f.clone(), other_f.clone());
193+
let strict_op = if less { BinOpKind::Lt } else { BinOpKind::Gt };
194+
cs_fold1(false, // need foldr,
195+
|cx, span, subexpr, self_f, other_fs| {
196+
// build up a series of chain ||'s and &&'s from the inside
197+
// out (hence foldr) to get lexical ordering, i.e. for op ==
198+
// `ast::lt`
199+
//
200+
// ```
201+
// self.f1 < other.f1 || (!(other.f1 < self.f1) &&
202+
// self.f2 < other.f2
203+
// )
204+
// ```
205+
//
206+
// and for op ==
207+
// `ast::le`
208+
//
209+
// ```
210+
// self.f1 < other.f1 || (self.f1 == other.f1 &&
211+
// self.f2 <= other.f2
212+
// )
213+
// ```
214+
//
215+
// The optimiser should remove the redundancy. We explicitly
216+
// get use the binops to avoid auto-deref dereferencing too many
217+
// layers of pointers, if the type includes pointers.
218+
//
219+
let other_f = match (other_fs.len(), other_fs.get(0)) {
220+
(1, Some(o_f)) => o_f,
221+
_ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
222+
};
218223

219-
let not_cmp = cx.expr_unary(span,
220-
ast::UnOp::Not,
221-
cx.expr_binary(span, op, other_f.clone(), self_f));
224+
let strict_ineq = cx.expr_binary(span, strict_op, self_f.clone(), other_f.clone());
222225

223-
let and = cx.expr_binary(span, BinOpKind::And, not_cmp, subexpr);
224-
cx.expr_binary(span, BinOpKind::Or, cmp, and)
225-
},
226-
cx.expr_bool(span, equal),
227-
Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
228-
if self_args.len() != 2 {
229-
cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
230-
} else {
231-
let op = match (less, equal) {
232-
(true, true) => LeOp,
233-
(true, false) => LtOp,
234-
(false, true) => GeOp,
235-
(false, false) => GtOp,
226+
let deleg_cmp = if !equal {
227+
cx.expr_unary(span,
228+
ast::UnOp::Not,
229+
cx.expr_binary(span, strict_op, other_f.clone(), self_f))
230+
} else {
231+
cx.expr_binary(span, BinOpKind::Eq, self_f, other_f.clone())
236232
};
237-
some_ordering_collapsed(cx, span, op, tag_tuple)
238-
}
239-
}),
240-
cx,
241-
span,
242-
substr)
233+
234+
let and = cx.expr_binary(span, BinOpKind::And, deleg_cmp, subexpr);
235+
cx.expr_binary(span, BinOpKind::Or, strict_ineq, and)
236+
},
237+
|cx, args| {
238+
match args {
239+
Some((span, self_f, other_fs)) => {
240+
// Special-case the base case to generate cleaner code with
241+
// fewer operations (e.g. `<=` instead of `<` and `==`).
242+
let other_f = match (other_fs.len(), other_fs.get(0)) {
243+
(1, Some(o_f)) => o_f,
244+
_ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
245+
};
246+
247+
let op = match (less, equal) {
248+
(false, false) => BinOpKind::Gt,
249+
(false, true) => BinOpKind::Ge,
250+
(true, false) => BinOpKind::Lt,
251+
(true, true) => BinOpKind::Le,
252+
};
253+
254+
cx.expr_binary(span, op, self_f, other_f.clone())
255+
}
256+
None => cx.expr_bool(span, equal)
257+
}
258+
},
259+
Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
260+
if self_args.len() != 2 {
261+
cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
262+
} else {
263+
let op = match (less, equal) {
264+
(false, false) => GtOp,
265+
(false, true) => GeOp,
266+
(true, false) => LtOp,
267+
(true, true) => LeOp,
268+
};
269+
some_ordering_collapsed(cx, span, op, tag_tuple)
270+
}
271+
}),
272+
cx,
273+
span,
274+
substr)
243275
}

src/libsyntax_ext/deriving/generic/mod.rs

+99-17
Original file line numberDiff line numberDiff line change
@@ -1680,12 +1680,55 @@ impl<'a> TraitDef<'a> {
16801680

16811681
// helpful premade recipes
16821682

1683+
pub fn cs_fold_fields<'a, F>(use_foldl: bool,
1684+
mut f: F,
1685+
base: P<Expr>,
1686+
cx: &mut ExtCtxt,
1687+
all_fields: &[FieldInfo<'a>])
1688+
-> P<Expr>
1689+
where F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>
1690+
{
1691+
if use_foldl {
1692+
all_fields.iter().fold(base, |old, field| {
1693+
f(cx, field.span, old, field.self_.clone(), &field.other)
1694+
})
1695+
} else {
1696+
all_fields.iter().rev().fold(base, |old, field| {
1697+
f(cx, field.span, old, field.self_.clone(), &field.other)
1698+
})
1699+
}
1700+
}
1701+
1702+
pub fn cs_fold_enumnonmatch(mut enum_nonmatch_f: EnumNonMatchCollapsedFunc,
1703+
cx: &mut ExtCtxt,
1704+
trait_span: Span,
1705+
substructure: &Substructure)
1706+
-> P<Expr>
1707+
{
1708+
match *substructure.fields {
1709+
EnumNonMatchingCollapsed(ref all_args, _, tuple) => {
1710+
enum_nonmatch_f(cx,
1711+
trait_span,
1712+
(&all_args[..], tuple),
1713+
substructure.nonself_args)
1714+
}
1715+
_ => cx.span_bug(trait_span, "cs_fold_enumnonmatch expected an EnumNonMatchingCollapsed")
1716+
}
1717+
}
1718+
1719+
pub fn cs_fold_static(cx: &mut ExtCtxt,
1720+
trait_span: Span)
1721+
-> P<Expr>
1722+
{
1723+
cx.span_bug(trait_span, "static function in `derive`")
1724+
}
1725+
16831726
/// Fold the fields. `use_foldl` controls whether this is done
16841727
/// left-to-right (`true`) or right-to-left (`false`).
16851728
pub fn cs_fold<F>(use_foldl: bool,
1686-
mut f: F,
1729+
f: F,
16871730
base: P<Expr>,
1688-
mut enum_nonmatch_f: EnumNonMatchCollapsedFunc,
1731+
enum_nonmatch_f: EnumNonMatchCollapsedFunc,
16891732
cx: &mut ExtCtxt,
16901733
trait_span: Span,
16911734
substructure: &Substructure)
@@ -1695,26 +1738,65 @@ pub fn cs_fold<F>(use_foldl: bool,
16951738
match *substructure.fields {
16961739
EnumMatching(.., ref all_fields) |
16971740
Struct(_, ref all_fields) => {
1698-
if use_foldl {
1699-
all_fields.iter().fold(base, |old, field| {
1700-
f(cx, field.span, old, field.self_.clone(), &field.other)
1701-
})
1702-
} else {
1703-
all_fields.iter().rev().fold(base, |old, field| {
1704-
f(cx, field.span, old, field.self_.clone(), &field.other)
1705-
})
1706-
}
1741+
cs_fold_fields(use_foldl, f, base, cx, all_fields)
17071742
}
1708-
EnumNonMatchingCollapsed(ref all_args, _, tuple) => {
1709-
enum_nonmatch_f(cx,
1710-
trait_span,
1711-
(&all_args[..], tuple),
1712-
substructure.nonself_args)
1743+
EnumNonMatchingCollapsed(..) => {
1744+
cs_fold_enumnonmatch(enum_nonmatch_f, cx, trait_span, substructure)
1745+
}
1746+
StaticEnum(..) | StaticStruct(..) => {
1747+
cs_fold_static(cx, trait_span)
17131748
}
1714-
StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"),
17151749
}
17161750
}
17171751

1752+
/// Function to fold over fields, with three cases, to generate more efficient and concise code.
1753+
/// When the `substructure` has grouped fields, there are two cases:
1754+
/// Zero fields: call the base case function with None (like the usual base case of `cs_fold`).
1755+
/// One or more fields: call the base case function on the first value (which depends on
1756+
/// `use_fold`), and use that as the base case. Then perform `cs_fold` on the remainder of the
1757+
/// fields.
1758+
/// When the `substructure` is a `EnumNonMatchingCollapsed`, the result of `enum_nonmatch_f`
1759+
/// is returned. Statics may not be folded over.
1760+
/// See `cs_op` in `partial_ord.rs` for a model example.
1761+
pub fn cs_fold1<F, B>(use_foldl: bool,
1762+
f: F,
1763+
mut b: B,
1764+
enum_nonmatch_f: EnumNonMatchCollapsedFunc,
1765+
cx: &mut ExtCtxt,
1766+
trait_span: Span,
1767+
substructure: &Substructure)
1768+
-> P<Expr>
1769+
where F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>,
1770+
B: FnMut(&mut ExtCtxt, Option<(Span, P<Expr>, &[P<Expr>])>) -> P<Expr>
1771+
{
1772+
match *substructure.fields {
1773+
EnumMatching(.., ref all_fields) |
1774+
Struct(_, ref all_fields) => {
1775+
let (base, all_fields) = match (all_fields.is_empty(), use_foldl) {
1776+
(false, true) => {
1777+
let field = &all_fields[0];
1778+
let args = (field.span, field.self_.clone(), &field.other[..]);
1779+
(b(cx, Some(args)), &all_fields[1..])
1780+
}
1781+
(false, false) => {
1782+
let idx = all_fields.len() - 1;
1783+
let field = &all_fields[idx];
1784+
let args = (field.span, field.self_.clone(), &field.other[..]);
1785+
(b(cx, Some(args)), &all_fields[..idx])
1786+
}
1787+
(true, _) => (b(cx, None), &all_fields[..])
1788+
};
1789+
1790+
cs_fold_fields(use_foldl, f, base, cx, all_fields)
1791+
}
1792+
EnumNonMatchingCollapsed(..) => {
1793+
cs_fold_enumnonmatch(enum_nonmatch_f, cx, trait_span, substructure)
1794+
}
1795+
StaticEnum(..) | StaticStruct(..) => {
1796+
cs_fold_static(cx, trait_span)
1797+
}
1798+
}
1799+
}
17181800

17191801
/// Call the method that is being derived on all the fields, and then
17201802
/// process the collected results. i.e.

src/test/compile-fail/derives-span-PartialOrd-enum-struct-variant.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -19,6 +19,8 @@ enum Enum {
1919
x: Error //~ ERROR
2020
//~^ ERROR
2121
//~^^ ERROR
22+
//~^^^ ERROR
23+
//~^^^^ ERROR
2224
}
2325
}
2426

src/test/compile-fail/derives-span-PartialOrd-enum.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -19,6 +19,8 @@ enum Enum {
1919
Error //~ ERROR
2020
//~^ ERROR
2121
//~^^ ERROR
22+
//~^^^ ERROR
23+
//~^^^^ ERROR
2224
)
2325
}
2426

0 commit comments

Comments
 (0)