about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2020-06-25 18:00:07 -0700
committerGitHub <noreply@github.com>2020-06-25 18:00:07 -0700
commitc50d9816c7b8fee1a7fa2fb7c6c47fc9b9ddd83f (patch)
treeea0ef58a17b46f3a5cbe679c4de3276990336d12 /src/libcore
parent23c9ac6b730f4e71b47f8714420f2609537b7114 (diff)
parent493199626baf8a0a8f6d3a19a089165d18b3f1fb (diff)
downloadrust-c50d9816c7b8fee1a7fa2fb7c6c47fc9b9ddd83f.tar.gz
rust-c50d9816c7b8fee1a7fa2fb7c6c47fc9b9ddd83f.zip
Rollup merge of #73418 - doctorn:variants-intrinsic, r=kennytm
Add unstable `core::mem::variant_count` intrinsic

Adds a new `const fn` intrinsic which can be used to determine the number of variants in an `enum`.

I've shown this to a couple of people and they invariably ask 'why on earth?', but there's actually a very neat use case:

At the moment, if you want to create an opaque array type that's indexed by an `enum` with one element for each variant, you either have to hard-code the number of variants, add a `LENGTH` variant or use a `Vec`, none of which are suitable in general (number of variants could change; pattern matching `LENGTH` becomes frustrating; might not have `alloc`). By including this intrinsic, it becomes possible to write the following:

```rust
#[derive(Copy, Clone)]
enum OpaqueIndex {
    A = 0,
    B,
    C,
}

struct OpaqueVec<T>(Box<[T; std::mem::num_variants::<OpaqueIndex>()]>);

impl<T> std::ops::Index<OpaqueIndex> for OpaqueVec<T> {
    type Output = T;

    fn index(&self, idx: OpaqueIndex) -> &Self::Output {
        &self.0[idx as usize]
    }
}
```

(We even have a use cases for this in `rustc` and I plan to use it to re-implement the lang-items table.)
Diffstat (limited to 'src/libcore')
-rw-r--r--src/libcore/intrinsics.rs15
-rw-r--r--src/libcore/lib.rs1
-rw-r--r--src/libcore/mem/mod.rs30
3 files changed, 46 insertions, 0 deletions
diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs
index 2298958b881..5d090187591 100644
--- a/src/libcore/intrinsics.rs
+++ b/src/libcore/intrinsics.rs
@@ -1917,6 +1917,15 @@ extern "rust-intrinsic" {
     #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")]
     pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
 
+    /// Returns the number of variants of the type `T` cast to a `usize`;
+    /// if `T` has no variants, returns 0. Uninhabited variants will be counted.
+    ///
+    /// The to-be-stabilized version of this intrinsic is
+    /// [`std::mem::variant_count`](../../std/mem/fn.variant_count.html)
+    #[rustc_const_unstable(feature = "variant_count", issue = "73662")]
+    #[cfg(not(bootstrap))]
+    pub fn variant_count<T>() -> usize;
+
     /// Rust's "try catch" construct which invokes the function pointer `try_fn`
     /// with the data pointer `data`.
     ///
@@ -1960,6 +1969,12 @@ extern "rust-intrinsic" {
     pub fn ptr_guaranteed_ne<T>(ptr: *const T, other: *const T) -> bool;
 }
 
+#[rustc_const_unstable(feature = "variant_count", issue = "73662")]
+#[cfg(bootstrap)]
+pub const fn variant_count<T>() -> usize {
+    0
+}
+
 // Some functions are defined here because they accidentally got made
 // available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
 // (`transmute` also falls into this category, but it cannot be wrapped due to the
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index 4eb2fdbd078..2b26e5303a8 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -124,6 +124,7 @@
 #![feature(unsized_locals)]
 #![feature(untagged_unions)]
 #![feature(unwind_attributes)]
+#![feature(variant_count)]
 #![feature(doc_alias)]
 #![feature(mmx_target_feature)]
 #![feature(tbm_target_feature)]
diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs
index 066bb8b3dc7..1bd7ae3a34e 100644
--- a/src/libcore/mem/mod.rs
+++ b/src/libcore/mem/mod.rs
@@ -999,3 +999,33 @@ impl<T> fmt::Debug for Discriminant<T> {
 pub const fn discriminant<T>(v: &T) -> Discriminant<T> {
     Discriminant(intrinsics::discriminant_value(v))
 }
+
+/// Returns the number of variants in the enum type `T`.
+///
+/// If `T` is not an enum, calling this function will not result in undefined behavior, but the
+/// return value is unspecified. Equally, if `T` is an enum with more variants than `usize::MAX`
+/// the return value is unspecified. Uninhabited variants will be counted.
+///
+/// # Examples
+///
+/// ```
+/// # #![feature(never_type)]
+/// # #![feature(variant_count)]
+///
+/// use std::mem;
+///
+/// enum Void {}
+/// enum Foo { A(&'static str), B(i32), C(i32) }
+///
+/// assert_eq!(mem::variant_count::<Void>(), 0);
+/// assert_eq!(mem::variant_count::<Foo>(), 3);
+///
+/// assert_eq!(mem::variant_count::<Option<!>>(), 2);
+/// assert_eq!(mem::variant_count::<Result<!, !>>(), 2);
+/// ```
+#[inline(always)]
+#[unstable(feature = "variant_count", issue = "73662")]
+#[rustc_const_unstable(feature = "variant_count", issue = "73662")]
+pub const fn variant_count<T>() -> usize {
+    intrinsics::variant_count::<T>()
+}