3 use crate::deriving
::generic
::ty
::*;
4 use crate::deriving
::generic
::*;
5 use crate::deriving
::{path_local, path_std, pathvec_std}
;
7 use rustc_expand
::base
::{Annotatable, ExtCtxt}
;
8 use rustc_span
::symbol
::{sym, Symbol}
;
10 use syntax
::ast
::{self, BinOpKind, Expr, MetaItem}
;
13 pub fn expand_deriving_partial_ord(
18 push
: &mut dyn FnMut(Annotatable
),
21 ($name
:expr
, $op
:expr
, $equal
:expr
) => {{
22 let inline
= cx
.meta_word(span
, sym
::inline
);
23 let attrs
= vec
![cx
.attribute(inline
)];
26 generics
: LifetimeBounds
::empty(),
27 explicit_self
: borrowed_explicit_self(),
28 args
: vec
![(borrowed_self(), "other")],
29 ret_ty
: Literal(path_local
!(bool
)),
32 unify_fieldless_variants
: true,
33 combine_substructure
: combine_substructure(Box
::new(|cx
, span
, substr
| {
34 cs_op($op
, $equal
, cx
, span
, substr
)
40 let ordering_ty
= Literal(path_std
!(cx
, cmp
::Ordering
));
41 let ret_ty
= Literal(Path
::new_(
42 pathvec_std
!(cx
, option
::Option
),
44 vec
![Box
::new(ordering_ty
)],
48 let inline
= cx
.meta_word(span
, sym
::inline
);
49 let attrs
= vec
![cx
.attribute(inline
)];
51 let partial_cmp_def
= MethodDef
{
53 generics
: LifetimeBounds
::empty(),
54 explicit_self
: borrowed_explicit_self(),
55 args
: vec
![(borrowed_self(), "other")],
59 unify_fieldless_variants
: true,
60 combine_substructure
: combine_substructure(Box
::new(|cx
, span
, substr
| {
61 cs_partial_cmp(cx
, span
, substr
)
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
) {
73 md
!("lt", true, false),
74 md
!("le", true, true),
75 md
!("gt", false, false),
76 md
!("ge", false, true),
80 let trait_def
= TraitDef
{
83 path
: path_std
!(cx
, cmp
::PartialOrd
),
84 additional_bounds
: vec
![],
85 generics
: LifetimeBounds
::empty(),
87 supports_unions
: false,
89 associated_types
: Vec
::new(),
91 trait_def
.expand(cx
, mitem
, item
, push
)
94 #[derive(Copy, Clone)]
103 pub fn some_ordering_collapsed(
104 cx
: &mut ExtCtxt
<'_
>,
107 self_arg_tags
: &[ast
::Ident
],
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_str
= match op
{
112 PartialCmpOp
=> "partial_cmp",
118 cx
.expr_method_call(span
, lft
, cx
.ident_of(op_str
, span
), vec
![rgt
])
121 pub fn cs_partial_cmp(cx
: &mut ExtCtxt
<'_
>, span
: Span
, substr
: &Substructure
<'_
>) -> P
<Expr
> {
122 let test_id
= ast
::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
);
127 let partial_cmp_path
= cx
.std_path(&[sym
::cmp
, sym
::PartialOrd
, sym
::partial_cmp
]);
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) => {
143 // foldr nests the if-elses correctly, leaving the first field
144 // as the outermost one, and the last as the innermost.
146 |cx
, span
, old
, self_f
, other_fs
| {
148 // Some(::std::cmp::Ordering::Equal) => old,
153 let other_f
= match other_fs
{
155 _
=> cx
.span_bug(span
, "not exactly 2 arguments in `derive(PartialOrd)`"),
159 vec
![cx
.expr_addr_of(span
, self_f
), cx
.expr_addr_of(span
, other_f
.clone())];
161 cx
.expr_call_global(span
, partial_cmp_path
.clone(), args
)
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
));
167 cx
.expr_match(span
, new
, vec
![eq_arm
, neq_arm
])
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)`")
174 some_ordering_collapsed(cx
, span
, PartialCmpOp
, tag_tuple
)
183 /// Strict inequality.
187 cx
: &mut ExtCtxt
<'_
>,
189 substr
: &Substructure
<'_
>,
191 let ordering_path
= |cx
: &mut ExtCtxt
<'_
>, name
: &str| {
193 cx
.path_global(span
, cx
.std_path(&[sym
::cmp
, sym
::Ordering
, Symbol
::intern(name
)])),
197 let par_cmp
= |cx
: &mut ExtCtxt
<'_
>, span
, self_f
: P
<Expr
>, other_fs
: &[P
<Expr
>], default| {
198 let other_f
= match other_fs
{
200 _
=> cx
.span_bug(span
, "not exactly 2 arguments in `derive(PartialOrd)`"),
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
])),
207 let cmp
= cx
.expr_call(
210 vec
![cx
.expr_addr_of(span
, self_f
), cx
.expr_addr_of(span
, other_f
.clone())],
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
])),
218 cx
.expr_call(span
, unwrap_path
, vec
![cmp
, default])
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 ==
229 // Ordering::then_with(
230 // Option::unwrap_or(
231 // PartialOrd::partial_cmp(self.f1, other.f1), Ordering::Equal)
233 // Option::unwrap_or(
234 // PartialOrd::partial_cmp(self.f2, other.f2), Ordering::Greater)
244 // Ordering::then_with(
245 // Option::unwrap_or(
246 // PartialOrd::partial_cmp(self.f1, other.f1), Ordering::Equal)
248 // Option::unwrap_or(
249 // PartialOrd::partial_cmp(self.f2, other.f2), Ordering::Greater)
252 // != Ordering::Greater
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.
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");
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
])),
266 cx
.expr_call(span
, then_with_path
, vec
![par_cmp
, cx
.lambda0(span
, subexpr
)])
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
)
273 None
=> cx
.expr_bool(span
, inclusive
),
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)`")
279 let op
= match (less
, inclusive
) {
280 (false, false) => GtOp
,
281 (false, true) => GeOp
,
282 (true, false) => LtOp
,
283 (true, true) => LeOp
,
285 some_ordering_collapsed(cx
, span
, op
, tag_tuple
)
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 }
;
298 cx
.expr_binary(span
, comp_op
, fold
, ordering
)