]>
Commit | Line | Data |
---|---|---|
d9579d0f AL |
1 | // Copyright 2013 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | pub use self::OrderingOp::*; | |
12 | ||
ff7c6d11 | 13 | use deriving::{path_local, pathvec_std, path_std}; |
9cc50fc6 SL |
14 | use deriving::generic::*; |
15 | use deriving::generic::ty::*; | |
16 | ||
5bcae85e SL |
17 | use syntax::ast::{self, BinOpKind, Expr, MetaItem}; |
18 | use syntax::ext::base::{Annotatable, ExtCtxt}; | |
9cc50fc6 | 19 | use syntax::ext::build::AstBuilder; |
9cc50fc6 | 20 | use syntax::ptr::P; |
476ff2be | 21 | use syntax::symbol::Symbol; |
3157f602 | 22 | use syntax_pos::Span; |
d9579d0f AL |
23 | |
24 | pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt, | |
25 | span: Span, | |
26 | mitem: &MetaItem, | |
62682a34 | 27 | item: &Annotatable, |
5bcae85e | 28 | push: &mut FnMut(Annotatable)) { |
d9579d0f AL |
29 | macro_rules! md { |
30 | ($name:expr, $op:expr, $equal:expr) => { { | |
476ff2be | 31 | let inline = cx.meta_word(span, Symbol::intern("inline")); |
c30ab7b3 | 32 | let attrs = vec![cx.attribute(span, inline)]; |
d9579d0f AL |
33 | MethodDef { |
34 | name: $name, | |
35 | generics: LifetimeBounds::empty(), | |
36 | explicit_self: borrowed_explicit_self(), | |
83c7162d | 37 | args: vec![(borrowed_self(), "other")], |
d9579d0f AL |
38 | ret_ty: Literal(path_local!(bool)), |
39 | attributes: attrs, | |
62682a34 | 40 | is_unsafe: false, |
a7813a04 | 41 | unify_fieldless_variants: true, |
d9579d0f AL |
42 | combine_substructure: combine_substructure(Box::new(|cx, span, substr| { |
43 | cs_op($op, $equal, cx, span, substr) | |
44 | })) | |
45 | } | |
46 | } } | |
47 | } | |
48 | ||
ff7c6d11 XL |
49 | let ordering_ty = Literal(path_std!(cx, cmp::Ordering)); |
50 | let ret_ty = Literal(Path::new_(pathvec_std!(cx, option::Option), | |
d9579d0f AL |
51 | None, |
52 | vec![Box::new(ordering_ty)], | |
ff7c6d11 | 53 | PathKind::Std)); |
d9579d0f | 54 | |
476ff2be | 55 | let inline = cx.meta_word(span, Symbol::intern("inline")); |
5bcae85e | 56 | let attrs = vec![cx.attribute(span, inline)]; |
d9579d0f AL |
57 | |
58 | let partial_cmp_def = MethodDef { | |
59 | name: "partial_cmp", | |
60 | generics: LifetimeBounds::empty(), | |
61 | explicit_self: borrowed_explicit_self(), | |
83c7162d | 62 | args: vec![(borrowed_self(), "other")], |
3b2f2976 | 63 | ret_ty, |
d9579d0f | 64 | attributes: attrs, |
62682a34 | 65 | is_unsafe: false, |
a7813a04 | 66 | unify_fieldless_variants: true, |
d9579d0f AL |
67 | combine_substructure: combine_substructure(Box::new(|cx, span, substr| { |
68 | cs_partial_cmp(cx, span, substr) | |
5bcae85e | 69 | })), |
d9579d0f AL |
70 | }; |
71 | ||
54a0048b SL |
72 | // avoid defining extra methods if we can |
73 | // c-like enums, enums without any fields and structs without fields | |
74 | // can safely define only `partial_cmp`. | |
75 | let methods = if is_type_without_fields(item) { | |
76 | vec![partial_cmp_def] | |
77 | } else { | |
5bcae85e SL |
78 | vec![partial_cmp_def, |
79 | md!("lt", true, false), | |
80 | md!("le", true, true), | |
81 | md!("gt", false, false), | |
82 | md!("ge", false, true)] | |
54a0048b SL |
83 | }; |
84 | ||
d9579d0f | 85 | let trait_def = TraitDef { |
3b2f2976 | 86 | span, |
d9579d0f | 87 | attributes: vec![], |
ff7c6d11 | 88 | path: path_std!(cx, cmp::PartialOrd), |
d9579d0f AL |
89 | additional_bounds: vec![], |
90 | generics: LifetimeBounds::empty(), | |
e9174d1e | 91 | is_unsafe: false, |
9e0c209e | 92 | supports_unions: false, |
3b2f2976 | 93 | methods, |
d9579d0f AL |
94 | associated_types: Vec::new(), |
95 | }; | |
62682a34 | 96 | trait_def.expand(cx, mitem, item, push) |
d9579d0f AL |
97 | } |
98 | ||
99 | #[derive(Copy, Clone)] | |
100 | pub enum OrderingOp { | |
5bcae85e SL |
101 | PartialCmpOp, |
102 | LtOp, | |
103 | LeOp, | |
104 | GtOp, | |
105 | GeOp, | |
d9579d0f AL |
106 | } |
107 | ||
108 | pub fn some_ordering_collapsed(cx: &mut ExtCtxt, | |
109 | span: Span, | |
110 | op: OrderingOp, | |
5bcae85e SL |
111 | self_arg_tags: &[ast::Ident]) |
112 | -> P<ast::Expr> { | |
d9579d0f AL |
113 | let lft = cx.expr_ident(span, self_arg_tags[0]); |
114 | let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1])); | |
115 | let op_str = match op { | |
116 | PartialCmpOp => "partial_cmp", | |
5bcae85e SL |
117 | LtOp => "lt", |
118 | LeOp => "le", | |
119 | GtOp => "gt", | |
120 | GeOp => "ge", | |
d9579d0f AL |
121 | }; |
122 | cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt]) | |
123 | } | |
124 | ||
5bcae85e | 125 | pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> { |
83c7162d | 126 | let test_id = cx.ident_of("cmp").gensym(); |
5bcae85e | 127 | let ordering = cx.path_global(span, cx.std_path(&["cmp", "Ordering", "Equal"])); |
54a0048b SL |
128 | let ordering_expr = cx.expr_path(ordering.clone()); |
129 | let equals_expr = cx.expr_some(span, ordering_expr); | |
d9579d0f | 130 | |
e9174d1e | 131 | let partial_cmp_path = cx.std_path(&["cmp", "PartialOrd", "partial_cmp"]); |
d9579d0f | 132 | |
5bcae85e SL |
133 | // Builds: |
134 | // | |
135 | // match ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1) { | |
136 | // ::std::option::Option::Some(::std::cmp::Ordering::Equal) => | |
137 | // match ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2) { | |
138 | // ::std::option::Option::Some(::std::cmp::Ordering::Equal) => { | |
139 | // ... | |
140 | // } | |
83c7162d | 141 | // cmp => cmp |
5bcae85e | 142 | // }, |
83c7162d | 143 | // cmp => cmp |
5bcae85e SL |
144 | // } |
145 | // | |
146 | cs_fold(// foldr nests the if-elses correctly, leaving the first field | |
147 | // as the outermost one, and the last as the innermost. | |
148 | false, | |
149 | |cx, span, old, self_f, other_fs| { | |
94b46f34 XL |
150 | // match new { |
151 | // Some(::std::cmp::Ordering::Equal) => old, | |
152 | // cmp => cmp | |
153 | // } | |
5bcae85e | 154 | |
94b46f34 XL |
155 | let new = { |
156 | let other_f = match (other_fs.len(), other_fs.get(0)) { | |
157 | (1, Some(o_f)) => o_f, | |
158 | _ => { | |
159 | cx.span_bug(span, | |
160 | "not exactly 2 arguments in `derive(PartialOrd)`") | |
161 | } | |
162 | }; | |
5bcae85e | 163 | |
94b46f34 XL |
164 | let args = vec![ |
165 | cx.expr_addr_of(span, self_f), | |
166 | cx.expr_addr_of(span, other_f.clone()), | |
167 | ]; | |
d9579d0f | 168 | |
94b46f34 XL |
169 | cx.expr_call_global(span, partial_cmp_path.clone(), args) |
170 | }; | |
5bcae85e | 171 | |
94b46f34 XL |
172 | let eq_arm = cx.arm(span, |
173 | vec![cx.pat_some(span, cx.pat_path(span, ordering.clone()))], | |
174 | old); | |
175 | let neq_arm = cx.arm(span, | |
176 | vec![cx.pat_ident(span, test_id)], | |
177 | cx.expr_ident(span, test_id)); | |
5bcae85e | 178 | |
94b46f34 XL |
179 | cx.expr_match(span, new, vec![eq_arm, neq_arm]) |
180 | }, | |
5bcae85e SL |
181 | equals_expr.clone(), |
182 | Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| { | |
183 | if self_args.len() != 2 { | |
184 | cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") | |
185 | } else { | |
186 | some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple) | |
187 | } | |
188 | }), | |
189 | cx, | |
190 | span, | |
191 | substr) | |
d9579d0f AL |
192 | } |
193 | ||
194 | /// Strict inequality. | |
94b46f34 XL |
195 | fn cs_op(less: bool, |
196 | inclusive: bool, | |
197 | cx: &mut ExtCtxt, | |
198 | span: Span, | |
199 | substr: &Substructure) -> P<Expr> { | |
200 | let ordering_path = |cx: &mut ExtCtxt, name: &str| { | |
201 | cx.expr_path(cx.path_global(span, cx.std_path(&["cmp", "Ordering", name]))) | |
202 | }; | |
203 | ||
204 | let par_cmp = |cx: &mut ExtCtxt, span, self_f: P<Expr>, other_fs: &[P<Expr>], default| { | |
205 | let other_f = match (other_fs.len(), other_fs.get(0)) { | |
206 | (1, Some(o_f)) => o_f, | |
207 | _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"), | |
208 | }; | |
209 | ||
210 | // `PartialOrd::partial_cmp(self.fi, other.fi)` | |
211 | let cmp_path = cx.expr_path(cx.path_global(span, cx.std_path(&["cmp", | |
212 | "PartialOrd", | |
213 | "partial_cmp"]))); | |
214 | let cmp = cx.expr_call(span, | |
215 | cmp_path, | |
216 | vec![cx.expr_addr_of(span, self_f), | |
217 | cx.expr_addr_of(span, other_f.clone())]); | |
218 | ||
219 | let default = ordering_path(cx, default); | |
220 | // `Option::unwrap_or(_, Ordering::Equal)` | |
221 | let unwrap_path = cx.expr_path(cx.path_global(span, cx.std_path(&["option", | |
222 | "Option", | |
223 | "unwrap_or"]))); | |
224 | cx.expr_call(span, unwrap_path, vec![cmp, default]) | |
225 | }; | |
226 | ||
227 | let fold = cs_fold1(false, // need foldr | |
83c7162d | 228 | |cx, span, subexpr, self_f, other_fs| { |
94b46f34 | 229 | // build up a series of `partial_cmp`s from the inside |
83c7162d XL |
230 | // out (hence foldr) to get lexical ordering, i.e. for op == |
231 | // `ast::lt` | |
232 | // | |
233 | // ``` | |
94b46f34 XL |
234 | // Ordering::then_with( |
235 | // Option::unwrap_or( | |
236 | // PartialOrd::partial_cmp(self.f1, other.f1), Ordering::Equal) | |
237 | // ), | |
238 | // Option::unwrap_or( | |
239 | // PartialOrd::partial_cmp(self.f2, other.f2), Ordering::Greater) | |
240 | // ) | |
83c7162d | 241 | // ) |
94b46f34 | 242 | // == Ordering::Less |
83c7162d XL |
243 | // ``` |
244 | // | |
245 | // and for op == | |
246 | // `ast::le` | |
247 | // | |
248 | // ``` | |
94b46f34 XL |
249 | // Ordering::then_with( |
250 | // Option::unwrap_or( | |
251 | // PartialOrd::partial_cmp(self.f1, other.f1), Ordering::Equal) | |
252 | // ), | |
253 | // Option::unwrap_or( | |
254 | // PartialOrd::partial_cmp(self.f2, other.f2), Ordering::Greater) | |
255 | // ) | |
83c7162d | 256 | // ) |
94b46f34 | 257 | // != Ordering::Greater |
83c7162d XL |
258 | // ``` |
259 | // | |
260 | // The optimiser should remove the redundancy. We explicitly | |
261 | // get use the binops to avoid auto-deref dereferencing too many | |
262 | // layers of pointers, if the type includes pointers. | |
5bcae85e | 263 | |
94b46f34 XL |
264 | // `Option::unwrap_or(PartialOrd::partial_cmp(self.fi, other.fi), Ordering::Equal)` |
265 | let par_cmp = par_cmp(cx, span, self_f, other_fs, "Equal"); | |
5bcae85e | 266 | |
94b46f34 XL |
267 | // `Ordering::then_with(Option::unwrap_or(..), ..)` |
268 | let then_with_path = cx.expr_path(cx.path_global(span, | |
269 | cx.std_path(&["cmp", | |
270 | "Ordering", | |
271 | "then_with"]))); | |
272 | cx.expr_call(span, then_with_path, vec![par_cmp, cx.lambda0(span, subexpr)]) | |
83c7162d XL |
273 | }, |
274 | |cx, args| { | |
275 | match args { | |
276 | Some((span, self_f, other_fs)) => { | |
94b46f34 XL |
277 | let opposite = if less { "Greater" } else { "Less" }; |
278 | par_cmp(cx, span, self_f, other_fs, opposite) | |
279 | }, | |
280 | None => cx.expr_bool(span, inclusive) | |
83c7162d XL |
281 | } |
282 | }, | |
283 | Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| { | |
284 | if self_args.len() != 2 { | |
285 | cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") | |
286 | } else { | |
94b46f34 | 287 | let op = match (less, inclusive) { |
83c7162d XL |
288 | (false, false) => GtOp, |
289 | (false, true) => GeOp, | |
290 | (true, false) => LtOp, | |
291 | (true, true) => LeOp, | |
292 | }; | |
293 | some_ordering_collapsed(cx, span, op, tag_tuple) | |
294 | } | |
295 | }), | |
296 | cx, | |
297 | span, | |
94b46f34 XL |
298 | substr); |
299 | ||
300 | match *substr.fields { | |
301 | EnumMatching(.., ref all_fields) | | |
302 | Struct(.., ref all_fields) if !all_fields.is_empty() => { | |
303 | let ordering = ordering_path(cx, if less ^ inclusive { "Less" } else { "Greater" }); | |
304 | let comp_op = if inclusive { BinOpKind::Ne } else { BinOpKind::Eq }; | |
305 | ||
306 | cx.expr_binary(span, comp_op, fold, ordering) | |
307 | } | |
308 | _ => fold | |
309 | } | |
d9579d0f | 310 | } |