Skip to content

Commit 598d4c8

Browse files
authored
Rollup merge of rust-lang#152003 - 9SonSteroids:trait_info_of, r=oli-obk
Reflection TypeId::trait_info_of *[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/rust/pull/152003)* This is for rust-lang#146922. As rust-lang#151236 was requested to be remade by someone I implemented the functionality as `TypeId::trait_info_of` which additionally allows getting the vtable pointer to build `dyn` Objects in recursive reflection. It allows checking if a TypeId implements a trait. Since this is my first PR feel free to tell me if there are any formal issues.
2 parents 218170d + 04e9918 commit 598d4c8

16 files changed

Lines changed: 343 additions & 86 deletions

File tree

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ 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, FIRST_VARIANT, Size};
66
use rustc_ast::Mutability;
77
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry};
88
use rustc_errors::msg;
@@ -24,7 +24,7 @@ use crate::interpret::{
2424
self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
2525
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar,
2626
compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub,
27-
throw_ub_custom, throw_unsup, throw_unsup_format,
27+
throw_ub_custom, throw_unsup, throw_unsup_format, type_implements_dyn_trait,
2828
};
2929

3030
/// When hitting this many interpreted terminators we emit a deny by default lint
@@ -598,6 +598,22 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
598598
}
599599
}
600600

601+
sym::type_id_vtable => {
602+
let tp_ty = ecx.read_type_id(&args[0])?;
603+
let result_ty = ecx.read_type_id(&args[1])?;
604+
605+
let (implements_trait, preds) = type_implements_dyn_trait(ecx, tp_ty, result_ty)?;
606+
607+
if implements_trait {
608+
let vtable_ptr = ecx.get_vtable_ptr(tp_ty, preds)?;
609+
// Writing a non-null pointer into an `Option<NonNull>` will automatically make it `Some`.
610+
ecx.write_pointer(vtable_ptr, dest)?;
611+
} else {
612+
// Write `None`
613+
ecx.write_discriminant(FIRST_VARIANT, dest)?;
614+
}
615+
}
616+
601617
sym::type_of => {
602618
let ty = ecx.read_type_id(&args[0])?;
603619
ecx.write_type_info(ty, dest)?;

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,16 @@
44
55
mod simd;
66

7-
use rustc_abi::{FIRST_VARIANT, FieldIdx, HasDataLayout, Size, VariantIdx};
7+
use rustc_abi::{FieldIdx, HasDataLayout, Size, VariantIdx};
88
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
99
use rustc_data_structures::assert_matches;
1010
use rustc_errors::msg;
11-
use rustc_hir::def_id::CRATE_DEF_ID;
12-
use rustc_infer::infer::TyCtxtInferExt;
1311
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint};
1412
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
1513
use rustc_middle::ty::layout::TyAndLayout;
16-
use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt, TypeVisitableExt};
14+
use rustc_middle::ty::{FloatTy, Ty, TyCtxt, TypeVisitableExt};
1715
use rustc_middle::{bug, span_bug, ty};
1816
use rustc_span::{Symbol, sym};
19-
use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt};
2017
use tracing::trace;
2118

2219
use super::memory::MemoryKind;
@@ -227,44 +224,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
227224

228225
self.write_scalar(Scalar::from_target_usize(offset, self), dest)?;
229226
}
230-
sym::vtable_for => {
231-
let tp_ty = instance.args.type_at(0);
232-
let result_ty = instance.args.type_at(1);
233-
234-
ensure_monomorphic_enough(tcx, tp_ty)?;
235-
ensure_monomorphic_enough(tcx, result_ty)?;
236-
let ty::Dynamic(preds, _) = result_ty.kind() else {
237-
span_bug!(
238-
self.find_closest_untracked_caller_location(),
239-
"Invalid type provided to vtable_for::<T, U>. U must be dyn Trait, got {result_ty}."
240-
);
241-
};
242-
243-
let (infcx, param_env) =
244-
self.tcx.infer_ctxt().build_with_typing_env(self.typing_env);
245-
246-
let ocx = ObligationCtxt::new(&infcx);
247-
ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| {
248-
let pred = pred.with_self_ty(tcx, tp_ty);
249-
// Lifetimes can only be 'static because of the bound on T
250-
let pred = ty::fold_regions(tcx, pred, |r, _| {
251-
if r == tcx.lifetimes.re_erased { tcx.lifetimes.re_static } else { r }
252-
});
253-
Obligation::new(tcx, ObligationCause::dummy(), param_env, pred)
254-
}));
255-
let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().is_empty();
256-
// Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default"
257-
let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty();
258-
259-
if regions_are_valid && type_impls_trait {
260-
let vtable_ptr = self.get_vtable_ptr(tp_ty, preds)?;
261-
// Writing a non-null pointer into an `Option<NonNull>` will automatically make it `Some`.
262-
self.write_pointer(vtable_ptr, dest)?;
263-
} else {
264-
// Write `None`
265-
self.write_discriminant(FIRST_VARIANT, dest)?;
266-
}
267-
}
268227
sym::variant_count => {
269228
let tp_ty = instance.args.type_at(0);
270229
let ty = match tp_ty.kind() {

compiler/rustc_const_eval/src/interpret/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@ use self::place::{MemPlace, Place};
3838
pub use self::projection::{OffsetMode, Projectable};
3939
pub use self::stack::{Frame, FrameInfo, LocalState, ReturnContinuation};
4040
pub use self::util::EnteredTraceSpan;
41-
pub(crate) use self::util::create_static_alloc;
41+
pub(crate) use self::util::{create_static_alloc, type_implements_dyn_trait};
4242
pub use self::validity::{CtfeValidationMode, RangeSet, RefTracking};
4343
pub use self::visitor::ValueVisitor;

compiler/rustc_const_eval/src/interpret/util.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,51 @@
1-
use rustc_hir::def_id::LocalDefId;
2-
use rustc_middle::mir;
1+
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
2+
use rustc_infer::infer::TyCtxtInferExt;
3+
use rustc_infer::traits::{Obligation, ObligationCause};
34
use rustc_middle::mir::interpret::{AllocInit, Allocation, GlobalAlloc, InterpResult, Pointer};
45
use rustc_middle::ty::layout::TyAndLayout;
5-
use rustc_middle::ty::{TyCtxt, TypeVisitable, TypeVisitableExt};
6+
use rustc_middle::ty::{PolyExistentialPredicate, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
7+
use rustc_middle::{mir, span_bug, ty};
8+
use rustc_trait_selection::traits::ObligationCtxt;
69
use tracing::debug;
710

811
use super::{InterpCx, MPlaceTy, MemoryKind, interp_ok, throw_inval};
912
use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult};
13+
use crate::interpret::Machine;
14+
15+
/// Checks if a type implements predicates.
16+
/// Calls `ensure_monomorphic_enough` on `ty` and `trait_ty` for you.
17+
pub(crate) fn type_implements_dyn_trait<'tcx, M: Machine<'tcx>>(
18+
ecx: &mut InterpCx<'tcx, M>,
19+
ty: Ty<'tcx>,
20+
trait_ty: Ty<'tcx>,
21+
) -> InterpResult<'tcx, (bool, &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>)> {
22+
ensure_monomorphic_enough(ecx.tcx.tcx, ty)?;
23+
ensure_monomorphic_enough(ecx.tcx.tcx, trait_ty)?;
24+
25+
let ty::Dynamic(preds, _) = trait_ty.kind() else {
26+
span_bug!(
27+
ecx.find_closest_untracked_caller_location(),
28+
"Invalid type provided to type_implements_predicates. U must be dyn Trait, got {trait_ty}."
29+
);
30+
};
31+
32+
let (infcx, param_env) = ecx.tcx.infer_ctxt().build_with_typing_env(ecx.typing_env);
33+
34+
let ocx = ObligationCtxt::new(&infcx);
35+
ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| {
36+
let pred = pred.with_self_ty(ecx.tcx.tcx, ty);
37+
// Lifetimes can only be 'static because of the bound on T
38+
let pred = rustc_middle::ty::fold_regions(ecx.tcx.tcx, pred, |r, _| {
39+
if r == ecx.tcx.tcx.lifetimes.re_erased { ecx.tcx.tcx.lifetimes.re_static } else { r }
40+
});
41+
Obligation::new(ecx.tcx.tcx, ObligationCause::dummy(), param_env, pred)
42+
}));
43+
let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().is_empty();
44+
// Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default"
45+
let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty();
46+
47+
interp_ok((regions_are_valid && type_impls_trait, preds))
48+
}
1049

1150
/// Checks whether a type contains generic parameters which must be instantiated.
1251
///

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,12 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
213213
| sym::truncf128
214214
| sym::type_id
215215
| sym::type_id_eq
216+
| sym::type_id_vtable
216217
| sym::type_name
217218
| sym::type_of
218219
| sym::ub_checks
219220
| sym::va_copy
220221
| sym::variant_count
221-
| sym::vtable_for
222222
| sym::wrapping_add
223223
| sym::wrapping_mul
224224
| sym::wrapping_sub
@@ -323,6 +323,25 @@ pub(crate) fn check_intrinsic_type(
323323
let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap();
324324
(0, 0, vec![type_id, type_id], tcx.types.bool)
325325
}
326+
sym::type_id_vtable => {
327+
let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span);
328+
let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata);
329+
let dyn_metadata_args =
330+
tcx.mk_args(&[Ty::new_ptr(tcx, tcx.types.unit, ty::Mutability::Not).into()]);
331+
let dyn_ty = Ty::new_adt(tcx, dyn_metadata_adt_ref, dyn_metadata_args);
332+
333+
let option_did = tcx.require_lang_item(LangItem::Option, span);
334+
let option_adt_ref = tcx.adt_def(option_did);
335+
let option_args = tcx.mk_args(&[dyn_ty.into()]);
336+
let ret_ty = Ty::new_adt(tcx, option_adt_ref, option_args);
337+
338+
(
339+
0,
340+
0,
341+
vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); 2],
342+
ret_ty,
343+
)
344+
}
326345
sym::type_of => (
327346
0,
328347
0,
@@ -675,20 +694,6 @@ pub(crate) fn check_intrinsic_type(
675694
(0, 0, vec![Ty::new_imm_ptr(tcx, tcx.types.unit)], tcx.types.usize)
676695
}
677696

678-
sym::vtable_for => {
679-
let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span);
680-
let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata);
681-
let dyn_metadata_args = tcx.mk_args(&[param(1).into()]);
682-
let dyn_ty = Ty::new_adt(tcx, dyn_metadata_adt_ref, dyn_metadata_args);
683-
684-
let option_did = tcx.require_lang_item(LangItem::Option, span);
685-
let option_adt_ref = tcx.adt_def(option_did);
686-
let option_args = tcx.mk_args(&[dyn_ty.into()]);
687-
let ret_ty = Ty::new_adt(tcx, option_adt_ref, option_args);
688-
689-
(2, 0, vec![], ret_ty)
690-
}
691-
692697
// This type check is not particularly useful, but the `where` bounds
693698
// on the definition in `core` do the heavy lifting for checking it.
694699
sym::aggregate_raw_ptr => (3, 0, vec![param(1), param(2)], param(0)),

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2054,6 +2054,7 @@ symbols! {
20542054
type_changing_struct_update,
20552055
type_id,
20562056
type_id_eq,
2057+
type_id_vtable,
20572058
type_info,
20582059
type_ir,
20592060
type_ir_infer_ctxt_like,
@@ -2206,7 +2207,6 @@ symbols! {
22062207
vsreg,
22072208
vsx,
22082209
vtable_align,
2209-
vtable_for,
22102210
vtable_size,
22112211
warn,
22122212
wasip2,

library/core/src/any.rs

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@
8686
8787
#![stable(feature = "rust1", since = "1.0.0")]
8888

89-
use crate::{fmt, hash, intrinsics, ptr};
89+
use crate::intrinsics::{self, type_id_vtable};
90+
use crate::mem::transmute;
91+
use crate::mem::type_info::{TraitImpl, TypeKind};
92+
use crate::{fmt, hash, ptr};
9093

9194
///////////////////////////////////////////////////////////////////////////////
9295
// Any trait
@@ -788,6 +791,67 @@ impl TypeId {
788791
const { intrinsics::type_id::<T>() }
789792
}
790793

794+
/// Checks if the [TypeId] implements the trait. If it does it returns [TraitImpl] which can be used to build a fat pointer.
795+
/// It can only be called at compile time. `self` must be the [TypeId] of a sized type or None will be returned.
796+
///
797+
/// # Examples
798+
///
799+
/// ```
800+
/// #![feature(type_info)]
801+
/// use std::any::{TypeId};
802+
///
803+
/// pub trait Blah {}
804+
/// impl Blah for u8 {}
805+
///
806+
/// assert!(const { TypeId::of::<u8>().trait_info_of::<dyn Blah>() }.is_some());
807+
/// assert!(const { TypeId::of::<u16>().trait_info_of::<dyn Blah>() }.is_none());
808+
/// ```
809+
#[unstable(feature = "type_info", issue = "146922")]
810+
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
811+
pub const fn trait_info_of<
812+
T: ptr::Pointee<Metadata = ptr::DynMetadata<T>> + ?Sized + 'static,
813+
>(
814+
self,
815+
) -> Option<TraitImpl<T>> {
816+
// SAFETY: The vtable was obtained for `T`, so it is guaranteed to be `DynMetadata<T>`.
817+
// The intrinsic can't infer this because it is designed to work with arbitrary TypeIds.
818+
unsafe { transmute(self.trait_info_of_trait_type_id(const { TypeId::of::<T>() })) }
819+
}
820+
821+
/// Checks if the [TypeId] implements the trait of `trait_represented_by_type_id`. If it does it returns [TraitImpl] which can be used to build a fat pointer.
822+
/// It can only be called at compile time. `self` must be the [TypeId] of a sized type or None will be returned.
823+
///
824+
/// # Examples
825+
///
826+
/// ```
827+
/// #![feature(type_info)]
828+
/// use std::any::{TypeId};
829+
///
830+
/// pub trait Blah {}
831+
/// impl Blah for u8 {}
832+
///
833+
/// assert!(const { TypeId::of::<u8>().trait_info_of_trait_type_id(TypeId::of::<dyn Blah>()) }.is_some());
834+
/// assert!(const { TypeId::of::<u16>().trait_info_of_trait_type_id(TypeId::of::<dyn Blah>()) }.is_none());
835+
/// ```
836+
#[unstable(feature = "type_info", issue = "146922")]
837+
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
838+
pub const fn trait_info_of_trait_type_id(
839+
self,
840+
trait_represented_by_type_id: TypeId,
841+
) -> Option<TraitImpl<*const ()>> {
842+
if self.info().size.is_none() {
843+
return None;
844+
}
845+
846+
if matches!(trait_represented_by_type_id.info().kind, TypeKind::DynTrait(_))
847+
&& let Some(vtable) = type_id_vtable(self, trait_represented_by_type_id)
848+
{
849+
Some(TraitImpl { vtable })
850+
} else {
851+
None
852+
}
853+
}
854+
791855
fn as_u128(self) -> u128 {
792856
let mut bytes = [0; 16];
793857

@@ -948,7 +1012,8 @@ pub const fn try_as_dyn<
9481012
>(
9491013
t: &T,
9501014
) -> Option<&U> {
951-
let vtable: Option<ptr::DynMetadata<U>> = const { intrinsics::vtable_for::<T, U>() };
1015+
let vtable: Option<ptr::DynMetadata<U>> =
1016+
const { TypeId::of::<T>().trait_info_of::<U>().as_ref().map(TraitImpl::get_vtable) };
9521017
match vtable {
9531018
Some(dyn_metadata) => {
9541019
let pointer = ptr::from_raw_parts(t, dyn_metadata);
@@ -1001,7 +1066,8 @@ pub const fn try_as_dyn_mut<
10011066
>(
10021067
t: &mut T,
10031068
) -> Option<&mut U> {
1004-
let vtable: Option<ptr::DynMetadata<U>> = const { intrinsics::vtable_for::<T, U>() };
1069+
let vtable: Option<ptr::DynMetadata<U>> =
1070+
const { TypeId::of::<T>().trait_info_of::<U>().as_ref().map(TraitImpl::get_vtable) };
10051071
match vtable {
10061072
Some(dyn_metadata) => {
10071073
let pointer = ptr::from_raw_parts_mut(t, dyn_metadata);

library/core/src/intrinsics/mod.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2751,18 +2751,6 @@ pub unsafe fn vtable_size(ptr: *const ()) -> usize;
27512751
#[rustc_intrinsic]
27522752
pub unsafe fn vtable_align(ptr: *const ()) -> usize;
27532753

2754-
/// The intrinsic returns the `U` vtable for `T` if `T` can be coerced to the trait object type `U`.
2755-
///
2756-
/// # Compile-time failures
2757-
/// Determining whether `T` can be coerced to the trait object type `U` requires trait resolution by the compiler.
2758-
/// In some cases, that resolution can exceed the recursion limit,
2759-
/// and compilation will fail instead of this function returning `None`.
2760-
#[rustc_nounwind]
2761-
#[unstable(feature = "core_intrinsics", issue = "none")]
2762-
#[rustc_intrinsic]
2763-
pub const fn vtable_for<T, U: ptr::Pointee<Metadata = ptr::DynMetadata<U>> + ?Sized>()
2764-
-> Option<ptr::DynMetadata<U>>;
2765-
27662754
/// The size of a type in bytes.
27672755
///
27682756
/// Note that, unlike most intrinsics, this is safe to call;
@@ -2864,6 +2852,20 @@ pub const unsafe fn size_of_val<T: ?Sized>(ptr: *const T) -> usize;
28642852
#[rustc_intrinsic_const_stable_indirect]
28652853
pub const unsafe fn align_of_val<T: ?Sized>(ptr: *const T) -> usize;
28662854

2855+
#[rustc_intrinsic]
2856+
#[unstable(feature = "core_intrinsics", issue = "none")]
2857+
/// Check if a type represented by a `TypeId` implements a trait represented by a `TypeId`.
2858+
/// It can only be called at compile time, the backends do
2859+
/// not implement it. If it implements the trait the dyn metadata gets returned for vtable access.
2860+
pub const fn type_id_vtable(
2861+
_id: crate::any::TypeId,
2862+
_trait: crate::any::TypeId,
2863+
) -> Option<ptr::DynMetadata<*const ()>> {
2864+
panic!(
2865+
"`TypeId::trait_info_of` and `trait_info_of_trait_type_id` can only be called at compile-time"
2866+
)
2867+
}
2868+
28672869
/// Compute the type information of a concrete type.
28682870
/// It can only be called at compile time, the backends do
28692871
/// not implement it.

0 commit comments

Comments
 (0)