From 31b74d23352cdf6177e27abb0e1f5dcde1487cca Mon Sep 17 00:00:00 2001 From: Katt <51190960+coolcatcoder@users.noreply.github.com> Date: Sat, 17 Jan 2026 12:19:00 +0100 Subject: [PATCH 1/4] Implements funtionality of type_id_implements_trait and its ilk --- .../src/const_eval/machine.rs | 54 +++++++++++++++++-- .../rustc_hir_analysis/src/check/intrinsic.rs | 14 +++++ compiler/rustc_span/src/symbol.rs | 2 + library/core/src/intrinsics/mod.rs | 18 +++++++ library/core/src/mem/type_info.rs | 27 +++++++++- 5 files changed, 111 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 719187b990122..7700474d1687b 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -5,16 +5,19 @@ use std::hash::Hash; use rustc_abi::{Align, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_middle::mir::AssertMessage; use rustc_middle::mir::interpret::{Pointer, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement}; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::{bug, mir}; +use rustc_middle::ty::{self, PolyExistentialPredicate, Ty, TyCtxt}; +use rustc_middle::{bug, mir, span_bug}; use rustc_span::{Span, Symbol, sym}; use rustc_target::callconv::FnAbi; +use rustc_trait_selection::traits::ObligationCtxt; use tracing::debug; use super::error::*; @@ -586,6 +589,51 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } } + sym::type_id_is_trait => { + let kind = ecx.read_type_id(&args[0])?.kind(); + let is_trait = matches!(kind, ty::Dynamic(..)); + ecx.write_scalar(Scalar::from_bool(is_trait), dest)?; + } + + sym::type_id_implements_trait => { + let type_from_type_id = ecx.read_type_id(&args[0])?; + let trait_from_type_id = ecx.read_type_id(&args[1])?; + + // TODO: Not quite sure how best to get this into scope... I'm not actually sure what it is meant to do. Please inform me. + // ensure_monomorphic_enough(tcx, result_ty)?; + + let ty::Dynamic(predicates, _) = trait_from_type_id.kind() else { + span_bug!( + ecx.find_closest_untracked_caller_location(), + "Invalid type provided to type_id_implements_trait. The second parameter must represent a dyn Trait, instead you gave us {trait_from_type_id}." + ); + }; + + let (infcx, param_env) = + ecx.tcx.infer_ctxt().build_with_typing_env(ecx.typing_env()); + + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligations(predicates.iter().map( + |predicate: PolyExistentialPredicate<'_>| { + let predicate = predicate.with_self_ty(ecx.tcx.tcx, type_from_type_id); + // Lifetimes can only be 'static because of the bound on T + let predicate = ty::fold_regions(ecx.tcx.tcx, predicate, |r, _| { + if r == ecx.tcx.tcx.lifetimes.re_erased { + ecx.tcx.tcx.lifetimes.re_static + } else { + r + } + }); + Obligation::new(ecx.tcx.tcx, ObligationCause::dummy(), param_env, predicate) + }, + )); + let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().is_empty(); + // Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default" + let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty(); + + ecx.write_scalar(Scalar::from_bool(type_impls_trait && regions_are_valid), dest)?; + } + sym::type_of => { let ty = ecx.read_type_id(&args[0])?; ecx.write_type_info(ty, dest)?; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index d3d167f6e2544..33bda7f9b66a1 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -213,6 +213,8 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::truncf128 | sym::type_id | sym::type_id_eq + | sym::type_id_implements_trait + | sym::type_id_is_trait | sym::type_name | sym::type_of | sym::ub_checks @@ -321,6 +323,18 @@ pub(crate) fn check_intrinsic_type( let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); (0, 0, vec![type_id, type_id], tcx.types.bool) } + sym::type_id_implements_trait => ( + 0, + 0, + vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); 2], + tcx.types.bool, + ), + sym::type_id_is_trait => ( + 0, + 0, + vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap()], + tcx.types.bool, + ), sym::type_of => ( 0, 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f0576002354dc..b40319508f7dc 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2331,6 +2331,8 @@ symbols! { type_const, type_id, type_id_eq, + type_id_implements_trait, + type_id_is_trait, type_info, type_ir, type_ir_infer_ctxt_like, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 8d112b4c5d187..b5b4741090d3a 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2846,6 +2846,24 @@ pub const unsafe fn size_of_val(ptr: *const T) -> usize; #[rustc_intrinsic_const_stable_indirect] pub const unsafe fn align_of_val(ptr: *const T) -> usize; +/// Check if a type represented by a `TypeId` is a dyn Trait. +/// It can only be called at compile time, the backends do +/// not implement it. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +pub const fn type_id_is_trait(_trait: crate::any::TypeId) -> bool { + panic!("`TypeId::implements_trait_represented_by_type_id` can only be called at compile-time") +} + +/// Check if a type represented by a `TypeId` implements a trait represented by a `TypeId`. +/// It can only be called at compile time, the backends do +/// not implement it. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +pub const fn type_id_implements_trait(_id: crate::any::TypeId, _trait: crate::any::TypeId) -> bool { + panic!("`TypeId::implements_trait` can only be called at compile-time") +} + /// Compute the type information of a concrete type. /// It can only be called at compile time, the backends do /// not implement it. diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index ee647ef55840a..e6e76317fdf04 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -2,7 +2,8 @@ //! runtime or const-eval processable way. use crate::any::TypeId; -use crate::intrinsics::type_of; +use crate::intrinsics::{type_id_implements_trait, type_id_is_trait, type_of}; +use crate::ptr; /// Compile-time type information. #[derive(Debug)] @@ -24,6 +25,30 @@ impl TypeId { pub const fn info(self) -> Type { type_of(self) } + + /// Checks if the type represented by the `TypeId` implements the trait. + /// It can only be called at compile time. + pub const fn implements_trait< + T: ptr::Pointee> + ?Sized + 'static, + >( + self, + ) -> bool { + type_id_implements_trait(self, TypeId::of::()) + } + + /// Checks if the type represented by the `TypeId` implements the trait represented by the secondary `TypeId`. + /// Returns `None` if the `trait_represented_by_type_id` is not a trait represented by type id. + /// It can only be called at compile time. + pub const fn implements_trait_represented_by_type_id( + self, + trait_represented_by_type_id: Self, + ) -> Option { + if type_id_is_trait(trait_represented_by_type_id) { + Some(type_id_implements_trait(self, trait_represented_by_type_id)) + } else { + None + } + } } impl Type { From 65f28a7860777d4ef4a24c440002de2bc7335c00 Mon Sep 17 00:00:00 2001 From: Katt <51190960+coolcatcoder@users.noreply.github.com> Date: Sat, 17 Jan 2026 12:19:09 +0100 Subject: [PATCH 2/4] De-duplicate! --- .../src/const_eval/machine.rs | 38 ++++--------------- .../src/interpret/intrinsics.rs | 25 ++---------- .../rustc_const_eval/src/interpret/mod.rs | 2 +- .../rustc_const_eval/src/interpret/util.rs | 37 +++++++++++++++++- 4 files changed, 46 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 7700474d1687b..49ab9d75253d6 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -5,24 +5,22 @@ use std::hash::Hash; use rustc_abi::{Align, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; -use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem}; -use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_middle::mir::AssertMessage; use rustc_middle::mir::interpret::{Pointer, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement}; -use rustc_middle::ty::{self, PolyExistentialPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::{Span, Symbol, sym}; use rustc_target::callconv::FnAbi; -use rustc_trait_selection::traits::ObligationCtxt; use tracing::debug; use super::error::*; use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; +use crate::interpret::util::{ensure_monomorphic_enough, type_implements_predicates}; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, RangeSet, Scalar, @@ -599,9 +597,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { let type_from_type_id = ecx.read_type_id(&args[0])?; let trait_from_type_id = ecx.read_type_id(&args[1])?; - // TODO: Not quite sure how best to get this into scope... I'm not actually sure what it is meant to do. Please inform me. - // ensure_monomorphic_enough(tcx, result_ty)?; - + ensure_monomorphic_enough(ecx.tcx.tcx, trait_from_type_id)?; let ty::Dynamic(predicates, _) = trait_from_type_id.kind() else { span_bug!( ecx.find_closest_untracked_caller_location(), @@ -609,29 +605,9 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ); }; - let (infcx, param_env) = - ecx.tcx.infer_ctxt().build_with_typing_env(ecx.typing_env()); - - let ocx = ObligationCtxt::new(&infcx); - ocx.register_obligations(predicates.iter().map( - |predicate: PolyExistentialPredicate<'_>| { - let predicate = predicate.with_self_ty(ecx.tcx.tcx, type_from_type_id); - // Lifetimes can only be 'static because of the bound on T - let predicate = ty::fold_regions(ecx.tcx.tcx, predicate, |r, _| { - if r == ecx.tcx.tcx.lifetimes.re_erased { - ecx.tcx.tcx.lifetimes.re_static - } else { - r - } - }); - Obligation::new(ecx.tcx.tcx, ObligationCause::dummy(), param_env, predicate) - }, - )); - let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().is_empty(); - // Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default" - let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty(); - - ecx.write_scalar(Scalar::from_bool(type_impls_trait && regions_are_valid), dest)?; + let implements = type_implements_predicates(ecx, type_from_type_id, predicates)?; + + ecx.write_scalar(Scalar::from_bool(implements), dest)?; } sym::type_of => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index fe1dd1b6eb352..75765e28a969e 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -8,15 +8,12 @@ use std::assert_matches::assert_matches; use rustc_abi::{FIRST_VARIANT, FieldIdx, HasDataLayout, Size, VariantIdx}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_hir::def_id::CRATE_DEF_ID; -use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{FloatTy, Ty, TyCtxt}; use rustc_middle::{bug, span_bug, ty}; use rustc_span::{Symbol, sym}; -use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; use tracing::trace; use super::memory::MemoryKind; @@ -28,6 +25,7 @@ use super::{ }; use crate::fluent_generated as fluent; use crate::interpret::Writeable; +use crate::interpret::util::type_implements_predicates; #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum MulAddType { @@ -227,7 +225,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let tp_ty = instance.args.type_at(0); let result_ty = instance.args.type_at(1); - ensure_monomorphic_enough(tcx, tp_ty)?; ensure_monomorphic_enough(tcx, result_ty)?; let ty::Dynamic(preds, _) = result_ty.kind() else { span_bug!( @@ -236,23 +233,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ); }; - let (infcx, param_env) = - self.tcx.infer_ctxt().build_with_typing_env(self.typing_env); - - let ocx = ObligationCtxt::new(&infcx); - ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| { - let pred = pred.with_self_ty(tcx, tp_ty); - // Lifetimes can only be 'static because of the bound on T - let pred = ty::fold_regions(tcx, pred, |r, _| { - if r == tcx.lifetimes.re_erased { tcx.lifetimes.re_static } else { r } - }); - Obligation::new(tcx, ObligationCause::dummy(), param_env, pred) - })); - let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().is_empty(); - // Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default" - let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty(); - - if regions_are_valid && type_impls_trait { + if type_implements_predicates(self, tp_ty, preds)? { let vtable_ptr = self.get_vtable_ptr(tp_ty, preds)?; // Writing a non-null pointer into an `Option` will automatically make it `Some`. self.write_pointer(vtable_ptr, dest)?; diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 2f365ec77b33e..b3f7ec6d359a0 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -15,7 +15,7 @@ mod projection; mod stack; mod step; mod traits; -mod util; +pub mod util; mod validity; mod visitor; diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 1e18a22be81c2..deba6ba4c3419 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,12 +1,45 @@ -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; +use rustc_infer::infer::TyCtxtInferExt; +#[allow(rustc::direct_use_of_rustc_type_ir)] +use rustc_infer::infer::canonical::ir::Interner; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_middle::mir; use rustc_middle::mir::interpret::{AllocInit, Allocation, GlobalAlloc, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::{PolyExistentialPredicate, Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_trait_selection::traits::ObligationCtxt; use tracing::debug; use super::{InterpCx, MPlaceTy, MemoryKind, interp_ok, throw_inval}; use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult}; +use crate::interpret::Machine; + +/// Checks if a type implements predicates. +/// Calls `ensure_monomorphic_enough` on `ty` for you. +pub(crate) fn type_implements_predicates<'tcx, M: Machine<'tcx>>( + ecx: &mut InterpCx<'tcx, M>, + ty: Ty<'tcx>, + preds: as Interner>::BoundExistentialPredicates, +) -> InterpResult<'tcx, bool> { + ensure_monomorphic_enough(ecx.tcx.tcx, ty)?; + + let (infcx, param_env) = ecx.tcx.infer_ctxt().build_with_typing_env(ecx.typing_env); + + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| { + let pred = pred.with_self_ty(ecx.tcx.tcx, ty); + // Lifetimes can only be 'static because of the bound on T + let pred = rustc_middle::ty::fold_regions(ecx.tcx.tcx, pred, |r, _| { + if r == ecx.tcx.tcx.lifetimes.re_erased { ecx.tcx.tcx.lifetimes.re_static } else { r } + }); + Obligation::new(ecx.tcx.tcx, ObligationCause::dummy(), param_env, pred) + })); + let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().is_empty(); + // Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default" + let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty(); + + interp_ok(regions_are_valid && type_impls_trait) +} /// Checks whether a type contains generic parameters which must be instantiated. /// From 98f244de8bd546ac09a3ecc91aa5fa26dd7c852e Mon Sep 17 00:00:00 2001 From: Katt <51190960+coolcatcoder@users.noreply.github.com> Date: Sat, 17 Jan 2026 12:19:19 +0100 Subject: [PATCH 3/4] Add test. --- library/coretests/tests/mem/type_info.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 7df632981ce1b..0ebd011e4d79d 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -125,3 +125,16 @@ fn test_references() { _ => unreachable!(), } } + +#[test] +fn test_implements_trait() { + struct Garlic; + trait Blah {} + impl Blah for Garlic {} + + const { + assert!(TypeId::of::().implements_trait::()); + assert!(TypeId::of::().implements_trait::()); + assert!(!TypeId::of::<*const Box>().implements_trait::()); + } +} From da054c731398ed9d3addd31f969aa85c52e83435 Mon Sep 17 00:00:00 2001 From: Katt <51190960+coolcatcoder@users.noreply.github.com> Date: Sat, 17 Jan 2026 12:19:26 +0100 Subject: [PATCH 4/4] Fmease fixes. --- compiler/rustc_const_eval/src/interpret/util.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index deba6ba4c3419..d204efe419fa4 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,12 +1,10 @@ use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; -#[allow(rustc::direct_use_of_rustc_type_ir)] -use rustc_infer::infer::canonical::ir::Interner; use rustc_infer::traits::{Obligation, ObligationCause}; -use rustc_middle::mir; use rustc_middle::mir::interpret::{AllocInit, Allocation, GlobalAlloc, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{PolyExistentialPredicate, Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_middle::{mir, ty}; use rustc_trait_selection::traits::ObligationCtxt; use tracing::debug; @@ -19,7 +17,7 @@ use crate::interpret::Machine; pub(crate) fn type_implements_predicates<'tcx, M: Machine<'tcx>>( ecx: &mut InterpCx<'tcx, M>, ty: Ty<'tcx>, - preds: as Interner>::BoundExistentialPredicates, + preds: &ty::List>, ) -> InterpResult<'tcx, bool> { ensure_monomorphic_enough(ecx.tcx.tcx, ty)?;