Auto merge of #146923 - oli-obk:comptime-reflect, r=BoxyUwU

Reflection MVP

I am opening this PR for discussion about the general design we should start out with, as there are various options (that are not too hard to transition between each other, so we should totally just pick one and go with it and reiterate later)

r? @scottmcm and @joshtriplett

project goal issue: https://github.com/rust-lang/rust-project-goals/issues/406
tracking issue: https://github.com/rust-lang/rust/issues/146922

The design currently implemented by this PR is

* `TypeId::info` (method, usually used as `id.info()` returns a `Type` struct
* the `Type` struct has fields that contain information about the type
* the most notable field is `kind`, which is a non-exhaustive enum over all possible type kinds and their specific information. So it has a `Tuple(Tuple)` variant, where the only field is a `Tuple` struct type that contains more information (The list of type ids that make up the tuple).
* To get nested type information (like the type of fields) you need to call `TypeId::info` again.
* There is only one language intrinsic to go from `TypeId` to `Type`, and it does all the work

An alternative design could be

* Lots of small methods (each backed by an intrinsic) on `TypeId` that return all the individual information pieces (size, align, number of fields, number of variants, ...)
* This is how C++ does it (see https://lemire.me/blog/2025/06/22/c26-will-include-compile-time-reflection-why-should-you-care/ and https://isocpp.org/files/papers/P2996R13.html#member-queries)
* Advantage: you only get the information you ask for, so it's probably cheaper if you get just one piece of information for lots of types (e.g. reimplementing size_of in terms of `TypeId::info` is likely expensive and wasteful)
* Disadvantage: lots of method calling (and `Option` return types, or "general" methods like `num_fields` returning 0 for primitives) instead of matching and field accesses
* a crates.io crate could implement `TypeId::info` in terms of this design

The backing implementation is modular enough that switching from one to the other is probably not an issue, and the alternative design could be easier for the CTFE engine's implementation, just not as nice to use for end users (without crates wrapping the logic)

One wart of this design that I'm fixing in separate branches is that `TypeId::info` will panic if used at runtime, while it should be uncallable
This commit is contained in:
rust-bors[bot] 2026-01-10 15:00:14 +00:00 committed by GitHub
commit f57eac1bf9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 475 additions and 53 deletions

View file

@ -8,7 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem};
use rustc_middle::mir::AssertMessage;
use rustc_middle::mir::interpret::ReportedErrorInfo;
use rustc_middle::mir::interpret::{Pointer, ReportedErrorInfo};
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement};
use rustc_middle::ty::{self, Ty, TyCtxt};
@ -22,7 +22,7 @@ use crate::errors::{LongRunning, LongRunningWarn};
use crate::fluent_generated as fluent;
use crate::interpret::{
self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar,
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, RangeSet, Scalar,
compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub,
throw_ub_custom, throw_unsup, throw_unsup_format,
};
@ -586,6 +586,11 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
}
}
sym::type_of => {
let ty = ecx.read_type_id(&args[0])?;
ecx.write_type_info(ty, 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 {

View file

@ -13,6 +13,7 @@ mod error;
mod eval_queries;
mod fn_queries;
mod machine;
mod type_info;
mod valtrees;
pub use self::dummy_machine::*;

View file

@ -0,0 +1,175 @@
use rustc_abi::FieldIdx;
use rustc_hir::LangItem;
use rustc_middle::mir::interpret::CtfeProvenance;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, ScalarInt, Ty};
use rustc_span::{Symbol, sym};
use crate::const_eval::CompileTimeMachine;
use crate::interpret::{
Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Writeable, interp_ok,
};
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
/// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place.
pub(crate) fn write_type_info(
&mut self,
ty: Ty<'tcx>,
dest: &impl Writeable<'tcx, CtfeProvenance>,
) -> InterpResult<'tcx> {
let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span);
let ty_struct = self.tcx.type_of(ty_struct).no_bound_vars().unwrap();
assert_eq!(ty_struct, dest.layout().ty);
let ty_struct = ty_struct.ty_adt_def().unwrap().non_enum_variant();
// 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)?))
};
match field.name {
sym::kind => {
let variant_index = match ty.kind() {
ty::Tuple(fields) => {
let (variant, variant_place) = downcast(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!(
1,
tuple_place
.layout()
.ty
.ty_adt_def()
.unwrap()
.non_enum_variant()
.fields
.len()
);
self.write_tuple_fields(tuple_place, fields, ty)?;
variant
}
// For now just merge all primitives into one `Leaf` variant with no data
ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Char | ty::Bool => {
downcast(sym::Leaf)?.0
}
ty::Adt(_, _)
| ty::Foreign(_)
| ty::Str
| ty::Array(_, _)
| ty::Pat(_, _)
| ty::Slice(_)
| ty::RawPtr(..)
| ty::Ref(..)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::UnsafeBinder(..)
| ty::Dynamic(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Never
| ty::Alias(..)
| ty::Param(_)
| ty::Bound(..)
| ty::Placeholder(_)
| ty::Infer(..)
| ty::Error(_) => downcast(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 size_field_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_scalar(
ScalarInt::try_from_target_usize(layout.size.bytes(), self.tcx.tcx)
.unwrap(),
&size_field_place,
)?;
variant
} else {
downcast(sym::None)?.0
};
self.write_discriminant(variant_index, &field_dest)?;
}
other => span_bug!(self.tcx.span, "unknown `Type` field {other}"),
}
}
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>,
idx: u64,
) -> InterpResult<'tcx> {
for (field_idx, field_ty_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_ty_field.name {
sym::ty => self.write_type_id(field_ty, &field_place)?,
sym::offset => {
let offset = layout.fields.offset(idx as usize);
self.write_scalar(
ScalarInt::try_from_target_usize(offset.bytes(), self.tcx.tcx).unwrap(),
&field_place,
)?;
}
other => {
span_bug!(self.tcx.def_span(field_ty_field.did), "unimplemented field {other}")
}
}
}
interp_ok(())
}
}

View file

@ -27,6 +27,7 @@ use super::{
throw_ub_custom, throw_ub_format,
};
use crate::fluent_generated as fluent;
use crate::interpret::Writeable;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum MulAddType {
@ -68,10 +69,10 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId
}
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// Generates a value of `TypeId` for `ty` in-place.
fn write_type_id(
pub(crate) fn write_type_id(
&mut self,
ty: Ty<'tcx>,
dest: &PlaceTy<'tcx, M::Provenance>,
dest: &impl Writeable<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()> {
let tcx = self.tcx;
let type_id_hash = tcx.type_id_hash(ty).as_u128();

View file

@ -278,6 +278,7 @@ language_item_table! {
PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None;
Type, sym::type_info, type_struct, Target::Struct, 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

View file

@ -213,6 +213,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::type_id
| sym::type_id_eq
| sym::type_name
| sym::type_of
| sym::ub_checks
| sym::variant_count
| sym::vtable_for
@ -308,13 +309,22 @@ pub(crate) fn check_intrinsic_type(
sym::needs_drop => (1, 0, vec![], tcx.types.bool),
sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)),
sym::type_id => {
(1, 0, vec![], tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity())
}
sym::type_id => (
1,
0,
vec![],
tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(),
),
sym::type_id_eq => {
let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity();
let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap();
(0, 0, vec![type_id, type_id], tcx.types.bool)
}
sym::type_of => (
0,
0,
vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap()],
tcx.type_of(tcx.lang_items().type_struct().unwrap()).no_bound_vars().unwrap(),
),
sym::offload => (
3,
0,

View file

@ -284,6 +284,7 @@ symbols! {
IteratorItem,
IteratorMap,
Layout,
Leaf,
Left,
LinkedList,
LintDiagnostic,
@ -302,6 +303,7 @@ symbols! {
Ordering,
OsStr,
OsString,
Other,
Output,
Param,
ParamSet,
@ -380,6 +382,7 @@ symbols! {
TryCapturePrintable,
TryFrom,
TryInto,
Tuple,
Ty,
TyCtxt,
TyKind,
@ -2318,6 +2321,7 @@ symbols! {
type_const,
type_id,
type_id_eq,
type_info,
type_ir,
type_ir_infer_ctxt_like,
type_ir_inherent,
@ -2325,6 +2329,7 @@ symbols! {
type_length_limit,
type_macros,
type_name,
type_of,
type_privacy_lints,
typed_swap_nonoverlapping,
u8,

View file

@ -2845,6 +2845,15 @@ pub const unsafe fn size_of_val<T: ?Sized>(ptr: *const T) -> usize;
#[rustc_intrinsic_const_stable_indirect]
pub const unsafe fn align_of_val<T: ?Sized>(ptr: *const T) -> usize;
/// Compute the type information of a concrete type.
/// It can only be called at compile time, the backends do
/// not implement it.
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
pub const fn type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type {
panic!("`TypeId::info` can only be called at compile-time")
}
/// Gets a static string slice containing the name of a type.
///
/// Note that, unlike most intrinsics, this can only be called at compile-time

View file

@ -124,6 +124,7 @@
#![feature(str_internals)]
#![feature(str_split_inclusive_remainder)]
#![feature(str_split_remainder)]
#![feature(type_info)]
#![feature(ub_checks)]
#![feature(unsafe_pinned)]
#![feature(utf16_extra)]

View file

@ -37,6 +37,9 @@ pub use drop_guard::DropGuard;
#[doc(inline)]
pub use crate::intrinsics::transmute;
#[unstable(feature = "type_info", issue = "146922")]
pub mod type_info;
/// Takes ownership and "forgets" about the value **without running its destructor**.
///
/// Any resources the value manages, such as heap memory or a file handle, will linger

View file

@ -0,0 +1,71 @@
//! MVP for exposing compile-time information about types in a
//! runtime or const-eval processable way.
use crate::any::TypeId;
use crate::intrinsics::type_of;
/// Compile-time type information.
#[derive(Debug)]
#[non_exhaustive]
#[lang = "type_info"]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Type {
/// Per-type information
pub kind: TypeKind,
/// Size of the type. `None` if it is unsized
pub size: Option<usize>,
}
impl TypeId {
/// Compute the type information of a concrete type.
/// It can only be called at compile time.
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
pub const fn info(self) -> Type {
type_of(self)
}
}
impl Type {
/// Returns the type information of the generic type parameter.
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
// FIXME(reflection): don't require the 'static bound
pub const fn of<T: 'static>() -> Self {
const { TypeId::of::<T>().info() }
}
}
/// Compile-time type information.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub enum TypeKind {
/// Tuples.
Tuple(Tuple),
/// Primitives
/// FIXME(#146922): disambiguate further
Leaf,
/// FIXME(#146922): add all the common types
Other,
}
/// Compile-time type information about tuples.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Tuple {
/// All fields of a tuple.
pub fields: &'static [Field],
}
/// Compile-time type information about fields of tuples, structs and enum variants.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Field {
/// The field's type.
pub ty: TypeId,
/// Offset in bytes from the parent type
pub offset: usize,
}

View file

@ -62,7 +62,6 @@ generate! {
MsrvStack,
Octal,
OpenOptions,
Other,
PathLookup,
Regex,
RegexBuilder,

View file

@ -2,7 +2,7 @@ error[E0277]: functions with the "rust-call" ABI must take a single non-self tup
--> $DIR/issue-22565-rust-call.rs:3:1
|
LL | extern "rust-call" fn b(_i: i32) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `i32`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `i32`
error: functions with the "rust-call" ABI must take a single non-self tuple argument
--> $DIR/issue-22565-rust-call.rs:17:5
@ -32,7 +32,7 @@ error[E0277]: functions with the "rust-call" ABI must take a single non-self tup
--> $DIR/issue-22565-rust-call.rs:27:7
|
LL | b(10);
| ^^ the trait `Tuple` is not implemented for `i32`
| ^^ the trait `std::marker::Tuple` is not implemented for `i32`
error: functions with the "rust-call" ABI must take a single non-self tuple argument
--> $DIR/issue-22565-rust-call.rs:29:5

View file

@ -2,7 +2,7 @@ error[E0059]: type parameter to bare `Fn` trait must be a tuple
--> $DIR/E0059.rs:3:11
|
LL | fn foo<F: Fn<i32>>(f: F) -> F::Output { f(3) }
| ^^^^^^^ the trait `Tuple` is not implemented for `i32`
| ^^^^^^^ the trait `std::marker::Tuple` is not implemented for `i32`
|
note: required by a bound in `Fn`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
@ -11,7 +11,7 @@ error[E0277]: `i32` is not a tuple
--> $DIR/E0059.rs:3:41
|
LL | fn foo<F: Fn<i32>>(f: F) -> F::Output { f(3) }
| ^^^^ the trait `Tuple` is not implemented for `i32`
| ^^^^ the trait `std::marker::Tuple` is not implemented for `i32`
error[E0059]: cannot use call notation; the first type parameter for the function trait is neither a tuple nor unit
--> $DIR/E0059.rs:3:41

View file

@ -384,11 +384,11 @@ LL | fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { pani
|
= note: multiple `impl`s satisfying `_: Fn()` found in the following crates: `alloc`, `core`:
- impl<A, F> Fn<A> for &F
where A: Tuple, F: Fn<A>, F: ?Sized;
where A: std::marker::Tuple, F: Fn<A>, F: ?Sized;
- impl<Args, F, A> Fn<Args> for Box<F, A>
where Args: Tuple, F: Fn<Args>, A: Allocator, F: ?Sized;
where Args: std::marker::Tuple, F: Fn<Args>, A: Allocator, F: ?Sized;
- impl<F, Args> Fn<Args> for Exclusive<F>
where F: Sync, F: Fn<Args>, Args: Tuple;
where F: Sync, F: Fn<Args>, Args: std::marker::Tuple;
error[E0118]: no nominal type found for inherent implementation
--> $DIR/where-allowed.rs:241:1

View file

@ -2,7 +2,7 @@ error[E0059]: type parameter to bare `FnOnce` trait must be a tuple
--> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:31:5
|
LL | extern "rust-call" fn call_once(mut self, a: A) -> Self::Output {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A`
|
note: required by a bound in `FnOnce`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
@ -15,7 +15,7 @@ error[E0059]: type parameter to bare `FnOnce` trait must be a tuple
--> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:24:12
|
LL | impl<A, B> FnOnce<A> for CachedFun<A, B>
| ^^^^^^^^^ the trait `Tuple` is not implemented for `A`
| ^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A`
|
note: required by a bound in `FnOnce`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
@ -28,7 +28,7 @@ error[E0059]: type parameter to bare `FnOnce` trait must be a tuple
--> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:45:5
|
LL | extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A`
|
note: required by a bound in `FnOnce`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
@ -41,7 +41,7 @@ error[E0059]: type parameter to bare `FnMut` trait must be a tuple
--> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:39:12
|
LL | impl<A, B> FnMut<A> for CachedFun<A, B>
| ^^^^^^^^ the trait `Tuple` is not implemented for `A`
| ^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A`
|
note: required by a bound in `FnMut`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
@ -54,7 +54,7 @@ error[E0277]: functions with the "rust-call" ABI must take a single non-self tup
--> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:31:5
|
LL | extern "rust-call" fn call_once(mut self, a: A) -> Self::Output {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A`
|
help: consider further restricting type parameter `A` with unstable trait `Tuple`
|
@ -65,7 +65,7 @@ error[E0277]: functions with the "rust-call" ABI must take a single non-self tup
--> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:45:5
|
LL | extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A`
|
help: consider further restricting type parameter `A` with unstable trait `Tuple`
|
@ -76,7 +76,7 @@ error[E0277]: `A` is not a tuple
--> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:34:19
|
LL | self.call_mut(a)
| -------- ^ the trait `Tuple` is not implemented for `A`
| -------- ^ the trait `std::marker::Tuple` is not implemented for `A`
| |
| required by a bound introduced by this call
|
@ -91,7 +91,7 @@ error[E0277]: `i32` is not a tuple
--> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:59:26
|
LL | cachedcoso.call_once(1);
| --------- ^ the trait `Tuple` is not implemented for `i32`
| --------- ^ the trait `std::marker::Tuple` is not implemented for `i32`
| |
| required by a bound introduced by this call
|

View file

@ -2,7 +2,7 @@ error[E0059]: type parameter to bare `FnMut` trait must be a tuple
--> $DIR/overloaded-calls-nontuple.rs:10:6
|
LL | impl FnMut<isize> for S {
| ^^^^^^^^^^^^ the trait `Tuple` is not implemented for `isize`
| ^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `isize`
|
note: required by a bound in `FnMut`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
@ -11,7 +11,7 @@ error[E0059]: type parameter to bare `FnOnce` trait must be a tuple
--> $DIR/overloaded-calls-nontuple.rs:18:6
|
LL | impl FnOnce<isize> for S {
| ^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `isize`
| ^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `isize`
|
note: required by a bound in `FnOnce`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
@ -20,19 +20,19 @@ error[E0277]: functions with the "rust-call" ABI must take a single non-self tup
--> $DIR/overloaded-calls-nontuple.rs:12:5
|
LL | extern "rust-call" fn call_mut(&mut self, z: isize) -> isize {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `isize`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `isize`
error[E0277]: functions with the "rust-call" ABI must take a single non-self tuple argument
--> $DIR/overloaded-calls-nontuple.rs:21:5
|
LL | extern "rust-call" fn call_once(mut self, z: isize) -> isize {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `isize`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `isize`
error[E0277]: `isize` is not a tuple
--> $DIR/overloaded-calls-nontuple.rs:23:23
|
LL | self.call_mut(z)
| -------- ^ the trait `Tuple` is not implemented for `isize`
| -------- ^ the trait `std::marker::Tuple` is not implemented for `isize`
| |
| required by a bound introduced by this call
|
@ -53,7 +53,7 @@ error[E0277]: `isize` is not a tuple
--> $DIR/overloaded-calls-nontuple.rs:29:10
|
LL | drop(s(3))
| ^^^^ the trait `Tuple` is not implemented for `isize`
| ^^^^ the trait `std::marker::Tuple` is not implemented for `isize`
error: aborting due to 7 previous errors

View file

@ -0,0 +1,30 @@
#![feature(type_info)]
//@ run-pass
//@ check-run-results
#![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,
}
fn main() {
println!("{:#?}", const { Type::of::<(u8, u8, ())>() }.kind);
println!("{:#?}", const { Type::of::<Foo>() }.kind);
println!("{:#?}", const { Type::of::<Bar>() }.kind);
println!("{:#?}", const { Type::of::<&Unsized>() }.kind);
println!("{:#?}", const { Type::of::<&str>() }.kind);
println!("{:#?}", const { Type::of::<&[u8]>() }.kind);
}

View file

@ -0,0 +1,23 @@
Tuple(
Tuple {
fields: [
Field {
ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
offset: 0,
},
Field {
ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
offset: 1,
},
Field {
ty: TypeId(0x41223169ff28813ba79b7268a2a968d9),
offset: 2,
},
],
},
)
Other
Other
Other
Other
Other

View file

@ -0,0 +1,8 @@
use std::mem::type_info::Type;
//~^ ERROR: use of unstable library feature `type_info`
fn main() {
let ty = std::mem::type_info::Type::of::<()>();
//~^ ERROR: use of unstable library feature `type_info`
//~| ERROR: use of unstable library feature `type_info`
}

View file

@ -0,0 +1,33 @@
error[E0658]: use of unstable library feature `type_info`
--> $DIR/feature_gate.rs:1:5
|
LL | use std::mem::type_info::Type;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #146922 <https://github.com/rust-lang/rust/issues/146922> for more information
= help: add `#![feature(type_info)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: use of unstable library feature `type_info`
--> $DIR/feature_gate.rs:5:14
|
LL | let ty = std::mem::type_info::Type::of::<()>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #146922 <https://github.com/rust-lang/rust/issues/146922> for more information
= help: add `#![feature(type_info)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: use of unstable library feature `type_info`
--> $DIR/feature_gate.rs:5:14
|
LL | let ty = std::mem::type_info::Type::of::<()>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #146922 <https://github.com/rust-lang/rust/issues/146922> for more information
= help: add `#![feature(type_info)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -0,0 +1,36 @@
#![feature(type_info)]
//@ run-pass
use std::mem::type_info::{Type, TypeKind};
fn assert_tuple_arity<T: 'static, const N: usize>() {
const {
match &Type::of::<T>().kind {
TypeKind::Tuple(tup) => {
assert!(tup.fields.len() == N);
}
_ => unreachable!(),
}
}
}
fn main() {
assert_tuple_arity::<(), 0>();
assert_tuple_arity::<(u8,), 1>();
assert_tuple_arity::<(u8, u8), 2>();
const {
match &Type::of::<(u8, u8)>().kind {
TypeKind::Tuple(tup) => {
let [a, b] = tup.fields else { unreachable!() };
assert!(a.offset == 0);
assert!(b.offset == 1);
match (&a.ty.info().kind, &b.ty.info().kind) {
(TypeKind::Leaf, TypeKind::Leaf) => {}
_ => unreachable!(),
}
}
_ => unreachable!(),
}
}
}

View file

@ -31,6 +31,10 @@ help: you might have meant to use the associated type
|
LL | let _: Self::Type;
| ++++++
help: consider importing this struct
|
LL + use std::mem::type_info::Type;
|
error[E0531]: cannot find tuple struct or tuple variant `Type` in this scope
--> $DIR/resolve-assoc-suggestions.rs:25:13

View file

@ -32,7 +32,7 @@ error[E0059]: type parameter to bare `Fn` trait must be a tuple
--> $DIR/fn-trait-notation.rs:4:8
|
LL | F: Fn<i32, Output = i32>,
| ^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `i32`
| ^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `i32`
|
note: required by a bound in `Fn`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
@ -47,7 +47,7 @@ error[E0277]: `i32` is not a tuple
--> $DIR/fn-trait-notation.rs:9:5
|
LL | f(3);
| ^^^^ the trait `Tuple` is not implemented for `i32`
| ^^^^ the trait `std::marker::Tuple` is not implemented for `i32`
error[E0308]: mismatched types
--> $DIR/fn-trait-notation.rs:17:5

View file

@ -68,7 +68,7 @@ body:
)
else_block: None
lint_level: Explicit(HirId(DefId(offset_of::concrete).10))
span: $DIR/offset_of.rs:37:5: 1437:57 (#0)
span: $DIR/offset_of.rs:37:5: 1440:57 (#0)
}
}
Stmt {
@ -117,7 +117,7 @@ body:
)
else_block: None
lint_level: Explicit(HirId(DefId(offset_of::concrete).20))
span: $DIR/offset_of.rs:38:5: 1437:57 (#0)
span: $DIR/offset_of.rs:38:5: 1440:57 (#0)
}
}
Stmt {
@ -166,7 +166,7 @@ body:
)
else_block: None
lint_level: Explicit(HirId(DefId(offset_of::concrete).30))
span: $DIR/offset_of.rs:39:5: 1437:57 (#0)
span: $DIR/offset_of.rs:39:5: 1440:57 (#0)
}
}
Stmt {
@ -215,7 +215,7 @@ body:
)
else_block: None
lint_level: Explicit(HirId(DefId(offset_of::concrete).40))
span: $DIR/offset_of.rs:40:5: 1437:57 (#0)
span: $DIR/offset_of.rs:40:5: 1440:57 (#0)
}
}
Stmt {
@ -264,7 +264,7 @@ body:
)
else_block: None
lint_level: Explicit(HirId(DefId(offset_of::concrete).50))
span: $DIR/offset_of.rs:41:5: 1437:57 (#0)
span: $DIR/offset_of.rs:41:5: 1440:57 (#0)
}
}
]
@ -864,7 +864,7 @@ body:
)
else_block: None
lint_level: Explicit(HirId(DefId(offset_of::generic).12))
span: $DIR/offset_of.rs:45:5: 1437:57 (#0)
span: $DIR/offset_of.rs:45:5: 1440:57 (#0)
}
}
Stmt {
@ -913,7 +913,7 @@ body:
)
else_block: None
lint_level: Explicit(HirId(DefId(offset_of::generic).24))
span: $DIR/offset_of.rs:46:5: 1437:57 (#0)
span: $DIR/offset_of.rs:46:5: 1440:57 (#0)
}
}
Stmt {
@ -962,7 +962,7 @@ body:
)
else_block: None
lint_level: Explicit(HirId(DefId(offset_of::generic).36))
span: $DIR/offset_of.rs:47:5: 1437:57 (#0)
span: $DIR/offset_of.rs:47:5: 1440:57 (#0)
}
}
Stmt {
@ -1011,7 +1011,7 @@ body:
)
else_block: None
lint_level: Explicit(HirId(DefId(offset_of::generic).48))
span: $DIR/offset_of.rs:48:5: 1437:57 (#0)
span: $DIR/offset_of.rs:48:5: 1440:57 (#0)
}
}
]

View file

@ -4,10 +4,10 @@ error[E0425]: cannot find type `Type` in this scope
LL | impl Generic<Type> for S {}
| ^^^^ not found in this scope
|
help: you might be missing a type parameter
help: consider importing this struct
|
LL + use std::mem::type_info::Type;
|
LL | impl<Type> Generic<Type> for S {}
| ++++++
error: aborting due to 1 previous error

View file

@ -9,11 +9,11 @@ LL | x = unconstrained_map();
|
= note: multiple `impl`s satisfying `_: Fn()` found in the following crates: `alloc`, `core`:
- impl<A, F> Fn<A> for &F
where A: Tuple, F: Fn<A>, F: ?Sized;
where A: std::marker::Tuple, F: Fn<A>, F: ?Sized;
- impl<Args, F, A> Fn<Args> for Box<F, A>
where Args: Tuple, F: Fn<Args>, A: Allocator, F: ?Sized;
where Args: std::marker::Tuple, F: Fn<Args>, A: Allocator, F: ?Sized;
- impl<F, Args> Fn<Args> for Exclusive<F>
where F: Sync, F: Fn<Args>, Args: Tuple;
where F: Sync, F: Fn<Args>, Args: std::marker::Tuple;
note: required by a bound in `unconstrained_map`
--> $DIR/well-formed-in-relate.rs:21:25
|

View file

@ -2,7 +2,7 @@ error[E0277]: `T` is not a tuple
--> $DIR/builtin-fail.rs:8:23
|
LL | assert_is_tuple::<T>();
| ^ the trait `Tuple` is not implemented for `T`
| ^ the trait `std::marker::Tuple` is not implemented for `T`
|
note: required by a bound in `assert_is_tuple`
--> $DIR/builtin-fail.rs:3:23
@ -18,7 +18,7 @@ error[E0277]: `i32` is not a tuple
--> $DIR/builtin-fail.rs:13:23
|
LL | assert_is_tuple::<i32>();
| ^^^ the trait `Tuple` is not implemented for `i32`
| ^^^ the trait `std::marker::Tuple` is not implemented for `i32`
|
note: required by a bound in `assert_is_tuple`
--> $DIR/builtin-fail.rs:3:23
@ -30,7 +30,7 @@ error[E0277]: `i32` is not a tuple
--> $DIR/builtin-fail.rs:15:24
|
LL | assert_is_tuple::<(i32)>();
| ^^^ the trait `Tuple` is not implemented for `i32`
| ^^^ the trait `std::marker::Tuple` is not implemented for `i32`
|
note: required by a bound in `assert_is_tuple`
--> $DIR/builtin-fail.rs:3:23
@ -44,7 +44,7 @@ error[E0277]: `TupleStruct` is not a tuple
LL | assert_is_tuple::<TupleStruct>();
| ^^^^^^^^^^^ unsatisfied trait bound
|
help: the trait `Tuple` is not implemented for `TupleStruct`
help: the trait `std::marker::Tuple` is not implemented for `TupleStruct`
--> $DIR/builtin-fail.rs:5:1
|
LL | struct TupleStruct(i32, i32);

View file

@ -2,7 +2,7 @@ error[E0277]: `&mut ()` is not a tuple
--> $DIR/issue-57404.rs:6:41
|
LL | handlers.unwrap().as_mut().call_mut(&mut ());
| -------- ^^^^^^^ the trait `Tuple` is not implemented for `&mut ()`
| -------- ^^^^^^^ the trait `std::marker::Tuple` is not implemented for `&mut ()`
| |
| required by a bound introduced by this call
|

View file

@ -2,7 +2,7 @@ error[E0059]: type parameter to bare `Fn` trait must be a tuple
--> $DIR/non-tupled-arg-mismatch.rs:3:9
|
LL | fn a<F: Fn<usize>>(f: F) {}
| ^^^^^^^^^ the trait `Tuple` is not implemented for `usize`
| ^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `usize`
|
note: required by a bound in `Fn`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL

View file

@ -1003,6 +1003,13 @@ cc = ["@lcnr"]
message = "HIR ty lowering was modified"
cc = ["@fmease"]
[mentions."library/core/src/mem/type_info.rs"]
message = """
The reflection data structures are tied exactly to the implementation
in the compiler. Make sure to also adjust `rustc_const_eval/src/const_eval/type_info.rs
"""
cc = ["@oli-obk"]
[mentions."compiler/rustc_error_codes/src/lib.rs"]
message = "Some changes occurred in diagnostic error codes"
cc = ["@GuillaumeGomez"]