Skip to content

Commit bad530b

Browse files
committed
Compile-Time Reflection MVP: tuples
1 parent a2db928 commit bad530b

File tree

28 files changed

+446
-41
lines changed

28 files changed

+446
-41
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ use std::borrow::{Borrow, Cow};
22
use std::fmt;
33
use std::hash::Hash;
44

5-
use rustc_abi::{Align, Size};
5+
use rustc_abi::{Align, FieldIdx, Size};
66
use rustc_ast::Mutability;
77
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry};
88
use rustc_hir::def_id::{DefId, LocalDefId};
99
use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem};
1010
use rustc_middle::mir::AssertMessage;
11-
use rustc_middle::mir::interpret::ReportedErrorInfo;
11+
use rustc_middle::mir::interpret::{Pointer, ReportedErrorInfo};
1212
use rustc_middle::query::TyCtxtAt;
1313
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement};
1414
use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -22,7 +22,7 @@ use crate::errors::{LongRunning, LongRunningWarn};
2222
use crate::fluent_generated as fluent;
2323
use crate::interpret::{
2424
self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
25-
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar,
25+
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, RangeSet, Scalar,
2626
compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub,
2727
throw_ub_custom, throw_unsup, throw_unsup_format,
2828
};
@@ -591,6 +591,14 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
591591
}
592592
}
593593

594+
sym::type_of => {
595+
let op = ecx.project_field(&args[0], FieldIdx::ZERO)?;
596+
let op = ecx.project_index(&op, 0)?;
597+
let id = ecx.read_scalar(&op)?.to_pointer(ecx)?;
598+
let (ty, _offset) = ecx.get_ptr_type_id(id)?;
599+
ecx.write_type_info(ty, dest)?;
600+
}
601+
594602
_ => {
595603
// We haven't handled the intrinsic, let's see if we can use a fallback body.
596604
if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {

compiler/rustc_const_eval/src/const_eval/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod error;
1313
mod eval_queries;
1414
mod fn_queries;
1515
mod machine;
16+
mod type_info;
1617
mod valtrees;
1718

1819
pub use self::dummy_machine::*;
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
use rustc_abi::FieldIdx;
2+
use rustc_hir::LangItem;
3+
use rustc_middle::mir::interpret::CtfeProvenance;
4+
use rustc_middle::span_bug;
5+
use rustc_middle::ty::layout::TyAndLayout;
6+
use rustc_middle::ty::{self, ScalarInt, Ty};
7+
use rustc_span::{Symbol, sym};
8+
9+
use crate::const_eval::CompileTimeMachine;
10+
use crate::interpret::{
11+
Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Writeable, interp_ok,
12+
};
13+
14+
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
15+
/// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place.
16+
pub(crate) fn write_type_info(
17+
&mut self,
18+
ty: Ty<'tcx>,
19+
dest: &impl Writeable<'tcx, CtfeProvenance>,
20+
) -> InterpResult<'tcx> {
21+
let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span);
22+
let ty_struct = self.tcx.type_of(ty_struct).instantiate_identity();
23+
assert_eq!(ty_struct, dest.layout().ty);
24+
let ty_struct = ty_struct.ty_adt_def().unwrap().non_enum_variant();
25+
// Fill all fields of the `TypeInfo` struct.
26+
for (idx, field) in ty_struct.fields.iter_enumerated() {
27+
let field_dest = self.project_field(dest, idx)?;
28+
let downcast = |name: Symbol| {
29+
let variants = field_dest.layout().ty.ty_adt_def().unwrap().variants();
30+
let variant_id = variants
31+
.iter_enumerated()
32+
.find(|(_idx, var)| var.name == name)
33+
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
34+
.0;
35+
36+
interp_ok((variant_id, self.project_downcast(&field_dest, variant_id)?))
37+
};
38+
match field.name {
39+
sym::kind => {
40+
let variant_index = match ty.kind() {
41+
ty::Tuple(fields) => {
42+
let (variant, variant_place) = downcast(sym::Tuple)?;
43+
// project to the single tuple variant field of `type_info::Tuple` struct type
44+
let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
45+
assert_eq!(
46+
1,
47+
tuple_place
48+
.layout()
49+
.ty
50+
.ty_adt_def()
51+
.unwrap()
52+
.non_enum_variant()
53+
.fields
54+
.len()
55+
);
56+
self.write_tuple_fields(tuple_place, fields, ty)?;
57+
variant
58+
}
59+
// For now just merge all primitives into one `Leaf` variant with no data
60+
ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Char | ty::Bool => {
61+
downcast(sym::Leaf)?.0
62+
}
63+
ty::Adt(_, _)
64+
| ty::Foreign(_)
65+
| ty::Str
66+
| ty::Array(_, _)
67+
| ty::Pat(_, _)
68+
| ty::Slice(_)
69+
| ty::RawPtr(..)
70+
| ty::Ref(..)
71+
| ty::FnDef(..)
72+
| ty::FnPtr(..)
73+
| ty::UnsafeBinder(..)
74+
| ty::Dynamic(..)
75+
| ty::Closure(..)
76+
| ty::CoroutineClosure(..)
77+
| ty::Coroutine(..)
78+
| ty::CoroutineWitness(..)
79+
| ty::Never
80+
| ty::Alias(..)
81+
| ty::Param(_)
82+
| ty::Bound(..)
83+
| ty::Placeholder(_)
84+
| ty::Infer(..)
85+
| ty::Error(_) => downcast(sym::Other)?.0,
86+
};
87+
self.write_discriminant(variant_index, &field_dest)?
88+
}
89+
other => span_bug!(self.tcx.span, "unknown `Type` field {other}"),
90+
}
91+
}
92+
93+
interp_ok(())
94+
}
95+
96+
pub(crate) fn write_tuple_fields(
97+
&mut self,
98+
tuple_place: impl Writeable<'tcx, CtfeProvenance>,
99+
fields: &[Ty<'tcx>],
100+
tuple_ty: Ty<'tcx>,
101+
) -> InterpResult<'tcx> {
102+
// project into the `type_info::Tuple::fields` field
103+
let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?;
104+
// get the `type_info::Field` type from `fields: &[Field]`
105+
let field_type = fields_slice_place
106+
.layout()
107+
.ty
108+
.builtin_deref(false)
109+
.unwrap()
110+
.sequence_element_type(self.tcx.tcx);
111+
// Create an array with as many elements as the number of fields in the inspected tuple
112+
let fields_layout =
113+
self.layout_of(Ty::new_array(self.tcx.tcx, field_type, fields.len() as u64))?;
114+
let fields_place = self.allocate(fields_layout, MemoryKind::Stack)?;
115+
let mut fields_places = self.project_array_fields(&fields_place)?;
116+
117+
let tuple_layout = self.layout_of(tuple_ty)?;
118+
119+
while let Some((i, place)) = fields_places.next(self)? {
120+
let field_ty = fields[i as usize];
121+
self.write_field(field_ty, place, tuple_layout, i)?;
122+
}
123+
124+
let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable);
125+
126+
let ptr = Immediate::new_slice(fields_place.ptr(), fields.len() as u64, self);
127+
128+
self.write_immediate(ptr, &fields_slice_place)
129+
}
130+
131+
fn write_field(
132+
&mut self,
133+
field_ty: Ty<'tcx>,
134+
place: MPlaceTy<'tcx>,
135+
layout: TyAndLayout<'tcx>,
136+
idx: u64,
137+
) -> InterpResult<'tcx> {
138+
for (field_idx, field_ty_field) in
139+
place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
140+
{
141+
let field_place = self.project_field(&place, field_idx)?;
142+
match field_ty_field.name {
143+
sym::ty => self.write_type_id(field_ty, &field_place)?,
144+
sym::offset => {
145+
let offset = layout.fields.offset(idx as usize);
146+
self.write_scalar(
147+
ScalarInt::try_from_target_usize(offset.bytes(), self.tcx.tcx).unwrap(),
148+
&field_place,
149+
)?;
150+
}
151+
other => {
152+
span_bug!(self.tcx.def_span(field_ty_field.did), "unimplemented field {other}")
153+
}
154+
}
155+
}
156+
interp_ok(())
157+
}
158+
}

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use super::{
2222
throw_ub_custom, throw_ub_format,
2323
};
2424
use crate::fluent_generated as fluent;
25+
use crate::interpret::Writeable;
2526

2627
/// Directly returns an `Allocation` containing an absolute path representation of the given type.
2728
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) {
@@ -32,10 +33,10 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId
3233
}
3334
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
3435
/// Generates a value of `TypeId` for `ty` in-place.
35-
fn write_type_id(
36+
pub(crate) fn write_type_id(
3637
&mut self,
3738
ty: Ty<'tcx>,
38-
dest: &PlaceTy<'tcx, M::Provenance>,
39+
dest: &impl Writeable<'tcx, M::Provenance>,
3940
) -> InterpResult<'tcx, ()> {
4041
let tcx = self.tcx;
4142
let type_id_hash = tcx.type_id_hash(ty).as_u128();

compiler/rustc_hir/src/lang_items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ language_item_table! {
274274
PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
275275
CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None;
276276

277+
Type, sym::type_info, type_struct, Target::Struct, GenericRequirement::None;
277278
TypeId, sym::type_id, type_id, Target::Struct, GenericRequirement::None;
278279

279280
// A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
210210
| sym::type_id
211211
| sym::type_id_eq
212212
| sym::type_name
213+
| sym::type_of
213214
| sym::ub_checks
214215
| sym::variant_count
215216
| sym::wrapping_add
@@ -310,6 +311,12 @@ pub(crate) fn check_intrinsic_type(
310311
let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity();
311312
(0, 0, vec![type_id, type_id], tcx.types.bool)
312313
}
314+
sym::type_of => (
315+
0,
316+
0,
317+
vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity()],
318+
tcx.type_of(tcx.lang_items().type_struct().unwrap()).instantiate_identity(),
319+
),
313320
sym::offset => (2, 0, vec![param(0), param(1)], param(0)),
314321
sym::arith_offset => (
315322
1,

compiler/rustc_span/src/symbol.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ symbols! {
281281
IteratorItem,
282282
IteratorMap,
283283
Layout,
284+
Leaf,
284285
Left,
285286
LinkedList,
286287
LintDiagnostic,
@@ -299,6 +300,7 @@ symbols! {
299300
Ordering,
300301
OsStr,
301302
OsString,
303+
Other,
302304
Output,
303305
Param,
304306
ParamSet,
@@ -375,6 +377,7 @@ symbols! {
375377
TryCapturePrintable,
376378
TryFrom,
377379
TryInto,
380+
Tuple,
378381
Ty,
379382
TyCtxt,
380383
TyKind,
@@ -2250,13 +2253,15 @@ symbols! {
22502253
type_const,
22512254
type_id,
22522255
type_id_eq,
2256+
type_info,
22532257
type_ir,
22542258
type_ir_infer_ctxt_like,
22552259
type_ir_inherent,
22562260
type_ir_interner,
22572261
type_length_limit,
22582262
type_macros,
22592263
type_name,
2264+
type_of,
22602265
type_privacy_lints,
22612266
typed_swap_nonoverlapping,
22622267
u8,

library/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
#![feature(str_internals)]
125125
#![feature(str_split_inclusive_remainder)]
126126
#![feature(str_split_remainder)]
127+
#![feature(type_info)]
127128
#![feature(ub_checks)]
128129
#![feature(unchecked_neg)]
129130
#![feature(unchecked_shifts)]

library/core/src/mem/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ pub use drop_guard::DropGuard;
3131
#[doc(inline)]
3232
pub use crate::intrinsics::transmute;
3333

34+
#[unstable(feature = "type_info", issue = "146922")]
35+
pub mod type_info;
36+
3437
/// Takes ownership and "forgets" about the value **without running its destructor**.
3538
///
3639
/// Any resources the value manages, such as heap memory or a file handle, will linger

library/core/src/mem/type_info.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//! MVP for exposing compile-time information about types in a
2+
//! runtime or const-eval processable way.
3+
4+
use crate::any::TypeId;
5+
6+
/// Compile-time type information.
7+
#[derive(Debug)]
8+
#[non_exhaustive]
9+
#[lang = "type_info"]
10+
#[unstable(feature = "type_info", issue = "146922")]
11+
pub struct Type {
12+
/// Per-type information
13+
pub kind: TypeKind,
14+
}
15+
16+
/// Compute the type information of a concrete type.
17+
/// It can only be called at compile time, the backends do
18+
/// not implement it.
19+
#[rustc_intrinsic]
20+
const fn type_of(_id: TypeId) -> Type {
21+
panic!("`TypeId::info` can only be called at compile-time")
22+
}
23+
24+
impl TypeId {
25+
/// Compute the type information of a concrete type.
26+
/// It can only be called at compile time.
27+
#[unstable(feature = "type_info", issue = "146922")]
28+
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
29+
pub const fn info(self) -> Type {
30+
type_of(self)
31+
}
32+
}
33+
34+
impl Type {
35+
/// Returns the type information of the generic type parameter.
36+
#[unstable(feature = "type_info", issue = "146922")]
37+
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
38+
pub const fn of<T: 'static>() -> Self {
39+
TypeId::of::<T>().info()
40+
}
41+
}
42+
43+
/// Compile-time type information.
44+
#[derive(Debug)]
45+
#[non_exhaustive]
46+
#[unstable(feature = "type_info", issue = "146922")]
47+
pub enum TypeKind {
48+
/// Tuples.
49+
Tuple(Tuple),
50+
/// Primitives
51+
/// FIXME(#146922): disambiguate further
52+
Leaf,
53+
/// FIXME(#146922): add all the common types
54+
Other,
55+
}
56+
57+
/// Compile-time type information about tuples.
58+
#[derive(Debug)]
59+
#[non_exhaustive]
60+
#[unstable(feature = "type_info", issue = "146922")]
61+
pub struct Tuple {
62+
/// All fields of a tuple.
63+
pub fields: &'static [Field],
64+
}
65+
66+
/// Compile-time type information about fields of tuples, structs and enum variants.
67+
#[derive(Debug)]
68+
#[non_exhaustive]
69+
#[unstable(feature = "type_info", issue = "146922")]
70+
pub struct Field {
71+
/// The field's type.
72+
pub ty: TypeId,
73+
/// Offset in bytes from the parent type
74+
pub offset: usize,
75+
}

0 commit comments

Comments
 (0)