Skip to content

Commit ade493e

Browse files
committed
Add support for trait object types in type_info reflection
1 parent 3d087e6 commit ade493e

File tree

4 files changed

+297
-2
lines changed

4 files changed

+297
-2
lines changed

compiler/rustc_const_eval/src/const_eval/type_info.rs

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use rustc_hir::LangItem;
44
use rustc_middle::mir::interpret::{CtfeProvenance, Scalar};
55
use rustc_middle::span_bug;
66
use rustc_middle::ty::layout::TyAndLayout;
7-
use rustc_middle::ty::{self, Const, ScalarInt, Ty};
7+
use rustc_middle::ty::{self, Const, Region, ScalarInt, Ty};
8+
use rustc_span::def_id::DefId;
89
use rustc_span::{Symbol, sym};
910

1011
use crate::const_eval::CompileTimeMachine;
@@ -112,6 +113,12 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
112113

113114
variant
114115
}
116+
ty::Dynamic(predicates, region) => {
117+
let (variant, variant_place) = downcast(sym::DynTrait)?;
118+
let dyn_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
119+
self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?;
120+
variant
121+
}
115122
ty::Adt(_, _)
116123
| ty::Foreign(_)
117124
| ty::Pat(_, _)
@@ -120,7 +127,6 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
120127
| ty::FnDef(..)
121128
| ty::FnPtr(..)
122129
| ty::UnsafeBinder(..)
123-
| ty::Dynamic(..)
124130
| ty::Closure(..)
125131
| ty::CoroutineClosure(..)
126132
| ty::Coroutine(..)
@@ -159,6 +165,140 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
159165
interp_ok(())
160166
}
161167

168+
fn write_dyn_trait_type_info(
169+
&mut self,
170+
dyn_place: impl Writeable<'tcx, CtfeProvenance>,
171+
data: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
172+
region: Region<'tcx>,
173+
) -> InterpResult<'tcx> {
174+
let tcx = self.tcx.tcx;
175+
176+
// Find the principal trait ref (for super trait collection), collect auto traits,
177+
// and collect all projection predicates (used when computing TypeId for each supertrait).
178+
let mut principal: Option<ty::Binder<'tcx, ty::ExistentialTraitRef<'tcx>>> = None;
179+
let mut auto_traits_def_ids: Vec<ty::Binder<'tcx, DefId>> = Vec::new();
180+
let mut projections: Vec<ty::Binder<'tcx, ty::ExistentialProjection<'tcx>>> = Vec::new();
181+
182+
for b in data.iter() {
183+
match b.skip_binder() {
184+
ty::ExistentialPredicate::Trait(tr) => principal = Some(b.rebind(tr)),
185+
ty::ExistentialPredicate::AutoTrait(did) => auto_traits_def_ids.push(b.rebind(did)),
186+
ty::ExistentialPredicate::Projection(p) => projections.push(b.rebind(p)),
187+
}
188+
}
189+
190+
// This is to make principal dyn type include Trait and projection predicates, excluding auto traits.
191+
let principal_ty: Option<Ty<'tcx>> = principal.map(|_tr| {
192+
let preds = tcx
193+
.mk_poly_existential_predicates_from_iter(data.iter().filter(|b| {
194+
!matches!(b.skip_binder(), ty::ExistentialPredicate::AutoTrait(_))
195+
}));
196+
Ty::new_dynamic(tcx, preds, region)
197+
});
198+
199+
// DynTrait { predicates: &'static [Trait] }
200+
for (field_idx, field) in
201+
dyn_place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
202+
{
203+
let field_place = self.project_field(&dyn_place, field_idx)?;
204+
match field.name {
205+
sym::predicates => {
206+
self.write_dyn_trait_predicates_slice(
207+
&field_place,
208+
principal_ty,
209+
&auto_traits_def_ids,
210+
region,
211+
)?;
212+
}
213+
other => {
214+
span_bug!(self.tcx.def_span(field.did), "unimplemented DynTrait field {other}")
215+
}
216+
}
217+
}
218+
219+
interp_ok(())
220+
}
221+
222+
fn mk_dyn_principal_auto_trait_ty(
223+
&self,
224+
auto_trait_def_id: ty::Binder<'tcx, DefId>,
225+
region: Region<'tcx>,
226+
) -> Ty<'tcx> {
227+
let tcx = self.tcx.tcx;
228+
229+
// Preserve the binder vars from the original auto-trait predicate.
230+
let pred_inner = ty::ExistentialPredicate::AutoTrait(auto_trait_def_id.skip_binder());
231+
let pred = ty::Binder::bind_with_vars(pred_inner, auto_trait_def_id.bound_vars());
232+
233+
let preds = tcx.mk_poly_existential_predicates_from_iter([pred].into_iter());
234+
Ty::new_dynamic(tcx, preds, region)
235+
}
236+
237+
fn write_dyn_trait_predicates_slice(
238+
&mut self,
239+
slice_place: &impl Writeable<'tcx, CtfeProvenance>,
240+
principal_ty: Option<Ty<'tcx>>,
241+
auto_trait_def_ids: &[ty::Binder<'tcx, DefId>],
242+
region: Region<'tcx>,
243+
) -> InterpResult<'tcx> {
244+
let tcx = self.tcx.tcx;
245+
246+
// total entries in DynTrait predicates
247+
let total_len = principal_ty.map(|_| 1).unwrap_or(0) + auto_trait_def_ids.len();
248+
249+
// element type = Trait
250+
let slice_ty = slice_place.layout().ty.builtin_deref(false).unwrap(); // [Trait]
251+
let elem_ty = slice_ty.sequence_element_type(tcx); // Trait
252+
253+
let arr_layout = self.layout_of(Ty::new_array(tcx, elem_ty, total_len as u64))?;
254+
let arr_place = self.allocate(arr_layout, MemoryKind::Stack)?;
255+
let mut elems = self.project_array_fields(&arr_place)?;
256+
257+
// principal entry (if any)
258+
if let Some(principal_ty) = principal_ty {
259+
let Some((_i, elem_place)) = elems.next(self)? else {
260+
span_bug!(self.tcx.span, "DynTrait.predicates length computed wrong (principal)");
261+
};
262+
self.write_dyn_trait_trait(elem_place, principal_ty, false)?;
263+
}
264+
265+
// auto trait entries
266+
for auto in auto_trait_def_ids {
267+
let Some((_i, elem_place)) = elems.next(self)? else {
268+
span_bug!(self.tcx.span, "DynTrait.predicates length computed wrong (auto)");
269+
};
270+
let auto_ty = self.mk_dyn_principal_auto_trait_ty(*auto, region);
271+
self.write_dyn_trait_trait(elem_place, auto_ty, true)?;
272+
}
273+
let arr_place = arr_place.map_provenance(CtfeProvenance::as_immutable);
274+
let imm = Immediate::new_slice(arr_place.ptr(), total_len as u64, self);
275+
self.write_immediate(imm, slice_place)
276+
}
277+
278+
fn write_dyn_trait_trait(
279+
&mut self,
280+
trait_place: MPlaceTy<'tcx>,
281+
trait_ty: Ty<'tcx>,
282+
is_auto: bool,
283+
) -> InterpResult<'tcx> {
284+
// Trait { super_traits: &'static [TypeId], is_auto: bool }
285+
for (field_idx, field) in
286+
trait_place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
287+
{
288+
let field_place = self.project_field(&trait_place, field_idx)?;
289+
match field.name {
290+
sym::ty => {
291+
self.write_type_id(trait_ty, &field_place)?;
292+
}
293+
sym::is_auto => self.write_scalar(Scalar::from_bool(is_auto), &field_place)?,
294+
other => {
295+
span_bug!(self.tcx.def_span(field.did), "unimplemented Trait field {other}")
296+
}
297+
}
298+
}
299+
interp_ok(())
300+
}
301+
162302
pub(crate) fn write_tuple_fields(
163303
&mut self,
164304
tuple_place: impl Writeable<'tcx, CtfeProvenance>,

compiler/rustc_span/src/symbol.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ symbols! {
186186
AtomicU64,
187187
AtomicU128,
188188
AtomicUsize,
189+
AutoTrait,
189190
BTreeEntry,
190191
BTreeMap,
191192
BTreeSet,
@@ -231,6 +232,7 @@ symbols! {
231232
Display,
232233
DoubleEndedIterator,
233234
Duration,
235+
DynTrait,
234236
Encodable,
235237
Encoder,
236238
Enumerate,
@@ -1294,6 +1296,7 @@ symbols! {
12941296
io_stdout,
12951297
irrefutable_let_patterns,
12961298
is,
1299+
is_auto,
12971300
is_val_statically_known,
12981301
isa_attribute,
12991302
isize,
@@ -1747,6 +1750,7 @@ symbols! {
17471750
precise_capturing_in_traits,
17481751
precise_pointer_size_matching,
17491752
precision,
1753+
predicates,
17501754
pref_align_of,
17511755
prefetch_read_data,
17521756
prefetch_read_instruction,

library/core/src/mem/type_info.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub enum TypeKind {
4545
Tuple(Tuple),
4646
/// Arrays.
4747
Array(Array),
48+
/// Dynamic Traits.
49+
DynTrait(DynTrait),
4850
/// Primitive boolean type.
4951
Bool(Bool),
5052
/// Primitive character type.
@@ -92,6 +94,26 @@ pub struct Array {
9294
pub len: usize,
9395
}
9496

97+
/// Compile-time type information about dynamic traits.
98+
#[derive(Debug)]
99+
#[non_exhaustive]
100+
#[unstable(feature = "type_info", issue = "146922")]
101+
pub struct DynTrait {
102+
/// The predicates of a dynamic trait.
103+
pub predicates: &'static [DynTraitPredicate],
104+
}
105+
106+
/// Compile-time type information about a dynamic trait.
107+
#[derive(Debug)]
108+
#[non_exhaustive]
109+
#[unstable(feature = "type_info", issue = "146922")]
110+
pub struct DynTraitPredicate {
111+
/// The type of the trait as a dynamic trait type.
112+
pub ty: TypeId,
113+
/// Whether the trait is an auto trait
114+
pub is_auto: bool,
115+
}
116+
95117
/// Compile-time type information about `bool`.
96118
#[derive(Debug)]
97119
#[non_exhaustive]

library/coretests/tests/mem/type_info.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,132 @@ fn test_references() {
125125
_ => unreachable!(),
126126
}
127127
}
128+
129+
#[test]
130+
fn test_dynamic_traits() {
131+
use std::collections::HashSet;
132+
use std::mem::type_info::DynTraitPredicate;
133+
trait A<T> {}
134+
135+
trait B<const CONST_NUM: i32> {
136+
type Foo;
137+
}
138+
139+
trait ProjectorTrait<'a, 'b> {}
140+
141+
trait FooTrait<'a, 'b, const CONST_NUM: i32> {}
142+
143+
fn preds_of<T: ?Sized + 'static>() -> &'static [DynTraitPredicate] {
144+
match const { Type::of::<T>() }.kind {
145+
TypeKind::DynTrait(d) => d.predicates,
146+
_ => unreachable!(),
147+
}
148+
}
149+
150+
fn pred<'a>(preds: &'a [DynTraitPredicate], want: TypeId) -> &'a DynTraitPredicate {
151+
preds
152+
.iter()
153+
.find(|p| p.ty == want)
154+
.unwrap_or_else(|| panic!("missing predicate for {want:?}"))
155+
}
156+
157+
fn assert_typeid_set_eq(actual: &[TypeId], expected: &[TypeId]) {
158+
let actual_set: HashSet<TypeId> = actual.iter().copied().collect();
159+
let expected_set: HashSet<TypeId> = expected.iter().copied().collect();
160+
assert_eq!(actual.len(), actual_set.len(), "duplicates present: {actual:?}");
161+
assert_eq!(
162+
actual_set, expected_set,
163+
"unexpected ids.\nactual: {actual:?}\nexpected: {expected:?}"
164+
);
165+
}
166+
167+
fn assert_predicates_exact(preds: &[DynTraitPredicate], expected_pred_ids: &[TypeId]) {
168+
let actual_pred_ids: Vec<TypeId> = preds.iter().map(|p| p.ty).collect();
169+
assert_typeid_set_eq(&actual_pred_ids, expected_pred_ids);
170+
}
171+
172+
// dyn Send
173+
{
174+
let preds = preds_of::<dyn Send>();
175+
assert_predicates_exact(preds, &[TypeId::of::<dyn Send>()]);
176+
177+
let p = pred(preds, TypeId::of::<dyn Send>());
178+
assert!(p.is_auto);
179+
}
180+
181+
// dyn A<i32>
182+
{
183+
let preds = preds_of::<dyn A<i32>>();
184+
assert_predicates_exact(preds, &[TypeId::of::<dyn A<i32>>()]);
185+
186+
let p = pred(preds, TypeId::of::<dyn A<i32>>());
187+
assert!(!p.is_auto);
188+
}
189+
190+
// dyn B<5, Foo = i32>
191+
{
192+
let preds = preds_of::<dyn B<5, Foo = i32>>();
193+
assert_predicates_exact(preds, &[TypeId::of::<dyn B<5, Foo = i32>>()]);
194+
195+
let e = pred(preds, TypeId::of::<dyn B<5, Foo = i32>>());
196+
assert!(!e.is_auto);
197+
}
198+
199+
// dyn for<'a> FooTrait<'a, 'a, 7>
200+
{
201+
let preds = preds_of::<dyn for<'a> FooTrait<'a, 'a, 7>>();
202+
assert_predicates_exact(preds, &[TypeId::of::<dyn for<'a> FooTrait<'a, 'a, 7>>()]);
203+
204+
let foo = pred(preds, TypeId::of::<dyn for<'a> FooTrait<'a, 'a, 7>>());
205+
assert!(!foo.is_auto);
206+
}
207+
208+
// dyn FooTrait<'static, 'static, 7>
209+
{
210+
let preds = preds_of::<dyn FooTrait<'static, 'static, 7>>();
211+
assert_predicates_exact(preds, &[TypeId::of::<dyn FooTrait<'static, 'static, 7>>()]);
212+
213+
let foo = pred(preds, TypeId::of::<dyn FooTrait<'static, 'static, 7>>());
214+
assert!(!foo.is_auto);
215+
}
216+
217+
// dyn for<'a, 'b> FooTrait<'a, 'b, 7>
218+
{
219+
let preds = preds_of::<dyn for<'a, 'b> FooTrait<'a, 'b, 7>>();
220+
assert_predicates_exact(preds, &[TypeId::of::<dyn for<'a, 'b> FooTrait<'a, 'b, 7>>()]);
221+
222+
let foo = pred(preds, TypeId::of::<dyn for<'a, 'b> FooTrait<'a, 'b, 7>>());
223+
assert!(!foo.is_auto);
224+
}
225+
226+
// dyn for<'a, 'b> ProjectorTrait<'a, 'b>
227+
{
228+
let preds = preds_of::<dyn for<'a, 'b> ProjectorTrait<'a, 'b>>();
229+
assert_predicates_exact(preds, &[TypeId::of::<dyn for<'a, 'b> ProjectorTrait<'a, 'b>>()]);
230+
231+
let proj = pred(preds, TypeId::of::<dyn for<'a, 'b> ProjectorTrait<'a, 'b>>());
232+
assert!(!proj.is_auto);
233+
}
234+
235+
// dyn for<'a> FooTrait<'a, 'a, 7> + Send + Sync
236+
{
237+
let preds = preds_of::<dyn for<'a> FooTrait<'a, 'a, 7> + Send + Sync>();
238+
assert_predicates_exact(
239+
preds,
240+
&[
241+
TypeId::of::<dyn for<'a> FooTrait<'a, 'a, 7>>(),
242+
TypeId::of::<dyn Send>(),
243+
TypeId::of::<dyn Sync>(),
244+
],
245+
);
246+
247+
let foo = pred(preds, TypeId::of::<dyn for<'a> FooTrait<'a, 'a, 7>>());
248+
assert!(!foo.is_auto);
249+
250+
let send = pred(preds, TypeId::of::<dyn Send>());
251+
assert!(send.is_auto);
252+
253+
let sync = pred(preds, TypeId::of::<dyn Sync>());
254+
assert!(sync.is_auto);
255+
}
256+
}

0 commit comments

Comments
 (0)