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