]> git.proxmox.com Git - rustc.git/blob - src/librustc_builtin_macros/deriving/cmp/partial_ord.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / src / librustc_builtin_macros / deriving / cmp / partial_ord.rs
1 pub use OrderingOp::*;
2
3 use crate::deriving::generic::ty::*;
4 use crate::deriving::generic::*;
5 use crate::deriving::{path_local, path_std, pathvec_std};
6
7 use rustc_ast::ptr::P;
8 use rustc_ast::{self as ast, BinOpKind, Expr, MetaItem};
9 use rustc_expand::base::{Annotatable, ExtCtxt};
10 use rustc_span::symbol::{sym, Ident, Symbol};
11 use rustc_span::Span;
12
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 ) {
20 macro_rules! md {
21 ($name:expr, $op:expr, $equal:expr) => {{
22 let inline = cx.meta_word(span, sym::inline);
23 let attrs = vec![cx.attribute(inline)];
24 MethodDef {
25 name: $name,
26 generics: Bounds::empty(),
27 explicit_self: borrowed_explicit_self(),
28 args: vec![(borrowed_self(), sym::other)],
29 ret_ty: Literal(path_local!(bool)),
30 attributes: attrs,
31 is_unsafe: false,
32 unify_fieldless_variants: true,
33 combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
34 cs_op($op, $equal, cx, span, substr)
35 })),
36 }
37 }};
38 }
39
40 let ordering_ty = Literal(path_std!(cmp::Ordering));
41 let ret_ty = Literal(Path::new_(
42 pathvec_std!(option::Option),
43 None,
44 vec![Box::new(ordering_ty)],
45 PathKind::Std,
46 ));
47
48 let inline = cx.meta_word(span, sym::inline);
49 let attrs = vec![cx.attribute(inline)];
50
51 let partial_cmp_def = MethodDef {
52 name: sym::partial_cmp,
53 generics: Bounds::empty(),
54 explicit_self: borrowed_explicit_self(),
55 args: vec![(borrowed_self(), sym::other)],
56 ret_ty,
57 attributes: attrs,
58 is_unsafe: false,
59 unify_fieldless_variants: true,
60 combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
61 cs_partial_cmp(cx, span, substr)
62 })),
63 };
64
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 {
71 vec![
72 partial_cmp_def,
73 md!(sym::lt, true, false),
74 md!(sym::le, true, true),
75 md!(sym::gt, false, false),
76 md!(sym::ge, false, true),
77 ]
78 };
79
80 let trait_def = TraitDef {
81 span,
82 attributes: vec![],
83 path: path_std!(cmp::PartialOrd),
84 additional_bounds: vec![],
85 generics: Bounds::empty(),
86 is_unsafe: false,
87 supports_unions: false,
88 methods,
89 associated_types: Vec::new(),
90 };
91 trait_def.expand(cx, mitem, item, push)
92 }
93
94 #[derive(Copy, Clone)]
95 pub enum OrderingOp {
96 PartialCmpOp,
97 LtOp,
98 LeOp,
99 GtOp,
100 GeOp,
101 }
102
103 pub fn some_ordering_collapsed(
104 cx: &mut ExtCtxt<'_>,
105 span: Span,
106 op: OrderingOp,
107 self_arg_tags: &[Ident],
108 ) -> P<ast::Expr> {
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]));
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,
117 };
118 cx.expr_method_call(span, lft, Ident::new(op_sym, span), vec![rgt])
119 }
120
121 pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
122 let test_id = Ident::new(sym::cmp, span);
123 let ordering = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal]));
124 let ordering_expr = cx.expr_path(ordering.clone());
125 let equals_expr = cx.expr_some(span, ordering_expr);
126
127 let partial_cmp_path = cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]);
128
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 // }
137 // cmp => cmp
138 // },
139 // cmp => cmp
140 // }
141 //
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 // }
151
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 };
157
158 let args =
159 vec![cx.expr_addr_of(span, self_f), cx.expr_addr_of(span, other_f.clone())];
160
161 cx.expr_call_global(span, partial_cmp_path.clone(), args)
162 };
163
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));
166
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 )
181 }
182
183 /// Strict inequality.
184 fn cs_op(
185 less: bool,
186 inclusive: bool,
187 cx: &mut ExtCtxt<'_>,
188 span: Span,
189 substr: &Substructure<'_>,
190 ) -> P<Expr> {
191 let ordering_path = |cx: &mut ExtCtxt<'_>, name: &str| {
192 cx.expr_path(
193 cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, Symbol::intern(name)])),
194 )
195 };
196
197 let par_cmp = |cx: &mut ExtCtxt<'_>, span, self_f: P<Expr>, other_fs: &[P<Expr>], default| {
198 let other_f = match other_fs {
199 [o_f] => o_f,
200 _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
201 };
202
203 // `PartialOrd::partial_cmp(self.fi, other.fi)`
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 );
212
213 let default = ordering_path(cx, default);
214 // `Option::unwrap_or(_, Ordering::Equal)`
215 let unwrap_path = cx.expr_path(
216 cx.path_global(span, cx.std_path(&[sym::option, sym::Option, sym::unwrap_or])),
217 );
218 cx.expr_call(span, unwrap_path, vec![cmp, default])
219 };
220
221 let fold = cs_fold1(
222 false, // need foldr
223 |cx, span, subexpr, self_f, other_fs| {
224 // build up a series of `partial_cmp`s from the inside
225 // out (hence foldr) to get lexical ordering, i.e., for op ==
226 // `ast::lt`
227 //
228 // ```
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 // )
236 // )
237 // == Ordering::Less
238 // ```
239 //
240 // and for op ==
241 // `ast::le`
242 //
243 // ```
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 // )
251 // )
252 // != Ordering::Greater
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.
258
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");
261
262 // `Ordering::then_with(Option::unwrap_or(..), ..)`
263 let then_with_path = cx.expr_path(
264 cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::then_with])),
265 );
266 cx.expr_call(span, then_with_path, vec![par_cmp, cx.lambda0(span, subexpr)])
267 },
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)
272 }
273 None => cx.expr_bool(span, inclusive),
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 {
279 let op = match (less, inclusive) {
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,
290 substr,
291 );
292
293 match *substr.fields {
294 EnumMatching(.., ref all_fields) | Struct(.., ref all_fields) if !all_fields.is_empty() => {
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 }
300 _ => fold,
301 }
302 }