Skip to content

Commit d36d351

Browse files
committed
Implement intrinsic
1 parent 1557fb0 commit d36d351

File tree

8 files changed

+107
-5
lines changed

8 files changed

+107
-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

+27
Original file line numberDiff line numberDiff line change
@@ -999,3 +999,30 @@ 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+
/// use std::mem;
1013+
///
1014+
/// enum Void {}
1015+
/// enum Foo { A(&'static str), B(i32), C(i32) }
1016+
///
1017+
/// assert_eq!(mem::variant_count::<Void>(), 0);
1018+
/// assert_eq!(mem::variant_count::<Foo>(), 3);
1019+
///
1020+
/// assert_eq!(mem::variant_count::<Option<!>>(), 2);
1021+
/// assert_eq!(mem::variant_count::<Result<!, !>>(), 2);
1022+
/// ```
1023+
#[inline(always)]
1024+
#[unstable(feature = "variant_count", issue = "73662")]
1025+
#[rustc_const_unstable(feature = "variant_count", issue = "73662")]
1026+
pub const fn variant_count<T>() -> usize {
1027+
intrinsics::variant_count::<T>()
1028+
}

src/librustc_codegen_llvm/intrinsic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
213213
}
214214
}
215215
"size_of" | "pref_align_of" | "min_align_of" | "needs_drop" | "type_id"
216-
| "type_name" => {
216+
| "type_name" | "variant_count" => {
217217
let value = self
218218
.tcx
219219
.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
@@ -830,6 +830,7 @@ symbols! {
830830
v1,
831831
val,
832832
var,
833+
variant_count,
833834
vec,
834835
Vec,
835836
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
}
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// run-pass
2+
#![allow(dead_code)]
3+
#![feature(variant_count)]
4+
5+
use std::mem::variant_count;
6+
7+
enum Void {}
8+
9+
enum Foo {
10+
A,
11+
B,
12+
C,
13+
}
14+
15+
enum Bar {
16+
A,
17+
B,
18+
C,
19+
D(usize),
20+
E { field_1: usize, field_2: Foo },
21+
}
22+
23+
struct Baz {
24+
a: u32,
25+
b: *const u8,
26+
}
27+
28+
const TEST_VOID: usize = variant_count::<Void>();
29+
const TEST_FOO: usize = variant_count::<Foo>();
30+
const TEST_BAR: usize = variant_count::<Bar>();
31+
32+
const NO_ICE_STRUCT: usize = variant_count::<Baz>();
33+
const NO_ICE_BOOL: usize = variant_count::<bool>();
34+
const NO_ICE_PRIM: usize = variant_count::<*const u8>();
35+
36+
fn main() {
37+
assert_eq!(TEST_VOID, 0);
38+
assert_eq!(TEST_FOO, 3);
39+
assert_eq!(TEST_BAR, 5);
40+
assert_eq!(variant_count::<Void>(), 0);
41+
assert_eq!(variant_count::<Foo>(), 3);
42+
assert_eq!(variant_count::<Bar>(), 5);
43+
assert_eq!(variant_count::<Option<char>>(), 2);
44+
assert_eq!(variant_count::<Option<!>>(), 2);
45+
assert_eq!(variant_count::<Result<!, !>>(), 2);
46+
}

0 commit comments

Comments
 (0)