Skip to content

Commit 305a3e0

Browse files
More assertions, tests, and miri coverage
1 parent 76eb4f3 commit 305a3e0

File tree

4 files changed

+101
-62
lines changed

4 files changed

+101
-62
lines changed

compiler/rustc_const_eval/src/interpret/cast.rs

+26-28
Original file line numberDiff line numberDiff line change
@@ -414,35 +414,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
414414

415415
// Sanity-check that `supertrait_vtable_slot` in this type's vtable indeed produces
416416
// our destination trait.
417-
if cfg!(debug_assertions) {
418-
let vptr_entry_idx =
419-
self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty));
420-
let vtable_entries = self.vtable_entries(data_a.principal(), ty);
421-
if let Some(entry_idx) = vptr_entry_idx {
422-
let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) =
423-
vtable_entries.get(entry_idx)
424-
else {
425-
span_bug!(
426-
self.cur_span(),
427-
"invalid vtable entry index in {} -> {} upcast",
428-
src_pointee_ty,
429-
dest_pointee_ty
430-
);
431-
};
432-
let erased_trait_ref =
433-
ty::ExistentialTraitRef::erase_self_ty(*self.tcx, upcast_trait_ref);
434-
assert!(data_b.principal().is_some_and(|b| self.eq_in_param_env(
435-
erased_trait_ref,
436-
self.tcx.instantiate_bound_regions_with_erased(b)
437-
)));
438-
} else {
439-
// In this case codegen would keep using the old vtable. We don't want to do
440-
// that as it has the wrong trait. The reason codegen can do this is that
441-
// one vtable is a prefix of the other, so we double-check that.
442-
let vtable_entries_b = self.vtable_entries(data_b.principal(), ty);
443-
assert!(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b);
417+
let vptr_entry_idx =
418+
self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty));
419+
let vtable_entries = self.vtable_entries(data_a.principal(), ty);
420+
if let Some(entry_idx) = vptr_entry_idx {
421+
let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) =
422+
vtable_entries.get(entry_idx)
423+
else {
424+
span_bug!(
425+
self.cur_span(),
426+
"invalid vtable entry index in {} -> {} upcast",
427+
src_pointee_ty,
428+
dest_pointee_ty
429+
);
444430
};
445-
}
431+
let erased_trait_ref =
432+
ty::ExistentialTraitRef::erase_self_ty(*self.tcx, upcast_trait_ref);
433+
assert!(data_b.principal().is_some_and(|b| self.eq_in_param_env(
434+
erased_trait_ref,
435+
self.tcx.instantiate_bound_regions_with_erased(b)
436+
)));
437+
} else {
438+
// In this case codegen would keep using the old vtable. We don't want to do
439+
// that as it has the wrong trait. The reason codegen can do this is that
440+
// one vtable is a prefix of the other, so we double-check that.
441+
let vtable_entries_b = self.vtable_entries(data_b.principal(), ty);
442+
assert!(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b);
443+
};
446444

447445
// Get the destination trait vtable and return that.
448446
let new_vptr = self.get_vtable_ptr(ty, data_b)?;

compiler/rustc_trait_selection/src/traits/vtable.rs

+23-34
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ use std::fmt::Debug;
22
use std::ops::ControlFlow;
33

44
use rustc_hir::def_id::DefId;
5-
use rustc_infer::infer::TyCtxtInferExt;
6-
use rustc_infer::traits::ObligationCause;
75
use rustc_infer::traits::util::PredicateSet;
86
use rustc_middle::bug;
97
use rustc_middle::query::Providers;
@@ -14,7 +12,7 @@ use rustc_span::DUMMY_SP;
1412
use smallvec::{SmallVec, smallvec};
1513
use tracing::debug;
1614

17-
use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method};
15+
use crate::traits::{impossible_predicates, is_vtable_safe_method};
1816

1917
#[derive(Clone, Debug)]
2018
pub enum VtblSegment<'tcx> {
@@ -230,6 +228,11 @@ fn vtable_entries<'tcx>(
230228
trait_ref: ty::TraitRef<'tcx>,
231229
) -> &'tcx [VtblEntry<'tcx>] {
232230
debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param());
231+
debug_assert_eq!(
232+
tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref),
233+
trait_ref,
234+
"vtable trait ref should be normalized"
235+
);
233236

234237
debug!("vtable_entries({:?})", trait_ref);
235238

@@ -307,6 +310,11 @@ fn vtable_entries<'tcx>(
307310
// for `Supertrait`'s methods in the vtable of `Subtrait`.
308311
pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRef<'tcx>) -> usize {
309312
debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
313+
debug_assert_eq!(
314+
tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
315+
key,
316+
"vtable trait ref should be normalized"
317+
);
310318

311319
let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
312320
bug!();
@@ -325,11 +333,9 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
325333
vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
326334
}
327335
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
328-
if trait_refs_are_compatible(
329-
tcx,
330-
ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
331-
target_principal,
332-
) {
336+
if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
337+
== target_principal
338+
{
333339
return ControlFlow::Break(vptr_offset);
334340
}
335341

@@ -360,6 +366,12 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
360366
),
361367
) -> Option<usize> {
362368
debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
369+
debug_assert_eq!(
370+
tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
371+
key,
372+
"upcasting trait refs should be normalized"
373+
);
374+
363375
let (source, target) = key;
364376

365377
// If the target principal is `None`, we can just return `None`.
@@ -386,11 +398,9 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
386398
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
387399
vptr_offset +=
388400
tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
389-
if trait_refs_are_compatible(
390-
tcx,
391-
ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
392-
target_principal,
393-
) {
401+
if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
402+
== target_principal
403+
{
394404
if emit_vptr {
395405
return ControlFlow::Break(Some(vptr_offset));
396406
} else {
@@ -410,27 +420,6 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
410420
prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
411421
}
412422

413-
fn trait_refs_are_compatible<'tcx>(
414-
tcx: TyCtxt<'tcx>,
415-
vtable_principal: ty::ExistentialTraitRef<'tcx>,
416-
target_principal: ty::ExistentialTraitRef<'tcx>,
417-
) -> bool {
418-
if vtable_principal.def_id != target_principal.def_id {
419-
return false;
420-
}
421-
422-
let (infcx, param_env) =
423-
tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized());
424-
let ocx = ObligationCtxt::new(&infcx);
425-
let source_principal = ocx.normalize(&ObligationCause::dummy(), param_env, vtable_principal);
426-
let target_principal = ocx.normalize(&ObligationCause::dummy(), param_env, target_principal);
427-
let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, target_principal, source_principal)
428-
else {
429-
return false;
430-
};
431-
ocx.select_all_or_error().is_empty()
432-
}
433-
434423
pub(super) fn provide(providers: &mut Providers) {
435424
*providers = Providers {
436425
own_existential_vtable_entries,

src/tools/miri/tests/pass/dyn-upcast.rs

+50
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ fn main() {
1010
replace_vptr();
1111
vtable_nop_cast();
1212
drop_principal();
13+
modulo_binder();
14+
modulo_assoc();
1315
}
1416

1517
fn vtable_nop_cast() {
@@ -482,3 +484,51 @@ fn drop_principal() {
482484
println!("before");
483485
drop(y);
484486
}
487+
488+
fn modulo_binder() {
489+
trait Supertrait<T> {
490+
fn _print_numbers(&self, mem: &[usize; 100]) {
491+
println!("{mem:?}");
492+
}
493+
}
494+
impl<T> Supertrait<T> for () {}
495+
496+
trait Trait<T, U>: Supertrait<T> + Supertrait<U> {
497+
fn say_hello(&self, _: &usize) {
498+
println!("Hello!");
499+
}
500+
}
501+
impl<T, U> Trait<T, U> for () {}
502+
503+
(&() as &'static dyn for<'a> Trait<&'static (), &'a ()>
504+
as &'static dyn Trait<&'static (), &'static ()>)
505+
.say_hello(&0);
506+
}
507+
508+
fn modulo_assoc() {
509+
trait Supertrait<T> {
510+
fn _print_numbers(&self, mem: &[usize; 100]) {
511+
println!("{mem:?}");
512+
}
513+
}
514+
impl<T> Supertrait<T> for () {}
515+
516+
trait Identity {
517+
type Selff;
518+
}
519+
impl<Selff> Identity for Selff {
520+
type Selff = Selff;
521+
}
522+
523+
trait Middle<T>: Supertrait<()> + Supertrait<T> {
524+
fn say_hello(&self, _: &usize) {
525+
println!("Hello!");
526+
}
527+
}
528+
impl<T> Middle<T> for () {}
529+
530+
trait Trait: Middle<<() as Identity>::Selff> {}
531+
impl Trait for () {}
532+
533+
(&() as &dyn Trait as &dyn Middle<()>).say_hello(&0);
534+
}

src/tools/miri/tests/pass/dyn-upcast.stdout

+2
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ before
22
goodbye
33
before
44
goodbye
5+
Hello!
6+
Hello!

0 commit comments

Comments
 (0)