More assertions, tests, and miri coverage
This commit is contained in:
parent
9dc41a048d
commit
d98b99af56
13 changed files with 173 additions and 86 deletions
|
@ -80,6 +80,8 @@ fn dyn_trait_in_self<'tcx>(
|
|||
if let GenericArgKind::Type(ty) = arg.unpack()
|
||||
&& let ty::Dynamic(data, _, _) = ty.kind()
|
||||
{
|
||||
// FIXME(arbitrary_self_types): This is likely broken for receivers which
|
||||
// have a "non-self" trait objects as a generic argument.
|
||||
return data
|
||||
.principal()
|
||||
.map(|principal| tcx.instantiate_bound_regions_with_erased(principal));
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::borrow::Cow;
|
|||
|
||||
use either::{Left, Right};
|
||||
use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef};
|
||||
use rustc_middle::{bug, mir, span_bug};
|
||||
|
@ -693,25 +694,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
trace!("Virtual call dispatches to {fn_inst:#?}");
|
||||
// We can also do the lookup based on `def_id` and `dyn_ty`, and check that that
|
||||
// produces the same result.
|
||||
if cfg!(debug_assertions) {
|
||||
let tcx = *self.tcx;
|
||||
|
||||
let trait_def_id = tcx.trait_of_item(def_id).unwrap();
|
||||
let virtual_trait_ref =
|
||||
ty::TraitRef::from_method(tcx, trait_def_id, instance.args);
|
||||
let existential_trait_ref =
|
||||
ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
|
||||
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
|
||||
|
||||
let concrete_method = Instance::expect_resolve_for_vtable(
|
||||
tcx,
|
||||
self.typing_env,
|
||||
def_id,
|
||||
instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args),
|
||||
self.cur_span(),
|
||||
);
|
||||
assert_eq!(fn_inst, concrete_method);
|
||||
}
|
||||
self.assert_virtual_instance_matches_concrete(dyn_ty, def_id, instance, fn_inst);
|
||||
|
||||
// Adjust receiver argument. Layout can be any (thin) ptr.
|
||||
let receiver_ty = Ty::new_mut_ptr(self.tcx.tcx, dyn_ty);
|
||||
|
@ -744,6 +727,30 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
fn assert_virtual_instance_matches_concrete(
|
||||
&self,
|
||||
dyn_ty: Ty<'tcx>,
|
||||
def_id: DefId,
|
||||
virtual_instance: ty::Instance<'tcx>,
|
||||
concrete_instance: ty::Instance<'tcx>,
|
||||
) {
|
||||
let tcx = *self.tcx;
|
||||
|
||||
let trait_def_id = tcx.trait_of_item(def_id).unwrap();
|
||||
let virtual_trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, virtual_instance.args);
|
||||
let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
|
||||
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
|
||||
|
||||
let concrete_method = Instance::expect_resolve_for_vtable(
|
||||
tcx,
|
||||
self.typing_env,
|
||||
def_id,
|
||||
virtual_instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args),
|
||||
self.cur_span(),
|
||||
);
|
||||
assert_eq!(concrete_instance, concrete_method);
|
||||
}
|
||||
|
||||
/// Initiate a tail call to this function -- popping the current stack frame, pushing the new
|
||||
/// stack frame and initializing the arguments.
|
||||
pub(super) fn init_fn_tail_call(
|
||||
|
|
|
@ -414,7 +414,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
// Sanity-check that `supertrait_vtable_slot` in this type's vtable indeed produces
|
||||
// our destination trait.
|
||||
if cfg!(debug_assertions) {
|
||||
let vptr_entry_idx =
|
||||
self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty));
|
||||
let vtable_entries = self.vtable_entries(data_a.principal(), ty);
|
||||
|
@ -442,7 +441,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let vtable_entries_b = self.vtable_entries(data_b.principal(), ty);
|
||||
assert!(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b);
|
||||
};
|
||||
}
|
||||
|
||||
// Get the destination trait vtable and return that.
|
||||
let new_vptr = self.get_vtable_ptr(ty, data_b)?;
|
||||
|
|
|
@ -2,8 +2,6 @@ use std::fmt::Debug;
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
use rustc_infer::traits::util::PredicateSet;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::query::Providers;
|
||||
|
@ -14,7 +12,7 @@ use rustc_span::DUMMY_SP;
|
|||
use smallvec::{SmallVec, smallvec};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method};
|
||||
use crate::traits::{impossible_predicates, is_vtable_safe_method};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum VtblSegment<'tcx> {
|
||||
|
@ -228,6 +226,11 @@ fn vtable_entries<'tcx>(
|
|||
trait_ref: ty::TraitRef<'tcx>,
|
||||
) -> &'tcx [VtblEntry<'tcx>] {
|
||||
debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param());
|
||||
debug_assert_eq!(
|
||||
tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref),
|
||||
trait_ref,
|
||||
"vtable trait ref should be normalized"
|
||||
);
|
||||
|
||||
debug!("vtable_entries({:?})", trait_ref);
|
||||
|
||||
|
@ -305,6 +308,11 @@ fn vtable_entries<'tcx>(
|
|||
// for `Supertrait`'s methods in the vtable of `Subtrait`.
|
||||
pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRef<'tcx>) -> usize {
|
||||
debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
|
||||
debug_assert_eq!(
|
||||
tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
|
||||
key,
|
||||
"vtable trait ref should be normalized"
|
||||
);
|
||||
|
||||
let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
|
||||
bug!();
|
||||
|
@ -323,11 +331,9 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
|
|||
vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
|
||||
}
|
||||
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
|
||||
if trait_refs_are_compatible(
|
||||
tcx,
|
||||
ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
|
||||
target_principal,
|
||||
) {
|
||||
if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
|
||||
== target_principal
|
||||
{
|
||||
return ControlFlow::Break(vptr_offset);
|
||||
}
|
||||
|
||||
|
@ -358,6 +364,12 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
|
|||
),
|
||||
) -> Option<usize> {
|
||||
debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
|
||||
debug_assert_eq!(
|
||||
tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
|
||||
key,
|
||||
"upcasting trait refs should be normalized"
|
||||
);
|
||||
|
||||
let (source, target) = key;
|
||||
|
||||
// If the target principal is `None`, we can just return `None`.
|
||||
|
@ -384,11 +396,9 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
|
|||
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
|
||||
vptr_offset +=
|
||||
tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
|
||||
if trait_refs_are_compatible(
|
||||
tcx,
|
||||
ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
|
||||
target_principal,
|
||||
) {
|
||||
if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
|
||||
== target_principal
|
||||
{
|
||||
if emit_vptr {
|
||||
return ControlFlow::Break(Some(vptr_offset));
|
||||
} else {
|
||||
|
@ -408,27 +418,6 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
|
|||
prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
|
||||
}
|
||||
|
||||
fn trait_refs_are_compatible<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
vtable_principal: ty::ExistentialTraitRef<'tcx>,
|
||||
target_principal: ty::ExistentialTraitRef<'tcx>,
|
||||
) -> bool {
|
||||
if vtable_principal.def_id != target_principal.def_id {
|
||||
return false;
|
||||
}
|
||||
|
||||
let (infcx, param_env) =
|
||||
tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized());
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
let source_principal = ocx.normalize(&ObligationCause::dummy(), param_env, vtable_principal);
|
||||
let target_principal = ocx.normalize(&ObligationCause::dummy(), param_env, target_principal);
|
||||
let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, target_principal, source_principal)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
ocx.select_all_or_error().is_empty()
|
||||
}
|
||||
|
||||
pub(super) fn provide(providers: &mut Providers) {
|
||||
*providers = Providers {
|
||||
own_existential_vtable_entries,
|
||||
|
|
|
@ -10,6 +10,8 @@ fn main() {
|
|||
replace_vptr();
|
||||
vtable_nop_cast();
|
||||
drop_principal();
|
||||
modulo_binder();
|
||||
modulo_assoc();
|
||||
}
|
||||
|
||||
fn vtable_nop_cast() {
|
||||
|
@ -482,3 +484,53 @@ fn drop_principal() {
|
|||
println!("before");
|
||||
drop(y);
|
||||
}
|
||||
|
||||
// Test for <https://github.com/rust-lang/rust/issues/135316>.
|
||||
fn modulo_binder() {
|
||||
trait Supertrait<T> {
|
||||
fn _print_numbers(&self, mem: &[usize; 100]) {
|
||||
println!("{mem:?}");
|
||||
}
|
||||
}
|
||||
impl<T> Supertrait<T> for () {}
|
||||
|
||||
trait Trait<T, U>: Supertrait<T> + Supertrait<U> {
|
||||
fn say_hello(&self, _: &usize) {
|
||||
println!("Hello!");
|
||||
}
|
||||
}
|
||||
impl<T, U> Trait<T, U> for () {}
|
||||
|
||||
(&() as &'static dyn for<'a> Trait<&'static (), &'a ()>
|
||||
as &'static dyn Trait<&'static (), &'static ()>)
|
||||
.say_hello(&0);
|
||||
}
|
||||
|
||||
// Test for <https://github.com/rust-lang/rust/issues/135315>.
|
||||
fn modulo_assoc() {
|
||||
trait Supertrait<T> {
|
||||
fn _print_numbers(&self, mem: &[usize; 100]) {
|
||||
println!("{mem:?}");
|
||||
}
|
||||
}
|
||||
impl<T> Supertrait<T> for () {}
|
||||
|
||||
trait Identity {
|
||||
type Selff;
|
||||
}
|
||||
impl<Selff> Identity for Selff {
|
||||
type Selff = Selff;
|
||||
}
|
||||
|
||||
trait Middle<T>: Supertrait<()> + Supertrait<T> {
|
||||
fn say_hello(&self, _: &usize) {
|
||||
println!("Hello!");
|
||||
}
|
||||
}
|
||||
impl<T> Middle<T> for () {}
|
||||
|
||||
trait Trait: Middle<<() as Identity>::Selff> {}
|
||||
impl Trait for () {}
|
||||
|
||||
(&() as &dyn Trait as &dyn Middle<()>).say_hello(&0);
|
||||
}
|
||||
|
|
|
@ -2,3 +2,5 @@ before
|
|||
goodbye
|
||||
before
|
||||
goodbye
|
||||
Hello!
|
||||
Hello!
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#![feature(rustc_attrs)]
|
||||
|
||||
// Test for <https://github.com/rust-lang/rust/issues/135316>.
|
||||
|
||||
trait Supertrait<T> {
|
||||
fn _print_numbers(&self, mem: &[usize; 100]) {
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ error: vtable entries: [
|
|||
Method(<dyn for<'a> Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)),
|
||||
Method(<dyn for<'a> Trait<&(), &'a ()> as Trait<&(), &()>>::say_hello - shim(reify)),
|
||||
]
|
||||
--> $DIR/multiple-supertraits-modulo-binder-vtable.rs:18:1
|
||||
--> $DIR/multiple-supertraits-modulo-binder-vtable.rs:20:1
|
||||
|
|
||||
LL | type First = dyn for<'a> Trait<&'static (), &'a ()>;
|
||||
| ^^^^^^^^^^
|
||||
|
@ -17,7 +17,7 @@ error: vtable entries: [
|
|||
Method(<dyn Trait<&(), &()> as Supertrait<&()>>::_print_numbers - shim(reify)),
|
||||
Method(<dyn Trait<&(), &()> as Trait<&(), &()>>::say_hello - shim(reify)),
|
||||
]
|
||||
--> $DIR/multiple-supertraits-modulo-binder-vtable.rs:22:1
|
||||
--> $DIR/multiple-supertraits-modulo-binder-vtable.rs:24:1
|
||||
|
|
||||
LL | type Second = dyn Trait<&'static (), &'static ()>;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//@ run-pass
|
||||
//@ check-run-results
|
||||
|
||||
// Test for <https://github.com/rust-lang/rust/issues/135316>.
|
||||
|
||||
#![feature(trait_upcasting)]
|
||||
|
||||
trait Supertrait<T> {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#![feature(rustc_attrs)]
|
||||
|
||||
#![feature(trait_upcasting)]
|
||||
|
||||
// Test for <https://github.com/rust-lang/rust/issues/135315>.
|
||||
|
||||
trait Supertrait<T> {
|
||||
fn _print_numbers(&self, mem: &[usize; 100]) {
|
||||
println!("{mem:?}");
|
||||
|
|
|
@ -5,7 +5,7 @@ error: vtable entries: [
|
|||
Method(<() as Supertrait<()>>::_print_numbers),
|
||||
Method(<() as Middle<()>>::say_hello),
|
||||
]
|
||||
--> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:29:1
|
||||
--> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:30:1
|
||||
|
|
||||
LL | impl Trait for () {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
@ -17,7 +17,7 @@ error: vtable entries: [
|
|||
Method(<dyn Middle<()> as Supertrait<()>>::_print_numbers - shim(reify)),
|
||||
Method(<dyn Middle<()> as Middle<()>>::say_hello - shim(reify)),
|
||||
]
|
||||
--> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:33:1
|
||||
--> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:34:1
|
||||
|
|
||||
LL | type Virtual = dyn Middle<()>;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#![feature(trait_upcasting)]
|
||||
|
||||
// Test for <https://github.com/rust-lang/rust/issues/135315>.
|
||||
|
||||
trait Supertrait<T> {
|
||||
fn _print_numbers(&self, mem: &[usize; 100]) {
|
||||
println!("{mem:?}");
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
//@ run-pass
|
||||
|
||||
#![feature(trait_upcasting)]
|
||||
|
||||
trait Super<U> {
|
||||
fn call(&self)
|
||||
where
|
||||
U: HigherRanked,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Super<T> for () {}
|
||||
|
||||
trait HigherRanked {}
|
||||
impl HigherRanked for for<'a> fn(&'a ()) {}
|
||||
|
||||
trait Unimplemented {}
|
||||
impl<T: Unimplemented> HigherRanked for T {}
|
||||
|
||||
trait Sub: Super<fn(&'static ())> + Super<for<'a> fn(&'a ())> {}
|
||||
impl Sub for () {}
|
||||
|
||||
fn main() {
|
||||
let a: &dyn Sub = &();
|
||||
// `Super<fn(&'static ())>` and `Super<for<'a> fn(&'a ())>` have different
|
||||
// vtables and we need to upcast to the latter!
|
||||
let b: &dyn Super<for<'a> fn(&'a ())> = a;
|
||||
b.call();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue