diff options
| author | Manish Goregaokar <manishsmail@gmail.com> | 2020-06-25 18:00:07 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-25 18:00:07 -0700 |
| commit | c50d9816c7b8fee1a7fa2fb7c6c47fc9b9ddd83f (patch) | |
| tree | ea0ef58a17b46f3a5cbe679c4de3276990336d12 /src/libcore | |
| parent | 23c9ac6b730f4e71b47f8714420f2609537b7114 (diff) | |
| parent | 493199626baf8a0a8f6d3a19a089165d18b3f1fb (diff) | |
| download | rust-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.rs | 15 | ||||
| -rw-r--r-- | src/libcore/lib.rs | 1 | ||||
| -rw-r--r-- | src/libcore/mem/mod.rs | 30 |
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>() +} |
