Skip to content

Commit da1c9c5

Browse files
committed
Compile-Time Reflection MVP: tuples
1 parent 23fced0 commit da1c9c5

File tree

29 files changed

+444
-40
lines changed

29 files changed

+444
-40
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ 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,11 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
591591
}
592592
}
593593

594+
sym::type_of => {
595+
let ty = ecx.read_type_id(&args[0])?;
596+
ecx.write_type_info(ty, dest)?;
597+
}
598+
594599
_ => {
595600
// We haven't handled the intrinsic, let's see if we can use a fallback body.
596601
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
@@ -24,6 +24,7 @@ use super::{
2424
throw_ub_custom, throw_ub_format,
2525
};
2626
use crate::fluent_generated as fluent;
27+
use crate::interpret::Writeable;
2728

2829
/// Directly returns an `Allocation` containing an absolute path representation of the given type.
2930
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) {
@@ -34,10 +35,10 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId
3435
}
3536
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
3637
/// Generates a value of `TypeId` for `ty` in-place.
37-
fn write_type_id(
38+
pub(crate) fn write_type_id(
3839
&mut self,
3940
ty: Ty<'tcx>,
40-
dest: &PlaceTy<'tcx, M::Provenance>,
41+
dest: &impl Writeable<'tcx, M::Provenance>,
4142
) -> InterpResult<'tcx, ()> {
4243
let tcx = self.tcx;
4344
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
@@ -276,6 +276,7 @@ language_item_table! {
276276
PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
277277
CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None;
278278

279+
Type, sym::type_info, type_struct, Target::Struct, GenericRequirement::None;
279280
TypeId, sym::type_id, type_id, Target::Struct, GenericRequirement::None;
280281

281282
// 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
@@ -282,6 +282,7 @@ symbols! {
282282
IteratorItem,
283283
IteratorMap,
284284
Layout,
285+
Leaf,
285286
Left,
286287
LinkedList,
287288
LintDiagnostic,
@@ -300,6 +301,7 @@ symbols! {
300301
Ordering,
301302
OsStr,
302303
OsString,
304+
Other,
303305
Output,
304306
Param,
305307
ParamSet,
@@ -377,6 +379,7 @@ symbols! {
377379
TryCapturePrintable,
378380
TryFrom,
379381
TryInto,
382+
Tuple,
380383
Ty,
381384
TyCtxt,
382385
TyKind,
@@ -2259,13 +2262,15 @@ symbols! {
22592262
type_const,
22602263
type_id,
22612264
type_id_eq,
2265+
type_info,
22622266
type_ir,
22632267
type_ir_infer_ctxt_like,
22642268
type_ir_inherent,
22652269
type_ir_interner,
22662270
type_length_limit,
22672271
type_macros,
22682272
type_name,
2273+
type_of,
22692274
type_privacy_lints,
22702275
typed_swap_nonoverlapping,
22712276
u8,

library/core/src/intrinsics/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2799,6 +2799,15 @@ pub const unsafe fn size_of_val<T: ?Sized>(ptr: *const T) -> usize;
27992799
#[rustc_intrinsic_const_stable_indirect]
28002800
pub const unsafe fn align_of_val<T: ?Sized>(ptr: *const T) -> usize;
28012801

2802+
/// Compute the type information of a concrete type.
2803+
/// It can only be called at compile time, the backends do
2804+
/// not implement it.
2805+
#[rustc_intrinsic]
2806+
#[unstable(feature = "core_intrinsics", issue = "none")]
2807+
pub const fn type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type {
2808+
panic!("`TypeId::info` can only be called at compile-time")
2809+
}
2810+
28022811
/// Gets a static string slice containing the name of a type.
28032812
///
28042813
/// Note that, unlike most intrinsics, this can only be called at compile-time

library/core/src/lib.rs

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

library/core/src/mem/mod.rs

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

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

0 commit comments

Comments
 (0)