Skip to content

Commit 300ec45

Browse files
committed
Compile-Time Reflection MVP: tuples
1 parent f6092f2 commit 300ec45

File tree

27 files changed

+414
-38
lines changed

27 files changed

+414
-38
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: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
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::{InterpCx, InterpResult, MPlaceTy, MemoryKind, Writeable, interp_ok};
11+
12+
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
13+
pub(crate) fn write_type_info(
14+
&mut self,
15+
ty: Ty<'tcx>,
16+
dest: &impl Writeable<'tcx, CtfeProvenance>,
17+
) -> InterpResult<'tcx> {
18+
let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span);
19+
let ty_struct = self.tcx.type_of(ty_struct).instantiate_identity();
20+
assert_eq!(ty_struct, dest.layout().ty);
21+
let ty_struct = ty_struct.ty_adt_def().unwrap().non_enum_variant();
22+
for (idx, field) in ty_struct.fields.iter_enumerated() {
23+
let field_dest = self.project_field(dest, idx)?;
24+
let downcast = |name: Symbol| {
25+
let variant_id = field_dest
26+
.layout()
27+
.ty
28+
.ty_adt_def()
29+
.unwrap()
30+
.variants()
31+
.iter_enumerated()
32+
.find(|(_idx, var)| var.name == name)
33+
.unwrap()
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+
// `Tuple` struct
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+
ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Char | ty::Bool => {
60+
downcast(sym::Leaf)?.0
61+
}
62+
ty::Adt(_, _)
63+
| ty::Foreign(_)
64+
| ty::Str
65+
| ty::Array(_, _)
66+
| ty::Pat(_, _)
67+
| ty::Slice(_)
68+
| ty::RawPtr(..)
69+
| ty::Ref(..)
70+
| ty::FnDef(..)
71+
| ty::FnPtr(..)
72+
| ty::UnsafeBinder(..)
73+
| ty::Dynamic(..)
74+
| ty::Closure(..)
75+
| ty::CoroutineClosure(..)
76+
| ty::Coroutine(..)
77+
| ty::CoroutineWitness(..)
78+
| ty::Never
79+
| ty::Alias(..)
80+
| ty::Param(_)
81+
| ty::Bound(..)
82+
| ty::Placeholder(_)
83+
| ty::Infer(..)
84+
| ty::Error(_) => downcast(sym::Other)?.0,
85+
};
86+
self.write_discriminant(variant_index, &field_dest)?
87+
}
88+
other => span_bug!(self.tcx.span, "unknown `Type` field {other}"),
89+
}
90+
}
91+
92+
interp_ok(())
93+
}
94+
95+
pub(crate) fn write_tuple_fields(
96+
&mut self,
97+
tuple_place: impl Writeable<'tcx, CtfeProvenance>,
98+
fields: &[Ty<'tcx>],
99+
tuple_ty: Ty<'tcx>,
100+
) -> InterpResult<'tcx> {
101+
// `fields` field
102+
let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?;
103+
let field_type = fields_slice_place
104+
.layout()
105+
.ty
106+
.builtin_deref(false)
107+
.unwrap()
108+
.sequence_element_type(self.tcx.tcx);
109+
let fields_layout =
110+
self.layout_of(Ty::new_array(self.tcx.tcx, field_type, fields.len() as u64))?;
111+
let fields_place = self.allocate(fields_layout, MemoryKind::Stack)?;
112+
let mut fields_places = self.project_array_fields(&fields_place)?;
113+
114+
let tuple_layout = self.layout_of(tuple_ty)?;
115+
116+
while let Some((i, place)) = fields_places.next(self)? {
117+
let field_ty = fields[i as usize];
118+
self.write_field(field_ty, place, tuple_layout, i)?;
119+
}
120+
121+
let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable);
122+
123+
let mut ptr = self.mplace_to_ref(&fields_place)?;
124+
ptr.layout = self.layout_of(Ty::new_imm_ref(
125+
self.tcx.tcx,
126+
self.tcx.lifetimes.re_static,
127+
fields_layout.ty,
128+
))?;
129+
130+
let slice_type = Ty::new_imm_ref(
131+
self.tcx.tcx,
132+
self.tcx.lifetimes.re_static,
133+
Ty::new_slice(self.tcx.tcx, field_type),
134+
);
135+
let slice_type = self.layout_of(slice_type)?;
136+
self.unsize_into(&ptr.into(), slice_type, &fields_slice_place)?;
137+
interp_ok(())
138+
}
139+
140+
fn write_field(
141+
&mut self,
142+
field_ty: Ty<'tcx>,
143+
place: MPlaceTy<'tcx>,
144+
layout: TyAndLayout<'tcx>,
145+
idx: u64,
146+
) -> InterpResult<'tcx> {
147+
for (field_idx, field_ty_field) in
148+
place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
149+
{
150+
let field_place = self.project_field(&place, field_idx)?;
151+
match field_ty_field.name {
152+
sym::ty => self.write_type_id(field_ty, &field_place)?,
153+
sym::offset => {
154+
let offset = layout.fields.offset(idx as usize);
155+
self.write_scalar(
156+
ScalarInt::try_from_target_usize(offset.bytes(), self.tcx.tcx).unwrap(),
157+
&field_place,
158+
)?;
159+
}
160+
other => {
161+
span_bug!(self.tcx.def_span(field_ty_field.did), "unimplemented field {other}")
162+
}
163+
}
164+
}
165+
interp_ok(())
166+
}
167+
}

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
@@ -202,6 +202,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
202202
| sym::type_id
203203
| sym::type_id_eq
204204
| sym::type_name
205+
| sym::type_of
205206
| sym::ub_checks
206207
| sym::variant_count
207208
| sym::wrapping_add
@@ -302,6 +303,12 @@ pub(crate) fn check_intrinsic_type(
302303
let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity();
303304
(0, 0, vec![type_id, type_id], tcx.types.bool)
304305
}
306+
sym::type_of => (
307+
0,
308+
0,
309+
vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity()],
310+
tcx.type_of(tcx.lang_items().type_struct().unwrap()).instantiate_identity(),
311+
),
305312
sym::offset => (2, 0, vec![param(0), param(1)], param(0)),
306313
sym::arith_offset => (
307314
1,

compiler/rustc_span/src/symbol.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,7 @@ symbols! {
13361336
loop_match,
13371337
lr,
13381338
lt,
1339+
Leaf,
13391340
m68k_target_feature,
13401341
macro_at_most_once_rep,
13411342
macro_attr,
@@ -1582,6 +1583,7 @@ symbols! {
15821583
overflow_checks,
15831584
overlapping_marker_traits,
15841585
owned_box,
1586+
Other,
15851587
packed,
15861588
packed_bundled_libs,
15871589
panic,
@@ -2244,15 +2246,18 @@ symbols! {
22442246
type_const,
22452247
type_id,
22462248
type_id_eq,
2249+
type_info,
22472250
type_ir,
22482251
type_ir_infer_ctxt_like,
22492252
type_ir_inherent,
22502253
type_ir_interner,
22512254
type_length_limit,
22522255
type_macros,
22532256
type_name,
2257+
type_of,
22542258
type_privacy_lints,
22552259
typed_swap_nonoverlapping,
2260+
Tuple,
22562261
u8,
22572262
u8_legacy_const_max,
22582263
u8_legacy_const_min,

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 = "none")]
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: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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+
Leaf,
52+
/// TODO
53+
Unimplemented,
54+
}
55+
56+
/// Compile-time type information about tuples.
57+
#[derive(Debug)]
58+
#[non_exhaustive]
59+
#[unstable(feature = "type_info", issue = "146922")]
60+
pub struct Tuple {
61+
/// All fields of a tuple.
62+
pub fields: &'static [Field],
63+
}
64+
65+
/// Compile-time type information about fields of tuples, structs and enum variants.
66+
#[derive(Debug)]
67+
#[non_exhaustive]
68+
#[unstable(feature = "type_info", issue = "146922")]
69+
pub struct Field {
70+
/// The field's type.
71+
pub ty: TypeId,
72+
/// Offset in bytes from the parent type
73+
pub offset: usize,
74+
}

0 commit comments

Comments
 (0)