diff --git a/frontend/exporter/src/types/new/full_def.rs b/frontend/exporter/src/types/new/full_def.rs index 2b02caaaf..50c86245c 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, - /// Whether this method will be included in the trait vtable. `false` if this is not a - /// trait method. - vtable_safe: bool, + /// 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, }, @@ -424,6 +425,90 @@ pub enum FullDefKind { SyntheticCoroutineBody, } +#[cfg(feature = "rustc")] +fn gen_dyn_sig<'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| args.truncate_to(tcx, container_generics)) + }; + + 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) + }, + }?; + + // 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 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; + } + + 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() { + // 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 + 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); + + return Some(normalized_sig.sinto(s)); + } + } + panic!("No principal trait found in dyn_self: {:?}", dyn_self); + } + _ => { + // If it's not a dyn trait, something went wrong + panic!("Unexpected dyn_self: {:?}", dyn_self); + } + } +} + #[cfg(feature = "rustc")] /// Construct the `FullDefKind` for this item. /// @@ -506,7 +591,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 +610,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 +756,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, + 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), } @@ -1016,10 +1091,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 +1110,7 @@ fn get_dyn_self_ty<'tcx, S: UnderOwnerState<'tcx>>( } else { ty }; - ty.sinto(s) + ty }) }