diff options
| author | bors <bors@rust-lang.org> | 2015-04-10 23:49:24 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2015-04-10 23:49:24 +0000 |
| commit | 93f7fe32d30b67baa6abf09c073c5862a978dc57 (patch) | |
| tree | 5b63cfec7a546fe3c190f8a6cfafc3118b903180 /src/libsyntax | |
| parent | c897ac04e2ebda378fd9e38f6ec0878ae3a2baf7 (diff) | |
| parent | 05aaad114f343ab6d484aa193b99e221089f4a8b (diff) | |
| download | rust-93f7fe32d30b67baa6abf09c073c5862a978dc57.tar.gz rust-93f7fe32d30b67baa6abf09c073c5862a978dc57.zip | |
Auto merge of #24270 - pnkfelix:use-disr-val-for-derive-ord, r=brson
Use `discriminant_value` intrinsic for `derive(PartialOrd)`
[breaking-change]
This is a [breaking-change] because it can change the result of comparison operators when enum discriminants have been explicitly assigned. Notably in a case like:
```rust
#[derive(PartialOrd)]
enum E { A = 2, B = 1}
```
Under the old deriving, `A < B` held, because `A` came before `B` in the order of declaration. But now we use the ordering according to the provided values, and thus `A > B`. (However, this change is very unlikely to break much, if any, code, since the orderings themselves should all remain well-defined, total, etc.)
Fix #15523
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/deriving/generic/mod.rs | 92 |
1 files changed, 63 insertions, 29 deletions
diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index 397775fdbfe..ac96375095e 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -201,6 +201,7 @@ use ext::base::ExtCtxt; use ext::build::AstBuilder; use codemap::{self, DUMMY_SP}; use codemap::Span; +use diagnostic::SpanHandler; use fold::MoveMap; use owned_slice::OwnedSlice; use parse::token::InternedString; @@ -391,6 +392,7 @@ impl<'a> TraitDef<'a> { ast::ItemEnum(ref enum_def, ref generics) => { self.expand_enum_def(cx, enum_def, + &item.attrs[..], item.ident, generics) } @@ -653,6 +655,7 @@ impl<'a> TraitDef<'a> { fn expand_enum_def(&self, cx: &mut ExtCtxt, enum_def: &EnumDef, + type_attrs: &[ast::Attribute], type_ident: Ident, generics: &Generics) -> P<ast::Item> { let mut field_tys = Vec::new(); @@ -687,6 +690,7 @@ impl<'a> TraitDef<'a> { method_def.expand_enum_method_body(cx, self, enum_def, + type_attrs, type_ident, self_args, &nonself_args[..]) @@ -706,13 +710,30 @@ impl<'a> TraitDef<'a> { } } -fn variant_to_pat(cx: &mut ExtCtxt, sp: Span, enum_ident: ast::Ident, variant: &ast::Variant) - -> P<ast::Pat> { - let path = cx.path(sp, vec![enum_ident, variant.node.name]); - cx.pat(sp, match variant.node.kind { - ast::TupleVariantKind(..) => ast::PatEnum(path, None), - ast::StructVariantKind(..) => ast::PatStruct(path, Vec::new(), true), - }) +fn find_repr_type_name(diagnostic: &SpanHandler, + type_attrs: &[ast::Attribute]) -> &'static str { + let mut repr_type_name = "i32"; + for a in type_attrs { + for r in &attr::find_repr_attrs(diagnostic, a) { + repr_type_name = match *r { + attr::ReprAny | attr::ReprPacked => continue, + attr::ReprExtern => "i32", + + attr::ReprInt(_, attr::SignedInt(ast::TyIs)) => "isize", + attr::ReprInt(_, attr::SignedInt(ast::TyI8)) => "i8", + attr::ReprInt(_, attr::SignedInt(ast::TyI16)) => "i16", + attr::ReprInt(_, attr::SignedInt(ast::TyI32)) => "i32", + attr::ReprInt(_, attr::SignedInt(ast::TyI64)) => "i64", + + attr::ReprInt(_, attr::UnsignedInt(ast::TyUs)) => "usize", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU8)) => "u8", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU16)) => "u16", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU32)) => "u32", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU64)) => "u64", + } + } + } + repr_type_name } impl<'a> MethodDef<'a> { @@ -983,12 +1004,13 @@ impl<'a> MethodDef<'a> { cx: &mut ExtCtxt, trait_: &TraitDef, enum_def: &EnumDef, + type_attrs: &[ast::Attribute], type_ident: Ident, self_args: Vec<P<Expr>>, nonself_args: &[P<Expr>]) -> P<Expr> { self.build_enum_match_tuple( - cx, trait_, enum_def, type_ident, self_args, nonself_args) + cx, trait_, enum_def, type_attrs, type_ident, self_args, nonself_args) } @@ -1022,6 +1044,7 @@ impl<'a> MethodDef<'a> { cx: &mut ExtCtxt, trait_: &TraitDef, enum_def: &EnumDef, + type_attrs: &[ast::Attribute], type_ident: Ident, self_args: Vec<P<Expr>>, nonself_args: &[P<Expr>]) -> P<Expr> { @@ -1044,8 +1067,8 @@ impl<'a> MethodDef<'a> { .collect::<Vec<ast::Ident>>(); // The `vi_idents` will be bound, solely in the catch-all, to - // a series of let statements mapping each self_arg to a usize - // corresponding to its variant index. + // a series of let statements mapping each self_arg to an int + // value corresponding to its discriminant. let vi_idents: Vec<ast::Ident> = self_arg_names.iter() .map(|name| { let vi_suffix = format!("{}_vi", &name[..]); cx.ident_of(&vi_suffix[..]) }) @@ -1160,33 +1183,44 @@ impl<'a> MethodDef<'a> { // unreachable-pattern error. // if variants.len() > 1 && self_args.len() > 1 { - let arms: Vec<ast::Arm> = variants.iter().enumerate() - .map(|(index, variant)| { - let pat = variant_to_pat(cx, sp, type_ident, &**variant); - let lit = ast::LitInt(index as u64, ast::UnsignedIntLit(ast::TyUs)); - cx.arm(sp, vec![pat], cx.expr_lit(sp, lit)) - }).collect(); - // Build a series of let statements mapping each self_arg - // to a usize corresponding to its variant index. + // to its discriminant value. If this is a C-style enum + // with a specific repr type, then casts the values to + // that type. Otherwise casts to `i32` (the default repr + // type). + // // i.e. for `enum E<T> { A, B(1), C(T, T) }`, and a deriving // with three Self args, builds three statements: // // ``` - // let __self0_vi = match self { - // A => 0, B(..) => 1, C(..) => 2 - // }; - // let __self1_vi = match __arg1 { - // A => 0, B(..) => 1, C(..) => 2 - // }; - // let __self2_vi = match __arg2 { - // A => 0, B(..) => 1, C(..) => 2 - // }; + // let __self0_vi = unsafe { + // std::intrinsics::discriminant_value(&self) } as i32; + // let __self1_vi = unsafe { + // std::intrinsics::discriminant_value(&__arg1) } as i32; + // let __self2_vi = unsafe { + // std::intrinsics::discriminant_value(&__arg2) } as i32; // ``` let mut index_let_stmts: Vec<P<ast::Stmt>> = Vec::new(); + + let target_type_name = + find_repr_type_name(&cx.parse_sess.span_diagnostic, type_attrs); + for (&ident, self_arg) in vi_idents.iter().zip(self_args.iter()) { - let variant_idx = cx.expr_match(sp, self_arg.clone(), arms.clone()); - let let_stmt = cx.stmt_let(sp, false, ident, variant_idx); + let path = vec![cx.ident_of_std("core"), + cx.ident_of("intrinsics"), + cx.ident_of("discriminant_value")]; + let call = cx.expr_call_global( + sp, path, vec![cx.expr_addr_of(sp, self_arg.clone())]); + let variant_value = cx.expr_block(P(ast::Block { + stmts: vec![], + expr: Some(call), + id: ast::DUMMY_NODE_ID, + rules: ast::UnsafeBlock(ast::CompilerGenerated), + span: sp })); + + let target_ty = cx.ty_ident(sp, cx.ident_of(target_type_name)); + let variant_disr = cx.expr_cast(sp, variant_value, target_ty); + let let_stmt = cx.stmt_let(sp, false, ident, variant_disr); index_let_stmts.push(let_stmt); } |
