Skip to content

Commit 2daa066

Browse files
committed
Keep dyn-compatible final methods in the vtable
1 parent 9e0a42c commit 2daa066

File tree

7 files changed

+65
-11
lines changed

7 files changed

+65
-11
lines changed

compiler/rustc_middle/src/queries.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,10 @@ rustc_queries! {
16311631
query is_dyn_compatible(trait_id: DefId) -> bool {
16321632
desc { "checking if trait `{}` is dyn-compatible", tcx.def_path_str(trait_id) }
16331633
}
1634+
query is_dyn_incompatible_final_assoc_fn_query(def_id: DefId) -> bool {
1635+
desc { "checking if final assoc fn `{}` is dyn-incompatible", tcx.def_path_str(def_id) }
1636+
feedable
1637+
}
16341638

16351639
/// Gets the ParameterEnvironment for a given item; this environment
16361640
/// will be in "user-facing" mode, meaning that it is suitable for

compiler/rustc_middle/src/ty/assoc.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,17 @@ impl<'tcx> TyCtxt<'tcx> {
333333
let parent_def_id = self.parent(fn_def_id);
334334
&self.associated_types_for_impl_traits_in_trait_or_impl(parent_def_id)[&fn_def_id]
335335
}
336+
337+
/// Returns true if the given final associated function violates the dyn-
338+
/// compatibility and has no `Self: Sized` requirement.
339+
///
340+
/// Such final associated functions will be excluded from the trait's vtable
341+
/// and could be called from a trait object directly.
342+
pub fn is_dyn_incompatible_final_assoc_fn(self, def_id: DefId) -> bool {
343+
matches!(self.def_kind(def_id), DefKind::AssocFn)
344+
&& matches!(self.defaultness(def_id), hir::Defaultness::Final)
345+
// Any item that has a `Self: Sized` requisite is non-dispatchable.
346+
&& !self.generics_require_sized_self(def_id)
347+
&& self.is_dyn_incompatible_final_assoc_fn_query(def_id)
348+
}
336349
}

compiler/rustc_middle/src/ty/context.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,13 @@ impl<'tcx> TyCtxt<'tcx> {
662662
pub fn feed_delayed_owner(self, key: LocalDefId, owner: MaybeOwner<'tcx>) {
663663
TyCtxtFeed { tcx: self, key }.delayed_owner(owner);
664664
}
665+
666+
pub fn feed_is_dyn_incompatible_final_assoc_fn(self, key: DefId, value: bool) {
667+
debug_assert_eq!(self.def_kind(key), DefKind::AssocFn);
668+
debug_assert!(self.defaultness(key).is_final());
669+
670+
TyCtxtFeed { tcx: self, key }.is_dyn_incompatible_final_assoc_fn_query(value)
671+
}
665672
}
666673

667674
impl<'tcx, KEY: Copy> TyCtxtFeed<'tcx, KEY> {

compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -314,11 +314,6 @@ pub fn dyn_compatibility_violations_for_assoc_item(
314314
trait_def_id: DefId,
315315
item: ty::AssocItem,
316316
) -> Vec<DynCompatibilityViolation> {
317-
// `final` assoc functions don't prevent a trait from being dyn-compatible
318-
if tcx.defaultness(item.def_id).is_final() {
319-
return Vec::new();
320-
}
321-
322317
// Any item that has a `Self: Sized` requisite is otherwise exempt from the regulations.
323318
if tcx.generics_require_sized_self(item.def_id) {
324319
return Vec::new();
@@ -360,7 +355,7 @@ pub fn dyn_compatibility_violations_for_assoc_item(
360355
.collect()
361356
}
362357
ty::AssocKind::Fn { name, .. } => {
363-
virtual_call_violations_for_method(tcx, trait_def_id, item)
358+
let violations: Vec<_> = virtual_call_violations_for_method(tcx, trait_def_id, item)
364359
.into_iter()
365360
.map(|v| {
366361
let node = tcx.hir_get_if_local(item.def_id);
@@ -377,7 +372,14 @@ pub fn dyn_compatibility_violations_for_assoc_item(
377372

378373
DynCompatibilityViolation::Method(name, v, span)
379374
})
380-
.collect()
375+
.collect();
376+
377+
if tcx.defaultness(item.def_id).is_final() {
378+
tcx.feed_is_dyn_incompatible_final_assoc_fn(item.def_id, !violations.is_empty());
379+
Vec::new()
380+
} else {
381+
violations
382+
}
381383
}
382384
ty::AssocKind::Type { data } => {
383385
if !tcx.generics_of(item.def_id).is_own_empty()

compiler/rustc_trait_selection/src/traits/vtable.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ fn own_existential_vtable_entries_iter(
210210
debug!("own_existential_vtable_entry: trait_method={:?}", trait_method);
211211
let def_id = trait_method.def_id;
212212

213-
// Final methods should not be included in the vtable.
214-
if trait_method.defaultness(tcx).is_final() {
213+
// Final methods should not be included in the vtable if they are not dyn-compatible.
214+
if tcx.defaultness(def_id).is_final() && tcx.is_dyn_incompatible_final_assoc_fn(def_id) {
215215
return None;
216216
}
217217

compiler/rustc_ty_utils/src/instance.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,10 @@ fn resolve_associated_item<'tcx>(
238238
traits::ImplSource::Builtin(BuiltinImplSource::Object(_), _) => {
239239
let trait_ref = ty::TraitRef::from_assoc(tcx, trait_id, rcvr_args);
240240

241-
// `final` methods should be called directly.
242-
if tcx.defaultness(trait_item_id).is_final() {
241+
// `final` methods should be called directly if they are not dyn-compatible.
242+
if tcx.defaultness(trait_item_id).is_final()
243+
&& tcx.is_dyn_incompatible_final_assoc_fn(trait_item_id)
244+
{
243245
return Ok(Some(ty::Instance::new_raw(trait_item_id, rcvr_args)));
244246
}
245247

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//@ run-pass
2+
3+
#![feature(final_associated_functions)]
4+
5+
trait Tr
6+
where
7+
Self: 'static,
8+
{
9+
final fn foo(&self) -> std::any::TypeId {
10+
std::any::TypeId::of::<Self>()
11+
}
12+
}
13+
14+
struct Foo;
15+
impl Tr for Foo {}
16+
17+
struct Bar;
18+
impl Tr for Bar {}
19+
20+
fn foo(t: &dyn Tr) -> std::any::TypeId {
21+
t.foo()
22+
}
23+
24+
fn main() {
25+
assert_ne!(foo(&Foo), foo(&Bar));
26+
}

0 commit comments

Comments
 (0)