]>
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 | ||
9cc50fc6 SL |
13 | use deriving::generic::*; |
14 | use deriving::generic::ty::*; | |
15 | ||
7453a54e | 16 | use syntax::ast::{MetaItem, Expr, BinOpKind, self}; |
9cc50fc6 SL |
17 | use syntax::codemap::Span; |
18 | use syntax::ext::base::{ExtCtxt, Annotatable}; | |
19 | use syntax::ext::build::AstBuilder; | |
20 | use syntax::parse::token::InternedString; | |
21 | use syntax::ptr::P; | |
d9579d0f AL |
22 | |
23 | pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt, | |
24 | span: Span, | |
25 | mitem: &MetaItem, | |
62682a34 | 26 | item: &Annotatable, |
d9579d0f AL |
27 | push: &mut FnMut(Annotatable)) |
28 | { | |
29 | macro_rules! md { | |
30 | ($name:expr, $op:expr, $equal:expr) => { { | |
31 | let inline = cx.meta_word(span, InternedString::new("inline")); | |
32 | let attrs = vec!(cx.attribute(span, inline)); | |
33 | MethodDef { | |
34 | name: $name, | |
35 | generics: LifetimeBounds::empty(), | |
36 | explicit_self: borrowed_explicit_self(), | |
37 | args: vec!(borrowed_self()), | |
38 | ret_ty: Literal(path_local!(bool)), | |
39 | attributes: attrs, | |
62682a34 | 40 | is_unsafe: false, |
d9579d0f AL |
41 | combine_substructure: combine_substructure(Box::new(|cx, span, substr| { |
42 | cs_op($op, $equal, cx, span, substr) | |
43 | })) | |
44 | } | |
45 | } } | |
46 | } | |
47 | ||
48 | let ordering_ty = Literal(path_std!(cx, core::cmp::Ordering)); | |
49 | let ret_ty = Literal(Path::new_(pathvec_std!(cx, core::option::Option), | |
50 | None, | |
51 | vec![Box::new(ordering_ty)], | |
52 | true)); | |
53 | ||
54 | let inline = cx.meta_word(span, InternedString::new("inline")); | |
55 | let attrs = vec!(cx.attribute(span, inline)); | |
56 | ||
57 | let partial_cmp_def = MethodDef { | |
58 | name: "partial_cmp", | |
59 | generics: LifetimeBounds::empty(), | |
60 | explicit_self: borrowed_explicit_self(), | |
61 | args: vec![borrowed_self()], | |
62 | ret_ty: ret_ty, | |
63 | attributes: attrs, | |
62682a34 | 64 | is_unsafe: false, |
d9579d0f AL |
65 | combine_substructure: combine_substructure(Box::new(|cx, span, substr| { |
66 | cs_partial_cmp(cx, span, substr) | |
67 | })) | |
68 | }; | |
69 | ||
54a0048b SL |
70 | // avoid defining extra methods if we can |
71 | // c-like enums, enums without any fields and structs without fields | |
72 | // can safely define only `partial_cmp`. | |
73 | let methods = if is_type_without_fields(item) { | |
74 | vec![partial_cmp_def] | |
75 | } else { | |
76 | vec![ | |
77 | partial_cmp_def, | |
78 | md!("lt", true, false), | |
79 | md!("le", true, true), | |
80 | md!("gt", false, false), | |
81 | md!("ge", false, true) | |
82 | ] | |
83 | }; | |
84 | ||
d9579d0f AL |
85 | let trait_def = TraitDef { |
86 | span: span, | |
87 | attributes: vec![], | |
88 | path: path_std!(cx, core::cmp::PartialOrd), | |
89 | additional_bounds: vec![], | |
90 | generics: LifetimeBounds::empty(), | |
e9174d1e | 91 | is_unsafe: false, |
54a0048b | 92 | methods: methods, |
d9579d0f AL |
93 | associated_types: Vec::new(), |
94 | }; | |
62682a34 | 95 | trait_def.expand(cx, mitem, item, push) |
d9579d0f AL |
96 | } |
97 | ||
98 | #[derive(Copy, Clone)] | |
99 | pub enum OrderingOp { | |
100 | PartialCmpOp, LtOp, LeOp, GtOp, GeOp, | |
101 | } | |
102 | ||
103 | pub fn some_ordering_collapsed(cx: &mut ExtCtxt, | |
104 | span: Span, | |
105 | op: OrderingOp, | |
106 | self_arg_tags: &[ast::Ident]) -> P<ast::Expr> { | |
107 | let lft = cx.expr_ident(span, self_arg_tags[0]); | |
108 | let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1])); | |
109 | let op_str = match op { | |
110 | PartialCmpOp => "partial_cmp", | |
111 | LtOp => "lt", LeOp => "le", | |
112 | GtOp => "gt", GeOp => "ge", | |
113 | }; | |
114 | cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt]) | |
115 | } | |
116 | ||
117 | pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span, | |
118 | substr: &Substructure) -> P<Expr> { | |
54a0048b | 119 | let test_id = cx.ident_of("__cmp"); |
d9579d0f | 120 | let ordering = cx.path_global(span, |
e9174d1e | 121 | cx.std_path(&["cmp", "Ordering", "Equal"])); |
54a0048b SL |
122 | let ordering_expr = cx.expr_path(ordering.clone()); |
123 | let equals_expr = cx.expr_some(span, ordering_expr); | |
d9579d0f | 124 | |
e9174d1e | 125 | let partial_cmp_path = cx.std_path(&["cmp", "PartialOrd", "partial_cmp"]); |
d9579d0f AL |
126 | |
127 | /* | |
128 | Builds: | |
129 | ||
54a0048b SL |
130 | match ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1) { |
131 | ::std::option::Option::Some(::std::cmp::Ordering::Equal) => | |
132 | match ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2) { | |
133 | ::std::option::Option::Some(::std::cmp::Ordering::Equal) => { | |
134 | ... | |
135 | } | |
136 | __cmp => __cmp | |
137 | }, | |
138 | __cmp => __cmp | |
d9579d0f | 139 | } |
d9579d0f AL |
140 | */ |
141 | cs_fold( | |
142 | // foldr nests the if-elses correctly, leaving the first field | |
143 | // as the outermost one, and the last as the innermost. | |
144 | false, | |
145 | |cx, span, old, self_f, other_fs| { | |
54a0048b SL |
146 | // match new { |
147 | // Some(::std::cmp::Ordering::Equal) => old, | |
148 | // __cmp => __cmp | |
d9579d0f AL |
149 | // } |
150 | ||
151 | let new = { | |
152 | let other_f = match (other_fs.len(), other_fs.get(0)) { | |
153 | (1, Some(o_f)) => o_f, | |
154 | _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"), | |
155 | }; | |
156 | ||
157 | let args = vec![ | |
158 | cx.expr_addr_of(span, self_f), | |
159 | cx.expr_addr_of(span, other_f.clone()), | |
160 | ]; | |
161 | ||
162 | cx.expr_call_global(span, partial_cmp_path.clone(), args) | |
163 | }; | |
164 | ||
54a0048b SL |
165 | let eq_arm = cx.arm(span, |
166 | vec![cx.pat_some(span, | |
167 | cx.pat_enum(span, | |
168 | ordering.clone(), | |
169 | vec![]))], | |
170 | old); | |
171 | let neq_arm = cx.arm(span, | |
172 | vec![cx.pat_ident(span, test_id)], | |
173 | cx.expr_ident(span, test_id)); | |
174 | ||
175 | cx.expr_match(span, new, vec![eq_arm, neq_arm]) | |
d9579d0f AL |
176 | }, |
177 | equals_expr.clone(), | |
178 | Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| { | |
179 | if self_args.len() != 2 { | |
180 | cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") | |
181 | } else { | |
182 | some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple) | |
183 | } | |
184 | }), | |
185 | cx, span, substr) | |
186 | } | |
187 | ||
188 | /// Strict inequality. | |
189 | fn cs_op(less: bool, equal: bool, cx: &mut ExtCtxt, | |
190 | span: Span, substr: &Substructure) -> P<Expr> { | |
7453a54e | 191 | let op = if less { BinOpKind::Lt } else { BinOpKind::Gt }; |
d9579d0f AL |
192 | cs_fold( |
193 | false, // need foldr, | |
194 | |cx, span, subexpr, self_f, other_fs| { | |
195 | /* | |
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()); | |
218 | ||
7453a54e | 219 | let not_cmp = cx.expr_unary(span, ast::UnOp::Not, |
d9579d0f AL |
220 | cx.expr_binary(span, op, other_f.clone(), self_f)); |
221 | ||
7453a54e SL |
222 | let and = cx.expr_binary(span, BinOpKind::And, not_cmp, subexpr); |
223 | cx.expr_binary(span, BinOpKind::Or, cmp, and) | |
d9579d0f AL |
224 | }, |
225 | cx.expr_bool(span, equal), | |
226 | Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| { | |
227 | if self_args.len() != 2 { | |
228 | cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") | |
229 | } else { | |
230 | let op = match (less, equal) { | |
231 | (true, true) => LeOp, (true, false) => LtOp, | |
232 | (false, true) => GeOp, (false, false) => GtOp, | |
233 | }; | |
234 | some_ordering_collapsed(cx, span, op, tag_tuple) | |
235 | } | |
236 | }), | |
237 | cx, span, substr) | |
238 | } |