Skip to content

Commit

Permalink
Auto merge of #36823 - durka:discriminant_value, r=nagisa
Browse files Browse the repository at this point in the history
add wrapper for discriminant_value, take 2

[This is #34785 reopened, since @bors apparently gave up on that thread.]

add wrapper for discriminant_value intrinsic

Implementation of [RFC 1696](https://github.com/rust-lang/rfcs/blob/master/text/1696-discriminant.md).

Wraps the `discriminant_value` intrinsic under the name `std::mem::discriminant`. In order to avoid prematurely leaking information about the implementation of enums, the return value is an opaque type, generic over the enum type, which implements Copy, Clone, PartialEq, Eq, Hash, and Debug (notably not PartialOrd). There is currently no way to get the value out excepting printing the debug representation.

The wrapper is safe and can be stabilized soon as per discussion in #24263.

cc @aturon
r? @nagisa
  • Loading branch information
bors committed Sep 30, 2016
2 parents 1d64acd + a84b550 commit 9548730
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 0 deletions.
82 changes: 82 additions & 0 deletions src/libcore/mem.rs
Expand Up @@ -15,7 +15,12 @@

#![stable(feature = "rust1", since = "1.0.0")]

use clone;
use cmp;
use fmt;
use hash;
use intrinsics;
use marker::{Copy, PhantomData, Sized};
use ptr;

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -647,3 +652,80 @@ pub fn drop<T>(_x: T) { }
pub unsafe fn transmute_copy<T, U>(src: &T) -> U {
ptr::read(src as *const T as *const U)
}

/// Opaque type representing the discriminant of an enum.
///
/// See the `discriminant` function in this module for more information.
#[unstable(feature = "discriminant_value", reason = "recently added, follows RFC", issue = "24263")]
pub struct Discriminant<T>(u64, PhantomData<*const T>);

// N.B. These trait implementations cannot be derived because we don't want any bounds on T.

#[unstable(feature = "discriminant_value", reason = "recently added, follows RFC", issue = "24263")]
impl<T> Copy for Discriminant<T> {}

#[unstable(feature = "discriminant_value", reason = "recently added, follows RFC", issue = "24263")]
impl<T> clone::Clone for Discriminant<T> {
fn clone(&self) -> Self {
*self
}
}

#[unstable(feature = "discriminant_value", reason = "recently added, follows RFC", issue = "24263")]
impl<T> cmp::PartialEq for Discriminant<T> {
fn eq(&self, rhs: &Self) -> bool {
self.0 == rhs.0
}
}

#[unstable(feature = "discriminant_value", reason = "recently added, follows RFC", issue = "24263")]
impl<T> cmp::Eq for Discriminant<T> {}

#[unstable(feature = "discriminant_value", reason = "recently added, follows RFC", issue = "24263")]
impl<T> hash::Hash for Discriminant<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}

#[unstable(feature = "discriminant_value", reason = "recently added, follows RFC", issue = "24263")]
impl<T> fmt::Debug for Discriminant<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_tuple("Discriminant")
.field(&self.0)
.finish()
}
}

/// Returns a value uniquely identifying the enum variant in `v`.
///
/// If `T` is not an enum, calling this function will not result in undefined behavior, but the
/// return value is unspecified.
///
/// # Stability
///
/// The discriminant of an enum variant may change if the enum definition changes. A discriminant
/// of some variant will not change between compilations with the same compiler.
///
/// # Examples
///
/// This can be used to compare enums that carry data, while disregarding
/// the actual data:
///
/// ```
/// #![feature(discriminant_value)]
/// use std::mem;
///
/// enum Foo { A(&'static str), B(i32), C(i32) }
///
/// assert!(mem::discriminant(&Foo::A("bar")) == mem::discriminant(&Foo::A("baz")));
/// assert!(mem::discriminant(&Foo::B(1)) == mem::discriminant(&Foo::B(2)));
/// assert!(mem::discriminant(&Foo::B(3)) != mem::discriminant(&Foo::C(3)));
/// ```
#[unstable(feature = "discriminant_value", reason = "recently added, follows RFC", issue = "24263")]
pub fn discriminant<T>(v: &T) -> Discriminant<T> {
unsafe {
Discriminant(intrinsics::discriminant_value(v), PhantomData)
}
}

28 changes: 28 additions & 0 deletions src/test/run-pass/discriminant_value-wrapper.rs
@@ -0,0 +1,28 @@
// Copyright 2016 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(discriminant_value)]

use std::mem;

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

pub fn main() {
assert!(mem::discriminant(&ADT::First(0,0)) == mem::discriminant(&ADT::First(1,1)));
assert!(mem::discriminant(&ADT::Second(5)) == mem::discriminant(&ADT::Second(6)));
assert!(mem::discriminant(&ADT::First(2,2)) != mem::discriminant(&ADT::Second(2)));

let _ = mem::discriminant(&10);
let _ = mem::discriminant(&"test");
}

0 comments on commit 9548730

Please sign in to comment.