Skip to content

Commit c414965

Browse files
committed
reflection: add coretests for variant_getter
1 parent 5204d40 commit c414965

7 files changed

Lines changed: 162 additions & 38 deletions

File tree

‎compiler/rustc_const_eval/src/const_eval/type_info/adt.rs‎

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,25 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
1515
place: &impl Writeable<'tcx, CtfeProvenance>,
1616
ty: Ty<'tcx>,
1717
) -> InterpResult<'tcx, ()> {
18-
// a discriminant is an int of some size (up to 128bits or 127... ?) // TODO(yara) figure out
1918
let cast_to = ty.discriminant_ty(*self.tcx);
2019
let discriminant_size = cast_to.primitive_size(*self.tcx);
2120

22-
let ty::Adt(def, _) = ty.kind() else {
23-
span_bug!(self.cur_span(), "expected enum type, got {ty}");
21+
let num_variants = if let ty::Adt(def, _) = ty.kind()
22+
&& def.is_enum()
23+
{
24+
def.variants().len() as u64
25+
} else {
26+
0
2427
};
25-
self.allocate_fill_and_write_slice_ptr(
26-
place,
27-
def.variants().len() as u64,
28-
|this, i, place| {
29-
let discriminant = ty
30-
.discriminant_for_variant(*this.tcx, VariantIdx::from_usize(i as usize))
31-
.expect("i is in range")
32-
.val;
33-
let discriminant = ScalarInt::try_from_uint(discriminant, discriminant_size)
34-
.expect("127 bits should be enough discriminant"); // TODO(yara): test this
35-
this.write_scalar(discriminant, &place)
36-
},
37-
)
28+
self.allocate_fill_and_write_slice_ptr(place, num_variants, |this, i, place| {
29+
let discriminant = ty
30+
.discriminant_for_variant(*this.tcx, VariantIdx::from_usize(i as usize))
31+
.expect("i is in range")
32+
.val;
33+
let discriminant = ScalarInt::try_from_uint(discriminant, discriminant_size)
34+
.expect("discriminant ty fits the current value");
35+
this.write_scalar(discriminant, &place)
36+
})
3837
}
3938

4039
// FIXME(type_info): No semver considerations for now

‎compiler/rustc_hir_analysis/src/check/intrinsic.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ pub(crate) fn check_intrinsic_type(
319319
let variant_id =
320320
tcx.type_of(tcx.lang_items().variant_id().unwrap()).no_bound_vars().unwrap();
321321
let unit_ptr = Ty::new_ptr(tcx, tcx.types.unit, ty::Mutability::Not);
322-
let fn_ty = ty::Binder::dummy(tcx.mk_fn_sig_safe_rust_abi([unit_ptr], variant_id));
322+
let fn_ty = ty::Binder::dummy(tcx.mk_fn_sig_unsafe_rust_abi([unit_ptr], variant_id));
323323
(0, 0, vec![type_id_ty()], Ty::new_fn_ptr(tcx, fn_ty))
324324
}
325325
sym::type_id_variant_lookup_table => {

‎library/core/src/intrinsics/reflection.rs‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,12 @@ pub fn type_id_name(_type_id: crate::any::TypeId) -> &'static str;
236236
#[rustc_intrinsic]
237237
#[unstable(feature = "core_intrinsics", issue = "none")]
238238
#[rustc_comptime]
239-
pub fn type_id_variant_getter(_type_id: crate::any::TypeId) -> fn(*const ()) -> VariantId;
239+
pub fn type_id_variant_getter(_type_id: crate::any::TypeId) -> unsafe fn(*const ()) -> VariantId;
240240

241-
/// Returns a lookup table for converting between VariantId and Discriminant. This
242-
/// is use internally by variant getter. **Please do not use this.**
241+
/// **Please do not use this intrinsic directly.**
242+
///
243+
/// This returns a lookup table for converting between VariantId and Discriminant. This is use
244+
/// internally by [`variant_getter`](core::any::TypeId::variant_getter).
243245
///
244246
/// The user-friendly way to get the `VariantId` of an enum at runtime is using
245247
/// the function pointer returned by [`core::any::TypeId::variant_getter`].

‎library/core/src/mem/type_info.rs‎

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,34 @@ pub struct Type {
2020
/// This function is not called directly instead a pointer to a monomorphized
2121
/// instance of it is returned by [`TypeId::variant_getter`].
2222
///
23+
/// # Errors
24+
/// It's an ICE to call this with anything not an enum. This is guarded for
25+
/// by [`TypeId::variant_getter`].
26+
///
2327
/// # Safety
2428
///
2529
/// See [`TypeId::variant_getter`] on how to safely use this function.
2630
#[lang = "variant_getter"]
27-
unsafe fn variant_getter<T: 'static>(val: *const ()) -> VariantId {
28-
// SAFETY up to the caller to uphold, documented in `TypeId::variant_getter`.
31+
unsafe fn variant_getter<T>(val: *const ()) -> VariantId
32+
where
33+
<T as DiscriminantKind>::Discriminant: 'static,
34+
{
35+
// SAFETY: up to the caller to uphold, documented in `TypeId::variant_getter`.
2936
let v: &T = unsafe { &*val.cast::<T>() };
3037
let discriminant = core::mem::discriminant(v);
3138
let lookup_table: &'static [<T as DiscriminantKind>::Discriminant] =
3239
const { intrinsics::reflection::type_id_variant_lookup_table::<T>() };
3340

41+
if lookup_table.is_empty() {
42+
// for types other then enums the table will be empty
43+
return VariantId { base: const { of::<T>() }, variant: 0 };
44+
}
45+
3446
let variant = lookup_table
3547
.iter()
3648
.position(|d| Discriminant(*d) == discriminant)
3749
.expect("table contains all possible discriminants");
38-
VariantId { base: const { TypeId::of::<T>() }, variant }
50+
VariantId { base: const { of::<T>() }, variant }
3951
}
4052

4153
/// Info of a trait implementation, you can retrieve the vtable with [Self::get_vtable]
@@ -708,6 +720,9 @@ impl TypeId {
708720
/// When called on a `TypeId` representing some enum `T` this returns a function pointer
709721
/// which can be used to get the `VariantId` of an instance of `T` at runtime.
710722
///
723+
/// When given a `TypeId` for something which does not represent an enum the returned
724+
/// function can still be called. It will then return the same as `TypeId::variant(0)`.
725+
///
711726
/// # Safety
712727
///
713728
/// While calling this function is completely safe the returned function pointer
@@ -719,39 +734,49 @@ impl TypeId {
719734
/// # Examples
720735
///
721736
/// ```
722-
/// #![feature(type_info)]
723-
/// # use std::any::TypeId;
737+
/// # #![feature(const_trait_impl)]
738+
/// # #![feature(const_heap)]
739+
/// # #![feature(type_info)]
740+
/// use std::any::TypeId;
724741
/// enum Kitty {
725742
/// Sleepy,
726743
/// Playful,
727744
/// Cuddly,
728745
/// }
729-
///
746+
730747
/// let names = const {
731748
/// let type_id = TypeId::of::<Kitty>();
732749
/// let mut names = Vec::new();
733-
///
750+
734751
/// while names.len() < type_id.variants() {
735-
/// let variant = type_id.variant(names.len());
736-
/// names.push(variant.name());
752+
/// let variant_id = type_id.variant(names.len());
753+
/// names.push((variant_id, variant_id.name()));
737754
/// }
738-
/// names
755+
/// names.const_make_global()
739756
/// };
740757
/// let variant_getter = const { TypeId::of::<Kitty>().variant_getter() };
741-
///
758+
742759
/// let kitty = Kitty::Playful;
743-
/// // Normally when using this API you no longer have the Type but only
744-
/// // a type erased pointer.
745-
/// let type_erased_kitty = (*&kitty) as *const ();
746-
///
747-
/// let variant_idx = variant_getter(type_erased_kitty);
748-
/// println!("kitty is: {}", names[variant_idx].to_lower());
760+
/// // Normally when using the getter you are manually offsetting pointers and you have no type.
761+
/// // We'll pretend here for brevity.
762+
/// let kitty_ptr = &kitty as *const Kitty;
763+
/// let kitty_ptr = kitty_ptr.cast::<()>();
764+
765+
/// let variant_id = variant_getter(kitty_ptr);
766+
/// let kitty_is_feeling = names
767+
/// .iter()
768+
/// .find(|(id, _)| *id == variant_id)
769+
/// .map(|(_, variant_name)| *variant_name)
770+
/// .expect("We have a list of steps for each VariantId")
771+
/// .to_lowercase();
772+
773+
/// assert_eq!(kitty_is_feeling, "playful");
749774
/// ```
750775
///
751776
#[unstable(feature = "type_info", issue = "146922")]
752777
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
753778
#[rustc_comptime]
754-
pub fn variant_getter(self) -> fn(*const ()) -> VariantId {
779+
pub fn variant_getter(self) -> unsafe fn(*const ()) -> VariantId {
755780
intrinsics::reflection::type_id_variant_getter(self)
756781
}
757782
}

‎library/coretests/tests/lib.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#![feature(const_destruct)]
3636
#![feature(const_drop_in_place)]
3737
#![feature(const_eval_select)]
38+
#![feature(const_heap)]
3839
#![feature(const_index)]
3940
#![feature(const_iter)]
4041
#![feature(const_nonzero_strict_ops)]

‎library/coretests/tests/mem/type_info.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::any::{Any, TypeId};
44
use std::mem::offset_of;
55
use std::mem::type_info::{Const, Generic, GenericType, Type, TypeKind};
66

7+
mod runtime;
8+
79
#[test]
810
fn test_arrays() {
911
// Normal array.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::any::TypeId;
2+
3+
fn as_unit_ptr<T: 'static>(test_case: &T) -> *const () {
4+
(test_case as *const T).cast()
5+
}
6+
7+
fn assert_unit_variant_getter<T: 'static>(test_input: &T) {
8+
let variant_getter = const { TypeId::of::<T>().variant_getter() };
9+
assert_eq!(variant_getter(as_unit_ptr(test_input)), const { TypeId::of::<T>().variant(0) });
10+
}
11+
12+
#[test]
13+
fn correct_variant_id() {
14+
use std::any::TypeId;
15+
#[repr(u128)]
16+
enum Kitty {
17+
Sleepy,
18+
Playful,
19+
Cuddly = u128::MAX,
20+
}
21+
22+
let variant_getter = const { TypeId::of::<Kitty>().variant_getter() };
23+
assert_eq!(
24+
variant_getter(as_unit_ptr(&Kitty::Sleepy)),
25+
const { TypeId::of::<Kitty>().variant(0) }
26+
);
27+
assert_eq!(
28+
variant_getter(as_unit_ptr(&Kitty::Playful)),
29+
const { TypeId::of::<Kitty>().variant(1) }
30+
);
31+
assert_eq!(
32+
variant_getter(as_unit_ptr(&Kitty::Cuddly)),
33+
const { TypeId::of::<Kitty>().variant(2) }
34+
);
35+
}
36+
37+
#[test]
38+
fn variant_getter_for_unit() {
39+
assert_unit_variant_getter(&());
40+
}
41+
42+
#[test]
43+
fn array() {
44+
assert_unit_variant_getter::<[u8; 3]>(&[0, 1, 2]);
45+
}
46+
47+
#[test]
48+
fn slice() {
49+
assert_unit_variant_getter::<&[u8]>(&[0, 1, 2].as_slice());
50+
}
51+
52+
#[test]
53+
fn strukt() {
54+
assert_unit_variant_getter(&String::new());
55+
}
56+
57+
#[test]
58+
fn union() {
59+
union TestUnion {
60+
first: i16,
61+
}
62+
let test_union = TestUnion { first: 0 };
63+
assert_unit_variant_getter(&test_union);
64+
}
65+
66+
#[test]
67+
fn primitives() {
68+
assert_unit_variant_getter(&true);
69+
assert_unit_variant_getter(&0u8);
70+
assert_unit_variant_getter(&'a');
71+
}
72+
73+
#[test]
74+
fn reference_types() {
75+
assert_unit_variant_getter(&&true);
76+
}
77+
78+
#[test]
79+
fn pointers_types() {
80+
let test_value = true;
81+
let test_pointer = &test_value as *const bool;
82+
assert_unit_variant_getter(&test_pointer);
83+
}
84+
85+
#[test]
86+
fn trait_obj() {
87+
let test_value = Box::new(String::new());
88+
let test_object = Box::leak(test_value) as &dyn std::fmt::Debug;
89+
assert_unit_variant_getter(&test_object);
90+
}
91+
92+
#[test]
93+
fn never_type() {
94+
let variant_getter = const { TypeId::of::<!>().variant_getter() };
95+
}

0 commit comments

Comments
 (0)