diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 50e321f9c7158f2fb2ec0b216d2012550d332b3f..4074087e1e67f58b0a36d64b5d164760adcede83 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1917,6 +1917,15 @@ #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] pub fn discriminant_value(v: &T) -> ::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() -> usize; + /// Rust's "try catch" construct which invokes the function pointer `try_fn` /// with the data pointer `data`. /// @@ -1960,6 +1969,12 @@ pub fn ptr_guaranteed_ne(ptr: *const T, other: *const T) -> bool; } +#[rustc_const_unstable(feature = "variant_count", issue = "73662")] +#[cfg(bootstrap)] +pub const fn variant_count() -> usize { + 0 +} + // Some functions are defined here because they accidentally got made // available in this module on stable. See . // (`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 4eb2fdbd07868a83352e199925cc4780e046d19b..2b26e5303a89cee5d67838452fb04d3cd3444b04 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 066bb8b3dc787a40ec00d36d7883dbcffb7a9ad6..d041571f26079eae2be466b776b24edaa4d5953d 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -999,3 +999,30 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { pub const fn discriminant(v: &T) -> Discriminant { 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 +/// +/// ``` +/// use std::mem; +/// +/// enum Void {} +/// enum Foo { A(&'static str), B(i32), C(i32) } +/// +/// assert_eq!(mem::variant_count::(), 0); +/// assert_eq!(mem::variant_count::(), 3); +/// +/// assert_eq!(mem::variant_count::>(), 2); +/// assert_eq!(mem::variant_count::>(), 2); +/// ``` +#[inline(always)] +#[unstable(feature = "variant_count", issue = "73662")] +#[rustc_const_unstable(feature = "variant_count", issue = "73662")] +pub const fn variant_count() -> usize { + intrinsics::variant_count::() +} diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 0a8525f06fa3d7d04d9208188489cd396e00d3c9..e43c814a6125ee5e4e90b2d7ea6b03ff50e3c451 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -213,7 +213,7 @@ fn codegen_intrinsic_call( } } "size_of" | "pref_align_of" | "min_align_of" | "needs_drop" | "type_id" - | "type_name" => { + | "type_name" | "variant_count" => { let value = self .tcx .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 6ac1e6be03674a9e1cc529ea69548b219c73ba0a..88ba28dab82e18081ea8ce1756a62b212081916b 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -69,6 +69,13 @@ fn numeric_intrinsic<'tcx, Tag>( ConstValue::from_machine_usize(n, &tcx) } sym::type_id => ConstValue::from_u64(tcx.type_id_hash(tp_ty)), + sym::variant_count => { + if let ty::Adt(ref adt, _) = tp_ty.kind { + ConstValue::from_machine_usize(adt.variants.len() as u64, &tcx) + } else { + ConstValue::from_machine_usize(0u64, &tcx) + } + } other => bug!("`{}` is not a zero arg intrinsic", other), }) } @@ -109,10 +116,13 @@ pub fn emulate_intrinsic( | sym::needs_drop | sym::size_of | sym::type_id - | sym::type_name => { + | sym::type_name + | sym::variant_count => { let gid = GlobalId { instance, promoted: None }; let ty = match intrinsic_name { - sym::min_align_of | sym::pref_align_of | sym::size_of => self.tcx.types.usize, + sym::min_align_of | sym::pref_align_of | sym::size_of | sym::variant_count => { + self.tcx.types.usize + } sym::needs_drop => self.tcx.types.bool, sym::type_id => self.tcx.types.u64, sym::type_name => self.tcx.mk_static_str(), diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 06d1f36622b94ef101b913a73f293c9c5793b1b7..07b05d37ed303c3d723b0151da8733349fae6fef 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -830,6 +830,7 @@ v1, val, var, + variant_count, vec, Vec, version, diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index ef6c7c14404a7239b9bf5b840e7f37f67ed5c66d..1c0b22ca7370bd5da2bdad633619a2abe85058c4 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -75,7 +75,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: &str) -> hir::Unsafety { | "saturating_sub" | "rotate_left" | "rotate_right" | "ctpop" | "ctlz" | "cttz" | "bswap" | "bitreverse" | "discriminant_value" | "type_id" | "likely" | "unlikely" | "ptr_guaranteed_eq" | "ptr_guaranteed_ne" | "minnumf32" | "minnumf64" | "maxnumf32" - | "maxnumf64" | "type_name" => hir::Unsafety::Normal, + | "maxnumf64" | "type_name" | "variant_count" => hir::Unsafety::Normal, _ => hir::Unsafety::Unsafe, } } @@ -137,7 +137,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { let unsafety = intrinsic_operation_unsafety(&name[..]); let (n_tps, inputs, output) = match &name[..] { "breakpoint" => (0, Vec::new(), tcx.mk_unit()), - "size_of" | "pref_align_of" | "min_align_of" => (1, Vec::new(), tcx.types.usize), + "size_of" | "pref_align_of" | "min_align_of" | "variant_count" => { + (1, Vec::new(), tcx.types.usize) + } "size_of_val" | "min_align_of_val" => { (1, vec![tcx.mk_imm_ptr(param(0))], tcx.types.usize) } diff --git a/src/test/ui/consts/const-variant-count.rs b/src/test/ui/consts/const-variant-count.rs new file mode 100644 index 0000000000000000000000000000000000000000..f21f6e9c291687462e85423466a7f78f7517d5e0 --- /dev/null +++ b/src/test/ui/consts/const-variant-count.rs @@ -0,0 +1,46 @@ +// run-pass +#![allow(dead_code)] +#![feature(variant_count)] + +use std::mem::variant_count; + +enum Void {} + +enum Foo { + A, + B, + C, +} + +enum Bar { + A, + B, + C, + D(usize), + E { field_1: usize, field_2: Foo }, +} + +struct Baz { + a: u32, + b: *const u8, +} + +const TEST_VOID: usize = variant_count::(); +const TEST_FOO: usize = variant_count::(); +const TEST_BAR: usize = variant_count::(); + +const NO_ICE_STRUCT: usize = variant_count::(); +const NO_ICE_BOOL: usize = variant_count::(); +const NO_ICE_PRIM: usize = variant_count::<*const u8>(); + +fn main() { + assert_eq!(TEST_VOID, 0); + assert_eq!(TEST_FOO, 3); + assert_eq!(TEST_BAR, 5); + assert_eq!(variant_count::(), 0); + assert_eq!(variant_count::(), 3); + assert_eq!(variant_count::(), 5); + assert_eq!(variant_count::>(), 2); + assert_eq!(variant_count::>(), 2); + assert_eq!(variant_count::>(), 2); +}