From 822f3e2c474365e3817fddbf876fd87834e9924f Mon Sep 17 00:00:00 2001 From: ssyram Date: Fri, 19 Sep 2025 23:55:49 +0800 Subject: [PATCH 1/6] AssocFn with vtable_receiver --- frontend/exporter/src/types/new/full_def.rs | 104 ++++++++++++++++---- 1 file changed, 85 insertions(+), 19 deletions(-) diff --git a/frontend/exporter/src/types/new/full_def.rs b/frontend/exporter/src/types/new/full_def.rs index 2b02caaaf..d179e2133 100644 --- a/frontend/exporter/src/types/new/full_def.rs +++ b/frontend/exporter/src/types/new/full_def.rs @@ -325,9 +325,9 @@ pub enum FullDefKind { associated_item: AssocItem, inline: InlineAttr, is_const: bool, - /// Whether this method will be included in the trait vtable. `false` if this is not a - /// trait method. - vtable_safe: bool, + /// The receiver type when this method is used in a vtable. `None` if this method is not + /// vtable safe. `Some(dyn_self)` if it is vtable safe. + vtable_receiver: Option, sig: PolyFnSig, body: Option, }, @@ -424,6 +424,82 @@ pub enum FullDefKind { SyntheticCoroutineBody, } +#[cfg(feature = "rustc")] +fn gen_vtable_receiver<'tcx>( + // The state that owns the method DefId + assoc_method_s: &StateWithOwner<'tcx>, + args: Option>, +) -> Option +{ + let def_id = assoc_method_s.owner_id(); + let tcx = assoc_method_s.base().tcx; + let assoc_item = tcx.associated_item(def_id); + let s = &assoc_method_s.with_owner_id(assoc_item.container_id(tcx)); + + // The args for the container + let container_args = { + let container_def_id = assoc_item.container_id(tcx); + let container_generics = tcx.generics_of(container_def_id); + args.map(|args| { + tcx.mk_args_from_iter(args.iter().take(container_generics.count())) + }) + }; + + let dyn_self: ty::Ty = match assoc_item.container { + ty::AssocItemContainer::Trait => { + get_trait_decl_dyn_self_ty(s, container_args) + }, + ty::AssocItemContainer::Impl => { + // For impl methods, compute concrete dyn_self from the impl's trait reference + let impl_def_id = assoc_item.container_id(tcx); + let Some(impl_trait_ref) = tcx.impl_trait_ref(impl_def_id) else { + // There might be inherent impl methods, which is surely not vtable safe. + return None; + }; + // Get the concrete trait reference by rebasing the impl's trait ref args onto `container_args` + let concrete_trait_ref = inst_binder(tcx, s.typing_env(), container_args, impl_trait_ref); + dyn_self_ty(tcx, s.typing_env(), concrete_trait_ref) + }, + }?; + + // Next, try to find the receiver "template" from the trait method declaration. + // It would be convenient for us to simply substitute `Self` with `dyn_self` in the receiver. + let origin_trait_method_id = match assoc_item.trait_item_def_id { + Some(id) => id, + // It is itself a trait method declaration + None => def_id, + }; + let origin_trait_method_sig = tcx.fn_sig(origin_trait_method_id); + let base_receiver_ty = origin_trait_method_sig.skip_binder().inputs().skip_binder()[0]; + + use ty::TypeFoldable; + + struct ReceiverReplacer<'tcx>(ty::TyCtxt<'tcx>, ty::Ty<'tcx>); + + impl<'tcx> ty::TypeFolder> for ReceiverReplacer<'tcx> { + fn cx(&self) -> ty::TyCtxt<'tcx> { + self.0 + } + fn fold_ty(&mut self, t: ty::Ty<'tcx>) -> ty::Ty<'tcx> { + use rustc_middle::ty::TypeSuperFoldable; + match t.kind() { + ty::Param(param) => { + if param.name == rustc_span::symbol::kw::SelfUpper { + assert!(param.index == 0); + return self.1.clone(); + } + return t.super_fold_with(self); + }, + _ => t.super_fold_with(self) + } + } + } + + let mut replacer = ReceiverReplacer(tcx, dyn_self); + + Some(base_receiver_ty.fold_with(&mut replacer).sinto(s)) +} + #[cfg(feature = "rustc")] /// Construct the `FullDefKind` for this item. /// @@ -506,7 +582,7 @@ where param_env: get_param_env(s, args), implied_predicates: get_implied_predicates(s, args), self_predicate: get_self_predicate(s, args), - dyn_self: get_dyn_self_ty(s, args), + dyn_self: get_trait_decl_dyn_self_ty(s, args).sinto(s), items: tcx .associated_items(def_id) .in_definition_order() @@ -525,7 +601,7 @@ where param_env: get_param_env(s, args), implied_predicates: get_implied_predicates(s, args), self_predicate: get_self_predicate(s, args), - dyn_self: get_dyn_self_ty(s, args), + dyn_self: get_trait_decl_dyn_self_ty(s, args).sinto(s), }, RDefKind::Impl { .. } => { use std::collections::HashMap; @@ -671,22 +747,12 @@ where }, RDefKind::AssocFn { .. } => { let item = tcx.associated_item(def_id); - let vtable_safe = match item.container { - ty::AssocItemContainer::Trait => { - rustc_trait_selection::traits::is_vtable_safe_method( - tcx, - item.container_id(tcx), - item, - ) - } - _ => false, - }; FullDefKind::AssocFn { param_env: get_param_env(s, args), associated_item: AssocItem::sfrom_instantiated(s, &item, args), inline: tcx.codegen_fn_attrs(def_id).inline.sinto(s), is_const: tcx.constness(def_id) == rustc_hir::Constness::Const, - vtable_safe, + vtable_receiver: gen_vtable_receiver(s, args), sig: get_method_sig(tcx, s.typing_env(), def_id, args).sinto(s), body: get_body(s, args), } @@ -1016,10 +1082,10 @@ fn get_self_predicate<'tcx, S: UnderOwnerState<'tcx>>( /// Generates a `dyn Trait::Ty..>` type for this trait. #[cfg(feature = "rustc")] -fn get_dyn_self_ty<'tcx, S: UnderOwnerState<'tcx>>( +fn get_trait_decl_dyn_self_ty<'tcx, S: UnderOwnerState<'tcx>>( s: &S, args: Option>, -) -> Option { +) -> Option> { let tcx = s.base().tcx; let typing_env = s.typing_env(); let def_id = s.owner_id(); @@ -1035,7 +1101,7 @@ fn get_dyn_self_ty<'tcx, S: UnderOwnerState<'tcx>>( } else { ty }; - ty.sinto(s) + ty }) } From 0b51ae954729d695a74ff5d3afdfa739da4d4e9f Mon Sep 17 00:00:00 2001 From: ssyram Date: Thu, 2 Oct 2025 11:22:31 +0800 Subject: [PATCH 2/6] vtable_receiver -> vtable_sig --- frontend/exporter/src/types/new/full_def.rs | 87 +++++++++++++-------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/frontend/exporter/src/types/new/full_def.rs b/frontend/exporter/src/types/new/full_def.rs index d179e2133..ba48b4357 100644 --- a/frontend/exporter/src/types/new/full_def.rs +++ b/frontend/exporter/src/types/new/full_def.rs @@ -325,9 +325,10 @@ pub enum FullDefKind { associated_item: AssocItem, inline: InlineAttr, is_const: bool, - /// The receiver type when this method is used in a vtable. `None` if this method is not - /// vtable safe. `Some(dyn_self)` if it is vtable safe. - vtable_receiver: Option, + /// The function signature when this method is used in a vtable. `None` if this method is not + /// vtable safe. `Some(sig)` if it is vtable safe, where `sig` is the trait method declaration's + /// signature with `Self` replaced by `dyn Trait` and associated types normalized. + dyn_sig: Option, sig: PolyFnSig, body: Option, }, @@ -425,11 +426,11 @@ pub enum FullDefKind { } #[cfg(feature = "rustc")] -fn gen_vtable_receiver<'tcx>( +fn gen_dyn_sig<'tcx>( // The state that owns the method DefId assoc_method_s: &StateWithOwner<'tcx>, args: Option>, -) -> Option +) -> Option { let def_id = assoc_method_s.owner_id(); let tcx = assoc_method_s.base().tcx; @@ -462,42 +463,62 @@ fn gen_vtable_receiver<'tcx>( }, }?; - // Next, try to find the receiver "template" from the trait method declaration. - // It would be convenient for us to simply substitute `Self` with `dyn_self` in the receiver. + // Get the original trait method declaration's signature let origin_trait_method_id = match assoc_item.trait_item_def_id { Some(id) => id, // It is itself a trait method declaration None => def_id, }; - let origin_trait_method_sig = tcx.fn_sig(origin_trait_method_id); - let base_receiver_ty = origin_trait_method_sig.skip_binder().inputs().skip_binder()[0]; - - use ty::TypeFoldable; - - struct ReceiverReplacer<'tcx>(ty::TyCtxt<'tcx>, ty::Ty<'tcx>); + + // Check if the method has its own type or const generics - if so, it's not vtable safe + // because you can't specify those generics when calling through a trait object. + // Note: lifetime generics are allowed in vtable-safe methods. + let method_generics = tcx.generics_of(origin_trait_method_id); + + // Check if the method has its own type or const parameters (lifetimes are OK) + let has_own_type_or_const_params = method_generics.own_params.iter().any(|param| { + matches!( + param.kind, + ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } + ) + }); + + if has_own_type_or_const_params { + return None; + } + + let origin_trait_method_sig_binder = tcx.fn_sig(origin_trait_method_id); + + // Extract the trait reference from dyn_self + // dyn_self is of form `dyn Trait`, we need to extract the trait args + match dyn_self.kind() { + ty::Dynamic(preds, _, _) => { + // Find the principal trait predicate + for pred in preds.iter() { + if let ty::ExistentialPredicate::Trait(trait_ref) = pred.skip_binder() { + // Build full args: dyn_self + trait args + // Note: trait_ref.args doesn't include Self (it's existential), so we prepend dyn_self + let mut full_args = vec![ty::GenericArg::from(dyn_self)]; + full_args.extend(trait_ref.args.iter()); + + let subst_args = tcx.mk_args(&full_args); + + // Instantiate the signature with the substitution args + let origin_trait_method_sig = origin_trait_method_sig_binder.instantiate(tcx, subst_args); + + // Normalize the signature to resolve associated types + let normalized_sig = normalize(tcx, s.typing_env(), origin_trait_method_sig); - impl<'tcx> ty::TypeFolder> for ReceiverReplacer<'tcx> { - fn cx(&self) -> ty::TyCtxt<'tcx> { - self.0 - } - fn fold_ty(&mut self, t: ty::Ty<'tcx>) -> ty::Ty<'tcx> { - use rustc_middle::ty::TypeSuperFoldable; - match t.kind() { - ty::Param(param) => { - if param.name == rustc_span::symbol::kw::SelfUpper { - assert!(param.index == 0); - return self.1.clone(); - } - return t.super_fold_with(self); - }, - _ => t.super_fold_with(self) + return Some(normalized_sig.sinto(s)); + } } + None + } + _ => { + // If it's not a dyn trait, something went wrong + panic!("Unexpected dyn_self: {:?}", dyn_self); } } - - let mut replacer = ReceiverReplacer(tcx, dyn_self); - - Some(base_receiver_ty.fold_with(&mut replacer).sinto(s)) } #[cfg(feature = "rustc")] @@ -752,7 +773,7 @@ where associated_item: AssocItem::sfrom_instantiated(s, &item, args), inline: tcx.codegen_fn_attrs(def_id).inline.sinto(s), is_const: tcx.constness(def_id) == rustc_hir::Constness::Const, - vtable_receiver: gen_vtable_receiver(s, args), + dyn_sig: gen_dyn_sig(s, args), sig: get_method_sig(tcx, s.typing_env(), def_id, args).sinto(s), body: get_body(s, args), } From 8616f553019c37750dbaec91a064960a332e1891 Mon Sep 17 00:00:00 2001 From: ssyram Date: Thu, 2 Oct 2025 11:32:32 +0800 Subject: [PATCH 3/6] use truncate_to --- frontend/exporter/src/types/new/full_def.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/exporter/src/types/new/full_def.rs b/frontend/exporter/src/types/new/full_def.rs index ba48b4357..4e59fc66b 100644 --- a/frontend/exporter/src/types/new/full_def.rs +++ b/frontend/exporter/src/types/new/full_def.rs @@ -441,9 +441,7 @@ fn gen_dyn_sig<'tcx>( let container_args = { let container_def_id = assoc_item.container_id(tcx); let container_generics = tcx.generics_of(container_def_id); - args.map(|args| { - tcx.mk_args_from_iter(args.iter().take(container_generics.count())) - }) + args.map(|args| args.truncate_to(tcx, container_generics)) }; let dyn_self: ty::Ty = match assoc_item.container { From ca11fbb663de7ed60e8c0fda3b37cb87079df443 Mon Sep 17 00:00:00 2001 From: ssyram Date: Thu, 2 Oct 2025 18:28:08 +0800 Subject: [PATCH 4/6] use `is_vtable_safe_method` --- frontend/exporter/src/types/new/full_def.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/frontend/exporter/src/types/new/full_def.rs b/frontend/exporter/src/types/new/full_def.rs index 4e59fc66b..c09f0adba 100644 --- a/frontend/exporter/src/types/new/full_def.rs +++ b/frontend/exporter/src/types/new/full_def.rs @@ -468,20 +468,8 @@ fn gen_dyn_sig<'tcx>( None => def_id, }; - // Check if the method has its own type or const generics - if so, it's not vtable safe - // because you can't specify those generics when calling through a trait object. - // Note: lifetime generics are allowed in vtable-safe methods. - let method_generics = tcx.generics_of(origin_trait_method_id); - - // Check if the method has its own type or const parameters (lifetimes are OK) - let has_own_type_or_const_params = method_generics.own_params.iter().any(|param| { - matches!( - param.kind, - ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } - ) - }); - - if has_own_type_or_const_params { + let trait_id = tcx.trait_of_item(origin_trait_method_id).unwrap(); + if !rustc_trait_selection::traits::is_vtable_safe_method(tcx, trait_id, assoc_item) { return None; } From 0ad425c9824a79b784f5df4a9fb86d7203187ed2 Mon Sep 17 00:00:00 2001 From: ssyram Date: Thu, 2 Oct 2025 18:34:30 +0800 Subject: [PATCH 5/6] comments as suggested --- frontend/exporter/src/types/new/full_def.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/exporter/src/types/new/full_def.rs b/frontend/exporter/src/types/new/full_def.rs index c09f0adba..2be4b9030 100644 --- a/frontend/exporter/src/types/new/full_def.rs +++ b/frontend/exporter/src/types/new/full_def.rs @@ -481,6 +481,8 @@ fn gen_dyn_sig<'tcx>( ty::Dynamic(preds, _, _) => { // Find the principal trait predicate for pred in preds.iter() { + // Safe to use `skip_binder` because we know the predicate we built in dyn_self_ty + // has no bound vars if let ty::ExistentialPredicate::Trait(trait_ref) = pred.skip_binder() { // Build full args: dyn_self + trait args // Note: trait_ref.args doesn't include Self (it's existential), so we prepend dyn_self From 589dad04483bb73434911953bfbaefd668fa03c2 Mon Sep 17 00:00:00 2001 From: ssyram Date: Thu, 2 Oct 2025 18:35:19 +0800 Subject: [PATCH 6/6] panic as suggested --- frontend/exporter/src/types/new/full_def.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/exporter/src/types/new/full_def.rs b/frontend/exporter/src/types/new/full_def.rs index 2be4b9030..50c86245c 100644 --- a/frontend/exporter/src/types/new/full_def.rs +++ b/frontend/exporter/src/types/new/full_def.rs @@ -500,7 +500,7 @@ fn gen_dyn_sig<'tcx>( return Some(normalized_sig.sinto(s)); } } - None + panic!("No principal trait found in dyn_self: {:?}", dyn_self); } _ => { // If it's not a dyn trait, something went wrong