Skip to content

Commit ebf7281

Browse files
committed
syntax: rewrite deriving(Ord) to not require Eq.
lt and gt are implement directly in terms of the corresponding method on their elements, and le and ge are the negations of these.
1 parent 6d5beda commit ebf7281

File tree

1 file changed

+59
-57
lines changed
  • src/libsyntax/ext/deriving/cmp

1 file changed

+59
-57
lines changed

src/libsyntax/ext/deriving/cmp/ord.rs

Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use core::prelude::*;
1212

13+
use ast;
1314
use ast::{meta_item, item, expr};
1415
use codemap::span;
1516
use ext::base::ExtCtxt;
@@ -21,104 +22,105 @@ pub fn expand_deriving_ord(cx: @ExtCtxt,
2122
mitem: @meta_item,
2223
in_items: ~[@item]) -> ~[@item] {
2324
macro_rules! md (
24-
($name:expr, $less:expr, $equal:expr) => {
25+
($name:expr, $func:expr, $op:expr) => {
2526
MethodDef {
2627
name: $name,
2728
generics: LifetimeBounds::empty(),
2829
explicit_self: borrowed_explicit_self(),
2930
args: ~[borrowed_self()],
3031
ret_ty: Literal(Path::new(~["bool"])),
3132
const_nonmatching: false,
32-
combine_substructure: |cx, span, substr|
33-
cs_ord($less, $equal, cx, span, substr)
33+
combine_substructure: |cx, span, substr| $func($op, cx, span, substr)
3434
}
3535
}
3636
);
3737

38-
39-
4038
let trait_def = TraitDef {
4139
path: Path::new(~["std", "cmp", "Ord"]),
42-
// XXX: Ord doesn't imply Eq yet
43-
additional_bounds: ~[Literal(Path::new(~["std", "cmp", "Eq"]))],
40+
additional_bounds: ~[],
4441
generics: LifetimeBounds::empty(),
4542
methods: ~[
46-
md!("lt", true, false),
47-
md!("le", true, true),
48-
md!("gt", false, false),
49-
md!("ge", false, true)
43+
md!("lt", cs_strict, true),
44+
md!("le", cs_nonstrict, true), // inverse operation
45+
md!("gt", cs_strict, false),
46+
md!("ge", cs_nonstrict, false)
5047
]
5148
};
5249
trait_def.expand(cx, span, mitem, in_items)
5350
}
5451

55-
/// `less`: is this `lt` or `le`? `equal`: is this `le` or `ge`?
56-
fn cs_ord(less: bool, equal: bool,
57-
cx: @ExtCtxt, span: span,
58-
substr: &Substructure) -> @expr {
59-
let binop = if less {
60-
cx.ident_of("lt")
61-
} else {
62-
cx.ident_of("gt")
63-
};
64-
let base = cx.expr_bool(span, equal);
65-
52+
/// Strict inequality.
53+
fn cs_strict(less: bool, cx: @ExtCtxt, span: span, substr: &Substructure) -> @expr {
54+
let op = if less {ast::lt} else {ast::gt};
6655
cs_fold(
6756
false, // need foldr,
6857
|cx, span, subexpr, self_f, other_fs| {
6958
/*
70-
71-
build up a series of nested ifs from the inside out to get
72-
lexical ordering (hence foldr), i.e.
59+
build up a series of chain ||'s and &&'s from the inside
60+
out (hence foldr) to get lexical ordering, i.e. for op ==
61+
`ast::lt`
7362
7463
```
75-
if self.f1 `binop` other.f1 {
76-
true
77-
} else if self.f1 == other.f1 {
78-
if self.f2 `binop` other.f2 {
79-
true
80-
} else if self.f2 == other.f2 {
81-
`equal`
82-
} else {
83-
false
84-
}
85-
} else {
86-
false
87-
}
64+
self.f1 < other.f1 || (!(other.f1 < self.f1) &&
65+
(self.f2 < other.f2 || (!(other.f2 < self.f2) &&
66+
(false)
67+
))
68+
)
8869
```
8970
90-
The inner "`equal`" case is only reached if the two
91-
items have all fields equal.
71+
The optimiser should remove the redundancy. We explicitly
72+
get use the binops to avoid auto-deref derefencing too many
73+
layers of pointers, if the type includes pointers.
9274
*/
93-
if other_fs.len() != 1 {
94-
cx.span_bug(span, "Not exactly 2 arguments in `deriving(Ord)`");
95-
}
75+
let other_f = match other_fs {
76+
[o_f] => o_f,
77+
_ => cx.span_bug(span, "Not exactly 2 arguments in `deriving(Ord)`")
78+
};
79+
80+
let cmp = cx.expr_binary(span, op,
81+
cx.expr_deref(span, self_f),
82+
cx.expr_deref(span, other_f));
9683

97-
let cmp = cx.expr_method_call(span,
98-
self_f, cx.ident_of("eq"), other_fs.to_owned());
99-
let elseif = cx.expr_if(span, cmp,
100-
subexpr, Some(cx.expr_bool(span, false)));
84+
let not_cmp = cx.expr_binary(span, op,
85+
cx.expr_deref(span, other_f),
86+
cx.expr_deref(span, self_f));
87+
let not_cmp = cx.expr_unary(span, ast::not, not_cmp);
10188

102-
let cmp = cx.expr_method_call(span,
103-
self_f, binop, other_fs.to_owned());
104-
cx.expr_if(span, cmp,
105-
cx.expr_bool(span, true), Some(elseif))
89+
let and = cx.expr_binary(span, ast::and,
90+
not_cmp, subexpr);
91+
cx.expr_binary(span, ast::or, cmp, and)
10692
},
107-
base,
93+
cx.expr_bool(span, false),
10894
|cx, span, args, _| {
10995
// nonmatching enums, order by the order the variants are
11096
// written
11197
match args {
11298
[(self_var, _, _),
11399
(other_var, _, _)] =>
114100
cx.expr_bool(span,
115-
if less {
116-
self_var < other_var
117-
} else {
118-
self_var > other_var
119-
}),
101+
if less {
102+
self_var < other_var
103+
} else {
104+
self_var > other_var
105+
}),
120106
_ => cx.span_bug(span, "Not exactly 2 arguments in `deriving(Ord)`")
121107
}
122108
},
123109
cx, span, substr)
124110
}
111+
112+
fn cs_nonstrict(less: bool, cx: @ExtCtxt, span: span, substr: &Substructure) -> @expr {
113+
// Example: ge becomes !(*self < *other), le becomes !(*self > *other)
114+
115+
let inverse_op = if less {ast::gt} else {ast::lt};
116+
match substr.self_args {
117+
[self_, other] => {
118+
let inverse_cmp = cx.expr_binary(span, inverse_op,
119+
cx.expr_deref(span, self_),
120+
cx.expr_deref(span, other));
121+
122+
cx.expr_unary(span, ast::not, inverse_cmp)
123+
}
124+
_ => cx.span_bug(span, "Not exactly 2 arguments in `deriving(Ord)`")
125+
}
126+
}

0 commit comments

Comments
 (0)