Skip to content

Commit 01627b7

Browse files
author
jasper3108
committed
Support getting TypeId's Trait and vtable
1 parent ef70767 commit 01627b7

13 files changed

Lines changed: 295 additions & 6 deletions

File tree

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 18 additions & 1 deletion
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;
@@ -21,6 +21,7 @@ use tracing::debug;
2121

2222
use super::error::*;
2323
use crate::errors::{LongRunning, LongRunningWarn};
24+
use crate::interpret::util::type_implements_dyn_trait;
2425
use crate::interpret::{
2526
self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
2627
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar,
@@ -601,6 +602,22 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
601602
}
602603
}
603604

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

compiler/rustc_const_eval/src/interpret/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ mod projection;
1515
mod stack;
1616
mod step;
1717
mod traits;
18-
mod util;
18+
pub(crate) mod util;
1919
mod validity;
2020
mod visitor;
2121

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 & 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::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
@@ -323,6 +324,25 @@ pub(crate) fn check_intrinsic_type(
323324
let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap();
324325
(0, 0, vec![type_id, type_id], tcx.types.bool)
325326
}
327+
sym::type_id_vtable => {
328+
let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span);
329+
let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata);
330+
let dyn_metadata_args =
331+
tcx.mk_args(&[Ty::new_ptr(tcx, tcx.types.unit, ty::Mutability::Not).into()]);
332+
let dyn_ty = Ty::new_adt(tcx, dyn_metadata_adt_ref, dyn_metadata_args);
333+
334+
let option_did = tcx.require_lang_item(LangItem::Option, span);
335+
let option_adt_ref = tcx.adt_def(option_did);
336+
let option_args = tcx.mk_args(&[dyn_ty.into()]);
337+
let ret_ty = Ty::new_adt(tcx, option_adt_ref, option_args);
338+
339+
(
340+
0,
341+
0,
342+
vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); 2],
343+
ret_ty,
344+
)
345+
}
326346
sym::type_of => (
327347
0,
328348
0,

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2358,6 +2358,7 @@ symbols! {
23582358
type_changing_struct_update,
23592359
type_id,
23602360
type_id_eq,
2361+
type_id_vtable,
23612362
type_info,
23622363
type_ir,
23632364
type_ir_infer_ctxt_like,

library/core/src/any.rs

Lines changed: 65 additions & 1 deletion
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

library/core/src/intrinsics/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2864,6 +2864,20 @@ pub const unsafe fn size_of_val<T: ?Sized>(ptr: *const T) -> usize;
28642864
#[rustc_intrinsic_const_stable_indirect]
28652865
pub const unsafe fn align_of_val<T: ?Sized>(ptr: *const T) -> usize;
28662866

2867+
#[rustc_intrinsic]
2868+
#[unstable(feature = "core_intrinsics", issue = "none")]
2869+
/// Check if a type represented by a `TypeId` implements a trait represented by a `TypeId`.
2870+
/// It can only be called at compile time, the backends do
2871+
/// not implement it. If it implements the trait the dyn metadata gets returned for vtable access.
2872+
pub const fn type_id_vtable(
2873+
_id: crate::any::TypeId,
2874+
_trait: crate::any::TypeId,
2875+
) -> Option<ptr::DynMetadata<*const ()>> {
2876+
panic!(
2877+
"`TypeId::trait_info_of` and `trait_info_of_trait_type_id` can only be called at compile-time"
2878+
)
2879+
}
2880+
28672881
/// Compute the type information of a concrete type.
28682882
/// It can only be called at compile time, the backends do
28692883
/// not implement it.

library/core/src/mem/type_info.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
44
use crate::any::TypeId;
55
use crate::intrinsics::{type_id, type_of};
6+
use crate::marker::PointeeSized;
7+
use crate::ptr::DynMetadata;
68

79
/// Compile-time type information.
810
#[derive(Debug)]
@@ -16,6 +18,19 @@ pub struct Type {
1618
pub size: Option<usize>,
1719
}
1820

21+
/// Info of a trait implementation, you can retrieve the vtable with [Self::get_vtable]
22+
#[derive(Debug, PartialEq, Eq)]
23+
pub struct TraitImpl<T: PointeeSized> {
24+
pub(crate) vtable: DynMetadata<T>,
25+
}
26+
27+
impl<T: PointeeSized> TraitImpl<T> {
28+
/// Gets the raw vtable for type reflection mapping
29+
pub fn get_vtable(&self) -> DynMetadata<T> {
30+
self.vtable
31+
}
32+
}
33+
1934
impl TypeId {
2035
/// Compute the type information of a concrete type.
2136
/// It can only be called at compile time.

library/coretests/tests/mem.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod fn_ptr;
2+
mod trait_info_of;
23
mod type_info;
34

45
use core::mem::*;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use std::any::TypeId;
2+
use std::ptr::DynMetadata;
3+
4+
struct Garlic(i32);
5+
trait Blah {
6+
fn get_truth(&self) -> i32;
7+
}
8+
impl Blah for Garlic {
9+
fn get_truth(&self) -> i32 {
10+
self.0 * 21
11+
}
12+
}
13+
14+
#[test]
15+
fn test_implements_trait() {
16+
const {
17+
assert!(TypeId::of::<Garlic>().trait_info_of::<dyn Blah>().is_some());
18+
assert!(TypeId::of::<Garlic>().trait_info_of::<dyn Blah + Send>().is_some());
19+
assert!(TypeId::of::<*const Box<Garlic>>().trait_info_of::<dyn Sync>().is_none());
20+
assert!(TypeId::of::<u8>().trait_info_of_trait_type_id(TypeId::of::<dyn Blah>()).is_none());
21+
}
22+
}
23+
24+
#[test]
25+
fn test_dyn_creation() {
26+
let garlic = Garlic(2);
27+
unsafe {
28+
assert_eq!(
29+
std::ptr::from_raw_parts::<dyn Blah>(
30+
&raw const garlic,
31+
const { TypeId::of::<Garlic>().trait_info_of::<dyn Blah>() }.unwrap().get_vtable()
32+
)
33+
.as_ref()
34+
.unwrap()
35+
.get_truth(),
36+
42
37+
);
38+
}
39+
40+
assert_eq!(
41+
const {
42+
TypeId::of::<Garlic>()
43+
.trait_info_of_trait_type_id(TypeId::of::<dyn Blah>())
44+
.unwrap()
45+
}.get_vtable(),
46+
unsafe {
47+
crate::mem::transmute::<_, DynMetadata<*const ()>>(
48+
const {
49+
TypeId::of::<Garlic>().trait_info_of::<dyn Blah>()
50+
}.unwrap().get_vtable(),
51+
)
52+
}
53+
);
54+
}
55+
56+
#[test]
57+
fn test_incorrect_use() {
58+
assert_eq!(
59+
const { TypeId::of::<i32>().trait_info_of_trait_type_id(TypeId::of::<u32>()) },
60+
None
61+
);
62+
}
63+
64+
trait DstTrait {}
65+
impl DstTrait for [i32] {}
66+
67+
#[test]
68+
fn dst_ice() {
69+
assert!(const { TypeId::of::<[i32]>().trait_info_of::<dyn DstTrait>() }.is_none());
70+
}

0 commit comments

Comments
 (0)