]> git.proxmox.com Git - rustc.git/blame - 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
CommitLineData
9fa01778 1pub use OrderingOp::*;
d9579d0f 2
9fa01778 3use crate::deriving::generic::ty::*;
dfeec247
XL
4use crate::deriving::generic::*;
5use crate::deriving::{path_local, path_std, pathvec_std};
9cc50fc6 6
74b04a01 7use rustc_ast::ptr::P;
3dfed10e 8use rustc_ast::{self as ast, BinOpKind, Expr, MetaItem};
dfeec247 9use rustc_expand::base::{Annotatable, ExtCtxt};
f9f354fc 10use rustc_span::symbol::{sym, Ident, Symbol};
dfeec247 11use rustc_span::Span;
d9579d0f 12
dfeec247
XL
13pub 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)]
95pub enum OrderingOp {
5bcae85e
SL
96 PartialCmpOp,
97 LtOp,
98 LeOp,
99 GtOp,
100 GeOp,
d9579d0f
AL
101}
102
e1599b0c
XL
103pub 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 121pub 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
184fn 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}