Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 81 additions & 5 deletions compiler/rustc_const_eval/src/const_eval/type_info.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rustc_abi::FieldIdx;
use rustc_hir::LangItem;
use rustc_middle::mir::interpret::CtfeProvenance;
use rustc_middle::mir::interpret::{CtfeProvenance, Scalar};
use rustc_middle::span_bug;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Const, ScalarInt, Ty};
Expand Down Expand Up @@ -35,6 +35,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {

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() {
Expand Down Expand Up @@ -64,13 +65,46 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {

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::Bool => {
let (variant, _variant_place) = downcast(sym::Bool)?;
variant
}
ty::Char => {
let (variant, _variant_place) = downcast(sym::Char)?;
variant
}
ty::Int(int_ty) => {
let (variant, variant_place) = downcast(sym::Int)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_int_type_info(
place,
int_ty.bit_width().unwrap_or_else(/* isize */ ptr_bit_width),
true,
)?;
variant
}
ty::Uint(uint_ty) => {
let (variant, variant_place) = downcast(sym::Int)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_int_type_info(
place,
uint_ty.bit_width().unwrap_or_else(/* usize */ ptr_bit_width),
false,
)?;
variant
}
ty::Float(float_ty) => {
let (variant, variant_place) = downcast(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)?;
variant
}
ty::Adt(_, _)
| ty::Foreign(_)
| ty::Str
| ty::Pat(_, _)
| ty::Slice(_)
| ty::RawPtr(..)
Expand Down Expand Up @@ -203,4 +237,46 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {

interp_ok(())
}

fn write_int_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
bit_width: u64,
signed: bool,
) -> InterpResult<'tcx> {
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::bit_width => self.write_scalar(
ScalarInt::try_from_target_usize(bit_width, self.tcx.tcx).unwrap(),
&field_place,
)?,
sym::signed => self.write_scalar(Scalar::from_bool(signed), &field_place)?,
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}
interp_ok(())
}

fn write_float_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
bit_width: u64,
) -> InterpResult<'tcx> {
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::bit_width => self.write_scalar(
ScalarInt::try_from_target_usize(bit_width, self.tcx.tcx).unwrap(),
&field_place,
)?,
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}
interp_ok(())
}
}
8 changes: 7 additions & 1 deletion compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ symbols! {
BTreeMap,
BTreeSet,
BinaryHeap,
Bool,
Borrow,
BorrowMut,
Break,
Expand All @@ -202,6 +203,7 @@ symbols! {
Capture,
Cell,
Center,
Char,
Child,
Cleanup,
Clone,
Expand Down Expand Up @@ -238,6 +240,7 @@ symbols! {
Error,
File,
FileType,
Float,
FmtArgumentsNew,
FmtWrite,
Fn,
Expand All @@ -263,6 +266,7 @@ symbols! {
IndexOutput,
Input,
Instant,
Int,
Into,
IntoFuture,
IntoIterator,
Expand All @@ -285,7 +289,6 @@ symbols! {
IteratorItem,
IteratorMap,
Layout,
Leaf,
Left,
LinkedList,
LintDiagnostic,
Expand Down Expand Up @@ -363,6 +366,7 @@ symbols! {
Some,
SpanCtxt,
Stdin,
Str,
String,
StructuralPartialEq,
SubdiagMessage,
Expand Down Expand Up @@ -584,6 +588,7 @@ symbols! {
binaryheap_iter,
bind_by_move_pattern_guards,
bindings_after_at,
bit_width,
bitand,
bitand_assign,
bitor,
Expand Down Expand Up @@ -2060,6 +2065,7 @@ symbols! {
shr_assign,
sig_dfl,
sig_ign,
signed,
simd,
simd_add,
simd_and,
Expand Down
57 changes: 54 additions & 3 deletions library/core/src/mem/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,16 @@ pub enum TypeKind {
Tuple(Tuple),
/// Arrays.
Array(Array),
/// Primitives
/// FIXME(#146922): disambiguate further
Leaf,
/// Primitive boolean type.
Bool(Bool),
/// Primitive character type.
Char(Char),
/// Primitive signed and unsigned integer type.
Int(Int),
/// Primitive floating-point type.
Float(Float),
/// String slice type.
Str(Str),
/// FIXME(#146922): add all the common types
Other,
}
Expand Down Expand Up @@ -82,3 +89,47 @@ pub struct Array {
/// The length of the array.
pub len: usize,
}

/// Compile-time type information about `bool`.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Bool {
// No additional information to provide for now.
}

/// Compile-time type information about `char`.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Char {
// No additional information to provide for now.
}

/// Compile-time type information about signed and unsigned integer types.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Int {
/// The bit width of the signed integer type.
pub bit_width: usize,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether we should use usize or u64 here to represent the bit width.

Besides, as Option is not used here, reflection on isize and usize will fall into the Int and Uint variants, and users cannot distinguish between them, which may be an issue.

Is it necessary for end users to distinguish between {u,i}size and {u,i}int? If yes, should we add a separate field or add distinct variants to indicate it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having a single Int variant that has a signed: bool field is nicer. It avoids some recurring matches that do Int(_) | Uint(_)

Copy link
Contributor

@Skgland Skgland Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether we should use usize or u64 here to represent the bit width.

My expectation would be u32 matching the associated {u,i}N::BITS123 constant that already exists on the integer types.

Footnotes

  1. https://doc.rust-lang.org/std/primitive.i8.html#associatedconstant.BITS

  2. https://doc.rust-lang.org/std/primitive.i128.html#associatedconstant.BITS

  3. https://doc.rust-lang.org/std/primitive.usize.html#associatedconstant.BITS

Copy link
Member Author

@SpriteOvO SpriteOvO Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Skgland Nice point. I found some previous discussions regarding the type of ::BITS constant. And during the stabilization of ::BITS, the choice of u32 affected some ecosystem crates (#81654), but soon after, these crates all accepted the u32 type.

So I think it makes sense to keep the type consistent with ::BITS here. Then I'd also like to change the name from bit_width to bits, also for consistency.

If there is no objection, I will make this change in a follow-up PR later (as this PR is already r+).

UPDATE: Follow-up PR #151235

/// Whether the integer type is signed.
pub signed: bool,
}

/// Compile-time type information about floating-point types.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Float {
/// The bit width of the floating-point type.
pub bit_width: usize,
}

/// Compile-time type information about string slice types.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Str {
// No additional information to provide for now.
}
45 changes: 43 additions & 2 deletions library/coretests/tests/mem/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,60 @@ fn test_tuples() {
assert_tuple_arity::<(u8, u8), 2>();

const {
match Type::of::<(u8, u8)>().kind {
match Type::of::<(i8, 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) => {}
(TypeKind::Int(a), TypeKind::Int(b)) => {
assert!(a.bit_width == 8 && a.signed);
assert!(b.bit_width == 8 && !b.signed);
}
_ => unreachable!(),
}
}
_ => unreachable!(),
}
}
}

#[test]
fn test_primitives() {
use TypeKind::*;

let Type { kind: Bool(_ty), size, .. } = (const { Type::of::<bool>() }) else { panic!() };
assert_eq!(size, Some(1));

let Type { kind: Char(_ty), size, .. } = (const { Type::of::<char>() }) else { panic!() };
assert_eq!(size, Some(4));

let Type { kind: Int(ty), size, .. } = (const { Type::of::<i32>() }) else { panic!() };
assert_eq!(size, Some(4));
assert_eq!(ty.bit_width, 32);
assert!(ty.signed);

let Type { kind: Int(ty), size, .. } = (const { Type::of::<isize>() }) else { panic!() };
assert_eq!(size, Some(size_of::<isize>()));
assert_eq!(ty.bit_width, size_of::<isize>() * 8);
assert!(ty.signed);

let Type { kind: Int(ty), size, .. } = (const { Type::of::<u32>() }) else { panic!() };
assert_eq!(size, Some(4));
assert_eq!(ty.bit_width, 32);
assert!(!ty.signed);

let Type { kind: Int(ty), size, .. } = (const { Type::of::<usize>() }) else { panic!() };
assert_eq!(size, Some(size_of::<usize>()));
assert_eq!(ty.bit_width, size_of::<usize>() * 8);
assert!(!ty.signed);

let Type { kind: Float(ty), size, .. } = (const { Type::of::<f32>() }) else { panic!() };
assert_eq!(size, Some(4));
assert_eq!(ty.bit_width, 32);

let Type { kind: Str(_ty), size, .. } = (const { Type::of::<str>() }) else { panic!() };
assert_eq!(size, None);
}
2 changes: 1 addition & 1 deletion tests/mir-opt/const_prop/invalid_constant.main.GVN.diff
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
debug _enum_without_variants => const [ZeroSized: Empty];
let _9: main::Str<"���">;
scope 4 {
debug _non_utf8_str => const Str::<"���">;
debug _non_utf8_str => const main::Str::<"���">;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
let _9: main::Str<"���">;
scope 4 {
- debug _non_utf8_str => _9;
+ debug _non_utf8_str => const Str::<"���">;
+ debug _non_utf8_str => const main::Str::<"���">;
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/lint/recommend-literal.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//~vv HELP consider importing this struct

type Real = double;
//~^ ERROR cannot find type `double` in this scope
//~| HELP perhaps you intended to use this type
Expand Down
Loading
Loading