Skip to content

Commit eed443b

Browse files
committed
syntax: Shrink the impl PartialOrd with #[derive(Ord)]
This PR generates a simplified `PartialOrd` implementation when the type is `Ord` and non-generic. It produces essentially: impl ::std::cmp::PartialOrd for $ty { fn partial_cmp(&self, other: &$ty) -> Option<std::cmp::Ordering> { Some(::std::cmp::Ord::cmp(self, other)) } }
1 parent 6dbb0ef commit eed443b

File tree

1 file changed

+150
-3
lines changed
  • src/libsyntax/ext/deriving/cmp

1 file changed

+150
-3
lines changed

src/libsyntax/ext/deriving/cmp/ord.rs

Lines changed: 150 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use abi;
1112
use ast;
1213
use ast::{MetaItem, Item, Expr};
13-
use codemap::Span;
14+
use codemap::{Span, respan};
1415
use ext::base::ExtCtxt;
1516
use ext::build::AstBuilder;
1617
use ext::deriving::generic::*;
1718
use ext::deriving::generic::ty::*;
18-
use parse::token::InternedString;
19+
use owned_slice::OwnedSlice;
20+
use parse::token::{InternedString, special_idents};
1921
use ptr::P;
2022

2123
use super::partial_ord;
@@ -52,7 +54,7 @@ pub fn expand_deriving_ord(cx: &mut ExtCtxt,
5254

5355
trait_def.expand(cx, mitem, item, push);
5456

55-
partial_ord::expand_deriving_partial_ord(cx, span, mitem, item, push)
57+
expand_deriving_partial_ord_when_ord(cx, span, mitem, item, push)
5658
}
5759

5860

@@ -143,3 +145,148 @@ pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
143145
}),
144146
cx, span, substr)
145147
}
148+
149+
fn expand_deriving_partial_ord_when_ord(cx: &mut ExtCtxt,
150+
span: Span,
151+
mitem: &MetaItem,
152+
item: &Item,
153+
push: &mut FnMut(P<Item>)) {
154+
// For generic types we need to destructure our value in order to recursively call clone.
155+
// However, as an optimization for non-generic types, we can just generate:
156+
//
157+
// impl<...> PartialOrd for $ty {
158+
// fn partial_cmp(&self, other: &$ty) -> Self { self.cmp(other) }
159+
// }
160+
//
161+
// But the generic deriving helpers do not support generating such a simple method. So we'll
162+
// build this method by hand. However, we want to take advantage of generic deriving generating
163+
// the `Generics` for us. So we'll generate an empty impl, then later on add our method. It's
164+
// not pretty, but it works until we get a more general purpose ast builder.
165+
match item.node {
166+
ast::ItemStruct(_, ref generics) | ast::ItemEnum(_, ref generics) => {
167+
if generics.is_type_parameterized() {
168+
partial_ord::expand_deriving_partial_ord(cx, span, mitem, item, push);
169+
return;
170+
}
171+
}
172+
_ => {
173+
cx.span_err(mitem.span, "`derive` may only be applied to structs and enums");
174+
return;
175+
}
176+
}
177+
178+
let trait_def = TraitDef {
179+
span: span,
180+
attributes: Vec::new(),
181+
path: path_std!(cx, core::cmp::PartialOrd),
182+
additional_bounds: Vec::new(),
183+
generics: LifetimeBounds::empty(),
184+
methods: Vec::new(),
185+
associated_types: Vec::new(),
186+
};
187+
188+
// We want to use the `cx` to build our ast, but it's passed by `&mut` to the expand method. So
189+
// we'll extract out the generated item by way of an option.
190+
let mut expanded_item = None;
191+
192+
trait_def.expand(cx, mitem, item, &mut |item: P<ast::Item>| {
193+
expanded_item = Some(item);
194+
});
195+
196+
let expanded_item = expanded_item.unwrap().map(|mut item| {
197+
match item.node {
198+
ast::ItemImpl(_, _, _, _, ref ty, ref mut impl_items) => {
199+
let self_arg = ast::Arg::new_self(span, ast::MutImmutable, special_idents::self_);
200+
let other_arg = cx.arg(
201+
span,
202+
cx.ident_of("other"),
203+
cx.ty_rptr(
204+
span,
205+
ty.clone(),
206+
None,
207+
ast::Mutability::MutImmutable,
208+
),
209+
);
210+
let decl = cx.fn_decl(
211+
vec![self_arg, other_arg],
212+
cx.ty_option(
213+
cx.ty_path(
214+
cx.path_global(
215+
span,
216+
vec![
217+
cx.ident_of_std("core"),
218+
cx.ident_of("cmp"),
219+
cx.ident_of("Ordering"),
220+
],
221+
)
222+
)
223+
)
224+
);
225+
226+
let sig = ast::MethodSig {
227+
unsafety: ast::Unsafety::Normal,
228+
abi: abi::Rust,
229+
decl: decl.clone(),
230+
generics: ast::Generics {
231+
lifetimes: Vec::new(),
232+
ty_params: OwnedSlice::empty(),
233+
where_clause: ast::WhereClause {
234+
id: ast::DUMMY_NODE_ID,
235+
predicates: Vec::new(),
236+
}
237+
},
238+
explicit_self: respan(
239+
span,
240+
ast::SelfRegion(None, ast::MutImmutable, cx.ident_of("self")),
241+
),
242+
};
243+
244+
let block = cx.block_expr(
245+
cx.expr_some(
246+
span,
247+
cx.expr_call(
248+
span,
249+
cx.expr_path(
250+
cx.path_global(
251+
span,
252+
vec![
253+
cx.ident_of_std("core"),
254+
cx.ident_of("cmp"),
255+
cx.ident_of("Ord"),
256+
cx.ident_of("cmp"),
257+
],
258+
),
259+
),
260+
vec![
261+
cx.expr_self(span),
262+
cx.expr_addr_of(
263+
span,
264+
cx.expr_ident(span, cx.ident_of("other"))
265+
),
266+
]
267+
)
268+
)
269+
);
270+
271+
let inline = cx.meta_word(span, InternedString::new("inline"));
272+
let attrs = vec!(cx.attribute(span, inline));
273+
274+
impl_items.push(P(ast::ImplItem {
275+
id: ast::DUMMY_NODE_ID,
276+
ident: cx.ident_of("partial_cmp"),
277+
vis: ast::Visibility::Inherited,
278+
attrs: attrs,
279+
node: ast::ImplItem_::MethodImplItem(sig, block),
280+
span: span,
281+
}));
282+
}
283+
_ => {
284+
cx.span_bug(span, "we should have gotten an impl")
285+
}
286+
};
287+
288+
item
289+
});
290+
291+
push(expanded_item)
292+
}

0 commit comments

Comments
 (0)