Skip to content

Commit d98b99a

Browse files
More assertions, tests, and miri coverage
1 parent 9dc41a0 commit d98b99a

13 files changed

+173
-86
lines changed

compiler/rustc_codegen_ssa/src/meth.rs

+2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ fn dyn_trait_in_self<'tcx>(
8080
if let GenericArgKind::Type(ty) = arg.unpack()
8181
&& let ty::Dynamic(data, _, _) = ty.kind()
8282
{
83+
// FIXME(arbitrary_self_types): This is likely broken for receivers which
84+
// have a "non-self" trait objects as a generic argument.
8385
return data
8486
.principal()
8587
.map(|principal| tcx.instantiate_bound_regions_with_erased(principal));

compiler/rustc_const_eval/src/interpret/call.rs

+26-19
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::borrow::Cow;
55

66
use either::{Left, Right};
77
use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx};
8+
use rustc_hir::def_id::DefId;
89
use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout};
910
use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef};
1011
use rustc_middle::{bug, mir, span_bug};
@@ -693,25 +694,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
693694
trace!("Virtual call dispatches to {fn_inst:#?}");
694695
// We can also do the lookup based on `def_id` and `dyn_ty`, and check that that
695696
// produces the same result.
696-
if cfg!(debug_assertions) {
697-
let tcx = *self.tcx;
698-
699-
let trait_def_id = tcx.trait_of_item(def_id).unwrap();
700-
let virtual_trait_ref =
701-
ty::TraitRef::from_method(tcx, trait_def_id, instance.args);
702-
let existential_trait_ref =
703-
ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
704-
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
705-
706-
let concrete_method = Instance::expect_resolve_for_vtable(
707-
tcx,
708-
self.typing_env,
709-
def_id,
710-
instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args),
711-
self.cur_span(),
712-
);
713-
assert_eq!(fn_inst, concrete_method);
714-
}
697+
self.assert_virtual_instance_matches_concrete(dyn_ty, def_id, instance, fn_inst);
715698

716699
// Adjust receiver argument. Layout can be any (thin) ptr.
717700
let receiver_ty = Ty::new_mut_ptr(self.tcx.tcx, dyn_ty);
@@ -744,6 +727,30 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
744727
}
745728
}
746729

730+
fn assert_virtual_instance_matches_concrete(
731+
&self,
732+
dyn_ty: Ty<'tcx>,
733+
def_id: DefId,
734+
virtual_instance: ty::Instance<'tcx>,
735+
concrete_instance: ty::Instance<'tcx>,
736+
) {
737+
let tcx = *self.tcx;
738+
739+
let trait_def_id = tcx.trait_of_item(def_id).unwrap();
740+
let virtual_trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, virtual_instance.args);
741+
let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
742+
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
743+
744+
let concrete_method = Instance::expect_resolve_for_vtable(
745+
tcx,
746+
self.typing_env,
747+
def_id,
748+
virtual_instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args),
749+
self.cur_span(),
750+
);
751+
assert_eq!(concrete_instance, concrete_method);
752+
}
753+
747754
/// Initiate a tail call to this function -- popping the current stack frame, pushing the new
748755
/// stack frame and initializing the arguments.
749756
pub(super) fn init_fn_tail_call(

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> {
@@ -228,6 +226,11 @@ fn vtable_entries<'tcx>(
228226
trait_ref: ty::TraitRef<'tcx>,
229227
) -> &'tcx [VtblEntry<'tcx>] {
230228
debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param());
229+
debug_assert_eq!(
230+
tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref),
231+
trait_ref,
232+
"vtable trait ref should be normalized"
233+
);
231234

232235
debug!("vtable_entries({:?})", trait_ref);
233236

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

309317
let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
310318
bug!();
@@ -323,11 +331,9 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
323331
vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
324332
}
325333
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
326-
if trait_refs_are_compatible(
327-
tcx,
328-
ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
329-
target_principal,
330-
) {
334+
if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
335+
== target_principal
336+
{
331337
return ControlFlow::Break(vptr_offset);
332338
}
333339

@@ -358,6 +364,12 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
358364
),
359365
) -> Option<usize> {
360366
debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
367+
debug_assert_eq!(
368+
tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
369+
key,
370+
"upcasting trait refs should be normalized"
371+
);
372+
361373
let (source, target) = key;
362374

363375
// If the target principal is `None`, we can just return `None`.
@@ -384,11 +396,9 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
384396
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
385397
vptr_offset +=
386398
tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
387-
if trait_refs_are_compatible(
388-
tcx,
389-
ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
390-
target_principal,
391-
) {
399+
if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
400+
== target_principal
401+
{
392402
if emit_vptr {
393403
return ControlFlow::Break(Some(vptr_offset));
394404
} else {
@@ -408,27 +418,6 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
408418
prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
409419
}
410420

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

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

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

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!

tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#![feature(rustc_attrs)]
22

3+
// Test for <https://github.com/rust-lang/rust/issues/135316>.
4+
35
trait Supertrait<T> {
46
fn _print_numbers(&self, mem: &[usize; 100]) {
57
}

tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ error: vtable entries: [
55
Method(<dyn for<'a> Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)),
66
Method(<dyn for<'a> Trait<&(), &'a ()> as Trait<&(), &()>>::say_hello - shim(reify)),
77
]
8-
--> $DIR/multiple-supertraits-modulo-binder-vtable.rs:18:1
8+
--> $DIR/multiple-supertraits-modulo-binder-vtable.rs:20:1
99
|
1010
LL | type First = dyn for<'a> Trait<&'static (), &'a ()>;
1111
| ^^^^^^^^^^
@@ -17,7 +17,7 @@ error: vtable entries: [
1717
Method(<dyn Trait<&(), &()> as Supertrait<&()>>::_print_numbers - shim(reify)),
1818
Method(<dyn Trait<&(), &()> as Trait<&(), &()>>::say_hello - shim(reify)),
1919
]
20-
--> $DIR/multiple-supertraits-modulo-binder-vtable.rs:22:1
20+
--> $DIR/multiple-supertraits-modulo-binder-vtable.rs:24:1
2121
|
2222
LL | type Second = dyn Trait<&'static (), &'static ()>;
2323
| ^^^^^^^^^^^

tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//@ run-pass
22
//@ check-run-results
33

4+
// Test for <https://github.com/rust-lang/rust/issues/135316>.
5+
46
#![feature(trait_upcasting)]
57

68
trait Supertrait<T> {

tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#![feature(rustc_attrs)]
2-
32
#![feature(trait_upcasting)]
43

4+
// Test for <https://github.com/rust-lang/rust/issues/135315>.
5+
56
trait Supertrait<T> {
67
fn _print_numbers(&self, mem: &[usize; 100]) {
78
println!("{mem:?}");

tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ error: vtable entries: [
55
Method(<() as Supertrait<()>>::_print_numbers),
66
Method(<() as Middle<()>>::say_hello),
77
]
8-
--> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:29:1
8+
--> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:30:1
99
|
1010
LL | impl Trait for () {}
1111
| ^^^^^^^^^^^^^^^^^
@@ -17,7 +17,7 @@ error: vtable entries: [
1717
Method(<dyn Middle<()> as Supertrait<()>>::_print_numbers - shim(reify)),
1818
Method(<dyn Middle<()> as Middle<()>>::say_hello - shim(reify)),
1919
]
20-
--> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:33:1
20+
--> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:34:1
2121
|
2222
LL | type Virtual = dyn Middle<()>;
2323
| ^^^^^^^^^^^^

tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#![feature(trait_upcasting)]
55

6+
// Test for <https://github.com/rust-lang/rust/issues/135315>.
7+
68
trait Supertrait<T> {
79
fn _print_numbers(&self, mem: &[usize; 100]) {
810
println!("{mem:?}");

0 commit comments

Comments
 (0)