|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
| 11 | +use abi; |
11 | 12 | use ast;
|
12 | 13 | use ast::{MetaItem, Item, Expr};
|
13 |
| -use codemap::Span; |
| 14 | +use codemap::{Span, respan}; |
14 | 15 | use ext::base::ExtCtxt;
|
15 | 16 | use ext::build::AstBuilder;
|
16 | 17 | use ext::deriving::generic::*;
|
17 | 18 | use ext::deriving::generic::ty::*;
|
18 |
| -use parse::token::InternedString; |
| 19 | +use owned_slice::OwnedSlice; |
| 20 | +use parse::token::{InternedString, special_idents}; |
19 | 21 | use ptr::P;
|
20 | 22 |
|
21 | 23 | use super::partial_ord;
|
@@ -52,7 +54,7 @@ pub fn expand_deriving_ord(cx: &mut ExtCtxt,
|
52 | 54 |
|
53 | 55 | trait_def.expand(cx, mitem, item, push);
|
54 | 56 |
|
55 |
| - partial_ord::expand_deriving_partial_ord(cx, span, mitem, item, push) |
| 57 | + expand_deriving_partial_ord_when_ord(cx, span, mitem, item, push) |
56 | 58 | }
|
57 | 59 |
|
58 | 60 |
|
@@ -143,3 +145,148 @@ pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
|
143 | 145 | }),
|
144 | 146 | cx, span, substr)
|
145 | 147 | }
|
| 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