diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 7c6ab642b2736..123cd90937620 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -1635,6 +1635,10 @@ rustc_queries! { query is_dyn_compatible(trait_id: DefId) -> bool { desc { "checking if trait `{}` is dyn-compatible", tcx.def_path_str(trait_id) } } + query is_dyn_incompatible_final_assoc_fn_query(def_id: DefId) -> bool { + desc { "checking if final assoc fn `{}` is dyn-incompatible", tcx.def_path_str(def_id) } + feedable + } /// Gets the ParameterEnvironment for a given item; this environment /// will be in "user-facing" mode, meaning that it is suitable for diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index cabe8514ae4be..7e5e7937f1535 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -333,4 +333,17 @@ impl<'tcx> TyCtxt<'tcx> { let parent_def_id = self.parent(fn_def_id); &self.associated_types_for_impl_traits_in_trait_or_impl(parent_def_id)[&fn_def_id] } + + /// Returns true if the given final associated function violates the dyn- + /// compatibility and has no `Self: Sized` requirement. + /// + /// Such final associated functions will be excluded from the trait's vtable + /// and could be called from a trait object directly. + pub fn is_dyn_incompatible_final_assoc_fn(self, def_id: DefId) -> bool { + matches!(self.def_kind(def_id), DefKind::AssocFn) + && matches!(self.defaultness(def_id), hir::Defaultness::Final) + // Any item that has a `Self: Sized` requisite is non-dispatchable. + && !self.generics_require_sized_self(def_id) + && self.is_dyn_incompatible_final_assoc_fn_query(def_id) + } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 03193400f88e9..5d8b9d26ffd52 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -720,6 +720,13 @@ impl<'tcx> TyCtxt<'tcx> { } TyCtxtFeed { tcx: self, key }.visibility(vis.to_def_id()) } + + pub fn feed_is_dyn_incompatible_final_assoc_fn(self, key: DefId, value: bool) { + debug_assert_eq!(self.def_kind(key), DefKind::AssocFn); + debug_assert!(self.defaultness(key).is_final()); + + TyCtxtFeed { tcx: self, key }.is_dyn_incompatible_final_assoc_fn_query(value) + } } impl<'tcx, KEY: Copy> TyCtxtFeed<'tcx, KEY> { diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index d1522ec89a016..1911bde7e77c0 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -325,11 +325,6 @@ pub fn dyn_compatibility_violations_for_assoc_item( trait_def_id: DefId, item: ty::AssocItem, ) -> Vec { - // `final` assoc functions don't prevent a trait from being dyn-compatible - if tcx.defaultness(item.def_id).is_final() { - return Vec::new(); - } - // Any item that has a `Self: Sized` requisite is otherwise exempt from the regulations. if tcx.generics_require_sized_self(item.def_id) { return Vec::new(); @@ -373,7 +368,7 @@ pub fn dyn_compatibility_violations_for_assoc_item( .collect() } ty::AssocKind::Fn { name, .. } => { - virtual_call_violations_for_method(tcx, trait_def_id, item) + let violations: Vec<_> = virtual_call_violations_for_method(tcx, trait_def_id, item) .into_iter() .map(|v| { let node = tcx.hir_get_if_local(item.def_id); @@ -390,7 +385,14 @@ pub fn dyn_compatibility_violations_for_assoc_item( DynCompatibilityViolation::Method(name, v, span) }) - .collect() + .collect(); + + if tcx.defaultness(item.def_id).is_final() { + tcx.feed_is_dyn_incompatible_final_assoc_fn(item.def_id, !violations.is_empty()); + Vec::new() + } else { + violations + } } ty::AssocKind::Type { data } => { if !tcx.generics_of(item.def_id).is_own_empty() diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index e3e65150a6d6c..35e3c0391bd06 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -212,8 +212,8 @@ fn own_existential_vtable_entries_iter( debug!("own_existential_vtable_entry: trait_method={:?}", trait_method); let def_id = trait_method.def_id; - // Final methods should not be included in the vtable. - if trait_method.defaultness(tcx).is_final() { + // Final methods should not be included in the vtable if they are not dyn-compatible. + if tcx.defaultness(def_id).is_final() && tcx.is_dyn_incompatible_final_assoc_fn(def_id) { return None; } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index c6e8ae49e4c9d..e94a370e38f40 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -239,8 +239,10 @@ fn resolve_associated_item<'tcx>( traits::ImplSource::Builtin(BuiltinImplSource::Object(_), _) => { let trait_ref = ty::TraitRef::from_assoc(tcx, trait_id, rcvr_args); - // `final` methods should be called directly. - if tcx.defaultness(trait_item_id).is_final() { + // `final` methods should be called directly if they are not dyn-compatible. + if tcx.defaultness(trait_item_id).is_final() + && tcx.is_dyn_incompatible_final_assoc_fn(trait_item_id) + { return Ok(Some(ty::Instance::new_raw(trait_item_id, rcvr_args))); } diff --git a/tests/ui/traits/final/dyn-compatible-final-methods.rs b/tests/ui/traits/final/dyn-compatible-final-methods.rs new file mode 100644 index 0000000000000..8f127da82db29 --- /dev/null +++ b/tests/ui/traits/final/dyn-compatible-final-methods.rs @@ -0,0 +1,26 @@ +//@ run-pass + +#![feature(final_associated_functions)] + +trait Tr +where + Self: 'static, +{ + final fn foo(&self) -> std::any::TypeId { + std::any::TypeId::of::() + } +} + +struct Foo; +impl Tr for Foo {} + +struct Bar; +impl Tr for Bar {} + +fn foo(t: &dyn Tr) -> std::any::TypeId { + t.foo() +} + +fn main() { + assert_ne!(foo(&Foo), foo(&Bar)); +}