Rollup merge of #151142 - SpriteOvO:type-info-adt, r=oli-obk

Support ADT types in type info reflection

Tracking issue: rust-lang/rust#146922 `#![feature(type_info)]`

This PR supports ADT types for feature `type_info` reflection.
(It's still a draft PR, with implementation in progress)

Note that this PR does not take SemVer into consideration (I left a FIXME comment). As discussed earlier ([comment](https://github.com/rust-lang/rust/pull/146923#discussion_r2372249477)), this requires further discussion. However, I hope we could get an initial implementation to land first, so we can start playing with it.

### Progress / Checklist

- [x] Struct support.
- [x] Enum
- [x] Union
- [x] Generics
- [ ] ~Methods~ Implemented and to be implemented in other PRs
- [ ] ~Traits~ Implemented and to be implemented in other PRs
- [x] Rebasing PR to `main` branch
  ~~(It's currently based on PR rust-lang/rust#151123, so here's an extra commit)~~
- [x] Cleanup and Rebase.
- [x] Fix field info for references. (see [comment](https://github.com/rust-lang/rust/pull/151142#discussion_r2777920512))

r? @oli-obk
This commit is contained in:
Urgau 2026-02-12 00:04:15 +01:00 committed by GitHub
commit c87a89ed14
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 678 additions and 576 deletions

View file

@ -1,4 +1,8 @@
use rustc_abi::FieldIdx;
mod adt;
use std::borrow::Cow;
use rustc_abi::{FieldIdx, VariantIdx};
use rustc_ast::Mutability;
use rustc_hir::LangItem;
use rustc_middle::span_bug;
@ -8,11 +12,58 @@ use rustc_span::{Symbol, sym};
use crate::const_eval::CompileTimeMachine;
use crate::interpret::{
CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Scalar, Writeable,
interp_ok,
CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Projectable, Scalar,
Writeable, interp_ok,
};
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
/// Equivalent to `project_downcast`, but identifies the variant by name instead of index.
fn downcast<'a>(
&self,
place: &(impl Writeable<'tcx, CtfeProvenance> + 'a),
name: Symbol,
) -> InterpResult<'tcx, (VariantIdx, impl Writeable<'tcx, CtfeProvenance> + 'a)> {
let variants = place.layout().ty.ty_adt_def().unwrap().variants();
let variant_idx = variants
.iter_enumerated()
.find(|(_idx, var)| var.name == name)
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
.0;
interp_ok((variant_idx, self.project_downcast(place, variant_idx)?))
}
// 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>,
len: u64,
writer: impl Fn(&mut Self, /* index */ u64, MPlaceTy<'tcx>) -> InterpResult<'tcx>,
) -> InterpResult<'tcx> {
// Array element type
let field_ty = slice_place
.layout()
.ty
.builtin_deref(false)
.unwrap()
.sequence_element_type(self.tcx.tcx);
// Allocate an array
let array_layout = self.layout_of(Ty::new_array(self.tcx.tcx, field_ty, len))?;
let array_place = self.allocate(array_layout, MemoryKind::Stack)?;
// Fill the array fields
let mut field_places = self.project_array_fields(&array_place)?;
while let Some((i, place)) = field_places.next(self)? {
writer(self, i, place)?;
}
// 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)
}
/// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place.
pub(crate) fn write_type_info(
&mut self,
@ -26,22 +77,13 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
// Fill all fields of the `TypeInfo` struct.
for (idx, field) in ty_struct.fields.iter_enumerated() {
let field_dest = self.project_field(dest, idx)?;
let downcast = |name: Symbol| {
let variants = field_dest.layout().ty.ty_adt_def().unwrap().variants();
let variant_id = variants
.iter_enumerated()
.find(|(_idx, var)| var.name == name)
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
.0;
interp_ok((variant_id, self.project_downcast(&field_dest, variant_id)?))
};
let ptr_bit_width = || self.tcx.data_layout.pointer_size().bits();
match field.name {
sym::kind => {
let variant_index = match ty.kind() {
ty::Tuple(fields) => {
let (variant, variant_place) = downcast(sym::Tuple)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Tuple)?;
// project to the single tuple variant field of `type_info::Tuple` struct type
let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
assert_eq!(
@ -55,11 +97,12 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
.fields
.len()
);
self.write_tuple_fields(tuple_place, fields, ty)?;
self.write_tuple_type_info(tuple_place, fields, ty)?;
variant
}
ty::Array(ty, len) => {
let (variant, variant_place) = downcast(sym::Array)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Array)?;
let array_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_array_type_info(array_place, *ty, *len)?;
@ -67,23 +110,29 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::Slice(ty) => {
let (variant, variant_place) = downcast(sym::Slice)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Slice)?;
let slice_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_slice_type_info(slice_place, *ty)?;
variant
}
ty::Adt(adt_def, generics) => {
self.write_adt_type_info(&field_dest, (ty, *adt_def), generics)?
}
ty::Bool => {
let (variant, _variant_place) = downcast(sym::Bool)?;
let (variant, _variant_place) =
self.downcast(&field_dest, sym::Bool)?;
variant
}
ty::Char => {
let (variant, _variant_place) = downcast(sym::Char)?;
let (variant, _variant_place) =
self.downcast(&field_dest, sym::Char)?;
variant
}
ty::Int(int_ty) => {
let (variant, variant_place) = downcast(sym::Int)?;
let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_int_type_info(
place,
@ -93,7 +142,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::Uint(uint_ty) => {
let (variant, variant_place) = downcast(sym::Int)?;
let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_int_type_info(
place,
@ -103,17 +152,19 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::Float(float_ty) => {
let (variant, variant_place) = downcast(sym::Float)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Float)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_float_type_info(place, float_ty.bit_width())?;
variant
}
ty::Str => {
let (variant, _variant_place) = downcast(sym::Str)?;
let (variant, _variant_place) = self.downcast(&field_dest, sym::Str)?;
variant
}
ty::Ref(_, ty, mutability) => {
let (variant, variant_place) = downcast(sym::Reference)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Reference)?;
let reference_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_reference_type_info(reference_place, *ty, *mutability)?;
@ -121,7 +172,8 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::RawPtr(ty, mutability) => {
let (variant, variant_place) = downcast(sym::Pointer)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Pointer)?;
let pointer_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;
@ -130,13 +182,13 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::Dynamic(predicates, region) => {
let (variant, variant_place) = downcast(sym::DynTrait)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::DynTrait)?;
let dyn_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?;
variant
}
ty::Adt(_, _)
| ty::Foreign(_)
ty::Foreign(_)
| ty::Pat(_, _)
| ty::FnDef(..)
| ty::FnPtr(..)
@ -151,14 +203,14 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
| ty::Bound(..)
| ty::Placeholder(_)
| ty::Infer(..)
| ty::Error(_) => downcast(sym::Other)?.0,
| ty::Error(_) => self.downcast(&field_dest, sym::Other)?.0,
};
self.write_discriminant(variant_index, &field_dest)?
}
sym::size => {
let layout = self.layout_of(ty)?;
let variant_index = if layout.is_sized() {
let (variant, variant_place) = downcast(sym::Some)?;
let (variant, variant_place) = self.downcast(&field_dest, sym::Some)?;
let size_field_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_scalar(
@ -168,7 +220,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
)?;
variant
} else {
downcast(sym::None)?.0
self.downcast(&field_dest, sym::None)?.0
};
self.write_discriminant(variant_index, &field_dest)?;
}
@ -179,46 +231,12 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
interp_ok(())
}
pub(crate) fn write_tuple_fields(
&mut self,
tuple_place: impl Writeable<'tcx, CtfeProvenance>,
fields: &[Ty<'tcx>],
tuple_ty: Ty<'tcx>,
) -> InterpResult<'tcx> {
// project into the `type_info::Tuple::fields` field
let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?;
// get the `type_info::Field` type from `fields: &[Field]`
let field_type = fields_slice_place
.layout()
.ty
.builtin_deref(false)
.unwrap()
.sequence_element_type(self.tcx.tcx);
// Create an array with as many elements as the number of fields in the inspected tuple
let fields_layout =
self.layout_of(Ty::new_array(self.tcx.tcx, field_type, fields.len() as u64))?;
let fields_place = self.allocate(fields_layout, MemoryKind::Stack)?;
let mut fields_places = self.project_array_fields(&fields_place)?;
let tuple_layout = self.layout_of(tuple_ty)?;
while let Some((i, place)) = fields_places.next(self)? {
let field_ty = fields[i as usize];
self.write_field(field_ty, place, tuple_layout, i)?;
}
let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable);
let ptr = Immediate::new_slice(fields_place.ptr(), fields.len() as u64, self);
self.write_immediate(ptr, &fields_slice_place)
}
fn write_field(
&mut self,
field_ty: Ty<'tcx>,
place: MPlaceTy<'tcx>,
layout: TyAndLayout<'tcx>,
name: Option<Symbol>,
idx: u64,
) -> InterpResult<'tcx> {
for (field_idx, field_ty_field) in
@ -226,7 +244,19 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
{
let field_place = self.project_field(&place, field_idx)?;
match field_ty_field.name {
sym::ty => self.write_type_id(field_ty, &field_place)?,
sym::name => {
let name = match name.as_ref() {
Some(name) => Cow::Borrowed(name.as_str()),
None => Cow::Owned(idx.to_string()), // For tuples
};
let name_place = self.allocate_str_dedup(&name)?;
let ptr = self.mplace_to_ref(&name_place)?;
self.write_immediate(*ptr, &field_place)?
}
sym::ty => {
let field_ty = self.tcx.erase_and_anonymize_regions(field_ty);
self.write_type_id(field_ty, &field_place)?
}
sym::offset => {
let offset = layout.fields.offset(idx as usize);
self.write_scalar(
@ -242,6 +272,24 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
interp_ok(())
}
pub(crate) fn write_tuple_type_info(
&mut self,
tuple_place: impl Writeable<'tcx, CtfeProvenance>,
fields: &[Ty<'tcx>],
tuple_ty: Ty<'tcx>,
) -> InterpResult<'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.len() as u64,
|this, i, place| {
let field_ty = fields[i as usize];
this.write_field(field_ty, place, tuple_layout, None, i)
},
)
}
pub(crate) fn write_array_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,

View file

@ -0,0 +1,276 @@
use rustc_abi::{FieldIdx, VariantIdx};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{
AdtDef, AdtKind, Const, ConstKind, GenericArgKind, GenericArgs, Region, Ty, VariantDef,
};
use rustc_middle::{bug, span_bug};
use rustc_span::sym;
use crate::const_eval::CompileTimeMachine;
use crate::interpret::{
CtfeProvenance, InterpCx, InterpResult, MPlaceTy, Projectable, Scalar, Writeable, interp_ok,
};
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
// FIXME(type_info): No semver considerations for now
pub(crate) fn write_adt_type_info(
&mut self,
place: &impl Writeable<'tcx, CtfeProvenance>,
adt: (Ty<'tcx>, AdtDef<'tcx>),
generics: &'tcx GenericArgs<'tcx>,
) -> InterpResult<'tcx, VariantIdx> {
let (adt_ty, adt_def) = adt;
let variant_idx = match adt_def.adt_kind() {
AdtKind::Struct => {
let (variant, variant_place) = self.downcast(place, sym::Struct)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_struct_type_info(
place,
(adt_ty, adt_def.variant(VariantIdx::ZERO)),
generics,
)?;
variant
}
AdtKind::Union => {
let (variant, variant_place) = self.downcast(place, sym::Union)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_union_type_info(
place,
(adt_ty, adt_def.variant(VariantIdx::ZERO)),
generics,
)?;
variant
}
AdtKind::Enum => {
let (variant, variant_place) = self.downcast(place, sym::Enum)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_enum_type_info(place, adt, generics)?;
variant
}
};
interp_ok(variant_idx)
}
pub(crate) fn write_struct_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
struct_: (Ty<'tcx>, &'tcx VariantDef),
generics: &'tcx GenericArgs<'tcx>,
) -> InterpResult<'tcx> {
let (struct_ty, struct_def) = struct_;
let struct_layout = self.layout_of(struct_ty)?;
for (field_idx, field) in
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
{
let field_place = self.project_field(&place, field_idx)?;
match field.name {
sym::generics => self.write_generics(field_place, generics)?,
sym::fields => {
self.write_variant_fields(field_place, struct_def, struct_layout, generics)?
}
sym::non_exhaustive => {
let is_non_exhaustive = struct_def.is_field_list_non_exhaustive();
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
}
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}
interp_ok(())
}
pub(crate) fn write_union_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
union_: (Ty<'tcx>, &'tcx VariantDef),
generics: &'tcx GenericArgs<'tcx>,
) -> InterpResult<'tcx> {
let (union_ty, union_def) = union_;
let union_layout = self.layout_of(union_ty)?;
for (field_idx, field) in
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
{
let field_place = self.project_field(&place, field_idx)?;
match field.name {
sym::generics => self.write_generics(field_place, generics)?,
sym::fields => {
self.write_variant_fields(field_place, union_def, union_layout, generics)?
}
sym::non_exhaustive => {
let is_non_exhaustive = union_def.is_field_list_non_exhaustive();
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
}
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}
interp_ok(())
}
pub(crate) fn write_enum_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
enum_: (Ty<'tcx>, AdtDef<'tcx>),
generics: &'tcx GenericArgs<'tcx>,
) -> InterpResult<'tcx> {
let (enum_ty, enum_def) = enum_;
let enum_layout = self.layout_of(enum_ty)?;
for (field_idx, field) in
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
{
let field_place = self.project_field(&place, field_idx)?;
match field.name {
sym::generics => self.write_generics(field_place, generics)?,
sym::variants => {
self.allocate_fill_and_write_slice_ptr(
field_place,
enum_def.variants().len() as u64,
|this, i, place| {
let variant_idx = VariantIdx::from_usize(i as usize);
let variant_def = &enum_def.variants()[variant_idx];
let variant_layout = enum_layout.for_variant(this, variant_idx);
this.write_enum_variant(place, (variant_layout, &variant_def), generics)
},
)?;
}
sym::non_exhaustive => {
let is_non_exhaustive = enum_def.is_variant_list_non_exhaustive();
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
}
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}
interp_ok(())
}
fn write_enum_variant(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
variant: (TyAndLayout<'tcx>, &'tcx VariantDef),
generics: &'tcx GenericArgs<'tcx>,
) -> InterpResult<'tcx> {
let (variant_layout, variant_def) = variant;
for (field_idx, field_def) in
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
{
let field_place = self.project_field(&place, field_idx)?;
match field_def.name {
sym::name => {
let name_place = self.allocate_str_dedup(variant_def.name.as_str())?;
let ptr = self.mplace_to_ref(&name_place)?;
self.write_immediate(*ptr, &field_place)?
}
sym::fields => {
self.write_variant_fields(field_place, &variant_def, variant_layout, generics)?
}
sym::non_exhaustive => {
let is_non_exhaustive = variant_def.is_field_list_non_exhaustive();
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
}
other => span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"),
}
}
interp_ok(())
}
// Write fields for struct, enum variants
fn write_variant_fields(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
variant_def: &'tcx VariantDef,
variant_layout: TyAndLayout<'tcx>,
generics: &'tcx GenericArgs<'tcx>,
) -> InterpResult<'tcx> {
self.allocate_fill_and_write_slice_ptr(
place,
variant_def.fields.len() as u64,
|this, i, place| {
let field_def = &variant_def.fields[FieldIdx::from_usize(i as usize)];
let field_ty = field_def.ty(*this.tcx, generics);
this.write_field(field_ty, place, variant_layout, Some(field_def.name), i)
},
)
}
fn write_generics(
&mut self,
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| {
match generics[i as usize].kind() {
GenericArgKind::Lifetime(region) => this.write_generic_lifetime(region, place),
GenericArgKind::Type(ty) => this.write_generic_type(ty, place),
GenericArgKind::Const(c) => this.write_generic_const(c, place),
}
})
}
fn write_generic_lifetime(
&mut self,
_region: Region<'tcx>,
place: MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let (variant_idx, _) = self.downcast(&place, sym::Lifetime)?;
self.write_discriminant(variant_idx, &place)?;
interp_ok(())
}
fn write_generic_type(&mut self, ty: Ty<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
let (variant_idx, variant_place) = self.downcast(&place, sym::Type)?;
let generic_type_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
for (field_idx, field_def) in generic_type_place
.layout()
.ty
.ty_adt_def()
.unwrap()
.non_enum_variant()
.fields
.iter_enumerated()
{
let field_place = self.project_field(&generic_type_place, field_idx)?;
match field_def.name {
sym::ty => self.write_type_id(ty, &field_place)?,
other => span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"),
}
}
self.write_discriminant(variant_idx, &place)?;
interp_ok(())
}
fn write_generic_const(&mut self, c: Const<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
let ConstKind::Value(c) = c.kind() else { bug!("expected a computed const, got {c:?}") };
let (variant_idx, variant_place) = self.downcast(&place, sym::Const)?;
let const_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
for (field_idx, field_def) in const_place
.layout()
.ty
.ty_adt_def()
.unwrap()
.non_enum_variant()
.fields
.iter_enumerated()
{
let field_place = self.project_field(&const_place, field_idx)?;
match field_def.name {
sym::ty => self.write_type_id(c.ty, &field_place)?,
other => span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"),
}
}
self.write_discriminant(variant_idx, &place)?;
interp_ok(())
}
}

View file

@ -13,7 +13,7 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint};
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt};
use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::{bug, span_bug, ty};
use rustc_span::{Symbol, sym};
use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt};
@ -73,6 +73,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
ty: Ty<'tcx>,
dest: &impl Writeable<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()> {
debug_assert!(
!ty.has_erasable_regions(),
"type {ty:?} has regions that need erasing before writing a TypeId",
);
let tcx = self.tcx;
let type_id_hash = tcx.type_id_hash(ty).as_u128();
let op = self.const_val_to_op(

View file

@ -211,6 +211,7 @@ symbols! {
CoercePointeeValidated,
CoerceUnsized,
Command,
Const,
ConstParamTy,
ConstParamTy_,
Context,
@ -234,6 +235,7 @@ symbols! {
DynTrait,
Encodable,
Encoder,
Enum,
Enumerate,
Eq,
Equal,
@ -292,6 +294,7 @@ symbols! {
IteratorMap,
Layout,
Left,
Lifetime,
LinkedList,
LintDiagnostic,
LintPass,
@ -372,6 +375,7 @@ symbols! {
Stdin,
Str,
String,
Struct,
StructuralPartialEq,
Subdiagnostic,
SymbolIntern,
@ -394,6 +398,8 @@ symbols! {
Ty,
TyCtxt,
TyKind,
Type,
Union,
Unknown,
Unsize,
UnsizedConstParamTy,
@ -1085,6 +1091,7 @@ symbols! {
ffi_returns_twice,
field,
field_init_shorthand,
fields,
file,
file_options,
flags,
@ -1173,6 +1180,7 @@ symbols! {
generic_const_parameter_types,
generic_param_attrs,
generic_pattern_types,
generics,
get_context,
global_alloc_ty,
global_allocator,
@ -2473,6 +2481,7 @@ symbols! {
values,
var,
variant_count,
variants,
vec,
vec_as_mut_slice,
vec_as_slice,

View file

@ -49,6 +49,12 @@ pub enum TypeKind {
Slice(Slice),
/// Dynamic Traits.
DynTrait(DynTrait),
/// Structs.
Struct(Struct),
/// Enums.
Enum(Enum),
/// Unions.
Union(Union),
/// Primitive boolean type.
Bool(Bool),
/// Primitive character type.
@ -81,6 +87,8 @@ pub struct Tuple {
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Field {
/// The name of the field.
pub name: &'static str,
/// The field's type.
pub ty: TypeId,
/// Offset in bytes from the parent type
@ -137,6 +145,95 @@ pub struct Trait {
pub is_auto: bool,
}
/// Compile-time type information about arrays.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Struct {
/// Instantiated generics of the struct.
pub generics: &'static [Generic],
/// All fields of the struct.
pub fields: &'static [Field],
/// Whether the struct field list is non-exhaustive.
pub non_exhaustive: bool,
}
/// Compile-time type information about unions.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Union {
/// Instantiated generics of the union.
pub generics: &'static [Generic],
/// All fields of the union.
pub fields: &'static [Field],
}
/// Compile-time type information about enums.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Enum {
/// Instantiated generics of the enum.
pub generics: &'static [Generic],
/// All variants of the enum.
pub variants: &'static [Variant],
/// Whether the enum variant list is non-exhaustive.
pub non_exhaustive: bool,
}
/// Compile-time type information about variants of enums.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Variant {
/// The name of the variant.
pub name: &'static str,
/// All fields of the variant.
pub fields: &'static [Field],
/// Whether the enum variant fields is non-exhaustive.
pub non_exhaustive: bool,
}
/// Compile-time type information about instantiated generics of structs, enum and union variants.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub enum Generic {
/// Lifetimes.
Lifetime(Lifetime),
/// Types.
Type(GenericType),
/// Const parameters.
Const(Const),
}
/// Compile-time type information about generic lifetimes.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Lifetime {
// No additional information to provide for now.
}
/// Compile-time type information about instantiated generic types.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct GenericType {
/// The type itself.
pub ty: TypeId,
}
/// Compile-time type information about generic const parameters.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Const {
/// The const's type.
pub ty: TypeId,
}
/// Compile-time type information about `bool`.
#[derive(Debug)]
#[non_exhaustive]

View file

@ -1,5 +1,8 @@
#![allow(dead_code)]
use std::any::{Any, TypeId};
use std::mem::type_info::{Type, TypeKind};
use std::mem::offset_of;
use std::mem::type_info::{Const, Generic, GenericType, Type, TypeKind};
#[test]
fn test_arrays() {
@ -66,6 +69,155 @@ fn test_tuples() {
}
}
#[test]
fn test_structs() {
use TypeKind::*;
const {
struct TestStruct {
first: u8,
second: u16,
reference: &'static u16,
}
let Type { kind: Struct(ty), size, .. } = Type::of::<TestStruct>() else { panic!() };
assert!(size == Some(size_of::<TestStruct>()));
assert!(!ty.non_exhaustive);
assert!(ty.fields.len() == 3);
assert!(ty.fields[0].name == "first");
assert!(ty.fields[0].ty == TypeId::of::<u8>());
assert!(ty.fields[0].offset == offset_of!(TestStruct, first));
assert!(ty.fields[1].name == "second");
assert!(ty.fields[1].ty == TypeId::of::<u16>());
assert!(ty.fields[1].offset == offset_of!(TestStruct, second));
assert!(ty.fields[2].name == "reference");
assert!(ty.fields[2].ty == TypeId::of::<&'static u16>());
assert!(ty.fields[2].offset == offset_of!(TestStruct, reference));
}
const {
#[non_exhaustive]
struct NonExhaustive {
a: u8,
}
let Type { kind: Struct(ty), .. } = Type::of::<NonExhaustive>() else { panic!() };
assert!(ty.non_exhaustive);
}
const {
struct TupleStruct(u8, u16);
let Type { kind: Struct(ty), .. } = Type::of::<TupleStruct>() else { panic!() };
assert!(ty.fields.len() == 2);
assert!(ty.fields[0].name == "0");
assert!(ty.fields[0].ty == TypeId::of::<u8>());
assert!(ty.fields[1].name == "1");
assert!(ty.fields[1].ty == TypeId::of::<u16>());
}
const {
struct Generics<'a, T, const C: u64> {
a: &'a T,
}
let Type { kind: Struct(ty), .. } = Type::of::<Generics<'static, i32, 1_u64>>() else {
panic!()
};
assert!(ty.fields.len() == 1);
assert!(ty.generics.len() == 3);
let Generic::Lifetime(_) = ty.generics[0] else { panic!() };
let Generic::Type(GenericType { ty: generic_ty, .. }) = ty.generics[1] else { panic!() };
assert!(generic_ty == TypeId::of::<i32>());
let Generic::Const(Const { ty: const_ty, .. }) = ty.generics[2] else { panic!() };
assert!(const_ty == TypeId::of::<u64>());
}
}
#[test]
fn test_unions() {
use TypeKind::*;
const {
union TestUnion {
first: i16,
second: u16,
}
let Type { kind: Union(ty), size, .. } = Type::of::<TestUnion>() else { panic!() };
assert!(size == Some(size_of::<TestUnion>()));
assert!(ty.fields.len() == 2);
assert!(ty.fields[0].name == "first");
assert!(ty.fields[0].offset == offset_of!(TestUnion, first));
assert!(ty.fields[1].name == "second");
assert!(ty.fields[1].offset == offset_of!(TestUnion, second));
}
const {
union Generics<'a, T: Copy, const C: u64> {
a: T,
z: &'a (),
}
let Type { kind: Union(ty), .. } = Type::of::<Generics<'static, i32, 1_u64>>() else {
panic!()
};
assert!(ty.fields.len() == 2);
assert!(ty.fields[0].offset == offset_of!(Generics<'static, i32, 1_u64>, a));
assert!(ty.fields[1].offset == offset_of!(Generics<'static, i32, 1_u64>, z));
assert!(ty.generics.len() == 3);
let Generic::Lifetime(_) = ty.generics[0] else { panic!() };
let Generic::Type(GenericType { ty: generic_ty, .. }) = ty.generics[1] else { panic!() };
assert!(generic_ty == TypeId::of::<i32>());
let Generic::Const(Const { ty: const_ty, .. }) = ty.generics[2] else { panic!() };
assert!(const_ty == TypeId::of::<u64>());
}
}
#[test]
fn test_enums() {
use TypeKind::*;
const {
enum E {
Some(u32),
None,
#[non_exhaustive]
Foomp {
a: (),
b: &'static str,
},
}
let Type { kind: Enum(ty), size, .. } = Type::of::<E>() else { panic!() };
assert!(size == Some(size_of::<E>()));
assert!(ty.variants.len() == 3);
assert!(ty.variants[0].name == "Some");
assert!(!ty.variants[0].non_exhaustive);
assert!(ty.variants[0].fields.len() == 1);
assert!(ty.variants[1].name == "None");
assert!(!ty.variants[1].non_exhaustive);
assert!(ty.variants[1].fields.len() == 0);
assert!(ty.variants[2].name == "Foomp");
assert!(ty.variants[2].non_exhaustive);
assert!(ty.variants[2].fields.len() == 2);
}
const {
let Type { kind: Enum(ty), size, .. } = Type::of::<Option<i32>>() else { panic!() };
assert!(size == Some(size_of::<Option<i32>>()));
assert!(ty.variants.len() == 2);
assert!(ty.generics.len() == 1);
let Generic::Type(GenericType { ty: generic_ty, .. }) = ty.generics[0] else { panic!() };
assert!(generic_ty == TypeId::of::<i32>());
}
}
#[test]
fn test_primitives() {
use TypeKind::*;

View file

@ -127,7 +127,7 @@ mod r#trait {
//~| HELP remove
type D = Box<dyn GenericType>;
//~^ ERROR missing generics for trait `GenericType`
//~^ ERROR missing generics for trait `r#trait::GenericType`
//~| HELP add missing
type E = Box<dyn GenericType<String, usize>>;

View file

@ -469,7 +469,7 @@ note: trait defined here, with 1 lifetime parameter: `'a`
LL | trait GenericLifetime<'a> {
| ^^^^^^^^^^^^^^^ --
error[E0107]: missing generics for trait `GenericType`
error[E0107]: missing generics for trait `r#trait::GenericType`
--> $DIR/wrong-number-of-args.rs:129:22
|
LL | type D = Box<dyn GenericType>;

View file

@ -1,17 +1,17 @@
error[E0599]: the method `clone` exists for struct `Struct<A>`, but its trait bounds were not satisfied
error[E0599]: the method `clone` exists for struct `issue_69725::Struct<A>`, but its trait bounds were not satisfied
--> $DIR/issue-69725.rs:9:32
|
LL | let _ = Struct::<A>::new().clone();
| ^^^^^ method cannot be called on `Struct<A>` due to unsatisfied trait bounds
| ^^^^^ method cannot be called on `issue_69725::Struct<A>` due to unsatisfied trait bounds
|
::: $DIR/auxiliary/issue-69725.rs:2:1
|
LL | pub struct Struct<A>(A);
| -------------------- doesn't satisfy `Struct<A>: Clone`
| -------------------- doesn't satisfy `issue_69725::Struct<A>: Clone`
|
= note: the following trait bounds were not satisfied:
`A: Clone`
which is required by `Struct<A>: Clone`
which is required by `issue_69725::Struct<A>: Clone`
help: consider restricting the type parameter to satisfy the trait bound
|
LL | fn crash<A>() where A: Clone {

View file

@ -10,7 +10,7 @@ mod foo {
Pub {};
//~^ ERROR missing field `private` in initializer of `Pub`
Enum::Variant { x: () };
//~^ ERROR missing field `y` in initializer of `Enum`
//~^ ERROR missing field `y` in initializer of `foo::Enum`
}
}
@ -21,9 +21,9 @@ fn correct() {
fn wrong() {
foo::Enum::Variant { x: () };
//~^ ERROR missing field `y` in initializer of `Enum`
//~^ ERROR missing field `y` in initializer of `foo::Enum`
foo::Enum::Variant { };
//~^ ERROR missing fields `x` and `y` in initializer of `Enum`
//~^ ERROR missing fields `x` and `y` in initializer of `foo::Enum`
}
fn main() {}

View file

@ -4,7 +4,7 @@ error[E0063]: missing field `private` in initializer of `Pub`
LL | Pub {};
| ^^^ missing `private`
error[E0063]: missing field `y` in initializer of `Enum`
error[E0063]: missing field `y` in initializer of `foo::Enum`
--> $DIR/issue-79593.rs:12:9
|
LL | Enum::Variant { x: () };
@ -18,13 +18,13 @@ LL | foo::Pub {};
|
= note: private field `private` that was not provided
error[E0063]: missing field `y` in initializer of `Enum`
error[E0063]: missing field `y` in initializer of `foo::Enum`
--> $DIR/issue-79593.rs:23:5
|
LL | foo::Enum::Variant { x: () };
| ^^^^^^^^^^^^^^^^^^ missing `y`
error[E0063]: missing fields `x` and `y` in initializer of `Enum`
error[E0063]: missing fields `x` and `y` in initializer of `foo::Enum`
--> $DIR/issue-79593.rs:25:5
|
LL | foo::Enum::Variant { };

View file

@ -1,225 +0,0 @@
Type {
kind: Tuple(
Tuple {
fields: [
Field {
ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
offset: 0,
},
Field {
ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
offset: 1,
},
Field {
ty: TypeId(0x41223169ff28813ba79b7268a2a968d9),
offset: 2,
},
],
},
),
size: Some(
2,
),
}
Type {
kind: Array(
Array {
element_ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
len: 2,
},
),
size: Some(
2,
),
}
Type {
kind: Int(
Int {
bits: 8,
signed: true,
},
),
size: Some(
1,
),
}
Type {
kind: Int(
Int {
bits: 32,
signed: true,
},
),
size: Some(
4,
),
}
Type {
kind: Int(
Int {
bits: 64,
signed: true,
},
),
size: Some(
8,
),
}
Type {
kind: Int(
Int {
bits: 128,
signed: true,
},
),
size: Some(
16,
),
}
Type {
kind: Int(
Int {
bits: 32,
signed: true,
},
),
size: Some(
4,
),
}
Type {
kind: Int(
Int {
bits: 8,
signed: false,
},
),
size: Some(
1,
),
}
Type {
kind: Int(
Int {
bits: 32,
signed: false,
},
),
size: Some(
4,
),
}
Type {
kind: Int(
Int {
bits: 64,
signed: false,
},
),
size: Some(
8,
),
}
Type {
kind: Int(
Int {
bits: 128,
signed: false,
},
),
size: Some(
16,
),
}
Type {
kind: Int(
Int {
bits: 32,
signed: false,
},
),
size: Some(
4,
),
}
Type {
kind: Other,
size: Some(
4,
),
}
Type {
kind: Other,
size: Some(
12,
),
}
Type {
kind: Reference(
Reference {
pointee: TypeId(0xda1b6da9bd297bb2900de9303aadea79),
mutable: false,
},
),
size: Some(
8,
),
}
Type {
kind: Reference(
Reference {
pointee: TypeId(0x474ccf3b5db264ef53916706f7d7bb2c),
mutable: false,
},
),
size: Some(
8,
),
}
Type {
kind: Reference(
Reference {
pointee: TypeId(0x641e3def269c37acc6dcb92bf8c5f196),
mutable: false,
},
),
size: Some(
8,
),
}
Type {
kind: Str(
Str,
),
size: None,
}
Type {
kind: Slice(
Slice {
element_ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
},
),
size: None,
}
Type {
kind: Reference(
Reference {
pointee: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
mutable: false,
},
),
size: Some(
4,
),
}
Type {
kind: Reference(
Reference {
pointee: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
mutable: true,
},
),
size: Some(
4,
),
}

View file

@ -1,225 +0,0 @@
Type {
kind: Tuple(
Tuple {
fields: [
Field {
ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
offset: 0,
},
Field {
ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
offset: 1,
},
Field {
ty: TypeId(0x41223169ff28813ba79b7268a2a968d9),
offset: 2,
},
],
},
),
size: Some(
2,
),
}
Type {
kind: Array(
Array {
element_ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
len: 2,
},
),
size: Some(
2,
),
}
Type {
kind: Int(
Int {
bits: 8,
signed: true,
},
),
size: Some(
1,
),
}
Type {
kind: Int(
Int {
bits: 32,
signed: true,
},
),
size: Some(
4,
),
}
Type {
kind: Int(
Int {
bits: 64,
signed: true,
},
),
size: Some(
8,
),
}
Type {
kind: Int(
Int {
bits: 128,
signed: true,
},
),
size: Some(
16,
),
}
Type {
kind: Int(
Int {
bits: 64,
signed: true,
},
),
size: Some(
8,
),
}
Type {
kind: Int(
Int {
bits: 8,
signed: false,
},
),
size: Some(
1,
),
}
Type {
kind: Int(
Int {
bits: 32,
signed: false,
},
),
size: Some(
4,
),
}
Type {
kind: Int(
Int {
bits: 64,
signed: false,
},
),
size: Some(
8,
),
}
Type {
kind: Int(
Int {
bits: 128,
signed: false,
},
),
size: Some(
16,
),
}
Type {
kind: Int(
Int {
bits: 64,
signed: false,
},
),
size: Some(
8,
),
}
Type {
kind: Other,
size: Some(
4,
),
}
Type {
kind: Other,
size: Some(
24,
),
}
Type {
kind: Reference(
Reference {
pointee: TypeId(0xda1b6da9bd297bb2900de9303aadea79),
mutable: false,
},
),
size: Some(
16,
),
}
Type {
kind: Reference(
Reference {
pointee: TypeId(0x474ccf3b5db264ef53916706f7d7bb2c),
mutable: false,
},
),
size: Some(
16,
),
}
Type {
kind: Reference(
Reference {
pointee: TypeId(0x641e3def269c37acc6dcb92bf8c5f196),
mutable: false,
},
),
size: Some(
16,
),
}
Type {
kind: Str(
Str,
),
size: None,
}
Type {
kind: Slice(
Slice {
element_ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
},
),
size: None,
}
Type {
kind: Reference(
Reference {
pointee: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
mutable: false,
},
),
size: Some(
8,
),
}
Type {
kind: Reference(
Reference {
pointee: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
mutable: true,
},
),
size: Some(
8,
),
}

View file

@ -1,45 +0,0 @@
// Some types whose length depends on the target pointer length will be dumped.
//@ revisions: bit32 bit64
//@[bit32] only-32bit
//@[bit64] only-64bit
//@ run-pass
//@ check-run-results
#![feature(type_info)]
#![allow(dead_code)]
use std::mem::type_info::Type;
struct Foo {
a: u32,
}
enum Bar {
Some(u32),
None,
Foomp { a: (), b: &'static str },
}
struct Unsized {
x: u16,
s: str,
}
macro_rules! dump_types {
($($ty:ty),+ $(,)?) => {
$(println!("{:#?}", const { Type::of::<$ty>() });)+
};
}
fn main() {
dump_types! {
(u8, u8, ()),
[u8; 2],
i8, i32, i64, i128, isize,
u8, u32, u64, u128, usize,
Foo, Bar,
&Unsized, &str, &[u8],
str, [u8],
&u8, &mut u8,
}
}

View file

@ -41,12 +41,22 @@ error[E0531]: cannot find tuple struct or tuple variant `Type` in this scope
|
LL | let Type(..);
| ^^^^ not found in this scope
|
help: consider importing this tuple variant
|
LL + use std::mem::type_info::Generic::Type;
|
error[E0425]: cannot find value `Type` in this scope
--> $DIR/resolve-assoc-suggestions.rs:27:9
|
LL | Type;
| ^^^^ not found in this scope
|
help: consider importing this tuple variant
|
LL + use std::mem::type_info::Generic::Type;
|
error[E0425]: cannot find type `method` in this scope
--> $DIR/resolve-assoc-suggestions.rs:30:16