diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index d715a6d3af8c5..c68d5cdb86a20 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -470,6 +470,8 @@ language_item_table! { // Experimental lang item for `Reflection and comptime`(https://goals.rust-lang.org/2025h2/reflection-and-comptime.html) FnPtr, sym::FnPtr, fn_ptr, Target::Struct, GenericRequirement::None; + VariantId, sym::VariantId, variant_id, Target::Struct, GenericRequirement::None; + VariantGetter, sym::variant_getter, variant_getter, Target::Fn, GenericRequirement::Exact(1); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index cb9432f9832e5..e8ffc12be7936 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::AssertMessage; use rustc_middle::mir::interpret::ReportedErrorInfo; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, TyAndLayout, ValidityRequirement}; -use rustc_middle::ty::{self, FieldInfo, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::{self, FieldInfo, Instance, ScalarInt, Ty, TyCtxt}; use rustc_middle::{mir, throw_machine_stop}; use rustc_span::{Span, Symbol, bug, span_bug, sym}; use rustc_target::callconv::FnAbi; @@ -22,8 +22,9 @@ use tracing::debug; use super::error::*; use crate::diagnostics::{LongRunning, LongRunningWarn}; +use crate::interpret::Immediate::ScalarPair; use crate::interpret::{ - self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, + self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, Frame, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, RetagMode, Scalar, compile_time_machine, ensure_monomorphic_enough, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_format, throw_unsup, throw_unsup_format, @@ -699,7 +700,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_scalar(Scalar::from_target_usize(fields_num as u64, ecx), dest)?; } - sym::type_id_field_representing_type => { + sym::type_id_field_representing => { let ty = ecx.read_type_id(&args[0])?; let variant_idx = ecx.read_target_usize(&args[1])? as usize; let field_idx = ecx.read_target_usize(&args[2])? as usize; @@ -770,6 +771,32 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_scalar(Scalar::from_target_usize(variants_num as u64, ecx), dest)?; } + sym::type_id_variant_getter => { + let ty = ecx.read_type_id(&args[0])?; + let getter_def_id = + ecx.tcx().require_lang_item(LangItem::VariantGetter, rustc_span::DUMMY_SP); + let generic_arg = ecx.tcx().mk_args(&[ty.into()]); + let instance = Instance::expect_resolve( + ecx.tcx(), + ecx.typing_env(), + getter_def_id, + generic_arg, + rustc_span::DUMMY_SP, + ); + let fn_ptr = ecx.fn_ptr(FnVal::Instance(instance)); + ecx.write_pointer(fn_ptr, dest)?; + } + + sym::type_id_variant_lookup_table => { + let ty = instance + .args + .first() + .expect("has one generic") + .as_type() + .expect("the generic arg is a Ty"); + ecx.write_slice_of_discriminant(dest, ty)?; + } + sym::type_id_variant_name => { let base = ecx.read_type_id(&args[0])?; @@ -796,6 +823,20 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { )?; } + sym::type_id_name => { + let ty = ecx.read_type_id(&args[0])?; + ensure_monomorphic_enough(ty)?; + let name = crate::util::type_name(ecx.tcx(), ty); + let ptr = ecx.allocate_bytes_dedup(name.as_str().as_bytes())?; + ecx.write_immediate( + ScalarPair( + Scalar::from_pointer(ptr, ecx), + Scalar::from_target_usize(name.as_str().len() as u64, ecx), + ), + dest, + )?; + } + sym::type_id_variant_non_exhaustive => { let base = ecx.read_type_id(&args[0])?; @@ -836,28 +877,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_scalar(Scalar::from_target_usize(offset, ecx), dest)?; } - sym::field_representing_type_name => { - let frt_ty = ecx.read_type_id(&args[0])?; - - let field_name = if let ty::Adt(def, args) = frt_ty.kind() - && let Some(FieldInfo { name, .. }) = - def.field_representing_type_info(ecx.tcx.tcx, args) - { - name - } else { - span_bug!(ecx.cur_span(), "expected field representing type, got {frt_ty}") - }; - let ptr = ecx.allocate_bytes_dedup(field_name.as_str().as_bytes())?; - ecx.write_immediate( - Immediate::ScalarPair( - Scalar::from_pointer(ptr, ecx), - Scalar::from_target_usize(field_name.as_str().len() as u64, ecx), - ), - dest, - )?; - } - - sym::field_representing_type_offset => { + sym::type_id_field_representing_field_offset => { let frt_ty = ecx.read_type_id(&args[0])?; let (ty, variant, field) = if let ty::Adt(def, args) = frt_ty.kind() @@ -877,7 +897,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_scalar(Scalar::from_target_usize(offset, ecx), dest)?; } - sym::field_representing_type_actual_type_id => { + sym::type_id_field_representing_outer_type_id => { let frt_ty = ecx.read_type_id(&args[0])?; let field_ty = if let ty::Adt(def, args) = frt_ty.kind() diff --git a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs index 4ff1b37e953d4..da35edecaff96 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs @@ -1,6 +1,6 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_middle::ty::{ - AdtDef, AdtKind, Const, ConstKind, GenericArgKind, GenericArgs, Region, Ty, + self, AdtDef, AdtKind, Const, ConstKind, GenericArgKind, GenericArgs, Region, ScalarInt, Ty, }; use rustc_span::{bug, span_bug, sym}; @@ -10,6 +10,32 @@ use crate::interpret::{ }; impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { + pub(crate) fn write_slice_of_discriminant( + &mut self, + place: &impl Writeable<'tcx, CtfeProvenance>, + ty: Ty<'tcx>, + ) -> InterpResult<'tcx, ()> { + let cast_to = ty.discriminant_ty(*self.tcx); + let discriminant_size = cast_to.primitive_size(*self.tcx); + + let num_variants = if let ty::Adt(def, _) = ty.kind() + && def.is_enum() + { + def.variants().len() as u64 + } else { + 0 + }; + self.allocate_fill_and_write_slice_ptr(place, num_variants, |this, i, place| { + let discriminant = ty + .discriminant_for_variant(*this.tcx, VariantIdx::from_usize(i as usize)) + .expect("i is in range") + .val; + let discriminant = ScalarInt::try_from_uint(discriminant, discriminant_size) + .expect("discriminant ty fits the current value"); + this.write_scalar(discriminant, &place) + }) + } + // FIXME(type_info): No semver considerations for now pub(crate) fn write_adt_type_info( &mut self, diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 678fc35c07a7c..d8b197de2e9db 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -77,7 +77,7 @@ pub enum VarArgCompatible { /// Directly returns an `Allocation` containing an absolute path representation of the given type. pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) { - let path = crate::util::type_name(tcx, ty); + let path = crate::util::type_name_and_path(tcx, ty); let bytes = path.into_bytes(); let len = bytes.len().try_into().unwrap(); (tcx.allocate_bytes_dedup(bytes, CTFE_ALLOC_SALT), len) diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 091f0f71cc50d..99f2c477623ff 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -4,6 +4,7 @@ #![feature(deref_patterns)] #![feature(slice_ptr_get)] #![feature(trait_alias)] +#![feature(try_blocks)] #![feature(unqualified_local_imports)] #![feature(yeet_expr)] #![warn(unqualified_local_imports)] diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 288a4df727c3d..dd51a128f9a86 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -14,7 +14,7 @@ pub use self::alignment::{most_packed_projection, place_unalignment}; pub use self::check_validity_requirement::check_validity_requirement; pub(crate) use self::check_validity_requirement::validate_scalar_in_layout; pub use self::compare_types::{relate_types, sub_types}; -pub use self::type_name::type_name; +pub use self::type_name::{type_name, type_name_and_path}; /// Classify whether an operator is "left-homogeneous", i.e., the LHS has the /// same type as the result. diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index d0cd1f57479c0..748edbf6708a0 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -4,7 +4,7 @@ use rustc_data_structures::intern::Interned; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::DisambiguatedDefPathData; use rustc_middle::ty::print::{PrettyPrinter, PrintError, Printer}; -use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt}; +use rustc_middle::ty::{self, FieldInfo, GenericArg, Ty, TyCtxt}; use rustc_span::bug; struct TypeNamePrinter<'tcx> { @@ -213,8 +213,40 @@ impl Write for TypeNamePrinter<'_> { } } -pub fn type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> String { +pub fn type_name_and_path<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> String { let mut p = TypeNamePrinter { tcx, path: String::new() }; p.print_type(ty).unwrap(); p.path } + +pub fn type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> String { + let mut s = String::new(); + + try { + match *ty.kind() { + ty::Adt(def, args) + if let Some(FieldInfo { name, .. }) = + def.field_representing_type_info(tcx, args) => + { + write!(s, "{name}")? + } + ty::Adt(def, _) => write!(s, "{}", tcx.item_name(def.did()))?, + ty::Dynamic(data, _) => { + let trait_def_id = data + .iter() + .map(|b| b.skip_binder()) + .find_map(|pred| match pred { + ty::ExistentialPredicate::Trait(tr) => Some(tr.def_id), + ty::ExistentialPredicate::AutoTrait(def_id) => Some(def_id), + _ => None, + }) + .expect("There should be a principal trait"); + write!(s, "{}", tcx.item_name(trait_def_id))?; + } + _ => (), + } + } + .unwrap(); + + s +} diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 0aa3e97568729..df2af85a3932d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -104,9 +104,6 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::fadd_algebraic | sym::fdiv_algebraic | sym::field_offset - | sym::field_representing_type_actual_type_id - | sym::field_representing_type_name - | sym::field_representing_type_offset | sym::floor | sym::fma | sym::fmul_algebraic @@ -173,16 +170,21 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::type_id_array_len | sym::type_id_element_ty | sym::type_id_eq - | sym::type_id_field_representing_type + | sym::type_id_field_representing + | sym::type_id_field_representing_field_offset + | sym::type_id_field_representing_outer_type_id | sym::type_id_fields | sym::type_id_function_ptr | sym::type_id_generics | sym::type_id_is_signed + | sym::type_id_name | sym::type_id_non_exhaustive | sym::type_id_points_mutably | sym::type_id_points_to | sym::type_id_size_of | sym::type_id_type_of + | sym::type_id_variant_getter + | sym::type_id_variant_lookup_table | sym::type_id_variant_name | sym::type_id_variant_non_exhaustive | sym::type_id_variants @@ -294,7 +296,7 @@ pub(crate) fn check_intrinsic_type( sym::type_id_array_len => (0, 0, vec![type_id_ty()], tcx.types.usize), sym::type_id_element_ty => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, type_id_ty())), sym::type_id_eq => (0, 0, vec![type_id_ty(), type_id_ty()], tcx.types.bool), - sym::type_id_field_representing_type => { + sym::type_id_field_representing => { (0, 0, vec![type_id_ty(), tcx.types.usize, tcx.types.usize], type_id_ty()) } sym::type_id_fields => (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.usize), @@ -313,6 +315,27 @@ pub(crate) fn check_intrinsic_type( sym::type_id_points_mutably => (0, 0, vec![type_id_ty()], tcx.types.bool), sym::type_id_points_to => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, type_id_ty())), sym::type_id_variants => (0, 0, vec![type_id_ty()], tcx.types.usize), + sym::type_id_variant_getter => { + let variant_id = + tcx.type_of(tcx.lang_items().variant_id().unwrap()).no_bound_vars().unwrap(); + let unit_ptr = Ty::new_ptr(tcx, tcx.types.unit, ty::Mutability::Not); + let fn_ty = ty::Binder::dummy(tcx.mk_fn_sig_unsafe_rust_abi([unit_ptr], variant_id)); + (0, 0, vec![type_id_ty()], Ty::new_fn_ptr(tcx, fn_ty)) + } + sym::type_id_variant_lookup_table => { + let assoc_items = tcx + .associated_item_def_ids(tcx.require_lang_item(LangItem::DiscriminantKind, span)); + let discriminant_def_id = assoc_items[0]; + let discriminant_ty = Ty::new_projection_from_args( + tcx, + ty::IsRigid::No, + discriminant_def_id, + tcx.mk_args(&[param(0).into()]), + ); + let slice = Ty::new_slice(tcx, discriminant_ty); + let slice = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, slice); + (1, 0, vec![], slice) + } sym::type_id_variant_name => { (0, 0, vec![type_id_ty(), tcx.types.usize], Ty::new_static_str(tcx)) } @@ -339,9 +362,9 @@ pub(crate) fn check_intrinsic_type( vec![type_id_ty()], tcx.type_of(tcx.lang_items().type_struct().unwrap()).no_bound_vars().unwrap(), ), - sym::field_representing_type_actual_type_id => (0, 0, vec![type_id_ty()], type_id_ty()), - sym::field_representing_type_name => (0, 0, vec![type_id_ty()], Ty::new_static_str(tcx)), - sym::field_representing_type_offset => (0, 0, vec![type_id_ty()], tcx.types.usize), + sym::type_id_field_representing_outer_type_id => (0, 0, vec![type_id_ty()], type_id_ty()), + sym::type_id_field_representing_field_offset => (0, 0, vec![type_id_ty()], tcx.types.usize), + sym::type_id_name => (0, 0, vec![type_id_ty()], Ty::new_static_str(tcx)), sym::type_id_non_exhaustive => (0, 0, vec![type_id_ty()], tcx.types.bool), sym::type_id_generics => ( 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 026a576ed68ce..7dd3706961862 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -362,6 +362,7 @@ symbols! { UnsafeFnPointer, Unsize, UnsizePointee, + VariantId, Vec, Wrapper, _DECLS, @@ -994,9 +995,6 @@ symbols! { field_offset, field_projections, field_representing_type, - field_representing_type_actual_type_id, - field_representing_type_name, - field_representing_type_offset, field_representing_type_raw, field_type, fields, @@ -2209,16 +2207,21 @@ symbols! { type_id_array_len, type_id_element_ty, type_id_eq, - type_id_field_representing_type, + type_id_field_representing, + type_id_field_representing_field_offset, + type_id_field_representing_outer_type_id, type_id_fields, type_id_function_ptr, type_id_generics, type_id_is_signed, + type_id_name, type_id_non_exhaustive, type_id_points_mutably, type_id_points_to, type_id_size_of, type_id_type_of, + type_id_variant_getter, + type_id_variant_lookup_table, type_id_variant_name, type_id_variant_non_exhaustive, type_id_variants, @@ -2361,6 +2364,7 @@ symbols! { var, variadic, variant_count, + variant_getter, vec, vector, verbatim, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 64cf978a4b0e4..5a0bc114f56f4 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2988,38 +2988,6 @@ pub const unsafe fn align_of_val(ptr: *const T) -> usize; #[rustc_comptime] pub fn type_name() -> &'static str; -/// Gets the actual field `TypeId` of the [`FieldRepresentingType`]'s `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::mem::type_info::FieldId::type_id`]. -/// -/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn field_representing_type_actual_type_id( - _frt_type_id: crate::any::TypeId, -) -> crate::any::TypeId; - -/// Gets the name of the field represented by the [`FieldRepresentingType`]'s `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::mem::type_info::FieldId::name`]. -/// -/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn field_representing_type_name(_frt_type_id: crate::any::TypeId) -> &'static str; - -/// Gets the name of the field represented by the [`FieldRepresentingType`]'s `TypeId`. -/// -/// The more user-friendly version of this intrinsic is [`core::mem::type_info::FieldId::name`]. -/// -/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType -#[rustc_intrinsic] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_comptime] -pub fn field_representing_type_offset(_frt_type_id: crate::any::TypeId) -> usize; - /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/intrinsics/reflection.rs b/library/core/src/intrinsics/reflection.rs index f0e0f9d66e033..6332d198879d9 100644 --- a/library/core/src/intrinsics/reflection.rs +++ b/library/core/src/intrinsics/reflection.rs @@ -3,6 +3,8 @@ //! This are all methods operating on [`TypeId`](core::any::TypeId) which is the //! entry point to all compile time reflection. +use crate::marker::DiscriminantKind; +use crate::mem::type_info::VariantId; use crate::ptr; /// Gets an identifier which is globally unique to the specified type. This @@ -160,12 +162,34 @@ pub fn type_id_generics(_id: crate::any::TypeId) -> &'static [crate::mem::type_i #[rustc_intrinsic] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_comptime] -pub fn type_id_field_representing_type( +pub fn type_id_field_representing( _id: crate::any::TypeId, _variant_index: usize, _field_index: usize, ) -> crate::any::TypeId; +/// Gets the actual field `TypeId` of the [`FieldRepresentingType`]'s `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::mem::type_info::FieldId::type_id`]. +/// +/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_field_representing_outer_type_id( + _frt_type_id: crate::any::TypeId, +) -> crate::any::TypeId; + +/// Gets the name of the field represented by the [`FieldRepresentingType`]'s `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::mem::type_info::FieldId::name`]. +/// +/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_field_representing_field_offset(_frt_type_id: crate::any::TypeId) -> usize; + /// Given a `TypeId` that represents a pointer this returns the `TypeId` which that pointer /// points to. When called on anything else this returns None. /// @@ -192,3 +216,36 @@ pub fn type_id_points_mutably(_id: crate::any::TypeId) -> bool; #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_comptime] pub fn type_id_function_ptr(_type_id: crate::any::TypeId) -> Option; + +/// Returns a non empty string when given a `TypeId` which represents a named +/// item such as struct, enum or trait. Otherwise returns an empty string. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::name`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_name(_type_id: crate::any::TypeId) -> &'static str; + +/// Returns a function pointer used to get the `VariantId` of an enum at runtime. +/// +/// # Safety +/// See [`core::any::TypeId::variant_getter`]. +/// +/// # Note +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::variant_getter`] +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_variant_getter(_type_id: crate::any::TypeId) -> unsafe fn(*const ()) -> VariantId; + +/// **Please do not use this intrinsic directly.** +/// +/// This returns a lookup table for converting between VariantId and Discriminant. This is use +/// internally by [`variant_getter`](core::any::TypeId::variant_getter). +/// +/// The user-friendly way to get the `VariantId` of an enum at runtime is using +/// the function pointer returned by [`core::any::TypeId::variant_getter`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_variant_lookup_table() -> &'static [::Discriminant]; diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 71a9627c24f9c..797429a7490a1 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -2,7 +2,8 @@ //! runtime or const-eval processable way. use crate::any::TypeId; -use crate::marker::PointeeSized; +use crate::marker::{DiscriminantKind, PointeeSized}; +use crate::mem::Discriminant; use crate::ptr::DynMetadata; use crate::{fmt, intrinsics}; @@ -16,6 +17,39 @@ pub struct Type { pub kind: TypeKind, } +/// This function is not called directly instead a pointer to a monomorphized +/// instance of it is returned by [`TypeId::variant_getter`]. +/// +/// # Errors +/// It's an ICE to call this with anything not an enum. This is guarded for +/// by [`TypeId::variant_getter`]. +/// +/// # Safety +/// +/// See [`TypeId::variant_getter`] on how to safely use this function. +#[lang = "variant_getter"] +unsafe fn variant_getter(val: *const ()) -> VariantId +where + ::Discriminant: 'static, +{ + // SAFETY: up to the caller to uphold, documented in `TypeId::variant_getter`. + let v: &T = unsafe { &*val.cast::() }; + let discriminant = core::mem::discriminant(v); + let lookup_table: &'static [::Discriminant] = + const { intrinsics::reflection::type_id_variant_lookup_table::() }; + + if lookup_table.is_empty() { + // for types other then enums the table will be empty + return VariantId { base: const { of::() }, variant: 0 }; + } + + let variant = lookup_table + .iter() + .position(|d| Discriminant(*d) == discriminant) + .expect("table contains all possible discriminants"); + VariantId { base: const { of::() }, variant } +} + /// Info of a trait implementation, you can retrieve the vtable with [Self::get_vtable] #[derive(Debug, PartialEq, Eq)] #[unstable(feature = "type_info", issue = "146922")] @@ -107,6 +141,7 @@ pub enum TypeKind { /// FIXME(#146922): add all the common types /// non exhaustive list: /// - Never + /// - Type (type alias) Other, } @@ -551,7 +586,7 @@ impl TypeId { #[rustc_comptime] pub fn field(self, variant_index: usize, field_index: usize) -> FieldId { FieldId { - frt_type_id: intrinsics::reflection::type_id_field_representing_type( + frt_type_id: intrinsics::reflection::type_id_field_representing( self, variant_index, field_index, @@ -656,12 +691,101 @@ impl TypeId { pub fn function_ptr(self) -> Option { intrinsics::reflection::type_id_function_ptr(self) } + + /// Returns a non empty string when given a `TypeId` which represents a named item such + /// as struct, enum or trait. Otherwise returns an empty string. + /// ``` + /// #![feature(type_info)] + /// # use std::any::TypeId; + /// + /// struct Kitty; + /// enum Color {} + /// trait Cute {} + /// + /// assert_eq!(const { TypeId::of::().name() },"Kitty"); + /// assert_eq!(const { TypeId::of::>().name() },"Vec"); + /// assert_eq!(const { TypeId::of::().name() }, "Color"); + /// assert_eq!(const { TypeId::of::().name() }, "Cute"); + /// + /// assert_eq!(const { TypeId::of::<()>().name() }, ""); + /// assert_eq!(const { TypeId::of::().name() }, ""); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn name(self) -> &'static str { + intrinsics::reflection::type_id_name(self) + } + + /// When called on a `TypeId` representing some enum `T` this returns a function pointer + /// which can be used to get the `VariantId` of an instance of `T` at runtime. + /// + /// When given a `TypeId` for something which does not represent an enum the returned + /// function can still be called. It will then return the same as `TypeId::variant(0)`. + /// + /// # Safety + /// + /// While calling this function is completely safe the returned function pointer + /// must only be provided a pointer which may be converted to a reference to `T`. + /// See [pointer-to-reference-conversion](https://doc.rust-lang.org/stable/std/ptr/index.html#pointer-to-reference-conversion) for the requirements. + /// + /// Providing the returned function with anything else is undefined behavior. + /// + /// # Examples + /// + /// ``` + /// # #![feature(const_trait_impl)] + /// # #![feature(const_heap)] + /// # #![feature(type_info)] + /// use std::any::TypeId; + /// enum Kitty { + /// Sleepy, + /// Playful, + /// Cuddly, + /// } + + /// let names = const { + /// let type_id = TypeId::of::(); + /// let mut names = Vec::new(); + + /// while names.len() < type_id.variants() { + /// let variant_id = type_id.variant(names.len()); + /// names.push((variant_id, variant_id.name())); + /// } + /// names.const_make_global() + /// }; + /// let variant_getter = const { TypeId::of::().variant_getter() }; + + /// let kitty = Kitty::Playful; + /// // Normally when using the getter you are manually offsetting pointers and you have no type. + /// // We'll pretend here for brevity. + /// let kitty_ptr = &kitty as *const Kitty; + /// let kitty_ptr = kitty_ptr.cast::<()>(); + + /// let variant_id = variant_getter(kitty_ptr); + /// let kitty_is_feeling = names + /// .iter() + /// .find(|(id, _)| *id == variant_id) + /// .map(|(_, variant_name)| *variant_name) + /// .expect("We have a list of steps for each VariantId") + /// .to_lowercase(); + + /// assert_eq!(kitty_is_feeling, "playful"); + /// ``` + /// + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn variant_getter(self) -> unsafe fn(*const ()) -> VariantId { + intrinsics::reflection::type_id_variant_getter(self) + } } /// Variant representing type ID. Representing a variant of an enum. #[derive(Copy, PartialOrd, Ord, Hash)] #[derive_const(Clone, PartialEq, Eq)] #[unstable(feature = "type_info", issue = "146922")] +#[lang = "VariantId"] pub struct VariantId { base: TypeId, variant: usize, @@ -742,7 +866,7 @@ impl FieldId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn type_id(self) -> TypeId { - intrinsics::field_representing_type_actual_type_id(self.frt_type_id) + intrinsics::reflection::type_id_field_representing_outer_type_id(self.frt_type_id) } /// Returns the name of the field. @@ -764,7 +888,7 @@ impl FieldId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn name(self) -> &'static str { - intrinsics::field_representing_type_name(self.frt_type_id) + intrinsics::reflection::type_id_name(self.frt_type_id) } /// Returns the offset of the field wrt to its containing type. /// @@ -786,6 +910,6 @@ impl FieldId { #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] pub fn offset(self) -> usize { - intrinsics::field_representing_type_offset(self.frt_type_id) + intrinsics::reflection::type_id_field_representing_field_offset(self.frt_type_id) } } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 087b6bb2bc363..207c7c8216002 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -35,6 +35,7 @@ #![feature(const_destruct)] #![feature(const_drop_in_place)] #![feature(const_eval_select)] +#![feature(const_heap)] #![feature(const_index)] #![feature(const_iter)] #![feature(const_nonzero_strict_ops)] diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 865b6a269afff..e5c554b117686 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -4,6 +4,8 @@ use std::any::{Any, TypeId}; use std::mem::offset_of; use std::mem::type_info::{Const, Generic, GenericType, Type, TypeKind}; +mod runtime; + #[test] fn test_arrays() { // Normal array. @@ -12,6 +14,7 @@ fn test_arrays() { let ty_id = TypeId::of::<[u16; 4]>(); assert!(ty_id.element_ty() == Some(TypeId::of::())); assert!(ty_id.array_len() == 4); + assert!(ty_id.name() == ""); } // Zero-length array. @@ -38,6 +41,7 @@ fn test_tuples() { assert!(ty_id.size() == Some(size_of::<()>())); assert!(ty_id.variants() == 1); assert!(ty_id.fields(0) == 0); + assert!(ty_id.name() == ""); let ty_id = TypeId::of::<(u8,)>(); assert!(ty_id.size() == Some(size_of::<(u8,)>())); @@ -66,6 +70,7 @@ fn test_structs() { let ty_id = TypeId::of::(); assert!(!ty_id.non_exhaustive()); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.name() == "TestStruct"); assert!(ty_id.variants() == 1); assert!(ty_id.fields(0) == 3); assert!(ty_id.field(0, 0).type_id() == TypeId::of::()); @@ -93,6 +98,7 @@ fn test_structs() { let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.name() == "TupleStruct"); assert!(ty_id.variants() == 1); assert!(ty_id.fields(0) == 2); assert!(ty_id.field(0, 0).type_id() == TypeId::of::()); @@ -107,6 +113,7 @@ fn test_structs() { } let ty_id = TypeId::of::>(); + assert!(ty_id.name() == "Generics"); assert!(ty_id.fields(0) == 1); assert!(ty_id.generics().len() == 3); @@ -130,6 +137,7 @@ fn test_unions() { let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.name() == "TestUnion"); assert!(ty_id.variants() == 1); assert!(ty_id.fields(0) == 2); assert!(ty_id.field(0, 0).name() == "first"); @@ -145,6 +153,7 @@ fn test_unions() { } let ty_id = TypeId::of::>(); + assert!(ty_id.name() == "Generics"); assert!(ty_id.fields(0) == 2); assert!(ty_id.field(0, 1).offset() == offset_of!(Generics<'static, i32, 1_u64>, a)); assert!(ty_id.field(0, 1).offset() == offset_of!(Generics<'static, i32, 1_u64>, z)); @@ -176,6 +185,7 @@ fn test_enums() { let ty_id = TypeId::of::(); assert!(!ty_id.non_exhaustive()); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.name() == "E"); assert!(ty_id.variants() == 3); assert!(ty_id.variant(0).name() == "Some"); @@ -199,20 +209,18 @@ fn test_enums() { const { let ty_id = TypeId::of::>(); + assert!(ty_id.size() == Some(size_of::>())); + assert!(ty_id.name() == "Option"); assert!(ty_id.variants() == 2); assert!(ty_id.generics().len() == 1); + assert!(ty_id.fields(0) == 0); // None + assert!(ty_id.fields(1) == 1); + assert!(ty_id.field(1, 0).type_id() == TypeId::of::()); let Generic::Type(GenericType { ty: generic_ty, .. }) = ty_id.generics()[0] else { panic!() }; assert!(generic_ty == TypeId::of::()); - - let ty_id = TypeId::of::>(); - assert!(ty_id.size() == Some(size_of::>())); - assert!(ty_id.variants() == 2); - assert!(ty_id.fields(0) == 0); - assert!(ty_id.fields(1) == 1); - assert!(ty_id.field(1, 0).type_id() == TypeId::of::()); } } @@ -224,45 +232,53 @@ fn test_primitives() { let Type { kind: Bool, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.name().is_empty()); assert!(ty_id.variants() == 1); let Type { kind: Char, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.name().is_empty()); assert!(ty_id.variants() == 1); let Type { kind: Int, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.is_signed()); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.name().is_empty()); assert!(ty_id.variants() == 1); let Type { kind: Int, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.is_signed()); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.name().is_empty()); assert!(ty_id.variants() == 1); let Type { kind: Int, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(!ty_id.is_signed()); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.name().is_empty()); assert!(ty_id.variants() == 1); let Type { kind: Int, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(!ty_id.is_signed()); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.name().is_empty()); assert!(ty_id.variants() == 1); let Type { kind: Float, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.name().is_empty()); assert!(ty_id.variants() == 1); let Type { kind: Str(_ty), .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.size() == None); + assert!(ty_id.name().is_empty()); assert!(ty_id.variants() == 1); } } @@ -276,6 +292,7 @@ fn test_references() { const { let ty = TypeId::of::<&u8>(); assert!(ty.points_to() == Some(TypeId::of::())); + assert!(ty.name().is_empty()); assert!(!ty.points_mutably()); } @@ -305,6 +322,7 @@ fn test_pointers() { const { let ty = TypeId::of::<*const u8>(); assert!(ty.points_to() == Some(TypeId::of::())); + assert!(ty.name().is_empty()); assert!(!ty.points_mutably()); } @@ -373,8 +391,9 @@ fn test_dynamic_traits() { let preds = preds_of::(); assert_predicates_exact(preds, &[TypeId::of::()]); - let p = pred(preds, TypeId::of::()); - assert!(p.trait_ty.is_auto); + const TYPE_ID: TypeId = TypeId::of::(); + assert!(pred(preds, TYPE_ID).trait_ty.is_auto); + assert!(const { TYPE_ID.name() } == "Send"); } // dyn A @@ -382,8 +401,9 @@ fn test_dynamic_traits() { let preds = preds_of::>(); assert_predicates_exact(preds, &[TypeId::of::>()]); - let p = pred(preds, TypeId::of::>()); - assert!(!p.trait_ty.is_auto); + const TYPE_ID: TypeId = TypeId::of::>(); + assert!(!pred(preds, TYPE_ID).trait_ty.is_auto); + assert!(const { TYPE_ID.name() } == "A"); } // dyn B<5, Foo = i32> @@ -391,8 +411,9 @@ fn test_dynamic_traits() { let preds = preds_of::>(); assert_predicates_exact(preds, &[TypeId::of::>()]); - let e = pred(preds, TypeId::of::>()); - assert!(!e.trait_ty.is_auto); + const TYPE_ID: TypeId = TypeId::of::>(); + assert!(!pred(preds, TYPE_ID).trait_ty.is_auto); + assert!(const { TYPE_ID.name() } == "B"); } // dyn for<'a> FooTrait<'a, 'a, 7> @@ -400,8 +421,9 @@ fn test_dynamic_traits() { let preds = preds_of:: FooTrait<'a, 'a, 7>>(); assert_predicates_exact(preds, &[TypeId::of:: FooTrait<'a, 'a, 7>>()]); - let foo = pred(preds, TypeId::of:: FooTrait<'a, 'a, 7>>()); - assert!(!foo.trait_ty.is_auto); + const TYPE_ID: TypeId = TypeId::of:: FooTrait<'a, 'a, 7>>(); + assert!(!pred(preds, TYPE_ID).trait_ty.is_auto); + assert!(const { TYPE_ID.name() } == "FooTrait"); } // dyn FooTrait<'static, 'static, 7> @@ -409,8 +431,9 @@ fn test_dynamic_traits() { let preds = preds_of::>(); assert_predicates_exact(preds, &[TypeId::of::>()]); - let foo = pred(preds, TypeId::of::>()); - assert!(!foo.trait_ty.is_auto); + const TYPE_ID: TypeId = TypeId::of::>(); + assert!(!pred(preds, TYPE_ID).trait_ty.is_auto); + assert!(const { TYPE_ID.name() } == "FooTrait"); } // dyn for<'a, 'b> FooTrait<'a, 'b, 7> @@ -418,8 +441,9 @@ fn test_dynamic_traits() { let preds = preds_of:: FooTrait<'a, 'b, 7>>(); assert_predicates_exact(preds, &[TypeId::of:: FooTrait<'a, 'b, 7>>()]); - let foo = pred(preds, TypeId::of:: FooTrait<'a, 'b, 7>>()); - assert!(!foo.trait_ty.is_auto); + const TYPE_ID: TypeId = TypeId::of:: FooTrait<'a, 'b, 7>>(); + assert!(!pred(preds, TYPE_ID).trait_ty.is_auto); + assert!(const { TYPE_ID.name() } == "FooTrait"); } // dyn for<'a, 'b> ProjectorTrait<'a, 'b> @@ -427,8 +451,9 @@ fn test_dynamic_traits() { let preds = preds_of:: ProjectorTrait<'a, 'b>>(); assert_predicates_exact(preds, &[TypeId::of:: ProjectorTrait<'a, 'b>>()]); - let proj = pred(preds, TypeId::of:: ProjectorTrait<'a, 'b>>()); - assert!(!proj.trait_ty.is_auto); + const TYPE_ID: TypeId = TypeId::of:: ProjectorTrait<'a, 'b>>(); + assert!(!pred(preds, TYPE_ID).trait_ty.is_auto); + assert!(const { TYPE_ID.name() } == "ProjectorTrait"); } // dyn for<'a> FooTrait<'a, 'a, 7> + Send + Sync @@ -443,13 +468,14 @@ fn test_dynamic_traits() { ], ); - let foo = pred(preds, TypeId::of:: FooTrait<'a, 'a, 7>>()); - assert!(!foo.trait_ty.is_auto); + const TYPE_ID: TypeId = TypeId::of:: FooTrait<'a, 'a, 7>>(); + assert!(!pred(preds, TYPE_ID).trait_ty.is_auto); + assert!(const { TYPE_ID.name() } == "FooTrait"); - let send = pred(preds, TypeId::of::()); - assert!(send.trait_ty.is_auto); + let type_id: TypeId = TypeId::of::(); + assert!(pred(preds, type_id).trait_ty.is_auto); - let sync = pred(preds, TypeId::of::()); - assert!(sync.trait_ty.is_auto); + let type_id: TypeId = TypeId::of::(); + assert!(pred(preds, type_id).trait_ty.is_auto); } } diff --git a/library/coretests/tests/mem/type_info/runtime.rs b/library/coretests/tests/mem/type_info/runtime.rs new file mode 100644 index 0000000000000..af6e639a8addc --- /dev/null +++ b/library/coretests/tests/mem/type_info/runtime.rs @@ -0,0 +1,95 @@ +use std::any::TypeId; + +fn as_unit_ptr(test_case: &T) -> *const () { + (test_case as *const T).cast() +} + +fn assert_unit_variant_getter(test_input: &T) { + let variant_getter = const { TypeId::of::().variant_getter() }; + assert_eq!(variant_getter(as_unit_ptr(test_input)), const { TypeId::of::().variant(0) }); +} + +#[test] +fn correct_variant_id() { + use std::any::TypeId; + #[repr(u128)] + enum Kitty { + Sleepy, + Playful, + Cuddly = u128::MAX, + } + + let variant_getter = const { TypeId::of::().variant_getter() }; + assert_eq!( + variant_getter(as_unit_ptr(&Kitty::Sleepy)), + const { TypeId::of::().variant(0) } + ); + assert_eq!( + variant_getter(as_unit_ptr(&Kitty::Playful)), + const { TypeId::of::().variant(1) } + ); + assert_eq!( + variant_getter(as_unit_ptr(&Kitty::Cuddly)), + const { TypeId::of::().variant(2) } + ); +} + +#[test] +fn variant_getter_for_unit() { + assert_unit_variant_getter(&()); +} + +#[test] +fn array() { + assert_unit_variant_getter::<[u8; 3]>(&[0, 1, 2]); +} + +#[test] +fn slice() { + assert_unit_variant_getter::<&[u8]>(&[0, 1, 2].as_slice()); +} + +#[test] +fn strukt() { + assert_unit_variant_getter(&String::new()); +} + +#[test] +fn union() { + union TestUnion { + first: i16, + } + let test_union = TestUnion { first: 0 }; + assert_unit_variant_getter(&test_union); +} + +#[test] +fn primitives() { + assert_unit_variant_getter(&true); + assert_unit_variant_getter(&0u8); + assert_unit_variant_getter(&'a'); +} + +#[test] +fn reference_types() { + assert_unit_variant_getter(&&true); +} + +#[test] +fn pointers_types() { + let test_value = true; + let test_pointer = &test_value as *const bool; + assert_unit_variant_getter(&test_pointer); +} + +#[test] +fn trait_obj() { + let test_value = Box::new(String::new()); + let test_object = Box::leak(test_value) as &dyn std::fmt::Debug; + assert_unit_variant_getter(&test_object); +} + +#[test] +fn never_type() { + let variant_getter = const { TypeId::of::().variant_getter() }; +}