From c7fc5f264663b2992cdb61acecbf256a2955828e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 16 Jul 2026 21:39:30 +0200 Subject: [PATCH 1/5] Add reflection method for field names --- .../src/const_eval/machine.rs | 27 ++++++++++++++++--- .../rustc_hir_analysis/src/check/intrinsic.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mod.rs | 10 +++++++ library/core/src/mem/type_info.rs | 22 +++++++++++++++ library/coretests/tests/mem/type_info.rs | 1 + 6 files changed, 60 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index c0756a25db8b1..1178ac8a03959 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -21,9 +21,9 @@ use super::error::*; use crate::diagnostics::{LongRunning, LongRunningWarn}; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, 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, + 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, type_implements_dyn_trait, }; @@ -712,6 +712,27 @@ 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_actual_type_id => { let frt_ty = ecx.read_type_id(&args[0])?; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cb6bf2cc7d623..3256ce3552265 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -115,6 +115,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::fdiv_algebraic | sym::field_offset | sym::field_representing_type_actual_type_id + | sym::field_representing_type_name | sym::floorf16 | sym::floorf32 | sym::floorf64 @@ -349,6 +350,7 @@ pub(crate) fn check_intrinsic_type( 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::offload => ( 3, 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fd555e6d97fd8..2275dcdb87cbb 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -970,6 +970,7 @@ symbols! { field_projections, field_representing_type, field_representing_type_actual_type_id, + field_representing_type_name, field_representing_type_raw, field_type, fields, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 87963bcce8ebb..3fd4d9c1bfb59 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3178,6 +3178,16 @@ 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; + /// 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/mem/type_info.rs b/library/core/src/mem/type_info.rs index 4c29daed20228..cc35096c145c6 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -596,4 +596,26 @@ impl FieldId { pub fn type_id(self) -> TypeId { intrinsics::field_representing_type_actual_type_id(self.frt_type_id) } + + /// Returns the name of the field. + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// + /// struct Point { + /// x: u32, + /// y: u32, + /// } + /// assert_eq!( + /// const { TypeId::of::().field(0, 0).name() }, + /// "x", + /// ); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[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) + } } diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 9a37a2ba0db5f..98d15c379b833 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -255,6 +255,7 @@ fn test_enums() { assert!(ty_id.field(0, 0).type_id() == TypeId::of::()); assert!(ty_id.field(2, 0).type_id() == TypeId::of::<()>()); assert!(ty_id.field(2, 1).type_id() == TypeId::of::<&str>()); + assert!(ty_id.field(2, 1).name() == "b"); } const { From 20d5e784afdb77ca4057156c1fbfc27f0f7dffa5 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 16 Jul 2026 22:07:59 +0200 Subject: [PATCH 2/5] Add reflection method for field offsets --- .../src/const_eval/machine.rs | 20 +++++++++++++++++ .../rustc_hir_analysis/src/check/intrinsic.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mod.rs | 10 +++++++++ library/core/src/mem/type_info.rs | 22 +++++++++++++++++++ 5 files changed, 55 insertions(+) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 1178ac8a03959..cb2016e515bc4 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -733,6 +733,26 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { )?; } + sym::field_representing_type_offset => { + let frt_ty = ecx.read_type_id(&args[0])?; + + let (ty, variant, field) = if let ty::Adt(def, args) = frt_ty.kind() + && let Some(FieldInfo { base, variant_idx, field_idx, .. }) = + def.field_representing_type_info(ecx.tcx.tcx, args) + { + (base, variant_idx, field_idx) + } else { + span_bug!(ecx.cur_span(), "expected field representing type, got {frt_ty}") + }; + let layout = ecx.layout_of(ty)?; + let cx = ty::layout::LayoutCx::new(ecx.tcx.tcx, ecx.typing_env()); + + let layout = layout.for_variant(&cx, variant); + let offset = layout.fields.offset(field.index()).bytes(); + + ecx.write_scalar(Scalar::from_target_usize(offset, ecx), dest)?; + } + sym::field_representing_type_actual_type_id => { let frt_ty = ecx.read_type_id(&args[0])?; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 3256ce3552265..a21114974f2c0 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -116,6 +116,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::field_offset | sym::field_representing_type_actual_type_id | sym::field_representing_type_name + | sym::field_representing_type_offset | sym::floorf16 | sym::floorf32 | sym::floorf64 @@ -351,6 +352,7 @@ pub(crate) fn check_intrinsic_type( ), 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::offload => ( 3, 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2275dcdb87cbb..41b505a1df9be 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -971,6 +971,7 @@ symbols! { field_representing_type, field_representing_type_actual_type_id, field_representing_type_name, + field_representing_type_offset, field_representing_type_raw, field_type, fields, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 3fd4d9c1bfb59..2f6677349ae2a 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3188,6 +3188,16 @@ pub fn field_representing_type_actual_type_id( #[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/mem/type_info.rs b/library/core/src/mem/type_info.rs index cc35096c145c6..b398cf6dafc37 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -618,4 +618,26 @@ impl FieldId { pub fn name(self) -> &'static str { intrinsics::field_representing_type_name(self.frt_type_id) } + /// Returns the offset of the field wrt to its containing type. + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// + /// #[repr(C)] + /// struct Point { + /// x: u32, + /// y: u32, + /// } + /// assert_eq!( + /// const { TypeId::of::().field(0, 1).offset() }, + /// 4, + /// ); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn offset(self) -> usize { + intrinsics::field_representing_type_offset(self.frt_type_id) + } } From 538f556305b45ab2efd8ca0d70fe518cd5b41924 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 21 Jul 2026 17:43:45 +0200 Subject: [PATCH 3/5] Add reflection method for fetching non-exhaustiveness --- .../rustc_const_eval/src/const_eval/machine.rs | 17 +++++++++++++++++ .../rustc_hir_analysis/src/check/intrinsic.rs | 2 ++ library/core/src/intrinsics/mod.rs | 6 ++++++ library/core/src/mem/type_info.rs | 13 +++++++++++++ library/coretests/tests/mem/type_info.rs | 1 + 5 files changed, 39 insertions(+) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index cb2016e515bc4..c80821f094cb4 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -767,6 +767,23 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_type_id(field_ty, dest)?; } + sym::non_exhaustive => { + let ty = ecx.read_type_id(&args[0])?; + + // FIXME(reflection): need a way to obtain non-exhaustiveness of a variant's fields. + let non_exhaustive = if let ty::Adt(def, _) = ty.kind() { + if def.is_enum() { + def.is_variant_list_non_exhaustive() + } else { + def.non_enum_variant().is_field_list_non_exhaustive() + } + } else { + false + }; + + ecx.write_scalar(Scalar::from_bool(non_exhaustive), dest)?; + } + _ => { // We haven't handled the intrinsic, let's see if we can use a fallback body. if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index a21114974f2c0..7c3a7b01f252b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -165,6 +165,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::minimumf128 | sym::mul_with_overflow | sym::needs_drop + | sym::non_exhaustive | sym::offload | sym::offset_of | sym::overflow_checks @@ -353,6 +354,7 @@ pub(crate) fn check_intrinsic_type( 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::non_exhaustive => (0, 0, vec![type_id_ty()], tcx.types.bool), sym::offload => ( 3, 0, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 2f6677349ae2a..28804ee53d901 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3198,6 +3198,12 @@ pub fn field_representing_type_name(_frt_type_id: crate::any::TypeId) -> &'stati #[rustc_comptime] pub fn field_representing_type_offset(_frt_type_id: crate::any::TypeId) -> usize; +/// Checks whether this type is non-exhaustive. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn non_exhaustive(_id: crate::any::TypeId) -> bool; + /// 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/mem/type_info.rs b/library/core/src/mem/type_info.rs index b398cf6dafc37..71a322abd7417 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -481,6 +481,10 @@ impl TypeId { #[unstable(feature = "type_info", issue = "146922")] #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] + // FIXME(type_info): Add enum variant pattern types and use them to represent individual variants + // Then add a `variant` method to get a wrapper around such a pattern type (similar to the FRT + // type we have) and add methods on that. It's the only way to really sensibly represent + // things like `non_exhaustive` which can be applied to variants as well. pub fn fields(self, variant_index: usize) -> usize { intrinsics::type_id_fields(self, variant_index) } @@ -557,6 +561,15 @@ impl TypeId { ), } } + + /// Returns whether a type is marked with `#[non_exhaustive]`. + /// Returns `false` for everything but adts. + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn non_exhaustive(self) -> bool { + intrinsics::non_exhaustive(self) + } } /// Field representing type ID. Representing a field of a struct, tuple or enum variant. diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 98d15c379b833..b2756babe7864 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -247,6 +247,7 @@ fn test_enums() { assert!(ty.variants[2].fields.len() == 2); let ty_id = TypeId::of::(); + assert!(!ty_id.non_exhaustive()); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 3); assert!(ty_id.fields(0) == 1); From 9381472c711af721961b2ac9eddf0902ad8a3e0c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 22 Jul 2026 12:30:54 +0200 Subject: [PATCH 4/5] Generalize some arguments to allow more kinds of input --- .../rustc_const_eval/src/const_eval/type_info.rs | 8 ++++---- .../src/const_eval/type_info/adt.rs | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 8f58e39d419ce..8fbb1ba4c9193 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -20,7 +20,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { // A general method to write an array to a static slice place. fn allocate_fill_and_write_slice_ptr( &mut self, - slice_place: impl Writeable<'tcx, CtfeProvenance>, + slice_place: &impl Writeable<'tcx, CtfeProvenance>, len: u64, writer: impl Fn(&mut Self, /* index */ u64, MPlaceTy<'tcx>) -> InterpResult<'tcx>, ) -> InterpResult<'tcx> { @@ -45,7 +45,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { // Write the slice pointing to the array let array_place = array_place.map_provenance(CtfeProvenance::as_immutable); let ptr = Immediate::new_slice(array_place.ptr(), len, self); - self.write_immediate(ptr, &slice_place) + self.write_immediate(ptr, slice_place) } /// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place. @@ -262,7 +262,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { let tuple_layout = self.layout_of(tuple_ty)?; let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?; self.allocate_fill_and_write_slice_ptr( - fields_slice_place, + &fields_slice_place, fields.len() as u64, |this, i, place| { let field_ty = fields[i as usize]; @@ -424,7 +424,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { sym::inputs => { let inputs = sig.inputs(); self.allocate_fill_and_write_slice_ptr( - field_place, + &field_place, inputs.len() as _, |this, i, place| this.write_type_id(inputs[i as usize], &place), )?; 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 298752324e382..09662d2bc0a98 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 @@ -66,7 +66,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { let field_place = self.project_field(&place, field_idx)?; match field.name { - sym::generics => self.write_generics(field_place, generics)?, + sym::generics => self.write_generics(&field_place, generics)?, sym::fields => { self.write_variant_fields(field_place, struct_def, struct_layout, generics)? } @@ -96,7 +96,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { let field_place = self.project_field(&place, field_idx)?; match field.name { - sym::generics => self.write_generics(field_place, generics)?, + sym::generics => self.write_generics(&field_place, generics)?, sym::fields => { self.write_variant_fields(field_place, union_def, union_layout, generics)? } @@ -126,10 +126,10 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { let field_place = self.project_field(&place, field_idx)?; match field.name { - sym::generics => self.write_generics(field_place, generics)?, + sym::generics => self.write_generics(&field_place, generics)?, sym::variants => { self.allocate_fill_and_write_slice_ptr( - field_place, + &field_place, enum_def.variants().len() as u64, |this, i, place| { let variant_idx = VariantIdx::from_usize(i as usize); @@ -190,7 +190,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { generics: &'tcx GenericArgs<'tcx>, ) -> InterpResult<'tcx> { self.allocate_fill_and_write_slice_ptr( - place, + &place, variant_def.fields.len() as u64, |this, i, place| { let field_def = &variant_def.fields[FieldIdx::from_usize(i as usize)]; @@ -200,9 +200,9 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { ) } - fn write_generics( + pub(super) fn write_generics( &mut self, - place: impl Writeable<'tcx, CtfeProvenance>, + place: &impl Writeable<'tcx, CtfeProvenance>, generics: &'tcx GenericArgs<'tcx>, ) -> InterpResult<'tcx> { self.allocate_fill_and_write_slice_ptr(place, generics.len() as u64, |this, i, place| { From cecd4a4fcb1fc28b330826f28ea20582051e353e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 22 Jul 2026 13:08:07 +0200 Subject: [PATCH 5/5] Add reflection method for getting a type's generic args --- .../src/const_eval/machine.rs | 5 +++ .../src/const_eval/type_info.rs | 43 +++++++++++++++++++ compiler/rustc_hir/src/lang_items.rs | 1 + .../rustc_hir_analysis/src/check/intrinsic.rs | 14 ++++++ compiler/rustc_span/src/symbol.rs | 2 + library/core/src/intrinsics/mod.rs | 7 +++ library/core/src/mem/type_info.rs | 10 +++++ library/coretests/tests/mem/type_info.rs | 1 + 8 files changed, 83 insertions(+) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index c80821f094cb4..3f52fbecb0950 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -767,6 +767,11 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_type_id(field_ty, dest)?; } + sym::type_id_generics => { + let ty = ecx.read_type_id(&args[0])?; + ecx.write_type_id_generics(dest, ty)?; + } + sym::non_exhaustive => { let ty = ecx.read_type_id(&args[0])?; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 8fbb1ba4c9193..7c0fef3734975 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -383,6 +383,49 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { interp_ok(()) } + pub(crate) fn write_type_id_generics( + &mut self, + place: &impl Writeable<'tcx, CtfeProvenance>, + ty: Ty<'tcx>, + ) -> InterpResult<'tcx> { + let generics: ty::Binder<'_, ty::GenericArgsRef<'_>> = match *ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Foreign(..) + | ty::Str + | ty::Array(..) + | ty::Pat(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(..) + | ty::Alias(..) + | ty::Param(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) + | ty::Error(..) + | ty::Slice(..) => ty::Binder::dummy(ty::GenericArgsRef::default()), + ty::Adt(_, args) => ty::Binder::dummy(args), + ty::FnDef(_, binder) => binder, + ty::UnsafeBinder(binder) => binder.rebind(ty::GenericArgsRef::default()), + ty::Closure(_, args) => ty::Binder::dummy(args), + ty::CoroutineClosure(_, args) => ty::Binder::dummy(args), + ty::Coroutine(_, args) => ty::Binder::dummy(args), + }; + + // FIXME(type_info): also provide the late bound vars to reflection + let generics = generics.skip_binder(); + + self.write_generics(place, generics) + } + pub(crate) fn write_fn_ptr_type_info( &mut self, place: impl Writeable<'tcx, CtfeProvenance>, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index e6e0b3726552f..00a2fc299efd8 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -276,6 +276,7 @@ language_item_table! { CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; Type, sym::type_info, type_struct, Target::Struct, GenericRequirement::None; + TypeGeneric, sym::type_info_generic, type_generic, Target::Enum, GenericRequirement::None; TypeId, sym::type_id, type_id, Target::Struct, GenericRequirement::None; // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 7c3a7b01f252b..67fddd87fbb1d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -219,6 +219,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::type_id_eq | sym::type_id_field_representing_type | sym::type_id_fields + | sym::type_id_generics | sym::type_id_variants | sym::type_id_vtable | sym::type_name @@ -355,6 +356,19 @@ pub(crate) fn check_intrinsic_type( 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::non_exhaustive => (0, 0, vec![type_id_ty()], tcx.types.bool), + sym::type_id_generics => ( + 0, + 0, + vec![type_id_ty()], + Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_static, + Ty::new_slice( + tcx, + tcx.type_of(tcx.lang_items().type_generic().unwrap()).no_bound_vars().unwrap(), + ), + ), + ), sym::offload => ( 3, 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 41b505a1df9be..a453cc6b555db 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2173,9 +2173,11 @@ symbols! { type_id_eq, type_id_field_representing_type, type_id_fields, + type_id_generics, type_id_variants, type_id_vtable, type_info, + type_info_generic, type_ir, type_ir_infer_ctxt_like, type_ir_inherent, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 28804ee53d901..fad2cb08a8a64 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3204,6 +3204,13 @@ pub fn field_representing_type_offset(_frt_type_id: crate::any::TypeId) -> usize #[rustc_comptime] pub fn non_exhaustive(_id: crate::any::TypeId) -> bool; +/// Returns the list of generic args on this type. +/// Only meaningful for Adts, closures, ... Everything else returns an empty slice. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_generics(_id: crate::any::TypeId) -> &'static [crate::mem::type_info::Generic]; + /// 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/mem/type_info.rs b/library/core/src/mem/type_info.rs index 71a322abd7417..66f85dab7b6c9 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -224,6 +224,7 @@ pub struct Variant { #[derive(Debug)] #[non_exhaustive] #[unstable(feature = "type_info", issue = "146922")] +#[lang = "type_info_generic"] pub enum Generic { /// Lifetimes. Lifetime(Lifetime), @@ -570,6 +571,15 @@ impl TypeId { pub fn non_exhaustive(self) -> bool { intrinsics::non_exhaustive(self) } + + /// Returns a list of generic parameters of the type. + /// Returns an empty slice for everything that doesn't have generics. + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn generics(self) -> &'static [Generic] { + intrinsics::type_id_generics(self) + } } /// Field representing type ID. Representing a field of a struct, tuple or enum variant. diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index b2756babe7864..095419b8792b1 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -257,6 +257,7 @@ fn test_enums() { assert!(ty_id.field(2, 0).type_id() == TypeId::of::<()>()); assert!(ty_id.field(2, 1).type_id() == TypeId::of::<&str>()); assert!(ty_id.field(2, 1).name() == "b"); + assert!(ty_id.generics().is_empty()); } const {