Skip to content

Commit 994c7ba

Browse files
committed
Compile-Time Reflection MVP: tuples
1 parent a417515 commit 994c7ba

File tree

30 files changed

+453
-49
lines changed

30 files changed

+453
-49
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
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2930
enum MulAddType {
@@ -63,10 +64,10 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId
6364
}
6465
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
6566
/// Generates a value of `TypeId` for `ty` in-place.
66-
fn write_type_id(
67+
pub(crate) fn write_type_id(
6768
&mut self,
6869
ty: Ty<'tcx>,
69-
dest: &PlaceTy<'tcx, M::Provenance>,
70+
dest: &impl Writeable<'tcx, M::Provenance>,
7071
) -> InterpResult<'tcx, ()> {
7172
let tcx = self.tcx;
7273
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
@@ -278,6 +278,7 @@ language_item_table! {
278278
PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
279279
CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None;
280280

281+
Type, sym::type_info, type_struct, Target::Struct, GenericRequirement::None;
281282
TypeId, sym::type_id, type_id, Target::Struct, GenericRequirement::None;
282283

283284
// 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
@@ -213,6 +213,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
213213
| sym::type_id
214214
| sym::type_id_eq
215215
| sym::type_name
216+
| sym::type_of
216217
| sym::ub_checks
217218
| sym::variant_count
218219
| sym::wrapping_add
@@ -314,6 +315,12 @@ pub(crate) fn check_intrinsic_type(
314315
let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity();
315316
(0, 0, vec![type_id, type_id], tcx.types.bool)
316317
}
318+
sym::type_of => (
319+
0,
320+
0,
321+
vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity()],
322+
tcx.type_of(tcx.lang_items().type_struct().unwrap()).instantiate_identity(),
323+
),
317324
sym::offload => (3, 0, vec![param(0), param(1)], param(2)),
318325
sym::offset => (2, 0, vec![param(0), param(1)], param(0)),
319326
sym::arith_offset => (

compiler/rustc_span/src/symbol.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ symbols! {
283283
IteratorItem,
284284
IteratorMap,
285285
Layout,
286+
Leaf,
286287
Left,
287288
LinkedList,
288289
LintDiagnostic,
@@ -301,6 +302,7 @@ symbols! {
301302
Ordering,
302303
OsStr,
303304
OsString,
305+
Other,
304306
Output,
305307
Param,
306308
ParamSet,
@@ -379,6 +381,7 @@ symbols! {
379381
TryCapturePrintable,
380382
TryFrom,
381383
TryInto,
384+
Tuple,
382385
Ty,
383386
TyCtxt,
384387
TyKind,
@@ -2303,13 +2306,15 @@ symbols! {
23032306
type_const,
23042307
type_id,
23052308
type_id_eq,
2309+
type_info,
23062310
type_ir,
23072311
type_ir_infer_ctxt_like,
23082312
type_ir_inherent,
23092313
type_ir_interner,
23102314
type_length_limit,
23112315
type_macros,
23122316
type_name,
2317+
type_of,
23132318
type_privacy_lints,
23142319
typed_swap_nonoverlapping,
23152320
u8,

library/core/src/intrinsics/mod.rs

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

2854+
/// Compute the type information of a concrete type.
2855+
/// It can only be called at compile time, the backends do
2856+
/// not implement it.
2857+
#[rustc_intrinsic]
2858+
#[unstable(feature = "core_intrinsics", issue = "none")]
2859+
pub const fn type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type {
2860+
panic!("`TypeId::info` can only be called at compile-time")
2861+
}
2862+
28542863
/// Gets a static string slice containing the name of a type.
28552864
///
28562865
/// 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
@@ -33,6 +33,9 @@ pub use drop_guard::DropGuard;
3333
#[doc(inline)]
3434
pub use crate::intrinsics::transmute;
3535

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

0 commit comments

Comments
 (0)