Skip to content

Commit c50d981

Browse files
authoredJun 26, 2020
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.)
2 parents 23c9ac6 + 4931996 commit c50d981

File tree

8 files changed

+111
-5
lines changed

8 files changed

+111
-5
lines changed
 

‎src/libcore/intrinsics.rs

+15
Original file line numberDiff line numberDiff line change
@@ -1917,6 +1917,15 @@ extern "rust-intrinsic" {
19171917
#[rustc_const_unstable(feature = "const_discriminant", issue = "69821")]
19181918
pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
19191919

1920+
/// Returns the number of variants of the type `T` cast to a `usize`;
1921+
/// if `T` has no variants, returns 0. Uninhabited variants will be counted.
1922+
///
1923+
/// The to-be-stabilized version of this intrinsic is
1924+
/// [`std::mem::variant_count`](../../std/mem/fn.variant_count.html)
1925+
#[rustc_const_unstable(feature = "variant_count", issue = "73662")]
1926+
#[cfg(not(bootstrap))]
1927+
pub fn variant_count<T>() -> usize;
1928+
19201929
/// Rust's "try catch" construct which invokes the function pointer `try_fn`
19211930
/// with the data pointer `data`.
19221931
///
@@ -1960,6 +1969,12 @@ extern "rust-intrinsic" {
19601969
pub fn ptr_guaranteed_ne<T>(ptr: *const T, other: *const T) -> bool;
19611970
}
19621971

1972+
#[rustc_const_unstable(feature = "variant_count", issue = "73662")]
1973+
#[cfg(bootstrap)]
1974+
pub const fn variant_count<T>() -> usize {
1975+
0
1976+
}
1977+
19631978
// Some functions are defined here because they accidentally got made
19641979
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
19651980
// (`transmute` also falls into this category, but it cannot be wrapped due to the

‎src/libcore/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
#![feature(unsized_locals)]
125125
#![feature(untagged_unions)]
126126
#![feature(unwind_attributes)]
127+
#![feature(variant_count)]
127128
#![feature(doc_alias)]
128129
#![feature(mmx_target_feature)]
129130
#![feature(tbm_target_feature)]

‎src/libcore/mem/mod.rs

+30
Original file line numberDiff line numberDiff line change
@@ -999,3 +999,33 @@ impl<T> fmt::Debug for Discriminant<T> {
999999
pub const fn discriminant<T>(v: &T) -> Discriminant<T> {
10001000
Discriminant(intrinsics::discriminant_value(v))
10011001
}
1002+
1003+
/// Returns the number of variants in the enum type `T`.
1004+
///
1005+
/// If `T` is not an enum, calling this function will not result in undefined behavior, but the
1006+
/// return value is unspecified. Equally, if `T` is an enum with more variants than `usize::MAX`
1007+
/// the return value is unspecified. Uninhabited variants will be counted.
1008+
///
1009+
/// # Examples
1010+
///
1011+
/// ```
1012+
/// # #![feature(never_type)]
1013+
/// # #![feature(variant_count)]
1014+
///
1015+
/// use std::mem;
1016+
///
1017+
/// enum Void {}
1018+
/// enum Foo { A(&'static str), B(i32), C(i32) }
1019+
///
1020+
/// assert_eq!(mem::variant_count::<Void>(), 0);
1021+
/// assert_eq!(mem::variant_count::<Foo>(), 3);
1022+
///
1023+
/// assert_eq!(mem::variant_count::<Option<!>>(), 2);
1024+
/// assert_eq!(mem::variant_count::<Result<!, !>>(), 2);
1025+
/// ```
1026+
#[inline(always)]
1027+
#[unstable(feature = "variant_count", issue = "73662")]
1028+
#[rustc_const_unstable(feature = "variant_count", issue = "73662")]
1029+
pub const fn variant_count<T>() -> usize {
1030+
intrinsics::variant_count::<T>()
1031+
}

‎src/librustc_codegen_llvm/intrinsic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
206206
}
207207
}
208208
"size_of" | "pref_align_of" | "min_align_of" | "needs_drop" | "type_id"
209-
| "type_name" => {
209+
| "type_name" | "variant_count" => {
210210
let value = self
211211
.tcx
212212
.const_eval_instance(ty::ParamEnv::reveal_all(), instance, None)

‎src/librustc_mir/interpret/intrinsics.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ crate fn eval_nullary_intrinsic<'tcx>(
6969
ConstValue::from_machine_usize(n, &tcx)
7070
}
7171
sym::type_id => ConstValue::from_u64(tcx.type_id_hash(tp_ty)),
72+
sym::variant_count => {
73+
if let ty::Adt(ref adt, _) = tp_ty.kind {
74+
ConstValue::from_machine_usize(adt.variants.len() as u64, &tcx)
75+
} else {
76+
ConstValue::from_machine_usize(0u64, &tcx)
77+
}
78+
}
7279
other => bug!("`{}` is not a zero arg intrinsic", other),
7380
})
7481
}
@@ -109,10 +116,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
109116
| sym::needs_drop
110117
| sym::size_of
111118
| sym::type_id
112-
| sym::type_name => {
119+
| sym::type_name
120+
| sym::variant_count => {
113121
let gid = GlobalId { instance, promoted: None };
114122
let ty = match intrinsic_name {
115-
sym::min_align_of | sym::pref_align_of | sym::size_of => self.tcx.types.usize,
123+
sym::min_align_of | sym::pref_align_of | sym::size_of | sym::variant_count => {
124+
self.tcx.types.usize
125+
}
116126
sym::needs_drop => self.tcx.types.bool,
117127
sym::type_id => self.tcx.types.u64,
118128
sym::type_name => self.tcx.mk_static_str(),

‎src/librustc_span/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,7 @@ symbols! {
832832
v1,
833833
val,
834834
var,
835+
variant_count,
835836
vec,
836837
Vec,
837838
version,

‎src/librustc_typeck/check/intrinsic.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: &str) -> hir::Unsafety {
7575
| "saturating_sub" | "rotate_left" | "rotate_right" | "ctpop" | "ctlz" | "cttz"
7676
| "bswap" | "bitreverse" | "discriminant_value" | "type_id" | "likely" | "unlikely"
7777
| "ptr_guaranteed_eq" | "ptr_guaranteed_ne" | "minnumf32" | "minnumf64" | "maxnumf32"
78-
| "maxnumf64" | "type_name" => hir::Unsafety::Normal,
78+
| "maxnumf64" | "type_name" | "variant_count" => hir::Unsafety::Normal,
7979
_ => hir::Unsafety::Unsafe,
8080
}
8181
}
@@ -137,7 +137,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
137137
let unsafety = intrinsic_operation_unsafety(&name[..]);
138138
let (n_tps, inputs, output) = match &name[..] {
139139
"breakpoint" => (0, Vec::new(), tcx.mk_unit()),
140-
"size_of" | "pref_align_of" | "min_align_of" => (1, Vec::new(), tcx.types.usize),
140+
"size_of" | "pref_align_of" | "min_align_of" | "variant_count" => {
141+
(1, Vec::new(), tcx.types.usize)
142+
}
141143
"size_of_val" | "min_align_of_val" => {
142144
(1, vec![tcx.mk_imm_ptr(param(0))], tcx.types.usize)
143145
}
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// run-pass
2+
#![allow(dead_code)]
3+
#![feature(variant_count)]
4+
#![feature(never_type)]
5+
6+
use std::mem::variant_count;
7+
8+
enum Void {}
9+
10+
enum Foo {
11+
A,
12+
B,
13+
C,
14+
}
15+
16+
enum Bar {
17+
A,
18+
B,
19+
C,
20+
D(usize),
21+
E { field_1: usize, field_2: Foo },
22+
}
23+
24+
struct Baz {
25+
a: u32,
26+
b: *const u8,
27+
}
28+
29+
const TEST_VOID: usize = variant_count::<Void>();
30+
const TEST_FOO: usize = variant_count::<Foo>();
31+
const TEST_BAR: usize = variant_count::<Bar>();
32+
33+
const NO_ICE_STRUCT: usize = variant_count::<Baz>();
34+
const NO_ICE_BOOL: usize = variant_count::<bool>();
35+
const NO_ICE_PRIM: usize = variant_count::<*const u8>();
36+
37+
fn main() {
38+
assert_eq!(TEST_VOID, 0);
39+
assert_eq!(TEST_FOO, 3);
40+
assert_eq!(TEST_BAR, 5);
41+
assert_eq!(variant_count::<Void>(), 0);
42+
assert_eq!(variant_count::<Foo>(), 3);
43+
assert_eq!(variant_count::<Bar>(), 5);
44+
assert_eq!(variant_count::<Option<char>>(), 2);
45+
assert_eq!(variant_count::<Option<!>>(), 2);
46+
assert_eq!(variant_count::<Result<!, !>>(), 2);
47+
}

0 commit comments

Comments
 (0)
Please sign in to comment.