Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use discriminant_value intrinsic for derive(PartialOrd) #24270

Merged
merged 10 commits into from
Apr 11, 2015
6 changes: 6 additions & 0 deletions src/libcore/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,4 +569,10 @@ extern "rust-intrinsic" {
pub fn overflowing_sub<T>(a: T, b: T) -> T;
/// Returns (a * b) mod 2^N, where N is the width of N in bits.
pub fn overflowing_mul<T>(a: T, b: T) -> T;

/// Returns the value of the discriminant for the variant in 'v',
/// cast to a `u64`; if `T` has no discriminant, returns 0.
// SNAP 5520801
#[cfg(not(stage0))]
pub fn discriminant_value<T>(v: &T) -> u64;
}
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ mod tuple;

#[doc(hidden)]
mod core {
pub use intrinsics;
pub use panicking;
pub use fmt;
pub use clone;
Expand Down
12 changes: 12 additions & 0 deletions src/librustc_trans/trans/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use llvm;
use llvm::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef, TypeKind};
use middle::subst;
use middle::subst::FnSpace;
use trans::adt;
use trans::base::*;
use trans::build::*;
use trans::callee;
Expand Down Expand Up @@ -683,6 +684,17 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
}
}

(_, "discriminant_value") => {
let val_ty = substs.types.get(FnSpace, 0);
match val_ty.sty {
ty::ty_enum(..) => {
let repr = adt::represent_type(ccx, *val_ty);
adt::trans_get_discr(bcx, &*repr, llargs[0], Some(llret_ty))
}
_ => C_null(llret_ty)
}
}

// This requires that atomic intrinsics follow a specific naming pattern:
// "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
(_, name) if name.starts_with("atomic_") => {
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5073,6 +5073,12 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {

"assume" => (0, vec![tcx.types.bool], ty::mk_nil(tcx)),

"discriminant_value" => (1, vec![
ty::mk_imm_rptr(tcx,
tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
ty::BrAnon(0))),
param(ccx, 0))], tcx.types.u64),

ref other => {
span_err!(tcx.sess, it.span, E0093,
"unrecognized intrinsic function: `{}`", *other);
Expand Down
92 changes: 63 additions & 29 deletions src/libsyntax/ext/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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[..])
Expand All @@ -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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like there's a chance that this could miss a repr attribute that is applied by a later pass of expansion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very interesting, I'm not even sure how we can handle that ... without some somewhat serious revisions to the expansion infrastructure (e.g. some way to schedule an expansion "at the end" ...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most conservative solution would be to change the intrinsic to return (sign, abs-value) to avoid having to figure out the repr at all, but that does make the comparisons a bit less efficient depending on how smart LLVM can be.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a simpler option. The intrinsic could be:

  • compare_variant_order<T:Reflect>(t: &T, u: &T)

which would yield a -1,0,1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(well, we may still want the discriminant_value intrinsic itself independently, but I am inclined to agree that this is an elegant way to resolve all of the issues here.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yeah, that's a good call.

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> {
Expand Down Expand Up @@ -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)
}


Expand Down Expand Up @@ -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> {
Expand All @@ -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[..]) })
Expand Down Expand Up @@ -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);
}

Expand Down
77 changes: 77 additions & 0 deletions src/test/run-pass/discriminant_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(core)]

extern crate core;
use core::intrinsics::discriminant_value;

enum CLike1 {
A,
B,
C,
D
}

enum CLike2 {
A = 5,
B = 2,
C = 19,
D
}

#[repr(i8)]
enum CLike3 {
A = 5,
B,
C = -1,
D
}

enum ADT {
First(u32, u32),
Second(u64)
}

enum NullablePointer {
Something(&'static u32),
Nothing
}

static CONST : u32 = 0xBEEF;

pub fn main() {
unsafe {

assert_eq!(discriminant_value(&CLike1::A), 0);
assert_eq!(discriminant_value(&CLike1::B), 1);
assert_eq!(discriminant_value(&CLike1::C), 2);
assert_eq!(discriminant_value(&CLike1::D), 3);

assert_eq!(discriminant_value(&CLike2::A), 5);
assert_eq!(discriminant_value(&CLike2::B), 2);
assert_eq!(discriminant_value(&CLike2::C), 19);
assert_eq!(discriminant_value(&CLike2::D), 20);

assert_eq!(discriminant_value(&CLike3::A), 5);
assert_eq!(discriminant_value(&CLike3::B), 6);
assert_eq!(discriminant_value(&CLike3::C), -1_i8 as u64);
assert_eq!(discriminant_value(&CLike3::D), 0);

assert_eq!(discriminant_value(&ADT::First(0,0)), 0);
assert_eq!(discriminant_value(&ADT::Second(5)), 1);

assert_eq!(discriminant_value(&NullablePointer::Nothing), 1);
assert_eq!(discriminant_value(&NullablePointer::Something(&CONST)), 0);

assert_eq!(discriminant_value(&10), 0);
assert_eq!(discriminant_value(&"test"), 0);
}
}
48 changes: 48 additions & 0 deletions src/test/run-pass/issue-15523-big.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Issue 15523: derive(PartialOrd) should use the provided
// discriminant values for the derived ordering.
//
// This test is checking corner cases that arise when you have
// 64-bit values in the variants.

#[derive(PartialEq, PartialOrd)]
#[repr(u64)]
enum Eu64 {
Pos2 = 2,
PosMax = !0,
Pos1 = 1,
}

#[derive(PartialEq, PartialOrd)]
#[repr(i64)]
enum Ei64 {
Pos2 = 2,
Neg1 = -1,
NegMin = 1 << 63,
PosMax = !(1 << 63),
Pos1 = 1,
}

fn main() {
assert!(Eu64::Pos2 > Eu64::Pos1);
assert!(Eu64::Pos2 < Eu64::PosMax);
assert!(Eu64::Pos1 < Eu64::PosMax);


assert!(Ei64::Pos2 > Ei64::Pos1);
assert!(Ei64::Pos2 > Ei64::Neg1);
assert!(Ei64::Pos1 > Ei64::Neg1);
assert!(Ei64::Pos2 > Ei64::NegMin);
assert!(Ei64::Pos1 > Ei64::NegMin);
assert!(Ei64::Pos2 < Ei64::PosMax);
assert!(Ei64::Pos1 < Ei64::PosMax);
}
Loading