Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
84 changes: 80 additions & 4 deletions compiler/rustc_const_eval/src/const_eval/type_info.rs
Original file line number Diff line number Diff line change
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,60 @@ 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)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_primitive_type_info(place, ty, None)?;
variant
}
ty::Char => {
let (variant, variant_place) = downcast(sym::Char)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_primitive_type_info(place, ty, None)?;
variant
}
ty::Int(int_ty) => {
let (variant, variant_place) = downcast(sym::Int)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_primitive_type_info(
place,
ty,
Some(
int_ty
.bit_width()
.unwrap_or_else(/* isize */ ptr_bit_width),
),
)?;
variant
}
ty::Uint(uint_ty) => {
let (variant, variant_place) = downcast(sym::Uint)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_primitive_type_info(
place,
ty,
Some(
uint_ty
.bit_width()
.unwrap_or_else(/* usize */ ptr_bit_width),
),
)?;
variant
}
ty::Float(float_ty) => {
let (variant, variant_place) = downcast(sym::Float)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_primitive_type_info(place, ty, Some(float_ty.bit_width()))?;
variant
}
ty::Str => {
let (variant, variant_place) = downcast(sym::Str)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_primitive_type_info(place, ty, None)?;
variant
}
ty::Adt(_, _)
| ty::Foreign(_)
| ty::Str
| ty::Pat(_, _)
| ty::Slice(_)
| ty::RawPtr(..)
Expand Down Expand Up @@ -203,4 +251,32 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {

interp_ok(())
}

// This method always writes to field `ty`.
// If field `bit_width` is present, it also writes to it (in which case parameter `write_bit_width` must be `Some`).
fn write_primitive_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
ty: Ty<'tcx>,
write_bit_width: Option<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::ty => self.write_type_id(ty, &field_place)?,
sym::bit_width => {
let bit_width = write_bit_width
.expect("type info struct needs a `bit_width` but none was provided");
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 All @@ -387,6 +391,7 @@ symbols! {
Ty,
TyCtxt,
TyKind,
Uint,
Unknown,
Unsize,
UnsizedConstParamTy,
Expand Down Expand Up @@ -584,6 +589,7 @@ symbols! {
binaryheap_iter,
bind_by_move_pattern_guards,
bindings_after_at,
bit_width,
bitand,
bitand_assign,
bitor,
Expand Down
75 changes: 72 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,18 @@ 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 integer type.
Int(Int),
/// Primitive unsigned integer type.
Uint(Uint),
/// 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 +91,63 @@ 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 {
/// The type id of `bool`.
pub ty: TypeId,
}

/// Compile-time type information about `char`.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Char {
/// The type id of `char`.
pub ty: TypeId,
}

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

Copy link
Copy Markdown
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
Copy Markdown
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(_)

@Skgland Skgland Jan 16, 2026

Copy link
Copy Markdown
Contributor

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

@SpriteOvO SpriteOvO Jan 16, 2026

Copy link
Copy Markdown
Member Author

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

}

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

/// Compile-time type information about floating-point types.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Float {
/// The type id of floating-point type.
pub ty: TypeId,
/// 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 {
/// The type id of `str`.
pub ty: TypeId,
}
39 changes: 38 additions & 1 deletion library/coretests/tests/mem/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,48 @@ fn test_tuples() {
assert!(b.offset == 1);

match (a.ty.info().kind, b.ty.info().kind) {
(TypeKind::Leaf, TypeKind::Leaf) => {}
(TypeKind::Uint(a), TypeKind::Uint(b)) => {
assert!(a.bit_width == 8);
assert!(b.bit_width == 8);
}
_ => 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);

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);

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

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

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